1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!options.network || player->connected_network)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (level.time == 0)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3308 if (!level_editor_test_game)
3311 /* needed if different viewport properties defined for playing */
3312 ChangeViewportPropertiesIfNeeded();
3316 DrawCompleteVideoDisplay();
3318 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3321 InitGameControlValues();
3323 /* don't play tapes over network */
3324 network_playing = (options.network && !tape.playing);
3326 for (i = 0; i < MAX_PLAYERS; i++)
3328 struct PlayerInfo *player = &stored_player[i];
3330 player->index_nr = i;
3331 player->index_bit = (1 << i);
3332 player->element_nr = EL_PLAYER_1 + i;
3334 player->present = FALSE;
3335 player->active = FALSE;
3336 player->mapped = FALSE;
3338 player->killed = FALSE;
3339 player->reanimated = FALSE;
3342 player->effective_action = 0;
3343 player->programmed_action = 0;
3345 player->mouse_action.lx = 0;
3346 player->mouse_action.ly = 0;
3347 player->mouse_action.button = 0;
3348 player->mouse_action.button_hint = 0;
3350 player->effective_mouse_action.lx = 0;
3351 player->effective_mouse_action.ly = 0;
3352 player->effective_mouse_action.button = 0;
3353 player->effective_mouse_action.button_hint = 0;
3356 player->score_final = 0;
3358 player->health = MAX_HEALTH;
3359 player->health_final = MAX_HEALTH;
3361 player->gems_still_needed = level.gems_needed;
3362 player->sokobanfields_still_needed = 0;
3363 player->lights_still_needed = 0;
3364 player->friends_still_needed = 0;
3366 for (j = 0; j < MAX_NUM_KEYS; j++)
3367 player->key[j] = FALSE;
3369 player->num_white_keys = 0;
3371 player->dynabomb_count = 0;
3372 player->dynabomb_size = 1;
3373 player->dynabombs_left = 0;
3374 player->dynabomb_xl = FALSE;
3376 player->MovDir = initial_move_dir;
3379 player->GfxDir = initial_move_dir;
3380 player->GfxAction = ACTION_DEFAULT;
3382 player->StepFrame = 0;
3384 player->initial_element = player->element_nr;
3385 player->artwork_element =
3386 (level.use_artwork_element[i] ? level.artwork_element[i] :
3387 player->element_nr);
3388 player->use_murphy = FALSE;
3390 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3391 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3393 player->gravity = level.initial_player_gravity[i];
3395 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3397 player->actual_frame_counter = 0;
3399 player->step_counter = 0;
3401 player->last_move_dir = initial_move_dir;
3403 player->is_active = FALSE;
3405 player->is_waiting = FALSE;
3406 player->is_moving = FALSE;
3407 player->is_auto_moving = FALSE;
3408 player->is_digging = FALSE;
3409 player->is_snapping = FALSE;
3410 player->is_collecting = FALSE;
3411 player->is_pushing = FALSE;
3412 player->is_switching = FALSE;
3413 player->is_dropping = FALSE;
3414 player->is_dropping_pressed = FALSE;
3416 player->is_bored = FALSE;
3417 player->is_sleeping = FALSE;
3419 player->was_waiting = TRUE;
3420 player->was_moving = FALSE;
3421 player->was_snapping = FALSE;
3422 player->was_dropping = FALSE;
3424 player->force_dropping = FALSE;
3426 player->frame_counter_bored = -1;
3427 player->frame_counter_sleeping = -1;
3429 player->anim_delay_counter = 0;
3430 player->post_delay_counter = 0;
3432 player->dir_waiting = initial_move_dir;
3433 player->action_waiting = ACTION_DEFAULT;
3434 player->last_action_waiting = ACTION_DEFAULT;
3435 player->special_action_bored = ACTION_DEFAULT;
3436 player->special_action_sleeping = ACTION_DEFAULT;
3438 player->switch_x = -1;
3439 player->switch_y = -1;
3441 player->drop_x = -1;
3442 player->drop_y = -1;
3444 player->show_envelope = 0;
3446 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3448 player->push_delay = -1; /* initialized when pushing starts */
3449 player->push_delay_value = game.initial_push_delay_value;
3451 player->drop_delay = 0;
3452 player->drop_pressed_delay = 0;
3454 player->last_jx = -1;
3455 player->last_jy = -1;
3459 player->shield_normal_time_left = 0;
3460 player->shield_deadly_time_left = 0;
3462 player->inventory_infinite_element = EL_UNDEFINED;
3463 player->inventory_size = 0;
3465 if (level.use_initial_inventory[i])
3467 for (j = 0; j < level.initial_inventory_size[i]; j++)
3469 int element = level.initial_inventory_content[i][j];
3470 int collect_count = element_info[element].collect_count_initial;
3473 if (!IS_CUSTOM_ELEMENT(element))
3476 if (collect_count == 0)
3477 player->inventory_infinite_element = element;
3479 for (k = 0; k < collect_count; k++)
3480 if (player->inventory_size < MAX_INVENTORY_SIZE)
3481 player->inventory_element[player->inventory_size++] = element;
3485 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486 SnapField(player, 0, 0);
3488 player->LevelSolved = FALSE;
3489 player->GameOver = FALSE;
3491 player->LevelSolved_GameWon = FALSE;
3492 player->LevelSolved_GameEnd = FALSE;
3493 player->LevelSolved_PanelOff = FALSE;
3494 player->LevelSolved_SaveTape = FALSE;
3495 player->LevelSolved_SaveScore = FALSE;
3497 player->LevelSolved_CountingTime = 0;
3498 player->LevelSolved_CountingScore = 0;
3499 player->LevelSolved_CountingHealth = 0;
3501 map_player_action[i] = i;
3504 network_player_action_received = FALSE;
3506 #if defined(NETWORK_AVALIABLE)
3507 /* initial null action */
3508 if (network_playing)
3509 SendToServer_MovePlayer(MV_NONE);
3518 TimeLeft = level.time;
3521 ScreenMovDir = MV_NONE;
3525 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3527 AllPlayersGone = FALSE;
3529 game.no_time_limit = (level.time == 0);
3531 game.yamyam_content_nr = 0;
3532 game.robot_wheel_active = FALSE;
3533 game.magic_wall_active = FALSE;
3534 game.magic_wall_time_left = 0;
3535 game.light_time_left = 0;
3536 game.timegate_time_left = 0;
3537 game.switchgate_pos = 0;
3538 game.wind_direction = level.wind_direction_initial;
3540 game.lenses_time_left = 0;
3541 game.magnify_time_left = 0;
3543 game.ball_state = level.ball_state_initial;
3544 game.ball_content_nr = 0;
3546 game.envelope_active = FALSE;
3548 for (i = 0; i < NUM_BELTS; i++)
3550 game.belt_dir[i] = MV_NONE;
3551 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3554 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3555 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3557 #if DEBUG_INIT_PLAYER
3560 printf("Player status at level initialization:\n");
3562 for (i = 0; i < MAX_PLAYERS; i++)
3564 struct PlayerInfo *player = &stored_player[i];
3566 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3570 player->connected_locally,
3571 player->connected_network,
3574 if (local_player == player)
3575 printf(" (local player)");
3582 SCAN_PLAYFIELD(x, y)
3584 Feld[x][y] = level.field[x][y];
3585 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3586 ChangeDelay[x][y] = 0;
3587 ChangePage[x][y] = -1;
3588 CustomValue[x][y] = 0; /* initialized in InitField() */
3589 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3591 WasJustMoving[x][y] = 0;
3592 WasJustFalling[x][y] = 0;
3593 CheckCollision[x][y] = 0;
3594 CheckImpact[x][y] = 0;
3596 Pushed[x][y] = FALSE;
3598 ChangeCount[x][y] = 0;
3599 ChangeEvent[x][y] = -1;
3601 ExplodePhase[x][y] = 0;
3602 ExplodeDelay[x][y] = 0;
3603 ExplodeField[x][y] = EX_TYPE_NONE;
3605 RunnerVisit[x][y] = 0;
3606 PlayerVisit[x][y] = 0;
3609 GfxRandom[x][y] = INIT_GFX_RANDOM();
3610 GfxElement[x][y] = EL_UNDEFINED;
3611 GfxAction[x][y] = ACTION_DEFAULT;
3612 GfxDir[x][y] = MV_NONE;
3613 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3616 SCAN_PLAYFIELD(x, y)
3618 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3620 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3622 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3625 InitField(x, y, TRUE);
3627 ResetGfxAnimation(x, y);
3632 for (i = 0; i < MAX_PLAYERS; i++)
3634 struct PlayerInfo *player = &stored_player[i];
3636 /* set number of special actions for bored and sleeping animation */
3637 player->num_special_action_bored =
3638 get_num_special_action(player->artwork_element,
3639 ACTION_BORING_1, ACTION_BORING_LAST);
3640 player->num_special_action_sleeping =
3641 get_num_special_action(player->artwork_element,
3642 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3645 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3646 emulate_sb ? EMU_SOKOBAN :
3647 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3649 /* initialize type of slippery elements */
3650 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3652 if (!IS_CUSTOM_ELEMENT(i))
3654 /* default: elements slip down either to the left or right randomly */
3655 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3657 /* SP style elements prefer to slip down on the left side */
3658 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3659 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3661 /* BD style elements prefer to slip down on the left side */
3662 if (game.emulation == EMU_BOULDERDASH)
3663 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3667 /* initialize explosion and ignition delay */
3668 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3670 if (!IS_CUSTOM_ELEMENT(i))
3673 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3674 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3675 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3676 int last_phase = (num_phase + 1) * delay;
3677 int half_phase = (num_phase / 2) * delay;
3679 element_info[i].explosion_delay = last_phase - 1;
3680 element_info[i].ignition_delay = half_phase;
3682 if (i == EL_BLACK_ORB)
3683 element_info[i].ignition_delay = 1;
3687 /* correct non-moving belts to start moving left */
3688 for (i = 0; i < NUM_BELTS; i++)
3689 if (game.belt_dir[i] == MV_NONE)
3690 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3692 #if USE_NEW_PLAYER_ASSIGNMENTS
3693 for (i = 0; i < MAX_PLAYERS; i++)
3695 stored_player[i].connected = FALSE;
3697 /* in network game mode, the local player might not be the first player */
3698 if (stored_player[i].connected_locally)
3699 local_player = &stored_player[i];
3702 if (!options.network)
3703 local_player->connected = TRUE;
3707 for (i = 0; i < MAX_PLAYERS; i++)
3708 stored_player[i].connected = tape.player_participates[i];
3710 else if (options.network)
3712 /* add team mode players connected over the network (needed for correct
3713 assignment of player figures from level to locally playing players) */
3715 for (i = 0; i < MAX_PLAYERS; i++)
3716 if (stored_player[i].connected_network)
3717 stored_player[i].connected = TRUE;
3719 else if (game.team_mode)
3721 /* try to guess locally connected team mode players (needed for correct
3722 assignment of player figures from level to locally playing players) */
3724 for (i = 0; i < MAX_PLAYERS; i++)
3725 if (setup.input[i].use_joystick ||
3726 setup.input[i].key.left != KSYM_UNDEFINED)
3727 stored_player[i].connected = TRUE;
3730 #if DEBUG_INIT_PLAYER
3733 printf("Player status after level initialization:\n");
3735 for (i = 0; i < MAX_PLAYERS; i++)
3737 struct PlayerInfo *player = &stored_player[i];
3739 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3743 player->connected_locally,
3744 player->connected_network,
3747 if (local_player == player)
3748 printf(" (local player)");
3755 #if DEBUG_INIT_PLAYER
3757 printf("Reassigning players ...\n");
3760 /* check if any connected player was not found in playfield */
3761 for (i = 0; i < MAX_PLAYERS; i++)
3763 struct PlayerInfo *player = &stored_player[i];
3765 if (player->connected && !player->present)
3767 struct PlayerInfo *field_player = NULL;
3769 #if DEBUG_INIT_PLAYER
3771 printf("- looking for field player for player %d ...\n", i + 1);
3774 /* assign first free player found that is present in the playfield */
3776 /* first try: look for unmapped playfield player that is not connected */
3777 for (j = 0; j < MAX_PLAYERS; j++)
3778 if (field_player == NULL &&
3779 stored_player[j].present &&
3780 !stored_player[j].mapped &&
3781 !stored_player[j].connected)
3782 field_player = &stored_player[j];
3784 /* second try: look for *any* unmapped playfield player */
3785 for (j = 0; j < MAX_PLAYERS; j++)
3786 if (field_player == NULL &&
3787 stored_player[j].present &&
3788 !stored_player[j].mapped)
3789 field_player = &stored_player[j];
3791 if (field_player != NULL)
3793 int jx = field_player->jx, jy = field_player->jy;
3795 #if DEBUG_INIT_PLAYER
3797 printf("- found player %d\n", field_player->index_nr + 1);
3800 player->present = FALSE;
3801 player->active = FALSE;
3803 field_player->present = TRUE;
3804 field_player->active = TRUE;
3807 player->initial_element = field_player->initial_element;
3808 player->artwork_element = field_player->artwork_element;
3810 player->block_last_field = field_player->block_last_field;
3811 player->block_delay_adjustment = field_player->block_delay_adjustment;
3814 StorePlayer[jx][jy] = field_player->element_nr;
3816 field_player->jx = field_player->last_jx = jx;
3817 field_player->jy = field_player->last_jy = jy;
3819 if (local_player == player)
3820 local_player = field_player;
3822 map_player_action[field_player->index_nr] = i;
3824 field_player->mapped = TRUE;
3826 #if DEBUG_INIT_PLAYER
3828 printf("- map_player_action[%d] == %d\n",
3829 field_player->index_nr + 1, i + 1);
3834 if (player->connected && player->present)
3835 player->mapped = TRUE;
3838 #if DEBUG_INIT_PLAYER
3841 printf("Player status after player assignment (first stage):\n");
3843 for (i = 0; i < MAX_PLAYERS; i++)
3845 struct PlayerInfo *player = &stored_player[i];
3847 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3851 player->connected_locally,
3852 player->connected_network,
3855 if (local_player == player)
3856 printf(" (local player)");
3865 /* check if any connected player was not found in playfield */
3866 for (i = 0; i < MAX_PLAYERS; i++)
3868 struct PlayerInfo *player = &stored_player[i];
3870 if (player->connected && !player->present)
3872 for (j = 0; j < MAX_PLAYERS; j++)
3874 struct PlayerInfo *field_player = &stored_player[j];
3875 int jx = field_player->jx, jy = field_player->jy;
3877 /* assign first free player found that is present in the playfield */
3878 if (field_player->present && !field_player->connected)
3880 player->present = TRUE;
3881 player->active = TRUE;
3883 field_player->present = FALSE;
3884 field_player->active = FALSE;
3886 player->initial_element = field_player->initial_element;
3887 player->artwork_element = field_player->artwork_element;
3889 player->block_last_field = field_player->block_last_field;
3890 player->block_delay_adjustment = field_player->block_delay_adjustment;
3892 StorePlayer[jx][jy] = player->element_nr;
3894 player->jx = player->last_jx = jx;
3895 player->jy = player->last_jy = jy;
3905 printf("::: local_player->present == %d\n", local_player->present);
3908 /* set focus to local player for network games, else to all players */
3909 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3910 game.centered_player_nr_next = game.centered_player_nr;
3911 game.set_centered_player = FALSE;
3913 if (network_playing && tape.recording)
3915 /* store client dependent player focus when recording network games */
3916 tape.centered_player_nr_next = game.centered_player_nr_next;
3917 tape.set_centered_player = TRUE;
3922 /* when playing a tape, eliminate all players who do not participate */
3924 #if USE_NEW_PLAYER_ASSIGNMENTS
3926 if (!game.team_mode)
3928 for (i = 0; i < MAX_PLAYERS; i++)
3930 if (stored_player[i].active &&
3931 !tape.player_participates[map_player_action[i]])
3933 struct PlayerInfo *player = &stored_player[i];
3934 int jx = player->jx, jy = player->jy;
3936 #if DEBUG_INIT_PLAYER
3938 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3941 player->active = FALSE;
3942 StorePlayer[jx][jy] = 0;
3943 Feld[jx][jy] = EL_EMPTY;
3950 for (i = 0; i < MAX_PLAYERS; i++)
3952 if (stored_player[i].active &&
3953 !tape.player_participates[i])
3955 struct PlayerInfo *player = &stored_player[i];
3956 int jx = player->jx, jy = player->jy;
3958 player->active = FALSE;
3959 StorePlayer[jx][jy] = 0;
3960 Feld[jx][jy] = EL_EMPTY;
3965 else if (!options.network && !game.team_mode) /* && !tape.playing */
3967 /* when in single player mode, eliminate all but the first active player */
3969 for (i = 0; i < MAX_PLAYERS; i++)
3971 if (stored_player[i].active)
3973 for (j = i + 1; j < MAX_PLAYERS; j++)
3975 if (stored_player[j].active)
3977 struct PlayerInfo *player = &stored_player[j];
3978 int jx = player->jx, jy = player->jy;
3980 player->active = FALSE;
3981 player->present = FALSE;
3983 StorePlayer[jx][jy] = 0;
3984 Feld[jx][jy] = EL_EMPTY;
3991 /* when recording the game, store which players take part in the game */
3994 #if USE_NEW_PLAYER_ASSIGNMENTS
3995 for (i = 0; i < MAX_PLAYERS; i++)
3996 if (stored_player[i].connected)
3997 tape.player_participates[i] = TRUE;
3999 for (i = 0; i < MAX_PLAYERS; i++)
4000 if (stored_player[i].active)
4001 tape.player_participates[i] = TRUE;
4005 #if DEBUG_INIT_PLAYER
4008 printf("Player status after player assignment (final stage):\n");
4010 for (i = 0; i < MAX_PLAYERS; i++)
4012 struct PlayerInfo *player = &stored_player[i];
4014 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4018 player->connected_locally,
4019 player->connected_network,
4022 if (local_player == player)
4023 printf(" (local player)");
4030 if (BorderElement == EL_EMPTY)
4033 SBX_Right = lev_fieldx - SCR_FIELDX;
4035 SBY_Lower = lev_fieldy - SCR_FIELDY;
4040 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4042 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4045 if (full_lev_fieldx <= SCR_FIELDX)
4046 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4047 if (full_lev_fieldy <= SCR_FIELDY)
4048 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4050 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4052 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4055 /* if local player not found, look for custom element that might create
4056 the player (make some assumptions about the right custom element) */
4057 if (!local_player->present)
4059 int start_x = 0, start_y = 0;
4060 int found_rating = 0;
4061 int found_element = EL_UNDEFINED;
4062 int player_nr = local_player->index_nr;
4064 SCAN_PLAYFIELD(x, y)
4066 int element = Feld[x][y];
4071 if (level.use_start_element[player_nr] &&
4072 level.start_element[player_nr] == element &&
4079 found_element = element;
4082 if (!IS_CUSTOM_ELEMENT(element))
4085 if (CAN_CHANGE(element))
4087 for (i = 0; i < element_info[element].num_change_pages; i++)
4089 /* check for player created from custom element as single target */
4090 content = element_info[element].change_page[i].target_element;
4091 is_player = ELEM_IS_PLAYER(content);
4093 if (is_player && (found_rating < 3 ||
4094 (found_rating == 3 && element < found_element)))
4100 found_element = element;
4105 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4107 /* check for player created from custom element as explosion content */
4108 content = element_info[element].content.e[xx][yy];
4109 is_player = ELEM_IS_PLAYER(content);
4111 if (is_player && (found_rating < 2 ||
4112 (found_rating == 2 && element < found_element)))
4114 start_x = x + xx - 1;
4115 start_y = y + yy - 1;
4118 found_element = element;
4121 if (!CAN_CHANGE(element))
4124 for (i = 0; i < element_info[element].num_change_pages; i++)
4126 /* check for player created from custom element as extended target */
4128 element_info[element].change_page[i].target_content.e[xx][yy];
4130 is_player = ELEM_IS_PLAYER(content);
4132 if (is_player && (found_rating < 1 ||
4133 (found_rating == 1 && element < found_element)))
4135 start_x = x + xx - 1;
4136 start_y = y + yy - 1;
4139 found_element = element;
4145 scroll_x = SCROLL_POSITION_X(start_x);
4146 scroll_y = SCROLL_POSITION_Y(start_y);
4150 scroll_x = SCROLL_POSITION_X(local_player->jx);
4151 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4154 /* !!! FIX THIS (START) !!! */
4155 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4157 InitGameEngine_EM();
4159 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4161 InitGameEngine_SP();
4163 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4165 InitGameEngine_MM();
4169 DrawLevel(REDRAW_FIELD);
4172 /* after drawing the level, correct some elements */
4173 if (game.timegate_time_left == 0)
4174 CloseAllOpenTimegates();
4177 /* blit playfield from scroll buffer to normal back buffer for fading in */
4178 BlitScreenToBitmap(backbuffer);
4179 /* !!! FIX THIS (END) !!! */
4181 DrawMaskedBorder(fade_mask);
4186 // full screen redraw is required at this point in the following cases:
4187 // - special editor door undrawn when game was started from level editor
4188 // - drawing area (playfield) was changed and has to be removed completely
4189 redraw_mask = REDRAW_ALL;
4193 if (!game.restart_level)
4195 /* copy default game door content to main double buffer */
4197 /* !!! CHECK AGAIN !!! */
4198 SetPanelBackground();
4199 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4200 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4203 SetPanelBackground();
4204 SetDrawBackgroundMask(REDRAW_DOOR_1);
4206 UpdateAndDisplayGameControlValues();
4208 if (!game.restart_level)
4214 CreateGameButtons();
4219 /* copy actual game door content to door double buffer for OpenDoor() */
4220 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4222 OpenDoor(DOOR_OPEN_ALL);
4224 KeyboardAutoRepeatOffUnlessAutoplay();
4226 #if DEBUG_INIT_PLAYER
4229 printf("Player status (final):\n");
4231 for (i = 0; i < MAX_PLAYERS; i++)
4233 struct PlayerInfo *player = &stored_player[i];
4235 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4239 player->connected_locally,
4240 player->connected_network,
4243 if (local_player == player)
4244 printf(" (local player)");
4257 if (!game.restart_level && !tape.playing)
4259 LevelStats_incPlayed(level_nr);
4261 SaveLevelSetup_SeriesInfo();
4264 game.restart_level = FALSE;
4265 game.restart_game_message = NULL;
4267 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4268 InitGameActions_MM();
4270 SaveEngineSnapshotToListInitial();
4272 if (!game.restart_level)
4274 PlaySound(SND_GAME_STARTING);
4276 if (setup.sound_music)
4281 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4282 int actual_player_x, int actual_player_y)
4284 /* this is used for non-R'n'D game engines to update certain engine values */
4286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4288 actual_player_x = correctLevelPosX_EM(actual_player_x);
4289 actual_player_y = correctLevelPosY_EM(actual_player_y);
4292 /* needed to determine if sounds are played within the visible screen area */
4293 scroll_x = actual_scroll_x;
4294 scroll_y = actual_scroll_y;
4296 /* needed to get player position for "follow finger" playing input method */
4297 local_player->jx = actual_player_x;
4298 local_player->jy = actual_player_y;
4301 void InitMovDir(int x, int y)
4303 int i, element = Feld[x][y];
4304 static int xy[4][2] =
4311 static int direction[3][4] =
4313 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4314 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4315 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4324 Feld[x][y] = EL_BUG;
4325 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4328 case EL_SPACESHIP_RIGHT:
4329 case EL_SPACESHIP_UP:
4330 case EL_SPACESHIP_LEFT:
4331 case EL_SPACESHIP_DOWN:
4332 Feld[x][y] = EL_SPACESHIP;
4333 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4336 case EL_BD_BUTTERFLY_RIGHT:
4337 case EL_BD_BUTTERFLY_UP:
4338 case EL_BD_BUTTERFLY_LEFT:
4339 case EL_BD_BUTTERFLY_DOWN:
4340 Feld[x][y] = EL_BD_BUTTERFLY;
4341 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4344 case EL_BD_FIREFLY_RIGHT:
4345 case EL_BD_FIREFLY_UP:
4346 case EL_BD_FIREFLY_LEFT:
4347 case EL_BD_FIREFLY_DOWN:
4348 Feld[x][y] = EL_BD_FIREFLY;
4349 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4352 case EL_PACMAN_RIGHT:
4354 case EL_PACMAN_LEFT:
4355 case EL_PACMAN_DOWN:
4356 Feld[x][y] = EL_PACMAN;
4357 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4360 case EL_YAMYAM_LEFT:
4361 case EL_YAMYAM_RIGHT:
4363 case EL_YAMYAM_DOWN:
4364 Feld[x][y] = EL_YAMYAM;
4365 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4368 case EL_SP_SNIKSNAK:
4369 MovDir[x][y] = MV_UP;
4372 case EL_SP_ELECTRON:
4373 MovDir[x][y] = MV_LEFT;
4380 Feld[x][y] = EL_MOLE;
4381 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4385 if (IS_CUSTOM_ELEMENT(element))
4387 struct ElementInfo *ei = &element_info[element];
4388 int move_direction_initial = ei->move_direction_initial;
4389 int move_pattern = ei->move_pattern;
4391 if (move_direction_initial == MV_START_PREVIOUS)
4393 if (MovDir[x][y] != MV_NONE)
4396 move_direction_initial = MV_START_AUTOMATIC;
4399 if (move_direction_initial == MV_START_RANDOM)
4400 MovDir[x][y] = 1 << RND(4);
4401 else if (move_direction_initial & MV_ANY_DIRECTION)
4402 MovDir[x][y] = move_direction_initial;
4403 else if (move_pattern == MV_ALL_DIRECTIONS ||
4404 move_pattern == MV_TURNING_LEFT ||
4405 move_pattern == MV_TURNING_RIGHT ||
4406 move_pattern == MV_TURNING_LEFT_RIGHT ||
4407 move_pattern == MV_TURNING_RIGHT_LEFT ||
4408 move_pattern == MV_TURNING_RANDOM)
4409 MovDir[x][y] = 1 << RND(4);
4410 else if (move_pattern == MV_HORIZONTAL)
4411 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4412 else if (move_pattern == MV_VERTICAL)
4413 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4414 else if (move_pattern & MV_ANY_DIRECTION)
4415 MovDir[x][y] = element_info[element].move_pattern;
4416 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4417 move_pattern == MV_ALONG_RIGHT_SIDE)
4419 /* use random direction as default start direction */
4420 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4421 MovDir[x][y] = 1 << RND(4);
4423 for (i = 0; i < NUM_DIRECTIONS; i++)
4425 int x1 = x + xy[i][0];
4426 int y1 = y + xy[i][1];
4428 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4430 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4431 MovDir[x][y] = direction[0][i];
4433 MovDir[x][y] = direction[1][i];
4442 MovDir[x][y] = 1 << RND(4);
4444 if (element != EL_BUG &&
4445 element != EL_SPACESHIP &&
4446 element != EL_BD_BUTTERFLY &&
4447 element != EL_BD_FIREFLY)
4450 for (i = 0; i < NUM_DIRECTIONS; i++)
4452 int x1 = x + xy[i][0];
4453 int y1 = y + xy[i][1];
4455 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4457 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4459 MovDir[x][y] = direction[0][i];
4462 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4463 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4465 MovDir[x][y] = direction[1][i];
4474 GfxDir[x][y] = MovDir[x][y];
4477 void InitAmoebaNr(int x, int y)
4480 int group_nr = AmoebeNachbarNr(x, y);
4484 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4486 if (AmoebaCnt[i] == 0)
4494 AmoebaNr[x][y] = group_nr;
4495 AmoebaCnt[group_nr]++;
4496 AmoebaCnt2[group_nr]++;
4499 static void PlayerWins(struct PlayerInfo *player)
4501 player->LevelSolved = TRUE;
4502 player->GameOver = TRUE;
4504 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4505 level.native_em_level->lev->score :
4506 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4509 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4510 MM_HEALTH(game_mm.laser_overload_value) :
4513 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4515 player->LevelSolved_CountingScore = player->score_final;
4516 player->LevelSolved_CountingHealth = player->health_final;
4521 static int time_count_steps;
4522 static int time, time_final;
4523 static int score, score_final;
4524 static int health, health_final;
4525 static int game_over_delay_1 = 0;
4526 static int game_over_delay_2 = 0;
4527 static int game_over_delay_3 = 0;
4528 int game_over_delay_value_1 = 50;
4529 int game_over_delay_value_2 = 25;
4530 int game_over_delay_value_3 = 50;
4532 if (!local_player->LevelSolved_GameWon)
4536 /* do not start end game actions before the player stops moving (to exit) */
4537 if (local_player->MovPos)
4540 local_player->LevelSolved_GameWon = TRUE;
4541 local_player->LevelSolved_SaveTape = tape.recording;
4542 local_player->LevelSolved_SaveScore = !tape.playing;
4546 LevelStats_incSolved(level_nr);
4548 SaveLevelSetup_SeriesInfo();
4551 if (tape.auto_play) /* tape might already be stopped here */
4552 tape.auto_play_level_solved = TRUE;
4556 game_over_delay_1 = 0;
4557 game_over_delay_2 = 0;
4558 game_over_delay_3 = game_over_delay_value_3;
4560 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4561 score = score_final = local_player->score_final;
4562 health = health_final = local_player->health_final;
4564 if (level.score[SC_TIME_BONUS] > 0)
4569 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4571 else if (game.no_time_limit && TimePlayed < 999)
4574 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4577 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4579 game_over_delay_1 = game_over_delay_value_1;
4581 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4584 score_final += health * level.score[SC_TIME_BONUS];
4586 game_over_delay_2 = game_over_delay_value_2;
4589 local_player->score_final = score_final;
4590 local_player->health_final = health_final;
4593 if (level_editor_test_game)
4596 score = score_final;
4598 local_player->LevelSolved_CountingTime = time;
4599 local_player->LevelSolved_CountingScore = score;
4601 game_panel_controls[GAME_PANEL_TIME].value = time;
4602 game_panel_controls[GAME_PANEL_SCORE].value = score;
4604 DisplayGameControlValues();
4607 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4609 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4611 /* close exit door after last player */
4612 if ((AllPlayersGone &&
4613 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4614 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4615 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4616 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4617 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4619 int element = Feld[ExitX][ExitY];
4621 Feld[ExitX][ExitY] =
4622 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4623 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4624 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4625 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4626 EL_EM_STEEL_EXIT_CLOSING);
4628 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4631 /* player disappears */
4632 DrawLevelField(ExitX, ExitY);
4635 for (i = 0; i < MAX_PLAYERS; i++)
4637 struct PlayerInfo *player = &stored_player[i];
4639 if (player->present)
4641 RemovePlayer(player);
4643 /* player disappears */
4644 DrawLevelField(player->jx, player->jy);
4649 PlaySound(SND_GAME_WINNING);
4652 if (game_over_delay_1 > 0)
4654 game_over_delay_1--;
4659 if (time != time_final)
4661 int time_to_go = ABS(time_final - time);
4662 int time_count_dir = (time < time_final ? +1 : -1);
4664 if (time_to_go < time_count_steps)
4665 time_count_steps = 1;
4667 time += time_count_steps * time_count_dir;
4668 score += time_count_steps * level.score[SC_TIME_BONUS];
4670 local_player->LevelSolved_CountingTime = time;
4671 local_player->LevelSolved_CountingScore = score;
4673 game_panel_controls[GAME_PANEL_TIME].value = time;
4674 game_panel_controls[GAME_PANEL_SCORE].value = score;
4676 DisplayGameControlValues();
4678 if (time == time_final)
4679 StopSound(SND_GAME_LEVELTIME_BONUS);
4680 else if (setup.sound_loops)
4681 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4683 PlaySound(SND_GAME_LEVELTIME_BONUS);
4688 if (game_over_delay_2 > 0)
4690 game_over_delay_2--;
4695 if (health != health_final)
4697 int health_count_dir = (health < health_final ? +1 : -1);
4699 health += health_count_dir;
4700 score += level.score[SC_TIME_BONUS];
4702 local_player->LevelSolved_CountingHealth = health;
4703 local_player->LevelSolved_CountingScore = score;
4705 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4706 game_panel_controls[GAME_PANEL_SCORE].value = score;
4708 DisplayGameControlValues();
4710 if (health == health_final)
4711 StopSound(SND_GAME_LEVELTIME_BONUS);
4712 else if (setup.sound_loops)
4713 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4715 PlaySound(SND_GAME_LEVELTIME_BONUS);
4720 local_player->LevelSolved_PanelOff = TRUE;
4722 if (game_over_delay_3 > 0)
4724 game_over_delay_3--;
4735 boolean raise_level = FALSE;
4737 local_player->LevelSolved_GameEnd = TRUE;
4739 if (local_player->LevelSolved_SaveTape)
4741 /* make sure that request dialog to save tape does not open door again */
4742 if (!global.use_envelope_request)
4743 CloseDoor(DOOR_CLOSE_1);
4745 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4748 /* if no tape is to be saved, close both doors simultaneously */
4749 CloseDoor(DOOR_CLOSE_ALL);
4751 if (level_editor_test_game)
4753 SetGameStatus(GAME_MODE_MAIN);
4760 if (!local_player->LevelSolved_SaveScore)
4762 SetGameStatus(GAME_MODE_MAIN);
4769 if (level_nr == leveldir_current->handicap_level)
4771 leveldir_current->handicap_level++;
4773 SaveLevelSetup_SeriesInfo();
4776 if (setup.increment_levels &&
4777 level_nr < leveldir_current->last_level)
4778 raise_level = TRUE; /* advance to next level */
4780 if ((hi_pos = NewHiScore()) >= 0)
4782 SetGameStatus(GAME_MODE_SCORES);
4784 DrawHallOfFame(hi_pos);
4794 SetGameStatus(GAME_MODE_MAIN);
4810 boolean one_score_entry_per_name = !program.many_scores_per_name;
4812 LoadScore(level_nr);
4814 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4815 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4818 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4820 if (local_player->score_final > highscore[k].Score)
4822 /* player has made it to the hall of fame */
4824 if (k < MAX_SCORE_ENTRIES - 1)
4826 int m = MAX_SCORE_ENTRIES - 1;
4828 if (one_score_entry_per_name)
4830 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4831 if (strEqual(setup.player_name, highscore[l].Name))
4834 if (m == k) /* player's new highscore overwrites his old one */
4838 for (l = m; l > k; l--)
4840 strcpy(highscore[l].Name, highscore[l - 1].Name);
4841 highscore[l].Score = highscore[l - 1].Score;
4847 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4848 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4849 highscore[k].Score = local_player->score_final;
4854 else if (one_score_entry_per_name &&
4855 !strncmp(setup.player_name, highscore[k].Name,
4856 MAX_PLAYER_NAME_LEN))
4857 break; /* player already there with a higher score */
4861 SaveScore(level_nr);
4866 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4868 int element = Feld[x][y];
4869 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4870 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4871 int horiz_move = (dx != 0);
4872 int sign = (horiz_move ? dx : dy);
4873 int step = sign * element_info[element].move_stepsize;
4875 /* special values for move stepsize for spring and things on conveyor belt */
4878 if (CAN_FALL(element) &&
4879 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4880 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4881 else if (element == EL_SPRING)
4882 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4888 inline static int getElementMoveStepsize(int x, int y)
4890 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4893 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4895 if (player->GfxAction != action || player->GfxDir != dir)
4897 player->GfxAction = action;
4898 player->GfxDir = dir;
4900 player->StepFrame = 0;
4904 static void ResetGfxFrame(int x, int y)
4906 // profiling showed that "autotest" spends 10~20% of its time in this function
4907 if (DrawingDeactivatedField())
4910 int element = Feld[x][y];
4911 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4913 if (graphic_info[graphic].anim_global_sync)
4914 GfxFrame[x][y] = FrameCounter;
4915 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4916 GfxFrame[x][y] = CustomValue[x][y];
4917 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4918 GfxFrame[x][y] = element_info[element].collect_score;
4919 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4920 GfxFrame[x][y] = ChangeDelay[x][y];
4923 static void ResetGfxAnimation(int x, int y)
4925 GfxAction[x][y] = ACTION_DEFAULT;
4926 GfxDir[x][y] = MovDir[x][y];
4929 ResetGfxFrame(x, y);
4932 static void ResetRandomAnimationValue(int x, int y)
4934 GfxRandom[x][y] = INIT_GFX_RANDOM();
4937 void InitMovingField(int x, int y, int direction)
4939 int element = Feld[x][y];
4940 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4941 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4944 boolean is_moving_before, is_moving_after;
4946 /* check if element was/is moving or being moved before/after mode change */
4947 is_moving_before = (WasJustMoving[x][y] != 0);
4948 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4950 /* reset animation only for moving elements which change direction of moving
4951 or which just started or stopped moving
4952 (else CEs with property "can move" / "not moving" are reset each frame) */
4953 if (is_moving_before != is_moving_after ||
4954 direction != MovDir[x][y])
4955 ResetGfxAnimation(x, y);
4957 MovDir[x][y] = direction;
4958 GfxDir[x][y] = direction;
4960 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4961 direction == MV_DOWN && CAN_FALL(element) ?
4962 ACTION_FALLING : ACTION_MOVING);
4964 /* this is needed for CEs with property "can move" / "not moving" */
4966 if (is_moving_after)
4968 if (Feld[newx][newy] == EL_EMPTY)
4969 Feld[newx][newy] = EL_BLOCKED;
4971 MovDir[newx][newy] = MovDir[x][y];
4973 CustomValue[newx][newy] = CustomValue[x][y];
4975 GfxFrame[newx][newy] = GfxFrame[x][y];
4976 GfxRandom[newx][newy] = GfxRandom[x][y];
4977 GfxAction[newx][newy] = GfxAction[x][y];
4978 GfxDir[newx][newy] = GfxDir[x][y];
4982 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4984 int direction = MovDir[x][y];
4985 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4986 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4992 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4994 int oldx = x, oldy = y;
4995 int direction = MovDir[x][y];
4997 if (direction == MV_LEFT)
4999 else if (direction == MV_RIGHT)
5001 else if (direction == MV_UP)
5003 else if (direction == MV_DOWN)
5006 *comes_from_x = oldx;
5007 *comes_from_y = oldy;
5010 int MovingOrBlocked2Element(int x, int y)
5012 int element = Feld[x][y];
5014 if (element == EL_BLOCKED)
5018 Blocked2Moving(x, y, &oldx, &oldy);
5019 return Feld[oldx][oldy];
5025 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5027 /* like MovingOrBlocked2Element(), but if element is moving
5028 and (x,y) is the field the moving element is just leaving,
5029 return EL_BLOCKED instead of the element value */
5030 int element = Feld[x][y];
5032 if (IS_MOVING(x, y))
5034 if (element == EL_BLOCKED)
5038 Blocked2Moving(x, y, &oldx, &oldy);
5039 return Feld[oldx][oldy];
5048 static void RemoveField(int x, int y)
5050 Feld[x][y] = EL_EMPTY;
5056 CustomValue[x][y] = 0;
5059 ChangeDelay[x][y] = 0;
5060 ChangePage[x][y] = -1;
5061 Pushed[x][y] = FALSE;
5063 GfxElement[x][y] = EL_UNDEFINED;
5064 GfxAction[x][y] = ACTION_DEFAULT;
5065 GfxDir[x][y] = MV_NONE;
5068 void RemoveMovingField(int x, int y)
5070 int oldx = x, oldy = y, newx = x, newy = y;
5071 int element = Feld[x][y];
5072 int next_element = EL_UNDEFINED;
5074 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5077 if (IS_MOVING(x, y))
5079 Moving2Blocked(x, y, &newx, &newy);
5081 if (Feld[newx][newy] != EL_BLOCKED)
5083 /* element is moving, but target field is not free (blocked), but
5084 already occupied by something different (example: acid pool);
5085 in this case, only remove the moving field, but not the target */
5087 RemoveField(oldx, oldy);
5089 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5091 TEST_DrawLevelField(oldx, oldy);
5096 else if (element == EL_BLOCKED)
5098 Blocked2Moving(x, y, &oldx, &oldy);
5099 if (!IS_MOVING(oldx, oldy))
5103 if (element == EL_BLOCKED &&
5104 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5105 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5106 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5107 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5108 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5109 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5110 next_element = get_next_element(Feld[oldx][oldy]);
5112 RemoveField(oldx, oldy);
5113 RemoveField(newx, newy);
5115 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5117 if (next_element != EL_UNDEFINED)
5118 Feld[oldx][oldy] = next_element;
5120 TEST_DrawLevelField(oldx, oldy);
5121 TEST_DrawLevelField(newx, newy);
5124 void DrawDynamite(int x, int y)
5126 int sx = SCREENX(x), sy = SCREENY(y);
5127 int graphic = el2img(Feld[x][y]);
5130 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5133 if (IS_WALKABLE_INSIDE(Back[x][y]))
5137 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5138 else if (Store[x][y])
5139 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5141 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5143 if (Back[x][y] || Store[x][y])
5144 DrawGraphicThruMask(sx, sy, graphic, frame);
5146 DrawGraphic(sx, sy, graphic, frame);
5149 void CheckDynamite(int x, int y)
5151 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5155 if (MovDelay[x][y] != 0)
5158 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5164 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5169 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5171 boolean num_checked_players = 0;
5174 for (i = 0; i < MAX_PLAYERS; i++)
5176 if (stored_player[i].active)
5178 int sx = stored_player[i].jx;
5179 int sy = stored_player[i].jy;
5181 if (num_checked_players == 0)
5188 *sx1 = MIN(*sx1, sx);
5189 *sy1 = MIN(*sy1, sy);
5190 *sx2 = MAX(*sx2, sx);
5191 *sy2 = MAX(*sy2, sy);
5194 num_checked_players++;
5199 static boolean checkIfAllPlayersFitToScreen_RND()
5201 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5203 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5205 return (sx2 - sx1 < SCR_FIELDX &&
5206 sy2 - sy1 < SCR_FIELDY);
5209 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5211 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5213 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5215 *sx = (sx1 + sx2) / 2;
5216 *sy = (sy1 + sy2) / 2;
5219 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5220 boolean center_screen, boolean quick_relocation)
5222 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5223 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5224 boolean no_delay = (tape.warp_forward);
5225 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5226 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5227 int new_scroll_x, new_scroll_y;
5229 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5231 /* case 1: quick relocation inside visible screen (without scrolling) */
5238 if (!level.shifted_relocation || center_screen)
5240 /* relocation _with_ centering of screen */
5242 new_scroll_x = SCROLL_POSITION_X(x);
5243 new_scroll_y = SCROLL_POSITION_Y(y);
5247 /* relocation _without_ centering of screen */
5249 int center_scroll_x = SCROLL_POSITION_X(old_x);
5250 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5251 int offset_x = x + (scroll_x - center_scroll_x);
5252 int offset_y = y + (scroll_y - center_scroll_y);
5254 /* for new screen position, apply previous offset to center position */
5255 new_scroll_x = SCROLL_POSITION_X(offset_x);
5256 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5259 if (quick_relocation)
5261 /* case 2: quick relocation (redraw without visible scrolling) */
5263 scroll_x = new_scroll_x;
5264 scroll_y = new_scroll_y;
5271 /* case 3: visible relocation (with scrolling to new position) */
5273 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5275 SetVideoFrameDelay(wait_delay_value);
5277 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5280 int fx = FX, fy = FY;
5282 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5283 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5285 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5291 fx += dx * TILEX / 2;
5292 fy += dy * TILEY / 2;
5294 ScrollLevel(dx, dy);
5297 /* scroll in two steps of half tile size to make things smoother */
5298 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5300 /* scroll second step to align at full tile size */
5301 BlitScreenToBitmap(window);
5307 SetVideoFrameDelay(frame_delay_value_old);
5310 void RelocatePlayer(int jx, int jy, int el_player_raw)
5312 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5313 int player_nr = GET_PLAYER_NR(el_player);
5314 struct PlayerInfo *player = &stored_player[player_nr];
5315 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5316 boolean no_delay = (tape.warp_forward);
5317 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5318 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5319 int old_jx = player->jx;
5320 int old_jy = player->jy;
5321 int old_element = Feld[old_jx][old_jy];
5322 int element = Feld[jx][jy];
5323 boolean player_relocated = (old_jx != jx || old_jy != jy);
5325 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5326 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5327 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5328 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5329 int leave_side_horiz = move_dir_horiz;
5330 int leave_side_vert = move_dir_vert;
5331 int enter_side = enter_side_horiz | enter_side_vert;
5332 int leave_side = leave_side_horiz | leave_side_vert;
5334 if (player->GameOver) /* do not reanimate dead player */
5337 if (!player_relocated) /* no need to relocate the player */
5340 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5342 RemoveField(jx, jy); /* temporarily remove newly placed player */
5343 DrawLevelField(jx, jy);
5346 if (player->present)
5348 while (player->MovPos)
5350 ScrollPlayer(player, SCROLL_GO_ON);
5351 ScrollScreen(NULL, SCROLL_GO_ON);
5353 AdvanceFrameAndPlayerCounters(player->index_nr);
5357 BackToFront_WithFrameDelay(wait_delay_value);
5360 DrawPlayer(player); /* needed here only to cleanup last field */
5361 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5363 player->is_moving = FALSE;
5366 if (IS_CUSTOM_ELEMENT(old_element))
5367 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5369 player->index_bit, leave_side);
5371 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5373 player->index_bit, leave_side);
5375 Feld[jx][jy] = el_player;
5376 InitPlayerField(jx, jy, el_player, TRUE);
5378 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5379 possible that the relocation target field did not contain a player element,
5380 but a walkable element, to which the new player was relocated -- in this
5381 case, restore that (already initialized!) element on the player field */
5382 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5384 Feld[jx][jy] = element; /* restore previously existing element */
5387 /* only visually relocate centered player */
5388 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5389 FALSE, level.instant_relocation);
5391 TestIfPlayerTouchesBadThing(jx, jy);
5392 TestIfPlayerTouchesCustomElement(jx, jy);
5394 if (IS_CUSTOM_ELEMENT(element))
5395 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5396 player->index_bit, enter_side);
5398 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5399 player->index_bit, enter_side);
5401 if (player->is_switching)
5403 /* ensure that relocation while still switching an element does not cause
5404 a new element to be treated as also switched directly after relocation
5405 (this is important for teleporter switches that teleport the player to
5406 a place where another teleporter switch is in the same direction, which
5407 would then incorrectly be treated as immediately switched before the
5408 direction key that caused the switch was released) */
5410 player->switch_x += jx - old_jx;
5411 player->switch_y += jy - old_jy;
5415 void Explode(int ex, int ey, int phase, int mode)
5421 /* !!! eliminate this variable !!! */
5422 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5424 if (game.explosions_delayed)
5426 ExplodeField[ex][ey] = mode;
5430 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5432 int center_element = Feld[ex][ey];
5433 int artwork_element, explosion_element; /* set these values later */
5435 /* remove things displayed in background while burning dynamite */
5436 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5439 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5441 /* put moving element to center field (and let it explode there) */
5442 center_element = MovingOrBlocked2Element(ex, ey);
5443 RemoveMovingField(ex, ey);
5444 Feld[ex][ey] = center_element;
5447 /* now "center_element" is finally determined -- set related values now */
5448 artwork_element = center_element; /* for custom player artwork */
5449 explosion_element = center_element; /* for custom player artwork */
5451 if (IS_PLAYER(ex, ey))
5453 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5455 artwork_element = stored_player[player_nr].artwork_element;
5457 if (level.use_explosion_element[player_nr])
5459 explosion_element = level.explosion_element[player_nr];
5460 artwork_element = explosion_element;
5464 if (mode == EX_TYPE_NORMAL ||
5465 mode == EX_TYPE_CENTER ||
5466 mode == EX_TYPE_CROSS)
5467 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5469 last_phase = element_info[explosion_element].explosion_delay + 1;
5471 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5473 int xx = x - ex + 1;
5474 int yy = y - ey + 1;
5477 if (!IN_LEV_FIELD(x, y) ||
5478 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5479 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5482 element = Feld[x][y];
5484 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5486 element = MovingOrBlocked2Element(x, y);
5488 if (!IS_EXPLOSION_PROOF(element))
5489 RemoveMovingField(x, y);
5492 /* indestructible elements can only explode in center (but not flames) */
5493 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5494 mode == EX_TYPE_BORDER)) ||
5495 element == EL_FLAMES)
5498 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5499 behaviour, for example when touching a yamyam that explodes to rocks
5500 with active deadly shield, a rock is created under the player !!! */
5501 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5503 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5504 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5505 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5507 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5510 if (IS_ACTIVE_BOMB(element))
5512 /* re-activate things under the bomb like gate or penguin */
5513 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5520 /* save walkable background elements while explosion on same tile */
5521 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5522 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5523 Back[x][y] = element;
5525 /* ignite explodable elements reached by other explosion */
5526 if (element == EL_EXPLOSION)
5527 element = Store2[x][y];
5529 if (AmoebaNr[x][y] &&
5530 (element == EL_AMOEBA_FULL ||
5531 element == EL_BD_AMOEBA ||
5532 element == EL_AMOEBA_GROWING))
5534 AmoebaCnt[AmoebaNr[x][y]]--;
5535 AmoebaCnt2[AmoebaNr[x][y]]--;
5540 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5542 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5544 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5546 if (PLAYERINFO(ex, ey)->use_murphy)
5547 Store[x][y] = EL_EMPTY;
5550 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5551 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5552 else if (ELEM_IS_PLAYER(center_element))
5553 Store[x][y] = EL_EMPTY;
5554 else if (center_element == EL_YAMYAM)
5555 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5556 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5557 Store[x][y] = element_info[center_element].content.e[xx][yy];
5559 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5560 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5561 otherwise) -- FIX THIS !!! */
5562 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5563 Store[x][y] = element_info[element].content.e[1][1];
5565 else if (!CAN_EXPLODE(element))
5566 Store[x][y] = element_info[element].content.e[1][1];
5569 Store[x][y] = EL_EMPTY;
5571 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5572 center_element == EL_AMOEBA_TO_DIAMOND)
5573 Store2[x][y] = element;
5575 Feld[x][y] = EL_EXPLOSION;
5576 GfxElement[x][y] = artwork_element;
5578 ExplodePhase[x][y] = 1;
5579 ExplodeDelay[x][y] = last_phase;
5584 if (center_element == EL_YAMYAM)
5585 game.yamyam_content_nr =
5586 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5598 GfxFrame[x][y] = 0; /* restart explosion animation */
5600 last_phase = ExplodeDelay[x][y];
5602 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5604 /* this can happen if the player leaves an explosion just in time */
5605 if (GfxElement[x][y] == EL_UNDEFINED)
5606 GfxElement[x][y] = EL_EMPTY;
5608 border_element = Store2[x][y];
5609 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5610 border_element = StorePlayer[x][y];
5612 if (phase == element_info[border_element].ignition_delay ||
5613 phase == last_phase)
5615 boolean border_explosion = FALSE;
5617 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5618 !PLAYER_EXPLOSION_PROTECTED(x, y))
5620 KillPlayerUnlessExplosionProtected(x, y);
5621 border_explosion = TRUE;
5623 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5625 Feld[x][y] = Store2[x][y];
5628 border_explosion = TRUE;
5630 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5632 AmoebeUmwandeln(x, y);
5634 border_explosion = TRUE;
5637 /* if an element just explodes due to another explosion (chain-reaction),
5638 do not immediately end the new explosion when it was the last frame of
5639 the explosion (as it would be done in the following "if"-statement!) */
5640 if (border_explosion && phase == last_phase)
5644 if (phase == last_phase)
5648 element = Feld[x][y] = Store[x][y];
5649 Store[x][y] = Store2[x][y] = 0;
5650 GfxElement[x][y] = EL_UNDEFINED;
5652 /* player can escape from explosions and might therefore be still alive */
5653 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5654 element <= EL_PLAYER_IS_EXPLODING_4)
5656 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5657 int explosion_element = EL_PLAYER_1 + player_nr;
5658 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5659 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5661 if (level.use_explosion_element[player_nr])
5662 explosion_element = level.explosion_element[player_nr];
5664 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5665 element_info[explosion_element].content.e[xx][yy]);
5668 /* restore probably existing indestructible background element */
5669 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5670 element = Feld[x][y] = Back[x][y];
5673 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5674 GfxDir[x][y] = MV_NONE;
5675 ChangeDelay[x][y] = 0;
5676 ChangePage[x][y] = -1;
5678 CustomValue[x][y] = 0;
5680 InitField_WithBug2(x, y, FALSE);
5682 TEST_DrawLevelField(x, y);
5684 TestIfElementTouchesCustomElement(x, y);
5686 if (GFX_CRUMBLED(element))
5687 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5689 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5690 StorePlayer[x][y] = 0;
5692 if (ELEM_IS_PLAYER(element))
5693 RelocatePlayer(x, y, element);
5695 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5697 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5698 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5701 TEST_DrawLevelFieldCrumbled(x, y);
5703 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5705 DrawLevelElement(x, y, Back[x][y]);
5706 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5708 else if (IS_WALKABLE_UNDER(Back[x][y]))
5710 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5711 DrawLevelElementThruMask(x, y, Back[x][y]);
5713 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5714 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5718 void DynaExplode(int ex, int ey)
5721 int dynabomb_element = Feld[ex][ey];
5722 int dynabomb_size = 1;
5723 boolean dynabomb_xl = FALSE;
5724 struct PlayerInfo *player;
5725 static int xy[4][2] =
5733 if (IS_ACTIVE_BOMB(dynabomb_element))
5735 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5736 dynabomb_size = player->dynabomb_size;
5737 dynabomb_xl = player->dynabomb_xl;
5738 player->dynabombs_left++;
5741 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5743 for (i = 0; i < NUM_DIRECTIONS; i++)
5745 for (j = 1; j <= dynabomb_size; j++)
5747 int x = ex + j * xy[i][0];
5748 int y = ey + j * xy[i][1];
5751 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5754 element = Feld[x][y];
5756 /* do not restart explosions of fields with active bombs */
5757 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5760 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5762 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5763 !IS_DIGGABLE(element) && !dynabomb_xl)
5769 void Bang(int x, int y)
5771 int element = MovingOrBlocked2Element(x, y);
5772 int explosion_type = EX_TYPE_NORMAL;
5774 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5776 struct PlayerInfo *player = PLAYERINFO(x, y);
5778 element = Feld[x][y] = player->initial_element;
5780 if (level.use_explosion_element[player->index_nr])
5782 int explosion_element = level.explosion_element[player->index_nr];
5784 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5785 explosion_type = EX_TYPE_CROSS;
5786 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5787 explosion_type = EX_TYPE_CENTER;
5795 case EL_BD_BUTTERFLY:
5798 case EL_DARK_YAMYAM:
5802 RaiseScoreElement(element);
5805 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5806 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5807 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5808 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5809 case EL_DYNABOMB_INCREASE_NUMBER:
5810 case EL_DYNABOMB_INCREASE_SIZE:
5811 case EL_DYNABOMB_INCREASE_POWER:
5812 explosion_type = EX_TYPE_DYNA;
5815 case EL_DC_LANDMINE:
5816 explosion_type = EX_TYPE_CENTER;
5821 case EL_LAMP_ACTIVE:
5822 case EL_AMOEBA_TO_DIAMOND:
5823 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5824 explosion_type = EX_TYPE_CENTER;
5828 if (element_info[element].explosion_type == EXPLODES_CROSS)
5829 explosion_type = EX_TYPE_CROSS;
5830 else if (element_info[element].explosion_type == EXPLODES_1X1)
5831 explosion_type = EX_TYPE_CENTER;
5835 if (explosion_type == EX_TYPE_DYNA)
5838 Explode(x, y, EX_PHASE_START, explosion_type);
5840 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5843 void SplashAcid(int x, int y)
5845 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5846 (!IN_LEV_FIELD(x - 1, y - 2) ||
5847 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5848 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5850 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5851 (!IN_LEV_FIELD(x + 1, y - 2) ||
5852 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5853 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5855 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5858 static void InitBeltMovement()
5860 static int belt_base_element[4] =
5862 EL_CONVEYOR_BELT_1_LEFT,
5863 EL_CONVEYOR_BELT_2_LEFT,
5864 EL_CONVEYOR_BELT_3_LEFT,
5865 EL_CONVEYOR_BELT_4_LEFT
5867 static int belt_base_active_element[4] =
5869 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5870 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5871 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5872 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5877 /* set frame order for belt animation graphic according to belt direction */
5878 for (i = 0; i < NUM_BELTS; i++)
5882 for (j = 0; j < NUM_BELT_PARTS; j++)
5884 int element = belt_base_active_element[belt_nr] + j;
5885 int graphic_1 = el2img(element);
5886 int graphic_2 = el2panelimg(element);
5888 if (game.belt_dir[i] == MV_LEFT)
5890 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5891 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5895 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5896 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5901 SCAN_PLAYFIELD(x, y)
5903 int element = Feld[x][y];
5905 for (i = 0; i < NUM_BELTS; i++)
5907 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5909 int e_belt_nr = getBeltNrFromBeltElement(element);
5912 if (e_belt_nr == belt_nr)
5914 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5916 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5923 static void ToggleBeltSwitch(int x, int y)
5925 static int belt_base_element[4] =
5927 EL_CONVEYOR_BELT_1_LEFT,
5928 EL_CONVEYOR_BELT_2_LEFT,
5929 EL_CONVEYOR_BELT_3_LEFT,
5930 EL_CONVEYOR_BELT_4_LEFT
5932 static int belt_base_active_element[4] =
5934 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5935 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5936 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5937 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5939 static int belt_base_switch_element[4] =
5941 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5942 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5943 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5944 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5946 static int belt_move_dir[4] =
5954 int element = Feld[x][y];
5955 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5956 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5957 int belt_dir = belt_move_dir[belt_dir_nr];
5960 if (!IS_BELT_SWITCH(element))
5963 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5964 game.belt_dir[belt_nr] = belt_dir;
5966 if (belt_dir_nr == 3)
5969 /* set frame order for belt animation graphic according to belt direction */
5970 for (i = 0; i < NUM_BELT_PARTS; i++)
5972 int element = belt_base_active_element[belt_nr] + i;
5973 int graphic_1 = el2img(element);
5974 int graphic_2 = el2panelimg(element);
5976 if (belt_dir == MV_LEFT)
5978 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5979 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5983 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5984 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5988 SCAN_PLAYFIELD(xx, yy)
5990 int element = Feld[xx][yy];
5992 if (IS_BELT_SWITCH(element))
5994 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5996 if (e_belt_nr == belt_nr)
5998 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5999 TEST_DrawLevelField(xx, yy);
6002 else if (IS_BELT(element) && belt_dir != MV_NONE)
6004 int e_belt_nr = getBeltNrFromBeltElement(element);
6006 if (e_belt_nr == belt_nr)
6008 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6010 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6011 TEST_DrawLevelField(xx, yy);
6014 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6016 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6018 if (e_belt_nr == belt_nr)
6020 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6022 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6023 TEST_DrawLevelField(xx, yy);
6029 static void ToggleSwitchgateSwitch(int x, int y)
6033 game.switchgate_pos = !game.switchgate_pos;
6035 SCAN_PLAYFIELD(xx, yy)
6037 int element = Feld[xx][yy];
6039 if (element == EL_SWITCHGATE_SWITCH_UP)
6041 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6042 TEST_DrawLevelField(xx, yy);
6044 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6046 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6047 TEST_DrawLevelField(xx, yy);
6049 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6051 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6052 TEST_DrawLevelField(xx, yy);
6054 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6056 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6057 TEST_DrawLevelField(xx, yy);
6059 else if (element == EL_SWITCHGATE_OPEN ||
6060 element == EL_SWITCHGATE_OPENING)
6062 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6064 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6066 else if (element == EL_SWITCHGATE_CLOSED ||
6067 element == EL_SWITCHGATE_CLOSING)
6069 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6071 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6076 static int getInvisibleActiveFromInvisibleElement(int element)
6078 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6079 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6080 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6084 static int getInvisibleFromInvisibleActiveElement(int element)
6086 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6087 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6088 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6092 static void RedrawAllLightSwitchesAndInvisibleElements()
6096 SCAN_PLAYFIELD(x, y)
6098 int element = Feld[x][y];
6100 if (element == EL_LIGHT_SWITCH &&
6101 game.light_time_left > 0)
6103 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6104 TEST_DrawLevelField(x, y);
6106 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6107 game.light_time_left == 0)
6109 Feld[x][y] = EL_LIGHT_SWITCH;
6110 TEST_DrawLevelField(x, y);
6112 else if (element == EL_EMC_DRIPPER &&
6113 game.light_time_left > 0)
6115 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6116 TEST_DrawLevelField(x, y);
6118 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6119 game.light_time_left == 0)
6121 Feld[x][y] = EL_EMC_DRIPPER;
6122 TEST_DrawLevelField(x, y);
6124 else if (element == EL_INVISIBLE_STEELWALL ||
6125 element == EL_INVISIBLE_WALL ||
6126 element == EL_INVISIBLE_SAND)
6128 if (game.light_time_left > 0)
6129 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6131 TEST_DrawLevelField(x, y);
6133 /* uncrumble neighbour fields, if needed */
6134 if (element == EL_INVISIBLE_SAND)
6135 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6137 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6138 element == EL_INVISIBLE_WALL_ACTIVE ||
6139 element == EL_INVISIBLE_SAND_ACTIVE)
6141 if (game.light_time_left == 0)
6142 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6144 TEST_DrawLevelField(x, y);
6146 /* re-crumble neighbour fields, if needed */
6147 if (element == EL_INVISIBLE_SAND)
6148 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6153 static void RedrawAllInvisibleElementsForLenses()
6157 SCAN_PLAYFIELD(x, y)
6159 int element = Feld[x][y];
6161 if (element == EL_EMC_DRIPPER &&
6162 game.lenses_time_left > 0)
6164 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6165 TEST_DrawLevelField(x, y);
6167 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6168 game.lenses_time_left == 0)
6170 Feld[x][y] = EL_EMC_DRIPPER;
6171 TEST_DrawLevelField(x, y);
6173 else if (element == EL_INVISIBLE_STEELWALL ||
6174 element == EL_INVISIBLE_WALL ||
6175 element == EL_INVISIBLE_SAND)
6177 if (game.lenses_time_left > 0)
6178 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6180 TEST_DrawLevelField(x, y);
6182 /* uncrumble neighbour fields, if needed */
6183 if (element == EL_INVISIBLE_SAND)
6184 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6186 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6187 element == EL_INVISIBLE_WALL_ACTIVE ||
6188 element == EL_INVISIBLE_SAND_ACTIVE)
6190 if (game.lenses_time_left == 0)
6191 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6193 TEST_DrawLevelField(x, y);
6195 /* re-crumble neighbour fields, if needed */
6196 if (element == EL_INVISIBLE_SAND)
6197 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6202 static void RedrawAllInvisibleElementsForMagnifier()
6206 SCAN_PLAYFIELD(x, y)
6208 int element = Feld[x][y];
6210 if (element == EL_EMC_FAKE_GRASS &&
6211 game.magnify_time_left > 0)
6213 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6214 TEST_DrawLevelField(x, y);
6216 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6217 game.magnify_time_left == 0)
6219 Feld[x][y] = EL_EMC_FAKE_GRASS;
6220 TEST_DrawLevelField(x, y);
6222 else if (IS_GATE_GRAY(element) &&
6223 game.magnify_time_left > 0)
6225 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6226 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6227 IS_EM_GATE_GRAY(element) ?
6228 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6229 IS_EMC_GATE_GRAY(element) ?
6230 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6231 IS_DC_GATE_GRAY(element) ?
6232 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6234 TEST_DrawLevelField(x, y);
6236 else if (IS_GATE_GRAY_ACTIVE(element) &&
6237 game.magnify_time_left == 0)
6239 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6240 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6241 IS_EM_GATE_GRAY_ACTIVE(element) ?
6242 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6243 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6244 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6245 IS_DC_GATE_GRAY_ACTIVE(element) ?
6246 EL_DC_GATE_WHITE_GRAY :
6248 TEST_DrawLevelField(x, y);
6253 static void ToggleLightSwitch(int x, int y)
6255 int element = Feld[x][y];
6257 game.light_time_left =
6258 (element == EL_LIGHT_SWITCH ?
6259 level.time_light * FRAMES_PER_SECOND : 0);
6261 RedrawAllLightSwitchesAndInvisibleElements();
6264 static void ActivateTimegateSwitch(int x, int y)
6268 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6270 SCAN_PLAYFIELD(xx, yy)
6272 int element = Feld[xx][yy];
6274 if (element == EL_TIMEGATE_CLOSED ||
6275 element == EL_TIMEGATE_CLOSING)
6277 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6278 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6282 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6284 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6285 TEST_DrawLevelField(xx, yy);
6291 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6292 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6295 void Impact(int x, int y)
6297 boolean last_line = (y == lev_fieldy - 1);
6298 boolean object_hit = FALSE;
6299 boolean impact = (last_line || object_hit);
6300 int element = Feld[x][y];
6301 int smashed = EL_STEELWALL;
6303 if (!last_line) /* check if element below was hit */
6305 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6308 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6309 MovDir[x][y + 1] != MV_DOWN ||
6310 MovPos[x][y + 1] <= TILEY / 2));
6312 /* do not smash moving elements that left the smashed field in time */
6313 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6314 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6317 #if USE_QUICKSAND_IMPACT_BUGFIX
6318 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6320 RemoveMovingField(x, y + 1);
6321 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6322 Feld[x][y + 2] = EL_ROCK;
6323 TEST_DrawLevelField(x, y + 2);
6328 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6330 RemoveMovingField(x, y + 1);
6331 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6332 Feld[x][y + 2] = EL_ROCK;
6333 TEST_DrawLevelField(x, y + 2);
6340 smashed = MovingOrBlocked2Element(x, y + 1);
6342 impact = (last_line || object_hit);
6345 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6347 SplashAcid(x, y + 1);
6351 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6352 /* only reset graphic animation if graphic really changes after impact */
6354 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6356 ResetGfxAnimation(x, y);
6357 TEST_DrawLevelField(x, y);
6360 if (impact && CAN_EXPLODE_IMPACT(element))
6365 else if (impact && element == EL_PEARL &&
6366 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6368 ResetGfxAnimation(x, y);
6370 Feld[x][y] = EL_PEARL_BREAKING;
6371 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6374 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6376 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6381 if (impact && element == EL_AMOEBA_DROP)
6383 if (object_hit && IS_PLAYER(x, y + 1))
6384 KillPlayerUnlessEnemyProtected(x, y + 1);
6385 else if (object_hit && smashed == EL_PENGUIN)
6389 Feld[x][y] = EL_AMOEBA_GROWING;
6390 Store[x][y] = EL_AMOEBA_WET;
6392 ResetRandomAnimationValue(x, y);
6397 if (object_hit) /* check which object was hit */
6399 if ((CAN_PASS_MAGIC_WALL(element) &&
6400 (smashed == EL_MAGIC_WALL ||
6401 smashed == EL_BD_MAGIC_WALL)) ||
6402 (CAN_PASS_DC_MAGIC_WALL(element) &&
6403 smashed == EL_DC_MAGIC_WALL))
6406 int activated_magic_wall =
6407 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6408 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6409 EL_DC_MAGIC_WALL_ACTIVE);
6411 /* activate magic wall / mill */
6412 SCAN_PLAYFIELD(xx, yy)
6414 if (Feld[xx][yy] == smashed)
6415 Feld[xx][yy] = activated_magic_wall;
6418 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6419 game.magic_wall_active = TRUE;
6421 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6422 SND_MAGIC_WALL_ACTIVATING :
6423 smashed == EL_BD_MAGIC_WALL ?
6424 SND_BD_MAGIC_WALL_ACTIVATING :
6425 SND_DC_MAGIC_WALL_ACTIVATING));
6428 if (IS_PLAYER(x, y + 1))
6430 if (CAN_SMASH_PLAYER(element))
6432 KillPlayerUnlessEnemyProtected(x, y + 1);
6436 else if (smashed == EL_PENGUIN)
6438 if (CAN_SMASH_PLAYER(element))
6444 else if (element == EL_BD_DIAMOND)
6446 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6452 else if (((element == EL_SP_INFOTRON ||
6453 element == EL_SP_ZONK) &&
6454 (smashed == EL_SP_SNIKSNAK ||
6455 smashed == EL_SP_ELECTRON ||
6456 smashed == EL_SP_DISK_ORANGE)) ||
6457 (element == EL_SP_INFOTRON &&
6458 smashed == EL_SP_DISK_YELLOW))
6463 else if (CAN_SMASH_EVERYTHING(element))
6465 if (IS_CLASSIC_ENEMY(smashed) ||
6466 CAN_EXPLODE_SMASHED(smashed))
6471 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6473 if (smashed == EL_LAMP ||
6474 smashed == EL_LAMP_ACTIVE)
6479 else if (smashed == EL_NUT)
6481 Feld[x][y + 1] = EL_NUT_BREAKING;
6482 PlayLevelSound(x, y, SND_NUT_BREAKING);
6483 RaiseScoreElement(EL_NUT);
6486 else if (smashed == EL_PEARL)
6488 ResetGfxAnimation(x, y);
6490 Feld[x][y + 1] = EL_PEARL_BREAKING;
6491 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6494 else if (smashed == EL_DIAMOND)
6496 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6497 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6500 else if (IS_BELT_SWITCH(smashed))
6502 ToggleBeltSwitch(x, y + 1);
6504 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6505 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6506 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6507 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6509 ToggleSwitchgateSwitch(x, y + 1);
6511 else if (smashed == EL_LIGHT_SWITCH ||
6512 smashed == EL_LIGHT_SWITCH_ACTIVE)
6514 ToggleLightSwitch(x, y + 1);
6518 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6520 CheckElementChangeBySide(x, y + 1, smashed, element,
6521 CE_SWITCHED, CH_SIDE_TOP);
6522 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6528 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6533 /* play sound of magic wall / mill */
6535 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6536 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6537 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6539 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6540 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6541 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6542 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6543 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6544 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6549 /* play sound of object that hits the ground */
6550 if (last_line || object_hit)
6551 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6554 inline static void TurnRoundExt(int x, int y)
6566 { 0, 0 }, { 0, 0 }, { 0, 0 },
6571 int left, right, back;
6575 { MV_DOWN, MV_UP, MV_RIGHT },
6576 { MV_UP, MV_DOWN, MV_LEFT },
6578 { MV_LEFT, MV_RIGHT, MV_DOWN },
6582 { MV_RIGHT, MV_LEFT, MV_UP }
6585 int element = Feld[x][y];
6586 int move_pattern = element_info[element].move_pattern;
6588 int old_move_dir = MovDir[x][y];
6589 int left_dir = turn[old_move_dir].left;
6590 int right_dir = turn[old_move_dir].right;
6591 int back_dir = turn[old_move_dir].back;
6593 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6594 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6595 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6596 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6598 int left_x = x + left_dx, left_y = y + left_dy;
6599 int right_x = x + right_dx, right_y = y + right_dy;
6600 int move_x = x + move_dx, move_y = y + move_dy;
6604 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6606 TestIfBadThingTouchesOtherBadThing(x, y);
6608 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6609 MovDir[x][y] = right_dir;
6610 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6611 MovDir[x][y] = left_dir;
6613 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6615 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6618 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6620 TestIfBadThingTouchesOtherBadThing(x, y);
6622 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6623 MovDir[x][y] = left_dir;
6624 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6625 MovDir[x][y] = right_dir;
6627 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6629 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6632 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6634 TestIfBadThingTouchesOtherBadThing(x, y);
6636 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6637 MovDir[x][y] = left_dir;
6638 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6639 MovDir[x][y] = right_dir;
6641 if (MovDir[x][y] != old_move_dir)
6644 else if (element == EL_YAMYAM)
6646 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6647 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6649 if (can_turn_left && can_turn_right)
6650 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6651 else if (can_turn_left)
6652 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6653 else if (can_turn_right)
6654 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6656 MovDir[x][y] = back_dir;
6658 MovDelay[x][y] = 16 + 16 * RND(3);
6660 else if (element == EL_DARK_YAMYAM)
6662 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6664 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6667 if (can_turn_left && can_turn_right)
6668 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6669 else if (can_turn_left)
6670 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6671 else if (can_turn_right)
6672 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6674 MovDir[x][y] = back_dir;
6676 MovDelay[x][y] = 16 + 16 * RND(3);
6678 else if (element == EL_PACMAN)
6680 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6681 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6683 if (can_turn_left && can_turn_right)
6684 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6685 else if (can_turn_left)
6686 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6687 else if (can_turn_right)
6688 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6690 MovDir[x][y] = back_dir;
6692 MovDelay[x][y] = 6 + RND(40);
6694 else if (element == EL_PIG)
6696 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6697 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6698 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6699 boolean should_turn_left, should_turn_right, should_move_on;
6701 int rnd = RND(rnd_value);
6703 should_turn_left = (can_turn_left &&
6705 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6706 y + back_dy + left_dy)));
6707 should_turn_right = (can_turn_right &&
6709 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6710 y + back_dy + right_dy)));
6711 should_move_on = (can_move_on &&
6714 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6715 y + move_dy + left_dy) ||
6716 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6717 y + move_dy + right_dy)));
6719 if (should_turn_left || should_turn_right || should_move_on)
6721 if (should_turn_left && should_turn_right && should_move_on)
6722 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6723 rnd < 2 * rnd_value / 3 ? right_dir :
6725 else if (should_turn_left && should_turn_right)
6726 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6727 else if (should_turn_left && should_move_on)
6728 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6729 else if (should_turn_right && should_move_on)
6730 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6731 else if (should_turn_left)
6732 MovDir[x][y] = left_dir;
6733 else if (should_turn_right)
6734 MovDir[x][y] = right_dir;
6735 else if (should_move_on)
6736 MovDir[x][y] = old_move_dir;
6738 else if (can_move_on && rnd > rnd_value / 8)
6739 MovDir[x][y] = old_move_dir;
6740 else if (can_turn_left && can_turn_right)
6741 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6742 else if (can_turn_left && rnd > rnd_value / 8)
6743 MovDir[x][y] = left_dir;
6744 else if (can_turn_right && rnd > rnd_value/8)
6745 MovDir[x][y] = right_dir;
6747 MovDir[x][y] = back_dir;
6749 xx = x + move_xy[MovDir[x][y]].dx;
6750 yy = y + move_xy[MovDir[x][y]].dy;
6752 if (!IN_LEV_FIELD(xx, yy) ||
6753 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6754 MovDir[x][y] = old_move_dir;
6758 else if (element == EL_DRAGON)
6760 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6761 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6762 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6764 int rnd = RND(rnd_value);
6766 if (can_move_on && rnd > rnd_value / 8)
6767 MovDir[x][y] = old_move_dir;
6768 else if (can_turn_left && can_turn_right)
6769 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6770 else if (can_turn_left && rnd > rnd_value / 8)
6771 MovDir[x][y] = left_dir;
6772 else if (can_turn_right && rnd > rnd_value / 8)
6773 MovDir[x][y] = right_dir;
6775 MovDir[x][y] = back_dir;
6777 xx = x + move_xy[MovDir[x][y]].dx;
6778 yy = y + move_xy[MovDir[x][y]].dy;
6780 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6781 MovDir[x][y] = old_move_dir;
6785 else if (element == EL_MOLE)
6787 boolean can_move_on =
6788 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6789 IS_AMOEBOID(Feld[move_x][move_y]) ||
6790 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6793 boolean can_turn_left =
6794 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6795 IS_AMOEBOID(Feld[left_x][left_y])));
6797 boolean can_turn_right =
6798 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6799 IS_AMOEBOID(Feld[right_x][right_y])));
6801 if (can_turn_left && can_turn_right)
6802 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6803 else if (can_turn_left)
6804 MovDir[x][y] = left_dir;
6806 MovDir[x][y] = right_dir;
6809 if (MovDir[x][y] != old_move_dir)
6812 else if (element == EL_BALLOON)
6814 MovDir[x][y] = game.wind_direction;
6817 else if (element == EL_SPRING)
6819 if (MovDir[x][y] & MV_HORIZONTAL)
6821 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6822 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6824 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6825 ResetGfxAnimation(move_x, move_y);
6826 TEST_DrawLevelField(move_x, move_y);
6828 MovDir[x][y] = back_dir;
6830 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6831 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6832 MovDir[x][y] = MV_NONE;
6837 else if (element == EL_ROBOT ||
6838 element == EL_SATELLITE ||
6839 element == EL_PENGUIN ||
6840 element == EL_EMC_ANDROID)
6842 int attr_x = -1, attr_y = -1;
6853 for (i = 0; i < MAX_PLAYERS; i++)
6855 struct PlayerInfo *player = &stored_player[i];
6856 int jx = player->jx, jy = player->jy;
6858 if (!player->active)
6862 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6870 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6871 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6872 game.engine_version < VERSION_IDENT(3,1,0,0)))
6878 if (element == EL_PENGUIN)
6881 static int xy[4][2] =
6889 for (i = 0; i < NUM_DIRECTIONS; i++)
6891 int ex = x + xy[i][0];
6892 int ey = y + xy[i][1];
6894 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6895 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6896 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6897 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6906 MovDir[x][y] = MV_NONE;
6908 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6909 else if (attr_x > x)
6910 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6912 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6913 else if (attr_y > y)
6914 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6916 if (element == EL_ROBOT)
6920 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6921 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6922 Moving2Blocked(x, y, &newx, &newy);
6924 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6925 MovDelay[x][y] = 8 + 8 * !RND(3);
6927 MovDelay[x][y] = 16;
6929 else if (element == EL_PENGUIN)
6935 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6937 boolean first_horiz = RND(2);
6938 int new_move_dir = MovDir[x][y];
6941 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6942 Moving2Blocked(x, y, &newx, &newy);
6944 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6948 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6949 Moving2Blocked(x, y, &newx, &newy);
6951 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6954 MovDir[x][y] = old_move_dir;
6958 else if (element == EL_SATELLITE)
6964 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6966 boolean first_horiz = RND(2);
6967 int new_move_dir = MovDir[x][y];
6970 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6971 Moving2Blocked(x, y, &newx, &newy);
6973 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6977 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6978 Moving2Blocked(x, y, &newx, &newy);
6980 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6983 MovDir[x][y] = old_move_dir;
6987 else if (element == EL_EMC_ANDROID)
6989 static int check_pos[16] =
6991 -1, /* 0 => (invalid) */
6992 7, /* 1 => MV_LEFT */
6993 3, /* 2 => MV_RIGHT */
6994 -1, /* 3 => (invalid) */
6996 0, /* 5 => MV_LEFT | MV_UP */
6997 2, /* 6 => MV_RIGHT | MV_UP */
6998 -1, /* 7 => (invalid) */
6999 5, /* 8 => MV_DOWN */
7000 6, /* 9 => MV_LEFT | MV_DOWN */
7001 4, /* 10 => MV_RIGHT | MV_DOWN */
7002 -1, /* 11 => (invalid) */
7003 -1, /* 12 => (invalid) */
7004 -1, /* 13 => (invalid) */
7005 -1, /* 14 => (invalid) */
7006 -1, /* 15 => (invalid) */
7014 { -1, -1, MV_LEFT | MV_UP },
7016 { +1, -1, MV_RIGHT | MV_UP },
7017 { +1, 0, MV_RIGHT },
7018 { +1, +1, MV_RIGHT | MV_DOWN },
7020 { -1, +1, MV_LEFT | MV_DOWN },
7023 int start_pos, check_order;
7024 boolean can_clone = FALSE;
7027 /* check if there is any free field around current position */
7028 for (i = 0; i < 8; i++)
7030 int newx = x + check_xy[i].dx;
7031 int newy = y + check_xy[i].dy;
7033 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7041 if (can_clone) /* randomly find an element to clone */
7045 start_pos = check_pos[RND(8)];
7046 check_order = (RND(2) ? -1 : +1);
7048 for (i = 0; i < 8; i++)
7050 int pos_raw = start_pos + i * check_order;
7051 int pos = (pos_raw + 8) % 8;
7052 int newx = x + check_xy[pos].dx;
7053 int newy = y + check_xy[pos].dy;
7055 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7057 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7058 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7060 Store[x][y] = Feld[newx][newy];
7069 if (can_clone) /* randomly find a direction to move */
7073 start_pos = check_pos[RND(8)];
7074 check_order = (RND(2) ? -1 : +1);
7076 for (i = 0; i < 8; i++)
7078 int pos_raw = start_pos + i * check_order;
7079 int pos = (pos_raw + 8) % 8;
7080 int newx = x + check_xy[pos].dx;
7081 int newy = y + check_xy[pos].dy;
7082 int new_move_dir = check_xy[pos].dir;
7084 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7086 MovDir[x][y] = new_move_dir;
7087 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7096 if (can_clone) /* cloning and moving successful */
7099 /* cannot clone -- try to move towards player */
7101 start_pos = check_pos[MovDir[x][y] & 0x0f];
7102 check_order = (RND(2) ? -1 : +1);
7104 for (i = 0; i < 3; i++)
7106 /* first check start_pos, then previous/next or (next/previous) pos */
7107 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7108 int pos = (pos_raw + 8) % 8;
7109 int newx = x + check_xy[pos].dx;
7110 int newy = y + check_xy[pos].dy;
7111 int new_move_dir = check_xy[pos].dir;
7113 if (IS_PLAYER(newx, newy))
7116 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7118 MovDir[x][y] = new_move_dir;
7119 MovDelay[x][y] = level.android_move_time * 8 + 1;
7126 else if (move_pattern == MV_TURNING_LEFT ||
7127 move_pattern == MV_TURNING_RIGHT ||
7128 move_pattern == MV_TURNING_LEFT_RIGHT ||
7129 move_pattern == MV_TURNING_RIGHT_LEFT ||
7130 move_pattern == MV_TURNING_RANDOM ||
7131 move_pattern == MV_ALL_DIRECTIONS)
7133 boolean can_turn_left =
7134 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7135 boolean can_turn_right =
7136 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7138 if (element_info[element].move_stepsize == 0) /* "not moving" */
7141 if (move_pattern == MV_TURNING_LEFT)
7142 MovDir[x][y] = left_dir;
7143 else if (move_pattern == MV_TURNING_RIGHT)
7144 MovDir[x][y] = right_dir;
7145 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7146 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7147 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7148 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7149 else if (move_pattern == MV_TURNING_RANDOM)
7150 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7151 can_turn_right && !can_turn_left ? right_dir :
7152 RND(2) ? left_dir : right_dir);
7153 else if (can_turn_left && can_turn_right)
7154 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7155 else if (can_turn_left)
7156 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7157 else if (can_turn_right)
7158 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7160 MovDir[x][y] = back_dir;
7162 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7164 else if (move_pattern == MV_HORIZONTAL ||
7165 move_pattern == MV_VERTICAL)
7167 if (move_pattern & old_move_dir)
7168 MovDir[x][y] = back_dir;
7169 else if (move_pattern == MV_HORIZONTAL)
7170 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7171 else if (move_pattern == MV_VERTICAL)
7172 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7174 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7176 else if (move_pattern & MV_ANY_DIRECTION)
7178 MovDir[x][y] = move_pattern;
7179 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7181 else if (move_pattern & MV_WIND_DIRECTION)
7183 MovDir[x][y] = game.wind_direction;
7184 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7186 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7188 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7189 MovDir[x][y] = left_dir;
7190 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7191 MovDir[x][y] = right_dir;
7193 if (MovDir[x][y] != old_move_dir)
7194 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7196 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7198 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7199 MovDir[x][y] = right_dir;
7200 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7201 MovDir[x][y] = left_dir;
7203 if (MovDir[x][y] != old_move_dir)
7204 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7206 else if (move_pattern == MV_TOWARDS_PLAYER ||
7207 move_pattern == MV_AWAY_FROM_PLAYER)
7209 int attr_x = -1, attr_y = -1;
7211 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7222 for (i = 0; i < MAX_PLAYERS; i++)
7224 struct PlayerInfo *player = &stored_player[i];
7225 int jx = player->jx, jy = player->jy;
7227 if (!player->active)
7231 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7239 MovDir[x][y] = MV_NONE;
7241 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7242 else if (attr_x > x)
7243 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7245 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7246 else if (attr_y > y)
7247 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7249 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7251 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7253 boolean first_horiz = RND(2);
7254 int new_move_dir = MovDir[x][y];
7256 if (element_info[element].move_stepsize == 0) /* "not moving" */
7258 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7259 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7265 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266 Moving2Blocked(x, y, &newx, &newy);
7268 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7272 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7273 Moving2Blocked(x, y, &newx, &newy);
7275 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7278 MovDir[x][y] = old_move_dir;
7281 else if (move_pattern == MV_WHEN_PUSHED ||
7282 move_pattern == MV_WHEN_DROPPED)
7284 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7285 MovDir[x][y] = MV_NONE;
7289 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7291 static int test_xy[7][2] =
7301 static int test_dir[7] =
7311 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7312 int move_preference = -1000000; /* start with very low preference */
7313 int new_move_dir = MV_NONE;
7314 int start_test = RND(4);
7317 for (i = 0; i < NUM_DIRECTIONS; i++)
7319 int move_dir = test_dir[start_test + i];
7320 int move_dir_preference;
7322 xx = x + test_xy[start_test + i][0];
7323 yy = y + test_xy[start_test + i][1];
7325 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7326 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7328 new_move_dir = move_dir;
7333 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7336 move_dir_preference = -1 * RunnerVisit[xx][yy];
7337 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7338 move_dir_preference = PlayerVisit[xx][yy];
7340 if (move_dir_preference > move_preference)
7342 /* prefer field that has not been visited for the longest time */
7343 move_preference = move_dir_preference;
7344 new_move_dir = move_dir;
7346 else if (move_dir_preference == move_preference &&
7347 move_dir == old_move_dir)
7349 /* prefer last direction when all directions are preferred equally */
7350 move_preference = move_dir_preference;
7351 new_move_dir = move_dir;
7355 MovDir[x][y] = new_move_dir;
7356 if (old_move_dir != new_move_dir)
7357 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7361 static void TurnRound(int x, int y)
7363 int direction = MovDir[x][y];
7367 GfxDir[x][y] = MovDir[x][y];
7369 if (direction != MovDir[x][y])
7373 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7375 ResetGfxFrame(x, y);
7378 static boolean JustBeingPushed(int x, int y)
7382 for (i = 0; i < MAX_PLAYERS; i++)
7384 struct PlayerInfo *player = &stored_player[i];
7386 if (player->active && player->is_pushing && player->MovPos)
7388 int next_jx = player->jx + (player->jx - player->last_jx);
7389 int next_jy = player->jy + (player->jy - player->last_jy);
7391 if (x == next_jx && y == next_jy)
7399 void StartMoving(int x, int y)
7401 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7402 int element = Feld[x][y];
7407 if (MovDelay[x][y] == 0)
7408 GfxAction[x][y] = ACTION_DEFAULT;
7410 if (CAN_FALL(element) && y < lev_fieldy - 1)
7412 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7413 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7414 if (JustBeingPushed(x, y))
7417 if (element == EL_QUICKSAND_FULL)
7419 if (IS_FREE(x, y + 1))
7421 InitMovingField(x, y, MV_DOWN);
7422 started_moving = TRUE;
7424 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7425 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7426 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7427 Store[x][y] = EL_ROCK;
7429 Store[x][y] = EL_ROCK;
7432 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7434 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7436 if (!MovDelay[x][y])
7438 MovDelay[x][y] = TILEY + 1;
7440 ResetGfxAnimation(x, y);
7441 ResetGfxAnimation(x, y + 1);
7446 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7447 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7454 Feld[x][y] = EL_QUICKSAND_EMPTY;
7455 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7456 Store[x][y + 1] = Store[x][y];
7459 PlayLevelSoundAction(x, y, ACTION_FILLING);
7461 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7463 if (!MovDelay[x][y])
7465 MovDelay[x][y] = TILEY + 1;
7467 ResetGfxAnimation(x, y);
7468 ResetGfxAnimation(x, y + 1);
7473 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7474 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7481 Feld[x][y] = EL_QUICKSAND_EMPTY;
7482 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7483 Store[x][y + 1] = Store[x][y];
7486 PlayLevelSoundAction(x, y, ACTION_FILLING);
7489 else if (element == EL_QUICKSAND_FAST_FULL)
7491 if (IS_FREE(x, y + 1))
7493 InitMovingField(x, y, MV_DOWN);
7494 started_moving = TRUE;
7496 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7497 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7498 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7499 Store[x][y] = EL_ROCK;
7501 Store[x][y] = EL_ROCK;
7504 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7506 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7508 if (!MovDelay[x][y])
7510 MovDelay[x][y] = TILEY + 1;
7512 ResetGfxAnimation(x, y);
7513 ResetGfxAnimation(x, y + 1);
7518 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7519 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7526 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7527 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7528 Store[x][y + 1] = Store[x][y];
7531 PlayLevelSoundAction(x, y, ACTION_FILLING);
7533 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7535 if (!MovDelay[x][y])
7537 MovDelay[x][y] = TILEY + 1;
7539 ResetGfxAnimation(x, y);
7540 ResetGfxAnimation(x, y + 1);
7545 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7546 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7553 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7554 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7555 Store[x][y + 1] = Store[x][y];
7558 PlayLevelSoundAction(x, y, ACTION_FILLING);
7561 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7562 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7564 InitMovingField(x, y, MV_DOWN);
7565 started_moving = TRUE;
7567 Feld[x][y] = EL_QUICKSAND_FILLING;
7568 Store[x][y] = element;
7570 PlayLevelSoundAction(x, y, ACTION_FILLING);
7572 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7573 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7575 InitMovingField(x, y, MV_DOWN);
7576 started_moving = TRUE;
7578 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7579 Store[x][y] = element;
7581 PlayLevelSoundAction(x, y, ACTION_FILLING);
7583 else if (element == EL_MAGIC_WALL_FULL)
7585 if (IS_FREE(x, y + 1))
7587 InitMovingField(x, y, MV_DOWN);
7588 started_moving = TRUE;
7590 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7591 Store[x][y] = EL_CHANGED(Store[x][y]);
7593 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7595 if (!MovDelay[x][y])
7596 MovDelay[x][y] = TILEY / 4 + 1;
7605 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7606 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7607 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7611 else if (element == EL_BD_MAGIC_WALL_FULL)
7613 if (IS_FREE(x, y + 1))
7615 InitMovingField(x, y, MV_DOWN);
7616 started_moving = TRUE;
7618 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7619 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7621 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7623 if (!MovDelay[x][y])
7624 MovDelay[x][y] = TILEY / 4 + 1;
7633 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7634 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7635 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7639 else if (element == EL_DC_MAGIC_WALL_FULL)
7641 if (IS_FREE(x, y + 1))
7643 InitMovingField(x, y, MV_DOWN);
7644 started_moving = TRUE;
7646 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7647 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7649 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7651 if (!MovDelay[x][y])
7652 MovDelay[x][y] = TILEY / 4 + 1;
7661 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7662 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7663 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7667 else if ((CAN_PASS_MAGIC_WALL(element) &&
7668 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7669 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7670 (CAN_PASS_DC_MAGIC_WALL(element) &&
7671 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7674 InitMovingField(x, y, MV_DOWN);
7675 started_moving = TRUE;
7678 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7679 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7680 EL_DC_MAGIC_WALL_FILLING);
7681 Store[x][y] = element;
7683 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7685 SplashAcid(x, y + 1);
7687 InitMovingField(x, y, MV_DOWN);
7688 started_moving = TRUE;
7690 Store[x][y] = EL_ACID;
7693 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7694 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7695 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7696 CAN_FALL(element) && WasJustFalling[x][y] &&
7697 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7699 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7700 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7701 (Feld[x][y + 1] == EL_BLOCKED)))
7703 /* this is needed for a special case not covered by calling "Impact()"
7704 from "ContinueMoving()": if an element moves to a tile directly below
7705 another element which was just falling on that tile (which was empty
7706 in the previous frame), the falling element above would just stop
7707 instead of smashing the element below (in previous version, the above
7708 element was just checked for "moving" instead of "falling", resulting
7709 in incorrect smashes caused by horizontal movement of the above
7710 element; also, the case of the player being the element to smash was
7711 simply not covered here... :-/ ) */
7713 CheckCollision[x][y] = 0;
7714 CheckImpact[x][y] = 0;
7718 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7720 if (MovDir[x][y] == MV_NONE)
7722 InitMovingField(x, y, MV_DOWN);
7723 started_moving = TRUE;
7726 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7728 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7729 MovDir[x][y] = MV_DOWN;
7731 InitMovingField(x, y, MV_DOWN);
7732 started_moving = TRUE;
7734 else if (element == EL_AMOEBA_DROP)
7736 Feld[x][y] = EL_AMOEBA_GROWING;
7737 Store[x][y] = EL_AMOEBA_WET;
7739 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7740 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7741 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7742 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7744 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7745 (IS_FREE(x - 1, y + 1) ||
7746 Feld[x - 1][y + 1] == EL_ACID));
7747 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7748 (IS_FREE(x + 1, y + 1) ||
7749 Feld[x + 1][y + 1] == EL_ACID));
7750 boolean can_fall_any = (can_fall_left || can_fall_right);
7751 boolean can_fall_both = (can_fall_left && can_fall_right);
7752 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7754 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7756 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7757 can_fall_right = FALSE;
7758 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7759 can_fall_left = FALSE;
7760 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7761 can_fall_right = FALSE;
7762 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7763 can_fall_left = FALSE;
7765 can_fall_any = (can_fall_left || can_fall_right);
7766 can_fall_both = FALSE;
7771 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7772 can_fall_right = FALSE; /* slip down on left side */
7774 can_fall_left = !(can_fall_right = RND(2));
7776 can_fall_both = FALSE;
7781 /* if not determined otherwise, prefer left side for slipping down */
7782 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7783 started_moving = TRUE;
7786 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7788 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7789 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7790 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7791 int belt_dir = game.belt_dir[belt_nr];
7793 if ((belt_dir == MV_LEFT && left_is_free) ||
7794 (belt_dir == MV_RIGHT && right_is_free))
7796 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7798 InitMovingField(x, y, belt_dir);
7799 started_moving = TRUE;
7801 Pushed[x][y] = TRUE;
7802 Pushed[nextx][y] = TRUE;
7804 GfxAction[x][y] = ACTION_DEFAULT;
7808 MovDir[x][y] = 0; /* if element was moving, stop it */
7813 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7814 if (CAN_MOVE(element) && !started_moving)
7816 int move_pattern = element_info[element].move_pattern;
7819 Moving2Blocked(x, y, &newx, &newy);
7821 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7824 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7825 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7827 WasJustMoving[x][y] = 0;
7828 CheckCollision[x][y] = 0;
7830 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7832 if (Feld[x][y] != element) /* element has changed */
7836 if (!MovDelay[x][y]) /* start new movement phase */
7838 /* all objects that can change their move direction after each step
7839 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7841 if (element != EL_YAMYAM &&
7842 element != EL_DARK_YAMYAM &&
7843 element != EL_PACMAN &&
7844 !(move_pattern & MV_ANY_DIRECTION) &&
7845 move_pattern != MV_TURNING_LEFT &&
7846 move_pattern != MV_TURNING_RIGHT &&
7847 move_pattern != MV_TURNING_LEFT_RIGHT &&
7848 move_pattern != MV_TURNING_RIGHT_LEFT &&
7849 move_pattern != MV_TURNING_RANDOM)
7853 if (MovDelay[x][y] && (element == EL_BUG ||
7854 element == EL_SPACESHIP ||
7855 element == EL_SP_SNIKSNAK ||
7856 element == EL_SP_ELECTRON ||
7857 element == EL_MOLE))
7858 TEST_DrawLevelField(x, y);
7862 if (MovDelay[x][y]) /* wait some time before next movement */
7866 if (element == EL_ROBOT ||
7867 element == EL_YAMYAM ||
7868 element == EL_DARK_YAMYAM)
7870 DrawLevelElementAnimationIfNeeded(x, y, element);
7871 PlayLevelSoundAction(x, y, ACTION_WAITING);
7873 else if (element == EL_SP_ELECTRON)
7874 DrawLevelElementAnimationIfNeeded(x, y, element);
7875 else if (element == EL_DRAGON)
7878 int dir = MovDir[x][y];
7879 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7880 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7881 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7882 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7883 dir == MV_UP ? IMG_FLAMES_1_UP :
7884 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7885 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7887 GfxAction[x][y] = ACTION_ATTACKING;
7889 if (IS_PLAYER(x, y))
7890 DrawPlayerField(x, y);
7892 TEST_DrawLevelField(x, y);
7894 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7896 for (i = 1; i <= 3; i++)
7898 int xx = x + i * dx;
7899 int yy = y + i * dy;
7900 int sx = SCREENX(xx);
7901 int sy = SCREENY(yy);
7902 int flame_graphic = graphic + (i - 1);
7904 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7909 int flamed = MovingOrBlocked2Element(xx, yy);
7911 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7914 RemoveMovingField(xx, yy);
7916 ChangeDelay[xx][yy] = 0;
7918 Feld[xx][yy] = EL_FLAMES;
7920 if (IN_SCR_FIELD(sx, sy))
7922 TEST_DrawLevelFieldCrumbled(xx, yy);
7923 DrawGraphic(sx, sy, flame_graphic, frame);
7928 if (Feld[xx][yy] == EL_FLAMES)
7929 Feld[xx][yy] = EL_EMPTY;
7930 TEST_DrawLevelField(xx, yy);
7935 if (MovDelay[x][y]) /* element still has to wait some time */
7937 PlayLevelSoundAction(x, y, ACTION_WAITING);
7943 /* now make next step */
7945 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7947 if (DONT_COLLIDE_WITH(element) &&
7948 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7949 !PLAYER_ENEMY_PROTECTED(newx, newy))
7951 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7956 else if (CAN_MOVE_INTO_ACID(element) &&
7957 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7958 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7959 (MovDir[x][y] == MV_DOWN ||
7960 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7962 SplashAcid(newx, newy);
7963 Store[x][y] = EL_ACID;
7965 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7967 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7968 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7969 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7970 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7973 TEST_DrawLevelField(x, y);
7975 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7976 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7977 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7979 local_player->friends_still_needed--;
7980 if (!local_player->friends_still_needed &&
7981 !local_player->GameOver && AllPlayersGone)
7982 PlayerWins(local_player);
7986 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7988 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7989 TEST_DrawLevelField(newx, newy);
7991 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7993 else if (!IS_FREE(newx, newy))
7995 GfxAction[x][y] = ACTION_WAITING;
7997 if (IS_PLAYER(x, y))
7998 DrawPlayerField(x, y);
8000 TEST_DrawLevelField(x, y);
8005 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8007 if (IS_FOOD_PIG(Feld[newx][newy]))
8009 if (IS_MOVING(newx, newy))
8010 RemoveMovingField(newx, newy);
8013 Feld[newx][newy] = EL_EMPTY;
8014 TEST_DrawLevelField(newx, newy);
8017 PlayLevelSound(x, y, SND_PIG_DIGGING);
8019 else if (!IS_FREE(newx, newy))
8021 if (IS_PLAYER(x, y))
8022 DrawPlayerField(x, y);
8024 TEST_DrawLevelField(x, y);
8029 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8031 if (Store[x][y] != EL_EMPTY)
8033 boolean can_clone = FALSE;
8036 /* check if element to clone is still there */
8037 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8039 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8047 /* cannot clone or target field not free anymore -- do not clone */
8048 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8049 Store[x][y] = EL_EMPTY;
8052 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8054 if (IS_MV_DIAGONAL(MovDir[x][y]))
8056 int diagonal_move_dir = MovDir[x][y];
8057 int stored = Store[x][y];
8058 int change_delay = 8;
8061 /* android is moving diagonally */
8063 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8065 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8066 GfxElement[x][y] = EL_EMC_ANDROID;
8067 GfxAction[x][y] = ACTION_SHRINKING;
8068 GfxDir[x][y] = diagonal_move_dir;
8069 ChangeDelay[x][y] = change_delay;
8071 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8074 DrawLevelGraphicAnimation(x, y, graphic);
8075 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8077 if (Feld[newx][newy] == EL_ACID)
8079 SplashAcid(newx, newy);
8084 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8086 Store[newx][newy] = EL_EMC_ANDROID;
8087 GfxElement[newx][newy] = EL_EMC_ANDROID;
8088 GfxAction[newx][newy] = ACTION_GROWING;
8089 GfxDir[newx][newy] = diagonal_move_dir;
8090 ChangeDelay[newx][newy] = change_delay;
8092 graphic = el_act_dir2img(GfxElement[newx][newy],
8093 GfxAction[newx][newy], GfxDir[newx][newy]);
8095 DrawLevelGraphicAnimation(newx, newy, graphic);
8096 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8102 Feld[newx][newy] = EL_EMPTY;
8103 TEST_DrawLevelField(newx, newy);
8105 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8108 else if (!IS_FREE(newx, newy))
8113 else if (IS_CUSTOM_ELEMENT(element) &&
8114 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8116 if (!DigFieldByCE(newx, newy, element))
8119 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8121 RunnerVisit[x][y] = FrameCounter;
8122 PlayerVisit[x][y] /= 8; /* expire player visit path */
8125 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8127 if (!IS_FREE(newx, newy))
8129 if (IS_PLAYER(x, y))
8130 DrawPlayerField(x, y);
8132 TEST_DrawLevelField(x, y);
8138 boolean wanna_flame = !RND(10);
8139 int dx = newx - x, dy = newy - y;
8140 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8141 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8142 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8143 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8144 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8145 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8148 IS_CLASSIC_ENEMY(element1) ||
8149 IS_CLASSIC_ENEMY(element2)) &&
8150 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8151 element1 != EL_FLAMES && element2 != EL_FLAMES)
8153 ResetGfxAnimation(x, y);
8154 GfxAction[x][y] = ACTION_ATTACKING;
8156 if (IS_PLAYER(x, y))
8157 DrawPlayerField(x, y);
8159 TEST_DrawLevelField(x, y);
8161 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8163 MovDelay[x][y] = 50;
8165 Feld[newx][newy] = EL_FLAMES;
8166 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8167 Feld[newx1][newy1] = EL_FLAMES;
8168 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8169 Feld[newx2][newy2] = EL_FLAMES;
8175 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8176 Feld[newx][newy] == EL_DIAMOND)
8178 if (IS_MOVING(newx, newy))
8179 RemoveMovingField(newx, newy);
8182 Feld[newx][newy] = EL_EMPTY;
8183 TEST_DrawLevelField(newx, newy);
8186 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8188 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8189 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8191 if (AmoebaNr[newx][newy])
8193 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8194 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8195 Feld[newx][newy] == EL_BD_AMOEBA)
8196 AmoebaCnt[AmoebaNr[newx][newy]]--;
8199 if (IS_MOVING(newx, newy))
8201 RemoveMovingField(newx, newy);
8205 Feld[newx][newy] = EL_EMPTY;
8206 TEST_DrawLevelField(newx, newy);
8209 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8211 else if ((element == EL_PACMAN || element == EL_MOLE)
8212 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8214 if (AmoebaNr[newx][newy])
8216 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8217 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8218 Feld[newx][newy] == EL_BD_AMOEBA)
8219 AmoebaCnt[AmoebaNr[newx][newy]]--;
8222 if (element == EL_MOLE)
8224 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8225 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8227 ResetGfxAnimation(x, y);
8228 GfxAction[x][y] = ACTION_DIGGING;
8229 TEST_DrawLevelField(x, y);
8231 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8233 return; /* wait for shrinking amoeba */
8235 else /* element == EL_PACMAN */
8237 Feld[newx][newy] = EL_EMPTY;
8238 TEST_DrawLevelField(newx, newy);
8239 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8242 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8243 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8244 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8246 /* wait for shrinking amoeba to completely disappear */
8249 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8251 /* object was running against a wall */
8255 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8256 DrawLevelElementAnimation(x, y, element);
8258 if (DONT_TOUCH(element))
8259 TestIfBadThingTouchesPlayer(x, y);
8264 InitMovingField(x, y, MovDir[x][y]);
8266 PlayLevelSoundAction(x, y, ACTION_MOVING);
8270 ContinueMoving(x, y);
8273 void ContinueMoving(int x, int y)
8275 int element = Feld[x][y];
8276 struct ElementInfo *ei = &element_info[element];
8277 int direction = MovDir[x][y];
8278 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8279 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8280 int newx = x + dx, newy = y + dy;
8281 int stored = Store[x][y];
8282 int stored_new = Store[newx][newy];
8283 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8284 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8285 boolean last_line = (newy == lev_fieldy - 1);
8287 MovPos[x][y] += getElementMoveStepsize(x, y);
8289 if (pushed_by_player) /* special case: moving object pushed by player */
8290 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8292 if (ABS(MovPos[x][y]) < TILEX)
8294 TEST_DrawLevelField(x, y);
8296 return; /* element is still moving */
8299 /* element reached destination field */
8301 Feld[x][y] = EL_EMPTY;
8302 Feld[newx][newy] = element;
8303 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8305 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8307 element = Feld[newx][newy] = EL_ACID;
8309 else if (element == EL_MOLE)
8311 Feld[x][y] = EL_SAND;
8313 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8315 else if (element == EL_QUICKSAND_FILLING)
8317 element = Feld[newx][newy] = get_next_element(element);
8318 Store[newx][newy] = Store[x][y];
8320 else if (element == EL_QUICKSAND_EMPTYING)
8322 Feld[x][y] = get_next_element(element);
8323 element = Feld[newx][newy] = Store[x][y];
8325 else if (element == EL_QUICKSAND_FAST_FILLING)
8327 element = Feld[newx][newy] = get_next_element(element);
8328 Store[newx][newy] = Store[x][y];
8330 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8332 Feld[x][y] = get_next_element(element);
8333 element = Feld[newx][newy] = Store[x][y];
8335 else if (element == EL_MAGIC_WALL_FILLING)
8337 element = Feld[newx][newy] = get_next_element(element);
8338 if (!game.magic_wall_active)
8339 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8340 Store[newx][newy] = Store[x][y];
8342 else if (element == EL_MAGIC_WALL_EMPTYING)
8344 Feld[x][y] = get_next_element(element);
8345 if (!game.magic_wall_active)
8346 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8347 element = Feld[newx][newy] = Store[x][y];
8349 InitField(newx, newy, FALSE);
8351 else if (element == EL_BD_MAGIC_WALL_FILLING)
8353 element = Feld[newx][newy] = get_next_element(element);
8354 if (!game.magic_wall_active)
8355 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8356 Store[newx][newy] = Store[x][y];
8358 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8360 Feld[x][y] = get_next_element(element);
8361 if (!game.magic_wall_active)
8362 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8363 element = Feld[newx][newy] = Store[x][y];
8365 InitField(newx, newy, FALSE);
8367 else if (element == EL_DC_MAGIC_WALL_FILLING)
8369 element = Feld[newx][newy] = get_next_element(element);
8370 if (!game.magic_wall_active)
8371 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8372 Store[newx][newy] = Store[x][y];
8374 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8376 Feld[x][y] = get_next_element(element);
8377 if (!game.magic_wall_active)
8378 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8379 element = Feld[newx][newy] = Store[x][y];
8381 InitField(newx, newy, FALSE);
8383 else if (element == EL_AMOEBA_DROPPING)
8385 Feld[x][y] = get_next_element(element);
8386 element = Feld[newx][newy] = Store[x][y];
8388 else if (element == EL_SOKOBAN_OBJECT)
8391 Feld[x][y] = Back[x][y];
8393 if (Back[newx][newy])
8394 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8396 Back[x][y] = Back[newx][newy] = 0;
8399 Store[x][y] = EL_EMPTY;
8404 MovDelay[newx][newy] = 0;
8406 if (CAN_CHANGE_OR_HAS_ACTION(element))
8408 /* copy element change control values to new field */
8409 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8410 ChangePage[newx][newy] = ChangePage[x][y];
8411 ChangeCount[newx][newy] = ChangeCount[x][y];
8412 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8415 CustomValue[newx][newy] = CustomValue[x][y];
8417 ChangeDelay[x][y] = 0;
8418 ChangePage[x][y] = -1;
8419 ChangeCount[x][y] = 0;
8420 ChangeEvent[x][y] = -1;
8422 CustomValue[x][y] = 0;
8424 /* copy animation control values to new field */
8425 GfxFrame[newx][newy] = GfxFrame[x][y];
8426 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8427 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8428 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8430 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8432 /* some elements can leave other elements behind after moving */
8433 if (ei->move_leave_element != EL_EMPTY &&
8434 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8435 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8437 int move_leave_element = ei->move_leave_element;
8439 /* this makes it possible to leave the removed element again */
8440 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8441 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8443 Feld[x][y] = move_leave_element;
8445 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8446 MovDir[x][y] = direction;
8448 InitField(x, y, FALSE);
8450 if (GFX_CRUMBLED(Feld[x][y]))
8451 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8453 if (ELEM_IS_PLAYER(move_leave_element))
8454 RelocatePlayer(x, y, move_leave_element);
8457 /* do this after checking for left-behind element */
8458 ResetGfxAnimation(x, y); /* reset animation values for old field */
8460 if (!CAN_MOVE(element) ||
8461 (CAN_FALL(element) && direction == MV_DOWN &&
8462 (element == EL_SPRING ||
8463 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8464 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8465 GfxDir[x][y] = MovDir[newx][newy] = 0;
8467 TEST_DrawLevelField(x, y);
8468 TEST_DrawLevelField(newx, newy);
8470 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8472 /* prevent pushed element from moving on in pushed direction */
8473 if (pushed_by_player && CAN_MOVE(element) &&
8474 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8475 !(element_info[element].move_pattern & direction))
8476 TurnRound(newx, newy);
8478 /* prevent elements on conveyor belt from moving on in last direction */
8479 if (pushed_by_conveyor && CAN_FALL(element) &&
8480 direction & MV_HORIZONTAL)
8481 MovDir[newx][newy] = 0;
8483 if (!pushed_by_player)
8485 int nextx = newx + dx, nexty = newy + dy;
8486 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8488 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8490 if (CAN_FALL(element) && direction == MV_DOWN)
8491 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8493 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8494 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8496 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8497 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8500 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8502 TestIfBadThingTouchesPlayer(newx, newy);
8503 TestIfBadThingTouchesFriend(newx, newy);
8505 if (!IS_CUSTOM_ELEMENT(element))
8506 TestIfBadThingTouchesOtherBadThing(newx, newy);
8508 else if (element == EL_PENGUIN)
8509 TestIfFriendTouchesBadThing(newx, newy);
8511 if (DONT_GET_HIT_BY(element))
8513 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8516 /* give the player one last chance (one more frame) to move away */
8517 if (CAN_FALL(element) && direction == MV_DOWN &&
8518 (last_line || (!IS_FREE(x, newy + 1) &&
8519 (!IS_PLAYER(x, newy + 1) ||
8520 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8523 if (pushed_by_player && !game.use_change_when_pushing_bug)
8525 int push_side = MV_DIR_OPPOSITE(direction);
8526 struct PlayerInfo *player = PLAYERINFO(x, y);
8528 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8529 player->index_bit, push_side);
8530 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8531 player->index_bit, push_side);
8534 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8535 MovDelay[newx][newy] = 1;
8537 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8539 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8540 TestIfElementHitsCustomElement(newx, newy, direction);
8541 TestIfPlayerTouchesCustomElement(newx, newy);
8542 TestIfElementTouchesCustomElement(newx, newy);
8544 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8545 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8546 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8547 MV_DIR_OPPOSITE(direction));
8550 int AmoebeNachbarNr(int ax, int ay)
8553 int element = Feld[ax][ay];
8555 static int xy[4][2] =
8563 for (i = 0; i < NUM_DIRECTIONS; i++)
8565 int x = ax + xy[i][0];
8566 int y = ay + xy[i][1];
8568 if (!IN_LEV_FIELD(x, y))
8571 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8572 group_nr = AmoebaNr[x][y];
8578 void AmoebenVereinigen(int ax, int ay)
8580 int i, x, y, xx, yy;
8581 int new_group_nr = AmoebaNr[ax][ay];
8582 static int xy[4][2] =
8590 if (new_group_nr == 0)
8593 for (i = 0; i < NUM_DIRECTIONS; i++)
8598 if (!IN_LEV_FIELD(x, y))
8601 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8602 Feld[x][y] == EL_BD_AMOEBA ||
8603 Feld[x][y] == EL_AMOEBA_DEAD) &&
8604 AmoebaNr[x][y] != new_group_nr)
8606 int old_group_nr = AmoebaNr[x][y];
8608 if (old_group_nr == 0)
8611 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8612 AmoebaCnt[old_group_nr] = 0;
8613 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8614 AmoebaCnt2[old_group_nr] = 0;
8616 SCAN_PLAYFIELD(xx, yy)
8618 if (AmoebaNr[xx][yy] == old_group_nr)
8619 AmoebaNr[xx][yy] = new_group_nr;
8625 void AmoebeUmwandeln(int ax, int ay)
8629 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8631 int group_nr = AmoebaNr[ax][ay];
8636 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8637 printf("AmoebeUmwandeln(): This should never happen!\n");
8642 SCAN_PLAYFIELD(x, y)
8644 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8647 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8651 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8652 SND_AMOEBA_TURNING_TO_GEM :
8653 SND_AMOEBA_TURNING_TO_ROCK));
8658 static int xy[4][2] =
8666 for (i = 0; i < NUM_DIRECTIONS; i++)
8671 if (!IN_LEV_FIELD(x, y))
8674 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8676 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8677 SND_AMOEBA_TURNING_TO_GEM :
8678 SND_AMOEBA_TURNING_TO_ROCK));
8685 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8688 int group_nr = AmoebaNr[ax][ay];
8689 boolean done = FALSE;
8694 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8695 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8700 SCAN_PLAYFIELD(x, y)
8702 if (AmoebaNr[x][y] == group_nr &&
8703 (Feld[x][y] == EL_AMOEBA_DEAD ||
8704 Feld[x][y] == EL_BD_AMOEBA ||
8705 Feld[x][y] == EL_AMOEBA_GROWING))
8708 Feld[x][y] = new_element;
8709 InitField(x, y, FALSE);
8710 TEST_DrawLevelField(x, y);
8716 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8717 SND_BD_AMOEBA_TURNING_TO_ROCK :
8718 SND_BD_AMOEBA_TURNING_TO_GEM));
8721 void AmoebeWaechst(int x, int y)
8723 static unsigned int sound_delay = 0;
8724 static unsigned int sound_delay_value = 0;
8726 if (!MovDelay[x][y]) /* start new growing cycle */
8730 if (DelayReached(&sound_delay, sound_delay_value))
8732 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8733 sound_delay_value = 30;
8737 if (MovDelay[x][y]) /* wait some time before growing bigger */
8740 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8742 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8743 6 - MovDelay[x][y]);
8745 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8748 if (!MovDelay[x][y])
8750 Feld[x][y] = Store[x][y];
8752 TEST_DrawLevelField(x, y);
8757 void AmoebaDisappearing(int x, int y)
8759 static unsigned int sound_delay = 0;
8760 static unsigned int sound_delay_value = 0;
8762 if (!MovDelay[x][y]) /* start new shrinking cycle */
8766 if (DelayReached(&sound_delay, sound_delay_value))
8767 sound_delay_value = 30;
8770 if (MovDelay[x][y]) /* wait some time before shrinking */
8773 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8775 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8776 6 - MovDelay[x][y]);
8778 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8781 if (!MovDelay[x][y])
8783 Feld[x][y] = EL_EMPTY;
8784 TEST_DrawLevelField(x, y);
8786 /* don't let mole enter this field in this cycle;
8787 (give priority to objects falling to this field from above) */
8793 void AmoebeAbleger(int ax, int ay)
8796 int element = Feld[ax][ay];
8797 int graphic = el2img(element);
8798 int newax = ax, neway = ay;
8799 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8800 static int xy[4][2] =
8808 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8810 Feld[ax][ay] = EL_AMOEBA_DEAD;
8811 TEST_DrawLevelField(ax, ay);
8815 if (IS_ANIMATED(graphic))
8816 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8818 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8819 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8821 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8824 if (MovDelay[ax][ay])
8828 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8831 int x = ax + xy[start][0];
8832 int y = ay + xy[start][1];
8834 if (!IN_LEV_FIELD(x, y))
8837 if (IS_FREE(x, y) ||
8838 CAN_GROW_INTO(Feld[x][y]) ||
8839 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8846 if (newax == ax && neway == ay)
8849 else /* normal or "filled" (BD style) amoeba */
8852 boolean waiting_for_player = FALSE;
8854 for (i = 0; i < NUM_DIRECTIONS; i++)
8856 int j = (start + i) % 4;
8857 int x = ax + xy[j][0];
8858 int y = ay + xy[j][1];
8860 if (!IN_LEV_FIELD(x, y))
8863 if (IS_FREE(x, y) ||
8864 CAN_GROW_INTO(Feld[x][y]) ||
8865 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8866 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8872 else if (IS_PLAYER(x, y))
8873 waiting_for_player = TRUE;
8876 if (newax == ax && neway == ay) /* amoeba cannot grow */
8878 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8880 Feld[ax][ay] = EL_AMOEBA_DEAD;
8881 TEST_DrawLevelField(ax, ay);
8882 AmoebaCnt[AmoebaNr[ax][ay]]--;
8884 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8886 if (element == EL_AMOEBA_FULL)
8887 AmoebeUmwandeln(ax, ay);
8888 else if (element == EL_BD_AMOEBA)
8889 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8894 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8896 /* amoeba gets larger by growing in some direction */
8898 int new_group_nr = AmoebaNr[ax][ay];
8901 if (new_group_nr == 0)
8903 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8904 printf("AmoebeAbleger(): This should never happen!\n");
8909 AmoebaNr[newax][neway] = new_group_nr;
8910 AmoebaCnt[new_group_nr]++;
8911 AmoebaCnt2[new_group_nr]++;
8913 /* if amoeba touches other amoeba(s) after growing, unify them */
8914 AmoebenVereinigen(newax, neway);
8916 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8918 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8924 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8925 (neway == lev_fieldy - 1 && newax != ax))
8927 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8928 Store[newax][neway] = element;
8930 else if (neway == ay || element == EL_EMC_DRIPPER)
8932 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8934 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8938 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8939 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8940 Store[ax][ay] = EL_AMOEBA_DROP;
8941 ContinueMoving(ax, ay);
8945 TEST_DrawLevelField(newax, neway);
8948 void Life(int ax, int ay)
8952 int element = Feld[ax][ay];
8953 int graphic = el2img(element);
8954 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8956 boolean changed = FALSE;
8958 if (IS_ANIMATED(graphic))
8959 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8964 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8965 MovDelay[ax][ay] = life_time;
8967 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8970 if (MovDelay[ax][ay])
8974 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8976 int xx = ax+x1, yy = ay+y1;
8979 if (!IN_LEV_FIELD(xx, yy))
8982 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8984 int x = xx+x2, y = yy+y2;
8986 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8989 if (((Feld[x][y] == element ||
8990 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8992 (IS_FREE(x, y) && Stop[x][y]))
8996 if (xx == ax && yy == ay) /* field in the middle */
8998 if (nachbarn < life_parameter[0] ||
8999 nachbarn > life_parameter[1])
9001 Feld[xx][yy] = EL_EMPTY;
9003 TEST_DrawLevelField(xx, yy);
9004 Stop[xx][yy] = TRUE;
9008 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9009 { /* free border field */
9010 if (nachbarn >= life_parameter[2] &&
9011 nachbarn <= life_parameter[3])
9013 Feld[xx][yy] = element;
9014 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9016 TEST_DrawLevelField(xx, yy);
9017 Stop[xx][yy] = TRUE;
9024 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9025 SND_GAME_OF_LIFE_GROWING);
9028 static void InitRobotWheel(int x, int y)
9030 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9033 static void RunRobotWheel(int x, int y)
9035 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9038 static void StopRobotWheel(int x, int y)
9040 if (ZX == x && ZY == y)
9044 game.robot_wheel_active = FALSE;
9048 static void InitTimegateWheel(int x, int y)
9050 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9053 static void RunTimegateWheel(int x, int y)
9055 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9058 static void InitMagicBallDelay(int x, int y)
9060 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9063 static void ActivateMagicBall(int bx, int by)
9067 if (level.ball_random)
9069 int pos_border = RND(8); /* select one of the eight border elements */
9070 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9071 int xx = pos_content % 3;
9072 int yy = pos_content / 3;
9077 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9078 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9082 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9084 int xx = x - bx + 1;
9085 int yy = y - by + 1;
9087 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9088 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9092 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9095 void CheckExit(int x, int y)
9097 if (local_player->gems_still_needed > 0 ||
9098 local_player->sokobanfields_still_needed > 0 ||
9099 local_player->lights_still_needed > 0)
9101 int element = Feld[x][y];
9102 int graphic = el2img(element);
9104 if (IS_ANIMATED(graphic))
9105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9113 Feld[x][y] = EL_EXIT_OPENING;
9115 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9118 void CheckExitEM(int x, int y)
9120 if (local_player->gems_still_needed > 0 ||
9121 local_player->sokobanfields_still_needed > 0 ||
9122 local_player->lights_still_needed > 0)
9124 int element = Feld[x][y];
9125 int graphic = el2img(element);
9127 if (IS_ANIMATED(graphic))
9128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9133 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9136 Feld[x][y] = EL_EM_EXIT_OPENING;
9138 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9141 void CheckExitSteel(int x, int y)
9143 if (local_player->gems_still_needed > 0 ||
9144 local_player->sokobanfields_still_needed > 0 ||
9145 local_player->lights_still_needed > 0)
9147 int element = Feld[x][y];
9148 int graphic = el2img(element);
9150 if (IS_ANIMATED(graphic))
9151 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9159 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9161 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9164 void CheckExitSteelEM(int x, int y)
9166 if (local_player->gems_still_needed > 0 ||
9167 local_player->sokobanfields_still_needed > 0 ||
9168 local_player->lights_still_needed > 0)
9170 int element = Feld[x][y];
9171 int graphic = el2img(element);
9173 if (IS_ANIMATED(graphic))
9174 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9179 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9182 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9184 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9187 void CheckExitSP(int x, int y)
9189 if (local_player->gems_still_needed > 0)
9191 int element = Feld[x][y];
9192 int graphic = el2img(element);
9194 if (IS_ANIMATED(graphic))
9195 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9200 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9203 Feld[x][y] = EL_SP_EXIT_OPENING;
9205 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9208 static void CloseAllOpenTimegates()
9212 SCAN_PLAYFIELD(x, y)
9214 int element = Feld[x][y];
9216 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9218 Feld[x][y] = EL_TIMEGATE_CLOSING;
9220 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9225 void DrawTwinkleOnField(int x, int y)
9227 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9230 if (Feld[x][y] == EL_BD_DIAMOND)
9233 if (MovDelay[x][y] == 0) /* next animation frame */
9234 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9236 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9240 DrawLevelElementAnimation(x, y, Feld[x][y]);
9242 if (MovDelay[x][y] != 0)
9244 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9245 10 - MovDelay[x][y]);
9247 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9252 void MauerWaechst(int x, int y)
9256 if (!MovDelay[x][y]) /* next animation frame */
9257 MovDelay[x][y] = 3 * delay;
9259 if (MovDelay[x][y]) /* wait some time before next frame */
9263 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9265 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9266 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9268 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9271 if (!MovDelay[x][y])
9273 if (MovDir[x][y] == MV_LEFT)
9275 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9276 TEST_DrawLevelField(x - 1, y);
9278 else if (MovDir[x][y] == MV_RIGHT)
9280 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9281 TEST_DrawLevelField(x + 1, y);
9283 else if (MovDir[x][y] == MV_UP)
9285 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9286 TEST_DrawLevelField(x, y - 1);
9290 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9291 TEST_DrawLevelField(x, y + 1);
9294 Feld[x][y] = Store[x][y];
9296 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9297 TEST_DrawLevelField(x, y);
9302 void MauerAbleger(int ax, int ay)
9304 int element = Feld[ax][ay];
9305 int graphic = el2img(element);
9306 boolean oben_frei = FALSE, unten_frei = FALSE;
9307 boolean links_frei = FALSE, rechts_frei = FALSE;
9308 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9309 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9310 boolean new_wall = FALSE;
9312 if (IS_ANIMATED(graphic))
9313 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9315 if (!MovDelay[ax][ay]) /* start building new wall */
9316 MovDelay[ax][ay] = 6;
9318 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9321 if (MovDelay[ax][ay])
9325 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9327 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9329 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9331 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9334 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9335 element == EL_EXPANDABLE_WALL_ANY)
9339 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9340 Store[ax][ay-1] = element;
9341 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9342 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9343 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9344 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9349 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9350 Store[ax][ay+1] = element;
9351 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9352 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9353 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9354 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9359 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9360 element == EL_EXPANDABLE_WALL_ANY ||
9361 element == EL_EXPANDABLE_WALL ||
9362 element == EL_BD_EXPANDABLE_WALL)
9366 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9367 Store[ax-1][ay] = element;
9368 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9369 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9370 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9371 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9377 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9378 Store[ax+1][ay] = element;
9379 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9380 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9381 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9382 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9387 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9388 TEST_DrawLevelField(ax, ay);
9390 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9392 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9393 unten_massiv = TRUE;
9394 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9395 links_massiv = TRUE;
9396 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9397 rechts_massiv = TRUE;
9399 if (((oben_massiv && unten_massiv) ||
9400 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9401 element == EL_EXPANDABLE_WALL) &&
9402 ((links_massiv && rechts_massiv) ||
9403 element == EL_EXPANDABLE_WALL_VERTICAL))
9404 Feld[ax][ay] = EL_WALL;
9407 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9410 void MauerAblegerStahl(int ax, int ay)
9412 int element = Feld[ax][ay];
9413 int graphic = el2img(element);
9414 boolean oben_frei = FALSE, unten_frei = FALSE;
9415 boolean links_frei = FALSE, rechts_frei = FALSE;
9416 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9417 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9418 boolean new_wall = FALSE;
9420 if (IS_ANIMATED(graphic))
9421 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9423 if (!MovDelay[ax][ay]) /* start building new wall */
9424 MovDelay[ax][ay] = 6;
9426 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9429 if (MovDelay[ax][ay])
9433 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9435 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9437 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9439 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9442 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9443 element == EL_EXPANDABLE_STEELWALL_ANY)
9447 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9448 Store[ax][ay-1] = element;
9449 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9450 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9451 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9452 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9457 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9458 Store[ax][ay+1] = element;
9459 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9460 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9461 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9462 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9467 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9468 element == EL_EXPANDABLE_STEELWALL_ANY)
9472 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9473 Store[ax-1][ay] = element;
9474 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9475 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9476 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9477 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9483 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9484 Store[ax+1][ay] = element;
9485 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9486 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9487 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9488 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9493 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9495 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9496 unten_massiv = TRUE;
9497 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9498 links_massiv = TRUE;
9499 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9500 rechts_massiv = TRUE;
9502 if (((oben_massiv && unten_massiv) ||
9503 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9504 ((links_massiv && rechts_massiv) ||
9505 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9506 Feld[ax][ay] = EL_STEELWALL;
9509 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9512 void CheckForDragon(int x, int y)
9515 boolean dragon_found = FALSE;
9516 static int xy[4][2] =
9524 for (i = 0; i < NUM_DIRECTIONS; i++)
9526 for (j = 0; j < 4; j++)
9528 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9530 if (IN_LEV_FIELD(xx, yy) &&
9531 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9533 if (Feld[xx][yy] == EL_DRAGON)
9534 dragon_found = TRUE;
9543 for (i = 0; i < NUM_DIRECTIONS; i++)
9545 for (j = 0; j < 3; j++)
9547 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9549 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9551 Feld[xx][yy] = EL_EMPTY;
9552 TEST_DrawLevelField(xx, yy);
9561 static void InitBuggyBase(int x, int y)
9563 int element = Feld[x][y];
9564 int activating_delay = FRAMES_PER_SECOND / 4;
9567 (element == EL_SP_BUGGY_BASE ?
9568 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9569 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9571 element == EL_SP_BUGGY_BASE_ACTIVE ?
9572 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9575 static void WarnBuggyBase(int x, int y)
9578 static int xy[4][2] =
9586 for (i = 0; i < NUM_DIRECTIONS; i++)
9588 int xx = x + xy[i][0];
9589 int yy = y + xy[i][1];
9591 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9593 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9600 static void InitTrap(int x, int y)
9602 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9605 static void ActivateTrap(int x, int y)
9607 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9610 static void ChangeActiveTrap(int x, int y)
9612 int graphic = IMG_TRAP_ACTIVE;
9614 /* if new animation frame was drawn, correct crumbled sand border */
9615 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9616 TEST_DrawLevelFieldCrumbled(x, y);
9619 static int getSpecialActionElement(int element, int number, int base_element)
9621 return (element != EL_EMPTY ? element :
9622 number != -1 ? base_element + number - 1 :
9626 static int getModifiedActionNumber(int value_old, int operator, int operand,
9627 int value_min, int value_max)
9629 int value_new = (operator == CA_MODE_SET ? operand :
9630 operator == CA_MODE_ADD ? value_old + operand :
9631 operator == CA_MODE_SUBTRACT ? value_old - operand :
9632 operator == CA_MODE_MULTIPLY ? value_old * operand :
9633 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9634 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9637 return (value_new < value_min ? value_min :
9638 value_new > value_max ? value_max :
9642 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9644 struct ElementInfo *ei = &element_info[element];
9645 struct ElementChangeInfo *change = &ei->change_page[page];
9646 int target_element = change->target_element;
9647 int action_type = change->action_type;
9648 int action_mode = change->action_mode;
9649 int action_arg = change->action_arg;
9650 int action_element = change->action_element;
9653 if (!change->has_action)
9656 /* ---------- determine action paramater values -------------------------- */
9658 int level_time_value =
9659 (level.time > 0 ? TimeLeft :
9662 int action_arg_element_raw =
9663 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9664 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9665 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9666 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9667 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9668 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9669 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9671 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9673 int action_arg_direction =
9674 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9675 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9676 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9677 change->actual_trigger_side :
9678 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9679 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9682 int action_arg_number_min =
9683 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9686 int action_arg_number_max =
9687 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9688 action_type == CA_SET_LEVEL_GEMS ? 999 :
9689 action_type == CA_SET_LEVEL_TIME ? 9999 :
9690 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9691 action_type == CA_SET_CE_VALUE ? 9999 :
9692 action_type == CA_SET_CE_SCORE ? 9999 :
9695 int action_arg_number_reset =
9696 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9697 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9698 action_type == CA_SET_LEVEL_TIME ? level.time :
9699 action_type == CA_SET_LEVEL_SCORE ? 0 :
9700 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9701 action_type == CA_SET_CE_SCORE ? 0 :
9704 int action_arg_number =
9705 (action_arg <= CA_ARG_MAX ? action_arg :
9706 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9707 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9708 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9709 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9710 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9711 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9712 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9713 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9714 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9715 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9716 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9717 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9718 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9719 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9720 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9721 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9722 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9723 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9724 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9725 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9726 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9729 int action_arg_number_old =
9730 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9731 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9732 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9733 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9734 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9737 int action_arg_number_new =
9738 getModifiedActionNumber(action_arg_number_old,
9739 action_mode, action_arg_number,
9740 action_arg_number_min, action_arg_number_max);
9742 int trigger_player_bits =
9743 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9744 change->actual_trigger_player_bits : change->trigger_player);
9746 int action_arg_player_bits =
9747 (action_arg >= CA_ARG_PLAYER_1 &&
9748 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9749 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9750 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9753 /* ---------- execute action -------------------------------------------- */
9755 switch (action_type)
9762 /* ---------- level actions ------------------------------------------- */
9764 case CA_RESTART_LEVEL:
9766 game.restart_level = TRUE;
9771 case CA_SHOW_ENVELOPE:
9773 int element = getSpecialActionElement(action_arg_element,
9774 action_arg_number, EL_ENVELOPE_1);
9776 if (IS_ENVELOPE(element))
9777 local_player->show_envelope = element;
9782 case CA_SET_LEVEL_TIME:
9784 if (level.time > 0) /* only modify limited time value */
9786 TimeLeft = action_arg_number_new;
9788 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9790 DisplayGameControlValues();
9792 if (!TimeLeft && setup.time_limit)
9793 for (i = 0; i < MAX_PLAYERS; i++)
9794 KillPlayer(&stored_player[i]);
9800 case CA_SET_LEVEL_SCORE:
9802 local_player->score = action_arg_number_new;
9804 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9806 DisplayGameControlValues();
9811 case CA_SET_LEVEL_GEMS:
9813 local_player->gems_still_needed = action_arg_number_new;
9815 game.snapshot.collected_item = TRUE;
9817 game_panel_controls[GAME_PANEL_GEMS].value =
9818 local_player->gems_still_needed;
9820 DisplayGameControlValues();
9825 case CA_SET_LEVEL_WIND:
9827 game.wind_direction = action_arg_direction;
9832 case CA_SET_LEVEL_RANDOM_SEED:
9834 /* ensure that setting a new random seed while playing is predictable */
9835 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9840 /* ---------- player actions ------------------------------------------ */
9842 case CA_MOVE_PLAYER:
9844 /* automatically move to the next field in specified direction */
9845 for (i = 0; i < MAX_PLAYERS; i++)
9846 if (trigger_player_bits & (1 << i))
9847 stored_player[i].programmed_action = action_arg_direction;
9852 case CA_EXIT_PLAYER:
9854 for (i = 0; i < MAX_PLAYERS; i++)
9855 if (action_arg_player_bits & (1 << i))
9856 PlayerWins(&stored_player[i]);
9861 case CA_KILL_PLAYER:
9863 for (i = 0; i < MAX_PLAYERS; i++)
9864 if (action_arg_player_bits & (1 << i))
9865 KillPlayer(&stored_player[i]);
9870 case CA_SET_PLAYER_KEYS:
9872 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9873 int element = getSpecialActionElement(action_arg_element,
9874 action_arg_number, EL_KEY_1);
9876 if (IS_KEY(element))
9878 for (i = 0; i < MAX_PLAYERS; i++)
9880 if (trigger_player_bits & (1 << i))
9882 stored_player[i].key[KEY_NR(element)] = key_state;
9884 DrawGameDoorValues();
9892 case CA_SET_PLAYER_SPEED:
9894 for (i = 0; i < MAX_PLAYERS; i++)
9896 if (trigger_player_bits & (1 << i))
9898 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9900 if (action_arg == CA_ARG_SPEED_FASTER &&
9901 stored_player[i].cannot_move)
9903 action_arg_number = STEPSIZE_VERY_SLOW;
9905 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9906 action_arg == CA_ARG_SPEED_FASTER)
9908 action_arg_number = 2;
9909 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9912 else if (action_arg == CA_ARG_NUMBER_RESET)
9914 action_arg_number = level.initial_player_stepsize[i];
9918 getModifiedActionNumber(move_stepsize,
9921 action_arg_number_min,
9922 action_arg_number_max);
9924 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9931 case CA_SET_PLAYER_SHIELD:
9933 for (i = 0; i < MAX_PLAYERS; i++)
9935 if (trigger_player_bits & (1 << i))
9937 if (action_arg == CA_ARG_SHIELD_OFF)
9939 stored_player[i].shield_normal_time_left = 0;
9940 stored_player[i].shield_deadly_time_left = 0;
9942 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9944 stored_player[i].shield_normal_time_left = 999999;
9946 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9948 stored_player[i].shield_normal_time_left = 999999;
9949 stored_player[i].shield_deadly_time_left = 999999;
9957 case CA_SET_PLAYER_GRAVITY:
9959 for (i = 0; i < MAX_PLAYERS; i++)
9961 if (trigger_player_bits & (1 << i))
9963 stored_player[i].gravity =
9964 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9965 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9966 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9967 stored_player[i].gravity);
9974 case CA_SET_PLAYER_ARTWORK:
9976 for (i = 0; i < MAX_PLAYERS; i++)
9978 if (trigger_player_bits & (1 << i))
9980 int artwork_element = action_arg_element;
9982 if (action_arg == CA_ARG_ELEMENT_RESET)
9984 (level.use_artwork_element[i] ? level.artwork_element[i] :
9985 stored_player[i].element_nr);
9987 if (stored_player[i].artwork_element != artwork_element)
9988 stored_player[i].Frame = 0;
9990 stored_player[i].artwork_element = artwork_element;
9992 SetPlayerWaiting(&stored_player[i], FALSE);
9994 /* set number of special actions for bored and sleeping animation */
9995 stored_player[i].num_special_action_bored =
9996 get_num_special_action(artwork_element,
9997 ACTION_BORING_1, ACTION_BORING_LAST);
9998 stored_player[i].num_special_action_sleeping =
9999 get_num_special_action(artwork_element,
10000 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10007 case CA_SET_PLAYER_INVENTORY:
10009 for (i = 0; i < MAX_PLAYERS; i++)
10011 struct PlayerInfo *player = &stored_player[i];
10014 if (trigger_player_bits & (1 << i))
10016 int inventory_element = action_arg_element;
10018 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10019 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10020 action_arg == CA_ARG_ELEMENT_ACTION)
10022 int element = inventory_element;
10023 int collect_count = element_info[element].collect_count_initial;
10025 if (!IS_CUSTOM_ELEMENT(element))
10028 if (collect_count == 0)
10029 player->inventory_infinite_element = element;
10031 for (k = 0; k < collect_count; k++)
10032 if (player->inventory_size < MAX_INVENTORY_SIZE)
10033 player->inventory_element[player->inventory_size++] =
10036 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10037 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10038 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10040 if (player->inventory_infinite_element != EL_UNDEFINED &&
10041 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10042 action_arg_element_raw))
10043 player->inventory_infinite_element = EL_UNDEFINED;
10045 for (k = 0, j = 0; j < player->inventory_size; j++)
10047 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10048 action_arg_element_raw))
10049 player->inventory_element[k++] = player->inventory_element[j];
10052 player->inventory_size = k;
10054 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10056 if (player->inventory_size > 0)
10058 for (j = 0; j < player->inventory_size - 1; j++)
10059 player->inventory_element[j] = player->inventory_element[j + 1];
10061 player->inventory_size--;
10064 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10066 if (player->inventory_size > 0)
10067 player->inventory_size--;
10069 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10071 player->inventory_infinite_element = EL_UNDEFINED;
10072 player->inventory_size = 0;
10074 else if (action_arg == CA_ARG_INVENTORY_RESET)
10076 player->inventory_infinite_element = EL_UNDEFINED;
10077 player->inventory_size = 0;
10079 if (level.use_initial_inventory[i])
10081 for (j = 0; j < level.initial_inventory_size[i]; j++)
10083 int element = level.initial_inventory_content[i][j];
10084 int collect_count = element_info[element].collect_count_initial;
10086 if (!IS_CUSTOM_ELEMENT(element))
10089 if (collect_count == 0)
10090 player->inventory_infinite_element = element;
10092 for (k = 0; k < collect_count; k++)
10093 if (player->inventory_size < MAX_INVENTORY_SIZE)
10094 player->inventory_element[player->inventory_size++] =
10105 /* ---------- CE actions ---------------------------------------------- */
10107 case CA_SET_CE_VALUE:
10109 int last_ce_value = CustomValue[x][y];
10111 CustomValue[x][y] = action_arg_number_new;
10113 if (CustomValue[x][y] != last_ce_value)
10115 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10116 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10118 if (CustomValue[x][y] == 0)
10120 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10121 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10128 case CA_SET_CE_SCORE:
10130 int last_ce_score = ei->collect_score;
10132 ei->collect_score = action_arg_number_new;
10134 if (ei->collect_score != last_ce_score)
10136 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10137 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10139 if (ei->collect_score == 0)
10143 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10144 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10147 This is a very special case that seems to be a mixture between
10148 CheckElementChange() and CheckTriggeredElementChange(): while
10149 the first one only affects single elements that are triggered
10150 directly, the second one affects multiple elements in the playfield
10151 that are triggered indirectly by another element. This is a third
10152 case: Changing the CE score always affects multiple identical CEs,
10153 so every affected CE must be checked, not only the single CE for
10154 which the CE score was changed in the first place (as every instance
10155 of that CE shares the same CE score, and therefore also can change)!
10157 SCAN_PLAYFIELD(xx, yy)
10159 if (Feld[xx][yy] == element)
10160 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10161 CE_SCORE_GETS_ZERO);
10169 case CA_SET_CE_ARTWORK:
10171 int artwork_element = action_arg_element;
10172 boolean reset_frame = FALSE;
10175 if (action_arg == CA_ARG_ELEMENT_RESET)
10176 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10179 if (ei->gfx_element != artwork_element)
10180 reset_frame = TRUE;
10182 ei->gfx_element = artwork_element;
10184 SCAN_PLAYFIELD(xx, yy)
10186 if (Feld[xx][yy] == element)
10190 ResetGfxAnimation(xx, yy);
10191 ResetRandomAnimationValue(xx, yy);
10194 TEST_DrawLevelField(xx, yy);
10201 /* ---------- engine actions ------------------------------------------ */
10203 case CA_SET_ENGINE_SCAN_MODE:
10205 InitPlayfieldScanMode(action_arg);
10215 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10217 int old_element = Feld[x][y];
10218 int new_element = GetElementFromGroupElement(element);
10219 int previous_move_direction = MovDir[x][y];
10220 int last_ce_value = CustomValue[x][y];
10221 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10222 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10223 boolean add_player_onto_element = (new_element_is_player &&
10224 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10225 IS_WALKABLE(old_element));
10227 if (!add_player_onto_element)
10229 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10230 RemoveMovingField(x, y);
10234 Feld[x][y] = new_element;
10236 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10237 MovDir[x][y] = previous_move_direction;
10239 if (element_info[new_element].use_last_ce_value)
10240 CustomValue[x][y] = last_ce_value;
10242 InitField_WithBug1(x, y, FALSE);
10244 new_element = Feld[x][y]; /* element may have changed */
10246 ResetGfxAnimation(x, y);
10247 ResetRandomAnimationValue(x, y);
10249 TEST_DrawLevelField(x, y);
10251 if (GFX_CRUMBLED(new_element))
10252 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10255 /* check if element under the player changes from accessible to unaccessible
10256 (needed for special case of dropping element which then changes) */
10257 /* (must be checked after creating new element for walkable group elements) */
10258 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10259 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10266 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10267 if (new_element_is_player)
10268 RelocatePlayer(x, y, new_element);
10271 ChangeCount[x][y]++; /* count number of changes in the same frame */
10273 TestIfBadThingTouchesPlayer(x, y);
10274 TestIfPlayerTouchesCustomElement(x, y);
10275 TestIfElementTouchesCustomElement(x, y);
10278 static void CreateField(int x, int y, int element)
10280 CreateFieldExt(x, y, element, FALSE);
10283 static void CreateElementFromChange(int x, int y, int element)
10285 element = GET_VALID_RUNTIME_ELEMENT(element);
10287 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10289 int old_element = Feld[x][y];
10291 /* prevent changed element from moving in same engine frame
10292 unless both old and new element can either fall or move */
10293 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10294 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10298 CreateFieldExt(x, y, element, TRUE);
10301 static boolean ChangeElement(int x, int y, int element, int page)
10303 struct ElementInfo *ei = &element_info[element];
10304 struct ElementChangeInfo *change = &ei->change_page[page];
10305 int ce_value = CustomValue[x][y];
10306 int ce_score = ei->collect_score;
10307 int target_element;
10308 int old_element = Feld[x][y];
10310 /* always use default change event to prevent running into a loop */
10311 if (ChangeEvent[x][y] == -1)
10312 ChangeEvent[x][y] = CE_DELAY;
10314 if (ChangeEvent[x][y] == CE_DELAY)
10316 /* reset actual trigger element, trigger player and action element */
10317 change->actual_trigger_element = EL_EMPTY;
10318 change->actual_trigger_player = EL_EMPTY;
10319 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10320 change->actual_trigger_side = CH_SIDE_NONE;
10321 change->actual_trigger_ce_value = 0;
10322 change->actual_trigger_ce_score = 0;
10325 /* do not change elements more than a specified maximum number of changes */
10326 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10329 ChangeCount[x][y]++; /* count number of changes in the same frame */
10331 if (change->explode)
10338 if (change->use_target_content)
10340 boolean complete_replace = TRUE;
10341 boolean can_replace[3][3];
10344 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10347 boolean is_walkable;
10348 boolean is_diggable;
10349 boolean is_collectible;
10350 boolean is_removable;
10351 boolean is_destructible;
10352 int ex = x + xx - 1;
10353 int ey = y + yy - 1;
10354 int content_element = change->target_content.e[xx][yy];
10357 can_replace[xx][yy] = TRUE;
10359 if (ex == x && ey == y) /* do not check changing element itself */
10362 if (content_element == EL_EMPTY_SPACE)
10364 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10369 if (!IN_LEV_FIELD(ex, ey))
10371 can_replace[xx][yy] = FALSE;
10372 complete_replace = FALSE;
10379 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10380 e = MovingOrBlocked2Element(ex, ey);
10382 is_empty = (IS_FREE(ex, ey) ||
10383 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10385 is_walkable = (is_empty || IS_WALKABLE(e));
10386 is_diggable = (is_empty || IS_DIGGABLE(e));
10387 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10388 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10389 is_removable = (is_diggable || is_collectible);
10391 can_replace[xx][yy] =
10392 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10393 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10394 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10395 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10396 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10397 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10398 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10400 if (!can_replace[xx][yy])
10401 complete_replace = FALSE;
10404 if (!change->only_if_complete || complete_replace)
10406 boolean something_has_changed = FALSE;
10408 if (change->only_if_complete && change->use_random_replace &&
10409 RND(100) < change->random_percentage)
10412 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10414 int ex = x + xx - 1;
10415 int ey = y + yy - 1;
10416 int content_element;
10418 if (can_replace[xx][yy] && (!change->use_random_replace ||
10419 RND(100) < change->random_percentage))
10421 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10422 RemoveMovingField(ex, ey);
10424 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10426 content_element = change->target_content.e[xx][yy];
10427 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10428 ce_value, ce_score);
10430 CreateElementFromChange(ex, ey, target_element);
10432 something_has_changed = TRUE;
10434 /* for symmetry reasons, freeze newly created border elements */
10435 if (ex != x || ey != y)
10436 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10440 if (something_has_changed)
10442 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10443 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10449 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10450 ce_value, ce_score);
10452 if (element == EL_DIAGONAL_GROWING ||
10453 element == EL_DIAGONAL_SHRINKING)
10455 target_element = Store[x][y];
10457 Store[x][y] = EL_EMPTY;
10460 CreateElementFromChange(x, y, target_element);
10462 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10463 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10466 /* this uses direct change before indirect change */
10467 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10472 static void HandleElementChange(int x, int y, int page)
10474 int element = MovingOrBlocked2Element(x, y);
10475 struct ElementInfo *ei = &element_info[element];
10476 struct ElementChangeInfo *change = &ei->change_page[page];
10477 boolean handle_action_before_change = FALSE;
10480 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10481 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10484 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10485 x, y, element, element_info[element].token_name);
10486 printf("HandleElementChange(): This should never happen!\n");
10491 /* this can happen with classic bombs on walkable, changing elements */
10492 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10497 if (ChangeDelay[x][y] == 0) /* initialize element change */
10499 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10501 if (change->can_change)
10503 /* !!! not clear why graphic animation should be reset at all here !!! */
10504 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10505 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10508 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10510 When using an animation frame delay of 1 (this only happens with
10511 "sp_zonk.moving.left/right" in the classic graphics), the default
10512 (non-moving) animation shows wrong animation frames (while the
10513 moving animation, like "sp_zonk.moving.left/right", is correct,
10514 so this graphical bug never shows up with the classic graphics).
10515 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10516 be drawn instead of the correct frames 0,1,2,3. This is caused by
10517 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10518 an element change: First when the change delay ("ChangeDelay[][]")
10519 counter has reached zero after decrementing, then a second time in
10520 the next frame (after "GfxFrame[][]" was already incremented) when
10521 "ChangeDelay[][]" is reset to the initial delay value again.
10523 This causes frame 0 to be drawn twice, while the last frame won't
10524 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10526 As some animations may already be cleverly designed around this bug
10527 (at least the "Snake Bite" snake tail animation does this), it cannot
10528 simply be fixed here without breaking such existing animations.
10529 Unfortunately, it cannot easily be detected if a graphics set was
10530 designed "before" or "after" the bug was fixed. As a workaround,
10531 a new graphics set option "game.graphics_engine_version" was added
10532 to be able to specify the game's major release version for which the
10533 graphics set was designed, which can then be used to decide if the
10534 bugfix should be used (version 4 and above) or not (version 3 or
10535 below, or if no version was specified at all, as with old sets).
10537 (The wrong/fixed animation frames can be tested with the test level set
10538 "test_gfxframe" and level "000", which contains a specially prepared
10539 custom element at level position (x/y) == (11/9) which uses the zonk
10540 animation mentioned above. Using "game.graphics_engine_version: 4"
10541 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10542 This can also be seen from the debug output for this test element.)
10545 /* when a custom element is about to change (for example by change delay),
10546 do not reset graphic animation when the custom element is moving */
10547 if (game.graphics_engine_version < 4 &&
10550 ResetGfxAnimation(x, y);
10551 ResetRandomAnimationValue(x, y);
10554 if (change->pre_change_function)
10555 change->pre_change_function(x, y);
10559 ChangeDelay[x][y]--;
10561 if (ChangeDelay[x][y] != 0) /* continue element change */
10563 if (change->can_change)
10565 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10567 if (IS_ANIMATED(graphic))
10568 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10570 if (change->change_function)
10571 change->change_function(x, y);
10574 else /* finish element change */
10576 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10578 page = ChangePage[x][y];
10579 ChangePage[x][y] = -1;
10581 change = &ei->change_page[page];
10584 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10586 ChangeDelay[x][y] = 1; /* try change after next move step */
10587 ChangePage[x][y] = page; /* remember page to use for change */
10592 /* special case: set new level random seed before changing element */
10593 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10594 handle_action_before_change = TRUE;
10596 if (change->has_action && handle_action_before_change)
10597 ExecuteCustomElementAction(x, y, element, page);
10599 if (change->can_change)
10601 if (ChangeElement(x, y, element, page))
10603 if (change->post_change_function)
10604 change->post_change_function(x, y);
10608 if (change->has_action && !handle_action_before_change)
10609 ExecuteCustomElementAction(x, y, element, page);
10613 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10614 int trigger_element,
10616 int trigger_player,
10620 boolean change_done_any = FALSE;
10621 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10624 if (!(trigger_events[trigger_element][trigger_event]))
10627 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10629 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10631 int element = EL_CUSTOM_START + i;
10632 boolean change_done = FALSE;
10635 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10636 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10639 for (p = 0; p < element_info[element].num_change_pages; p++)
10641 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10643 if (change->can_change_or_has_action &&
10644 change->has_event[trigger_event] &&
10645 change->trigger_side & trigger_side &&
10646 change->trigger_player & trigger_player &&
10647 change->trigger_page & trigger_page_bits &&
10648 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10650 change->actual_trigger_element = trigger_element;
10651 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10652 change->actual_trigger_player_bits = trigger_player;
10653 change->actual_trigger_side = trigger_side;
10654 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10655 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10657 if ((change->can_change && !change_done) || change->has_action)
10661 SCAN_PLAYFIELD(x, y)
10663 if (Feld[x][y] == element)
10665 if (change->can_change && !change_done)
10667 /* if element already changed in this frame, not only prevent
10668 another element change (checked in ChangeElement()), but
10669 also prevent additional element actions for this element */
10671 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10672 !level.use_action_after_change_bug)
10675 ChangeDelay[x][y] = 1;
10676 ChangeEvent[x][y] = trigger_event;
10678 HandleElementChange(x, y, p);
10680 else if (change->has_action)
10682 /* if element already changed in this frame, not only prevent
10683 another element change (checked in ChangeElement()), but
10684 also prevent additional element actions for this element */
10686 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10687 !level.use_action_after_change_bug)
10690 ExecuteCustomElementAction(x, y, element, p);
10691 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10696 if (change->can_change)
10698 change_done = TRUE;
10699 change_done_any = TRUE;
10706 RECURSION_LOOP_DETECTION_END();
10708 return change_done_any;
10711 static boolean CheckElementChangeExt(int x, int y,
10713 int trigger_element,
10715 int trigger_player,
10718 boolean change_done = FALSE;
10721 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10722 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10725 if (Feld[x][y] == EL_BLOCKED)
10727 Blocked2Moving(x, y, &x, &y);
10728 element = Feld[x][y];
10731 /* check if element has already changed or is about to change after moving */
10732 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10733 Feld[x][y] != element) ||
10735 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10736 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10737 ChangePage[x][y] != -1)))
10740 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10742 for (p = 0; p < element_info[element].num_change_pages; p++)
10744 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10746 /* check trigger element for all events where the element that is checked
10747 for changing interacts with a directly adjacent element -- this is
10748 different to element changes that affect other elements to change on the
10749 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10750 boolean check_trigger_element =
10751 (trigger_event == CE_TOUCHING_X ||
10752 trigger_event == CE_HITTING_X ||
10753 trigger_event == CE_HIT_BY_X ||
10754 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10756 if (change->can_change_or_has_action &&
10757 change->has_event[trigger_event] &&
10758 change->trigger_side & trigger_side &&
10759 change->trigger_player & trigger_player &&
10760 (!check_trigger_element ||
10761 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10763 change->actual_trigger_element = trigger_element;
10764 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10765 change->actual_trigger_player_bits = trigger_player;
10766 change->actual_trigger_side = trigger_side;
10767 change->actual_trigger_ce_value = CustomValue[x][y];
10768 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10770 /* special case: trigger element not at (x,y) position for some events */
10771 if (check_trigger_element)
10783 { 0, 0 }, { 0, 0 }, { 0, 0 },
10787 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10788 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10790 change->actual_trigger_ce_value = CustomValue[xx][yy];
10791 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10794 if (change->can_change && !change_done)
10796 ChangeDelay[x][y] = 1;
10797 ChangeEvent[x][y] = trigger_event;
10799 HandleElementChange(x, y, p);
10801 change_done = TRUE;
10803 else if (change->has_action)
10805 ExecuteCustomElementAction(x, y, element, p);
10806 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10811 RECURSION_LOOP_DETECTION_END();
10813 return change_done;
10816 static void PlayPlayerSound(struct PlayerInfo *player)
10818 int jx = player->jx, jy = player->jy;
10819 int sound_element = player->artwork_element;
10820 int last_action = player->last_action_waiting;
10821 int action = player->action_waiting;
10823 if (player->is_waiting)
10825 if (action != last_action)
10826 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10828 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10832 if (action != last_action)
10833 StopSound(element_info[sound_element].sound[last_action]);
10835 if (last_action == ACTION_SLEEPING)
10836 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10840 static void PlayAllPlayersSound()
10844 for (i = 0; i < MAX_PLAYERS; i++)
10845 if (stored_player[i].active)
10846 PlayPlayerSound(&stored_player[i]);
10849 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10851 boolean last_waiting = player->is_waiting;
10852 int move_dir = player->MovDir;
10854 player->dir_waiting = move_dir;
10855 player->last_action_waiting = player->action_waiting;
10859 if (!last_waiting) /* not waiting -> waiting */
10861 player->is_waiting = TRUE;
10863 player->frame_counter_bored =
10865 game.player_boring_delay_fixed +
10866 GetSimpleRandom(game.player_boring_delay_random);
10867 player->frame_counter_sleeping =
10869 game.player_sleeping_delay_fixed +
10870 GetSimpleRandom(game.player_sleeping_delay_random);
10872 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10875 if (game.player_sleeping_delay_fixed +
10876 game.player_sleeping_delay_random > 0 &&
10877 player->anim_delay_counter == 0 &&
10878 player->post_delay_counter == 0 &&
10879 FrameCounter >= player->frame_counter_sleeping)
10880 player->is_sleeping = TRUE;
10881 else if (game.player_boring_delay_fixed +
10882 game.player_boring_delay_random > 0 &&
10883 FrameCounter >= player->frame_counter_bored)
10884 player->is_bored = TRUE;
10886 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10887 player->is_bored ? ACTION_BORING :
10890 if (player->is_sleeping && player->use_murphy)
10892 /* special case for sleeping Murphy when leaning against non-free tile */
10894 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10895 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10896 !IS_MOVING(player->jx - 1, player->jy)))
10897 move_dir = MV_LEFT;
10898 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10899 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10900 !IS_MOVING(player->jx + 1, player->jy)))
10901 move_dir = MV_RIGHT;
10903 player->is_sleeping = FALSE;
10905 player->dir_waiting = move_dir;
10908 if (player->is_sleeping)
10910 if (player->num_special_action_sleeping > 0)
10912 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10914 int last_special_action = player->special_action_sleeping;
10915 int num_special_action = player->num_special_action_sleeping;
10916 int special_action =
10917 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10918 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10919 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10920 last_special_action + 1 : ACTION_SLEEPING);
10921 int special_graphic =
10922 el_act_dir2img(player->artwork_element, special_action, move_dir);
10924 player->anim_delay_counter =
10925 graphic_info[special_graphic].anim_delay_fixed +
10926 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10927 player->post_delay_counter =
10928 graphic_info[special_graphic].post_delay_fixed +
10929 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10931 player->special_action_sleeping = special_action;
10934 if (player->anim_delay_counter > 0)
10936 player->action_waiting = player->special_action_sleeping;
10937 player->anim_delay_counter--;
10939 else if (player->post_delay_counter > 0)
10941 player->post_delay_counter--;
10945 else if (player->is_bored)
10947 if (player->num_special_action_bored > 0)
10949 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10951 int special_action =
10952 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10953 int special_graphic =
10954 el_act_dir2img(player->artwork_element, special_action, move_dir);
10956 player->anim_delay_counter =
10957 graphic_info[special_graphic].anim_delay_fixed +
10958 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10959 player->post_delay_counter =
10960 graphic_info[special_graphic].post_delay_fixed +
10961 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10963 player->special_action_bored = special_action;
10966 if (player->anim_delay_counter > 0)
10968 player->action_waiting = player->special_action_bored;
10969 player->anim_delay_counter--;
10971 else if (player->post_delay_counter > 0)
10973 player->post_delay_counter--;
10978 else if (last_waiting) /* waiting -> not waiting */
10980 player->is_waiting = FALSE;
10981 player->is_bored = FALSE;
10982 player->is_sleeping = FALSE;
10984 player->frame_counter_bored = -1;
10985 player->frame_counter_sleeping = -1;
10987 player->anim_delay_counter = 0;
10988 player->post_delay_counter = 0;
10990 player->dir_waiting = player->MovDir;
10991 player->action_waiting = ACTION_DEFAULT;
10993 player->special_action_bored = ACTION_DEFAULT;
10994 player->special_action_sleeping = ACTION_DEFAULT;
10998 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11000 if ((!player->is_moving && player->was_moving) ||
11001 (player->MovPos == 0 && player->was_moving) ||
11002 (player->is_snapping && !player->was_snapping) ||
11003 (player->is_dropping && !player->was_dropping))
11005 if (!CheckSaveEngineSnapshotToList())
11008 player->was_moving = FALSE;
11009 player->was_snapping = TRUE;
11010 player->was_dropping = TRUE;
11014 if (player->is_moving)
11015 player->was_moving = TRUE;
11017 if (!player->is_snapping)
11018 player->was_snapping = FALSE;
11020 if (!player->is_dropping)
11021 player->was_dropping = FALSE;
11025 static void CheckSingleStepMode(struct PlayerInfo *player)
11027 if (tape.single_step && tape.recording && !tape.pausing)
11029 /* as it is called "single step mode", just return to pause mode when the
11030 player stopped moving after one tile (or never starts moving at all) */
11031 if (!player->is_moving &&
11032 !player->is_pushing &&
11033 !player->is_dropping_pressed)
11035 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11036 SnapField(player, 0, 0); /* stop snapping */
11040 CheckSaveEngineSnapshot(player);
11043 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11045 int left = player_action & JOY_LEFT;
11046 int right = player_action & JOY_RIGHT;
11047 int up = player_action & JOY_UP;
11048 int down = player_action & JOY_DOWN;
11049 int button1 = player_action & JOY_BUTTON_1;
11050 int button2 = player_action & JOY_BUTTON_2;
11051 int dx = (left ? -1 : right ? 1 : 0);
11052 int dy = (up ? -1 : down ? 1 : 0);
11054 if (!player->active || tape.pausing)
11060 SnapField(player, dx, dy);
11064 DropElement(player);
11066 MovePlayer(player, dx, dy);
11069 CheckSingleStepMode(player);
11071 SetPlayerWaiting(player, FALSE);
11073 return player_action;
11077 /* no actions for this player (no input at player's configured device) */
11079 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11080 SnapField(player, 0, 0);
11081 CheckGravityMovementWhenNotMoving(player);
11083 if (player->MovPos == 0)
11084 SetPlayerWaiting(player, TRUE);
11086 if (player->MovPos == 0) /* needed for tape.playing */
11087 player->is_moving = FALSE;
11089 player->is_dropping = FALSE;
11090 player->is_dropping_pressed = FALSE;
11091 player->drop_pressed_delay = 0;
11093 CheckSingleStepMode(player);
11099 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11102 if (!tape.use_mouse)
11105 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11106 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11107 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11110 static void SetTapeActionFromMouseAction(byte *tape_action,
11111 struct MouseActionInfo *mouse_action)
11113 if (!tape.use_mouse)
11116 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11117 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11118 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11121 static void CheckLevelTime()
11125 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11126 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11128 if (level.native_em_level->lev->home == 0) /* all players at home */
11130 PlayerWins(local_player);
11132 AllPlayersGone = TRUE;
11134 level.native_em_level->lev->home = -1;
11137 if (level.native_em_level->ply[0]->alive == 0 &&
11138 level.native_em_level->ply[1]->alive == 0 &&
11139 level.native_em_level->ply[2]->alive == 0 &&
11140 level.native_em_level->ply[3]->alive == 0) /* all dead */
11141 AllPlayersGone = TRUE;
11143 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11145 if (game_sp.LevelSolved &&
11146 !game_sp.GameOver) /* game won */
11148 PlayerWins(local_player);
11150 game_sp.GameOver = TRUE;
11152 AllPlayersGone = TRUE;
11155 if (game_sp.GameOver) /* game lost */
11156 AllPlayersGone = TRUE;
11158 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11160 if (game_mm.level_solved &&
11161 !game_mm.game_over) /* game won */
11163 PlayerWins(local_player);
11165 game_mm.game_over = TRUE;
11167 AllPlayersGone = TRUE;
11170 if (game_mm.game_over) /* game lost */
11171 AllPlayersGone = TRUE;
11174 if (TimeFrames >= FRAMES_PER_SECOND)
11179 for (i = 0; i < MAX_PLAYERS; i++)
11181 struct PlayerInfo *player = &stored_player[i];
11183 if (SHIELD_ON(player))
11185 player->shield_normal_time_left--;
11187 if (player->shield_deadly_time_left > 0)
11188 player->shield_deadly_time_left--;
11192 if (!local_player->LevelSolved && !level.use_step_counter)
11200 if (TimeLeft <= 10 && setup.time_limit)
11201 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11203 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11204 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11206 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11208 if (!TimeLeft && setup.time_limit)
11210 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11211 level.native_em_level->lev->killed_out_of_time = TRUE;
11213 for (i = 0; i < MAX_PLAYERS; i++)
11214 KillPlayer(&stored_player[i]);
11217 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11219 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11222 level.native_em_level->lev->time =
11223 (game.no_time_limit ? TimePlayed : TimeLeft);
11226 if (tape.recording || tape.playing)
11227 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11230 if (tape.recording || tape.playing)
11231 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11233 UpdateAndDisplayGameControlValues();
11236 void AdvanceFrameAndPlayerCounters(int player_nr)
11240 /* advance frame counters (global frame counter and time frame counter) */
11244 /* advance player counters (counters for move delay, move animation etc.) */
11245 for (i = 0; i < MAX_PLAYERS; i++)
11247 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11248 int move_delay_value = stored_player[i].move_delay_value;
11249 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11251 if (!advance_player_counters) /* not all players may be affected */
11254 if (move_frames == 0) /* less than one move per game frame */
11256 int stepsize = TILEX / move_delay_value;
11257 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11258 int count = (stored_player[i].is_moving ?
11259 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11261 if (count % delay == 0)
11265 stored_player[i].Frame += move_frames;
11267 if (stored_player[i].MovPos != 0)
11268 stored_player[i].StepFrame += move_frames;
11270 if (stored_player[i].move_delay > 0)
11271 stored_player[i].move_delay--;
11273 /* due to bugs in previous versions, counter must count up, not down */
11274 if (stored_player[i].push_delay != -1)
11275 stored_player[i].push_delay++;
11277 if (stored_player[i].drop_delay > 0)
11278 stored_player[i].drop_delay--;
11280 if (stored_player[i].is_dropping_pressed)
11281 stored_player[i].drop_pressed_delay++;
11285 void StartGameActions(boolean init_network_game, boolean record_tape,
11288 unsigned int new_random_seed = InitRND(random_seed);
11291 TapeStartRecording(new_random_seed);
11293 #if defined(NETWORK_AVALIABLE)
11294 if (init_network_game)
11296 SendToServer_StartPlaying();
11305 void GameActionsExt()
11308 static unsigned int game_frame_delay = 0;
11310 unsigned int game_frame_delay_value;
11311 byte *recorded_player_action;
11312 byte summarized_player_action = 0;
11313 byte tape_action[MAX_PLAYERS];
11316 /* detect endless loops, caused by custom element programming */
11317 if (recursion_loop_detected && recursion_loop_depth == 0)
11319 char *message = getStringCat3("Internal Error! Element ",
11320 EL_NAME(recursion_loop_element),
11321 " caused endless loop! Quit the game?");
11323 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11324 EL_NAME(recursion_loop_element));
11326 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11328 recursion_loop_detected = FALSE; /* if game should be continued */
11335 if (game.restart_level)
11336 StartGameActions(options.network, setup.autorecord, level.random_seed);
11338 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11339 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11341 if (level.native_em_level->lev->home == 0) /* all players at home */
11343 PlayerWins(local_player);
11345 AllPlayersGone = TRUE;
11347 level.native_em_level->lev->home = -1;
11350 if (level.native_em_level->ply[0]->alive == 0 &&
11351 level.native_em_level->ply[1]->alive == 0 &&
11352 level.native_em_level->ply[2]->alive == 0 &&
11353 level.native_em_level->ply[3]->alive == 0) /* all dead */
11354 AllPlayersGone = TRUE;
11356 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11358 if (game_sp.LevelSolved &&
11359 !game_sp.GameOver) /* game won */
11361 PlayerWins(local_player);
11363 game_sp.GameOver = TRUE;
11365 AllPlayersGone = TRUE;
11368 if (game_sp.GameOver) /* game lost */
11369 AllPlayersGone = TRUE;
11371 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11373 if (game_mm.level_solved &&
11374 !game_mm.game_over) /* game won */
11376 PlayerWins(local_player);
11378 game_mm.game_over = TRUE;
11380 AllPlayersGone = TRUE;
11383 if (game_mm.game_over) /* game lost */
11384 AllPlayersGone = TRUE;
11387 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11390 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11393 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11396 game_frame_delay_value =
11397 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11399 if (tape.playing && tape.warp_forward && !tape.pausing)
11400 game_frame_delay_value = 0;
11402 SetVideoFrameDelay(game_frame_delay_value);
11406 /* ---------- main game synchronization point ---------- */
11408 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11410 printf("::: skip == %d\n", skip);
11413 /* ---------- main game synchronization point ---------- */
11415 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11419 if (network_playing && !network_player_action_received)
11421 /* try to get network player actions in time */
11423 #if defined(NETWORK_AVALIABLE)
11424 /* last chance to get network player actions without main loop delay */
11425 HandleNetworking();
11428 /* game was quit by network peer */
11429 if (game_status != GAME_MODE_PLAYING)
11432 if (!network_player_action_received)
11433 return; /* failed to get network player actions in time */
11435 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11441 /* at this point we know that we really continue executing the game */
11443 network_player_action_received = FALSE;
11445 /* when playing tape, read previously recorded player input from tape data */
11446 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11448 local_player->effective_mouse_action = local_player->mouse_action;
11450 if (recorded_player_action != NULL)
11451 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11452 recorded_player_action);
11454 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11458 if (tape.set_centered_player)
11460 game.centered_player_nr_next = tape.centered_player_nr_next;
11461 game.set_centered_player = TRUE;
11464 for (i = 0; i < MAX_PLAYERS; i++)
11466 summarized_player_action |= stored_player[i].action;
11468 if (!network_playing && (game.team_mode || tape.playing))
11469 stored_player[i].effective_action = stored_player[i].action;
11472 #if defined(NETWORK_AVALIABLE)
11473 if (network_playing)
11474 SendToServer_MovePlayer(summarized_player_action);
11477 // summarize all actions at local players mapped input device position
11478 // (this allows using different input devices in single player mode)
11479 if (!options.network && !game.team_mode)
11480 stored_player[map_player_action[local_player->index_nr]].effective_action =
11481 summarized_player_action;
11483 if (tape.recording &&
11485 setup.input_on_focus &&
11486 game.centered_player_nr != -1)
11488 for (i = 0; i < MAX_PLAYERS; i++)
11489 stored_player[i].effective_action =
11490 (i == game.centered_player_nr ? summarized_player_action : 0);
11493 if (recorded_player_action != NULL)
11494 for (i = 0; i < MAX_PLAYERS; i++)
11495 stored_player[i].effective_action = recorded_player_action[i];
11497 for (i = 0; i < MAX_PLAYERS; i++)
11499 tape_action[i] = stored_player[i].effective_action;
11501 /* (this may happen in the RND game engine if a player was not present on
11502 the playfield on level start, but appeared later from a custom element */
11503 if (setup.team_mode &&
11506 !tape.player_participates[i])
11507 tape.player_participates[i] = TRUE;
11510 SetTapeActionFromMouseAction(tape_action,
11511 &local_player->effective_mouse_action);
11513 /* only record actions from input devices, but not programmed actions */
11514 if (tape.recording)
11515 TapeRecordAction(tape_action);
11517 #if USE_NEW_PLAYER_ASSIGNMENTS
11518 // !!! also map player actions in single player mode !!!
11519 // if (game.team_mode)
11522 byte mapped_action[MAX_PLAYERS];
11524 #if DEBUG_PLAYER_ACTIONS
11526 for (i = 0; i < MAX_PLAYERS; i++)
11527 printf(" %d, ", stored_player[i].effective_action);
11530 for (i = 0; i < MAX_PLAYERS; i++)
11531 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11533 for (i = 0; i < MAX_PLAYERS; i++)
11534 stored_player[i].effective_action = mapped_action[i];
11536 #if DEBUG_PLAYER_ACTIONS
11538 for (i = 0; i < MAX_PLAYERS; i++)
11539 printf(" %d, ", stored_player[i].effective_action);
11543 #if DEBUG_PLAYER_ACTIONS
11547 for (i = 0; i < MAX_PLAYERS; i++)
11548 printf(" %d, ", stored_player[i].effective_action);
11554 for (i = 0; i < MAX_PLAYERS; i++)
11556 // allow engine snapshot in case of changed movement attempt
11557 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11558 (stored_player[i].effective_action & KEY_MOTION))
11559 game.snapshot.changed_action = TRUE;
11561 // allow engine snapshot in case of snapping/dropping attempt
11562 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11563 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11564 game.snapshot.changed_action = TRUE;
11566 game.snapshot.last_action[i] = stored_player[i].effective_action;
11569 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11571 GameActions_EM_Main();
11573 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11575 GameActions_SP_Main();
11577 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11579 GameActions_MM_Main();
11583 GameActions_RND_Main();
11586 BlitScreenToBitmap(backbuffer);
11590 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11592 if (global.show_frames_per_second)
11594 static unsigned int fps_counter = 0;
11595 static int fps_frames = 0;
11596 unsigned int fps_delay_ms = Counter() - fps_counter;
11600 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11602 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11605 fps_counter = Counter();
11607 /* always draw FPS to screen after FPS value was updated */
11608 redraw_mask |= REDRAW_FPS;
11611 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11612 if (GetDrawDeactivationMask() == REDRAW_NONE)
11613 redraw_mask |= REDRAW_FPS;
11617 static void GameActions_CheckSaveEngineSnapshot()
11619 if (!game.snapshot.save_snapshot)
11622 // clear flag for saving snapshot _before_ saving snapshot
11623 game.snapshot.save_snapshot = FALSE;
11625 SaveEngineSnapshotToList();
11632 GameActions_CheckSaveEngineSnapshot();
11635 void GameActions_EM_Main()
11637 byte effective_action[MAX_PLAYERS];
11638 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11641 for (i = 0; i < MAX_PLAYERS; i++)
11642 effective_action[i] = stored_player[i].effective_action;
11644 GameActions_EM(effective_action, warp_mode);
11647 void GameActions_SP_Main()
11649 byte effective_action[MAX_PLAYERS];
11650 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11653 for (i = 0; i < MAX_PLAYERS; i++)
11654 effective_action[i] = stored_player[i].effective_action;
11656 GameActions_SP(effective_action, warp_mode);
11658 for (i = 0; i < MAX_PLAYERS; i++)
11660 if (stored_player[i].force_dropping)
11661 stored_player[i].action |= KEY_BUTTON_DROP;
11663 stored_player[i].force_dropping = FALSE;
11667 void GameActions_MM_Main()
11669 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11671 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11674 void GameActions_RND_Main()
11679 void GameActions_RND()
11681 int magic_wall_x = 0, magic_wall_y = 0;
11682 int i, x, y, element, graphic, last_gfx_frame;
11684 InitPlayfieldScanModeVars();
11686 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11688 SCAN_PLAYFIELD(x, y)
11690 ChangeCount[x][y] = 0;
11691 ChangeEvent[x][y] = -1;
11695 if (game.set_centered_player)
11697 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11699 /* switching to "all players" only possible if all players fit to screen */
11700 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11702 game.centered_player_nr_next = game.centered_player_nr;
11703 game.set_centered_player = FALSE;
11706 /* do not switch focus to non-existing (or non-active) player */
11707 if (game.centered_player_nr_next >= 0 &&
11708 !stored_player[game.centered_player_nr_next].active)
11710 game.centered_player_nr_next = game.centered_player_nr;
11711 game.set_centered_player = FALSE;
11715 if (game.set_centered_player &&
11716 ScreenMovPos == 0) /* screen currently aligned at tile position */
11720 if (game.centered_player_nr_next == -1)
11722 setScreenCenteredToAllPlayers(&sx, &sy);
11726 sx = stored_player[game.centered_player_nr_next].jx;
11727 sy = stored_player[game.centered_player_nr_next].jy;
11730 game.centered_player_nr = game.centered_player_nr_next;
11731 game.set_centered_player = FALSE;
11733 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11734 DrawGameDoorValues();
11737 for (i = 0; i < MAX_PLAYERS; i++)
11739 int actual_player_action = stored_player[i].effective_action;
11742 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11743 - rnd_equinox_tetrachloride 048
11744 - rnd_equinox_tetrachloride_ii 096
11745 - rnd_emanuel_schmieg 002
11746 - doctor_sloan_ww 001, 020
11748 if (stored_player[i].MovPos == 0)
11749 CheckGravityMovement(&stored_player[i]);
11752 /* overwrite programmed action with tape action */
11753 if (stored_player[i].programmed_action)
11754 actual_player_action = stored_player[i].programmed_action;
11756 PlayerActions(&stored_player[i], actual_player_action);
11758 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11761 ScrollScreen(NULL, SCROLL_GO_ON);
11763 /* for backwards compatibility, the following code emulates a fixed bug that
11764 occured when pushing elements (causing elements that just made their last
11765 pushing step to already (if possible) make their first falling step in the
11766 same game frame, which is bad); this code is also needed to use the famous
11767 "spring push bug" which is used in older levels and might be wanted to be
11768 used also in newer levels, but in this case the buggy pushing code is only
11769 affecting the "spring" element and no other elements */
11771 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11773 for (i = 0; i < MAX_PLAYERS; i++)
11775 struct PlayerInfo *player = &stored_player[i];
11776 int x = player->jx;
11777 int y = player->jy;
11779 if (player->active && player->is_pushing && player->is_moving &&
11781 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11782 Feld[x][y] == EL_SPRING))
11784 ContinueMoving(x, y);
11786 /* continue moving after pushing (this is actually a bug) */
11787 if (!IS_MOVING(x, y))
11788 Stop[x][y] = FALSE;
11793 SCAN_PLAYFIELD(x, y)
11795 ChangeCount[x][y] = 0;
11796 ChangeEvent[x][y] = -1;
11798 /* this must be handled before main playfield loop */
11799 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11802 if (MovDelay[x][y] <= 0)
11806 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11809 if (MovDelay[x][y] <= 0)
11812 TEST_DrawLevelField(x, y);
11814 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11819 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11821 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11822 printf("GameActions(): This should never happen!\n");
11824 ChangePage[x][y] = -1;
11828 Stop[x][y] = FALSE;
11829 if (WasJustMoving[x][y] > 0)
11830 WasJustMoving[x][y]--;
11831 if (WasJustFalling[x][y] > 0)
11832 WasJustFalling[x][y]--;
11833 if (CheckCollision[x][y] > 0)
11834 CheckCollision[x][y]--;
11835 if (CheckImpact[x][y] > 0)
11836 CheckImpact[x][y]--;
11840 /* reset finished pushing action (not done in ContinueMoving() to allow
11841 continuous pushing animation for elements with zero push delay) */
11842 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11844 ResetGfxAnimation(x, y);
11845 TEST_DrawLevelField(x, y);
11849 if (IS_BLOCKED(x, y))
11853 Blocked2Moving(x, y, &oldx, &oldy);
11854 if (!IS_MOVING(oldx, oldy))
11856 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11857 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11858 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11859 printf("GameActions(): This should never happen!\n");
11865 SCAN_PLAYFIELD(x, y)
11867 element = Feld[x][y];
11868 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869 last_gfx_frame = GfxFrame[x][y];
11871 ResetGfxFrame(x, y);
11873 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11874 DrawLevelGraphicAnimation(x, y, graphic);
11876 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11877 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11878 ResetRandomAnimationValue(x, y);
11880 SetRandomAnimationValue(x, y);
11882 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11884 if (IS_INACTIVE(element))
11886 if (IS_ANIMATED(graphic))
11887 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11892 /* this may take place after moving, so 'element' may have changed */
11893 if (IS_CHANGING(x, y) &&
11894 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11896 int page = element_info[element].event_page_nr[CE_DELAY];
11898 HandleElementChange(x, y, page);
11900 element = Feld[x][y];
11901 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11904 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11908 element = Feld[x][y];
11909 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11911 if (IS_ANIMATED(graphic) &&
11912 !IS_MOVING(x, y) &&
11914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11916 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11917 TEST_DrawTwinkleOnField(x, y);
11919 else if (element == EL_ACID)
11922 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11924 else if ((element == EL_EXIT_OPEN ||
11925 element == EL_EM_EXIT_OPEN ||
11926 element == EL_SP_EXIT_OPEN ||
11927 element == EL_STEEL_EXIT_OPEN ||
11928 element == EL_EM_STEEL_EXIT_OPEN ||
11929 element == EL_SP_TERMINAL ||
11930 element == EL_SP_TERMINAL_ACTIVE ||
11931 element == EL_EXTRA_TIME ||
11932 element == EL_SHIELD_NORMAL ||
11933 element == EL_SHIELD_DEADLY) &&
11934 IS_ANIMATED(graphic))
11935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11936 else if (IS_MOVING(x, y))
11937 ContinueMoving(x, y);
11938 else if (IS_ACTIVE_BOMB(element))
11939 CheckDynamite(x, y);
11940 else if (element == EL_AMOEBA_GROWING)
11941 AmoebeWaechst(x, y);
11942 else if (element == EL_AMOEBA_SHRINKING)
11943 AmoebaDisappearing(x, y);
11945 #if !USE_NEW_AMOEBA_CODE
11946 else if (IS_AMOEBALIVE(element))
11947 AmoebeAbleger(x, y);
11950 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11952 else if (element == EL_EXIT_CLOSED)
11954 else if (element == EL_EM_EXIT_CLOSED)
11956 else if (element == EL_STEEL_EXIT_CLOSED)
11957 CheckExitSteel(x, y);
11958 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11959 CheckExitSteelEM(x, y);
11960 else if (element == EL_SP_EXIT_CLOSED)
11962 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11963 element == EL_EXPANDABLE_STEELWALL_GROWING)
11964 MauerWaechst(x, y);
11965 else if (element == EL_EXPANDABLE_WALL ||
11966 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11967 element == EL_EXPANDABLE_WALL_VERTICAL ||
11968 element == EL_EXPANDABLE_WALL_ANY ||
11969 element == EL_BD_EXPANDABLE_WALL)
11970 MauerAbleger(x, y);
11971 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11972 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11973 element == EL_EXPANDABLE_STEELWALL_ANY)
11974 MauerAblegerStahl(x, y);
11975 else if (element == EL_FLAMES)
11976 CheckForDragon(x, y);
11977 else if (element == EL_EXPLOSION)
11978 ; /* drawing of correct explosion animation is handled separately */
11979 else if (element == EL_ELEMENT_SNAPPING ||
11980 element == EL_DIAGONAL_SHRINKING ||
11981 element == EL_DIAGONAL_GROWING)
11983 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11985 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11987 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11988 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11990 if (IS_BELT_ACTIVE(element))
11991 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11993 if (game.magic_wall_active)
11995 int jx = local_player->jx, jy = local_player->jy;
11997 /* play the element sound at the position nearest to the player */
11998 if ((element == EL_MAGIC_WALL_FULL ||
11999 element == EL_MAGIC_WALL_ACTIVE ||
12000 element == EL_MAGIC_WALL_EMPTYING ||
12001 element == EL_BD_MAGIC_WALL_FULL ||
12002 element == EL_BD_MAGIC_WALL_ACTIVE ||
12003 element == EL_BD_MAGIC_WALL_EMPTYING ||
12004 element == EL_DC_MAGIC_WALL_FULL ||
12005 element == EL_DC_MAGIC_WALL_ACTIVE ||
12006 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12007 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12015 #if USE_NEW_AMOEBA_CODE
12016 /* new experimental amoeba growth stuff */
12017 if (!(FrameCounter % 8))
12019 static unsigned int random = 1684108901;
12021 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12023 x = RND(lev_fieldx);
12024 y = RND(lev_fieldy);
12025 element = Feld[x][y];
12027 if (!IS_PLAYER(x,y) &&
12028 (element == EL_EMPTY ||
12029 CAN_GROW_INTO(element) ||
12030 element == EL_QUICKSAND_EMPTY ||
12031 element == EL_QUICKSAND_FAST_EMPTY ||
12032 element == EL_ACID_SPLASH_LEFT ||
12033 element == EL_ACID_SPLASH_RIGHT))
12035 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12036 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12037 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12038 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12039 Feld[x][y] = EL_AMOEBA_DROP;
12042 random = random * 129 + 1;
12047 game.explosions_delayed = FALSE;
12049 SCAN_PLAYFIELD(x, y)
12051 element = Feld[x][y];
12053 if (ExplodeField[x][y])
12054 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12055 else if (element == EL_EXPLOSION)
12056 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12058 ExplodeField[x][y] = EX_TYPE_NONE;
12061 game.explosions_delayed = TRUE;
12063 if (game.magic_wall_active)
12065 if (!(game.magic_wall_time_left % 4))
12067 int element = Feld[magic_wall_x][magic_wall_y];
12069 if (element == EL_BD_MAGIC_WALL_FULL ||
12070 element == EL_BD_MAGIC_WALL_ACTIVE ||
12071 element == EL_BD_MAGIC_WALL_EMPTYING)
12072 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12073 else if (element == EL_DC_MAGIC_WALL_FULL ||
12074 element == EL_DC_MAGIC_WALL_ACTIVE ||
12075 element == EL_DC_MAGIC_WALL_EMPTYING)
12076 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12078 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12081 if (game.magic_wall_time_left > 0)
12083 game.magic_wall_time_left--;
12085 if (!game.magic_wall_time_left)
12087 SCAN_PLAYFIELD(x, y)
12089 element = Feld[x][y];
12091 if (element == EL_MAGIC_WALL_ACTIVE ||
12092 element == EL_MAGIC_WALL_FULL)
12094 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12095 TEST_DrawLevelField(x, y);
12097 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12098 element == EL_BD_MAGIC_WALL_FULL)
12100 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12101 TEST_DrawLevelField(x, y);
12103 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12104 element == EL_DC_MAGIC_WALL_FULL)
12106 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12107 TEST_DrawLevelField(x, y);
12111 game.magic_wall_active = FALSE;
12116 if (game.light_time_left > 0)
12118 game.light_time_left--;
12120 if (game.light_time_left == 0)
12121 RedrawAllLightSwitchesAndInvisibleElements();
12124 if (game.timegate_time_left > 0)
12126 game.timegate_time_left--;
12128 if (game.timegate_time_left == 0)
12129 CloseAllOpenTimegates();
12132 if (game.lenses_time_left > 0)
12134 game.lenses_time_left--;
12136 if (game.lenses_time_left == 0)
12137 RedrawAllInvisibleElementsForLenses();
12140 if (game.magnify_time_left > 0)
12142 game.magnify_time_left--;
12144 if (game.magnify_time_left == 0)
12145 RedrawAllInvisibleElementsForMagnifier();
12148 for (i = 0; i < MAX_PLAYERS; i++)
12150 struct PlayerInfo *player = &stored_player[i];
12152 if (SHIELD_ON(player))
12154 if (player->shield_deadly_time_left)
12155 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12156 else if (player->shield_normal_time_left)
12157 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12161 #if USE_DELAYED_GFX_REDRAW
12162 SCAN_PLAYFIELD(x, y)
12164 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12166 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12167 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12169 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12170 DrawLevelField(x, y);
12172 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12173 DrawLevelFieldCrumbled(x, y);
12175 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12176 DrawLevelFieldCrumbledNeighbours(x, y);
12178 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12179 DrawTwinkleOnField(x, y);
12182 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12187 PlayAllPlayersSound();
12189 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12191 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12193 local_player->show_envelope = 0;
12196 /* use random number generator in every frame to make it less predictable */
12197 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12201 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12203 int min_x = x, min_y = y, max_x = x, max_y = y;
12206 for (i = 0; i < MAX_PLAYERS; i++)
12208 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12210 if (!stored_player[i].active || &stored_player[i] == player)
12213 min_x = MIN(min_x, jx);
12214 min_y = MIN(min_y, jy);
12215 max_x = MAX(max_x, jx);
12216 max_y = MAX(max_y, jy);
12219 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12222 static boolean AllPlayersInVisibleScreen()
12226 for (i = 0; i < MAX_PLAYERS; i++)
12228 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12230 if (!stored_player[i].active)
12233 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12240 void ScrollLevel(int dx, int dy)
12242 int scroll_offset = 2 * TILEX_VAR;
12245 BlitBitmap(drawto_field, drawto_field,
12246 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12247 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12248 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12249 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12250 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12251 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12255 x = (dx == 1 ? BX1 : BX2);
12256 for (y = BY1; y <= BY2; y++)
12257 DrawScreenField(x, y);
12262 y = (dy == 1 ? BY1 : BY2);
12263 for (x = BX1; x <= BX2; x++)
12264 DrawScreenField(x, y);
12267 redraw_mask |= REDRAW_FIELD;
12270 static boolean canFallDown(struct PlayerInfo *player)
12272 int jx = player->jx, jy = player->jy;
12274 return (IN_LEV_FIELD(jx, jy + 1) &&
12275 (IS_FREE(jx, jy + 1) ||
12276 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12277 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12278 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12281 static boolean canPassField(int x, int y, int move_dir)
12283 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12284 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12285 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12286 int nextx = x + dx;
12287 int nexty = y + dy;
12288 int element = Feld[x][y];
12290 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12291 !CAN_MOVE(element) &&
12292 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12293 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12294 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12297 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12299 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12300 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12301 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12305 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12306 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12307 (IS_DIGGABLE(Feld[newx][newy]) ||
12308 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12309 canPassField(newx, newy, move_dir)));
12312 static void CheckGravityMovement(struct PlayerInfo *player)
12314 if (player->gravity && !player->programmed_action)
12316 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12317 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12318 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12319 int jx = player->jx, jy = player->jy;
12320 boolean player_is_moving_to_valid_field =
12321 (!player_is_snapping &&
12322 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12323 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12324 boolean player_can_fall_down = canFallDown(player);
12326 if (player_can_fall_down &&
12327 !player_is_moving_to_valid_field)
12328 player->programmed_action = MV_DOWN;
12332 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12334 return CheckGravityMovement(player);
12336 if (player->gravity && !player->programmed_action)
12338 int jx = player->jx, jy = player->jy;
12339 boolean field_under_player_is_free =
12340 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12341 boolean player_is_standing_on_valid_field =
12342 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12343 (IS_WALKABLE(Feld[jx][jy]) &&
12344 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12346 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12347 player->programmed_action = MV_DOWN;
12352 MovePlayerOneStep()
12353 -----------------------------------------------------------------------------
12354 dx, dy: direction (non-diagonal) to try to move the player to
12355 real_dx, real_dy: direction as read from input device (can be diagonal)
12358 boolean MovePlayerOneStep(struct PlayerInfo *player,
12359 int dx, int dy, int real_dx, int real_dy)
12361 int jx = player->jx, jy = player->jy;
12362 int new_jx = jx + dx, new_jy = jy + dy;
12364 boolean player_can_move = !player->cannot_move;
12366 if (!player->active || (!dx && !dy))
12367 return MP_NO_ACTION;
12369 player->MovDir = (dx < 0 ? MV_LEFT :
12370 dx > 0 ? MV_RIGHT :
12372 dy > 0 ? MV_DOWN : MV_NONE);
12374 if (!IN_LEV_FIELD(new_jx, new_jy))
12375 return MP_NO_ACTION;
12377 if (!player_can_move)
12379 if (player->MovPos == 0)
12381 player->is_moving = FALSE;
12382 player->is_digging = FALSE;
12383 player->is_collecting = FALSE;
12384 player->is_snapping = FALSE;
12385 player->is_pushing = FALSE;
12389 if (!options.network && game.centered_player_nr == -1 &&
12390 !AllPlayersInSight(player, new_jx, new_jy))
12391 return MP_NO_ACTION;
12393 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12394 if (can_move != MP_MOVING)
12397 /* check if DigField() has caused relocation of the player */
12398 if (player->jx != jx || player->jy != jy)
12399 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12401 StorePlayer[jx][jy] = 0;
12402 player->last_jx = jx;
12403 player->last_jy = jy;
12404 player->jx = new_jx;
12405 player->jy = new_jy;
12406 StorePlayer[new_jx][new_jy] = player->element_nr;
12408 if (player->move_delay_value_next != -1)
12410 player->move_delay_value = player->move_delay_value_next;
12411 player->move_delay_value_next = -1;
12415 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12417 player->step_counter++;
12419 PlayerVisit[jx][jy] = FrameCounter;
12421 player->is_moving = TRUE;
12424 /* should better be called in MovePlayer(), but this breaks some tapes */
12425 ScrollPlayer(player, SCROLL_INIT);
12431 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12433 int jx = player->jx, jy = player->jy;
12434 int old_jx = jx, old_jy = jy;
12435 int moved = MP_NO_ACTION;
12437 if (!player->active)
12442 if (player->MovPos == 0)
12444 player->is_moving = FALSE;
12445 player->is_digging = FALSE;
12446 player->is_collecting = FALSE;
12447 player->is_snapping = FALSE;
12448 player->is_pushing = FALSE;
12454 if (player->move_delay > 0)
12457 player->move_delay = -1; /* set to "uninitialized" value */
12459 /* store if player is automatically moved to next field */
12460 player->is_auto_moving = (player->programmed_action != MV_NONE);
12462 /* remove the last programmed player action */
12463 player->programmed_action = 0;
12465 if (player->MovPos)
12467 /* should only happen if pre-1.2 tape recordings are played */
12468 /* this is only for backward compatibility */
12470 int original_move_delay_value = player->move_delay_value;
12473 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12477 /* scroll remaining steps with finest movement resolution */
12478 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12480 while (player->MovPos)
12482 ScrollPlayer(player, SCROLL_GO_ON);
12483 ScrollScreen(NULL, SCROLL_GO_ON);
12485 AdvanceFrameAndPlayerCounters(player->index_nr);
12488 BackToFront_WithFrameDelay(0);
12491 player->move_delay_value = original_move_delay_value;
12494 player->is_active = FALSE;
12496 if (player->last_move_dir & MV_HORIZONTAL)
12498 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12499 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12503 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12504 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12507 if (!moved && !player->is_active)
12509 player->is_moving = FALSE;
12510 player->is_digging = FALSE;
12511 player->is_collecting = FALSE;
12512 player->is_snapping = FALSE;
12513 player->is_pushing = FALSE;
12519 if (moved & MP_MOVING && !ScreenMovPos &&
12520 (player->index_nr == game.centered_player_nr ||
12521 game.centered_player_nr == -1))
12523 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12524 int offset = game.scroll_delay_value;
12526 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12528 /* actual player has left the screen -- scroll in that direction */
12529 if (jx != old_jx) /* player has moved horizontally */
12530 scroll_x += (jx - old_jx);
12531 else /* player has moved vertically */
12532 scroll_y += (jy - old_jy);
12536 if (jx != old_jx) /* player has moved horizontally */
12538 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12539 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12540 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12542 /* don't scroll over playfield boundaries */
12543 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12544 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12546 /* don't scroll more than one field at a time */
12547 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12549 /* don't scroll against the player's moving direction */
12550 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12551 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12552 scroll_x = old_scroll_x;
12554 else /* player has moved vertically */
12556 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12557 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12558 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12560 /* don't scroll over playfield boundaries */
12561 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12562 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12564 /* don't scroll more than one field at a time */
12565 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12567 /* don't scroll against the player's moving direction */
12568 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12569 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12570 scroll_y = old_scroll_y;
12574 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12576 if (!options.network && game.centered_player_nr == -1 &&
12577 !AllPlayersInVisibleScreen())
12579 scroll_x = old_scroll_x;
12580 scroll_y = old_scroll_y;
12584 ScrollScreen(player, SCROLL_INIT);
12585 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12590 player->StepFrame = 0;
12592 if (moved & MP_MOVING)
12594 if (old_jx != jx && old_jy == jy)
12595 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12596 else if (old_jx == jx && old_jy != jy)
12597 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12599 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12601 player->last_move_dir = player->MovDir;
12602 player->is_moving = TRUE;
12603 player->is_snapping = FALSE;
12604 player->is_switching = FALSE;
12605 player->is_dropping = FALSE;
12606 player->is_dropping_pressed = FALSE;
12607 player->drop_pressed_delay = 0;
12610 /* should better be called here than above, but this breaks some tapes */
12611 ScrollPlayer(player, SCROLL_INIT);
12616 CheckGravityMovementWhenNotMoving(player);
12618 player->is_moving = FALSE;
12620 /* at this point, the player is allowed to move, but cannot move right now
12621 (e.g. because of something blocking the way) -- ensure that the player
12622 is also allowed to move in the next frame (in old versions before 3.1.1,
12623 the player was forced to wait again for eight frames before next try) */
12625 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12626 player->move_delay = 0; /* allow direct movement in the next frame */
12629 if (player->move_delay == -1) /* not yet initialized by DigField() */
12630 player->move_delay = player->move_delay_value;
12632 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12634 TestIfPlayerTouchesBadThing(jx, jy);
12635 TestIfPlayerTouchesCustomElement(jx, jy);
12638 if (!player->active)
12639 RemovePlayer(player);
12644 void ScrollPlayer(struct PlayerInfo *player, int mode)
12646 int jx = player->jx, jy = player->jy;
12647 int last_jx = player->last_jx, last_jy = player->last_jy;
12648 int move_stepsize = TILEX / player->move_delay_value;
12650 if (!player->active)
12653 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12656 if (mode == SCROLL_INIT)
12658 player->actual_frame_counter = FrameCounter;
12659 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12661 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12662 Feld[last_jx][last_jy] == EL_EMPTY)
12664 int last_field_block_delay = 0; /* start with no blocking at all */
12665 int block_delay_adjustment = player->block_delay_adjustment;
12667 /* if player blocks last field, add delay for exactly one move */
12668 if (player->block_last_field)
12670 last_field_block_delay += player->move_delay_value;
12672 /* when blocking enabled, prevent moving up despite gravity */
12673 if (player->gravity && player->MovDir == MV_UP)
12674 block_delay_adjustment = -1;
12677 /* add block delay adjustment (also possible when not blocking) */
12678 last_field_block_delay += block_delay_adjustment;
12680 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12681 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12684 if (player->MovPos != 0) /* player has not yet reached destination */
12687 else if (!FrameReached(&player->actual_frame_counter, 1))
12690 if (player->MovPos != 0)
12692 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12693 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12695 /* before DrawPlayer() to draw correct player graphic for this case */
12696 if (player->MovPos == 0)
12697 CheckGravityMovement(player);
12700 if (player->MovPos == 0) /* player reached destination field */
12702 if (player->move_delay_reset_counter > 0)
12704 player->move_delay_reset_counter--;
12706 if (player->move_delay_reset_counter == 0)
12708 /* continue with normal speed after quickly moving through gate */
12709 HALVE_PLAYER_SPEED(player);
12711 /* be able to make the next move without delay */
12712 player->move_delay = 0;
12716 player->last_jx = jx;
12717 player->last_jy = jy;
12719 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12720 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12721 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12722 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12723 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12724 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12725 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12726 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12728 DrawPlayer(player); /* needed here only to cleanup last field */
12729 RemovePlayer(player);
12731 if (local_player->friends_still_needed == 0 ||
12732 IS_SP_ELEMENT(Feld[jx][jy]))
12733 PlayerWins(player);
12736 /* this breaks one level: "machine", level 000 */
12738 int move_direction = player->MovDir;
12739 int enter_side = MV_DIR_OPPOSITE(move_direction);
12740 int leave_side = move_direction;
12741 int old_jx = last_jx;
12742 int old_jy = last_jy;
12743 int old_element = Feld[old_jx][old_jy];
12744 int new_element = Feld[jx][jy];
12746 if (IS_CUSTOM_ELEMENT(old_element))
12747 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12749 player->index_bit, leave_side);
12751 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12752 CE_PLAYER_LEAVES_X,
12753 player->index_bit, leave_side);
12755 if (IS_CUSTOM_ELEMENT(new_element))
12756 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12757 player->index_bit, enter_side);
12759 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12760 CE_PLAYER_ENTERS_X,
12761 player->index_bit, enter_side);
12763 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12764 CE_MOVE_OF_X, move_direction);
12767 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12769 TestIfPlayerTouchesBadThing(jx, jy);
12770 TestIfPlayerTouchesCustomElement(jx, jy);
12772 /* needed because pushed element has not yet reached its destination,
12773 so it would trigger a change event at its previous field location */
12774 if (!player->is_pushing)
12775 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12777 if (!player->active)
12778 RemovePlayer(player);
12781 if (!local_player->LevelSolved && level.use_step_counter)
12791 if (TimeLeft <= 10 && setup.time_limit)
12792 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12794 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12796 DisplayGameControlValues();
12798 if (!TimeLeft && setup.time_limit)
12799 for (i = 0; i < MAX_PLAYERS; i++)
12800 KillPlayer(&stored_player[i]);
12802 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12804 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12806 DisplayGameControlValues();
12810 if (tape.single_step && tape.recording && !tape.pausing &&
12811 !player->programmed_action)
12812 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12814 if (!player->programmed_action)
12815 CheckSaveEngineSnapshot(player);
12819 void ScrollScreen(struct PlayerInfo *player, int mode)
12821 static unsigned int screen_frame_counter = 0;
12823 if (mode == SCROLL_INIT)
12825 /* set scrolling step size according to actual player's moving speed */
12826 ScrollStepSize = TILEX / player->move_delay_value;
12828 screen_frame_counter = FrameCounter;
12829 ScreenMovDir = player->MovDir;
12830 ScreenMovPos = player->MovPos;
12831 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12834 else if (!FrameReached(&screen_frame_counter, 1))
12839 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12840 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12841 redraw_mask |= REDRAW_FIELD;
12844 ScreenMovDir = MV_NONE;
12847 void TestIfPlayerTouchesCustomElement(int x, int y)
12849 static int xy[4][2] =
12856 static int trigger_sides[4][2] =
12858 /* center side border side */
12859 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12860 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12861 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12862 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12864 static int touch_dir[4] =
12866 MV_LEFT | MV_RIGHT,
12871 int center_element = Feld[x][y]; /* should always be non-moving! */
12874 for (i = 0; i < NUM_DIRECTIONS; i++)
12876 int xx = x + xy[i][0];
12877 int yy = y + xy[i][1];
12878 int center_side = trigger_sides[i][0];
12879 int border_side = trigger_sides[i][1];
12880 int border_element;
12882 if (!IN_LEV_FIELD(xx, yy))
12885 if (IS_PLAYER(x, y)) /* player found at center element */
12887 struct PlayerInfo *player = PLAYERINFO(x, y);
12889 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12890 border_element = Feld[xx][yy]; /* may be moving! */
12891 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12892 border_element = Feld[xx][yy];
12893 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12894 border_element = MovingOrBlocked2Element(xx, yy);
12896 continue; /* center and border element do not touch */
12898 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12899 player->index_bit, border_side);
12900 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12901 CE_PLAYER_TOUCHES_X,
12902 player->index_bit, border_side);
12905 /* use player element that is initially defined in the level playfield,
12906 not the player element that corresponds to the runtime player number
12907 (example: a level that contains EL_PLAYER_3 as the only player would
12908 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12909 int player_element = PLAYERINFO(x, y)->initial_element;
12911 CheckElementChangeBySide(xx, yy, border_element, player_element,
12912 CE_TOUCHING_X, border_side);
12915 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12917 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12919 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12921 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12922 continue; /* center and border element do not touch */
12925 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12926 player->index_bit, center_side);
12927 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12928 CE_PLAYER_TOUCHES_X,
12929 player->index_bit, center_side);
12932 /* use player element that is initially defined in the level playfield,
12933 not the player element that corresponds to the runtime player number
12934 (example: a level that contains EL_PLAYER_3 as the only player would
12935 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12936 int player_element = PLAYERINFO(xx, yy)->initial_element;
12938 CheckElementChangeBySide(x, y, center_element, player_element,
12939 CE_TOUCHING_X, center_side);
12947 void TestIfElementTouchesCustomElement(int x, int y)
12949 static int xy[4][2] =
12956 static int trigger_sides[4][2] =
12958 /* center side border side */
12959 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12960 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12961 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12962 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12964 static int touch_dir[4] =
12966 MV_LEFT | MV_RIGHT,
12971 boolean change_center_element = FALSE;
12972 int center_element = Feld[x][y]; /* should always be non-moving! */
12973 int border_element_old[NUM_DIRECTIONS];
12976 for (i = 0; i < NUM_DIRECTIONS; i++)
12978 int xx = x + xy[i][0];
12979 int yy = y + xy[i][1];
12980 int border_element;
12982 border_element_old[i] = -1;
12984 if (!IN_LEV_FIELD(xx, yy))
12987 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12988 border_element = Feld[xx][yy]; /* may be moving! */
12989 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12990 border_element = Feld[xx][yy];
12991 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12992 border_element = MovingOrBlocked2Element(xx, yy);
12994 continue; /* center and border element do not touch */
12996 border_element_old[i] = border_element;
12999 for (i = 0; i < NUM_DIRECTIONS; i++)
13001 int xx = x + xy[i][0];
13002 int yy = y + xy[i][1];
13003 int center_side = trigger_sides[i][0];
13004 int border_element = border_element_old[i];
13006 if (border_element == -1)
13009 /* check for change of border element */
13010 CheckElementChangeBySide(xx, yy, border_element, center_element,
13011 CE_TOUCHING_X, center_side);
13013 /* (center element cannot be player, so we dont have to check this here) */
13016 for (i = 0; i < NUM_DIRECTIONS; i++)
13018 int xx = x + xy[i][0];
13019 int yy = y + xy[i][1];
13020 int border_side = trigger_sides[i][1];
13021 int border_element = border_element_old[i];
13023 if (border_element == -1)
13026 /* check for change of center element (but change it only once) */
13027 if (!change_center_element)
13028 change_center_element =
13029 CheckElementChangeBySide(x, y, center_element, border_element,
13030 CE_TOUCHING_X, border_side);
13032 if (IS_PLAYER(xx, yy))
13034 /* use player element that is initially defined in the level playfield,
13035 not the player element that corresponds to the runtime player number
13036 (example: a level that contains EL_PLAYER_3 as the only player would
13037 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13038 int player_element = PLAYERINFO(xx, yy)->initial_element;
13040 CheckElementChangeBySide(x, y, center_element, player_element,
13041 CE_TOUCHING_X, border_side);
13046 void TestIfElementHitsCustomElement(int x, int y, int direction)
13048 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13049 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13050 int hitx = x + dx, hity = y + dy;
13051 int hitting_element = Feld[x][y];
13052 int touched_element;
13054 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13057 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13058 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13060 if (IN_LEV_FIELD(hitx, hity))
13062 int opposite_direction = MV_DIR_OPPOSITE(direction);
13063 int hitting_side = direction;
13064 int touched_side = opposite_direction;
13065 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13066 MovDir[hitx][hity] != direction ||
13067 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13073 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13074 CE_HITTING_X, touched_side);
13076 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13077 CE_HIT_BY_X, hitting_side);
13079 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13080 CE_HIT_BY_SOMETHING, opposite_direction);
13082 if (IS_PLAYER(hitx, hity))
13084 /* use player element that is initially defined in the level playfield,
13085 not the player element that corresponds to the runtime player number
13086 (example: a level that contains EL_PLAYER_3 as the only player would
13087 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13088 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13090 CheckElementChangeBySide(x, y, hitting_element, player_element,
13091 CE_HITTING_X, touched_side);
13096 /* "hitting something" is also true when hitting the playfield border */
13097 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13098 CE_HITTING_SOMETHING, direction);
13101 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13103 int i, kill_x = -1, kill_y = -1;
13105 int bad_element = -1;
13106 static int test_xy[4][2] =
13113 static int test_dir[4] =
13121 for (i = 0; i < NUM_DIRECTIONS; i++)
13123 int test_x, test_y, test_move_dir, test_element;
13125 test_x = good_x + test_xy[i][0];
13126 test_y = good_y + test_xy[i][1];
13128 if (!IN_LEV_FIELD(test_x, test_y))
13132 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13134 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13136 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13137 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13139 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13140 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13144 bad_element = test_element;
13150 if (kill_x != -1 || kill_y != -1)
13152 if (IS_PLAYER(good_x, good_y))
13154 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13156 if (player->shield_deadly_time_left > 0 &&
13157 !IS_INDESTRUCTIBLE(bad_element))
13158 Bang(kill_x, kill_y);
13159 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13160 KillPlayer(player);
13163 Bang(good_x, good_y);
13167 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13169 int i, kill_x = -1, kill_y = -1;
13170 int bad_element = Feld[bad_x][bad_y];
13171 static int test_xy[4][2] =
13178 static int touch_dir[4] =
13180 MV_LEFT | MV_RIGHT,
13185 static int test_dir[4] =
13193 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13196 for (i = 0; i < NUM_DIRECTIONS; i++)
13198 int test_x, test_y, test_move_dir, test_element;
13200 test_x = bad_x + test_xy[i][0];
13201 test_y = bad_y + test_xy[i][1];
13203 if (!IN_LEV_FIELD(test_x, test_y))
13207 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13209 test_element = Feld[test_x][test_y];
13211 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13212 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13214 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13215 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13217 /* good thing is player or penguin that does not move away */
13218 if (IS_PLAYER(test_x, test_y))
13220 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13222 if (bad_element == EL_ROBOT && player->is_moving)
13223 continue; /* robot does not kill player if he is moving */
13225 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13227 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13228 continue; /* center and border element do not touch */
13236 else if (test_element == EL_PENGUIN)
13246 if (kill_x != -1 || kill_y != -1)
13248 if (IS_PLAYER(kill_x, kill_y))
13250 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13252 if (player->shield_deadly_time_left > 0 &&
13253 !IS_INDESTRUCTIBLE(bad_element))
13254 Bang(bad_x, bad_y);
13255 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13256 KillPlayer(player);
13259 Bang(kill_x, kill_y);
13263 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13265 int bad_element = Feld[bad_x][bad_y];
13266 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13267 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13268 int test_x = bad_x + dx, test_y = bad_y + dy;
13269 int test_move_dir, test_element;
13270 int kill_x = -1, kill_y = -1;
13272 if (!IN_LEV_FIELD(test_x, test_y))
13276 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13278 test_element = Feld[test_x][test_y];
13280 if (test_move_dir != bad_move_dir)
13282 /* good thing can be player or penguin that does not move away */
13283 if (IS_PLAYER(test_x, test_y))
13285 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13287 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13288 player as being hit when he is moving towards the bad thing, because
13289 the "get hit by" condition would be lost after the player stops) */
13290 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13291 return; /* player moves away from bad thing */
13296 else if (test_element == EL_PENGUIN)
13303 if (kill_x != -1 || kill_y != -1)
13305 if (IS_PLAYER(kill_x, kill_y))
13307 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13309 if (player->shield_deadly_time_left > 0 &&
13310 !IS_INDESTRUCTIBLE(bad_element))
13311 Bang(bad_x, bad_y);
13312 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13313 KillPlayer(player);
13316 Bang(kill_x, kill_y);
13320 void TestIfPlayerTouchesBadThing(int x, int y)
13322 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13325 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13327 TestIfGoodThingHitsBadThing(x, y, move_dir);
13330 void TestIfBadThingTouchesPlayer(int x, int y)
13332 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13335 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13337 TestIfBadThingHitsGoodThing(x, y, move_dir);
13340 void TestIfFriendTouchesBadThing(int x, int y)
13342 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13345 void TestIfBadThingTouchesFriend(int x, int y)
13347 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13350 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13352 int i, kill_x = bad_x, kill_y = bad_y;
13353 static int xy[4][2] =
13361 for (i = 0; i < NUM_DIRECTIONS; i++)
13365 x = bad_x + xy[i][0];
13366 y = bad_y + xy[i][1];
13367 if (!IN_LEV_FIELD(x, y))
13370 element = Feld[x][y];
13371 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13372 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13380 if (kill_x != bad_x || kill_y != bad_y)
13381 Bang(bad_x, bad_y);
13384 void KillPlayer(struct PlayerInfo *player)
13386 int jx = player->jx, jy = player->jy;
13388 if (!player->active)
13392 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13393 player->killed, player->active, player->reanimated);
13396 /* the following code was introduced to prevent an infinite loop when calling
13398 -> CheckTriggeredElementChangeExt()
13399 -> ExecuteCustomElementAction()
13401 -> (infinitely repeating the above sequence of function calls)
13402 which occurs when killing the player while having a CE with the setting
13403 "kill player X when explosion of <player X>"; the solution using a new
13404 field "player->killed" was chosen for backwards compatibility, although
13405 clever use of the fields "player->active" etc. would probably also work */
13407 if (player->killed)
13411 player->killed = TRUE;
13413 /* remove accessible field at the player's position */
13414 Feld[jx][jy] = EL_EMPTY;
13416 /* deactivate shield (else Bang()/Explode() would not work right) */
13417 player->shield_normal_time_left = 0;
13418 player->shield_deadly_time_left = 0;
13421 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13422 player->killed, player->active, player->reanimated);
13428 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13429 player->killed, player->active, player->reanimated);
13432 if (player->reanimated) /* killed player may have been reanimated */
13433 player->killed = player->reanimated = FALSE;
13435 BuryPlayer(player);
13438 static void KillPlayerUnlessEnemyProtected(int x, int y)
13440 if (!PLAYER_ENEMY_PROTECTED(x, y))
13441 KillPlayer(PLAYERINFO(x, y));
13444 static void KillPlayerUnlessExplosionProtected(int x, int y)
13446 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13447 KillPlayer(PLAYERINFO(x, y));
13450 void BuryPlayer(struct PlayerInfo *player)
13452 int jx = player->jx, jy = player->jy;
13454 if (!player->active)
13457 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13458 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13460 player->GameOver = TRUE;
13461 RemovePlayer(player);
13464 void RemovePlayer(struct PlayerInfo *player)
13466 int jx = player->jx, jy = player->jy;
13467 int i, found = FALSE;
13469 player->present = FALSE;
13470 player->active = FALSE;
13472 if (!ExplodeField[jx][jy])
13473 StorePlayer[jx][jy] = 0;
13475 if (player->is_moving)
13476 TEST_DrawLevelField(player->last_jx, player->last_jy);
13478 for (i = 0; i < MAX_PLAYERS; i++)
13479 if (stored_player[i].active)
13483 AllPlayersGone = TRUE;
13489 static void setFieldForSnapping(int x, int y, int element, int direction)
13491 struct ElementInfo *ei = &element_info[element];
13492 int direction_bit = MV_DIR_TO_BIT(direction);
13493 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13494 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13495 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13497 Feld[x][y] = EL_ELEMENT_SNAPPING;
13498 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13500 ResetGfxAnimation(x, y);
13502 GfxElement[x][y] = element;
13503 GfxAction[x][y] = action;
13504 GfxDir[x][y] = direction;
13505 GfxFrame[x][y] = -1;
13509 =============================================================================
13510 checkDiagonalPushing()
13511 -----------------------------------------------------------------------------
13512 check if diagonal input device direction results in pushing of object
13513 (by checking if the alternative direction is walkable, diggable, ...)
13514 =============================================================================
13517 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13518 int x, int y, int real_dx, int real_dy)
13520 int jx, jy, dx, dy, xx, yy;
13522 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13525 /* diagonal direction: check alternative direction */
13530 xx = jx + (dx == 0 ? real_dx : 0);
13531 yy = jy + (dy == 0 ? real_dy : 0);
13533 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13537 =============================================================================
13539 -----------------------------------------------------------------------------
13540 x, y: field next to player (non-diagonal) to try to dig to
13541 real_dx, real_dy: direction as read from input device (can be diagonal)
13542 =============================================================================
13545 static int DigField(struct PlayerInfo *player,
13546 int oldx, int oldy, int x, int y,
13547 int real_dx, int real_dy, int mode)
13549 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13550 boolean player_was_pushing = player->is_pushing;
13551 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13552 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13553 int jx = oldx, jy = oldy;
13554 int dx = x - jx, dy = y - jy;
13555 int nextx = x + dx, nexty = y + dy;
13556 int move_direction = (dx == -1 ? MV_LEFT :
13557 dx == +1 ? MV_RIGHT :
13559 dy == +1 ? MV_DOWN : MV_NONE);
13560 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13561 int dig_side = MV_DIR_OPPOSITE(move_direction);
13562 int old_element = Feld[jx][jy];
13563 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13566 if (is_player) /* function can also be called by EL_PENGUIN */
13568 if (player->MovPos == 0)
13570 player->is_digging = FALSE;
13571 player->is_collecting = FALSE;
13574 if (player->MovPos == 0) /* last pushing move finished */
13575 player->is_pushing = FALSE;
13577 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13579 player->is_switching = FALSE;
13580 player->push_delay = -1;
13582 return MP_NO_ACTION;
13586 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13587 old_element = Back[jx][jy];
13589 /* in case of element dropped at player position, check background */
13590 else if (Back[jx][jy] != EL_EMPTY &&
13591 game.engine_version >= VERSION_IDENT(2,2,0,0))
13592 old_element = Back[jx][jy];
13594 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13595 return MP_NO_ACTION; /* field has no opening in this direction */
13597 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13598 return MP_NO_ACTION; /* field has no opening in this direction */
13600 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13604 Feld[jx][jy] = player->artwork_element;
13605 InitMovingField(jx, jy, MV_DOWN);
13606 Store[jx][jy] = EL_ACID;
13607 ContinueMoving(jx, jy);
13608 BuryPlayer(player);
13610 return MP_DONT_RUN_INTO;
13613 if (player_can_move && DONT_RUN_INTO(element))
13615 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13617 return MP_DONT_RUN_INTO;
13620 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13621 return MP_NO_ACTION;
13623 collect_count = element_info[element].collect_count_initial;
13625 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13626 return MP_NO_ACTION;
13628 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13629 player_can_move = player_can_move_or_snap;
13631 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13632 game.engine_version >= VERSION_IDENT(2,2,0,0))
13634 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13635 player->index_bit, dig_side);
13636 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13637 player->index_bit, dig_side);
13639 if (element == EL_DC_LANDMINE)
13642 if (Feld[x][y] != element) /* field changed by snapping */
13645 return MP_NO_ACTION;
13648 if (player->gravity && is_player && !player->is_auto_moving &&
13649 canFallDown(player) && move_direction != MV_DOWN &&
13650 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13651 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13653 if (player_can_move &&
13654 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13656 int sound_element = SND_ELEMENT(element);
13657 int sound_action = ACTION_WALKING;
13659 if (IS_RND_GATE(element))
13661 if (!player->key[RND_GATE_NR(element)])
13662 return MP_NO_ACTION;
13664 else if (IS_RND_GATE_GRAY(element))
13666 if (!player->key[RND_GATE_GRAY_NR(element)])
13667 return MP_NO_ACTION;
13669 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13671 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13672 return MP_NO_ACTION;
13674 else if (element == EL_EXIT_OPEN ||
13675 element == EL_EM_EXIT_OPEN ||
13676 element == EL_EM_EXIT_OPENING ||
13677 element == EL_STEEL_EXIT_OPEN ||
13678 element == EL_EM_STEEL_EXIT_OPEN ||
13679 element == EL_EM_STEEL_EXIT_OPENING ||
13680 element == EL_SP_EXIT_OPEN ||
13681 element == EL_SP_EXIT_OPENING)
13683 sound_action = ACTION_PASSING; /* player is passing exit */
13685 else if (element == EL_EMPTY)
13687 sound_action = ACTION_MOVING; /* nothing to walk on */
13690 /* play sound from background or player, whatever is available */
13691 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13692 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13694 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13696 else if (player_can_move &&
13697 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13699 if (!ACCESS_FROM(element, opposite_direction))
13700 return MP_NO_ACTION; /* field not accessible from this direction */
13702 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13703 return MP_NO_ACTION;
13705 if (IS_EM_GATE(element))
13707 if (!player->key[EM_GATE_NR(element)])
13708 return MP_NO_ACTION;
13710 else if (IS_EM_GATE_GRAY(element))
13712 if (!player->key[EM_GATE_GRAY_NR(element)])
13713 return MP_NO_ACTION;
13715 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13717 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13718 return MP_NO_ACTION;
13720 else if (IS_EMC_GATE(element))
13722 if (!player->key[EMC_GATE_NR(element)])
13723 return MP_NO_ACTION;
13725 else if (IS_EMC_GATE_GRAY(element))
13727 if (!player->key[EMC_GATE_GRAY_NR(element)])
13728 return MP_NO_ACTION;
13730 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13732 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13733 return MP_NO_ACTION;
13735 else if (element == EL_DC_GATE_WHITE ||
13736 element == EL_DC_GATE_WHITE_GRAY ||
13737 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13739 if (player->num_white_keys == 0)
13740 return MP_NO_ACTION;
13742 player->num_white_keys--;
13744 else if (IS_SP_PORT(element))
13746 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13747 element == EL_SP_GRAVITY_PORT_RIGHT ||
13748 element == EL_SP_GRAVITY_PORT_UP ||
13749 element == EL_SP_GRAVITY_PORT_DOWN)
13750 player->gravity = !player->gravity;
13751 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13752 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13753 element == EL_SP_GRAVITY_ON_PORT_UP ||
13754 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13755 player->gravity = TRUE;
13756 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13757 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13758 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13759 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13760 player->gravity = FALSE;
13763 /* automatically move to the next field with double speed */
13764 player->programmed_action = move_direction;
13766 if (player->move_delay_reset_counter == 0)
13768 player->move_delay_reset_counter = 2; /* two double speed steps */
13770 DOUBLE_PLAYER_SPEED(player);
13773 PlayLevelSoundAction(x, y, ACTION_PASSING);
13775 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13779 if (mode != DF_SNAP)
13781 GfxElement[x][y] = GFX_ELEMENT(element);
13782 player->is_digging = TRUE;
13785 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13787 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13788 player->index_bit, dig_side);
13790 if (mode == DF_SNAP)
13792 if (level.block_snap_field)
13793 setFieldForSnapping(x, y, element, move_direction);
13795 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13797 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13798 player->index_bit, dig_side);
13801 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13805 if (is_player && mode != DF_SNAP)
13807 GfxElement[x][y] = element;
13808 player->is_collecting = TRUE;
13811 if (element == EL_SPEED_PILL)
13813 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13815 else if (element == EL_EXTRA_TIME && level.time > 0)
13817 TimeLeft += level.extra_time;
13819 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13821 DisplayGameControlValues();
13823 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13825 player->shield_normal_time_left += level.shield_normal_time;
13826 if (element == EL_SHIELD_DEADLY)
13827 player->shield_deadly_time_left += level.shield_deadly_time;
13829 else if (element == EL_DYNAMITE ||
13830 element == EL_EM_DYNAMITE ||
13831 element == EL_SP_DISK_RED)
13833 if (player->inventory_size < MAX_INVENTORY_SIZE)
13834 player->inventory_element[player->inventory_size++] = element;
13836 DrawGameDoorValues();
13838 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13840 player->dynabomb_count++;
13841 player->dynabombs_left++;
13843 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13845 player->dynabomb_size++;
13847 else if (element == EL_DYNABOMB_INCREASE_POWER)
13849 player->dynabomb_xl = TRUE;
13851 else if (IS_KEY(element))
13853 player->key[KEY_NR(element)] = TRUE;
13855 DrawGameDoorValues();
13857 else if (element == EL_DC_KEY_WHITE)
13859 player->num_white_keys++;
13861 /* display white keys? */
13862 /* DrawGameDoorValues(); */
13864 else if (IS_ENVELOPE(element))
13866 player->show_envelope = element;
13868 else if (element == EL_EMC_LENSES)
13870 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13872 RedrawAllInvisibleElementsForLenses();
13874 else if (element == EL_EMC_MAGNIFIER)
13876 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13878 RedrawAllInvisibleElementsForMagnifier();
13880 else if (IS_DROPPABLE(element) ||
13881 IS_THROWABLE(element)) /* can be collected and dropped */
13885 if (collect_count == 0)
13886 player->inventory_infinite_element = element;
13888 for (i = 0; i < collect_count; i++)
13889 if (player->inventory_size < MAX_INVENTORY_SIZE)
13890 player->inventory_element[player->inventory_size++] = element;
13892 DrawGameDoorValues();
13894 else if (collect_count > 0)
13896 local_player->gems_still_needed -= collect_count;
13897 if (local_player->gems_still_needed < 0)
13898 local_player->gems_still_needed = 0;
13900 game.snapshot.collected_item = TRUE;
13902 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13904 DisplayGameControlValues();
13907 RaiseScoreElement(element);
13908 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13911 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13912 player->index_bit, dig_side);
13914 if (mode == DF_SNAP)
13916 if (level.block_snap_field)
13917 setFieldForSnapping(x, y, element, move_direction);
13919 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13921 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13922 player->index_bit, dig_side);
13925 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13927 if (mode == DF_SNAP && element != EL_BD_ROCK)
13928 return MP_NO_ACTION;
13930 if (CAN_FALL(element) && dy)
13931 return MP_NO_ACTION;
13933 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13934 !(element == EL_SPRING && level.use_spring_bug))
13935 return MP_NO_ACTION;
13937 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13938 ((move_direction & MV_VERTICAL &&
13939 ((element_info[element].move_pattern & MV_LEFT &&
13940 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13941 (element_info[element].move_pattern & MV_RIGHT &&
13942 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13943 (move_direction & MV_HORIZONTAL &&
13944 ((element_info[element].move_pattern & MV_UP &&
13945 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13946 (element_info[element].move_pattern & MV_DOWN &&
13947 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13948 return MP_NO_ACTION;
13950 /* do not push elements already moving away faster than player */
13951 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13952 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13953 return MP_NO_ACTION;
13955 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13957 if (player->push_delay_value == -1 || !player_was_pushing)
13958 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13960 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13962 if (player->push_delay_value == -1)
13963 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13965 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13967 if (!player->is_pushing)
13968 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13971 player->is_pushing = TRUE;
13972 player->is_active = TRUE;
13974 if (!(IN_LEV_FIELD(nextx, nexty) &&
13975 (IS_FREE(nextx, nexty) ||
13976 (IS_SB_ELEMENT(element) &&
13977 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13978 (IS_CUSTOM_ELEMENT(element) &&
13979 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13980 return MP_NO_ACTION;
13982 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13983 return MP_NO_ACTION;
13985 if (player->push_delay == -1) /* new pushing; restart delay */
13986 player->push_delay = 0;
13988 if (player->push_delay < player->push_delay_value &&
13989 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13990 element != EL_SPRING && element != EL_BALLOON)
13992 /* make sure that there is no move delay before next try to push */
13993 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13994 player->move_delay = 0;
13996 return MP_NO_ACTION;
13999 if (IS_CUSTOM_ELEMENT(element) &&
14000 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14002 if (!DigFieldByCE(nextx, nexty, element))
14003 return MP_NO_ACTION;
14006 if (IS_SB_ELEMENT(element))
14008 if (element == EL_SOKOBAN_FIELD_FULL)
14010 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14011 local_player->sokobanfields_still_needed++;
14014 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14016 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14017 local_player->sokobanfields_still_needed--;
14020 Feld[x][y] = EL_SOKOBAN_OBJECT;
14022 if (Back[x][y] == Back[nextx][nexty])
14023 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14024 else if (Back[x][y] != 0)
14025 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14028 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14031 if (local_player->sokobanfields_still_needed == 0 &&
14032 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14034 PlayerWins(player);
14036 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14040 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14042 InitMovingField(x, y, move_direction);
14043 GfxAction[x][y] = ACTION_PUSHING;
14045 if (mode == DF_SNAP)
14046 ContinueMoving(x, y);
14048 MovPos[x][y] = (dx != 0 ? dx : dy);
14050 Pushed[x][y] = TRUE;
14051 Pushed[nextx][nexty] = TRUE;
14053 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14054 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14056 player->push_delay_value = -1; /* get new value later */
14058 /* check for element change _after_ element has been pushed */
14059 if (game.use_change_when_pushing_bug)
14061 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14062 player->index_bit, dig_side);
14063 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14064 player->index_bit, dig_side);
14067 else if (IS_SWITCHABLE(element))
14069 if (PLAYER_SWITCHING(player, x, y))
14071 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14072 player->index_bit, dig_side);
14077 player->is_switching = TRUE;
14078 player->switch_x = x;
14079 player->switch_y = y;
14081 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14083 if (element == EL_ROBOT_WHEEL)
14085 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14089 game.robot_wheel_active = TRUE;
14091 TEST_DrawLevelField(x, y);
14093 else if (element == EL_SP_TERMINAL)
14097 SCAN_PLAYFIELD(xx, yy)
14099 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14103 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14105 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14107 ResetGfxAnimation(xx, yy);
14108 TEST_DrawLevelField(xx, yy);
14112 else if (IS_BELT_SWITCH(element))
14114 ToggleBeltSwitch(x, y);
14116 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14117 element == EL_SWITCHGATE_SWITCH_DOWN ||
14118 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14119 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14121 ToggleSwitchgateSwitch(x, y);
14123 else if (element == EL_LIGHT_SWITCH ||
14124 element == EL_LIGHT_SWITCH_ACTIVE)
14126 ToggleLightSwitch(x, y);
14128 else if (element == EL_TIMEGATE_SWITCH ||
14129 element == EL_DC_TIMEGATE_SWITCH)
14131 ActivateTimegateSwitch(x, y);
14133 else if (element == EL_BALLOON_SWITCH_LEFT ||
14134 element == EL_BALLOON_SWITCH_RIGHT ||
14135 element == EL_BALLOON_SWITCH_UP ||
14136 element == EL_BALLOON_SWITCH_DOWN ||
14137 element == EL_BALLOON_SWITCH_NONE ||
14138 element == EL_BALLOON_SWITCH_ANY)
14140 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14141 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14142 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14143 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14144 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14147 else if (element == EL_LAMP)
14149 Feld[x][y] = EL_LAMP_ACTIVE;
14150 local_player->lights_still_needed--;
14152 ResetGfxAnimation(x, y);
14153 TEST_DrawLevelField(x, y);
14155 else if (element == EL_TIME_ORB_FULL)
14157 Feld[x][y] = EL_TIME_ORB_EMPTY;
14159 if (level.time > 0 || level.use_time_orb_bug)
14161 TimeLeft += level.time_orb_time;
14162 game.no_time_limit = FALSE;
14164 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14166 DisplayGameControlValues();
14169 ResetGfxAnimation(x, y);
14170 TEST_DrawLevelField(x, y);
14172 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14173 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14177 game.ball_state = !game.ball_state;
14179 SCAN_PLAYFIELD(xx, yy)
14181 int e = Feld[xx][yy];
14183 if (game.ball_state)
14185 if (e == EL_EMC_MAGIC_BALL)
14186 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14187 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14188 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14192 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14193 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14194 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14195 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14201 player->index_bit, dig_side);
14203 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14204 player->index_bit, dig_side);
14206 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14207 player->index_bit, dig_side);
14213 if (!PLAYER_SWITCHING(player, x, y))
14215 player->is_switching = TRUE;
14216 player->switch_x = x;
14217 player->switch_y = y;
14219 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14220 player->index_bit, dig_side);
14221 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14222 player->index_bit, dig_side);
14224 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14225 player->index_bit, dig_side);
14226 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14227 player->index_bit, dig_side);
14230 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14231 player->index_bit, dig_side);
14232 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14233 player->index_bit, dig_side);
14235 return MP_NO_ACTION;
14238 player->push_delay = -1;
14240 if (is_player) /* function can also be called by EL_PENGUIN */
14242 if (Feld[x][y] != element) /* really digged/collected something */
14244 player->is_collecting = !player->is_digging;
14245 player->is_active = TRUE;
14252 static boolean DigFieldByCE(int x, int y, int digging_element)
14254 int element = Feld[x][y];
14256 if (!IS_FREE(x, y))
14258 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14259 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14262 /* no element can dig solid indestructible elements */
14263 if (IS_INDESTRUCTIBLE(element) &&
14264 !IS_DIGGABLE(element) &&
14265 !IS_COLLECTIBLE(element))
14268 if (AmoebaNr[x][y] &&
14269 (element == EL_AMOEBA_FULL ||
14270 element == EL_BD_AMOEBA ||
14271 element == EL_AMOEBA_GROWING))
14273 AmoebaCnt[AmoebaNr[x][y]]--;
14274 AmoebaCnt2[AmoebaNr[x][y]]--;
14277 if (IS_MOVING(x, y))
14278 RemoveMovingField(x, y);
14282 TEST_DrawLevelField(x, y);
14285 /* if digged element was about to explode, prevent the explosion */
14286 ExplodeField[x][y] = EX_TYPE_NONE;
14288 PlayLevelSoundAction(x, y, action);
14291 Store[x][y] = EL_EMPTY;
14293 /* this makes it possible to leave the removed element again */
14294 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14295 Store[x][y] = element;
14300 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14302 int jx = player->jx, jy = player->jy;
14303 int x = jx + dx, y = jy + dy;
14304 int snap_direction = (dx == -1 ? MV_LEFT :
14305 dx == +1 ? MV_RIGHT :
14307 dy == +1 ? MV_DOWN : MV_NONE);
14308 boolean can_continue_snapping = (level.continuous_snapping &&
14309 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14311 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14314 if (!player->active || !IN_LEV_FIELD(x, y))
14322 if (player->MovPos == 0)
14323 player->is_pushing = FALSE;
14325 player->is_snapping = FALSE;
14327 if (player->MovPos == 0)
14329 player->is_moving = FALSE;
14330 player->is_digging = FALSE;
14331 player->is_collecting = FALSE;
14337 /* prevent snapping with already pressed snap key when not allowed */
14338 if (player->is_snapping && !can_continue_snapping)
14341 player->MovDir = snap_direction;
14343 if (player->MovPos == 0)
14345 player->is_moving = FALSE;
14346 player->is_digging = FALSE;
14347 player->is_collecting = FALSE;
14350 player->is_dropping = FALSE;
14351 player->is_dropping_pressed = FALSE;
14352 player->drop_pressed_delay = 0;
14354 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14357 player->is_snapping = TRUE;
14358 player->is_active = TRUE;
14360 if (player->MovPos == 0)
14362 player->is_moving = FALSE;
14363 player->is_digging = FALSE;
14364 player->is_collecting = FALSE;
14367 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14368 TEST_DrawLevelField(player->last_jx, player->last_jy);
14370 TEST_DrawLevelField(x, y);
14375 static boolean DropElement(struct PlayerInfo *player)
14377 int old_element, new_element;
14378 int dropx = player->jx, dropy = player->jy;
14379 int drop_direction = player->MovDir;
14380 int drop_side = drop_direction;
14381 int drop_element = get_next_dropped_element(player);
14383 /* do not drop an element on top of another element; when holding drop key
14384 pressed without moving, dropped element must move away before the next
14385 element can be dropped (this is especially important if the next element
14386 is dynamite, which can be placed on background for historical reasons) */
14387 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14390 if (IS_THROWABLE(drop_element))
14392 dropx += GET_DX_FROM_DIR(drop_direction);
14393 dropy += GET_DY_FROM_DIR(drop_direction);
14395 if (!IN_LEV_FIELD(dropx, dropy))
14399 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14400 new_element = drop_element; /* default: no change when dropping */
14402 /* check if player is active, not moving and ready to drop */
14403 if (!player->active || player->MovPos || player->drop_delay > 0)
14406 /* check if player has anything that can be dropped */
14407 if (new_element == EL_UNDEFINED)
14410 /* only set if player has anything that can be dropped */
14411 player->is_dropping_pressed = TRUE;
14413 /* check if drop key was pressed long enough for EM style dynamite */
14414 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14417 /* check if anything can be dropped at the current position */
14418 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14421 /* collected custom elements can only be dropped on empty fields */
14422 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14425 if (old_element != EL_EMPTY)
14426 Back[dropx][dropy] = old_element; /* store old element on this field */
14428 ResetGfxAnimation(dropx, dropy);
14429 ResetRandomAnimationValue(dropx, dropy);
14431 if (player->inventory_size > 0 ||
14432 player->inventory_infinite_element != EL_UNDEFINED)
14434 if (player->inventory_size > 0)
14436 player->inventory_size--;
14438 DrawGameDoorValues();
14440 if (new_element == EL_DYNAMITE)
14441 new_element = EL_DYNAMITE_ACTIVE;
14442 else if (new_element == EL_EM_DYNAMITE)
14443 new_element = EL_EM_DYNAMITE_ACTIVE;
14444 else if (new_element == EL_SP_DISK_RED)
14445 new_element = EL_SP_DISK_RED_ACTIVE;
14448 Feld[dropx][dropy] = new_element;
14450 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14451 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14452 el2img(Feld[dropx][dropy]), 0);
14454 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14456 /* needed if previous element just changed to "empty" in the last frame */
14457 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14459 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14460 player->index_bit, drop_side);
14461 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14463 player->index_bit, drop_side);
14465 TestIfElementTouchesCustomElement(dropx, dropy);
14467 else /* player is dropping a dyna bomb */
14469 player->dynabombs_left--;
14471 Feld[dropx][dropy] = new_element;
14473 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14474 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14475 el2img(Feld[dropx][dropy]), 0);
14477 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14480 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14481 InitField_WithBug1(dropx, dropy, FALSE);
14483 new_element = Feld[dropx][dropy]; /* element might have changed */
14485 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14486 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14488 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14489 MovDir[dropx][dropy] = drop_direction;
14491 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14493 /* do not cause impact style collision by dropping elements that can fall */
14494 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14497 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14498 player->is_dropping = TRUE;
14500 player->drop_pressed_delay = 0;
14501 player->is_dropping_pressed = FALSE;
14503 player->drop_x = dropx;
14504 player->drop_y = dropy;
14509 /* ------------------------------------------------------------------------- */
14510 /* game sound playing functions */
14511 /* ------------------------------------------------------------------------- */
14513 static int *loop_sound_frame = NULL;
14514 static int *loop_sound_volume = NULL;
14516 void InitPlayLevelSound()
14518 int num_sounds = getSoundListSize();
14520 checked_free(loop_sound_frame);
14521 checked_free(loop_sound_volume);
14523 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14524 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14527 static void PlayLevelSound(int x, int y, int nr)
14529 int sx = SCREENX(x), sy = SCREENY(y);
14530 int volume, stereo_position;
14531 int max_distance = 8;
14532 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14534 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14535 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14538 if (!IN_LEV_FIELD(x, y) ||
14539 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14540 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14543 volume = SOUND_MAX_VOLUME;
14545 if (!IN_SCR_FIELD(sx, sy))
14547 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14548 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14550 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14553 stereo_position = (SOUND_MAX_LEFT +
14554 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14555 (SCR_FIELDX + 2 * max_distance));
14557 if (IS_LOOP_SOUND(nr))
14559 /* This assures that quieter loop sounds do not overwrite louder ones,
14560 while restarting sound volume comparison with each new game frame. */
14562 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14565 loop_sound_volume[nr] = volume;
14566 loop_sound_frame[nr] = FrameCounter;
14569 PlaySoundExt(nr, volume, stereo_position, type);
14572 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14574 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14575 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14576 y < LEVELY(BY1) ? LEVELY(BY1) :
14577 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14581 static void PlayLevelSoundAction(int x, int y, int action)
14583 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14586 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14588 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14590 if (sound_effect != SND_UNDEFINED)
14591 PlayLevelSound(x, y, sound_effect);
14594 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14597 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14599 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14600 PlayLevelSound(x, y, sound_effect);
14603 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14605 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14607 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14608 PlayLevelSound(x, y, sound_effect);
14611 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14613 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14615 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14616 StopSound(sound_effect);
14619 static int getLevelMusicNr()
14621 if (levelset.music[level_nr] != MUS_UNDEFINED)
14622 return levelset.music[level_nr]; /* from config file */
14624 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14627 static void FadeLevelSounds()
14632 static void FadeLevelMusic()
14634 int music_nr = getLevelMusicNr();
14635 char *curr_music = getCurrentlyPlayingMusicFilename();
14636 char *next_music = getMusicInfoEntryFilename(music_nr);
14638 if (!strEqual(curr_music, next_music))
14642 void FadeLevelSoundsAndMusic()
14648 static void PlayLevelMusic()
14650 int music_nr = getLevelMusicNr();
14651 char *curr_music = getCurrentlyPlayingMusicFilename();
14652 char *next_music = getMusicInfoEntryFilename(music_nr);
14654 if (!strEqual(curr_music, next_music))
14655 PlayMusic(music_nr);
14658 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14660 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14661 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14662 int x = xx - 1 - offset;
14663 int y = yy - 1 - offset;
14668 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14672 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14676 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14680 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14684 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14688 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14692 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14695 case SAMPLE_android_clone:
14696 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14699 case SAMPLE_android_move:
14700 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14703 case SAMPLE_spring:
14704 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14708 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14715 case SAMPLE_eater_eat:
14716 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14720 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14723 case SAMPLE_collect:
14724 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14727 case SAMPLE_diamond:
14728 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14731 case SAMPLE_squash:
14732 /* !!! CHECK THIS !!! */
14734 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14740 case SAMPLE_wonderfall:
14741 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14745 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14749 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14753 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14757 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14761 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14765 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14768 case SAMPLE_wonder:
14769 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14773 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14776 case SAMPLE_exit_open:
14777 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14780 case SAMPLE_exit_leave:
14781 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14784 case SAMPLE_dynamite:
14785 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14789 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14793 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14797 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14801 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14805 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14809 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14813 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14818 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14820 int element = map_element_SP_to_RND(element_sp);
14821 int action = map_action_SP_to_RND(action_sp);
14822 int offset = (setup.sp_show_border_elements ? 0 : 1);
14823 int x = xx - offset;
14824 int y = yy - offset;
14826 PlayLevelSoundElementAction(x, y, element, action);
14829 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14831 int element = map_element_MM_to_RND(element_mm);
14832 int action = map_action_MM_to_RND(action_mm);
14834 int x = xx - offset;
14835 int y = yy - offset;
14837 if (!IS_MM_ELEMENT(element))
14838 element = EL_MM_DEFAULT;
14840 PlayLevelSoundElementAction(x, y, element, action);
14843 void PlaySound_MM(int sound_mm)
14845 int sound = map_sound_MM_to_RND(sound_mm);
14847 if (sound == SND_UNDEFINED)
14853 void PlaySoundLoop_MM(int sound_mm)
14855 int sound = map_sound_MM_to_RND(sound_mm);
14857 if (sound == SND_UNDEFINED)
14860 PlaySoundLoop(sound);
14863 void StopSound_MM(int sound_mm)
14865 int sound = map_sound_MM_to_RND(sound_mm);
14867 if (sound == SND_UNDEFINED)
14873 void RaiseScore(int value)
14875 local_player->score += value;
14877 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14879 DisplayGameControlValues();
14882 void RaiseScoreElement(int element)
14887 case EL_BD_DIAMOND:
14888 case EL_EMERALD_YELLOW:
14889 case EL_EMERALD_RED:
14890 case EL_EMERALD_PURPLE:
14891 case EL_SP_INFOTRON:
14892 RaiseScore(level.score[SC_EMERALD]);
14895 RaiseScore(level.score[SC_DIAMOND]);
14898 RaiseScore(level.score[SC_CRYSTAL]);
14901 RaiseScore(level.score[SC_PEARL]);
14904 case EL_BD_BUTTERFLY:
14905 case EL_SP_ELECTRON:
14906 RaiseScore(level.score[SC_BUG]);
14909 case EL_BD_FIREFLY:
14910 case EL_SP_SNIKSNAK:
14911 RaiseScore(level.score[SC_SPACESHIP]);
14914 case EL_DARK_YAMYAM:
14915 RaiseScore(level.score[SC_YAMYAM]);
14918 RaiseScore(level.score[SC_ROBOT]);
14921 RaiseScore(level.score[SC_PACMAN]);
14924 RaiseScore(level.score[SC_NUT]);
14927 case EL_EM_DYNAMITE:
14928 case EL_SP_DISK_RED:
14929 case EL_DYNABOMB_INCREASE_NUMBER:
14930 case EL_DYNABOMB_INCREASE_SIZE:
14931 case EL_DYNABOMB_INCREASE_POWER:
14932 RaiseScore(level.score[SC_DYNAMITE]);
14934 case EL_SHIELD_NORMAL:
14935 case EL_SHIELD_DEADLY:
14936 RaiseScore(level.score[SC_SHIELD]);
14938 case EL_EXTRA_TIME:
14939 RaiseScore(level.extra_time_score);
14953 case EL_DC_KEY_WHITE:
14954 RaiseScore(level.score[SC_KEY]);
14957 RaiseScore(element_info[element].collect_score);
14962 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14964 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14966 /* closing door required in case of envelope style request dialogs */
14968 CloseDoor(DOOR_CLOSE_1);
14970 #if defined(NETWORK_AVALIABLE)
14971 if (options.network)
14972 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14977 FadeSkipNextFadeIn();
14979 SetGameStatus(GAME_MODE_MAIN);
14984 else /* continue playing the game */
14986 if (tape.playing && tape.deactivate_display)
14987 TapeDeactivateDisplayOff(TRUE);
14989 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14991 if (tape.playing && tape.deactivate_display)
14992 TapeDeactivateDisplayOn();
14996 void RequestQuitGame(boolean ask_if_really_quit)
14998 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14999 boolean skip_request = AllPlayersGone || quick_quit;
15001 RequestQuitGameExt(skip_request, quick_quit,
15002 "Do you really want to quit the game?");
15005 void RequestRestartGame(char *message)
15007 game.restart_game_message = NULL;
15009 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
15011 StartGameActions(options.network, setup.autorecord, level.random_seed);
15015 SetGameStatus(GAME_MODE_MAIN);
15022 /* ------------------------------------------------------------------------- */
15023 /* random generator functions */
15024 /* ------------------------------------------------------------------------- */
15026 unsigned int InitEngineRandom_RND(int seed)
15028 game.num_random_calls = 0;
15030 return InitEngineRandom(seed);
15033 unsigned int RND(int max)
15037 game.num_random_calls++;
15039 return GetEngineRandom(max);
15046 /* ------------------------------------------------------------------------- */
15047 /* game engine snapshot handling functions */
15048 /* ------------------------------------------------------------------------- */
15050 struct EngineSnapshotInfo
15052 /* runtime values for custom element collect score */
15053 int collect_score[NUM_CUSTOM_ELEMENTS];
15055 /* runtime values for group element choice position */
15056 int choice_pos[NUM_GROUP_ELEMENTS];
15058 /* runtime values for belt position animations */
15059 int belt_graphic[4][NUM_BELT_PARTS];
15060 int belt_anim_mode[4][NUM_BELT_PARTS];
15063 static struct EngineSnapshotInfo engine_snapshot_rnd;
15064 static char *snapshot_level_identifier = NULL;
15065 static int snapshot_level_nr = -1;
15067 static void SaveEngineSnapshotValues_RND()
15069 static int belt_base_active_element[4] =
15071 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15072 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15073 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15074 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15078 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15080 int element = EL_CUSTOM_START + i;
15082 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15085 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15087 int element = EL_GROUP_START + i;
15089 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15092 for (i = 0; i < 4; i++)
15094 for (j = 0; j < NUM_BELT_PARTS; j++)
15096 int element = belt_base_active_element[i] + j;
15097 int graphic = el2img(element);
15098 int anim_mode = graphic_info[graphic].anim_mode;
15100 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15101 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15106 static void LoadEngineSnapshotValues_RND()
15108 unsigned int num_random_calls = game.num_random_calls;
15111 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15113 int element = EL_CUSTOM_START + i;
15115 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15118 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15120 int element = EL_GROUP_START + i;
15122 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15125 for (i = 0; i < 4; i++)
15127 for (j = 0; j < NUM_BELT_PARTS; j++)
15129 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15130 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15132 graphic_info[graphic].anim_mode = anim_mode;
15136 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15138 InitRND(tape.random_seed);
15139 for (i = 0; i < num_random_calls; i++)
15143 if (game.num_random_calls != num_random_calls)
15145 Error(ERR_INFO, "number of random calls out of sync");
15146 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15147 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15148 Error(ERR_EXIT, "this should not happen -- please debug");
15152 void FreeEngineSnapshotSingle()
15154 FreeSnapshotSingle();
15156 setString(&snapshot_level_identifier, NULL);
15157 snapshot_level_nr = -1;
15160 void FreeEngineSnapshotList()
15162 FreeSnapshotList();
15165 ListNode *SaveEngineSnapshotBuffers()
15167 ListNode *buffers = NULL;
15169 /* copy some special values to a structure better suited for the snapshot */
15171 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15172 SaveEngineSnapshotValues_RND();
15173 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15174 SaveEngineSnapshotValues_EM();
15175 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15176 SaveEngineSnapshotValues_SP(&buffers);
15177 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15178 SaveEngineSnapshotValues_MM(&buffers);
15180 /* save values stored in special snapshot structure */
15182 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15184 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15186 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15188 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15191 /* save further RND engine values */
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15197 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15198 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15200 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15203 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15205 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15210 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15214 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15216 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15217 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15219 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15220 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15221 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15222 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15223 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15224 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15225 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15226 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15227 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15228 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15229 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15230 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15231 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15232 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15233 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15234 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15235 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15236 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15238 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15239 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15241 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15242 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15243 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15245 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15246 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15248 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15249 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15250 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15251 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15252 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15254 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15255 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15258 ListNode *node = engine_snapshot_list_rnd;
15261 while (node != NULL)
15263 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15268 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15274 void SaveEngineSnapshotSingle()
15276 ListNode *buffers = SaveEngineSnapshotBuffers();
15278 /* finally save all snapshot buffers to single snapshot */
15279 SaveSnapshotSingle(buffers);
15281 /* save level identification information */
15282 setString(&snapshot_level_identifier, leveldir_current->identifier);
15283 snapshot_level_nr = level_nr;
15286 boolean CheckSaveEngineSnapshotToList()
15288 boolean save_snapshot =
15289 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15290 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15291 game.snapshot.changed_action) ||
15292 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15293 game.snapshot.collected_item));
15295 game.snapshot.changed_action = FALSE;
15296 game.snapshot.collected_item = FALSE;
15297 game.snapshot.save_snapshot = save_snapshot;
15299 return save_snapshot;
15302 void SaveEngineSnapshotToList()
15304 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15308 ListNode *buffers = SaveEngineSnapshotBuffers();
15310 /* finally save all snapshot buffers to snapshot list */
15311 SaveSnapshotToList(buffers);
15314 void SaveEngineSnapshotToListInitial()
15316 FreeEngineSnapshotList();
15318 SaveEngineSnapshotToList();
15321 void LoadEngineSnapshotValues()
15323 /* restore special values from snapshot structure */
15325 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15326 LoadEngineSnapshotValues_RND();
15327 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15328 LoadEngineSnapshotValues_EM();
15329 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15330 LoadEngineSnapshotValues_SP();
15331 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15332 LoadEngineSnapshotValues_MM();
15335 void LoadEngineSnapshotSingle()
15337 LoadSnapshotSingle();
15339 LoadEngineSnapshotValues();
15342 void LoadEngineSnapshot_Undo(int steps)
15344 LoadSnapshotFromList_Older(steps);
15346 LoadEngineSnapshotValues();
15349 void LoadEngineSnapshot_Redo(int steps)
15351 LoadSnapshotFromList_Newer(steps);
15353 LoadEngineSnapshotValues();
15356 boolean CheckEngineSnapshotSingle()
15358 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15359 snapshot_level_nr == level_nr);
15362 boolean CheckEngineSnapshotList()
15364 return CheckSnapshotList();
15368 /* ---------- new game button stuff ---------------------------------------- */
15375 boolean *setup_value;
15376 boolean allowed_on_tape;
15378 } gamebutton_info[NUM_GAME_BUTTONS] =
15381 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15382 GAME_CTRL_ID_STOP, NULL,
15386 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15387 GAME_CTRL_ID_PAUSE, NULL,
15391 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15392 GAME_CTRL_ID_PLAY, NULL,
15396 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15397 GAME_CTRL_ID_UNDO, NULL,
15401 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15402 GAME_CTRL_ID_REDO, NULL,
15406 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15407 GAME_CTRL_ID_SAVE, NULL,
15411 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15412 GAME_CTRL_ID_PAUSE2, NULL,
15416 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15417 GAME_CTRL_ID_LOAD, NULL,
15421 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15422 GAME_CTRL_ID_PANEL_STOP, NULL,
15426 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15427 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15428 FALSE, "pause game"
15431 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15432 GAME_CTRL_ID_PANEL_PLAY, NULL,
15436 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15437 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15438 TRUE, "background music on/off"
15441 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15442 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15443 TRUE, "sound loops on/off"
15446 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15447 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15448 TRUE, "normal sounds on/off"
15451 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15452 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15453 FALSE, "background music on/off"
15456 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15457 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15458 FALSE, "sound loops on/off"
15461 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15462 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15463 FALSE, "normal sounds on/off"
15467 void CreateGameButtons()
15471 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15473 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15474 struct XY *pos = gamebutton_info[i].pos;
15475 struct GadgetInfo *gi;
15478 unsigned int event_mask;
15479 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15480 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15481 int base_x = (on_tape ? VX : DX);
15482 int base_y = (on_tape ? VY : DY);
15483 int gd_x = gfx->src_x;
15484 int gd_y = gfx->src_y;
15485 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15486 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15487 int gd_xa = gfx->src_x + gfx->active_xoffset;
15488 int gd_ya = gfx->src_y + gfx->active_yoffset;
15489 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15490 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15493 if (gfx->bitmap == NULL)
15495 game_gadget[id] = NULL;
15500 if (id == GAME_CTRL_ID_STOP ||
15501 id == GAME_CTRL_ID_PANEL_STOP ||
15502 id == GAME_CTRL_ID_PLAY ||
15503 id == GAME_CTRL_ID_PANEL_PLAY ||
15504 id == GAME_CTRL_ID_SAVE ||
15505 id == GAME_CTRL_ID_LOAD)
15507 button_type = GD_TYPE_NORMAL_BUTTON;
15509 event_mask = GD_EVENT_RELEASED;
15511 else if (id == GAME_CTRL_ID_UNDO ||
15512 id == GAME_CTRL_ID_REDO)
15514 button_type = GD_TYPE_NORMAL_BUTTON;
15516 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15520 button_type = GD_TYPE_CHECK_BUTTON;
15521 checked = (gamebutton_info[i].setup_value != NULL ?
15522 *gamebutton_info[i].setup_value : FALSE);
15523 event_mask = GD_EVENT_PRESSED;
15526 gi = CreateGadget(GDI_CUSTOM_ID, id,
15527 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15528 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15529 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15530 GDI_WIDTH, gfx->width,
15531 GDI_HEIGHT, gfx->height,
15532 GDI_TYPE, button_type,
15533 GDI_STATE, GD_BUTTON_UNPRESSED,
15534 GDI_CHECKED, checked,
15535 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15536 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15537 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15538 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15539 GDI_DIRECT_DRAW, FALSE,
15540 GDI_EVENT_MASK, event_mask,
15541 GDI_CALLBACK_ACTION, HandleGameButtons,
15545 Error(ERR_EXIT, "cannot create gadget");
15547 game_gadget[id] = gi;
15551 void FreeGameButtons()
15555 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15556 FreeGadget(game_gadget[i]);
15559 static void UnmapGameButtonsAtSamePosition(int id)
15563 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15565 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15566 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15567 UnmapGadget(game_gadget[i]);
15570 static void UnmapGameButtonsAtSamePosition_All()
15572 if (setup.show_snapshot_buttons)
15574 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15575 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15576 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15580 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15581 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15582 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15584 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15585 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15586 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15590 static void MapGameButtonsAtSamePosition(int id)
15594 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15596 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15597 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15598 MapGadget(game_gadget[i]);
15600 UnmapGameButtonsAtSamePosition_All();
15603 void MapUndoRedoButtons()
15605 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15606 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15608 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15609 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15611 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15614 void UnmapUndoRedoButtons()
15616 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15617 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15619 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15620 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15622 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15625 void MapGameButtonsExt(boolean on_tape)
15629 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15630 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15631 i != GAME_CTRL_ID_UNDO &&
15632 i != GAME_CTRL_ID_REDO)
15633 MapGadget(game_gadget[i]);
15635 UnmapGameButtonsAtSamePosition_All();
15637 RedrawGameButtons();
15640 void UnmapGameButtonsExt(boolean on_tape)
15644 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15645 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15646 UnmapGadget(game_gadget[i]);
15649 void RedrawGameButtonsExt(boolean on_tape)
15653 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15654 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15655 RedrawGadget(game_gadget[i]);
15657 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15658 redraw_mask &= ~REDRAW_ALL;
15661 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15666 gi->checked = state;
15669 void RedrawSoundButtonGadget(int id)
15671 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15672 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15673 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15674 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15675 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15676 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15679 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15680 RedrawGadget(game_gadget[id2]);
15683 void MapGameButtons()
15685 MapGameButtonsExt(FALSE);
15688 void UnmapGameButtons()
15690 UnmapGameButtonsExt(FALSE);
15693 void RedrawGameButtons()
15695 RedrawGameButtonsExt(FALSE);
15698 void MapGameButtonsOnTape()
15700 MapGameButtonsExt(TRUE);
15703 void UnmapGameButtonsOnTape()
15705 UnmapGameButtonsExt(TRUE);
15708 void RedrawGameButtonsOnTape()
15710 RedrawGameButtonsExt(TRUE);
15713 void GameUndoRedoExt()
15715 ClearPlayerAction();
15717 tape.pausing = TRUE;
15720 UpdateAndDisplayGameControlValues();
15722 DrawCompleteVideoDisplay();
15723 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15724 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15725 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15730 void GameUndo(int steps)
15732 if (!CheckEngineSnapshotList())
15735 LoadEngineSnapshot_Undo(steps);
15740 void GameRedo(int steps)
15742 if (!CheckEngineSnapshotList())
15745 LoadEngineSnapshot_Redo(steps);
15750 static void HandleGameButtonsExt(int id, int button)
15752 static boolean game_undo_executed = FALSE;
15753 int steps = BUTTON_STEPSIZE(button);
15754 boolean handle_game_buttons =
15755 (game_status == GAME_MODE_PLAYING ||
15756 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15758 if (!handle_game_buttons)
15763 case GAME_CTRL_ID_STOP:
15764 case GAME_CTRL_ID_PANEL_STOP:
15765 if (game_status == GAME_MODE_MAIN)
15771 RequestQuitGame(TRUE);
15775 case GAME_CTRL_ID_PAUSE:
15776 case GAME_CTRL_ID_PAUSE2:
15777 case GAME_CTRL_ID_PANEL_PAUSE:
15778 if (options.network && game_status == GAME_MODE_PLAYING)
15780 #if defined(NETWORK_AVALIABLE)
15782 SendToServer_ContinuePlaying();
15784 SendToServer_PausePlaying();
15788 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15790 game_undo_executed = FALSE;
15794 case GAME_CTRL_ID_PLAY:
15795 case GAME_CTRL_ID_PANEL_PLAY:
15796 if (game_status == GAME_MODE_MAIN)
15798 StartGameActions(options.network, setup.autorecord, level.random_seed);
15800 else if (tape.pausing)
15802 #if defined(NETWORK_AVALIABLE)
15803 if (options.network)
15804 SendToServer_ContinuePlaying();
15807 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15811 case GAME_CTRL_ID_UNDO:
15812 // Important: When using "save snapshot when collecting an item" mode,
15813 // load last (current) snapshot for first "undo" after pressing "pause"
15814 // (else the last-but-one snapshot would be loaded, because the snapshot
15815 // pointer already points to the last snapshot when pressing "pause",
15816 // which is fine for "every step/move" mode, but not for "every collect")
15817 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15818 !game_undo_executed)
15821 game_undo_executed = TRUE;
15826 case GAME_CTRL_ID_REDO:
15830 case GAME_CTRL_ID_SAVE:
15834 case GAME_CTRL_ID_LOAD:
15838 case SOUND_CTRL_ID_MUSIC:
15839 case SOUND_CTRL_ID_PANEL_MUSIC:
15840 if (setup.sound_music)
15842 setup.sound_music = FALSE;
15846 else if (audio.music_available)
15848 setup.sound = setup.sound_music = TRUE;
15850 SetAudioMode(setup.sound);
15852 if (game_status == GAME_MODE_PLAYING)
15856 RedrawSoundButtonGadget(id);
15860 case SOUND_CTRL_ID_LOOPS:
15861 case SOUND_CTRL_ID_PANEL_LOOPS:
15862 if (setup.sound_loops)
15863 setup.sound_loops = FALSE;
15864 else if (audio.loops_available)
15866 setup.sound = setup.sound_loops = TRUE;
15868 SetAudioMode(setup.sound);
15871 RedrawSoundButtonGadget(id);
15875 case SOUND_CTRL_ID_SIMPLE:
15876 case SOUND_CTRL_ID_PANEL_SIMPLE:
15877 if (setup.sound_simple)
15878 setup.sound_simple = FALSE;
15879 else if (audio.sound_available)
15881 setup.sound = setup.sound_simple = TRUE;
15883 SetAudioMode(setup.sound);
15886 RedrawSoundButtonGadget(id);
15895 static void HandleGameButtons(struct GadgetInfo *gi)
15897 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15900 void HandleSoundButtonKeys(Key key)
15902 if (key == setup.shortcut.sound_simple)
15903 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15904 else if (key == setup.shortcut.sound_loops)
15905 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15906 else if (key == setup.shortcut.sound_music)
15907 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);