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(int);
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 *);
1115 void ExitPlayer(struct PlayerInfo *);
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1126 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1128 if (recursion_loop_detected) \
1131 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1133 recursion_loop_detected = TRUE; \
1134 recursion_loop_element = (e); \
1137 recursion_loop_depth++; \
1140 #define RECURSION_LOOP_DETECTION_END() \
1142 recursion_loop_depth--; \
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1149 static int map_player_action[MAX_PLAYERS];
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after */
1154 /* a specified time, eventually calling a function when changing */
1155 /* ------------------------------------------------------------------------- */
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1175 struct ChangingElementInfo
1180 void (*pre_change_function)(int x, int y);
1181 void (*change_function)(int x, int y);
1182 void (*post_change_function)(int x, int y);
1185 static struct ChangingElementInfo change_delay_list[] =
1220 EL_STEEL_EXIT_OPENING,
1228 EL_STEEL_EXIT_CLOSING,
1229 EL_STEEL_EXIT_CLOSED,
1252 EL_EM_STEEL_EXIT_OPENING,
1253 EL_EM_STEEL_EXIT_OPEN,
1260 EL_EM_STEEL_EXIT_CLOSING,
1284 EL_SWITCHGATE_OPENING,
1292 EL_SWITCHGATE_CLOSING,
1293 EL_SWITCHGATE_CLOSED,
1300 EL_TIMEGATE_OPENING,
1308 EL_TIMEGATE_CLOSING,
1317 EL_ACID_SPLASH_LEFT,
1325 EL_ACID_SPLASH_RIGHT,
1334 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVATING,
1342 EL_SP_BUGGY_BASE_ACTIVE,
1349 EL_SP_BUGGY_BASE_ACTIVE,
1373 EL_ROBOT_WHEEL_ACTIVE,
1381 EL_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390 EL_DC_TIMEGATE_SWITCH,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1398 EL_EMC_MAGIC_BALL_ACTIVE,
1405 EL_EMC_SPRING_BUMPER_ACTIVE,
1406 EL_EMC_SPRING_BUMPER,
1413 EL_DIAGONAL_SHRINKING,
1421 EL_DIAGONAL_GROWING,
1442 int push_delay_fixed, push_delay_random;
1446 { EL_SPRING, 0, 0 },
1447 { EL_BALLOON, 0, 0 },
1449 { EL_SOKOBAN_OBJECT, 2, 0 },
1450 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1451 { EL_SATELLITE, 2, 0 },
1452 { EL_SP_DISK_YELLOW, 2, 0 },
1454 { EL_UNDEFINED, 0, 0 },
1462 move_stepsize_list[] =
1464 { EL_AMOEBA_DROP, 2 },
1465 { EL_AMOEBA_DROPPING, 2 },
1466 { EL_QUICKSAND_FILLING, 1 },
1467 { EL_QUICKSAND_EMPTYING, 1 },
1468 { EL_QUICKSAND_FAST_FILLING, 2 },
1469 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470 { EL_MAGIC_WALL_FILLING, 2 },
1471 { EL_MAGIC_WALL_EMPTYING, 2 },
1472 { EL_BD_MAGIC_WALL_FILLING, 2 },
1473 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_DC_MAGIC_WALL_FILLING, 2 },
1475 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_UNDEFINED, 0 },
1485 collect_count_list[] =
1488 { EL_BD_DIAMOND, 1 },
1489 { EL_EMERALD_YELLOW, 1 },
1490 { EL_EMERALD_RED, 1 },
1491 { EL_EMERALD_PURPLE, 1 },
1493 { EL_SP_INFOTRON, 1 },
1497 { EL_UNDEFINED, 0 },
1505 access_direction_list[] =
1507 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1510 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1513 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1514 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1515 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1516 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1517 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1519 { EL_SP_PORT_LEFT, MV_RIGHT },
1520 { EL_SP_PORT_RIGHT, MV_LEFT },
1521 { EL_SP_PORT_UP, MV_DOWN },
1522 { EL_SP_PORT_DOWN, MV_UP },
1523 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1524 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1525 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1527 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1528 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1530 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1531 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1532 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1533 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1534 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1535 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1536 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1537 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1539 { EL_UNDEFINED, MV_NONE }
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1544 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1547 IS_JUST_CHANGING(x, y))
1549 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1557 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1558 (y) >= 0 && (y) <= lev_fieldy - 1; \
1559 (y) += playfield_scan_delta_y) \
1560 for ((x) = playfield_scan_start_x; \
1561 (x) >= 0 && (x) <= lev_fieldx - 1; \
1562 (x) += playfield_scan_delta_x)
1565 void DEBUG_SetMaximumDynamite()
1569 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571 local_player->inventory_element[local_player->inventory_size++] =
1576 static void InitPlayfieldScanModeVars()
1578 if (game.use_reverse_scan_direction)
1580 playfield_scan_start_x = lev_fieldx - 1;
1581 playfield_scan_start_y = lev_fieldy - 1;
1583 playfield_scan_delta_x = -1;
1584 playfield_scan_delta_y = -1;
1588 playfield_scan_start_x = 0;
1589 playfield_scan_start_y = 0;
1591 playfield_scan_delta_x = 1;
1592 playfield_scan_delta_y = 1;
1596 static void InitPlayfieldScanMode(int mode)
1598 game.use_reverse_scan_direction =
1599 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1601 InitPlayfieldScanModeVars();
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1607 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1609 /* make sure that stepsize value is always a power of 2 */
1610 move_stepsize = (1 << log_2(move_stepsize));
1612 return TILEX / move_stepsize;
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618 int player_nr = player->index_nr;
1619 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1622 /* do no immediately change move delay -- the player might just be moving */
1623 player->move_delay_value_next = move_delay;
1625 /* information if player can move must be set separately */
1626 player->cannot_move = cannot_move;
1630 player->move_delay = game.initial_move_delay[player_nr];
1631 player->move_delay_value = game.initial_move_delay_value[player_nr];
1633 player->move_delay_value_next = -1;
1635 player->move_delay_reset_counter = 0;
1639 void GetPlayerConfig()
1641 GameFrameDelay = setup.game_frame_delay;
1643 if (!audio.sound_available)
1644 setup.sound_simple = FALSE;
1646 if (!audio.loops_available)
1647 setup.sound_loops = FALSE;
1649 if (!audio.music_available)
1650 setup.sound_music = FALSE;
1652 if (!video.fullscreen_available)
1653 setup.fullscreen = FALSE;
1655 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1657 SetAudioMode(setup.sound);
1660 int GetElementFromGroupElement(int element)
1662 if (IS_GROUP_ELEMENT(element))
1664 struct ElementGroupInfo *group = element_info[element].group;
1665 int last_anim_random_frame = gfx.anim_random_frame;
1668 if (group->choice_mode == ANIM_RANDOM)
1669 gfx.anim_random_frame = RND(group->num_elements_resolved);
1671 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672 group->choice_mode, 0,
1675 if (group->choice_mode == ANIM_RANDOM)
1676 gfx.anim_random_frame = last_anim_random_frame;
1678 group->choice_pos++;
1680 element = group->element_resolved[element_pos];
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1688 if (element == EL_SP_MURPHY)
1692 if (stored_player[0].present)
1694 Feld[x][y] = EL_SP_MURPHY_CLONE;
1700 stored_player[0].initial_element = element;
1701 stored_player[0].use_murphy = TRUE;
1703 if (!level.use_artwork_element[0])
1704 stored_player[0].artwork_element = EL_SP_MURPHY;
1707 Feld[x][y] = EL_PLAYER_1;
1713 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714 int jx = player->jx, jy = player->jy;
1716 player->present = TRUE;
1718 player->block_last_field = (element == EL_SP_MURPHY ?
1719 level.sp_block_last_field :
1720 level.block_last_field);
1722 /* ---------- initialize player's last field block delay --------------- */
1724 /* always start with reliable default value (no adjustment needed) */
1725 player->block_delay_adjustment = 0;
1727 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728 if (player->block_last_field && element == EL_SP_MURPHY)
1729 player->block_delay_adjustment = 1;
1731 /* special case 2: in game engines before 3.1.1, blocking was different */
1732 if (game.use_block_last_field_bug)
1733 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1735 if (!network.enabled || player->connected_network)
1737 player->active = TRUE;
1739 /* remove potentially duplicate players */
1740 if (StorePlayer[jx][jy] == Feld[x][y])
1741 StorePlayer[jx][jy] = 0;
1743 StorePlayer[x][y] = Feld[x][y];
1745 #if DEBUG_INIT_PLAYER
1748 printf("- player element %d activated", player->element_nr);
1749 printf(" (local player is %d and currently %s)\n",
1750 local_player->element_nr,
1751 local_player->active ? "active" : "not active");
1756 Feld[x][y] = EL_EMPTY;
1758 player->jx = player->last_jx = x;
1759 player->jy = player->last_jy = y;
1764 int player_nr = GET_PLAYER_NR(element);
1765 struct PlayerInfo *player = &stored_player[player_nr];
1767 if (player->active && player->killed)
1768 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1772 static void InitField(int x, int y, boolean init_game)
1774 int element = Feld[x][y];
1783 InitPlayerField(x, y, element, init_game);
1786 case EL_SOKOBAN_FIELD_PLAYER:
1787 element = Feld[x][y] = EL_PLAYER_1;
1788 InitField(x, y, init_game);
1790 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791 InitField(x, y, init_game);
1794 case EL_SOKOBAN_FIELD_EMPTY:
1795 local_player->sokobanfields_still_needed++;
1799 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1817 case EL_SPACESHIP_RIGHT:
1818 case EL_SPACESHIP_UP:
1819 case EL_SPACESHIP_LEFT:
1820 case EL_SPACESHIP_DOWN:
1821 case EL_BD_BUTTERFLY:
1822 case EL_BD_BUTTERFLY_RIGHT:
1823 case EL_BD_BUTTERFLY_UP:
1824 case EL_BD_BUTTERFLY_LEFT:
1825 case EL_BD_BUTTERFLY_DOWN:
1827 case EL_BD_FIREFLY_RIGHT:
1828 case EL_BD_FIREFLY_UP:
1829 case EL_BD_FIREFLY_LEFT:
1830 case EL_BD_FIREFLY_DOWN:
1831 case EL_PACMAN_RIGHT:
1833 case EL_PACMAN_LEFT:
1834 case EL_PACMAN_DOWN:
1836 case EL_YAMYAM_LEFT:
1837 case EL_YAMYAM_RIGHT:
1839 case EL_YAMYAM_DOWN:
1840 case EL_DARK_YAMYAM:
1843 case EL_SP_SNIKSNAK:
1844 case EL_SP_ELECTRON:
1853 case EL_AMOEBA_FULL:
1858 case EL_AMOEBA_DROP:
1859 if (y == lev_fieldy - 1)
1861 Feld[x][y] = EL_AMOEBA_GROWING;
1862 Store[x][y] = EL_AMOEBA_WET;
1866 case EL_DYNAMITE_ACTIVE:
1867 case EL_SP_DISK_RED_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872 MovDelay[x][y] = 96;
1875 case EL_EM_DYNAMITE_ACTIVE:
1876 MovDelay[x][y] = 32;
1880 local_player->lights_still_needed++;
1884 local_player->friends_still_needed++;
1889 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1892 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1906 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1910 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1912 game.belt_dir[belt_nr] = belt_dir;
1913 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1915 else /* more than one switch -- set it like the first switch */
1917 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1922 case EL_LIGHT_SWITCH_ACTIVE:
1924 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1927 case EL_INVISIBLE_STEELWALL:
1928 case EL_INVISIBLE_WALL:
1929 case EL_INVISIBLE_SAND:
1930 if (game.light_time_left > 0 ||
1931 game.lenses_time_left > 0)
1932 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1935 case EL_EMC_MAGIC_BALL:
1936 if (game.ball_state)
1937 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1940 case EL_EMC_MAGIC_BALL_SWITCH:
1941 if (game.ball_state)
1942 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1945 case EL_TRIGGER_PLAYER:
1946 case EL_TRIGGER_ELEMENT:
1947 case EL_TRIGGER_CE_VALUE:
1948 case EL_TRIGGER_CE_SCORE:
1950 case EL_ANY_ELEMENT:
1951 case EL_CURRENT_CE_VALUE:
1952 case EL_CURRENT_CE_SCORE:
1969 /* reference elements should not be used on the playfield */
1970 Feld[x][y] = EL_EMPTY;
1974 if (IS_CUSTOM_ELEMENT(element))
1976 if (CAN_MOVE(element))
1979 if (!element_info[element].use_last_ce_value || init_game)
1980 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1982 else if (IS_GROUP_ELEMENT(element))
1984 Feld[x][y] = GetElementFromGroupElement(element);
1986 InitField(x, y, init_game);
1993 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1998 InitField(x, y, init_game);
2000 /* not needed to call InitMovDir() -- already done by InitField()! */
2001 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002 CAN_MOVE(Feld[x][y]))
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2008 int old_element = Feld[x][y];
2010 InitField(x, y, init_game);
2012 /* not needed to call InitMovDir() -- already done by InitField()! */
2013 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014 CAN_MOVE(old_element) &&
2015 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2018 /* this case is in fact a combination of not less than three bugs:
2019 first, it calls InitMovDir() for elements that can move, although this is
2020 already done by InitField(); then, it checks the element that was at this
2021 field _before_ the call to InitField() (which can change it); lastly, it
2022 was not called for "mole with direction" elements, which were treated as
2023 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2027 static int get_key_element_from_nr(int key_nr)
2029 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031 EL_EM_KEY_1 : EL_KEY_1);
2033 return key_base_element + key_nr;
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2038 return (player->inventory_size > 0 ?
2039 player->inventory_element[player->inventory_size - 1] :
2040 player->inventory_infinite_element != EL_UNDEFINED ?
2041 player->inventory_infinite_element :
2042 player->dynabombs_left > 0 ?
2043 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2049 /* pos >= 0: get element from bottom of the stack;
2050 pos < 0: get element from top of the stack */
2054 int min_inventory_size = -pos;
2055 int inventory_pos = player->inventory_size - min_inventory_size;
2056 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2058 return (player->inventory_size >= min_inventory_size ?
2059 player->inventory_element[inventory_pos] :
2060 player->inventory_infinite_element != EL_UNDEFINED ?
2061 player->inventory_infinite_element :
2062 player->dynabombs_left >= min_dynabombs_left ?
2063 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2068 int min_dynabombs_left = pos + 1;
2069 int min_inventory_size = pos + 1 - player->dynabombs_left;
2070 int inventory_pos = pos - player->dynabombs_left;
2072 return (player->inventory_infinite_element != EL_UNDEFINED ?
2073 player->inventory_infinite_element :
2074 player->dynabombs_left >= min_dynabombs_left ?
2075 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076 player->inventory_size >= min_inventory_size ?
2077 player->inventory_element[inventory_pos] :
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2084 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2088 if (gpo1->sort_priority != gpo2->sort_priority)
2089 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2091 compare_result = gpo1->nr - gpo2->nr;
2093 return compare_result;
2096 int getPlayerInventorySize(int player_nr)
2098 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099 return level.native_em_level->ply[player_nr]->dynamite;
2100 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101 return level.native_sp_level->game_sp->red_disk_count;
2103 return stored_player[player_nr].inventory_size;
2106 void InitGameControlValues()
2110 for (i = 0; game_panel_controls[i].nr != -1; i++)
2112 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114 struct TextPosInfo *pos = gpc->pos;
2116 int type = gpc->type;
2120 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121 Error(ERR_EXIT, "this should not happen -- please debug");
2124 /* force update of game controls after initialization */
2125 gpc->value = gpc->last_value = -1;
2126 gpc->frame = gpc->last_frame = -1;
2127 gpc->gfx_frame = -1;
2129 /* determine panel value width for later calculation of alignment */
2130 if (type == TYPE_INTEGER || type == TYPE_STRING)
2132 pos->width = pos->size * getFontWidth(pos->font);
2133 pos->height = getFontHeight(pos->font);
2135 else if (type == TYPE_ELEMENT)
2137 pos->width = pos->size;
2138 pos->height = pos->size;
2141 /* fill structure for game panel draw order */
2143 gpo->sort_priority = pos->sort_priority;
2146 /* sort game panel controls according to sort_priority and control number */
2147 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2151 void UpdatePlayfieldElementCount()
2153 boolean use_element_count = FALSE;
2156 /* first check if it is needed at all to calculate playfield element count */
2157 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159 use_element_count = TRUE;
2161 if (!use_element_count)
2164 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165 element_info[i].element_count = 0;
2167 SCAN_PLAYFIELD(x, y)
2169 element_info[Feld[x][y]].element_count++;
2172 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174 if (IS_IN_GROUP(j, i))
2175 element_info[EL_GROUP_START + i].element_count +=
2176 element_info[j].element_count;
2179 void UpdateGameControlValues()
2182 int time = (local_player->LevelSolved ?
2183 local_player->LevelSolved_CountingTime :
2184 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185 level.native_em_level->lev->time :
2186 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187 level.native_sp_level->game_sp->time_played :
2188 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189 game_mm.energy_left :
2190 game.no_time_limit ? TimePlayed : TimeLeft);
2191 int score = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingScore :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->score :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->score :
2197 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2199 local_player->score);
2200 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201 level.native_em_level->lev->required :
2202 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203 level.native_sp_level->game_sp->infotrons_still_needed :
2204 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205 game_mm.kettles_still_needed :
2206 local_player->gems_still_needed);
2207 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208 level.native_em_level->lev->required > 0 :
2209 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212 game_mm.kettles_still_needed > 0 ||
2213 game_mm.lights_still_needed > 0 :
2214 local_player->gems_still_needed > 0 ||
2215 local_player->sokobanfields_still_needed > 0 ||
2216 local_player->lights_still_needed > 0);
2217 int health = (local_player->LevelSolved ?
2218 local_player->LevelSolved_CountingHealth :
2219 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220 MM_HEALTH(game_mm.laser_overload_value) :
2221 local_player->health);
2223 UpdatePlayfieldElementCount();
2225 /* update game panel control values */
2227 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2228 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2231 for (i = 0; i < MAX_NUM_KEYS; i++)
2232 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236 if (game.centered_player_nr == -1)
2238 for (i = 0; i < MAX_PLAYERS; i++)
2240 /* only one player in Supaplex game engine */
2241 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2244 for (k = 0; k < MAX_NUM_KEYS; k++)
2246 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248 if (level.native_em_level->ply[i]->keys & (1 << k))
2249 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250 get_key_element_from_nr(k);
2252 else if (stored_player[i].key[k])
2253 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2254 get_key_element_from_nr(k);
2257 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258 getPlayerInventorySize(i);
2260 if (stored_player[i].num_white_keys > 0)
2261 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2265 stored_player[i].num_white_keys;
2270 int player_nr = game.centered_player_nr;
2272 for (k = 0; k < MAX_NUM_KEYS; k++)
2274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2277 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278 get_key_element_from_nr(k);
2280 else if (stored_player[player_nr].key[k])
2281 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282 get_key_element_from_nr(k);
2285 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286 getPlayerInventorySize(player_nr);
2288 if (stored_player[player_nr].num_white_keys > 0)
2289 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2292 stored_player[player_nr].num_white_keys;
2295 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2298 get_inventory_element_from_pos(local_player, i);
2299 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, -i - 1);
2303 game_panel_controls[GAME_PANEL_SCORE].value = score;
2304 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306 game_panel_controls[GAME_PANEL_TIME].value = time;
2308 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2309 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2310 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312 if (level.time == 0)
2313 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2318 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326 local_player->shield_normal_time_left;
2327 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331 local_player->shield_deadly_time_left;
2333 game_panel_controls[GAME_PANEL_EXIT].value =
2334 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340 EL_EMC_MAGIC_BALL_SWITCH);
2342 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345 game.light_time_left;
2347 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350 game.timegate_time_left;
2352 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358 game.lenses_time_left;
2360 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363 game.magnify_time_left;
2365 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2367 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2369 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2370 EL_BALLOON_SWITCH_NONE);
2372 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373 local_player->dynabomb_count;
2374 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375 local_player->dynabomb_size;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379 game_panel_controls[GAME_PANEL_PENGUINS].value =
2380 local_player->friends_still_needed;
2382 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383 local_player->sokobanfields_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385 local_player->sokobanfields_still_needed;
2387 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390 for (i = 0; i < NUM_BELTS; i++)
2392 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402 game.magic_wall_time_left;
2404 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2405 local_player->gravity;
2407 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2408 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2412 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2413 game.panel.element[i].id : EL_UNDEFINED);
2415 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2417 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2418 element_info[game.panel.element_count[i].id].element_count : 0);
2420 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2422 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2423 element_info[game.panel.ce_score[i].id].collect_score : 0);
2425 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2427 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2428 element_info[game.panel.ce_score_element[i].id].collect_score :
2431 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2433 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435 /* update game panel control frames */
2437 for (i = 0; game_panel_controls[i].nr != -1; i++)
2439 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441 if (gpc->type == TYPE_ELEMENT)
2443 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445 int last_anim_random_frame = gfx.anim_random_frame;
2446 int element = gpc->value;
2447 int graphic = el2panelimg(element);
2449 if (gpc->value != gpc->last_value)
2452 gpc->gfx_random = INIT_GFX_RANDOM();
2458 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2459 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2460 gpc->gfx_random = INIT_GFX_RANDOM();
2463 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2464 gfx.anim_random_frame = gpc->gfx_random;
2466 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2467 gpc->gfx_frame = element_info[element].collect_score;
2469 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473 gfx.anim_random_frame = last_anim_random_frame;
2476 else if (gpc->type == TYPE_GRAPHIC)
2478 if (gpc->graphic != IMG_UNDEFINED)
2480 int last_anim_random_frame = gfx.anim_random_frame;
2481 int graphic = gpc->graphic;
2483 if (gpc->value != gpc->last_value)
2486 gpc->gfx_random = INIT_GFX_RANDOM();
2492 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494 gpc->gfx_random = INIT_GFX_RANDOM();
2497 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498 gfx.anim_random_frame = gpc->gfx_random;
2500 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503 gfx.anim_random_frame = last_anim_random_frame;
2509 void DisplayGameControlValues()
2511 boolean redraw_panel = FALSE;
2514 for (i = 0; game_panel_controls[i].nr != -1; i++)
2516 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518 if (PANEL_DEACTIVATED(gpc->pos))
2521 if (gpc->value == gpc->last_value &&
2522 gpc->frame == gpc->last_frame)
2525 redraw_panel = TRUE;
2531 /* copy default game door content to main double buffer */
2533 /* !!! CHECK AGAIN !!! */
2534 SetPanelBackground();
2535 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2536 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538 /* redraw game control buttons */
2539 RedrawGameButtons();
2541 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545 int nr = game_panel_order[i].nr;
2546 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2547 struct TextPosInfo *pos = gpc->pos;
2548 int type = gpc->type;
2549 int value = gpc->value;
2550 int frame = gpc->frame;
2551 int size = pos->size;
2552 int font = pos->font;
2553 boolean draw_masked = pos->draw_masked;
2554 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556 if (PANEL_DEACTIVATED(pos))
2559 gpc->last_value = value;
2560 gpc->last_frame = frame;
2562 if (type == TYPE_INTEGER)
2564 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2565 nr == GAME_PANEL_TIME)
2567 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569 if (use_dynamic_size) /* use dynamic number of digits */
2571 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2572 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2573 int size2 = size1 + 1;
2574 int font1 = pos->font;
2575 int font2 = pos->font_alt;
2577 size = (value < value_change ? size1 : size2);
2578 font = (value < value_change ? font1 : font2);
2582 /* correct text size if "digits" is zero or less */
2584 size = strlen(int2str(value, size));
2586 /* dynamically correct text alignment */
2587 pos->width = size * getFontWidth(font);
2589 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2590 int2str(value, size), font, mask_mode);
2592 else if (type == TYPE_ELEMENT)
2594 int element, graphic;
2598 int dst_x = PANEL_XPOS(pos);
2599 int dst_y = PANEL_YPOS(pos);
2601 if (value != EL_UNDEFINED && value != EL_EMPTY)
2604 graphic = el2panelimg(value);
2606 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2611 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2614 width = graphic_info[graphic].width * size / TILESIZE;
2615 height = graphic_info[graphic].height * size / TILESIZE;
2618 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2621 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2625 else if (type == TYPE_GRAPHIC)
2627 int graphic = gpc->graphic;
2628 int graphic_active = gpc->graphic_active;
2632 int dst_x = PANEL_XPOS(pos);
2633 int dst_y = PANEL_YPOS(pos);
2634 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2635 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637 if (graphic != IMG_UNDEFINED && !skip)
2639 if (pos->style == STYLE_REVERSE)
2640 value = 100 - value;
2642 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644 if (pos->direction & MV_HORIZONTAL)
2646 width = graphic_info[graphic_active].width * value / 100;
2647 height = graphic_info[graphic_active].height;
2649 if (pos->direction == MV_LEFT)
2651 src_x += graphic_info[graphic_active].width - width;
2652 dst_x += graphic_info[graphic_active].width - width;
2657 width = graphic_info[graphic_active].width;
2658 height = graphic_info[graphic_active].height * value / 100;
2660 if (pos->direction == MV_UP)
2662 src_y += graphic_info[graphic_active].height - height;
2663 dst_y += graphic_info[graphic_active].height - height;
2668 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676 if (pos->direction & MV_HORIZONTAL)
2678 if (pos->direction == MV_RIGHT)
2685 dst_x = PANEL_XPOS(pos);
2688 width = graphic_info[graphic].width - width;
2692 if (pos->direction == MV_DOWN)
2699 dst_y = PANEL_YPOS(pos);
2702 height = graphic_info[graphic].height - height;
2706 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2709 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2713 else if (type == TYPE_STRING)
2715 boolean active = (value != 0);
2716 char *state_normal = "off";
2717 char *state_active = "on";
2718 char *state = (active ? state_active : state_normal);
2719 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2720 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2721 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2722 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2724 if (nr == GAME_PANEL_GRAVITY_STATE)
2726 int font1 = pos->font; /* (used for normal state) */
2727 int font2 = pos->font_alt; /* (used for active state) */
2729 font = (active ? font2 : font1);
2738 /* don't truncate output if "chars" is zero or less */
2741 /* dynamically correct text alignment */
2742 pos->width = size * getFontWidth(font);
2745 s_cut = getStringCopyN(s, size);
2747 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2748 s_cut, font, mask_mode);
2754 redraw_mask |= REDRAW_DOOR_1;
2757 SetGameStatus(GAME_MODE_PLAYING);
2760 void UpdateAndDisplayGameControlValues()
2762 if (tape.deactivate_display)
2765 UpdateGameControlValues();
2766 DisplayGameControlValues();
2769 void UpdateGameDoorValues()
2771 UpdateGameControlValues();
2774 void DrawGameDoorValues()
2776 DisplayGameControlValues();
2781 =============================================================================
2783 -----------------------------------------------------------------------------
2784 initialize game engine due to level / tape version number
2785 =============================================================================
2788 static void InitGameEngine()
2790 int i, j, k, l, x, y;
2792 /* set game engine from tape file when re-playing, else from level file */
2793 game.engine_version = (tape.playing ? tape.engine_version :
2794 level.game_version);
2796 /* set single or multi-player game mode (needed for re-playing tapes) */
2797 game.team_mode = setup.team_mode;
2801 int num_players = 0;
2803 for (i = 0; i < MAX_PLAYERS; i++)
2804 if (tape.player_participates[i])
2807 /* multi-player tapes contain input data for more than one player */
2808 game.team_mode = (num_players > 1);
2811 /* ---------------------------------------------------------------------- */
2812 /* set flags for bugs and changes according to active game engine version */
2813 /* ---------------------------------------------------------------------- */
2816 Summary of bugfix/change:
2817 Fixed handling for custom elements that change when pushed by the player.
2819 Fixed/changed in version:
2823 Before 3.1.0, custom elements that "change when pushing" changed directly
2824 after the player started pushing them (until then handled in "DigField()").
2825 Since 3.1.0, these custom elements are not changed until the "pushing"
2826 move of the element is finished (now handled in "ContinueMoving()").
2828 Affected levels/tapes:
2829 The first condition is generally needed for all levels/tapes before version
2830 3.1.0, which might use the old behaviour before it was changed; known tapes
2831 that are affected are some tapes from the level set "Walpurgis Gardens" by
2833 The second condition is an exception from the above case and is needed for
2834 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2835 above (including some development versions of 3.1.0), but before it was
2836 known that this change would break tapes like the above and was fixed in
2837 3.1.1, so that the changed behaviour was active although the engine version
2838 while recording maybe was before 3.1.0. There is at least one tape that is
2839 affected by this exception, which is the tape for the one-level set "Bug
2840 Machine" by Juergen Bonhagen.
2843 game.use_change_when_pushing_bug =
2844 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2847 tape.game_version < VERSION_IDENT(3,1,1,0)));
2850 Summary of bugfix/change:
2851 Fixed handling for blocking the field the player leaves when moving.
2853 Fixed/changed in version:
2857 Before 3.1.1, when "block last field when moving" was enabled, the field
2858 the player is leaving when moving was blocked for the time of the move,
2859 and was directly unblocked afterwards. This resulted in the last field
2860 being blocked for exactly one less than the number of frames of one player
2861 move. Additionally, even when blocking was disabled, the last field was
2862 blocked for exactly one frame.
2863 Since 3.1.1, due to changes in player movement handling, the last field
2864 is not blocked at all when blocking is disabled. When blocking is enabled,
2865 the last field is blocked for exactly the number of frames of one player
2866 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2867 last field is blocked for exactly one more than the number of frames of
2870 Affected levels/tapes:
2871 (!!! yet to be determined -- probably many !!!)
2874 game.use_block_last_field_bug =
2875 (game.engine_version < VERSION_IDENT(3,1,1,0));
2877 game_em.use_single_button =
2878 (game.engine_version > VERSION_IDENT(4,0,0,2));
2880 game_em.use_snap_key_bug =
2881 (game.engine_version < VERSION_IDENT(4,0,1,0));
2883 /* ---------------------------------------------------------------------- */
2885 /* set maximal allowed number of custom element changes per game frame */
2886 game.max_num_changes_per_frame = 1;
2888 /* default scan direction: scan playfield from top/left to bottom/right */
2889 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891 /* dynamically adjust element properties according to game engine version */
2892 InitElementPropertiesEngine(game.engine_version);
2895 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2896 printf(" tape version == %06d [%s] [file: %06d]\n",
2897 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899 printf(" => game.engine_version == %06d\n", game.engine_version);
2902 /* ---------- initialize player's initial move delay --------------------- */
2904 /* dynamically adjust player properties according to level information */
2905 for (i = 0; i < MAX_PLAYERS; i++)
2906 game.initial_move_delay_value[i] =
2907 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909 /* dynamically adjust player properties according to game engine version */
2910 for (i = 0; i < MAX_PLAYERS; i++)
2911 game.initial_move_delay[i] =
2912 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2913 game.initial_move_delay_value[i] : 0);
2915 /* ---------- initialize player's initial push delay --------------------- */
2917 /* dynamically adjust player properties according to game engine version */
2918 game.initial_push_delay_value =
2919 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921 /* ---------- initialize changing elements ------------------------------- */
2923 /* initialize changing elements information */
2924 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926 struct ElementInfo *ei = &element_info[i];
2928 /* this pointer might have been changed in the level editor */
2929 ei->change = &ei->change_page[0];
2931 if (!IS_CUSTOM_ELEMENT(i))
2933 ei->change->target_element = EL_EMPTY_SPACE;
2934 ei->change->delay_fixed = 0;
2935 ei->change->delay_random = 0;
2936 ei->change->delay_frames = 1;
2939 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941 ei->has_change_event[j] = FALSE;
2943 ei->event_page_nr[j] = 0;
2944 ei->event_page[j] = &ei->change_page[0];
2948 /* add changing elements from pre-defined list */
2949 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2952 struct ElementInfo *ei = &element_info[ch_delay->element];
2954 ei->change->target_element = ch_delay->target_element;
2955 ei->change->delay_fixed = ch_delay->change_delay;
2957 ei->change->pre_change_function = ch_delay->pre_change_function;
2958 ei->change->change_function = ch_delay->change_function;
2959 ei->change->post_change_function = ch_delay->post_change_function;
2961 ei->change->can_change = TRUE;
2962 ei->change->can_change_or_has_action = TRUE;
2964 ei->has_change_event[CE_DELAY] = TRUE;
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2967 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2970 /* ---------- initialize internal run-time variables --------------------- */
2972 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976 for (j = 0; j < ei->num_change_pages; j++)
2978 ei->change_page[j].can_change_or_has_action =
2979 (ei->change_page[j].can_change |
2980 ei->change_page[j].has_action);
2984 /* add change events from custom element configuration */
2985 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989 for (j = 0; j < ei->num_change_pages; j++)
2991 if (!ei->change_page[j].can_change_or_has_action)
2994 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996 /* only add event page for the first page found with this event */
2997 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999 ei->has_change_event[k] = TRUE;
3001 ei->event_page_nr[k] = j;
3002 ei->event_page[k] = &ei->change_page[j];
3008 /* ---------- initialize reference elements in change conditions --------- */
3010 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012 int element = EL_CUSTOM_START + i;
3013 struct ElementInfo *ei = &element_info[element];
3015 for (j = 0; j < ei->num_change_pages; j++)
3017 int trigger_element = ei->change_page[j].initial_trigger_element;
3019 if (trigger_element >= EL_PREV_CE_8 &&
3020 trigger_element <= EL_NEXT_CE_8)
3021 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023 ei->change_page[j].trigger_element = trigger_element;
3027 /* ---------- initialize run-time trigger player and element ------------- */
3029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033 for (j = 0; j < ei->num_change_pages; j++)
3035 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3037 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3038 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3039 ei->change_page[j].actual_trigger_ce_value = 0;
3040 ei->change_page[j].actual_trigger_ce_score = 0;
3044 /* ---------- initialize trigger events ---------------------------------- */
3046 /* initialize trigger events information */
3047 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3048 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049 trigger_events[i][j] = FALSE;
3051 /* add trigger events from element change event properties */
3052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054 struct ElementInfo *ei = &element_info[i];
3056 for (j = 0; j < ei->num_change_pages; j++)
3058 if (!ei->change_page[j].can_change_or_has_action)
3061 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063 int trigger_element = ei->change_page[j].trigger_element;
3065 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067 if (ei->change_page[j].has_event[k])
3069 if (IS_GROUP_ELEMENT(trigger_element))
3071 struct ElementGroupInfo *group =
3072 element_info[trigger_element].group;
3074 for (l = 0; l < group->num_elements_resolved; l++)
3075 trigger_events[group->element_resolved[l]][k] = TRUE;
3077 else if (trigger_element == EL_ANY_ELEMENT)
3078 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3079 trigger_events[l][k] = TRUE;
3081 trigger_events[trigger_element][k] = TRUE;
3088 /* ---------- initialize push delay -------------------------------------- */
3090 /* initialize push delay values to default */
3091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093 if (!IS_CUSTOM_ELEMENT(i))
3095 /* set default push delay values (corrected since version 3.0.7-1) */
3096 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098 element_info[i].push_delay_fixed = 2;
3099 element_info[i].push_delay_random = 8;
3103 element_info[i].push_delay_fixed = 8;
3104 element_info[i].push_delay_random = 8;
3109 /* set push delay value for certain elements from pre-defined list */
3110 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112 int e = push_delay_list[i].element;
3114 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3115 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3118 /* set push delay value for Supaplex elements for newer engine versions */
3119 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123 if (IS_SP_ELEMENT(i))
3125 /* set SP push delay to just enough to push under a falling zonk */
3126 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128 element_info[i].push_delay_fixed = delay;
3129 element_info[i].push_delay_random = 0;
3134 /* ---------- initialize move stepsize ----------------------------------- */
3136 /* initialize move stepsize values to default */
3137 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3138 if (!IS_CUSTOM_ELEMENT(i))
3139 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141 /* set move stepsize value for certain elements from pre-defined list */
3142 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144 int e = move_stepsize_list[i].element;
3146 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3149 /* ---------- initialize collect score ----------------------------------- */
3151 /* initialize collect score values for custom elements from initial value */
3152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153 if (IS_CUSTOM_ELEMENT(i))
3154 element_info[i].collect_score = element_info[i].collect_score_initial;
3156 /* ---------- initialize collect count ----------------------------------- */
3158 /* initialize collect count values for non-custom elements */
3159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160 if (!IS_CUSTOM_ELEMENT(i))
3161 element_info[i].collect_count_initial = 0;
3163 /* add collect count values for all elements from pre-defined list */
3164 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3165 element_info[collect_count_list[i].element].collect_count_initial =
3166 collect_count_list[i].count;
3168 /* ---------- initialize access direction -------------------------------- */
3170 /* initialize access direction values to default (access from every side) */
3171 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3172 if (!IS_CUSTOM_ELEMENT(i))
3173 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175 /* set access direction value for certain elements from pre-defined list */
3176 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3177 element_info[access_direction_list[i].element].access_direction =
3178 access_direction_list[i].direction;
3180 /* ---------- initialize explosion content ------------------------------- */
3181 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183 if (IS_CUSTOM_ELEMENT(i))
3186 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190 element_info[i].content.e[x][y] =
3191 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3192 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3193 i == EL_PLAYER_3 ? EL_EMERALD :
3194 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3195 i == EL_MOLE ? EL_EMERALD_RED :
3196 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3197 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3198 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3199 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3200 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3201 i == EL_WALL_EMERALD ? EL_EMERALD :
3202 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3203 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3204 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3205 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3206 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3207 i == EL_WALL_PEARL ? EL_PEARL :
3208 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3213 /* ---------- initialize recursion detection ------------------------------ */
3214 recursion_loop_depth = 0;
3215 recursion_loop_detected = FALSE;
3216 recursion_loop_element = EL_UNDEFINED;
3218 /* ---------- initialize graphics engine ---------------------------------- */
3219 game.scroll_delay_value =
3220 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3221 setup.scroll_delay ? setup.scroll_delay_value : 0);
3222 game.scroll_delay_value =
3223 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225 /* ---------- initialize game engine snapshots ---------------------------- */
3226 for (i = 0; i < MAX_PLAYERS; i++)
3227 game.snapshot.last_action[i] = 0;
3228 game.snapshot.changed_action = FALSE;
3229 game.snapshot.collected_item = FALSE;
3230 game.snapshot.mode =
3231 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3232 SNAPSHOT_MODE_EVERY_STEP :
3233 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3234 SNAPSHOT_MODE_EVERY_MOVE :
3235 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3236 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3237 game.snapshot.save_snapshot = FALSE;
3239 /* ---------- initialize level time for Supaplex engine ------------------- */
3240 /* Supaplex levels with time limit currently unsupported -- should be added */
3241 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3245 int get_num_special_action(int element, int action_first, int action_last)
3247 int num_special_action = 0;
3250 for (i = action_first; i <= action_last; i++)
3252 boolean found = FALSE;
3254 for (j = 0; j < NUM_DIRECTIONS; j++)
3255 if (el_act_dir2img(element, i, j) !=
3256 el_act_dir2img(element, ACTION_DEFAULT, j))
3260 num_special_action++;
3265 return num_special_action;
3270 =============================================================================
3272 -----------------------------------------------------------------------------
3273 initialize and start new game
3274 =============================================================================
3277 #if DEBUG_INIT_PLAYER
3278 static void DebugPrintPlayerStatus(char *message)
3285 printf("%s:\n", message);
3287 for (i = 0; i < MAX_PLAYERS; i++)
3289 struct PlayerInfo *player = &stored_player[i];
3291 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3295 player->connected_locally,
3296 player->connected_network,
3299 if (local_player == player)
3300 printf(" (local player)");
3309 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3310 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3311 int fade_mask = REDRAW_FIELD;
3313 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3314 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3315 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3316 int initial_move_dir = MV_DOWN;
3319 // required here to update video display before fading (FIX THIS)
3320 DrawMaskedBorder(REDRAW_DOOR_2);
3322 if (!game.restart_level)
3323 CloseDoor(DOOR_CLOSE_1);
3325 SetGameStatus(GAME_MODE_PLAYING);
3327 if (level_editor_test_game)
3328 FadeSkipNextFadeIn();
3330 FadeSetEnterScreen();
3332 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3333 fade_mask = REDRAW_ALL;
3335 FadeLevelSoundsAndMusic();
3337 ExpireSoundLoops(TRUE);
3339 if (!level_editor_test_game)
3342 /* needed if different viewport properties defined for playing */
3343 ChangeViewportPropertiesIfNeeded();
3347 DrawCompleteVideoDisplay();
3349 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3352 InitGameControlValues();
3354 /* don't play tapes over network */
3355 network_playing = (network.enabled && !tape.playing);
3357 for (i = 0; i < MAX_PLAYERS; i++)
3359 struct PlayerInfo *player = &stored_player[i];
3361 player->index_nr = i;
3362 player->index_bit = (1 << i);
3363 player->element_nr = EL_PLAYER_1 + i;
3365 player->present = FALSE;
3366 player->active = FALSE;
3367 player->mapped = FALSE;
3369 player->killed = FALSE;
3370 player->reanimated = FALSE;
3373 player->effective_action = 0;
3374 player->programmed_action = 0;
3376 player->mouse_action.lx = 0;
3377 player->mouse_action.ly = 0;
3378 player->mouse_action.button = 0;
3379 player->mouse_action.button_hint = 0;
3381 player->effective_mouse_action.lx = 0;
3382 player->effective_mouse_action.ly = 0;
3383 player->effective_mouse_action.button = 0;
3384 player->effective_mouse_action.button_hint = 0;
3387 player->score_final = 0;
3389 player->health = MAX_HEALTH;
3390 player->health_final = MAX_HEALTH;
3392 player->gems_still_needed = level.gems_needed;
3393 player->sokobanfields_still_needed = 0;
3394 player->lights_still_needed = 0;
3395 player->players_still_needed = 0;
3396 player->friends_still_needed = 0;
3398 for (j = 0; j < MAX_NUM_KEYS; j++)
3399 player->key[j] = FALSE;
3401 player->num_white_keys = 0;
3403 player->dynabomb_count = 0;
3404 player->dynabomb_size = 1;
3405 player->dynabombs_left = 0;
3406 player->dynabomb_xl = FALSE;
3408 player->MovDir = initial_move_dir;
3411 player->GfxDir = initial_move_dir;
3412 player->GfxAction = ACTION_DEFAULT;
3414 player->StepFrame = 0;
3416 player->initial_element = player->element_nr;
3417 player->artwork_element =
3418 (level.use_artwork_element[i] ? level.artwork_element[i] :
3419 player->element_nr);
3420 player->use_murphy = FALSE;
3422 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3423 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3425 player->gravity = level.initial_player_gravity[i];
3427 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3429 player->actual_frame_counter = 0;
3431 player->step_counter = 0;
3433 player->last_move_dir = initial_move_dir;
3435 player->is_active = FALSE;
3437 player->is_waiting = FALSE;
3438 player->is_moving = FALSE;
3439 player->is_auto_moving = FALSE;
3440 player->is_digging = FALSE;
3441 player->is_snapping = FALSE;
3442 player->is_collecting = FALSE;
3443 player->is_pushing = FALSE;
3444 player->is_switching = FALSE;
3445 player->is_dropping = FALSE;
3446 player->is_dropping_pressed = FALSE;
3448 player->is_bored = FALSE;
3449 player->is_sleeping = FALSE;
3451 player->was_waiting = TRUE;
3452 player->was_moving = FALSE;
3453 player->was_snapping = FALSE;
3454 player->was_dropping = FALSE;
3456 player->force_dropping = FALSE;
3458 player->frame_counter_bored = -1;
3459 player->frame_counter_sleeping = -1;
3461 player->anim_delay_counter = 0;
3462 player->post_delay_counter = 0;
3464 player->dir_waiting = initial_move_dir;
3465 player->action_waiting = ACTION_DEFAULT;
3466 player->last_action_waiting = ACTION_DEFAULT;
3467 player->special_action_bored = ACTION_DEFAULT;
3468 player->special_action_sleeping = ACTION_DEFAULT;
3470 player->switch_x = -1;
3471 player->switch_y = -1;
3473 player->drop_x = -1;
3474 player->drop_y = -1;
3476 player->show_envelope = 0;
3478 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3480 player->push_delay = -1; /* initialized when pushing starts */
3481 player->push_delay_value = game.initial_push_delay_value;
3483 player->drop_delay = 0;
3484 player->drop_pressed_delay = 0;
3486 player->last_jx = -1;
3487 player->last_jy = -1;
3491 player->shield_normal_time_left = 0;
3492 player->shield_deadly_time_left = 0;
3494 player->inventory_infinite_element = EL_UNDEFINED;
3495 player->inventory_size = 0;
3497 if (level.use_initial_inventory[i])
3499 for (j = 0; j < level.initial_inventory_size[i]; j++)
3501 int element = level.initial_inventory_content[i][j];
3502 int collect_count = element_info[element].collect_count_initial;
3505 if (!IS_CUSTOM_ELEMENT(element))
3508 if (collect_count == 0)
3509 player->inventory_infinite_element = element;
3511 for (k = 0; k < collect_count; k++)
3512 if (player->inventory_size < MAX_INVENTORY_SIZE)
3513 player->inventory_element[player->inventory_size++] = element;
3517 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3518 SnapField(player, 0, 0);
3520 player->LevelSolved = FALSE;
3521 player->GameOver = FALSE;
3523 player->LevelSolved_GameWon = FALSE;
3524 player->LevelSolved_GameEnd = FALSE;
3525 player->LevelSolved_PanelOff = FALSE;
3526 player->LevelSolved_SaveTape = FALSE;
3527 player->LevelSolved_SaveScore = FALSE;
3529 player->LevelSolved_CountingTime = 0;
3530 player->LevelSolved_CountingScore = 0;
3531 player->LevelSolved_CountingHealth = 0;
3533 map_player_action[i] = i;
3536 network_player_action_received = FALSE;
3538 /* initial null action */
3539 if (network_playing)
3540 SendToServer_MovePlayer(MV_NONE);
3548 TimeLeft = level.time;
3551 ScreenMovDir = MV_NONE;
3555 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3557 AllPlayersGone = FALSE;
3559 game.no_time_limit = (level.time == 0);
3561 game.yamyam_content_nr = 0;
3562 game.robot_wheel_active = FALSE;
3563 game.magic_wall_active = FALSE;
3564 game.magic_wall_time_left = 0;
3565 game.light_time_left = 0;
3566 game.timegate_time_left = 0;
3567 game.switchgate_pos = 0;
3568 game.wind_direction = level.wind_direction_initial;
3570 game.lenses_time_left = 0;
3571 game.magnify_time_left = 0;
3573 game.ball_state = level.ball_state_initial;
3574 game.ball_content_nr = 0;
3576 game.envelope_active = FALSE;
3578 for (i = 0; i < NUM_BELTS; i++)
3580 game.belt_dir[i] = MV_NONE;
3581 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3584 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3585 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3587 #if DEBUG_INIT_PLAYER
3588 DebugPrintPlayerStatus("Player status at level initialization");
3591 SCAN_PLAYFIELD(x, y)
3593 Feld[x][y] = level.field[x][y];
3594 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3595 ChangeDelay[x][y] = 0;
3596 ChangePage[x][y] = -1;
3597 CustomValue[x][y] = 0; /* initialized in InitField() */
3598 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3600 WasJustMoving[x][y] = 0;
3601 WasJustFalling[x][y] = 0;
3602 CheckCollision[x][y] = 0;
3603 CheckImpact[x][y] = 0;
3605 Pushed[x][y] = FALSE;
3607 ChangeCount[x][y] = 0;
3608 ChangeEvent[x][y] = -1;
3610 ExplodePhase[x][y] = 0;
3611 ExplodeDelay[x][y] = 0;
3612 ExplodeField[x][y] = EX_TYPE_NONE;
3614 RunnerVisit[x][y] = 0;
3615 PlayerVisit[x][y] = 0;
3618 GfxRandom[x][y] = INIT_GFX_RANDOM();
3619 GfxElement[x][y] = EL_UNDEFINED;
3620 GfxAction[x][y] = ACTION_DEFAULT;
3621 GfxDir[x][y] = MV_NONE;
3622 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3625 SCAN_PLAYFIELD(x, y)
3627 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3629 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3631 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3634 InitField(x, y, TRUE);
3636 ResetGfxAnimation(x, y);
3641 for (i = 0; i < MAX_PLAYERS; i++)
3643 struct PlayerInfo *player = &stored_player[i];
3645 /* set number of special actions for bored and sleeping animation */
3646 player->num_special_action_bored =
3647 get_num_special_action(player->artwork_element,
3648 ACTION_BORING_1, ACTION_BORING_LAST);
3649 player->num_special_action_sleeping =
3650 get_num_special_action(player->artwork_element,
3651 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3654 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3655 emulate_sb ? EMU_SOKOBAN :
3656 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3658 /* initialize type of slippery elements */
3659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661 if (!IS_CUSTOM_ELEMENT(i))
3663 /* default: elements slip down either to the left or right randomly */
3664 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3666 /* SP style elements prefer to slip down on the left side */
3667 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3668 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3670 /* BD style elements prefer to slip down on the left side */
3671 if (game.emulation == EMU_BOULDERDASH)
3672 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3676 /* initialize explosion and ignition delay */
3677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3679 if (!IS_CUSTOM_ELEMENT(i))
3682 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3683 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3684 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3685 int last_phase = (num_phase + 1) * delay;
3686 int half_phase = (num_phase / 2) * delay;
3688 element_info[i].explosion_delay = last_phase - 1;
3689 element_info[i].ignition_delay = half_phase;
3691 if (i == EL_BLACK_ORB)
3692 element_info[i].ignition_delay = 1;
3696 /* correct non-moving belts to start moving left */
3697 for (i = 0; i < NUM_BELTS; i++)
3698 if (game.belt_dir[i] == MV_NONE)
3699 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3701 #if USE_NEW_PLAYER_ASSIGNMENTS
3702 for (i = 0; i < MAX_PLAYERS; i++)
3704 stored_player[i].connected = FALSE;
3706 /* in network game mode, the local player might not be the first player */
3707 if (stored_player[i].connected_locally)
3708 local_player = &stored_player[i];
3711 if (!network.enabled)
3712 local_player->connected = TRUE;
3716 for (i = 0; i < MAX_PLAYERS; i++)
3717 stored_player[i].connected = tape.player_participates[i];
3719 else if (network.enabled)
3721 /* add team mode players connected over the network (needed for correct
3722 assignment of player figures from level to locally playing players) */
3724 for (i = 0; i < MAX_PLAYERS; i++)
3725 if (stored_player[i].connected_network)
3726 stored_player[i].connected = TRUE;
3728 else if (game.team_mode)
3730 /* try to guess locally connected team mode players (needed for correct
3731 assignment of player figures from level to locally playing players) */
3733 for (i = 0; i < MAX_PLAYERS; i++)
3734 if (setup.input[i].use_joystick ||
3735 setup.input[i].key.left != KSYM_UNDEFINED)
3736 stored_player[i].connected = TRUE;
3739 #if DEBUG_INIT_PLAYER
3740 DebugPrintPlayerStatus("Player status after level initialization");
3743 #if DEBUG_INIT_PLAYER
3745 printf("Reassigning players ...\n");
3748 /* check if any connected player was not found in playfield */
3749 for (i = 0; i < MAX_PLAYERS; i++)
3751 struct PlayerInfo *player = &stored_player[i];
3753 if (player->connected && !player->present)
3755 struct PlayerInfo *field_player = NULL;
3757 #if DEBUG_INIT_PLAYER
3759 printf("- looking for field player for player %d ...\n", i + 1);
3762 /* assign first free player found that is present in the playfield */
3764 /* first try: look for unmapped playfield player that is not connected */
3765 for (j = 0; j < MAX_PLAYERS; j++)
3766 if (field_player == NULL &&
3767 stored_player[j].present &&
3768 !stored_player[j].mapped &&
3769 !stored_player[j].connected)
3770 field_player = &stored_player[j];
3772 /* second try: look for *any* unmapped playfield player */
3773 for (j = 0; j < MAX_PLAYERS; j++)
3774 if (field_player == NULL &&
3775 stored_player[j].present &&
3776 !stored_player[j].mapped)
3777 field_player = &stored_player[j];
3779 if (field_player != NULL)
3781 int jx = field_player->jx, jy = field_player->jy;
3783 #if DEBUG_INIT_PLAYER
3785 printf("- found player %d\n", field_player->index_nr + 1);
3788 player->present = FALSE;
3789 player->active = FALSE;
3791 field_player->present = TRUE;
3792 field_player->active = TRUE;
3795 player->initial_element = field_player->initial_element;
3796 player->artwork_element = field_player->artwork_element;
3798 player->block_last_field = field_player->block_last_field;
3799 player->block_delay_adjustment = field_player->block_delay_adjustment;
3802 StorePlayer[jx][jy] = field_player->element_nr;
3804 field_player->jx = field_player->last_jx = jx;
3805 field_player->jy = field_player->last_jy = jy;
3807 if (local_player == player)
3808 local_player = field_player;
3810 map_player_action[field_player->index_nr] = i;
3812 field_player->mapped = TRUE;
3814 #if DEBUG_INIT_PLAYER
3816 printf("- map_player_action[%d] == %d\n",
3817 field_player->index_nr + 1, i + 1);
3822 if (player->connected && player->present)
3823 player->mapped = TRUE;
3826 #if DEBUG_INIT_PLAYER
3827 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3832 /* check if any connected player was not found in playfield */
3833 for (i = 0; i < MAX_PLAYERS; i++)
3835 struct PlayerInfo *player = &stored_player[i];
3837 if (player->connected && !player->present)
3839 for (j = 0; j < MAX_PLAYERS; j++)
3841 struct PlayerInfo *field_player = &stored_player[j];
3842 int jx = field_player->jx, jy = field_player->jy;
3844 /* assign first free player found that is present in the playfield */
3845 if (field_player->present && !field_player->connected)
3847 player->present = TRUE;
3848 player->active = TRUE;
3850 field_player->present = FALSE;
3851 field_player->active = FALSE;
3853 player->initial_element = field_player->initial_element;
3854 player->artwork_element = field_player->artwork_element;
3856 player->block_last_field = field_player->block_last_field;
3857 player->block_delay_adjustment = field_player->block_delay_adjustment;
3859 StorePlayer[jx][jy] = player->element_nr;
3861 player->jx = player->last_jx = jx;
3862 player->jy = player->last_jy = jy;
3872 printf("::: local_player->present == %d\n", local_player->present);
3875 /* set focus to local player for network games, else to all players */
3876 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3877 game.centered_player_nr_next = game.centered_player_nr;
3878 game.set_centered_player = FALSE;
3880 if (network_playing && tape.recording)
3882 /* store client dependent player focus when recording network games */
3883 tape.centered_player_nr_next = game.centered_player_nr_next;
3884 tape.set_centered_player = TRUE;
3889 /* when playing a tape, eliminate all players who do not participate */
3891 #if USE_NEW_PLAYER_ASSIGNMENTS
3893 if (!game.team_mode)
3895 for (i = 0; i < MAX_PLAYERS; i++)
3897 if (stored_player[i].active &&
3898 !tape.player_participates[map_player_action[i]])
3900 struct PlayerInfo *player = &stored_player[i];
3901 int jx = player->jx, jy = player->jy;
3903 #if DEBUG_INIT_PLAYER
3905 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3908 player->active = FALSE;
3909 StorePlayer[jx][jy] = 0;
3910 Feld[jx][jy] = EL_EMPTY;
3917 for (i = 0; i < MAX_PLAYERS; i++)
3919 if (stored_player[i].active &&
3920 !tape.player_participates[i])
3922 struct PlayerInfo *player = &stored_player[i];
3923 int jx = player->jx, jy = player->jy;
3925 player->active = FALSE;
3926 StorePlayer[jx][jy] = 0;
3927 Feld[jx][jy] = EL_EMPTY;
3932 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3934 /* when in single player mode, eliminate all but the local player */
3936 for (i = 0; i < MAX_PLAYERS; i++)
3938 struct PlayerInfo *player = &stored_player[i];
3940 if (player->active && player != local_player)
3942 int jx = player->jx, jy = player->jy;
3944 player->active = FALSE;
3945 player->present = FALSE;
3947 StorePlayer[jx][jy] = 0;
3948 Feld[jx][jy] = EL_EMPTY;
3953 for (i = 0; i < MAX_PLAYERS; i++)
3954 if (stored_player[i].active)
3955 local_player->players_still_needed++;
3957 /* when recording the game, store which players take part in the game */
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961 for (i = 0; i < MAX_PLAYERS; i++)
3962 if (stored_player[i].connected)
3963 tape.player_participates[i] = TRUE;
3965 for (i = 0; i < MAX_PLAYERS; i++)
3966 if (stored_player[i].active)
3967 tape.player_participates[i] = TRUE;
3971 #if DEBUG_INIT_PLAYER
3972 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3975 if (BorderElement == EL_EMPTY)
3978 SBX_Right = lev_fieldx - SCR_FIELDX;
3980 SBY_Lower = lev_fieldy - SCR_FIELDY;
3985 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3987 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3990 if (full_lev_fieldx <= SCR_FIELDX)
3991 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3992 if (full_lev_fieldy <= SCR_FIELDY)
3993 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3995 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3997 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4000 /* if local player not found, look for custom element that might create
4001 the player (make some assumptions about the right custom element) */
4002 if (!local_player->present)
4004 int start_x = 0, start_y = 0;
4005 int found_rating = 0;
4006 int found_element = EL_UNDEFINED;
4007 int player_nr = local_player->index_nr;
4009 SCAN_PLAYFIELD(x, y)
4011 int element = Feld[x][y];
4016 if (level.use_start_element[player_nr] &&
4017 level.start_element[player_nr] == element &&
4024 found_element = element;
4027 if (!IS_CUSTOM_ELEMENT(element))
4030 if (CAN_CHANGE(element))
4032 for (i = 0; i < element_info[element].num_change_pages; i++)
4034 /* check for player created from custom element as single target */
4035 content = element_info[element].change_page[i].target_element;
4036 is_player = ELEM_IS_PLAYER(content);
4038 if (is_player && (found_rating < 3 ||
4039 (found_rating == 3 && element < found_element)))
4045 found_element = element;
4050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4052 /* check for player created from custom element as explosion content */
4053 content = element_info[element].content.e[xx][yy];
4054 is_player = ELEM_IS_PLAYER(content);
4056 if (is_player && (found_rating < 2 ||
4057 (found_rating == 2 && element < found_element)))
4059 start_x = x + xx - 1;
4060 start_y = y + yy - 1;
4063 found_element = element;
4066 if (!CAN_CHANGE(element))
4069 for (i = 0; i < element_info[element].num_change_pages; i++)
4071 /* check for player created from custom element as extended target */
4073 element_info[element].change_page[i].target_content.e[xx][yy];
4075 is_player = ELEM_IS_PLAYER(content);
4077 if (is_player && (found_rating < 1 ||
4078 (found_rating == 1 && element < found_element)))
4080 start_x = x + xx - 1;
4081 start_y = y + yy - 1;
4084 found_element = element;
4090 scroll_x = SCROLL_POSITION_X(start_x);
4091 scroll_y = SCROLL_POSITION_Y(start_y);
4095 scroll_x = SCROLL_POSITION_X(local_player->jx);
4096 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4099 /* !!! FIX THIS (START) !!! */
4100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4102 InitGameEngine_EM();
4104 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4106 InitGameEngine_SP();
4108 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4110 InitGameEngine_MM();
4114 DrawLevel(REDRAW_FIELD);
4117 /* after drawing the level, correct some elements */
4118 if (game.timegate_time_left == 0)
4119 CloseAllOpenTimegates();
4122 /* blit playfield from scroll buffer to normal back buffer for fading in */
4123 BlitScreenToBitmap(backbuffer);
4124 /* !!! FIX THIS (END) !!! */
4126 DrawMaskedBorder(fade_mask);
4131 // full screen redraw is required at this point in the following cases:
4132 // - special editor door undrawn when game was started from level editor
4133 // - drawing area (playfield) was changed and has to be removed completely
4134 redraw_mask = REDRAW_ALL;
4138 if (!game.restart_level)
4140 /* copy default game door content to main double buffer */
4142 /* !!! CHECK AGAIN !!! */
4143 SetPanelBackground();
4144 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4145 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4148 SetPanelBackground();
4149 SetDrawBackgroundMask(REDRAW_DOOR_1);
4151 UpdateAndDisplayGameControlValues();
4153 if (!game.restart_level)
4159 CreateGameButtons();
4164 /* copy actual game door content to door double buffer for OpenDoor() */
4165 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167 OpenDoor(DOOR_OPEN_ALL);
4169 KeyboardAutoRepeatOffUnlessAutoplay();
4171 #if DEBUG_INIT_PLAYER
4172 DebugPrintPlayerStatus("Player status (final)");
4181 if (!game.restart_level && !tape.playing)
4183 LevelStats_incPlayed(level_nr);
4185 SaveLevelSetup_SeriesInfo();
4188 game.restart_level = FALSE;
4189 game.restart_game_message = NULL;
4191 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4192 InitGameActions_MM();
4194 SaveEngineSnapshotToListInitial();
4196 if (!game.restart_level)
4198 PlaySound(SND_GAME_STARTING);
4200 if (setup.sound_music)
4205 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4206 int actual_player_x, int actual_player_y)
4208 /* this is used for non-R'n'D game engines to update certain engine values */
4210 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4212 actual_player_x = correctLevelPosX_EM(actual_player_x);
4213 actual_player_y = correctLevelPosY_EM(actual_player_y);
4216 /* needed to determine if sounds are played within the visible screen area */
4217 scroll_x = actual_scroll_x;
4218 scroll_y = actual_scroll_y;
4220 /* needed to get player position for "follow finger" playing input method */
4221 local_player->jx = actual_player_x;
4222 local_player->jy = actual_player_y;
4225 void InitMovDir(int x, int y)
4227 int i, element = Feld[x][y];
4228 static int xy[4][2] =
4235 static int direction[3][4] =
4237 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4238 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4239 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4248 Feld[x][y] = EL_BUG;
4249 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4252 case EL_SPACESHIP_RIGHT:
4253 case EL_SPACESHIP_UP:
4254 case EL_SPACESHIP_LEFT:
4255 case EL_SPACESHIP_DOWN:
4256 Feld[x][y] = EL_SPACESHIP;
4257 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4260 case EL_BD_BUTTERFLY_RIGHT:
4261 case EL_BD_BUTTERFLY_UP:
4262 case EL_BD_BUTTERFLY_LEFT:
4263 case EL_BD_BUTTERFLY_DOWN:
4264 Feld[x][y] = EL_BD_BUTTERFLY;
4265 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4268 case EL_BD_FIREFLY_RIGHT:
4269 case EL_BD_FIREFLY_UP:
4270 case EL_BD_FIREFLY_LEFT:
4271 case EL_BD_FIREFLY_DOWN:
4272 Feld[x][y] = EL_BD_FIREFLY;
4273 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4276 case EL_PACMAN_RIGHT:
4278 case EL_PACMAN_LEFT:
4279 case EL_PACMAN_DOWN:
4280 Feld[x][y] = EL_PACMAN;
4281 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4284 case EL_YAMYAM_LEFT:
4285 case EL_YAMYAM_RIGHT:
4287 case EL_YAMYAM_DOWN:
4288 Feld[x][y] = EL_YAMYAM;
4289 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4292 case EL_SP_SNIKSNAK:
4293 MovDir[x][y] = MV_UP;
4296 case EL_SP_ELECTRON:
4297 MovDir[x][y] = MV_LEFT;
4304 Feld[x][y] = EL_MOLE;
4305 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4309 if (IS_CUSTOM_ELEMENT(element))
4311 struct ElementInfo *ei = &element_info[element];
4312 int move_direction_initial = ei->move_direction_initial;
4313 int move_pattern = ei->move_pattern;
4315 if (move_direction_initial == MV_START_PREVIOUS)
4317 if (MovDir[x][y] != MV_NONE)
4320 move_direction_initial = MV_START_AUTOMATIC;
4323 if (move_direction_initial == MV_START_RANDOM)
4324 MovDir[x][y] = 1 << RND(4);
4325 else if (move_direction_initial & MV_ANY_DIRECTION)
4326 MovDir[x][y] = move_direction_initial;
4327 else if (move_pattern == MV_ALL_DIRECTIONS ||
4328 move_pattern == MV_TURNING_LEFT ||
4329 move_pattern == MV_TURNING_RIGHT ||
4330 move_pattern == MV_TURNING_LEFT_RIGHT ||
4331 move_pattern == MV_TURNING_RIGHT_LEFT ||
4332 move_pattern == MV_TURNING_RANDOM)
4333 MovDir[x][y] = 1 << RND(4);
4334 else if (move_pattern == MV_HORIZONTAL)
4335 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4336 else if (move_pattern == MV_VERTICAL)
4337 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4338 else if (move_pattern & MV_ANY_DIRECTION)
4339 MovDir[x][y] = element_info[element].move_pattern;
4340 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4341 move_pattern == MV_ALONG_RIGHT_SIDE)
4343 /* use random direction as default start direction */
4344 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4345 MovDir[x][y] = 1 << RND(4);
4347 for (i = 0; i < NUM_DIRECTIONS; i++)
4349 int x1 = x + xy[i][0];
4350 int y1 = y + xy[i][1];
4352 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4354 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4355 MovDir[x][y] = direction[0][i];
4357 MovDir[x][y] = direction[1][i];
4366 MovDir[x][y] = 1 << RND(4);
4368 if (element != EL_BUG &&
4369 element != EL_SPACESHIP &&
4370 element != EL_BD_BUTTERFLY &&
4371 element != EL_BD_FIREFLY)
4374 for (i = 0; i < NUM_DIRECTIONS; i++)
4376 int x1 = x + xy[i][0];
4377 int y1 = y + xy[i][1];
4379 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4381 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4383 MovDir[x][y] = direction[0][i];
4386 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4387 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4389 MovDir[x][y] = direction[1][i];
4398 GfxDir[x][y] = MovDir[x][y];
4401 void InitAmoebaNr(int x, int y)
4404 int group_nr = AmoebeNachbarNr(x, y);
4408 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4410 if (AmoebaCnt[i] == 0)
4418 AmoebaNr[x][y] = group_nr;
4419 AmoebaCnt[group_nr]++;
4420 AmoebaCnt2[group_nr]++;
4423 static void PlayerWins(struct PlayerInfo *player)
4425 player->LevelSolved = TRUE;
4426 player->GameOver = TRUE;
4428 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4429 level.native_em_level->lev->score :
4430 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4433 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4434 MM_HEALTH(game_mm.laser_overload_value) :
4437 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4439 player->LevelSolved_CountingScore = player->score_final;
4440 player->LevelSolved_CountingHealth = player->health_final;
4445 static int time_count_steps;
4446 static int time, time_final;
4447 static int score, score_final;
4448 static int health, health_final;
4449 static int game_over_delay_1 = 0;
4450 static int game_over_delay_2 = 0;
4451 static int game_over_delay_3 = 0;
4452 int game_over_delay_value_1 = 50;
4453 int game_over_delay_value_2 = 25;
4454 int game_over_delay_value_3 = 50;
4456 if (!local_player->LevelSolved_GameWon)
4460 /* do not start end game actions before the player stops moving (to exit) */
4461 if (local_player->MovPos)
4464 local_player->LevelSolved_GameWon = TRUE;
4465 local_player->LevelSolved_SaveTape = tape.recording;
4466 local_player->LevelSolved_SaveScore = !tape.playing;
4470 LevelStats_incSolved(level_nr);
4472 SaveLevelSetup_SeriesInfo();
4475 if (tape.auto_play) /* tape might already be stopped here */
4476 tape.auto_play_level_solved = TRUE;
4480 game_over_delay_1 = 0;
4481 game_over_delay_2 = 0;
4482 game_over_delay_3 = game_over_delay_value_3;
4484 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4485 score = score_final = local_player->score_final;
4486 health = health_final = local_player->health_final;
4488 if (level.score[SC_TIME_BONUS] > 0)
4493 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4495 else if (game.no_time_limit && TimePlayed < 999)
4498 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4501 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4503 game_over_delay_1 = game_over_delay_value_1;
4505 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4508 score_final += health * level.score[SC_TIME_BONUS];
4510 game_over_delay_2 = game_over_delay_value_2;
4513 local_player->score_final = score_final;
4514 local_player->health_final = health_final;
4517 if (level_editor_test_game)
4520 score = score_final;
4522 local_player->LevelSolved_CountingTime = time;
4523 local_player->LevelSolved_CountingScore = score;
4525 game_panel_controls[GAME_PANEL_TIME].value = time;
4526 game_panel_controls[GAME_PANEL_SCORE].value = score;
4528 DisplayGameControlValues();
4531 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4533 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4535 /* close exit door after last player */
4536 if ((AllPlayersGone &&
4537 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4538 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4539 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4540 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4541 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4543 int element = Feld[ExitX][ExitY];
4545 Feld[ExitX][ExitY] =
4546 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4547 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4548 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4549 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4550 EL_EM_STEEL_EXIT_CLOSING);
4552 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4555 /* player disappears */
4556 DrawLevelField(ExitX, ExitY);
4559 for (i = 0; i < MAX_PLAYERS; i++)
4561 struct PlayerInfo *player = &stored_player[i];
4563 if (player->present)
4565 RemovePlayer(player);
4567 /* player disappears */
4568 DrawLevelField(player->jx, player->jy);
4573 PlaySound(SND_GAME_WINNING);
4576 if (game_over_delay_1 > 0)
4578 game_over_delay_1--;
4583 if (time != time_final)
4585 int time_to_go = ABS(time_final - time);
4586 int time_count_dir = (time < time_final ? +1 : -1);
4588 if (time_to_go < time_count_steps)
4589 time_count_steps = 1;
4591 time += time_count_steps * time_count_dir;
4592 score += time_count_steps * level.score[SC_TIME_BONUS];
4594 local_player->LevelSolved_CountingTime = time;
4595 local_player->LevelSolved_CountingScore = score;
4597 game_panel_controls[GAME_PANEL_TIME].value = time;
4598 game_panel_controls[GAME_PANEL_SCORE].value = score;
4600 DisplayGameControlValues();
4602 if (time == time_final)
4603 StopSound(SND_GAME_LEVELTIME_BONUS);
4604 else if (setup.sound_loops)
4605 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4607 PlaySound(SND_GAME_LEVELTIME_BONUS);
4612 if (game_over_delay_2 > 0)
4614 game_over_delay_2--;
4619 if (health != health_final)
4621 int health_count_dir = (health < health_final ? +1 : -1);
4623 health += health_count_dir;
4624 score += level.score[SC_TIME_BONUS];
4626 local_player->LevelSolved_CountingHealth = health;
4627 local_player->LevelSolved_CountingScore = score;
4629 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4630 game_panel_controls[GAME_PANEL_SCORE].value = score;
4632 DisplayGameControlValues();
4634 if (health == health_final)
4635 StopSound(SND_GAME_LEVELTIME_BONUS);
4636 else if (setup.sound_loops)
4637 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4639 PlaySound(SND_GAME_LEVELTIME_BONUS);
4644 local_player->LevelSolved_PanelOff = TRUE;
4646 if (game_over_delay_3 > 0)
4648 game_over_delay_3--;
4659 int last_level_nr = level_nr;
4661 local_player->LevelSolved_GameEnd = TRUE;
4663 if (local_player->LevelSolved_SaveTape)
4665 /* make sure that request dialog to save tape does not open door again */
4666 if (!global.use_envelope_request)
4667 CloseDoor(DOOR_CLOSE_1);
4669 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4672 /* if no tape is to be saved, close both doors simultaneously */
4673 CloseDoor(DOOR_CLOSE_ALL);
4675 if (level_editor_test_game)
4677 SetGameStatus(GAME_MODE_MAIN);
4684 if (!local_player->LevelSolved_SaveScore)
4686 SetGameStatus(GAME_MODE_MAIN);
4693 if (level_nr == leveldir_current->handicap_level)
4695 leveldir_current->handicap_level++;
4697 SaveLevelSetup_SeriesInfo();
4700 if (setup.increment_levels &&
4701 level_nr < leveldir_current->last_level)
4703 level_nr++; /* advance to next level */
4704 TapeErase(); /* start with empty tape */
4706 if (setup.auto_play_next_level)
4708 LoadLevel(level_nr);
4710 SaveLevelSetup_SeriesInfo();
4714 hi_pos = NewHiScore(last_level_nr);
4716 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4718 SetGameStatus(GAME_MODE_SCORES);
4720 DrawHallOfFame(last_level_nr, hi_pos);
4722 else if (!setup.auto_play_next_level || !setup.increment_levels)
4724 SetGameStatus(GAME_MODE_MAIN);
4730 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4734 int NewHiScore(int level_nr)
4738 boolean one_score_entry_per_name = !program.many_scores_per_name;
4740 LoadScore(level_nr);
4742 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4743 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4746 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4748 if (local_player->score_final > highscore[k].Score)
4750 /* player has made it to the hall of fame */
4752 if (k < MAX_SCORE_ENTRIES - 1)
4754 int m = MAX_SCORE_ENTRIES - 1;
4756 if (one_score_entry_per_name)
4758 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4759 if (strEqual(setup.player_name, highscore[l].Name))
4762 if (m == k) /* player's new highscore overwrites his old one */
4766 for (l = m; l > k; l--)
4768 strcpy(highscore[l].Name, highscore[l - 1].Name);
4769 highscore[l].Score = highscore[l - 1].Score;
4775 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4776 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4777 highscore[k].Score = local_player->score_final;
4782 else if (one_score_entry_per_name &&
4783 !strncmp(setup.player_name, highscore[k].Name,
4784 MAX_PLAYER_NAME_LEN))
4785 break; /* player already there with a higher score */
4789 SaveScore(level_nr);
4794 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4796 int element = Feld[x][y];
4797 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4798 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4799 int horiz_move = (dx != 0);
4800 int sign = (horiz_move ? dx : dy);
4801 int step = sign * element_info[element].move_stepsize;
4803 /* special values for move stepsize for spring and things on conveyor belt */
4806 if (CAN_FALL(element) &&
4807 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4808 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4809 else if (element == EL_SPRING)
4810 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4816 inline static int getElementMoveStepsize(int x, int y)
4818 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4821 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4823 if (player->GfxAction != action || player->GfxDir != dir)
4825 player->GfxAction = action;
4826 player->GfxDir = dir;
4828 player->StepFrame = 0;
4832 static void ResetGfxFrame(int x, int y)
4834 // profiling showed that "autotest" spends 10~20% of its time in this function
4835 if (DrawingDeactivatedField())
4838 int element = Feld[x][y];
4839 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4841 if (graphic_info[graphic].anim_global_sync)
4842 GfxFrame[x][y] = FrameCounter;
4843 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4844 GfxFrame[x][y] = CustomValue[x][y];
4845 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4846 GfxFrame[x][y] = element_info[element].collect_score;
4847 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4848 GfxFrame[x][y] = ChangeDelay[x][y];
4851 static void ResetGfxAnimation(int x, int y)
4853 GfxAction[x][y] = ACTION_DEFAULT;
4854 GfxDir[x][y] = MovDir[x][y];
4857 ResetGfxFrame(x, y);
4860 static void ResetRandomAnimationValue(int x, int y)
4862 GfxRandom[x][y] = INIT_GFX_RANDOM();
4865 void InitMovingField(int x, int y, int direction)
4867 int element = Feld[x][y];
4868 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4869 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4872 boolean is_moving_before, is_moving_after;
4874 /* check if element was/is moving or being moved before/after mode change */
4875 is_moving_before = (WasJustMoving[x][y] != 0);
4876 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4878 /* reset animation only for moving elements which change direction of moving
4879 or which just started or stopped moving
4880 (else CEs with property "can move" / "not moving" are reset each frame) */
4881 if (is_moving_before != is_moving_after ||
4882 direction != MovDir[x][y])
4883 ResetGfxAnimation(x, y);
4885 MovDir[x][y] = direction;
4886 GfxDir[x][y] = direction;
4888 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4889 direction == MV_DOWN && CAN_FALL(element) ?
4890 ACTION_FALLING : ACTION_MOVING);
4892 /* this is needed for CEs with property "can move" / "not moving" */
4894 if (is_moving_after)
4896 if (Feld[newx][newy] == EL_EMPTY)
4897 Feld[newx][newy] = EL_BLOCKED;
4899 MovDir[newx][newy] = MovDir[x][y];
4901 CustomValue[newx][newy] = CustomValue[x][y];
4903 GfxFrame[newx][newy] = GfxFrame[x][y];
4904 GfxRandom[newx][newy] = GfxRandom[x][y];
4905 GfxAction[newx][newy] = GfxAction[x][y];
4906 GfxDir[newx][newy] = GfxDir[x][y];
4910 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4912 int direction = MovDir[x][y];
4913 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4914 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4920 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4922 int oldx = x, oldy = y;
4923 int direction = MovDir[x][y];
4925 if (direction == MV_LEFT)
4927 else if (direction == MV_RIGHT)
4929 else if (direction == MV_UP)
4931 else if (direction == MV_DOWN)
4934 *comes_from_x = oldx;
4935 *comes_from_y = oldy;
4938 int MovingOrBlocked2Element(int x, int y)
4940 int element = Feld[x][y];
4942 if (element == EL_BLOCKED)
4946 Blocked2Moving(x, y, &oldx, &oldy);
4947 return Feld[oldx][oldy];
4953 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4955 /* like MovingOrBlocked2Element(), but if element is moving
4956 and (x,y) is the field the moving element is just leaving,
4957 return EL_BLOCKED instead of the element value */
4958 int element = Feld[x][y];
4960 if (IS_MOVING(x, y))
4962 if (element == EL_BLOCKED)
4966 Blocked2Moving(x, y, &oldx, &oldy);
4967 return Feld[oldx][oldy];
4976 static void RemoveField(int x, int y)
4978 Feld[x][y] = EL_EMPTY;
4984 CustomValue[x][y] = 0;
4987 ChangeDelay[x][y] = 0;
4988 ChangePage[x][y] = -1;
4989 Pushed[x][y] = FALSE;
4991 GfxElement[x][y] = EL_UNDEFINED;
4992 GfxAction[x][y] = ACTION_DEFAULT;
4993 GfxDir[x][y] = MV_NONE;
4996 void RemoveMovingField(int x, int y)
4998 int oldx = x, oldy = y, newx = x, newy = y;
4999 int element = Feld[x][y];
5000 int next_element = EL_UNDEFINED;
5002 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5005 if (IS_MOVING(x, y))
5007 Moving2Blocked(x, y, &newx, &newy);
5009 if (Feld[newx][newy] != EL_BLOCKED)
5011 /* element is moving, but target field is not free (blocked), but
5012 already occupied by something different (example: acid pool);
5013 in this case, only remove the moving field, but not the target */
5015 RemoveField(oldx, oldy);
5017 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5019 TEST_DrawLevelField(oldx, oldy);
5024 else if (element == EL_BLOCKED)
5026 Blocked2Moving(x, y, &oldx, &oldy);
5027 if (!IS_MOVING(oldx, oldy))
5031 if (element == EL_BLOCKED &&
5032 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5033 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5034 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5035 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5036 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5037 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5038 next_element = get_next_element(Feld[oldx][oldy]);
5040 RemoveField(oldx, oldy);
5041 RemoveField(newx, newy);
5043 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5045 if (next_element != EL_UNDEFINED)
5046 Feld[oldx][oldy] = next_element;
5048 TEST_DrawLevelField(oldx, oldy);
5049 TEST_DrawLevelField(newx, newy);
5052 void DrawDynamite(int x, int y)
5054 int sx = SCREENX(x), sy = SCREENY(y);
5055 int graphic = el2img(Feld[x][y]);
5058 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5061 if (IS_WALKABLE_INSIDE(Back[x][y]))
5065 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5066 else if (Store[x][y])
5067 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5069 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5071 if (Back[x][y] || Store[x][y])
5072 DrawGraphicThruMask(sx, sy, graphic, frame);
5074 DrawGraphic(sx, sy, graphic, frame);
5077 void CheckDynamite(int x, int y)
5079 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5083 if (MovDelay[x][y] != 0)
5086 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5092 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5097 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5099 boolean num_checked_players = 0;
5102 for (i = 0; i < MAX_PLAYERS; i++)
5104 if (stored_player[i].active)
5106 int sx = stored_player[i].jx;
5107 int sy = stored_player[i].jy;
5109 if (num_checked_players == 0)
5116 *sx1 = MIN(*sx1, sx);
5117 *sy1 = MIN(*sy1, sy);
5118 *sx2 = MAX(*sx2, sx);
5119 *sy2 = MAX(*sy2, sy);
5122 num_checked_players++;
5127 static boolean checkIfAllPlayersFitToScreen_RND()
5129 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5131 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5133 return (sx2 - sx1 < SCR_FIELDX &&
5134 sy2 - sy1 < SCR_FIELDY);
5137 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5139 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5141 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5143 *sx = (sx1 + sx2) / 2;
5144 *sy = (sy1 + sy2) / 2;
5147 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5148 boolean center_screen, boolean quick_relocation)
5150 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5151 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5152 boolean no_delay = (tape.warp_forward);
5153 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5154 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5155 int new_scroll_x, new_scroll_y;
5157 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5159 /* case 1: quick relocation inside visible screen (without scrolling) */
5166 if (!level.shifted_relocation || center_screen)
5168 /* relocation _with_ centering of screen */
5170 new_scroll_x = SCROLL_POSITION_X(x);
5171 new_scroll_y = SCROLL_POSITION_Y(y);
5175 /* relocation _without_ centering of screen */
5177 int center_scroll_x = SCROLL_POSITION_X(old_x);
5178 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5179 int offset_x = x + (scroll_x - center_scroll_x);
5180 int offset_y = y + (scroll_y - center_scroll_y);
5182 /* for new screen position, apply previous offset to center position */
5183 new_scroll_x = SCROLL_POSITION_X(offset_x);
5184 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5187 if (quick_relocation)
5189 /* case 2: quick relocation (redraw without visible scrolling) */
5191 scroll_x = new_scroll_x;
5192 scroll_y = new_scroll_y;
5199 /* case 3: visible relocation (with scrolling to new position) */
5201 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5203 SetVideoFrameDelay(wait_delay_value);
5205 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5208 int fx = FX, fy = FY;
5210 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5211 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5213 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5219 fx += dx * TILEX / 2;
5220 fy += dy * TILEY / 2;
5222 ScrollLevel(dx, dy);
5225 /* scroll in two steps of half tile size to make things smoother */
5226 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5228 /* scroll second step to align at full tile size */
5229 BlitScreenToBitmap(window);
5235 SetVideoFrameDelay(frame_delay_value_old);
5238 void RelocatePlayer(int jx, int jy, int el_player_raw)
5240 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5241 int player_nr = GET_PLAYER_NR(el_player);
5242 struct PlayerInfo *player = &stored_player[player_nr];
5243 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5244 boolean no_delay = (tape.warp_forward);
5245 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5246 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5247 int old_jx = player->jx;
5248 int old_jy = player->jy;
5249 int old_element = Feld[old_jx][old_jy];
5250 int element = Feld[jx][jy];
5251 boolean player_relocated = (old_jx != jx || old_jy != jy);
5253 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5254 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5255 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5256 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5257 int leave_side_horiz = move_dir_horiz;
5258 int leave_side_vert = move_dir_vert;
5259 int enter_side = enter_side_horiz | enter_side_vert;
5260 int leave_side = leave_side_horiz | leave_side_vert;
5262 if (player->GameOver) /* do not reanimate dead player */
5265 if (!player_relocated) /* no need to relocate the player */
5268 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5270 RemoveField(jx, jy); /* temporarily remove newly placed player */
5271 DrawLevelField(jx, jy);
5274 if (player->present)
5276 while (player->MovPos)
5278 ScrollPlayer(player, SCROLL_GO_ON);
5279 ScrollScreen(NULL, SCROLL_GO_ON);
5281 AdvanceFrameAndPlayerCounters(player->index_nr);
5285 BackToFront_WithFrameDelay(wait_delay_value);
5288 DrawPlayer(player); /* needed here only to cleanup last field */
5289 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5291 player->is_moving = FALSE;
5294 if (IS_CUSTOM_ELEMENT(old_element))
5295 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5297 player->index_bit, leave_side);
5299 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5301 player->index_bit, leave_side);
5303 Feld[jx][jy] = el_player;
5304 InitPlayerField(jx, jy, el_player, TRUE);
5306 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5307 possible that the relocation target field did not contain a player element,
5308 but a walkable element, to which the new player was relocated -- in this
5309 case, restore that (already initialized!) element on the player field */
5310 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5312 Feld[jx][jy] = element; /* restore previously existing element */
5315 /* only visually relocate centered player */
5316 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5317 FALSE, level.instant_relocation);
5319 TestIfPlayerTouchesBadThing(jx, jy);
5320 TestIfPlayerTouchesCustomElement(jx, jy);
5322 if (IS_CUSTOM_ELEMENT(element))
5323 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5324 player->index_bit, enter_side);
5326 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5327 player->index_bit, enter_side);
5329 if (player->is_switching)
5331 /* ensure that relocation while still switching an element does not cause
5332 a new element to be treated as also switched directly after relocation
5333 (this is important for teleporter switches that teleport the player to
5334 a place where another teleporter switch is in the same direction, which
5335 would then incorrectly be treated as immediately switched before the
5336 direction key that caused the switch was released) */
5338 player->switch_x += jx - old_jx;
5339 player->switch_y += jy - old_jy;
5343 void Explode(int ex, int ey, int phase, int mode)
5349 /* !!! eliminate this variable !!! */
5350 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5352 if (game.explosions_delayed)
5354 ExplodeField[ex][ey] = mode;
5358 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5360 int center_element = Feld[ex][ey];
5361 int artwork_element, explosion_element; /* set these values later */
5363 /* remove things displayed in background while burning dynamite */
5364 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5367 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5369 /* put moving element to center field (and let it explode there) */
5370 center_element = MovingOrBlocked2Element(ex, ey);
5371 RemoveMovingField(ex, ey);
5372 Feld[ex][ey] = center_element;
5375 /* now "center_element" is finally determined -- set related values now */
5376 artwork_element = center_element; /* for custom player artwork */
5377 explosion_element = center_element; /* for custom player artwork */
5379 if (IS_PLAYER(ex, ey))
5381 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5383 artwork_element = stored_player[player_nr].artwork_element;
5385 if (level.use_explosion_element[player_nr])
5387 explosion_element = level.explosion_element[player_nr];
5388 artwork_element = explosion_element;
5392 if (mode == EX_TYPE_NORMAL ||
5393 mode == EX_TYPE_CENTER ||
5394 mode == EX_TYPE_CROSS)
5395 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5397 last_phase = element_info[explosion_element].explosion_delay + 1;
5399 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5401 int xx = x - ex + 1;
5402 int yy = y - ey + 1;
5405 if (!IN_LEV_FIELD(x, y) ||
5406 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5407 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5410 element = Feld[x][y];
5412 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5414 element = MovingOrBlocked2Element(x, y);
5416 if (!IS_EXPLOSION_PROOF(element))
5417 RemoveMovingField(x, y);
5420 /* indestructible elements can only explode in center (but not flames) */
5421 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5422 mode == EX_TYPE_BORDER)) ||
5423 element == EL_FLAMES)
5426 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5427 behaviour, for example when touching a yamyam that explodes to rocks
5428 with active deadly shield, a rock is created under the player !!! */
5429 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5431 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5432 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5433 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5435 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5438 if (IS_ACTIVE_BOMB(element))
5440 /* re-activate things under the bomb like gate or penguin */
5441 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5448 /* save walkable background elements while explosion on same tile */
5449 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5450 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5451 Back[x][y] = element;
5453 /* ignite explodable elements reached by other explosion */
5454 if (element == EL_EXPLOSION)
5455 element = Store2[x][y];
5457 if (AmoebaNr[x][y] &&
5458 (element == EL_AMOEBA_FULL ||
5459 element == EL_BD_AMOEBA ||
5460 element == EL_AMOEBA_GROWING))
5462 AmoebaCnt[AmoebaNr[x][y]]--;
5463 AmoebaCnt2[AmoebaNr[x][y]]--;
5468 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5470 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5472 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5474 if (PLAYERINFO(ex, ey)->use_murphy)
5475 Store[x][y] = EL_EMPTY;
5478 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5479 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5480 else if (ELEM_IS_PLAYER(center_element))
5481 Store[x][y] = EL_EMPTY;
5482 else if (center_element == EL_YAMYAM)
5483 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5484 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5485 Store[x][y] = element_info[center_element].content.e[xx][yy];
5487 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5488 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5489 otherwise) -- FIX THIS !!! */
5490 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5491 Store[x][y] = element_info[element].content.e[1][1];
5493 else if (!CAN_EXPLODE(element))
5494 Store[x][y] = element_info[element].content.e[1][1];
5497 Store[x][y] = EL_EMPTY;
5499 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5500 center_element == EL_AMOEBA_TO_DIAMOND)
5501 Store2[x][y] = element;
5503 Feld[x][y] = EL_EXPLOSION;
5504 GfxElement[x][y] = artwork_element;
5506 ExplodePhase[x][y] = 1;
5507 ExplodeDelay[x][y] = last_phase;
5512 if (center_element == EL_YAMYAM)
5513 game.yamyam_content_nr =
5514 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5526 GfxFrame[x][y] = 0; /* restart explosion animation */
5528 last_phase = ExplodeDelay[x][y];
5530 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5532 /* this can happen if the player leaves an explosion just in time */
5533 if (GfxElement[x][y] == EL_UNDEFINED)
5534 GfxElement[x][y] = EL_EMPTY;
5536 border_element = Store2[x][y];
5537 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5538 border_element = StorePlayer[x][y];
5540 if (phase == element_info[border_element].ignition_delay ||
5541 phase == last_phase)
5543 boolean border_explosion = FALSE;
5545 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5546 !PLAYER_EXPLOSION_PROTECTED(x, y))
5548 KillPlayerUnlessExplosionProtected(x, y);
5549 border_explosion = TRUE;
5551 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5553 Feld[x][y] = Store2[x][y];
5556 border_explosion = TRUE;
5558 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5560 AmoebeUmwandeln(x, y);
5562 border_explosion = TRUE;
5565 /* if an element just explodes due to another explosion (chain-reaction),
5566 do not immediately end the new explosion when it was the last frame of
5567 the explosion (as it would be done in the following "if"-statement!) */
5568 if (border_explosion && phase == last_phase)
5572 if (phase == last_phase)
5576 element = Feld[x][y] = Store[x][y];
5577 Store[x][y] = Store2[x][y] = 0;
5578 GfxElement[x][y] = EL_UNDEFINED;
5580 /* player can escape from explosions and might therefore be still alive */
5581 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5582 element <= EL_PLAYER_IS_EXPLODING_4)
5584 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5585 int explosion_element = EL_PLAYER_1 + player_nr;
5586 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5587 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5589 if (level.use_explosion_element[player_nr])
5590 explosion_element = level.explosion_element[player_nr];
5592 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5593 element_info[explosion_element].content.e[xx][yy]);
5596 /* restore probably existing indestructible background element */
5597 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5598 element = Feld[x][y] = Back[x][y];
5601 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5602 GfxDir[x][y] = MV_NONE;
5603 ChangeDelay[x][y] = 0;
5604 ChangePage[x][y] = -1;
5606 CustomValue[x][y] = 0;
5608 InitField_WithBug2(x, y, FALSE);
5610 TEST_DrawLevelField(x, y);
5612 TestIfElementTouchesCustomElement(x, y);
5614 if (GFX_CRUMBLED(element))
5615 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5617 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5618 StorePlayer[x][y] = 0;
5620 if (ELEM_IS_PLAYER(element))
5621 RelocatePlayer(x, y, element);
5623 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5625 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5626 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5629 TEST_DrawLevelFieldCrumbled(x, y);
5631 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5633 DrawLevelElement(x, y, Back[x][y]);
5634 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5636 else if (IS_WALKABLE_UNDER(Back[x][y]))
5638 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5639 DrawLevelElementThruMask(x, y, Back[x][y]);
5641 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5642 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5646 void DynaExplode(int ex, int ey)
5649 int dynabomb_element = Feld[ex][ey];
5650 int dynabomb_size = 1;
5651 boolean dynabomb_xl = FALSE;
5652 struct PlayerInfo *player;
5653 static int xy[4][2] =
5661 if (IS_ACTIVE_BOMB(dynabomb_element))
5663 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5664 dynabomb_size = player->dynabomb_size;
5665 dynabomb_xl = player->dynabomb_xl;
5666 player->dynabombs_left++;
5669 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5671 for (i = 0; i < NUM_DIRECTIONS; i++)
5673 for (j = 1; j <= dynabomb_size; j++)
5675 int x = ex + j * xy[i][0];
5676 int y = ey + j * xy[i][1];
5679 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5682 element = Feld[x][y];
5684 /* do not restart explosions of fields with active bombs */
5685 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5688 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5690 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5691 !IS_DIGGABLE(element) && !dynabomb_xl)
5697 void Bang(int x, int y)
5699 int element = MovingOrBlocked2Element(x, y);
5700 int explosion_type = EX_TYPE_NORMAL;
5702 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5704 struct PlayerInfo *player = PLAYERINFO(x, y);
5706 element = Feld[x][y] = player->initial_element;
5708 if (level.use_explosion_element[player->index_nr])
5710 int explosion_element = level.explosion_element[player->index_nr];
5712 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5713 explosion_type = EX_TYPE_CROSS;
5714 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5715 explosion_type = EX_TYPE_CENTER;
5723 case EL_BD_BUTTERFLY:
5726 case EL_DARK_YAMYAM:
5730 RaiseScoreElement(element);
5733 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5734 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5735 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5736 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5737 case EL_DYNABOMB_INCREASE_NUMBER:
5738 case EL_DYNABOMB_INCREASE_SIZE:
5739 case EL_DYNABOMB_INCREASE_POWER:
5740 explosion_type = EX_TYPE_DYNA;
5743 case EL_DC_LANDMINE:
5744 explosion_type = EX_TYPE_CENTER;
5749 case EL_LAMP_ACTIVE:
5750 case EL_AMOEBA_TO_DIAMOND:
5751 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5752 explosion_type = EX_TYPE_CENTER;
5756 if (element_info[element].explosion_type == EXPLODES_CROSS)
5757 explosion_type = EX_TYPE_CROSS;
5758 else if (element_info[element].explosion_type == EXPLODES_1X1)
5759 explosion_type = EX_TYPE_CENTER;
5763 if (explosion_type == EX_TYPE_DYNA)
5766 Explode(x, y, EX_PHASE_START, explosion_type);
5768 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5771 void SplashAcid(int x, int y)
5773 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5774 (!IN_LEV_FIELD(x - 1, y - 2) ||
5775 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5776 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5778 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5779 (!IN_LEV_FIELD(x + 1, y - 2) ||
5780 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5781 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5783 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5786 static void InitBeltMovement()
5788 static int belt_base_element[4] =
5790 EL_CONVEYOR_BELT_1_LEFT,
5791 EL_CONVEYOR_BELT_2_LEFT,
5792 EL_CONVEYOR_BELT_3_LEFT,
5793 EL_CONVEYOR_BELT_4_LEFT
5795 static int belt_base_active_element[4] =
5797 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5798 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5799 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5800 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5805 /* set frame order for belt animation graphic according to belt direction */
5806 for (i = 0; i < NUM_BELTS; i++)
5810 for (j = 0; j < NUM_BELT_PARTS; j++)
5812 int element = belt_base_active_element[belt_nr] + j;
5813 int graphic_1 = el2img(element);
5814 int graphic_2 = el2panelimg(element);
5816 if (game.belt_dir[i] == MV_LEFT)
5818 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5819 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5823 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5824 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5829 SCAN_PLAYFIELD(x, y)
5831 int element = Feld[x][y];
5833 for (i = 0; i < NUM_BELTS; i++)
5835 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5837 int e_belt_nr = getBeltNrFromBeltElement(element);
5840 if (e_belt_nr == belt_nr)
5842 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5844 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5851 static void ToggleBeltSwitch(int x, int y)
5853 static int belt_base_element[4] =
5855 EL_CONVEYOR_BELT_1_LEFT,
5856 EL_CONVEYOR_BELT_2_LEFT,
5857 EL_CONVEYOR_BELT_3_LEFT,
5858 EL_CONVEYOR_BELT_4_LEFT
5860 static int belt_base_active_element[4] =
5862 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5863 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5864 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5865 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5867 static int belt_base_switch_element[4] =
5869 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5870 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5871 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5872 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5874 static int belt_move_dir[4] =
5882 int element = Feld[x][y];
5883 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5884 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5885 int belt_dir = belt_move_dir[belt_dir_nr];
5888 if (!IS_BELT_SWITCH(element))
5891 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5892 game.belt_dir[belt_nr] = belt_dir;
5894 if (belt_dir_nr == 3)
5897 /* set frame order for belt animation graphic according to belt direction */
5898 for (i = 0; i < NUM_BELT_PARTS; i++)
5900 int element = belt_base_active_element[belt_nr] + i;
5901 int graphic_1 = el2img(element);
5902 int graphic_2 = el2panelimg(element);
5904 if (belt_dir == MV_LEFT)
5906 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5907 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5911 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5912 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5916 SCAN_PLAYFIELD(xx, yy)
5918 int element = Feld[xx][yy];
5920 if (IS_BELT_SWITCH(element))
5922 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5924 if (e_belt_nr == belt_nr)
5926 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5927 TEST_DrawLevelField(xx, yy);
5930 else if (IS_BELT(element) && belt_dir != MV_NONE)
5932 int e_belt_nr = getBeltNrFromBeltElement(element);
5934 if (e_belt_nr == belt_nr)
5936 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5938 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5939 TEST_DrawLevelField(xx, yy);
5942 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5944 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5946 if (e_belt_nr == belt_nr)
5948 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5950 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5951 TEST_DrawLevelField(xx, yy);
5957 static void ToggleSwitchgateSwitch(int x, int y)
5961 game.switchgate_pos = !game.switchgate_pos;
5963 SCAN_PLAYFIELD(xx, yy)
5965 int element = Feld[xx][yy];
5967 if (element == EL_SWITCHGATE_SWITCH_UP)
5969 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5970 TEST_DrawLevelField(xx, yy);
5972 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5974 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5975 TEST_DrawLevelField(xx, yy);
5977 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5979 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5980 TEST_DrawLevelField(xx, yy);
5982 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5984 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5985 TEST_DrawLevelField(xx, yy);
5987 else if (element == EL_SWITCHGATE_OPEN ||
5988 element == EL_SWITCHGATE_OPENING)
5990 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5992 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5994 else if (element == EL_SWITCHGATE_CLOSED ||
5995 element == EL_SWITCHGATE_CLOSING)
5997 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5999 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6004 static int getInvisibleActiveFromInvisibleElement(int element)
6006 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6007 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6008 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6012 static int getInvisibleFromInvisibleActiveElement(int element)
6014 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6015 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6016 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6020 static void RedrawAllLightSwitchesAndInvisibleElements()
6024 SCAN_PLAYFIELD(x, y)
6026 int element = Feld[x][y];
6028 if (element == EL_LIGHT_SWITCH &&
6029 game.light_time_left > 0)
6031 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6032 TEST_DrawLevelField(x, y);
6034 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6035 game.light_time_left == 0)
6037 Feld[x][y] = EL_LIGHT_SWITCH;
6038 TEST_DrawLevelField(x, y);
6040 else if (element == EL_EMC_DRIPPER &&
6041 game.light_time_left > 0)
6043 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6044 TEST_DrawLevelField(x, y);
6046 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6047 game.light_time_left == 0)
6049 Feld[x][y] = EL_EMC_DRIPPER;
6050 TEST_DrawLevelField(x, y);
6052 else if (element == EL_INVISIBLE_STEELWALL ||
6053 element == EL_INVISIBLE_WALL ||
6054 element == EL_INVISIBLE_SAND)
6056 if (game.light_time_left > 0)
6057 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6059 TEST_DrawLevelField(x, y);
6061 /* uncrumble neighbour fields, if needed */
6062 if (element == EL_INVISIBLE_SAND)
6063 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6065 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6066 element == EL_INVISIBLE_WALL_ACTIVE ||
6067 element == EL_INVISIBLE_SAND_ACTIVE)
6069 if (game.light_time_left == 0)
6070 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6072 TEST_DrawLevelField(x, y);
6074 /* re-crumble neighbour fields, if needed */
6075 if (element == EL_INVISIBLE_SAND)
6076 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6081 static void RedrawAllInvisibleElementsForLenses()
6085 SCAN_PLAYFIELD(x, y)
6087 int element = Feld[x][y];
6089 if (element == EL_EMC_DRIPPER &&
6090 game.lenses_time_left > 0)
6092 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6093 TEST_DrawLevelField(x, y);
6095 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6096 game.lenses_time_left == 0)
6098 Feld[x][y] = EL_EMC_DRIPPER;
6099 TEST_DrawLevelField(x, y);
6101 else if (element == EL_INVISIBLE_STEELWALL ||
6102 element == EL_INVISIBLE_WALL ||
6103 element == EL_INVISIBLE_SAND)
6105 if (game.lenses_time_left > 0)
6106 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6108 TEST_DrawLevelField(x, y);
6110 /* uncrumble neighbour fields, if needed */
6111 if (element == EL_INVISIBLE_SAND)
6112 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6114 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6115 element == EL_INVISIBLE_WALL_ACTIVE ||
6116 element == EL_INVISIBLE_SAND_ACTIVE)
6118 if (game.lenses_time_left == 0)
6119 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6121 TEST_DrawLevelField(x, y);
6123 /* re-crumble neighbour fields, if needed */
6124 if (element == EL_INVISIBLE_SAND)
6125 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6130 static void RedrawAllInvisibleElementsForMagnifier()
6134 SCAN_PLAYFIELD(x, y)
6136 int element = Feld[x][y];
6138 if (element == EL_EMC_FAKE_GRASS &&
6139 game.magnify_time_left > 0)
6141 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6142 TEST_DrawLevelField(x, y);
6144 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6145 game.magnify_time_left == 0)
6147 Feld[x][y] = EL_EMC_FAKE_GRASS;
6148 TEST_DrawLevelField(x, y);
6150 else if (IS_GATE_GRAY(element) &&
6151 game.magnify_time_left > 0)
6153 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6154 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6155 IS_EM_GATE_GRAY(element) ?
6156 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6157 IS_EMC_GATE_GRAY(element) ?
6158 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6159 IS_DC_GATE_GRAY(element) ?
6160 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6162 TEST_DrawLevelField(x, y);
6164 else if (IS_GATE_GRAY_ACTIVE(element) &&
6165 game.magnify_time_left == 0)
6167 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6168 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6169 IS_EM_GATE_GRAY_ACTIVE(element) ?
6170 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6171 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6172 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6173 IS_DC_GATE_GRAY_ACTIVE(element) ?
6174 EL_DC_GATE_WHITE_GRAY :
6176 TEST_DrawLevelField(x, y);
6181 static void ToggleLightSwitch(int x, int y)
6183 int element = Feld[x][y];
6185 game.light_time_left =
6186 (element == EL_LIGHT_SWITCH ?
6187 level.time_light * FRAMES_PER_SECOND : 0);
6189 RedrawAllLightSwitchesAndInvisibleElements();
6192 static void ActivateTimegateSwitch(int x, int y)
6196 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6198 SCAN_PLAYFIELD(xx, yy)
6200 int element = Feld[xx][yy];
6202 if (element == EL_TIMEGATE_CLOSED ||
6203 element == EL_TIMEGATE_CLOSING)
6205 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6206 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6210 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6212 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6213 TEST_DrawLevelField(xx, yy);
6219 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6220 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6223 void Impact(int x, int y)
6225 boolean last_line = (y == lev_fieldy - 1);
6226 boolean object_hit = FALSE;
6227 boolean impact = (last_line || object_hit);
6228 int element = Feld[x][y];
6229 int smashed = EL_STEELWALL;
6231 if (!last_line) /* check if element below was hit */
6233 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6236 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6237 MovDir[x][y + 1] != MV_DOWN ||
6238 MovPos[x][y + 1] <= TILEY / 2));
6240 /* do not smash moving elements that left the smashed field in time */
6241 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6242 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6245 #if USE_QUICKSAND_IMPACT_BUGFIX
6246 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6248 RemoveMovingField(x, y + 1);
6249 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6250 Feld[x][y + 2] = EL_ROCK;
6251 TEST_DrawLevelField(x, y + 2);
6256 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6258 RemoveMovingField(x, y + 1);
6259 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6260 Feld[x][y + 2] = EL_ROCK;
6261 TEST_DrawLevelField(x, y + 2);
6268 smashed = MovingOrBlocked2Element(x, y + 1);
6270 impact = (last_line || object_hit);
6273 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6275 SplashAcid(x, y + 1);
6279 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6280 /* only reset graphic animation if graphic really changes after impact */
6282 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6284 ResetGfxAnimation(x, y);
6285 TEST_DrawLevelField(x, y);
6288 if (impact && CAN_EXPLODE_IMPACT(element))
6293 else if (impact && element == EL_PEARL &&
6294 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6296 ResetGfxAnimation(x, y);
6298 Feld[x][y] = EL_PEARL_BREAKING;
6299 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6302 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6304 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6309 if (impact && element == EL_AMOEBA_DROP)
6311 if (object_hit && IS_PLAYER(x, y + 1))
6312 KillPlayerUnlessEnemyProtected(x, y + 1);
6313 else if (object_hit && smashed == EL_PENGUIN)
6317 Feld[x][y] = EL_AMOEBA_GROWING;
6318 Store[x][y] = EL_AMOEBA_WET;
6320 ResetRandomAnimationValue(x, y);
6325 if (object_hit) /* check which object was hit */
6327 if ((CAN_PASS_MAGIC_WALL(element) &&
6328 (smashed == EL_MAGIC_WALL ||
6329 smashed == EL_BD_MAGIC_WALL)) ||
6330 (CAN_PASS_DC_MAGIC_WALL(element) &&
6331 smashed == EL_DC_MAGIC_WALL))
6334 int activated_magic_wall =
6335 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6336 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6337 EL_DC_MAGIC_WALL_ACTIVE);
6339 /* activate magic wall / mill */
6340 SCAN_PLAYFIELD(xx, yy)
6342 if (Feld[xx][yy] == smashed)
6343 Feld[xx][yy] = activated_magic_wall;
6346 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6347 game.magic_wall_active = TRUE;
6349 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6350 SND_MAGIC_WALL_ACTIVATING :
6351 smashed == EL_BD_MAGIC_WALL ?
6352 SND_BD_MAGIC_WALL_ACTIVATING :
6353 SND_DC_MAGIC_WALL_ACTIVATING));
6356 if (IS_PLAYER(x, y + 1))
6358 if (CAN_SMASH_PLAYER(element))
6360 KillPlayerUnlessEnemyProtected(x, y + 1);
6364 else if (smashed == EL_PENGUIN)
6366 if (CAN_SMASH_PLAYER(element))
6372 else if (element == EL_BD_DIAMOND)
6374 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6380 else if (((element == EL_SP_INFOTRON ||
6381 element == EL_SP_ZONK) &&
6382 (smashed == EL_SP_SNIKSNAK ||
6383 smashed == EL_SP_ELECTRON ||
6384 smashed == EL_SP_DISK_ORANGE)) ||
6385 (element == EL_SP_INFOTRON &&
6386 smashed == EL_SP_DISK_YELLOW))
6391 else if (CAN_SMASH_EVERYTHING(element))
6393 if (IS_CLASSIC_ENEMY(smashed) ||
6394 CAN_EXPLODE_SMASHED(smashed))
6399 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6401 if (smashed == EL_LAMP ||
6402 smashed == EL_LAMP_ACTIVE)
6407 else if (smashed == EL_NUT)
6409 Feld[x][y + 1] = EL_NUT_BREAKING;
6410 PlayLevelSound(x, y, SND_NUT_BREAKING);
6411 RaiseScoreElement(EL_NUT);
6414 else if (smashed == EL_PEARL)
6416 ResetGfxAnimation(x, y);
6418 Feld[x][y + 1] = EL_PEARL_BREAKING;
6419 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6422 else if (smashed == EL_DIAMOND)
6424 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6425 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6428 else if (IS_BELT_SWITCH(smashed))
6430 ToggleBeltSwitch(x, y + 1);
6432 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6433 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6434 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6435 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6437 ToggleSwitchgateSwitch(x, y + 1);
6439 else if (smashed == EL_LIGHT_SWITCH ||
6440 smashed == EL_LIGHT_SWITCH_ACTIVE)
6442 ToggleLightSwitch(x, y + 1);
6446 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6448 CheckElementChangeBySide(x, y + 1, smashed, element,
6449 CE_SWITCHED, CH_SIDE_TOP);
6450 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6456 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6461 /* play sound of magic wall / mill */
6463 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6464 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6465 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6467 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6468 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6469 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6470 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6471 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6472 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6477 /* play sound of object that hits the ground */
6478 if (last_line || object_hit)
6479 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6482 inline static void TurnRoundExt(int x, int y)
6494 { 0, 0 }, { 0, 0 }, { 0, 0 },
6499 int left, right, back;
6503 { MV_DOWN, MV_UP, MV_RIGHT },
6504 { MV_UP, MV_DOWN, MV_LEFT },
6506 { MV_LEFT, MV_RIGHT, MV_DOWN },
6510 { MV_RIGHT, MV_LEFT, MV_UP }
6513 int element = Feld[x][y];
6514 int move_pattern = element_info[element].move_pattern;
6516 int old_move_dir = MovDir[x][y];
6517 int left_dir = turn[old_move_dir].left;
6518 int right_dir = turn[old_move_dir].right;
6519 int back_dir = turn[old_move_dir].back;
6521 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6522 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6523 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6524 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6526 int left_x = x + left_dx, left_y = y + left_dy;
6527 int right_x = x + right_dx, right_y = y + right_dy;
6528 int move_x = x + move_dx, move_y = y + move_dy;
6532 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6534 TestIfBadThingTouchesOtherBadThing(x, y);
6536 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6537 MovDir[x][y] = right_dir;
6538 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6539 MovDir[x][y] = left_dir;
6541 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6543 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6546 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6548 TestIfBadThingTouchesOtherBadThing(x, y);
6550 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6551 MovDir[x][y] = left_dir;
6552 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6553 MovDir[x][y] = right_dir;
6555 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6557 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6560 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6562 TestIfBadThingTouchesOtherBadThing(x, y);
6564 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6565 MovDir[x][y] = left_dir;
6566 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6567 MovDir[x][y] = right_dir;
6569 if (MovDir[x][y] != old_move_dir)
6572 else if (element == EL_YAMYAM)
6574 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6575 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6577 if (can_turn_left && can_turn_right)
6578 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6579 else if (can_turn_left)
6580 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6581 else if (can_turn_right)
6582 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6584 MovDir[x][y] = back_dir;
6586 MovDelay[x][y] = 16 + 16 * RND(3);
6588 else if (element == EL_DARK_YAMYAM)
6590 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6592 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6595 if (can_turn_left && can_turn_right)
6596 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6597 else if (can_turn_left)
6598 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6599 else if (can_turn_right)
6600 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6602 MovDir[x][y] = back_dir;
6604 MovDelay[x][y] = 16 + 16 * RND(3);
6606 else if (element == EL_PACMAN)
6608 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6609 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6611 if (can_turn_left && can_turn_right)
6612 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6613 else if (can_turn_left)
6614 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6615 else if (can_turn_right)
6616 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6618 MovDir[x][y] = back_dir;
6620 MovDelay[x][y] = 6 + RND(40);
6622 else if (element == EL_PIG)
6624 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6625 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6626 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6627 boolean should_turn_left, should_turn_right, should_move_on;
6629 int rnd = RND(rnd_value);
6631 should_turn_left = (can_turn_left &&
6633 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6634 y + back_dy + left_dy)));
6635 should_turn_right = (can_turn_right &&
6637 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6638 y + back_dy + right_dy)));
6639 should_move_on = (can_move_on &&
6642 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6643 y + move_dy + left_dy) ||
6644 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6645 y + move_dy + right_dy)));
6647 if (should_turn_left || should_turn_right || should_move_on)
6649 if (should_turn_left && should_turn_right && should_move_on)
6650 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6651 rnd < 2 * rnd_value / 3 ? right_dir :
6653 else if (should_turn_left && should_turn_right)
6654 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6655 else if (should_turn_left && should_move_on)
6656 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6657 else if (should_turn_right && should_move_on)
6658 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6659 else if (should_turn_left)
6660 MovDir[x][y] = left_dir;
6661 else if (should_turn_right)
6662 MovDir[x][y] = right_dir;
6663 else if (should_move_on)
6664 MovDir[x][y] = old_move_dir;
6666 else if (can_move_on && rnd > rnd_value / 8)
6667 MovDir[x][y] = old_move_dir;
6668 else if (can_turn_left && can_turn_right)
6669 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6670 else if (can_turn_left && rnd > rnd_value / 8)
6671 MovDir[x][y] = left_dir;
6672 else if (can_turn_right && rnd > rnd_value/8)
6673 MovDir[x][y] = right_dir;
6675 MovDir[x][y] = back_dir;
6677 xx = x + move_xy[MovDir[x][y]].dx;
6678 yy = y + move_xy[MovDir[x][y]].dy;
6680 if (!IN_LEV_FIELD(xx, yy) ||
6681 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6682 MovDir[x][y] = old_move_dir;
6686 else if (element == EL_DRAGON)
6688 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6689 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6690 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6692 int rnd = RND(rnd_value);
6694 if (can_move_on && rnd > rnd_value / 8)
6695 MovDir[x][y] = old_move_dir;
6696 else if (can_turn_left && can_turn_right)
6697 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6698 else if (can_turn_left && rnd > rnd_value / 8)
6699 MovDir[x][y] = left_dir;
6700 else if (can_turn_right && rnd > rnd_value / 8)
6701 MovDir[x][y] = right_dir;
6703 MovDir[x][y] = back_dir;
6705 xx = x + move_xy[MovDir[x][y]].dx;
6706 yy = y + move_xy[MovDir[x][y]].dy;
6708 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6709 MovDir[x][y] = old_move_dir;
6713 else if (element == EL_MOLE)
6715 boolean can_move_on =
6716 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6717 IS_AMOEBOID(Feld[move_x][move_y]) ||
6718 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6721 boolean can_turn_left =
6722 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6723 IS_AMOEBOID(Feld[left_x][left_y])));
6725 boolean can_turn_right =
6726 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6727 IS_AMOEBOID(Feld[right_x][right_y])));
6729 if (can_turn_left && can_turn_right)
6730 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6731 else if (can_turn_left)
6732 MovDir[x][y] = left_dir;
6734 MovDir[x][y] = right_dir;
6737 if (MovDir[x][y] != old_move_dir)
6740 else if (element == EL_BALLOON)
6742 MovDir[x][y] = game.wind_direction;
6745 else if (element == EL_SPRING)
6747 if (MovDir[x][y] & MV_HORIZONTAL)
6749 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6750 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6752 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6753 ResetGfxAnimation(move_x, move_y);
6754 TEST_DrawLevelField(move_x, move_y);
6756 MovDir[x][y] = back_dir;
6758 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6759 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6760 MovDir[x][y] = MV_NONE;
6765 else if (element == EL_ROBOT ||
6766 element == EL_SATELLITE ||
6767 element == EL_PENGUIN ||
6768 element == EL_EMC_ANDROID)
6770 int attr_x = -1, attr_y = -1;
6781 for (i = 0; i < MAX_PLAYERS; i++)
6783 struct PlayerInfo *player = &stored_player[i];
6784 int jx = player->jx, jy = player->jy;
6786 if (!player->active)
6790 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6798 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6799 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6800 game.engine_version < VERSION_IDENT(3,1,0,0)))
6806 if (element == EL_PENGUIN)
6809 static int xy[4][2] =
6817 for (i = 0; i < NUM_DIRECTIONS; i++)
6819 int ex = x + xy[i][0];
6820 int ey = y + xy[i][1];
6822 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6823 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6824 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6825 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6834 MovDir[x][y] = MV_NONE;
6836 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6837 else if (attr_x > x)
6838 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6840 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6841 else if (attr_y > y)
6842 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6844 if (element == EL_ROBOT)
6848 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6849 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6850 Moving2Blocked(x, y, &newx, &newy);
6852 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6853 MovDelay[x][y] = 8 + 8 * !RND(3);
6855 MovDelay[x][y] = 16;
6857 else if (element == EL_PENGUIN)
6863 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6865 boolean first_horiz = RND(2);
6866 int new_move_dir = MovDir[x][y];
6869 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6870 Moving2Blocked(x, y, &newx, &newy);
6872 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6876 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6877 Moving2Blocked(x, y, &newx, &newy);
6879 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6882 MovDir[x][y] = old_move_dir;
6886 else if (element == EL_SATELLITE)
6892 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6894 boolean first_horiz = RND(2);
6895 int new_move_dir = MovDir[x][y];
6898 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6899 Moving2Blocked(x, y, &newx, &newy);
6901 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6905 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6906 Moving2Blocked(x, y, &newx, &newy);
6908 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6911 MovDir[x][y] = old_move_dir;
6915 else if (element == EL_EMC_ANDROID)
6917 static int check_pos[16] =
6919 -1, /* 0 => (invalid) */
6920 7, /* 1 => MV_LEFT */
6921 3, /* 2 => MV_RIGHT */
6922 -1, /* 3 => (invalid) */
6924 0, /* 5 => MV_LEFT | MV_UP */
6925 2, /* 6 => MV_RIGHT | MV_UP */
6926 -1, /* 7 => (invalid) */
6927 5, /* 8 => MV_DOWN */
6928 6, /* 9 => MV_LEFT | MV_DOWN */
6929 4, /* 10 => MV_RIGHT | MV_DOWN */
6930 -1, /* 11 => (invalid) */
6931 -1, /* 12 => (invalid) */
6932 -1, /* 13 => (invalid) */
6933 -1, /* 14 => (invalid) */
6934 -1, /* 15 => (invalid) */
6942 { -1, -1, MV_LEFT | MV_UP },
6944 { +1, -1, MV_RIGHT | MV_UP },
6945 { +1, 0, MV_RIGHT },
6946 { +1, +1, MV_RIGHT | MV_DOWN },
6948 { -1, +1, MV_LEFT | MV_DOWN },
6951 int start_pos, check_order;
6952 boolean can_clone = FALSE;
6955 /* check if there is any free field around current position */
6956 for (i = 0; i < 8; i++)
6958 int newx = x + check_xy[i].dx;
6959 int newy = y + check_xy[i].dy;
6961 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6969 if (can_clone) /* randomly find an element to clone */
6973 start_pos = check_pos[RND(8)];
6974 check_order = (RND(2) ? -1 : +1);
6976 for (i = 0; i < 8; i++)
6978 int pos_raw = start_pos + i * check_order;
6979 int pos = (pos_raw + 8) % 8;
6980 int newx = x + check_xy[pos].dx;
6981 int newy = y + check_xy[pos].dy;
6983 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6985 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6986 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6988 Store[x][y] = Feld[newx][newy];
6997 if (can_clone) /* randomly find a direction to move */
7001 start_pos = check_pos[RND(8)];
7002 check_order = (RND(2) ? -1 : +1);
7004 for (i = 0; i < 8; i++)
7006 int pos_raw = start_pos + i * check_order;
7007 int pos = (pos_raw + 8) % 8;
7008 int newx = x + check_xy[pos].dx;
7009 int newy = y + check_xy[pos].dy;
7010 int new_move_dir = check_xy[pos].dir;
7012 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7014 MovDir[x][y] = new_move_dir;
7015 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7024 if (can_clone) /* cloning and moving successful */
7027 /* cannot clone -- try to move towards player */
7029 start_pos = check_pos[MovDir[x][y] & 0x0f];
7030 check_order = (RND(2) ? -1 : +1);
7032 for (i = 0; i < 3; i++)
7034 /* first check start_pos, then previous/next or (next/previous) pos */
7035 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7036 int pos = (pos_raw + 8) % 8;
7037 int newx = x + check_xy[pos].dx;
7038 int newy = y + check_xy[pos].dy;
7039 int new_move_dir = check_xy[pos].dir;
7041 if (IS_PLAYER(newx, newy))
7044 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7046 MovDir[x][y] = new_move_dir;
7047 MovDelay[x][y] = level.android_move_time * 8 + 1;
7054 else if (move_pattern == MV_TURNING_LEFT ||
7055 move_pattern == MV_TURNING_RIGHT ||
7056 move_pattern == MV_TURNING_LEFT_RIGHT ||
7057 move_pattern == MV_TURNING_RIGHT_LEFT ||
7058 move_pattern == MV_TURNING_RANDOM ||
7059 move_pattern == MV_ALL_DIRECTIONS)
7061 boolean can_turn_left =
7062 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7063 boolean can_turn_right =
7064 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7066 if (element_info[element].move_stepsize == 0) /* "not moving" */
7069 if (move_pattern == MV_TURNING_LEFT)
7070 MovDir[x][y] = left_dir;
7071 else if (move_pattern == MV_TURNING_RIGHT)
7072 MovDir[x][y] = right_dir;
7073 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7074 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7075 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7076 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7077 else if (move_pattern == MV_TURNING_RANDOM)
7078 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7079 can_turn_right && !can_turn_left ? right_dir :
7080 RND(2) ? left_dir : right_dir);
7081 else if (can_turn_left && can_turn_right)
7082 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7083 else if (can_turn_left)
7084 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7085 else if (can_turn_right)
7086 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7088 MovDir[x][y] = back_dir;
7090 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7092 else if (move_pattern == MV_HORIZONTAL ||
7093 move_pattern == MV_VERTICAL)
7095 if (move_pattern & old_move_dir)
7096 MovDir[x][y] = back_dir;
7097 else if (move_pattern == MV_HORIZONTAL)
7098 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7099 else if (move_pattern == MV_VERTICAL)
7100 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7102 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7104 else if (move_pattern & MV_ANY_DIRECTION)
7106 MovDir[x][y] = move_pattern;
7107 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7109 else if (move_pattern & MV_WIND_DIRECTION)
7111 MovDir[x][y] = game.wind_direction;
7112 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7114 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7116 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7117 MovDir[x][y] = left_dir;
7118 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7119 MovDir[x][y] = right_dir;
7121 if (MovDir[x][y] != old_move_dir)
7122 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7124 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7126 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7127 MovDir[x][y] = right_dir;
7128 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7129 MovDir[x][y] = left_dir;
7131 if (MovDir[x][y] != old_move_dir)
7132 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7134 else if (move_pattern == MV_TOWARDS_PLAYER ||
7135 move_pattern == MV_AWAY_FROM_PLAYER)
7137 int attr_x = -1, attr_y = -1;
7139 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7150 for (i = 0; i < MAX_PLAYERS; i++)
7152 struct PlayerInfo *player = &stored_player[i];
7153 int jx = player->jx, jy = player->jy;
7155 if (!player->active)
7159 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7167 MovDir[x][y] = MV_NONE;
7169 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7170 else if (attr_x > x)
7171 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7173 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7174 else if (attr_y > y)
7175 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7177 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7181 boolean first_horiz = RND(2);
7182 int new_move_dir = MovDir[x][y];
7184 if (element_info[element].move_stepsize == 0) /* "not moving" */
7186 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7187 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7193 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7194 Moving2Blocked(x, y, &newx, &newy);
7196 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7200 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7201 Moving2Blocked(x, y, &newx, &newy);
7203 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7206 MovDir[x][y] = old_move_dir;
7209 else if (move_pattern == MV_WHEN_PUSHED ||
7210 move_pattern == MV_WHEN_DROPPED)
7212 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7213 MovDir[x][y] = MV_NONE;
7217 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7219 static int test_xy[7][2] =
7229 static int test_dir[7] =
7239 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7240 int move_preference = -1000000; /* start with very low preference */
7241 int new_move_dir = MV_NONE;
7242 int start_test = RND(4);
7245 for (i = 0; i < NUM_DIRECTIONS; i++)
7247 int move_dir = test_dir[start_test + i];
7248 int move_dir_preference;
7250 xx = x + test_xy[start_test + i][0];
7251 yy = y + test_xy[start_test + i][1];
7253 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7254 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7256 new_move_dir = move_dir;
7261 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7264 move_dir_preference = -1 * RunnerVisit[xx][yy];
7265 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7266 move_dir_preference = PlayerVisit[xx][yy];
7268 if (move_dir_preference > move_preference)
7270 /* prefer field that has not been visited for the longest time */
7271 move_preference = move_dir_preference;
7272 new_move_dir = move_dir;
7274 else if (move_dir_preference == move_preference &&
7275 move_dir == old_move_dir)
7277 /* prefer last direction when all directions are preferred equally */
7278 move_preference = move_dir_preference;
7279 new_move_dir = move_dir;
7283 MovDir[x][y] = new_move_dir;
7284 if (old_move_dir != new_move_dir)
7285 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7289 static void TurnRound(int x, int y)
7291 int direction = MovDir[x][y];
7295 GfxDir[x][y] = MovDir[x][y];
7297 if (direction != MovDir[x][y])
7301 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7303 ResetGfxFrame(x, y);
7306 static boolean JustBeingPushed(int x, int y)
7310 for (i = 0; i < MAX_PLAYERS; i++)
7312 struct PlayerInfo *player = &stored_player[i];
7314 if (player->active && player->is_pushing && player->MovPos)
7316 int next_jx = player->jx + (player->jx - player->last_jx);
7317 int next_jy = player->jy + (player->jy - player->last_jy);
7319 if (x == next_jx && y == next_jy)
7327 void StartMoving(int x, int y)
7329 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7330 int element = Feld[x][y];
7335 if (MovDelay[x][y] == 0)
7336 GfxAction[x][y] = ACTION_DEFAULT;
7338 if (CAN_FALL(element) && y < lev_fieldy - 1)
7340 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7341 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7342 if (JustBeingPushed(x, y))
7345 if (element == EL_QUICKSAND_FULL)
7347 if (IS_FREE(x, y + 1))
7349 InitMovingField(x, y, MV_DOWN);
7350 started_moving = TRUE;
7352 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7353 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7354 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7355 Store[x][y] = EL_ROCK;
7357 Store[x][y] = EL_ROCK;
7360 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7362 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7364 if (!MovDelay[x][y])
7366 MovDelay[x][y] = TILEY + 1;
7368 ResetGfxAnimation(x, y);
7369 ResetGfxAnimation(x, y + 1);
7374 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7375 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7382 Feld[x][y] = EL_QUICKSAND_EMPTY;
7383 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7384 Store[x][y + 1] = Store[x][y];
7387 PlayLevelSoundAction(x, y, ACTION_FILLING);
7389 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7391 if (!MovDelay[x][y])
7393 MovDelay[x][y] = TILEY + 1;
7395 ResetGfxAnimation(x, y);
7396 ResetGfxAnimation(x, y + 1);
7401 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7402 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7409 Feld[x][y] = EL_QUICKSAND_EMPTY;
7410 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7411 Store[x][y + 1] = Store[x][y];
7414 PlayLevelSoundAction(x, y, ACTION_FILLING);
7417 else if (element == EL_QUICKSAND_FAST_FULL)
7419 if (IS_FREE(x, y + 1))
7421 InitMovingField(x, y, MV_DOWN);
7422 started_moving = TRUE;
7424 Feld[x][y] = EL_QUICKSAND_FAST_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_FAST_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_FAST_EMPTYING);
7447 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7454 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7455 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7456 Store[x][y + 1] = Store[x][y];
7459 PlayLevelSoundAction(x, y, ACTION_FILLING);
7461 else if (Feld[x][y + 1] == EL_QUICKSAND_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_FAST_EMPTYING);
7474 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7481 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7482 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7483 Store[x][y + 1] = Store[x][y];
7486 PlayLevelSoundAction(x, y, ACTION_FILLING);
7489 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7490 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7492 InitMovingField(x, y, MV_DOWN);
7493 started_moving = TRUE;
7495 Feld[x][y] = EL_QUICKSAND_FILLING;
7496 Store[x][y] = element;
7498 PlayLevelSoundAction(x, y, ACTION_FILLING);
7500 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7501 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7503 InitMovingField(x, y, MV_DOWN);
7504 started_moving = TRUE;
7506 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7507 Store[x][y] = element;
7509 PlayLevelSoundAction(x, y, ACTION_FILLING);
7511 else if (element == EL_MAGIC_WALL_FULL)
7513 if (IS_FREE(x, y + 1))
7515 InitMovingField(x, y, MV_DOWN);
7516 started_moving = TRUE;
7518 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7519 Store[x][y] = EL_CHANGED(Store[x][y]);
7521 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7523 if (!MovDelay[x][y])
7524 MovDelay[x][y] = TILEY / 4 + 1;
7533 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7534 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7535 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7539 else if (element == EL_BD_MAGIC_WALL_FULL)
7541 if (IS_FREE(x, y + 1))
7543 InitMovingField(x, y, MV_DOWN);
7544 started_moving = TRUE;
7546 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7547 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7549 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7551 if (!MovDelay[x][y])
7552 MovDelay[x][y] = TILEY / 4 + 1;
7561 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7562 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7563 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7567 else if (element == EL_DC_MAGIC_WALL_FULL)
7569 if (IS_FREE(x, y + 1))
7571 InitMovingField(x, y, MV_DOWN);
7572 started_moving = TRUE;
7574 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7575 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7577 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7579 if (!MovDelay[x][y])
7580 MovDelay[x][y] = TILEY / 4 + 1;
7589 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7590 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7591 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7595 else if ((CAN_PASS_MAGIC_WALL(element) &&
7596 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7597 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7598 (CAN_PASS_DC_MAGIC_WALL(element) &&
7599 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7602 InitMovingField(x, y, MV_DOWN);
7603 started_moving = TRUE;
7606 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7607 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7608 EL_DC_MAGIC_WALL_FILLING);
7609 Store[x][y] = element;
7611 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7613 SplashAcid(x, y + 1);
7615 InitMovingField(x, y, MV_DOWN);
7616 started_moving = TRUE;
7618 Store[x][y] = EL_ACID;
7621 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7622 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7623 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7624 CAN_FALL(element) && WasJustFalling[x][y] &&
7625 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7627 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7628 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7629 (Feld[x][y + 1] == EL_BLOCKED)))
7631 /* this is needed for a special case not covered by calling "Impact()"
7632 from "ContinueMoving()": if an element moves to a tile directly below
7633 another element which was just falling on that tile (which was empty
7634 in the previous frame), the falling element above would just stop
7635 instead of smashing the element below (in previous version, the above
7636 element was just checked for "moving" instead of "falling", resulting
7637 in incorrect smashes caused by horizontal movement of the above
7638 element; also, the case of the player being the element to smash was
7639 simply not covered here... :-/ ) */
7641 CheckCollision[x][y] = 0;
7642 CheckImpact[x][y] = 0;
7646 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7648 if (MovDir[x][y] == MV_NONE)
7650 InitMovingField(x, y, MV_DOWN);
7651 started_moving = TRUE;
7654 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7656 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7657 MovDir[x][y] = MV_DOWN;
7659 InitMovingField(x, y, MV_DOWN);
7660 started_moving = TRUE;
7662 else if (element == EL_AMOEBA_DROP)
7664 Feld[x][y] = EL_AMOEBA_GROWING;
7665 Store[x][y] = EL_AMOEBA_WET;
7667 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7668 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7669 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7670 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7672 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7673 (IS_FREE(x - 1, y + 1) ||
7674 Feld[x - 1][y + 1] == EL_ACID));
7675 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7676 (IS_FREE(x + 1, y + 1) ||
7677 Feld[x + 1][y + 1] == EL_ACID));
7678 boolean can_fall_any = (can_fall_left || can_fall_right);
7679 boolean can_fall_both = (can_fall_left && can_fall_right);
7680 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7682 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7684 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7685 can_fall_right = FALSE;
7686 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7687 can_fall_left = FALSE;
7688 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7689 can_fall_right = FALSE;
7690 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7691 can_fall_left = FALSE;
7693 can_fall_any = (can_fall_left || can_fall_right);
7694 can_fall_both = FALSE;
7699 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7700 can_fall_right = FALSE; /* slip down on left side */
7702 can_fall_left = !(can_fall_right = RND(2));
7704 can_fall_both = FALSE;
7709 /* if not determined otherwise, prefer left side for slipping down */
7710 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7711 started_moving = TRUE;
7714 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7716 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7717 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7718 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7719 int belt_dir = game.belt_dir[belt_nr];
7721 if ((belt_dir == MV_LEFT && left_is_free) ||
7722 (belt_dir == MV_RIGHT && right_is_free))
7724 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7726 InitMovingField(x, y, belt_dir);
7727 started_moving = TRUE;
7729 Pushed[x][y] = TRUE;
7730 Pushed[nextx][y] = TRUE;
7732 GfxAction[x][y] = ACTION_DEFAULT;
7736 MovDir[x][y] = 0; /* if element was moving, stop it */
7741 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7742 if (CAN_MOVE(element) && !started_moving)
7744 int move_pattern = element_info[element].move_pattern;
7747 Moving2Blocked(x, y, &newx, &newy);
7749 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7752 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7753 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7755 WasJustMoving[x][y] = 0;
7756 CheckCollision[x][y] = 0;
7758 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7760 if (Feld[x][y] != element) /* element has changed */
7764 if (!MovDelay[x][y]) /* start new movement phase */
7766 /* all objects that can change their move direction after each step
7767 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7769 if (element != EL_YAMYAM &&
7770 element != EL_DARK_YAMYAM &&
7771 element != EL_PACMAN &&
7772 !(move_pattern & MV_ANY_DIRECTION) &&
7773 move_pattern != MV_TURNING_LEFT &&
7774 move_pattern != MV_TURNING_RIGHT &&
7775 move_pattern != MV_TURNING_LEFT_RIGHT &&
7776 move_pattern != MV_TURNING_RIGHT_LEFT &&
7777 move_pattern != MV_TURNING_RANDOM)
7781 if (MovDelay[x][y] && (element == EL_BUG ||
7782 element == EL_SPACESHIP ||
7783 element == EL_SP_SNIKSNAK ||
7784 element == EL_SP_ELECTRON ||
7785 element == EL_MOLE))
7786 TEST_DrawLevelField(x, y);
7790 if (MovDelay[x][y]) /* wait some time before next movement */
7794 if (element == EL_ROBOT ||
7795 element == EL_YAMYAM ||
7796 element == EL_DARK_YAMYAM)
7798 DrawLevelElementAnimationIfNeeded(x, y, element);
7799 PlayLevelSoundAction(x, y, ACTION_WAITING);
7801 else if (element == EL_SP_ELECTRON)
7802 DrawLevelElementAnimationIfNeeded(x, y, element);
7803 else if (element == EL_DRAGON)
7806 int dir = MovDir[x][y];
7807 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7808 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7809 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7810 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7811 dir == MV_UP ? IMG_FLAMES_1_UP :
7812 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7813 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7815 GfxAction[x][y] = ACTION_ATTACKING;
7817 if (IS_PLAYER(x, y))
7818 DrawPlayerField(x, y);
7820 TEST_DrawLevelField(x, y);
7822 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7824 for (i = 1; i <= 3; i++)
7826 int xx = x + i * dx;
7827 int yy = y + i * dy;
7828 int sx = SCREENX(xx);
7829 int sy = SCREENY(yy);
7830 int flame_graphic = graphic + (i - 1);
7832 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7837 int flamed = MovingOrBlocked2Element(xx, yy);
7839 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7842 RemoveMovingField(xx, yy);
7844 ChangeDelay[xx][yy] = 0;
7846 Feld[xx][yy] = EL_FLAMES;
7848 if (IN_SCR_FIELD(sx, sy))
7850 TEST_DrawLevelFieldCrumbled(xx, yy);
7851 DrawGraphic(sx, sy, flame_graphic, frame);
7856 if (Feld[xx][yy] == EL_FLAMES)
7857 Feld[xx][yy] = EL_EMPTY;
7858 TEST_DrawLevelField(xx, yy);
7863 if (MovDelay[x][y]) /* element still has to wait some time */
7865 PlayLevelSoundAction(x, y, ACTION_WAITING);
7871 /* now make next step */
7873 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7875 if (DONT_COLLIDE_WITH(element) &&
7876 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7877 !PLAYER_ENEMY_PROTECTED(newx, newy))
7879 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7884 else if (CAN_MOVE_INTO_ACID(element) &&
7885 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7886 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7887 (MovDir[x][y] == MV_DOWN ||
7888 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7890 SplashAcid(newx, newy);
7891 Store[x][y] = EL_ACID;
7893 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7895 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7896 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7897 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7898 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7901 TEST_DrawLevelField(x, y);
7903 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7904 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7905 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7907 local_player->friends_still_needed--;
7908 if (!local_player->friends_still_needed &&
7909 !local_player->GameOver && AllPlayersGone)
7910 PlayerWins(local_player);
7914 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7916 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7917 TEST_DrawLevelField(newx, newy);
7919 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7921 else if (!IS_FREE(newx, newy))
7923 GfxAction[x][y] = ACTION_WAITING;
7925 if (IS_PLAYER(x, y))
7926 DrawPlayerField(x, y);
7928 TEST_DrawLevelField(x, y);
7933 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7935 if (IS_FOOD_PIG(Feld[newx][newy]))
7937 if (IS_MOVING(newx, newy))
7938 RemoveMovingField(newx, newy);
7941 Feld[newx][newy] = EL_EMPTY;
7942 TEST_DrawLevelField(newx, newy);
7945 PlayLevelSound(x, y, SND_PIG_DIGGING);
7947 else if (!IS_FREE(newx, newy))
7949 if (IS_PLAYER(x, y))
7950 DrawPlayerField(x, y);
7952 TEST_DrawLevelField(x, y);
7957 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7959 if (Store[x][y] != EL_EMPTY)
7961 boolean can_clone = FALSE;
7964 /* check if element to clone is still there */
7965 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7967 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7975 /* cannot clone or target field not free anymore -- do not clone */
7976 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7977 Store[x][y] = EL_EMPTY;
7980 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7982 if (IS_MV_DIAGONAL(MovDir[x][y]))
7984 int diagonal_move_dir = MovDir[x][y];
7985 int stored = Store[x][y];
7986 int change_delay = 8;
7989 /* android is moving diagonally */
7991 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7993 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7994 GfxElement[x][y] = EL_EMC_ANDROID;
7995 GfxAction[x][y] = ACTION_SHRINKING;
7996 GfxDir[x][y] = diagonal_move_dir;
7997 ChangeDelay[x][y] = change_delay;
7999 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8002 DrawLevelGraphicAnimation(x, y, graphic);
8003 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8005 if (Feld[newx][newy] == EL_ACID)
8007 SplashAcid(newx, newy);
8012 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8014 Store[newx][newy] = EL_EMC_ANDROID;
8015 GfxElement[newx][newy] = EL_EMC_ANDROID;
8016 GfxAction[newx][newy] = ACTION_GROWING;
8017 GfxDir[newx][newy] = diagonal_move_dir;
8018 ChangeDelay[newx][newy] = change_delay;
8020 graphic = el_act_dir2img(GfxElement[newx][newy],
8021 GfxAction[newx][newy], GfxDir[newx][newy]);
8023 DrawLevelGraphicAnimation(newx, newy, graphic);
8024 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8030 Feld[newx][newy] = EL_EMPTY;
8031 TEST_DrawLevelField(newx, newy);
8033 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8036 else if (!IS_FREE(newx, newy))
8041 else if (IS_CUSTOM_ELEMENT(element) &&
8042 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8044 if (!DigFieldByCE(newx, newy, element))
8047 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8049 RunnerVisit[x][y] = FrameCounter;
8050 PlayerVisit[x][y] /= 8; /* expire player visit path */
8053 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8055 if (!IS_FREE(newx, newy))
8057 if (IS_PLAYER(x, y))
8058 DrawPlayerField(x, y);
8060 TEST_DrawLevelField(x, y);
8066 boolean wanna_flame = !RND(10);
8067 int dx = newx - x, dy = newy - y;
8068 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8069 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8070 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8071 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8072 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8073 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8076 IS_CLASSIC_ENEMY(element1) ||
8077 IS_CLASSIC_ENEMY(element2)) &&
8078 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8079 element1 != EL_FLAMES && element2 != EL_FLAMES)
8081 ResetGfxAnimation(x, y);
8082 GfxAction[x][y] = ACTION_ATTACKING;
8084 if (IS_PLAYER(x, y))
8085 DrawPlayerField(x, y);
8087 TEST_DrawLevelField(x, y);
8089 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8091 MovDelay[x][y] = 50;
8093 Feld[newx][newy] = EL_FLAMES;
8094 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8095 Feld[newx1][newy1] = EL_FLAMES;
8096 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8097 Feld[newx2][newy2] = EL_FLAMES;
8103 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8104 Feld[newx][newy] == EL_DIAMOND)
8106 if (IS_MOVING(newx, newy))
8107 RemoveMovingField(newx, newy);
8110 Feld[newx][newy] = EL_EMPTY;
8111 TEST_DrawLevelField(newx, newy);
8114 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8116 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8117 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8119 if (AmoebaNr[newx][newy])
8121 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8122 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8123 Feld[newx][newy] == EL_BD_AMOEBA)
8124 AmoebaCnt[AmoebaNr[newx][newy]]--;
8127 if (IS_MOVING(newx, newy))
8129 RemoveMovingField(newx, newy);
8133 Feld[newx][newy] = EL_EMPTY;
8134 TEST_DrawLevelField(newx, newy);
8137 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8139 else if ((element == EL_PACMAN || element == EL_MOLE)
8140 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8142 if (AmoebaNr[newx][newy])
8144 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8145 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8146 Feld[newx][newy] == EL_BD_AMOEBA)
8147 AmoebaCnt[AmoebaNr[newx][newy]]--;
8150 if (element == EL_MOLE)
8152 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8153 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8155 ResetGfxAnimation(x, y);
8156 GfxAction[x][y] = ACTION_DIGGING;
8157 TEST_DrawLevelField(x, y);
8159 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8161 return; /* wait for shrinking amoeba */
8163 else /* element == EL_PACMAN */
8165 Feld[newx][newy] = EL_EMPTY;
8166 TEST_DrawLevelField(newx, newy);
8167 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8170 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8171 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8172 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8174 /* wait for shrinking amoeba to completely disappear */
8177 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8179 /* object was running against a wall */
8183 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8184 DrawLevelElementAnimation(x, y, element);
8186 if (DONT_TOUCH(element))
8187 TestIfBadThingTouchesPlayer(x, y);
8192 InitMovingField(x, y, MovDir[x][y]);
8194 PlayLevelSoundAction(x, y, ACTION_MOVING);
8198 ContinueMoving(x, y);
8201 void ContinueMoving(int x, int y)
8203 int element = Feld[x][y];
8204 struct ElementInfo *ei = &element_info[element];
8205 int direction = MovDir[x][y];
8206 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8207 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8208 int newx = x + dx, newy = y + dy;
8209 int stored = Store[x][y];
8210 int stored_new = Store[newx][newy];
8211 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8212 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8213 boolean last_line = (newy == lev_fieldy - 1);
8215 MovPos[x][y] += getElementMoveStepsize(x, y);
8217 if (pushed_by_player) /* special case: moving object pushed by player */
8218 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8220 if (ABS(MovPos[x][y]) < TILEX)
8222 TEST_DrawLevelField(x, y);
8224 return; /* element is still moving */
8227 /* element reached destination field */
8229 Feld[x][y] = EL_EMPTY;
8230 Feld[newx][newy] = element;
8231 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8233 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8235 element = Feld[newx][newy] = EL_ACID;
8237 else if (element == EL_MOLE)
8239 Feld[x][y] = EL_SAND;
8241 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8243 else if (element == EL_QUICKSAND_FILLING)
8245 element = Feld[newx][newy] = get_next_element(element);
8246 Store[newx][newy] = Store[x][y];
8248 else if (element == EL_QUICKSAND_EMPTYING)
8250 Feld[x][y] = get_next_element(element);
8251 element = Feld[newx][newy] = Store[x][y];
8253 else if (element == EL_QUICKSAND_FAST_FILLING)
8255 element = Feld[newx][newy] = get_next_element(element);
8256 Store[newx][newy] = Store[x][y];
8258 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8260 Feld[x][y] = get_next_element(element);
8261 element = Feld[newx][newy] = Store[x][y];
8263 else if (element == EL_MAGIC_WALL_FILLING)
8265 element = Feld[newx][newy] = get_next_element(element);
8266 if (!game.magic_wall_active)
8267 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8268 Store[newx][newy] = Store[x][y];
8270 else if (element == EL_MAGIC_WALL_EMPTYING)
8272 Feld[x][y] = get_next_element(element);
8273 if (!game.magic_wall_active)
8274 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8275 element = Feld[newx][newy] = Store[x][y];
8277 InitField(newx, newy, FALSE);
8279 else if (element == EL_BD_MAGIC_WALL_FILLING)
8281 element = Feld[newx][newy] = get_next_element(element);
8282 if (!game.magic_wall_active)
8283 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8284 Store[newx][newy] = Store[x][y];
8286 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8288 Feld[x][y] = get_next_element(element);
8289 if (!game.magic_wall_active)
8290 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8291 element = Feld[newx][newy] = Store[x][y];
8293 InitField(newx, newy, FALSE);
8295 else if (element == EL_DC_MAGIC_WALL_FILLING)
8297 element = Feld[newx][newy] = get_next_element(element);
8298 if (!game.magic_wall_active)
8299 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8300 Store[newx][newy] = Store[x][y];
8302 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8304 Feld[x][y] = get_next_element(element);
8305 if (!game.magic_wall_active)
8306 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8307 element = Feld[newx][newy] = Store[x][y];
8309 InitField(newx, newy, FALSE);
8311 else if (element == EL_AMOEBA_DROPPING)
8313 Feld[x][y] = get_next_element(element);
8314 element = Feld[newx][newy] = Store[x][y];
8316 else if (element == EL_SOKOBAN_OBJECT)
8319 Feld[x][y] = Back[x][y];
8321 if (Back[newx][newy])
8322 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8324 Back[x][y] = Back[newx][newy] = 0;
8327 Store[x][y] = EL_EMPTY;
8332 MovDelay[newx][newy] = 0;
8334 if (CAN_CHANGE_OR_HAS_ACTION(element))
8336 /* copy element change control values to new field */
8337 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8338 ChangePage[newx][newy] = ChangePage[x][y];
8339 ChangeCount[newx][newy] = ChangeCount[x][y];
8340 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8343 CustomValue[newx][newy] = CustomValue[x][y];
8345 ChangeDelay[x][y] = 0;
8346 ChangePage[x][y] = -1;
8347 ChangeCount[x][y] = 0;
8348 ChangeEvent[x][y] = -1;
8350 CustomValue[x][y] = 0;
8352 /* copy animation control values to new field */
8353 GfxFrame[newx][newy] = GfxFrame[x][y];
8354 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8355 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8356 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8358 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8360 /* some elements can leave other elements behind after moving */
8361 if (ei->move_leave_element != EL_EMPTY &&
8362 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8363 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8365 int move_leave_element = ei->move_leave_element;
8367 /* this makes it possible to leave the removed element again */
8368 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8369 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8371 Feld[x][y] = move_leave_element;
8373 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8374 MovDir[x][y] = direction;
8376 InitField(x, y, FALSE);
8378 if (GFX_CRUMBLED(Feld[x][y]))
8379 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8381 if (ELEM_IS_PLAYER(move_leave_element))
8382 RelocatePlayer(x, y, move_leave_element);
8385 /* do this after checking for left-behind element */
8386 ResetGfxAnimation(x, y); /* reset animation values for old field */
8388 if (!CAN_MOVE(element) ||
8389 (CAN_FALL(element) && direction == MV_DOWN &&
8390 (element == EL_SPRING ||
8391 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8392 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8393 GfxDir[x][y] = MovDir[newx][newy] = 0;
8395 TEST_DrawLevelField(x, y);
8396 TEST_DrawLevelField(newx, newy);
8398 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8400 /* prevent pushed element from moving on in pushed direction */
8401 if (pushed_by_player && CAN_MOVE(element) &&
8402 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8403 !(element_info[element].move_pattern & direction))
8404 TurnRound(newx, newy);
8406 /* prevent elements on conveyor belt from moving on in last direction */
8407 if (pushed_by_conveyor && CAN_FALL(element) &&
8408 direction & MV_HORIZONTAL)
8409 MovDir[newx][newy] = 0;
8411 if (!pushed_by_player)
8413 int nextx = newx + dx, nexty = newy + dy;
8414 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8416 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8418 if (CAN_FALL(element) && direction == MV_DOWN)
8419 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8421 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8422 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8424 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8425 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8428 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8430 TestIfBadThingTouchesPlayer(newx, newy);
8431 TestIfBadThingTouchesFriend(newx, newy);
8433 if (!IS_CUSTOM_ELEMENT(element))
8434 TestIfBadThingTouchesOtherBadThing(newx, newy);
8436 else if (element == EL_PENGUIN)
8437 TestIfFriendTouchesBadThing(newx, newy);
8439 if (DONT_GET_HIT_BY(element))
8441 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8444 /* give the player one last chance (one more frame) to move away */
8445 if (CAN_FALL(element) && direction == MV_DOWN &&
8446 (last_line || (!IS_FREE(x, newy + 1) &&
8447 (!IS_PLAYER(x, newy + 1) ||
8448 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8451 if (pushed_by_player && !game.use_change_when_pushing_bug)
8453 int push_side = MV_DIR_OPPOSITE(direction);
8454 struct PlayerInfo *player = PLAYERINFO(x, y);
8456 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8457 player->index_bit, push_side);
8458 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8459 player->index_bit, push_side);
8462 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8463 MovDelay[newx][newy] = 1;
8465 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8467 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8468 TestIfElementHitsCustomElement(newx, newy, direction);
8469 TestIfPlayerTouchesCustomElement(newx, newy);
8470 TestIfElementTouchesCustomElement(newx, newy);
8472 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8473 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8474 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8475 MV_DIR_OPPOSITE(direction));
8478 int AmoebeNachbarNr(int ax, int ay)
8481 int element = Feld[ax][ay];
8483 static int xy[4][2] =
8491 for (i = 0; i < NUM_DIRECTIONS; i++)
8493 int x = ax + xy[i][0];
8494 int y = ay + xy[i][1];
8496 if (!IN_LEV_FIELD(x, y))
8499 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8500 group_nr = AmoebaNr[x][y];
8506 void AmoebenVereinigen(int ax, int ay)
8508 int i, x, y, xx, yy;
8509 int new_group_nr = AmoebaNr[ax][ay];
8510 static int xy[4][2] =
8518 if (new_group_nr == 0)
8521 for (i = 0; i < NUM_DIRECTIONS; i++)
8526 if (!IN_LEV_FIELD(x, y))
8529 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8530 Feld[x][y] == EL_BD_AMOEBA ||
8531 Feld[x][y] == EL_AMOEBA_DEAD) &&
8532 AmoebaNr[x][y] != new_group_nr)
8534 int old_group_nr = AmoebaNr[x][y];
8536 if (old_group_nr == 0)
8539 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8540 AmoebaCnt[old_group_nr] = 0;
8541 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8542 AmoebaCnt2[old_group_nr] = 0;
8544 SCAN_PLAYFIELD(xx, yy)
8546 if (AmoebaNr[xx][yy] == old_group_nr)
8547 AmoebaNr[xx][yy] = new_group_nr;
8553 void AmoebeUmwandeln(int ax, int ay)
8557 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8559 int group_nr = AmoebaNr[ax][ay];
8564 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8565 printf("AmoebeUmwandeln(): This should never happen!\n");
8570 SCAN_PLAYFIELD(x, y)
8572 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8575 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8579 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8580 SND_AMOEBA_TURNING_TO_GEM :
8581 SND_AMOEBA_TURNING_TO_ROCK));
8586 static int xy[4][2] =
8594 for (i = 0; i < NUM_DIRECTIONS; i++)
8599 if (!IN_LEV_FIELD(x, y))
8602 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8604 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8605 SND_AMOEBA_TURNING_TO_GEM :
8606 SND_AMOEBA_TURNING_TO_ROCK));
8613 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8616 int group_nr = AmoebaNr[ax][ay];
8617 boolean done = FALSE;
8622 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8623 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8628 SCAN_PLAYFIELD(x, y)
8630 if (AmoebaNr[x][y] == group_nr &&
8631 (Feld[x][y] == EL_AMOEBA_DEAD ||
8632 Feld[x][y] == EL_BD_AMOEBA ||
8633 Feld[x][y] == EL_AMOEBA_GROWING))
8636 Feld[x][y] = new_element;
8637 InitField(x, y, FALSE);
8638 TEST_DrawLevelField(x, y);
8644 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8645 SND_BD_AMOEBA_TURNING_TO_ROCK :
8646 SND_BD_AMOEBA_TURNING_TO_GEM));
8649 void AmoebeWaechst(int x, int y)
8651 static unsigned int sound_delay = 0;
8652 static unsigned int sound_delay_value = 0;
8654 if (!MovDelay[x][y]) /* start new growing cycle */
8658 if (DelayReached(&sound_delay, sound_delay_value))
8660 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8661 sound_delay_value = 30;
8665 if (MovDelay[x][y]) /* wait some time before growing bigger */
8668 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8670 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8671 6 - MovDelay[x][y]);
8673 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8676 if (!MovDelay[x][y])
8678 Feld[x][y] = Store[x][y];
8680 TEST_DrawLevelField(x, y);
8685 void AmoebaDisappearing(int x, int y)
8687 static unsigned int sound_delay = 0;
8688 static unsigned int sound_delay_value = 0;
8690 if (!MovDelay[x][y]) /* start new shrinking cycle */
8694 if (DelayReached(&sound_delay, sound_delay_value))
8695 sound_delay_value = 30;
8698 if (MovDelay[x][y]) /* wait some time before shrinking */
8701 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8703 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8704 6 - MovDelay[x][y]);
8706 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8709 if (!MovDelay[x][y])
8711 Feld[x][y] = EL_EMPTY;
8712 TEST_DrawLevelField(x, y);
8714 /* don't let mole enter this field in this cycle;
8715 (give priority to objects falling to this field from above) */
8721 void AmoebeAbleger(int ax, int ay)
8724 int element = Feld[ax][ay];
8725 int graphic = el2img(element);
8726 int newax = ax, neway = ay;
8727 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8728 static int xy[4][2] =
8736 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8738 Feld[ax][ay] = EL_AMOEBA_DEAD;
8739 TEST_DrawLevelField(ax, ay);
8743 if (IS_ANIMATED(graphic))
8744 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8746 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8747 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8749 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8752 if (MovDelay[ax][ay])
8756 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8759 int x = ax + xy[start][0];
8760 int y = ay + xy[start][1];
8762 if (!IN_LEV_FIELD(x, y))
8765 if (IS_FREE(x, y) ||
8766 CAN_GROW_INTO(Feld[x][y]) ||
8767 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8768 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8774 if (newax == ax && neway == ay)
8777 else /* normal or "filled" (BD style) amoeba */
8780 boolean waiting_for_player = FALSE;
8782 for (i = 0; i < NUM_DIRECTIONS; i++)
8784 int j = (start + i) % 4;
8785 int x = ax + xy[j][0];
8786 int y = ay + xy[j][1];
8788 if (!IN_LEV_FIELD(x, y))
8791 if (IS_FREE(x, y) ||
8792 CAN_GROW_INTO(Feld[x][y]) ||
8793 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8794 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8800 else if (IS_PLAYER(x, y))
8801 waiting_for_player = TRUE;
8804 if (newax == ax && neway == ay) /* amoeba cannot grow */
8806 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8808 Feld[ax][ay] = EL_AMOEBA_DEAD;
8809 TEST_DrawLevelField(ax, ay);
8810 AmoebaCnt[AmoebaNr[ax][ay]]--;
8812 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8814 if (element == EL_AMOEBA_FULL)
8815 AmoebeUmwandeln(ax, ay);
8816 else if (element == EL_BD_AMOEBA)
8817 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8822 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8824 /* amoeba gets larger by growing in some direction */
8826 int new_group_nr = AmoebaNr[ax][ay];
8829 if (new_group_nr == 0)
8831 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8832 printf("AmoebeAbleger(): This should never happen!\n");
8837 AmoebaNr[newax][neway] = new_group_nr;
8838 AmoebaCnt[new_group_nr]++;
8839 AmoebaCnt2[new_group_nr]++;
8841 /* if amoeba touches other amoeba(s) after growing, unify them */
8842 AmoebenVereinigen(newax, neway);
8844 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8846 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8852 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8853 (neway == lev_fieldy - 1 && newax != ax))
8855 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8856 Store[newax][neway] = element;
8858 else if (neway == ay || element == EL_EMC_DRIPPER)
8860 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8862 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8866 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8867 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8868 Store[ax][ay] = EL_AMOEBA_DROP;
8869 ContinueMoving(ax, ay);
8873 TEST_DrawLevelField(newax, neway);
8876 void Life(int ax, int ay)
8880 int element = Feld[ax][ay];
8881 int graphic = el2img(element);
8882 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8884 boolean changed = FALSE;
8886 if (IS_ANIMATED(graphic))
8887 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8892 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8893 MovDelay[ax][ay] = life_time;
8895 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8898 if (MovDelay[ax][ay])
8902 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8904 int xx = ax+x1, yy = ay+y1;
8907 if (!IN_LEV_FIELD(xx, yy))
8910 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8912 int x = xx+x2, y = yy+y2;
8914 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8917 if (((Feld[x][y] == element ||
8918 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8920 (IS_FREE(x, y) && Stop[x][y]))
8924 if (xx == ax && yy == ay) /* field in the middle */
8926 if (nachbarn < life_parameter[0] ||
8927 nachbarn > life_parameter[1])
8929 Feld[xx][yy] = EL_EMPTY;
8931 TEST_DrawLevelField(xx, yy);
8932 Stop[xx][yy] = TRUE;
8936 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8937 { /* free border field */
8938 if (nachbarn >= life_parameter[2] &&
8939 nachbarn <= life_parameter[3])
8941 Feld[xx][yy] = element;
8942 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8944 TEST_DrawLevelField(xx, yy);
8945 Stop[xx][yy] = TRUE;
8952 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8953 SND_GAME_OF_LIFE_GROWING);
8956 static void InitRobotWheel(int x, int y)
8958 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8961 static void RunRobotWheel(int x, int y)
8963 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8966 static void StopRobotWheel(int x, int y)
8968 if (ZX == x && ZY == y)
8972 game.robot_wheel_active = FALSE;
8976 static void InitTimegateWheel(int x, int y)
8978 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8981 static void RunTimegateWheel(int x, int y)
8983 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8986 static void InitMagicBallDelay(int x, int y)
8988 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8991 static void ActivateMagicBall(int bx, int by)
8995 if (level.ball_random)
8997 int pos_border = RND(8); /* select one of the eight border elements */
8998 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8999 int xx = pos_content % 3;
9000 int yy = pos_content / 3;
9005 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9006 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9010 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9012 int xx = x - bx + 1;
9013 int yy = y - by + 1;
9015 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9016 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9020 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9023 void CheckExit(int x, int y)
9025 if (local_player->gems_still_needed > 0 ||
9026 local_player->sokobanfields_still_needed > 0 ||
9027 local_player->lights_still_needed > 0)
9029 int element = Feld[x][y];
9030 int graphic = el2img(element);
9032 if (IS_ANIMATED(graphic))
9033 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9038 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9041 Feld[x][y] = EL_EXIT_OPENING;
9043 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9046 void CheckExitEM(int x, int y)
9048 if (local_player->gems_still_needed > 0 ||
9049 local_player->sokobanfields_still_needed > 0 ||
9050 local_player->lights_still_needed > 0)
9052 int element = Feld[x][y];
9053 int graphic = el2img(element);
9055 if (IS_ANIMATED(graphic))
9056 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9061 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9064 Feld[x][y] = EL_EM_EXIT_OPENING;
9066 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9069 void CheckExitSteel(int x, int y)
9071 if (local_player->gems_still_needed > 0 ||
9072 local_player->sokobanfields_still_needed > 0 ||
9073 local_player->lights_still_needed > 0)
9075 int element = Feld[x][y];
9076 int graphic = el2img(element);
9078 if (IS_ANIMATED(graphic))
9079 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9084 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9087 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9089 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9092 void CheckExitSteelEM(int x, int y)
9094 if (local_player->gems_still_needed > 0 ||
9095 local_player->sokobanfields_still_needed > 0 ||
9096 local_player->lights_still_needed > 0)
9098 int element = Feld[x][y];
9099 int graphic = el2img(element);
9101 if (IS_ANIMATED(graphic))
9102 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9107 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9110 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9112 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9115 void CheckExitSP(int x, int y)
9117 if (local_player->gems_still_needed > 0)
9119 int element = Feld[x][y];
9120 int graphic = el2img(element);
9122 if (IS_ANIMATED(graphic))
9123 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9128 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9131 Feld[x][y] = EL_SP_EXIT_OPENING;
9133 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9136 static void CloseAllOpenTimegates()
9140 SCAN_PLAYFIELD(x, y)
9142 int element = Feld[x][y];
9144 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9146 Feld[x][y] = EL_TIMEGATE_CLOSING;
9148 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9153 void DrawTwinkleOnField(int x, int y)
9155 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9158 if (Feld[x][y] == EL_BD_DIAMOND)
9161 if (MovDelay[x][y] == 0) /* next animation frame */
9162 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9164 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9168 DrawLevelElementAnimation(x, y, Feld[x][y]);
9170 if (MovDelay[x][y] != 0)
9172 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9173 10 - MovDelay[x][y]);
9175 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9180 void MauerWaechst(int x, int y)
9184 if (!MovDelay[x][y]) /* next animation frame */
9185 MovDelay[x][y] = 3 * delay;
9187 if (MovDelay[x][y]) /* wait some time before next frame */
9191 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9193 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9194 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9196 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9199 if (!MovDelay[x][y])
9201 if (MovDir[x][y] == MV_LEFT)
9203 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9204 TEST_DrawLevelField(x - 1, y);
9206 else if (MovDir[x][y] == MV_RIGHT)
9208 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9209 TEST_DrawLevelField(x + 1, y);
9211 else if (MovDir[x][y] == MV_UP)
9213 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9214 TEST_DrawLevelField(x, y - 1);
9218 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9219 TEST_DrawLevelField(x, y + 1);
9222 Feld[x][y] = Store[x][y];
9224 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9225 TEST_DrawLevelField(x, y);
9230 void MauerAbleger(int ax, int ay)
9232 int element = Feld[ax][ay];
9233 int graphic = el2img(element);
9234 boolean oben_frei = FALSE, unten_frei = FALSE;
9235 boolean links_frei = FALSE, rechts_frei = FALSE;
9236 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9237 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9238 boolean new_wall = FALSE;
9240 if (IS_ANIMATED(graphic))
9241 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9243 if (!MovDelay[ax][ay]) /* start building new wall */
9244 MovDelay[ax][ay] = 6;
9246 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9249 if (MovDelay[ax][ay])
9253 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9255 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9257 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9259 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9262 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9263 element == EL_EXPANDABLE_WALL_ANY)
9267 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9268 Store[ax][ay-1] = element;
9269 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9270 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9271 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9272 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9277 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9278 Store[ax][ay+1] = element;
9279 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9280 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9281 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9282 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9287 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9288 element == EL_EXPANDABLE_WALL_ANY ||
9289 element == EL_EXPANDABLE_WALL ||
9290 element == EL_BD_EXPANDABLE_WALL)
9294 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9295 Store[ax-1][ay] = element;
9296 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9297 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9298 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9299 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9305 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9306 Store[ax+1][ay] = element;
9307 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9308 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9309 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9310 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9315 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9316 TEST_DrawLevelField(ax, ay);
9318 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9320 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9321 unten_massiv = TRUE;
9322 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9323 links_massiv = TRUE;
9324 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9325 rechts_massiv = TRUE;
9327 if (((oben_massiv && unten_massiv) ||
9328 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9329 element == EL_EXPANDABLE_WALL) &&
9330 ((links_massiv && rechts_massiv) ||
9331 element == EL_EXPANDABLE_WALL_VERTICAL))
9332 Feld[ax][ay] = EL_WALL;
9335 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9338 void MauerAblegerStahl(int ax, int ay)
9340 int element = Feld[ax][ay];
9341 int graphic = el2img(element);
9342 boolean oben_frei = FALSE, unten_frei = FALSE;
9343 boolean links_frei = FALSE, rechts_frei = FALSE;
9344 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9345 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9346 boolean new_wall = FALSE;
9348 if (IS_ANIMATED(graphic))
9349 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9351 if (!MovDelay[ax][ay]) /* start building new wall */
9352 MovDelay[ax][ay] = 6;
9354 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9357 if (MovDelay[ax][ay])
9361 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9363 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9365 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9367 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9370 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9371 element == EL_EXPANDABLE_STEELWALL_ANY)
9375 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9376 Store[ax][ay-1] = element;
9377 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9378 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9379 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9380 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9385 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9386 Store[ax][ay+1] = element;
9387 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9388 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9389 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9390 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9395 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9396 element == EL_EXPANDABLE_STEELWALL_ANY)
9400 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9401 Store[ax-1][ay] = element;
9402 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9403 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9404 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9405 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9411 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9412 Store[ax+1][ay] = element;
9413 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9414 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9415 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9416 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9421 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9423 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9424 unten_massiv = TRUE;
9425 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9426 links_massiv = TRUE;
9427 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9428 rechts_massiv = TRUE;
9430 if (((oben_massiv && unten_massiv) ||
9431 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9432 ((links_massiv && rechts_massiv) ||
9433 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9434 Feld[ax][ay] = EL_STEELWALL;
9437 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9440 void CheckForDragon(int x, int y)
9443 boolean dragon_found = FALSE;
9444 static int xy[4][2] =
9452 for (i = 0; i < NUM_DIRECTIONS; i++)
9454 for (j = 0; j < 4; j++)
9456 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9458 if (IN_LEV_FIELD(xx, yy) &&
9459 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9461 if (Feld[xx][yy] == EL_DRAGON)
9462 dragon_found = TRUE;
9471 for (i = 0; i < NUM_DIRECTIONS; i++)
9473 for (j = 0; j < 3; j++)
9475 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9477 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9479 Feld[xx][yy] = EL_EMPTY;
9480 TEST_DrawLevelField(xx, yy);
9489 static void InitBuggyBase(int x, int y)
9491 int element = Feld[x][y];
9492 int activating_delay = FRAMES_PER_SECOND / 4;
9495 (element == EL_SP_BUGGY_BASE ?
9496 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9497 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9499 element == EL_SP_BUGGY_BASE_ACTIVE ?
9500 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9503 static void WarnBuggyBase(int x, int y)
9506 static int xy[4][2] =
9514 for (i = 0; i < NUM_DIRECTIONS; i++)
9516 int xx = x + xy[i][0];
9517 int yy = y + xy[i][1];
9519 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9521 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9528 static void InitTrap(int x, int y)
9530 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9533 static void ActivateTrap(int x, int y)
9535 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9538 static void ChangeActiveTrap(int x, int y)
9540 int graphic = IMG_TRAP_ACTIVE;
9542 /* if new animation frame was drawn, correct crumbled sand border */
9543 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9544 TEST_DrawLevelFieldCrumbled(x, y);
9547 static int getSpecialActionElement(int element, int number, int base_element)
9549 return (element != EL_EMPTY ? element :
9550 number != -1 ? base_element + number - 1 :
9554 static int getModifiedActionNumber(int value_old, int operator, int operand,
9555 int value_min, int value_max)
9557 int value_new = (operator == CA_MODE_SET ? operand :
9558 operator == CA_MODE_ADD ? value_old + operand :
9559 operator == CA_MODE_SUBTRACT ? value_old - operand :
9560 operator == CA_MODE_MULTIPLY ? value_old * operand :
9561 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9562 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9565 return (value_new < value_min ? value_min :
9566 value_new > value_max ? value_max :
9570 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9572 struct ElementInfo *ei = &element_info[element];
9573 struct ElementChangeInfo *change = &ei->change_page[page];
9574 int target_element = change->target_element;
9575 int action_type = change->action_type;
9576 int action_mode = change->action_mode;
9577 int action_arg = change->action_arg;
9578 int action_element = change->action_element;
9581 if (!change->has_action)
9584 /* ---------- determine action paramater values -------------------------- */
9586 int level_time_value =
9587 (level.time > 0 ? TimeLeft :
9590 int action_arg_element_raw =
9591 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9592 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9593 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9594 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9595 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9596 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9597 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9599 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9601 int action_arg_direction =
9602 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9603 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9604 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9605 change->actual_trigger_side :
9606 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9607 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9610 int action_arg_number_min =
9611 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9614 int action_arg_number_max =
9615 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9616 action_type == CA_SET_LEVEL_GEMS ? 999 :
9617 action_type == CA_SET_LEVEL_TIME ? 9999 :
9618 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9619 action_type == CA_SET_CE_VALUE ? 9999 :
9620 action_type == CA_SET_CE_SCORE ? 9999 :
9623 int action_arg_number_reset =
9624 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9625 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9626 action_type == CA_SET_LEVEL_TIME ? level.time :
9627 action_type == CA_SET_LEVEL_SCORE ? 0 :
9628 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9629 action_type == CA_SET_CE_SCORE ? 0 :
9632 int action_arg_number =
9633 (action_arg <= CA_ARG_MAX ? action_arg :
9634 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9635 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9636 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9637 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9638 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9639 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9640 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9641 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9642 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9643 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9644 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9645 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9646 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9647 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9648 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9649 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9650 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9651 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9652 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9653 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9654 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9657 int action_arg_number_old =
9658 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9659 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9660 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9661 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9662 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9665 int action_arg_number_new =
9666 getModifiedActionNumber(action_arg_number_old,
9667 action_mode, action_arg_number,
9668 action_arg_number_min, action_arg_number_max);
9670 int trigger_player_bits =
9671 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9672 change->actual_trigger_player_bits : change->trigger_player);
9674 int action_arg_player_bits =
9675 (action_arg >= CA_ARG_PLAYER_1 &&
9676 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9677 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9678 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9681 /* ---------- execute action -------------------------------------------- */
9683 switch (action_type)
9690 /* ---------- level actions ------------------------------------------- */
9692 case CA_RESTART_LEVEL:
9694 game.restart_level = TRUE;
9699 case CA_SHOW_ENVELOPE:
9701 int element = getSpecialActionElement(action_arg_element,
9702 action_arg_number, EL_ENVELOPE_1);
9704 if (IS_ENVELOPE(element))
9705 local_player->show_envelope = element;
9710 case CA_SET_LEVEL_TIME:
9712 if (level.time > 0) /* only modify limited time value */
9714 TimeLeft = action_arg_number_new;
9716 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9718 DisplayGameControlValues();
9720 if (!TimeLeft && setup.time_limit)
9721 for (i = 0; i < MAX_PLAYERS; i++)
9722 KillPlayer(&stored_player[i]);
9728 case CA_SET_LEVEL_SCORE:
9730 local_player->score = action_arg_number_new;
9732 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9734 DisplayGameControlValues();
9739 case CA_SET_LEVEL_GEMS:
9741 local_player->gems_still_needed = action_arg_number_new;
9743 game.snapshot.collected_item = TRUE;
9745 game_panel_controls[GAME_PANEL_GEMS].value =
9746 local_player->gems_still_needed;
9748 DisplayGameControlValues();
9753 case CA_SET_LEVEL_WIND:
9755 game.wind_direction = action_arg_direction;
9760 case CA_SET_LEVEL_RANDOM_SEED:
9762 /* ensure that setting a new random seed while playing is predictable */
9763 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9768 /* ---------- player actions ------------------------------------------ */
9770 case CA_MOVE_PLAYER:
9772 /* automatically move to the next field in specified direction */
9773 for (i = 0; i < MAX_PLAYERS; i++)
9774 if (trigger_player_bits & (1 << i))
9775 stored_player[i].programmed_action = action_arg_direction;
9780 case CA_EXIT_PLAYER:
9782 for (i = 0; i < MAX_PLAYERS; i++)
9783 if (action_arg_player_bits & (1 << i))
9784 ExitPlayer(&stored_player[i]);
9787 PlayerWins(local_player);
9792 case CA_KILL_PLAYER:
9794 for (i = 0; i < MAX_PLAYERS; i++)
9795 if (action_arg_player_bits & (1 << i))
9796 KillPlayer(&stored_player[i]);
9801 case CA_SET_PLAYER_KEYS:
9803 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9804 int element = getSpecialActionElement(action_arg_element,
9805 action_arg_number, EL_KEY_1);
9807 if (IS_KEY(element))
9809 for (i = 0; i < MAX_PLAYERS; i++)
9811 if (trigger_player_bits & (1 << i))
9813 stored_player[i].key[KEY_NR(element)] = key_state;
9815 DrawGameDoorValues();
9823 case CA_SET_PLAYER_SPEED:
9825 for (i = 0; i < MAX_PLAYERS; i++)
9827 if (trigger_player_bits & (1 << i))
9829 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9831 if (action_arg == CA_ARG_SPEED_FASTER &&
9832 stored_player[i].cannot_move)
9834 action_arg_number = STEPSIZE_VERY_SLOW;
9836 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9837 action_arg == CA_ARG_SPEED_FASTER)
9839 action_arg_number = 2;
9840 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9843 else if (action_arg == CA_ARG_NUMBER_RESET)
9845 action_arg_number = level.initial_player_stepsize[i];
9849 getModifiedActionNumber(move_stepsize,
9852 action_arg_number_min,
9853 action_arg_number_max);
9855 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9862 case CA_SET_PLAYER_SHIELD:
9864 for (i = 0; i < MAX_PLAYERS; i++)
9866 if (trigger_player_bits & (1 << i))
9868 if (action_arg == CA_ARG_SHIELD_OFF)
9870 stored_player[i].shield_normal_time_left = 0;
9871 stored_player[i].shield_deadly_time_left = 0;
9873 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9875 stored_player[i].shield_normal_time_left = 999999;
9877 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9879 stored_player[i].shield_normal_time_left = 999999;
9880 stored_player[i].shield_deadly_time_left = 999999;
9888 case CA_SET_PLAYER_GRAVITY:
9890 for (i = 0; i < MAX_PLAYERS; i++)
9892 if (trigger_player_bits & (1 << i))
9894 stored_player[i].gravity =
9895 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9896 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9897 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9898 stored_player[i].gravity);
9905 case CA_SET_PLAYER_ARTWORK:
9907 for (i = 0; i < MAX_PLAYERS; i++)
9909 if (trigger_player_bits & (1 << i))
9911 int artwork_element = action_arg_element;
9913 if (action_arg == CA_ARG_ELEMENT_RESET)
9915 (level.use_artwork_element[i] ? level.artwork_element[i] :
9916 stored_player[i].element_nr);
9918 if (stored_player[i].artwork_element != artwork_element)
9919 stored_player[i].Frame = 0;
9921 stored_player[i].artwork_element = artwork_element;
9923 SetPlayerWaiting(&stored_player[i], FALSE);
9925 /* set number of special actions for bored and sleeping animation */
9926 stored_player[i].num_special_action_bored =
9927 get_num_special_action(artwork_element,
9928 ACTION_BORING_1, ACTION_BORING_LAST);
9929 stored_player[i].num_special_action_sleeping =
9930 get_num_special_action(artwork_element,
9931 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9938 case CA_SET_PLAYER_INVENTORY:
9940 for (i = 0; i < MAX_PLAYERS; i++)
9942 struct PlayerInfo *player = &stored_player[i];
9945 if (trigger_player_bits & (1 << i))
9947 int inventory_element = action_arg_element;
9949 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9950 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9951 action_arg == CA_ARG_ELEMENT_ACTION)
9953 int element = inventory_element;
9954 int collect_count = element_info[element].collect_count_initial;
9956 if (!IS_CUSTOM_ELEMENT(element))
9959 if (collect_count == 0)
9960 player->inventory_infinite_element = element;
9962 for (k = 0; k < collect_count; k++)
9963 if (player->inventory_size < MAX_INVENTORY_SIZE)
9964 player->inventory_element[player->inventory_size++] =
9967 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9968 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9969 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9971 if (player->inventory_infinite_element != EL_UNDEFINED &&
9972 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9973 action_arg_element_raw))
9974 player->inventory_infinite_element = EL_UNDEFINED;
9976 for (k = 0, j = 0; j < player->inventory_size; j++)
9978 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9979 action_arg_element_raw))
9980 player->inventory_element[k++] = player->inventory_element[j];
9983 player->inventory_size = k;
9985 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9987 if (player->inventory_size > 0)
9989 for (j = 0; j < player->inventory_size - 1; j++)
9990 player->inventory_element[j] = player->inventory_element[j + 1];
9992 player->inventory_size--;
9995 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9997 if (player->inventory_size > 0)
9998 player->inventory_size--;
10000 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10002 player->inventory_infinite_element = EL_UNDEFINED;
10003 player->inventory_size = 0;
10005 else if (action_arg == CA_ARG_INVENTORY_RESET)
10007 player->inventory_infinite_element = EL_UNDEFINED;
10008 player->inventory_size = 0;
10010 if (level.use_initial_inventory[i])
10012 for (j = 0; j < level.initial_inventory_size[i]; j++)
10014 int element = level.initial_inventory_content[i][j];
10015 int collect_count = element_info[element].collect_count_initial;
10017 if (!IS_CUSTOM_ELEMENT(element))
10020 if (collect_count == 0)
10021 player->inventory_infinite_element = element;
10023 for (k = 0; k < collect_count; k++)
10024 if (player->inventory_size < MAX_INVENTORY_SIZE)
10025 player->inventory_element[player->inventory_size++] =
10036 /* ---------- CE actions ---------------------------------------------- */
10038 case CA_SET_CE_VALUE:
10040 int last_ce_value = CustomValue[x][y];
10042 CustomValue[x][y] = action_arg_number_new;
10044 if (CustomValue[x][y] != last_ce_value)
10046 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10047 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10049 if (CustomValue[x][y] == 0)
10051 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10052 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10059 case CA_SET_CE_SCORE:
10061 int last_ce_score = ei->collect_score;
10063 ei->collect_score = action_arg_number_new;
10065 if (ei->collect_score != last_ce_score)
10067 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10068 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10070 if (ei->collect_score == 0)
10074 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10075 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10078 This is a very special case that seems to be a mixture between
10079 CheckElementChange() and CheckTriggeredElementChange(): while
10080 the first one only affects single elements that are triggered
10081 directly, the second one affects multiple elements in the playfield
10082 that are triggered indirectly by another element. This is a third
10083 case: Changing the CE score always affects multiple identical CEs,
10084 so every affected CE must be checked, not only the single CE for
10085 which the CE score was changed in the first place (as every instance
10086 of that CE shares the same CE score, and therefore also can change)!
10088 SCAN_PLAYFIELD(xx, yy)
10090 if (Feld[xx][yy] == element)
10091 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10092 CE_SCORE_GETS_ZERO);
10100 case CA_SET_CE_ARTWORK:
10102 int artwork_element = action_arg_element;
10103 boolean reset_frame = FALSE;
10106 if (action_arg == CA_ARG_ELEMENT_RESET)
10107 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10110 if (ei->gfx_element != artwork_element)
10111 reset_frame = TRUE;
10113 ei->gfx_element = artwork_element;
10115 SCAN_PLAYFIELD(xx, yy)
10117 if (Feld[xx][yy] == element)
10121 ResetGfxAnimation(xx, yy);
10122 ResetRandomAnimationValue(xx, yy);
10125 TEST_DrawLevelField(xx, yy);
10132 /* ---------- engine actions ------------------------------------------ */
10134 case CA_SET_ENGINE_SCAN_MODE:
10136 InitPlayfieldScanMode(action_arg);
10146 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10148 int old_element = Feld[x][y];
10149 int new_element = GetElementFromGroupElement(element);
10150 int previous_move_direction = MovDir[x][y];
10151 int last_ce_value = CustomValue[x][y];
10152 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10153 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10154 boolean add_player_onto_element = (new_element_is_player &&
10155 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10156 IS_WALKABLE(old_element));
10158 if (!add_player_onto_element)
10160 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10161 RemoveMovingField(x, y);
10165 Feld[x][y] = new_element;
10167 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10168 MovDir[x][y] = previous_move_direction;
10170 if (element_info[new_element].use_last_ce_value)
10171 CustomValue[x][y] = last_ce_value;
10173 InitField_WithBug1(x, y, FALSE);
10175 new_element = Feld[x][y]; /* element may have changed */
10177 ResetGfxAnimation(x, y);
10178 ResetRandomAnimationValue(x, y);
10180 TEST_DrawLevelField(x, y);
10182 if (GFX_CRUMBLED(new_element))
10183 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10186 /* check if element under the player changes from accessible to unaccessible
10187 (needed for special case of dropping element which then changes) */
10188 /* (must be checked after creating new element for walkable group elements) */
10189 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10190 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10197 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10198 if (new_element_is_player)
10199 RelocatePlayer(x, y, new_element);
10202 ChangeCount[x][y]++; /* count number of changes in the same frame */
10204 TestIfBadThingTouchesPlayer(x, y);
10205 TestIfPlayerTouchesCustomElement(x, y);
10206 TestIfElementTouchesCustomElement(x, y);
10209 static void CreateField(int x, int y, int element)
10211 CreateFieldExt(x, y, element, FALSE);
10214 static void CreateElementFromChange(int x, int y, int element)
10216 element = GET_VALID_RUNTIME_ELEMENT(element);
10218 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10220 int old_element = Feld[x][y];
10222 /* prevent changed element from moving in same engine frame
10223 unless both old and new element can either fall or move */
10224 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10225 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10229 CreateFieldExt(x, y, element, TRUE);
10232 static boolean ChangeElement(int x, int y, int element, int page)
10234 struct ElementInfo *ei = &element_info[element];
10235 struct ElementChangeInfo *change = &ei->change_page[page];
10236 int ce_value = CustomValue[x][y];
10237 int ce_score = ei->collect_score;
10238 int target_element;
10239 int old_element = Feld[x][y];
10241 /* always use default change event to prevent running into a loop */
10242 if (ChangeEvent[x][y] == -1)
10243 ChangeEvent[x][y] = CE_DELAY;
10245 if (ChangeEvent[x][y] == CE_DELAY)
10247 /* reset actual trigger element, trigger player and action element */
10248 change->actual_trigger_element = EL_EMPTY;
10249 change->actual_trigger_player = EL_EMPTY;
10250 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10251 change->actual_trigger_side = CH_SIDE_NONE;
10252 change->actual_trigger_ce_value = 0;
10253 change->actual_trigger_ce_score = 0;
10256 /* do not change elements more than a specified maximum number of changes */
10257 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10260 ChangeCount[x][y]++; /* count number of changes in the same frame */
10262 if (change->explode)
10269 if (change->use_target_content)
10271 boolean complete_replace = TRUE;
10272 boolean can_replace[3][3];
10275 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10278 boolean is_walkable;
10279 boolean is_diggable;
10280 boolean is_collectible;
10281 boolean is_removable;
10282 boolean is_destructible;
10283 int ex = x + xx - 1;
10284 int ey = y + yy - 1;
10285 int content_element = change->target_content.e[xx][yy];
10288 can_replace[xx][yy] = TRUE;
10290 if (ex == x && ey == y) /* do not check changing element itself */
10293 if (content_element == EL_EMPTY_SPACE)
10295 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10300 if (!IN_LEV_FIELD(ex, ey))
10302 can_replace[xx][yy] = FALSE;
10303 complete_replace = FALSE;
10310 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10311 e = MovingOrBlocked2Element(ex, ey);
10313 is_empty = (IS_FREE(ex, ey) ||
10314 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10316 is_walkable = (is_empty || IS_WALKABLE(e));
10317 is_diggable = (is_empty || IS_DIGGABLE(e));
10318 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10319 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10320 is_removable = (is_diggable || is_collectible);
10322 can_replace[xx][yy] =
10323 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10324 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10325 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10326 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10327 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10328 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10329 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10331 if (!can_replace[xx][yy])
10332 complete_replace = FALSE;
10335 if (!change->only_if_complete || complete_replace)
10337 boolean something_has_changed = FALSE;
10339 if (change->only_if_complete && change->use_random_replace &&
10340 RND(100) < change->random_percentage)
10343 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10345 int ex = x + xx - 1;
10346 int ey = y + yy - 1;
10347 int content_element;
10349 if (can_replace[xx][yy] && (!change->use_random_replace ||
10350 RND(100) < change->random_percentage))
10352 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10353 RemoveMovingField(ex, ey);
10355 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10357 content_element = change->target_content.e[xx][yy];
10358 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10359 ce_value, ce_score);
10361 CreateElementFromChange(ex, ey, target_element);
10363 something_has_changed = TRUE;
10365 /* for symmetry reasons, freeze newly created border elements */
10366 if (ex != x || ey != y)
10367 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10371 if (something_has_changed)
10373 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10374 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10380 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10381 ce_value, ce_score);
10383 if (element == EL_DIAGONAL_GROWING ||
10384 element == EL_DIAGONAL_SHRINKING)
10386 target_element = Store[x][y];
10388 Store[x][y] = EL_EMPTY;
10391 CreateElementFromChange(x, y, target_element);
10393 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10394 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10397 /* this uses direct change before indirect change */
10398 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10403 static void HandleElementChange(int x, int y, int page)
10405 int element = MovingOrBlocked2Element(x, y);
10406 struct ElementInfo *ei = &element_info[element];
10407 struct ElementChangeInfo *change = &ei->change_page[page];
10408 boolean handle_action_before_change = FALSE;
10411 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10412 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10415 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10416 x, y, element, element_info[element].token_name);
10417 printf("HandleElementChange(): This should never happen!\n");
10422 /* this can happen with classic bombs on walkable, changing elements */
10423 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10428 if (ChangeDelay[x][y] == 0) /* initialize element change */
10430 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10432 if (change->can_change)
10434 /* !!! not clear why graphic animation should be reset at all here !!! */
10435 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10436 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10439 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10441 When using an animation frame delay of 1 (this only happens with
10442 "sp_zonk.moving.left/right" in the classic graphics), the default
10443 (non-moving) animation shows wrong animation frames (while the
10444 moving animation, like "sp_zonk.moving.left/right", is correct,
10445 so this graphical bug never shows up with the classic graphics).
10446 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10447 be drawn instead of the correct frames 0,1,2,3. This is caused by
10448 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10449 an element change: First when the change delay ("ChangeDelay[][]")
10450 counter has reached zero after decrementing, then a second time in
10451 the next frame (after "GfxFrame[][]" was already incremented) when
10452 "ChangeDelay[][]" is reset to the initial delay value again.
10454 This causes frame 0 to be drawn twice, while the last frame won't
10455 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10457 As some animations may already be cleverly designed around this bug
10458 (at least the "Snake Bite" snake tail animation does this), it cannot
10459 simply be fixed here without breaking such existing animations.
10460 Unfortunately, it cannot easily be detected if a graphics set was
10461 designed "before" or "after" the bug was fixed. As a workaround,
10462 a new graphics set option "game.graphics_engine_version" was added
10463 to be able to specify the game's major release version for which the
10464 graphics set was designed, which can then be used to decide if the
10465 bugfix should be used (version 4 and above) or not (version 3 or
10466 below, or if no version was specified at all, as with old sets).
10468 (The wrong/fixed animation frames can be tested with the test level set
10469 "test_gfxframe" and level "000", which contains a specially prepared
10470 custom element at level position (x/y) == (11/9) which uses the zonk
10471 animation mentioned above. Using "game.graphics_engine_version: 4"
10472 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10473 This can also be seen from the debug output for this test element.)
10476 /* when a custom element is about to change (for example by change delay),
10477 do not reset graphic animation when the custom element is moving */
10478 if (game.graphics_engine_version < 4 &&
10481 ResetGfxAnimation(x, y);
10482 ResetRandomAnimationValue(x, y);
10485 if (change->pre_change_function)
10486 change->pre_change_function(x, y);
10490 ChangeDelay[x][y]--;
10492 if (ChangeDelay[x][y] != 0) /* continue element change */
10494 if (change->can_change)
10496 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10498 if (IS_ANIMATED(graphic))
10499 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10501 if (change->change_function)
10502 change->change_function(x, y);
10505 else /* finish element change */
10507 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10509 page = ChangePage[x][y];
10510 ChangePage[x][y] = -1;
10512 change = &ei->change_page[page];
10515 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10517 ChangeDelay[x][y] = 1; /* try change after next move step */
10518 ChangePage[x][y] = page; /* remember page to use for change */
10523 /* special case: set new level random seed before changing element */
10524 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10525 handle_action_before_change = TRUE;
10527 if (change->has_action && handle_action_before_change)
10528 ExecuteCustomElementAction(x, y, element, page);
10530 if (change->can_change)
10532 if (ChangeElement(x, y, element, page))
10534 if (change->post_change_function)
10535 change->post_change_function(x, y);
10539 if (change->has_action && !handle_action_before_change)
10540 ExecuteCustomElementAction(x, y, element, page);
10544 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10545 int trigger_element,
10547 int trigger_player,
10551 boolean change_done_any = FALSE;
10552 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10555 if (!(trigger_events[trigger_element][trigger_event]))
10558 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10560 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10562 int element = EL_CUSTOM_START + i;
10563 boolean change_done = FALSE;
10566 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10567 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10570 for (p = 0; p < element_info[element].num_change_pages; p++)
10572 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10574 if (change->can_change_or_has_action &&
10575 change->has_event[trigger_event] &&
10576 change->trigger_side & trigger_side &&
10577 change->trigger_player & trigger_player &&
10578 change->trigger_page & trigger_page_bits &&
10579 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10581 change->actual_trigger_element = trigger_element;
10582 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10583 change->actual_trigger_player_bits = trigger_player;
10584 change->actual_trigger_side = trigger_side;
10585 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10586 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10588 if ((change->can_change && !change_done) || change->has_action)
10592 SCAN_PLAYFIELD(x, y)
10594 if (Feld[x][y] == element)
10596 if (change->can_change && !change_done)
10598 /* if element already changed in this frame, not only prevent
10599 another element change (checked in ChangeElement()), but
10600 also prevent additional element actions for this element */
10602 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10603 !level.use_action_after_change_bug)
10606 ChangeDelay[x][y] = 1;
10607 ChangeEvent[x][y] = trigger_event;
10609 HandleElementChange(x, y, p);
10611 else if (change->has_action)
10613 /* if element already changed in this frame, not only prevent
10614 another element change (checked in ChangeElement()), but
10615 also prevent additional element actions for this element */
10617 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10618 !level.use_action_after_change_bug)
10621 ExecuteCustomElementAction(x, y, element, p);
10622 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10627 if (change->can_change)
10629 change_done = TRUE;
10630 change_done_any = TRUE;
10637 RECURSION_LOOP_DETECTION_END();
10639 return change_done_any;
10642 static boolean CheckElementChangeExt(int x, int y,
10644 int trigger_element,
10646 int trigger_player,
10649 boolean change_done = FALSE;
10652 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10653 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10656 if (Feld[x][y] == EL_BLOCKED)
10658 Blocked2Moving(x, y, &x, &y);
10659 element = Feld[x][y];
10662 /* check if element has already changed or is about to change after moving */
10663 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10664 Feld[x][y] != element) ||
10666 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10667 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10668 ChangePage[x][y] != -1)))
10671 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10673 for (p = 0; p < element_info[element].num_change_pages; p++)
10675 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10677 /* check trigger element for all events where the element that is checked
10678 for changing interacts with a directly adjacent element -- this is
10679 different to element changes that affect other elements to change on the
10680 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10681 boolean check_trigger_element =
10682 (trigger_event == CE_TOUCHING_X ||
10683 trigger_event == CE_HITTING_X ||
10684 trigger_event == CE_HIT_BY_X ||
10685 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10687 if (change->can_change_or_has_action &&
10688 change->has_event[trigger_event] &&
10689 change->trigger_side & trigger_side &&
10690 change->trigger_player & trigger_player &&
10691 (!check_trigger_element ||
10692 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10694 change->actual_trigger_element = trigger_element;
10695 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10696 change->actual_trigger_player_bits = trigger_player;
10697 change->actual_trigger_side = trigger_side;
10698 change->actual_trigger_ce_value = CustomValue[x][y];
10699 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10701 /* special case: trigger element not at (x,y) position for some events */
10702 if (check_trigger_element)
10714 { 0, 0 }, { 0, 0 }, { 0, 0 },
10718 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10719 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10721 change->actual_trigger_ce_value = CustomValue[xx][yy];
10722 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10725 if (change->can_change && !change_done)
10727 ChangeDelay[x][y] = 1;
10728 ChangeEvent[x][y] = trigger_event;
10730 HandleElementChange(x, y, p);
10732 change_done = TRUE;
10734 else if (change->has_action)
10736 ExecuteCustomElementAction(x, y, element, p);
10737 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10742 RECURSION_LOOP_DETECTION_END();
10744 return change_done;
10747 static void PlayPlayerSound(struct PlayerInfo *player)
10749 int jx = player->jx, jy = player->jy;
10750 int sound_element = player->artwork_element;
10751 int last_action = player->last_action_waiting;
10752 int action = player->action_waiting;
10754 if (player->is_waiting)
10756 if (action != last_action)
10757 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10759 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10763 if (action != last_action)
10764 StopSound(element_info[sound_element].sound[last_action]);
10766 if (last_action == ACTION_SLEEPING)
10767 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10771 static void PlayAllPlayersSound()
10775 for (i = 0; i < MAX_PLAYERS; i++)
10776 if (stored_player[i].active)
10777 PlayPlayerSound(&stored_player[i]);
10780 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10782 boolean last_waiting = player->is_waiting;
10783 int move_dir = player->MovDir;
10785 player->dir_waiting = move_dir;
10786 player->last_action_waiting = player->action_waiting;
10790 if (!last_waiting) /* not waiting -> waiting */
10792 player->is_waiting = TRUE;
10794 player->frame_counter_bored =
10796 game.player_boring_delay_fixed +
10797 GetSimpleRandom(game.player_boring_delay_random);
10798 player->frame_counter_sleeping =
10800 game.player_sleeping_delay_fixed +
10801 GetSimpleRandom(game.player_sleeping_delay_random);
10803 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10806 if (game.player_sleeping_delay_fixed +
10807 game.player_sleeping_delay_random > 0 &&
10808 player->anim_delay_counter == 0 &&
10809 player->post_delay_counter == 0 &&
10810 FrameCounter >= player->frame_counter_sleeping)
10811 player->is_sleeping = TRUE;
10812 else if (game.player_boring_delay_fixed +
10813 game.player_boring_delay_random > 0 &&
10814 FrameCounter >= player->frame_counter_bored)
10815 player->is_bored = TRUE;
10817 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10818 player->is_bored ? ACTION_BORING :
10821 if (player->is_sleeping && player->use_murphy)
10823 /* special case for sleeping Murphy when leaning against non-free tile */
10825 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10826 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10827 !IS_MOVING(player->jx - 1, player->jy)))
10828 move_dir = MV_LEFT;
10829 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10830 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10831 !IS_MOVING(player->jx + 1, player->jy)))
10832 move_dir = MV_RIGHT;
10834 player->is_sleeping = FALSE;
10836 player->dir_waiting = move_dir;
10839 if (player->is_sleeping)
10841 if (player->num_special_action_sleeping > 0)
10843 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10845 int last_special_action = player->special_action_sleeping;
10846 int num_special_action = player->num_special_action_sleeping;
10847 int special_action =
10848 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10849 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10850 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10851 last_special_action + 1 : ACTION_SLEEPING);
10852 int special_graphic =
10853 el_act_dir2img(player->artwork_element, special_action, move_dir);
10855 player->anim_delay_counter =
10856 graphic_info[special_graphic].anim_delay_fixed +
10857 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10858 player->post_delay_counter =
10859 graphic_info[special_graphic].post_delay_fixed +
10860 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10862 player->special_action_sleeping = special_action;
10865 if (player->anim_delay_counter > 0)
10867 player->action_waiting = player->special_action_sleeping;
10868 player->anim_delay_counter--;
10870 else if (player->post_delay_counter > 0)
10872 player->post_delay_counter--;
10876 else if (player->is_bored)
10878 if (player->num_special_action_bored > 0)
10880 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10882 int special_action =
10883 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10884 int special_graphic =
10885 el_act_dir2img(player->artwork_element, special_action, move_dir);
10887 player->anim_delay_counter =
10888 graphic_info[special_graphic].anim_delay_fixed +
10889 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10890 player->post_delay_counter =
10891 graphic_info[special_graphic].post_delay_fixed +
10892 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10894 player->special_action_bored = special_action;
10897 if (player->anim_delay_counter > 0)
10899 player->action_waiting = player->special_action_bored;
10900 player->anim_delay_counter--;
10902 else if (player->post_delay_counter > 0)
10904 player->post_delay_counter--;
10909 else if (last_waiting) /* waiting -> not waiting */
10911 player->is_waiting = FALSE;
10912 player->is_bored = FALSE;
10913 player->is_sleeping = FALSE;
10915 player->frame_counter_bored = -1;
10916 player->frame_counter_sleeping = -1;
10918 player->anim_delay_counter = 0;
10919 player->post_delay_counter = 0;
10921 player->dir_waiting = player->MovDir;
10922 player->action_waiting = ACTION_DEFAULT;
10924 player->special_action_bored = ACTION_DEFAULT;
10925 player->special_action_sleeping = ACTION_DEFAULT;
10929 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10931 if ((!player->is_moving && player->was_moving) ||
10932 (player->MovPos == 0 && player->was_moving) ||
10933 (player->is_snapping && !player->was_snapping) ||
10934 (player->is_dropping && !player->was_dropping))
10936 if (!CheckSaveEngineSnapshotToList())
10939 player->was_moving = FALSE;
10940 player->was_snapping = TRUE;
10941 player->was_dropping = TRUE;
10945 if (player->is_moving)
10946 player->was_moving = TRUE;
10948 if (!player->is_snapping)
10949 player->was_snapping = FALSE;
10951 if (!player->is_dropping)
10952 player->was_dropping = FALSE;
10956 static void CheckSingleStepMode(struct PlayerInfo *player)
10958 if (tape.single_step && tape.recording && !tape.pausing)
10960 /* as it is called "single step mode", just return to pause mode when the
10961 player stopped moving after one tile (or never starts moving at all) */
10962 if (!player->is_moving &&
10963 !player->is_pushing &&
10964 !player->is_dropping_pressed)
10966 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10967 SnapField(player, 0, 0); /* stop snapping */
10971 CheckSaveEngineSnapshot(player);
10974 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10976 int left = player_action & JOY_LEFT;
10977 int right = player_action & JOY_RIGHT;
10978 int up = player_action & JOY_UP;
10979 int down = player_action & JOY_DOWN;
10980 int button1 = player_action & JOY_BUTTON_1;
10981 int button2 = player_action & JOY_BUTTON_2;
10982 int dx = (left ? -1 : right ? 1 : 0);
10983 int dy = (up ? -1 : down ? 1 : 0);
10985 if (!player->active || tape.pausing)
10991 SnapField(player, dx, dy);
10995 DropElement(player);
10997 MovePlayer(player, dx, dy);
11000 CheckSingleStepMode(player);
11002 SetPlayerWaiting(player, FALSE);
11004 return player_action;
11008 /* no actions for this player (no input at player's configured device) */
11010 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11011 SnapField(player, 0, 0);
11012 CheckGravityMovementWhenNotMoving(player);
11014 if (player->MovPos == 0)
11015 SetPlayerWaiting(player, TRUE);
11017 if (player->MovPos == 0) /* needed for tape.playing */
11018 player->is_moving = FALSE;
11020 player->is_dropping = FALSE;
11021 player->is_dropping_pressed = FALSE;
11022 player->drop_pressed_delay = 0;
11024 CheckSingleStepMode(player);
11030 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11033 if (!tape.use_mouse)
11036 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11037 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11038 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11041 static void SetTapeActionFromMouseAction(byte *tape_action,
11042 struct MouseActionInfo *mouse_action)
11044 if (!tape.use_mouse)
11047 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11048 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11049 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11052 static void CheckLevelTime()
11056 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11057 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11059 if (level.native_em_level->lev->home == 0) /* all players at home */
11061 PlayerWins(local_player);
11063 AllPlayersGone = TRUE;
11065 level.native_em_level->lev->home = -1;
11068 if (level.native_em_level->ply[0]->alive == 0 &&
11069 level.native_em_level->ply[1]->alive == 0 &&
11070 level.native_em_level->ply[2]->alive == 0 &&
11071 level.native_em_level->ply[3]->alive == 0) /* all dead */
11072 AllPlayersGone = TRUE;
11074 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11076 if (game_sp.LevelSolved &&
11077 !game_sp.GameOver) /* game won */
11079 PlayerWins(local_player);
11081 game_sp.GameOver = TRUE;
11083 AllPlayersGone = TRUE;
11086 if (game_sp.GameOver) /* game lost */
11087 AllPlayersGone = TRUE;
11089 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11091 if (game_mm.level_solved &&
11092 !game_mm.game_over) /* game won */
11094 PlayerWins(local_player);
11096 game_mm.game_over = TRUE;
11098 AllPlayersGone = TRUE;
11101 if (game_mm.game_over) /* game lost */
11102 AllPlayersGone = TRUE;
11105 if (TimeFrames >= FRAMES_PER_SECOND)
11110 for (i = 0; i < MAX_PLAYERS; i++)
11112 struct PlayerInfo *player = &stored_player[i];
11114 if (SHIELD_ON(player))
11116 player->shield_normal_time_left--;
11118 if (player->shield_deadly_time_left > 0)
11119 player->shield_deadly_time_left--;
11123 if (!local_player->LevelSolved && !level.use_step_counter)
11131 if (TimeLeft <= 10 && setup.time_limit)
11132 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11134 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11135 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11137 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11139 if (!TimeLeft && setup.time_limit)
11141 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11142 level.native_em_level->lev->killed_out_of_time = TRUE;
11144 for (i = 0; i < MAX_PLAYERS; i++)
11145 KillPlayer(&stored_player[i]);
11148 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11150 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11153 level.native_em_level->lev->time =
11154 (game.no_time_limit ? TimePlayed : TimeLeft);
11157 if (tape.recording || tape.playing)
11158 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11161 if (tape.recording || tape.playing)
11162 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11164 UpdateAndDisplayGameControlValues();
11167 void AdvanceFrameAndPlayerCounters(int player_nr)
11171 /* advance frame counters (global frame counter and time frame counter) */
11175 /* advance player counters (counters for move delay, move animation etc.) */
11176 for (i = 0; i < MAX_PLAYERS; i++)
11178 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11179 int move_delay_value = stored_player[i].move_delay_value;
11180 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11182 if (!advance_player_counters) /* not all players may be affected */
11185 if (move_frames == 0) /* less than one move per game frame */
11187 int stepsize = TILEX / move_delay_value;
11188 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11189 int count = (stored_player[i].is_moving ?
11190 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11192 if (count % delay == 0)
11196 stored_player[i].Frame += move_frames;
11198 if (stored_player[i].MovPos != 0)
11199 stored_player[i].StepFrame += move_frames;
11201 if (stored_player[i].move_delay > 0)
11202 stored_player[i].move_delay--;
11204 /* due to bugs in previous versions, counter must count up, not down */
11205 if (stored_player[i].push_delay != -1)
11206 stored_player[i].push_delay++;
11208 if (stored_player[i].drop_delay > 0)
11209 stored_player[i].drop_delay--;
11211 if (stored_player[i].is_dropping_pressed)
11212 stored_player[i].drop_pressed_delay++;
11216 void StartGameActions(boolean init_network_game, boolean record_tape,
11219 unsigned int new_random_seed = InitRND(random_seed);
11222 TapeStartRecording(new_random_seed);
11224 if (init_network_game)
11226 SendToServer_StartPlaying();
11234 void GameActionsExt()
11237 static unsigned int game_frame_delay = 0;
11239 unsigned int game_frame_delay_value;
11240 byte *recorded_player_action;
11241 byte summarized_player_action = 0;
11242 byte tape_action[MAX_PLAYERS];
11245 /* detect endless loops, caused by custom element programming */
11246 if (recursion_loop_detected && recursion_loop_depth == 0)
11248 char *message = getStringCat3("Internal Error! Element ",
11249 EL_NAME(recursion_loop_element),
11250 " caused endless loop! Quit the game?");
11252 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11253 EL_NAME(recursion_loop_element));
11255 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11257 recursion_loop_detected = FALSE; /* if game should be continued */
11264 if (game.restart_level)
11265 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11267 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11268 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11270 if (level.native_em_level->lev->home == 0) /* all players at home */
11272 PlayerWins(local_player);
11274 AllPlayersGone = TRUE;
11276 level.native_em_level->lev->home = -1;
11279 if (level.native_em_level->ply[0]->alive == 0 &&
11280 level.native_em_level->ply[1]->alive == 0 &&
11281 level.native_em_level->ply[2]->alive == 0 &&
11282 level.native_em_level->ply[3]->alive == 0) /* all dead */
11283 AllPlayersGone = TRUE;
11285 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11287 if (game_sp.LevelSolved &&
11288 !game_sp.GameOver) /* game won */
11290 PlayerWins(local_player);
11292 game_sp.GameOver = TRUE;
11294 AllPlayersGone = TRUE;
11297 if (game_sp.GameOver) /* game lost */
11298 AllPlayersGone = TRUE;
11300 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11302 if (game_mm.level_solved &&
11303 !game_mm.game_over) /* game won */
11305 PlayerWins(local_player);
11307 game_mm.game_over = TRUE;
11309 AllPlayersGone = TRUE;
11312 if (game_mm.game_over) /* game lost */
11313 AllPlayersGone = TRUE;
11316 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11319 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11322 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11325 game_frame_delay_value =
11326 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11328 if (tape.playing && tape.warp_forward && !tape.pausing)
11329 game_frame_delay_value = 0;
11331 SetVideoFrameDelay(game_frame_delay_value);
11335 /* ---------- main game synchronization point ---------- */
11337 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11339 printf("::: skip == %d\n", skip);
11342 /* ---------- main game synchronization point ---------- */
11344 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11348 if (network_playing && !network_player_action_received)
11350 /* try to get network player actions in time */
11352 /* last chance to get network player actions without main loop delay */
11353 HandleNetworking();
11355 /* game was quit by network peer */
11356 if (game_status != GAME_MODE_PLAYING)
11359 if (!network_player_action_received)
11360 return; /* failed to get network player actions in time */
11362 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11368 /* at this point we know that we really continue executing the game */
11370 network_player_action_received = FALSE;
11372 /* when playing tape, read previously recorded player input from tape data */
11373 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11375 local_player->effective_mouse_action = local_player->mouse_action;
11377 if (recorded_player_action != NULL)
11378 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11379 recorded_player_action);
11381 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11385 if (tape.set_centered_player)
11387 game.centered_player_nr_next = tape.centered_player_nr_next;
11388 game.set_centered_player = TRUE;
11391 for (i = 0; i < MAX_PLAYERS; i++)
11393 summarized_player_action |= stored_player[i].action;
11395 if (!network_playing && (game.team_mode || tape.playing))
11396 stored_player[i].effective_action = stored_player[i].action;
11399 if (network_playing)
11400 SendToServer_MovePlayer(summarized_player_action);
11402 // summarize all actions at local players mapped input device position
11403 // (this allows using different input devices in single player mode)
11404 if (!network.enabled && !game.team_mode)
11405 stored_player[map_player_action[local_player->index_nr]].effective_action =
11406 summarized_player_action;
11408 if (tape.recording &&
11410 setup.input_on_focus &&
11411 game.centered_player_nr != -1)
11413 for (i = 0; i < MAX_PLAYERS; i++)
11414 stored_player[i].effective_action =
11415 (i == game.centered_player_nr ? summarized_player_action : 0);
11418 if (recorded_player_action != NULL)
11419 for (i = 0; i < MAX_PLAYERS; i++)
11420 stored_player[i].effective_action = recorded_player_action[i];
11422 for (i = 0; i < MAX_PLAYERS; i++)
11424 tape_action[i] = stored_player[i].effective_action;
11426 /* (this may happen in the RND game engine if a player was not present on
11427 the playfield on level start, but appeared later from a custom element */
11428 if (setup.team_mode &&
11431 !tape.player_participates[i])
11432 tape.player_participates[i] = TRUE;
11435 SetTapeActionFromMouseAction(tape_action,
11436 &local_player->effective_mouse_action);
11438 /* only record actions from input devices, but not programmed actions */
11439 if (tape.recording)
11440 TapeRecordAction(tape_action);
11442 #if USE_NEW_PLAYER_ASSIGNMENTS
11443 // !!! also map player actions in single player mode !!!
11444 // if (game.team_mode)
11447 byte mapped_action[MAX_PLAYERS];
11449 #if DEBUG_PLAYER_ACTIONS
11451 for (i = 0; i < MAX_PLAYERS; i++)
11452 printf(" %d, ", stored_player[i].effective_action);
11455 for (i = 0; i < MAX_PLAYERS; i++)
11456 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11458 for (i = 0; i < MAX_PLAYERS; i++)
11459 stored_player[i].effective_action = mapped_action[i];
11461 #if DEBUG_PLAYER_ACTIONS
11463 for (i = 0; i < MAX_PLAYERS; i++)
11464 printf(" %d, ", stored_player[i].effective_action);
11468 #if DEBUG_PLAYER_ACTIONS
11472 for (i = 0; i < MAX_PLAYERS; i++)
11473 printf(" %d, ", stored_player[i].effective_action);
11479 for (i = 0; i < MAX_PLAYERS; i++)
11481 // allow engine snapshot in case of changed movement attempt
11482 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11483 (stored_player[i].effective_action & KEY_MOTION))
11484 game.snapshot.changed_action = TRUE;
11486 // allow engine snapshot in case of snapping/dropping attempt
11487 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11488 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11489 game.snapshot.changed_action = TRUE;
11491 game.snapshot.last_action[i] = stored_player[i].effective_action;
11494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11496 GameActions_EM_Main();
11498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11500 GameActions_SP_Main();
11502 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11504 GameActions_MM_Main();
11508 GameActions_RND_Main();
11511 BlitScreenToBitmap(backbuffer);
11515 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11517 if (global.show_frames_per_second)
11519 static unsigned int fps_counter = 0;
11520 static int fps_frames = 0;
11521 unsigned int fps_delay_ms = Counter() - fps_counter;
11525 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11527 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11530 fps_counter = Counter();
11532 /* always draw FPS to screen after FPS value was updated */
11533 redraw_mask |= REDRAW_FPS;
11536 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11537 if (GetDrawDeactivationMask() == REDRAW_NONE)
11538 redraw_mask |= REDRAW_FPS;
11542 static void GameActions_CheckSaveEngineSnapshot()
11544 if (!game.snapshot.save_snapshot)
11547 // clear flag for saving snapshot _before_ saving snapshot
11548 game.snapshot.save_snapshot = FALSE;
11550 SaveEngineSnapshotToList();
11557 GameActions_CheckSaveEngineSnapshot();
11560 void GameActions_EM_Main()
11562 byte effective_action[MAX_PLAYERS];
11563 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11566 for (i = 0; i < MAX_PLAYERS; i++)
11567 effective_action[i] = stored_player[i].effective_action;
11569 GameActions_EM(effective_action, warp_mode);
11572 void GameActions_SP_Main()
11574 byte effective_action[MAX_PLAYERS];
11575 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11578 for (i = 0; i < MAX_PLAYERS; i++)
11579 effective_action[i] = stored_player[i].effective_action;
11581 GameActions_SP(effective_action, warp_mode);
11583 for (i = 0; i < MAX_PLAYERS; i++)
11585 if (stored_player[i].force_dropping)
11586 stored_player[i].action |= KEY_BUTTON_DROP;
11588 stored_player[i].force_dropping = FALSE;
11592 void GameActions_MM_Main()
11594 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11596 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11599 void GameActions_RND_Main()
11604 void GameActions_RND()
11606 int magic_wall_x = 0, magic_wall_y = 0;
11607 int i, x, y, element, graphic, last_gfx_frame;
11609 InitPlayfieldScanModeVars();
11611 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11613 SCAN_PLAYFIELD(x, y)
11615 ChangeCount[x][y] = 0;
11616 ChangeEvent[x][y] = -1;
11620 if (game.set_centered_player)
11622 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11624 /* switching to "all players" only possible if all players fit to screen */
11625 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11627 game.centered_player_nr_next = game.centered_player_nr;
11628 game.set_centered_player = FALSE;
11631 /* do not switch focus to non-existing (or non-active) player */
11632 if (game.centered_player_nr_next >= 0 &&
11633 !stored_player[game.centered_player_nr_next].active)
11635 game.centered_player_nr_next = game.centered_player_nr;
11636 game.set_centered_player = FALSE;
11640 if (game.set_centered_player &&
11641 ScreenMovPos == 0) /* screen currently aligned at tile position */
11645 if (game.centered_player_nr_next == -1)
11647 setScreenCenteredToAllPlayers(&sx, &sy);
11651 sx = stored_player[game.centered_player_nr_next].jx;
11652 sy = stored_player[game.centered_player_nr_next].jy;
11655 game.centered_player_nr = game.centered_player_nr_next;
11656 game.set_centered_player = FALSE;
11658 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11659 DrawGameDoorValues();
11662 for (i = 0; i < MAX_PLAYERS; i++)
11664 int actual_player_action = stored_player[i].effective_action;
11667 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11668 - rnd_equinox_tetrachloride 048
11669 - rnd_equinox_tetrachloride_ii 096
11670 - rnd_emanuel_schmieg 002
11671 - doctor_sloan_ww 001, 020
11673 if (stored_player[i].MovPos == 0)
11674 CheckGravityMovement(&stored_player[i]);
11677 /* overwrite programmed action with tape action */
11678 if (stored_player[i].programmed_action)
11679 actual_player_action = stored_player[i].programmed_action;
11681 PlayerActions(&stored_player[i], actual_player_action);
11683 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11686 ScrollScreen(NULL, SCROLL_GO_ON);
11688 /* for backwards compatibility, the following code emulates a fixed bug that
11689 occured when pushing elements (causing elements that just made their last
11690 pushing step to already (if possible) make their first falling step in the
11691 same game frame, which is bad); this code is also needed to use the famous
11692 "spring push bug" which is used in older levels and might be wanted to be
11693 used also in newer levels, but in this case the buggy pushing code is only
11694 affecting the "spring" element and no other elements */
11696 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11698 for (i = 0; i < MAX_PLAYERS; i++)
11700 struct PlayerInfo *player = &stored_player[i];
11701 int x = player->jx;
11702 int y = player->jy;
11704 if (player->active && player->is_pushing && player->is_moving &&
11706 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11707 Feld[x][y] == EL_SPRING))
11709 ContinueMoving(x, y);
11711 /* continue moving after pushing (this is actually a bug) */
11712 if (!IS_MOVING(x, y))
11713 Stop[x][y] = FALSE;
11718 SCAN_PLAYFIELD(x, y)
11720 ChangeCount[x][y] = 0;
11721 ChangeEvent[x][y] = -1;
11723 /* this must be handled before main playfield loop */
11724 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11727 if (MovDelay[x][y] <= 0)
11731 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11734 if (MovDelay[x][y] <= 0)
11737 TEST_DrawLevelField(x, y);
11739 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11744 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11746 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11747 printf("GameActions(): This should never happen!\n");
11749 ChangePage[x][y] = -1;
11753 Stop[x][y] = FALSE;
11754 if (WasJustMoving[x][y] > 0)
11755 WasJustMoving[x][y]--;
11756 if (WasJustFalling[x][y] > 0)
11757 WasJustFalling[x][y]--;
11758 if (CheckCollision[x][y] > 0)
11759 CheckCollision[x][y]--;
11760 if (CheckImpact[x][y] > 0)
11761 CheckImpact[x][y]--;
11765 /* reset finished pushing action (not done in ContinueMoving() to allow
11766 continuous pushing animation for elements with zero push delay) */
11767 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11769 ResetGfxAnimation(x, y);
11770 TEST_DrawLevelField(x, y);
11774 if (IS_BLOCKED(x, y))
11778 Blocked2Moving(x, y, &oldx, &oldy);
11779 if (!IS_MOVING(oldx, oldy))
11781 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11782 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11783 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11784 printf("GameActions(): This should never happen!\n");
11790 SCAN_PLAYFIELD(x, y)
11792 element = Feld[x][y];
11793 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11794 last_gfx_frame = GfxFrame[x][y];
11796 ResetGfxFrame(x, y);
11798 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11799 DrawLevelGraphicAnimation(x, y, graphic);
11801 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11802 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11803 ResetRandomAnimationValue(x, y);
11805 SetRandomAnimationValue(x, y);
11807 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11809 if (IS_INACTIVE(element))
11811 if (IS_ANIMATED(graphic))
11812 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11817 /* this may take place after moving, so 'element' may have changed */
11818 if (IS_CHANGING(x, y) &&
11819 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11821 int page = element_info[element].event_page_nr[CE_DELAY];
11823 HandleElementChange(x, y, page);
11825 element = Feld[x][y];
11826 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11829 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11833 element = Feld[x][y];
11834 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11836 if (IS_ANIMATED(graphic) &&
11837 !IS_MOVING(x, y) &&
11839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11841 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11842 TEST_DrawTwinkleOnField(x, y);
11844 else if (element == EL_ACID)
11847 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11849 else if ((element == EL_EXIT_OPEN ||
11850 element == EL_EM_EXIT_OPEN ||
11851 element == EL_SP_EXIT_OPEN ||
11852 element == EL_STEEL_EXIT_OPEN ||
11853 element == EL_EM_STEEL_EXIT_OPEN ||
11854 element == EL_SP_TERMINAL ||
11855 element == EL_SP_TERMINAL_ACTIVE ||
11856 element == EL_EXTRA_TIME ||
11857 element == EL_SHIELD_NORMAL ||
11858 element == EL_SHIELD_DEADLY) &&
11859 IS_ANIMATED(graphic))
11860 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11861 else if (IS_MOVING(x, y))
11862 ContinueMoving(x, y);
11863 else if (IS_ACTIVE_BOMB(element))
11864 CheckDynamite(x, y);
11865 else if (element == EL_AMOEBA_GROWING)
11866 AmoebeWaechst(x, y);
11867 else if (element == EL_AMOEBA_SHRINKING)
11868 AmoebaDisappearing(x, y);
11870 #if !USE_NEW_AMOEBA_CODE
11871 else if (IS_AMOEBALIVE(element))
11872 AmoebeAbleger(x, y);
11875 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11877 else if (element == EL_EXIT_CLOSED)
11879 else if (element == EL_EM_EXIT_CLOSED)
11881 else if (element == EL_STEEL_EXIT_CLOSED)
11882 CheckExitSteel(x, y);
11883 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11884 CheckExitSteelEM(x, y);
11885 else if (element == EL_SP_EXIT_CLOSED)
11887 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11888 element == EL_EXPANDABLE_STEELWALL_GROWING)
11889 MauerWaechst(x, y);
11890 else if (element == EL_EXPANDABLE_WALL ||
11891 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11892 element == EL_EXPANDABLE_WALL_VERTICAL ||
11893 element == EL_EXPANDABLE_WALL_ANY ||
11894 element == EL_BD_EXPANDABLE_WALL)
11895 MauerAbleger(x, y);
11896 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11897 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11898 element == EL_EXPANDABLE_STEELWALL_ANY)
11899 MauerAblegerStahl(x, y);
11900 else if (element == EL_FLAMES)
11901 CheckForDragon(x, y);
11902 else if (element == EL_EXPLOSION)
11903 ; /* drawing of correct explosion animation is handled separately */
11904 else if (element == EL_ELEMENT_SNAPPING ||
11905 element == EL_DIAGONAL_SHRINKING ||
11906 element == EL_DIAGONAL_GROWING)
11908 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11910 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11912 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11913 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11915 if (IS_BELT_ACTIVE(element))
11916 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11918 if (game.magic_wall_active)
11920 int jx = local_player->jx, jy = local_player->jy;
11922 /* play the element sound at the position nearest to the player */
11923 if ((element == EL_MAGIC_WALL_FULL ||
11924 element == EL_MAGIC_WALL_ACTIVE ||
11925 element == EL_MAGIC_WALL_EMPTYING ||
11926 element == EL_BD_MAGIC_WALL_FULL ||
11927 element == EL_BD_MAGIC_WALL_ACTIVE ||
11928 element == EL_BD_MAGIC_WALL_EMPTYING ||
11929 element == EL_DC_MAGIC_WALL_FULL ||
11930 element == EL_DC_MAGIC_WALL_ACTIVE ||
11931 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11932 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11940 #if USE_NEW_AMOEBA_CODE
11941 /* new experimental amoeba growth stuff */
11942 if (!(FrameCounter % 8))
11944 static unsigned int random = 1684108901;
11946 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11948 x = RND(lev_fieldx);
11949 y = RND(lev_fieldy);
11950 element = Feld[x][y];
11952 if (!IS_PLAYER(x,y) &&
11953 (element == EL_EMPTY ||
11954 CAN_GROW_INTO(element) ||
11955 element == EL_QUICKSAND_EMPTY ||
11956 element == EL_QUICKSAND_FAST_EMPTY ||
11957 element == EL_ACID_SPLASH_LEFT ||
11958 element == EL_ACID_SPLASH_RIGHT))
11960 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11961 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11962 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11963 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11964 Feld[x][y] = EL_AMOEBA_DROP;
11967 random = random * 129 + 1;
11972 game.explosions_delayed = FALSE;
11974 SCAN_PLAYFIELD(x, y)
11976 element = Feld[x][y];
11978 if (ExplodeField[x][y])
11979 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11980 else if (element == EL_EXPLOSION)
11981 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11983 ExplodeField[x][y] = EX_TYPE_NONE;
11986 game.explosions_delayed = TRUE;
11988 if (game.magic_wall_active)
11990 if (!(game.magic_wall_time_left % 4))
11992 int element = Feld[magic_wall_x][magic_wall_y];
11994 if (element == EL_BD_MAGIC_WALL_FULL ||
11995 element == EL_BD_MAGIC_WALL_ACTIVE ||
11996 element == EL_BD_MAGIC_WALL_EMPTYING)
11997 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11998 else if (element == EL_DC_MAGIC_WALL_FULL ||
11999 element == EL_DC_MAGIC_WALL_ACTIVE ||
12000 element == EL_DC_MAGIC_WALL_EMPTYING)
12001 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12003 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12006 if (game.magic_wall_time_left > 0)
12008 game.magic_wall_time_left--;
12010 if (!game.magic_wall_time_left)
12012 SCAN_PLAYFIELD(x, y)
12014 element = Feld[x][y];
12016 if (element == EL_MAGIC_WALL_ACTIVE ||
12017 element == EL_MAGIC_WALL_FULL)
12019 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12020 TEST_DrawLevelField(x, y);
12022 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12023 element == EL_BD_MAGIC_WALL_FULL)
12025 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12026 TEST_DrawLevelField(x, y);
12028 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12029 element == EL_DC_MAGIC_WALL_FULL)
12031 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12032 TEST_DrawLevelField(x, y);
12036 game.magic_wall_active = FALSE;
12041 if (game.light_time_left > 0)
12043 game.light_time_left--;
12045 if (game.light_time_left == 0)
12046 RedrawAllLightSwitchesAndInvisibleElements();
12049 if (game.timegate_time_left > 0)
12051 game.timegate_time_left--;
12053 if (game.timegate_time_left == 0)
12054 CloseAllOpenTimegates();
12057 if (game.lenses_time_left > 0)
12059 game.lenses_time_left--;
12061 if (game.lenses_time_left == 0)
12062 RedrawAllInvisibleElementsForLenses();
12065 if (game.magnify_time_left > 0)
12067 game.magnify_time_left--;
12069 if (game.magnify_time_left == 0)
12070 RedrawAllInvisibleElementsForMagnifier();
12073 for (i = 0; i < MAX_PLAYERS; i++)
12075 struct PlayerInfo *player = &stored_player[i];
12077 if (SHIELD_ON(player))
12079 if (player->shield_deadly_time_left)
12080 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12081 else if (player->shield_normal_time_left)
12082 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12086 #if USE_DELAYED_GFX_REDRAW
12087 SCAN_PLAYFIELD(x, y)
12089 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12091 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12092 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12094 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12095 DrawLevelField(x, y);
12097 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12098 DrawLevelFieldCrumbled(x, y);
12100 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12101 DrawLevelFieldCrumbledNeighbours(x, y);
12103 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12104 DrawTwinkleOnField(x, y);
12107 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12112 PlayAllPlayersSound();
12114 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12116 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12118 local_player->show_envelope = 0;
12121 /* use random number generator in every frame to make it less predictable */
12122 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12126 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12128 int min_x = x, min_y = y, max_x = x, max_y = y;
12131 for (i = 0; i < MAX_PLAYERS; i++)
12133 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12135 if (!stored_player[i].active || &stored_player[i] == player)
12138 min_x = MIN(min_x, jx);
12139 min_y = MIN(min_y, jy);
12140 max_x = MAX(max_x, jx);
12141 max_y = MAX(max_y, jy);
12144 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12147 static boolean AllPlayersInVisibleScreen()
12151 for (i = 0; i < MAX_PLAYERS; i++)
12153 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12155 if (!stored_player[i].active)
12158 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12165 void ScrollLevel(int dx, int dy)
12167 int scroll_offset = 2 * TILEX_VAR;
12170 BlitBitmap(drawto_field, drawto_field,
12171 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12172 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12173 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12174 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12175 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12176 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12180 x = (dx == 1 ? BX1 : BX2);
12181 for (y = BY1; y <= BY2; y++)
12182 DrawScreenField(x, y);
12187 y = (dy == 1 ? BY1 : BY2);
12188 for (x = BX1; x <= BX2; x++)
12189 DrawScreenField(x, y);
12192 redraw_mask |= REDRAW_FIELD;
12195 static boolean canFallDown(struct PlayerInfo *player)
12197 int jx = player->jx, jy = player->jy;
12199 return (IN_LEV_FIELD(jx, jy + 1) &&
12200 (IS_FREE(jx, jy + 1) ||
12201 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12202 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12203 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12206 static boolean canPassField(int x, int y, int move_dir)
12208 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12209 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12210 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12211 int nextx = x + dx;
12212 int nexty = y + dy;
12213 int element = Feld[x][y];
12215 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12216 !CAN_MOVE(element) &&
12217 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12218 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12219 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12222 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12224 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12225 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12226 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12230 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12231 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12232 (IS_DIGGABLE(Feld[newx][newy]) ||
12233 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12234 canPassField(newx, newy, move_dir)));
12237 static void CheckGravityMovement(struct PlayerInfo *player)
12239 if (player->gravity && !player->programmed_action)
12241 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12242 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12243 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12244 int jx = player->jx, jy = player->jy;
12245 boolean player_is_moving_to_valid_field =
12246 (!player_is_snapping &&
12247 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12248 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12249 boolean player_can_fall_down = canFallDown(player);
12251 if (player_can_fall_down &&
12252 !player_is_moving_to_valid_field)
12253 player->programmed_action = MV_DOWN;
12257 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12259 return CheckGravityMovement(player);
12261 if (player->gravity && !player->programmed_action)
12263 int jx = player->jx, jy = player->jy;
12264 boolean field_under_player_is_free =
12265 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12266 boolean player_is_standing_on_valid_field =
12267 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12268 (IS_WALKABLE(Feld[jx][jy]) &&
12269 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12271 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12272 player->programmed_action = MV_DOWN;
12277 MovePlayerOneStep()
12278 -----------------------------------------------------------------------------
12279 dx, dy: direction (non-diagonal) to try to move the player to
12280 real_dx, real_dy: direction as read from input device (can be diagonal)
12283 boolean MovePlayerOneStep(struct PlayerInfo *player,
12284 int dx, int dy, int real_dx, int real_dy)
12286 int jx = player->jx, jy = player->jy;
12287 int new_jx = jx + dx, new_jy = jy + dy;
12289 boolean player_can_move = !player->cannot_move;
12291 if (!player->active || (!dx && !dy))
12292 return MP_NO_ACTION;
12294 player->MovDir = (dx < 0 ? MV_LEFT :
12295 dx > 0 ? MV_RIGHT :
12297 dy > 0 ? MV_DOWN : MV_NONE);
12299 if (!IN_LEV_FIELD(new_jx, new_jy))
12300 return MP_NO_ACTION;
12302 if (!player_can_move)
12304 if (player->MovPos == 0)
12306 player->is_moving = FALSE;
12307 player->is_digging = FALSE;
12308 player->is_collecting = FALSE;
12309 player->is_snapping = FALSE;
12310 player->is_pushing = FALSE;
12314 if (!network.enabled && game.centered_player_nr == -1 &&
12315 !AllPlayersInSight(player, new_jx, new_jy))
12316 return MP_NO_ACTION;
12318 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12319 if (can_move != MP_MOVING)
12322 /* check if DigField() has caused relocation of the player */
12323 if (player->jx != jx || player->jy != jy)
12324 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12326 StorePlayer[jx][jy] = 0;
12327 player->last_jx = jx;
12328 player->last_jy = jy;
12329 player->jx = new_jx;
12330 player->jy = new_jy;
12331 StorePlayer[new_jx][new_jy] = player->element_nr;
12333 if (player->move_delay_value_next != -1)
12335 player->move_delay_value = player->move_delay_value_next;
12336 player->move_delay_value_next = -1;
12340 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12342 player->step_counter++;
12344 PlayerVisit[jx][jy] = FrameCounter;
12346 player->is_moving = TRUE;
12349 /* should better be called in MovePlayer(), but this breaks some tapes */
12350 ScrollPlayer(player, SCROLL_INIT);
12356 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12358 int jx = player->jx, jy = player->jy;
12359 int old_jx = jx, old_jy = jy;
12360 int moved = MP_NO_ACTION;
12362 if (!player->active)
12367 if (player->MovPos == 0)
12369 player->is_moving = FALSE;
12370 player->is_digging = FALSE;
12371 player->is_collecting = FALSE;
12372 player->is_snapping = FALSE;
12373 player->is_pushing = FALSE;
12379 if (player->move_delay > 0)
12382 player->move_delay = -1; /* set to "uninitialized" value */
12384 /* store if player is automatically moved to next field */
12385 player->is_auto_moving = (player->programmed_action != MV_NONE);
12387 /* remove the last programmed player action */
12388 player->programmed_action = 0;
12390 if (player->MovPos)
12392 /* should only happen if pre-1.2 tape recordings are played */
12393 /* this is only for backward compatibility */
12395 int original_move_delay_value = player->move_delay_value;
12398 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12402 /* scroll remaining steps with finest movement resolution */
12403 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12405 while (player->MovPos)
12407 ScrollPlayer(player, SCROLL_GO_ON);
12408 ScrollScreen(NULL, SCROLL_GO_ON);
12410 AdvanceFrameAndPlayerCounters(player->index_nr);
12413 BackToFront_WithFrameDelay(0);
12416 player->move_delay_value = original_move_delay_value;
12419 player->is_active = FALSE;
12421 if (player->last_move_dir & MV_HORIZONTAL)
12423 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12424 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12428 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12429 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12432 if (!moved && !player->is_active)
12434 player->is_moving = FALSE;
12435 player->is_digging = FALSE;
12436 player->is_collecting = FALSE;
12437 player->is_snapping = FALSE;
12438 player->is_pushing = FALSE;
12444 if (moved & MP_MOVING && !ScreenMovPos &&
12445 (player->index_nr == game.centered_player_nr ||
12446 game.centered_player_nr == -1))
12448 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12449 int offset = game.scroll_delay_value;
12451 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12453 /* actual player has left the screen -- scroll in that direction */
12454 if (jx != old_jx) /* player has moved horizontally */
12455 scroll_x += (jx - old_jx);
12456 else /* player has moved vertically */
12457 scroll_y += (jy - old_jy);
12461 if (jx != old_jx) /* player has moved horizontally */
12463 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12464 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12465 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12467 /* don't scroll over playfield boundaries */
12468 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12469 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12471 /* don't scroll more than one field at a time */
12472 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12474 /* don't scroll against the player's moving direction */
12475 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12476 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12477 scroll_x = old_scroll_x;
12479 else /* player has moved vertically */
12481 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12482 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12483 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12485 /* don't scroll over playfield boundaries */
12486 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12487 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12489 /* don't scroll more than one field at a time */
12490 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12492 /* don't scroll against the player's moving direction */
12493 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12494 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12495 scroll_y = old_scroll_y;
12499 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12501 if (!network.enabled && game.centered_player_nr == -1 &&
12502 !AllPlayersInVisibleScreen())
12504 scroll_x = old_scroll_x;
12505 scroll_y = old_scroll_y;
12509 ScrollScreen(player, SCROLL_INIT);
12510 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12515 player->StepFrame = 0;
12517 if (moved & MP_MOVING)
12519 if (old_jx != jx && old_jy == jy)
12520 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12521 else if (old_jx == jx && old_jy != jy)
12522 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12524 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12526 player->last_move_dir = player->MovDir;
12527 player->is_moving = TRUE;
12528 player->is_snapping = FALSE;
12529 player->is_switching = FALSE;
12530 player->is_dropping = FALSE;
12531 player->is_dropping_pressed = FALSE;
12532 player->drop_pressed_delay = 0;
12535 /* should better be called here than above, but this breaks some tapes */
12536 ScrollPlayer(player, SCROLL_INIT);
12541 CheckGravityMovementWhenNotMoving(player);
12543 player->is_moving = FALSE;
12545 /* at this point, the player is allowed to move, but cannot move right now
12546 (e.g. because of something blocking the way) -- ensure that the player
12547 is also allowed to move in the next frame (in old versions before 3.1.1,
12548 the player was forced to wait again for eight frames before next try) */
12550 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12551 player->move_delay = 0; /* allow direct movement in the next frame */
12554 if (player->move_delay == -1) /* not yet initialized by DigField() */
12555 player->move_delay = player->move_delay_value;
12557 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12559 TestIfPlayerTouchesBadThing(jx, jy);
12560 TestIfPlayerTouchesCustomElement(jx, jy);
12563 if (!player->active)
12564 RemovePlayer(player);
12569 void ScrollPlayer(struct PlayerInfo *player, int mode)
12571 int jx = player->jx, jy = player->jy;
12572 int last_jx = player->last_jx, last_jy = player->last_jy;
12573 int move_stepsize = TILEX / player->move_delay_value;
12575 if (!player->active)
12578 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12581 if (mode == SCROLL_INIT)
12583 player->actual_frame_counter = FrameCounter;
12584 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12586 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12587 Feld[last_jx][last_jy] == EL_EMPTY)
12589 int last_field_block_delay = 0; /* start with no blocking at all */
12590 int block_delay_adjustment = player->block_delay_adjustment;
12592 /* if player blocks last field, add delay for exactly one move */
12593 if (player->block_last_field)
12595 last_field_block_delay += player->move_delay_value;
12597 /* when blocking enabled, prevent moving up despite gravity */
12598 if (player->gravity && player->MovDir == MV_UP)
12599 block_delay_adjustment = -1;
12602 /* add block delay adjustment (also possible when not blocking) */
12603 last_field_block_delay += block_delay_adjustment;
12605 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12606 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12609 if (player->MovPos != 0) /* player has not yet reached destination */
12612 else if (!FrameReached(&player->actual_frame_counter, 1))
12615 if (player->MovPos != 0)
12617 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12618 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12620 /* before DrawPlayer() to draw correct player graphic for this case */
12621 if (player->MovPos == 0)
12622 CheckGravityMovement(player);
12625 if (player->MovPos == 0) /* player reached destination field */
12627 if (player->move_delay_reset_counter > 0)
12629 player->move_delay_reset_counter--;
12631 if (player->move_delay_reset_counter == 0)
12633 /* continue with normal speed after quickly moving through gate */
12634 HALVE_PLAYER_SPEED(player);
12636 /* be able to make the next move without delay */
12637 player->move_delay = 0;
12641 player->last_jx = jx;
12642 player->last_jy = jy;
12644 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12645 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12646 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12647 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12648 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12649 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12650 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12651 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12653 ExitPlayer(player);
12655 if ((local_player->friends_still_needed == 0 ||
12656 IS_SP_ELEMENT(Feld[jx][jy])) &&
12658 PlayerWins(local_player);
12661 /* this breaks one level: "machine", level 000 */
12663 int move_direction = player->MovDir;
12664 int enter_side = MV_DIR_OPPOSITE(move_direction);
12665 int leave_side = move_direction;
12666 int old_jx = last_jx;
12667 int old_jy = last_jy;
12668 int old_element = Feld[old_jx][old_jy];
12669 int new_element = Feld[jx][jy];
12671 if (IS_CUSTOM_ELEMENT(old_element))
12672 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12674 player->index_bit, leave_side);
12676 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12677 CE_PLAYER_LEAVES_X,
12678 player->index_bit, leave_side);
12680 if (IS_CUSTOM_ELEMENT(new_element))
12681 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12682 player->index_bit, enter_side);
12684 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12685 CE_PLAYER_ENTERS_X,
12686 player->index_bit, enter_side);
12688 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12689 CE_MOVE_OF_X, move_direction);
12692 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12694 TestIfPlayerTouchesBadThing(jx, jy);
12695 TestIfPlayerTouchesCustomElement(jx, jy);
12697 /* needed because pushed element has not yet reached its destination,
12698 so it would trigger a change event at its previous field location */
12699 if (!player->is_pushing)
12700 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12702 if (!player->active)
12703 RemovePlayer(player);
12706 if (!local_player->LevelSolved && level.use_step_counter)
12716 if (TimeLeft <= 10 && setup.time_limit)
12717 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12719 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12721 DisplayGameControlValues();
12723 if (!TimeLeft && setup.time_limit)
12724 for (i = 0; i < MAX_PLAYERS; i++)
12725 KillPlayer(&stored_player[i]);
12727 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12729 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12731 DisplayGameControlValues();
12735 if (tape.single_step && tape.recording && !tape.pausing &&
12736 !player->programmed_action)
12737 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12739 if (!player->programmed_action)
12740 CheckSaveEngineSnapshot(player);
12744 void ScrollScreen(struct PlayerInfo *player, int mode)
12746 static unsigned int screen_frame_counter = 0;
12748 if (mode == SCROLL_INIT)
12750 /* set scrolling step size according to actual player's moving speed */
12751 ScrollStepSize = TILEX / player->move_delay_value;
12753 screen_frame_counter = FrameCounter;
12754 ScreenMovDir = player->MovDir;
12755 ScreenMovPos = player->MovPos;
12756 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12759 else if (!FrameReached(&screen_frame_counter, 1))
12764 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12765 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12766 redraw_mask |= REDRAW_FIELD;
12769 ScreenMovDir = MV_NONE;
12772 void TestIfPlayerTouchesCustomElement(int x, int y)
12774 static int xy[4][2] =
12781 static int trigger_sides[4][2] =
12783 /* center side border side */
12784 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12785 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12786 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12787 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12789 static int touch_dir[4] =
12791 MV_LEFT | MV_RIGHT,
12796 int center_element = Feld[x][y]; /* should always be non-moving! */
12799 for (i = 0; i < NUM_DIRECTIONS; i++)
12801 int xx = x + xy[i][0];
12802 int yy = y + xy[i][1];
12803 int center_side = trigger_sides[i][0];
12804 int border_side = trigger_sides[i][1];
12805 int border_element;
12807 if (!IN_LEV_FIELD(xx, yy))
12810 if (IS_PLAYER(x, y)) /* player found at center element */
12812 struct PlayerInfo *player = PLAYERINFO(x, y);
12814 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12815 border_element = Feld[xx][yy]; /* may be moving! */
12816 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12817 border_element = Feld[xx][yy];
12818 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12819 border_element = MovingOrBlocked2Element(xx, yy);
12821 continue; /* center and border element do not touch */
12823 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12824 player->index_bit, border_side);
12825 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12826 CE_PLAYER_TOUCHES_X,
12827 player->index_bit, border_side);
12830 /* use player element that is initially defined in the level playfield,
12831 not the player element that corresponds to the runtime player number
12832 (example: a level that contains EL_PLAYER_3 as the only player would
12833 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12834 int player_element = PLAYERINFO(x, y)->initial_element;
12836 CheckElementChangeBySide(xx, yy, border_element, player_element,
12837 CE_TOUCHING_X, border_side);
12840 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12842 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12844 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12846 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12847 continue; /* center and border element do not touch */
12850 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12851 player->index_bit, center_side);
12852 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12853 CE_PLAYER_TOUCHES_X,
12854 player->index_bit, center_side);
12857 /* use player element that is initially defined in the level playfield,
12858 not the player element that corresponds to the runtime player number
12859 (example: a level that contains EL_PLAYER_3 as the only player would
12860 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12861 int player_element = PLAYERINFO(xx, yy)->initial_element;
12863 CheckElementChangeBySide(x, y, center_element, player_element,
12864 CE_TOUCHING_X, center_side);
12872 void TestIfElementTouchesCustomElement(int x, int y)
12874 static int xy[4][2] =
12881 static int trigger_sides[4][2] =
12883 /* center side border side */
12884 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12885 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12886 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12887 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12889 static int touch_dir[4] =
12891 MV_LEFT | MV_RIGHT,
12896 boolean change_center_element = FALSE;
12897 int center_element = Feld[x][y]; /* should always be non-moving! */
12898 int border_element_old[NUM_DIRECTIONS];
12901 for (i = 0; i < NUM_DIRECTIONS; i++)
12903 int xx = x + xy[i][0];
12904 int yy = y + xy[i][1];
12905 int border_element;
12907 border_element_old[i] = -1;
12909 if (!IN_LEV_FIELD(xx, yy))
12912 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12913 border_element = Feld[xx][yy]; /* may be moving! */
12914 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12915 border_element = Feld[xx][yy];
12916 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12917 border_element = MovingOrBlocked2Element(xx, yy);
12919 continue; /* center and border element do not touch */
12921 border_element_old[i] = border_element;
12924 for (i = 0; i < NUM_DIRECTIONS; i++)
12926 int xx = x + xy[i][0];
12927 int yy = y + xy[i][1];
12928 int center_side = trigger_sides[i][0];
12929 int border_element = border_element_old[i];
12931 if (border_element == -1)
12934 /* check for change of border element */
12935 CheckElementChangeBySide(xx, yy, border_element, center_element,
12936 CE_TOUCHING_X, center_side);
12938 /* (center element cannot be player, so we dont have to check this here) */
12941 for (i = 0; i < NUM_DIRECTIONS; i++)
12943 int xx = x + xy[i][0];
12944 int yy = y + xy[i][1];
12945 int border_side = trigger_sides[i][1];
12946 int border_element = border_element_old[i];
12948 if (border_element == -1)
12951 /* check for change of center element (but change it only once) */
12952 if (!change_center_element)
12953 change_center_element =
12954 CheckElementChangeBySide(x, y, center_element, border_element,
12955 CE_TOUCHING_X, border_side);
12957 if (IS_PLAYER(xx, yy))
12959 /* use player element that is initially defined in the level playfield,
12960 not the player element that corresponds to the runtime player number
12961 (example: a level that contains EL_PLAYER_3 as the only player would
12962 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12963 int player_element = PLAYERINFO(xx, yy)->initial_element;
12965 CheckElementChangeBySide(x, y, center_element, player_element,
12966 CE_TOUCHING_X, border_side);
12971 void TestIfElementHitsCustomElement(int x, int y, int direction)
12973 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12974 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12975 int hitx = x + dx, hity = y + dy;
12976 int hitting_element = Feld[x][y];
12977 int touched_element;
12979 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12982 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12983 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12985 if (IN_LEV_FIELD(hitx, hity))
12987 int opposite_direction = MV_DIR_OPPOSITE(direction);
12988 int hitting_side = direction;
12989 int touched_side = opposite_direction;
12990 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12991 MovDir[hitx][hity] != direction ||
12992 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12998 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12999 CE_HITTING_X, touched_side);
13001 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13002 CE_HIT_BY_X, hitting_side);
13004 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13005 CE_HIT_BY_SOMETHING, opposite_direction);
13007 if (IS_PLAYER(hitx, hity))
13009 /* use player element that is initially defined in the level playfield,
13010 not the player element that corresponds to the runtime player number
13011 (example: a level that contains EL_PLAYER_3 as the only player would
13012 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13013 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13015 CheckElementChangeBySide(x, y, hitting_element, player_element,
13016 CE_HITTING_X, touched_side);
13021 /* "hitting something" is also true when hitting the playfield border */
13022 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13023 CE_HITTING_SOMETHING, direction);
13026 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13028 int i, kill_x = -1, kill_y = -1;
13030 int bad_element = -1;
13031 static int test_xy[4][2] =
13038 static int test_dir[4] =
13046 for (i = 0; i < NUM_DIRECTIONS; i++)
13048 int test_x, test_y, test_move_dir, test_element;
13050 test_x = good_x + test_xy[i][0];
13051 test_y = good_y + test_xy[i][1];
13053 if (!IN_LEV_FIELD(test_x, test_y))
13057 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13059 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13061 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13062 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13064 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13065 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13069 bad_element = test_element;
13075 if (kill_x != -1 || kill_y != -1)
13077 if (IS_PLAYER(good_x, good_y))
13079 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13081 if (player->shield_deadly_time_left > 0 &&
13082 !IS_INDESTRUCTIBLE(bad_element))
13083 Bang(kill_x, kill_y);
13084 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13085 KillPlayer(player);
13088 Bang(good_x, good_y);
13092 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13094 int i, kill_x = -1, kill_y = -1;
13095 int bad_element = Feld[bad_x][bad_y];
13096 static int test_xy[4][2] =
13103 static int touch_dir[4] =
13105 MV_LEFT | MV_RIGHT,
13110 static int test_dir[4] =
13118 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13121 for (i = 0; i < NUM_DIRECTIONS; i++)
13123 int test_x, test_y, test_move_dir, test_element;
13125 test_x = bad_x + test_xy[i][0];
13126 test_y = bad_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 = Feld[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(bad_element) && bad_move_dir == test_dir[i]) ||
13140 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13142 /* good thing is player or penguin that does not move away */
13143 if (IS_PLAYER(test_x, test_y))
13145 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13147 if (bad_element == EL_ROBOT && player->is_moving)
13148 continue; /* robot does not kill player if he is moving */
13150 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13152 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13153 continue; /* center and border element do not touch */
13161 else if (test_element == EL_PENGUIN)
13171 if (kill_x != -1 || kill_y != -1)
13173 if (IS_PLAYER(kill_x, kill_y))
13175 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13177 if (player->shield_deadly_time_left > 0 &&
13178 !IS_INDESTRUCTIBLE(bad_element))
13179 Bang(bad_x, bad_y);
13180 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13181 KillPlayer(player);
13184 Bang(kill_x, kill_y);
13188 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13190 int bad_element = Feld[bad_x][bad_y];
13191 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13192 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13193 int test_x = bad_x + dx, test_y = bad_y + dy;
13194 int test_move_dir, test_element;
13195 int kill_x = -1, kill_y = -1;
13197 if (!IN_LEV_FIELD(test_x, test_y))
13201 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13203 test_element = Feld[test_x][test_y];
13205 if (test_move_dir != bad_move_dir)
13207 /* good thing can be player or penguin that does not move away */
13208 if (IS_PLAYER(test_x, test_y))
13210 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13212 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13213 player as being hit when he is moving towards the bad thing, because
13214 the "get hit by" condition would be lost after the player stops) */
13215 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13216 return; /* player moves away from bad thing */
13221 else if (test_element == EL_PENGUIN)
13228 if (kill_x != -1 || kill_y != -1)
13230 if (IS_PLAYER(kill_x, kill_y))
13232 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13234 if (player->shield_deadly_time_left > 0 &&
13235 !IS_INDESTRUCTIBLE(bad_element))
13236 Bang(bad_x, bad_y);
13237 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13238 KillPlayer(player);
13241 Bang(kill_x, kill_y);
13245 void TestIfPlayerTouchesBadThing(int x, int y)
13247 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13250 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13252 TestIfGoodThingHitsBadThing(x, y, move_dir);
13255 void TestIfBadThingTouchesPlayer(int x, int y)
13257 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13260 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13262 TestIfBadThingHitsGoodThing(x, y, move_dir);
13265 void TestIfFriendTouchesBadThing(int x, int y)
13267 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13270 void TestIfBadThingTouchesFriend(int x, int y)
13272 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13275 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13277 int i, kill_x = bad_x, kill_y = bad_y;
13278 static int xy[4][2] =
13286 for (i = 0; i < NUM_DIRECTIONS; i++)
13290 x = bad_x + xy[i][0];
13291 y = bad_y + xy[i][1];
13292 if (!IN_LEV_FIELD(x, y))
13295 element = Feld[x][y];
13296 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13297 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13305 if (kill_x != bad_x || kill_y != bad_y)
13306 Bang(bad_x, bad_y);
13309 void KillPlayer(struct PlayerInfo *player)
13311 int jx = player->jx, jy = player->jy;
13313 if (!player->active)
13317 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13318 player->killed, player->active, player->reanimated);
13321 /* the following code was introduced to prevent an infinite loop when calling
13323 -> CheckTriggeredElementChangeExt()
13324 -> ExecuteCustomElementAction()
13326 -> (infinitely repeating the above sequence of function calls)
13327 which occurs when killing the player while having a CE with the setting
13328 "kill player X when explosion of <player X>"; the solution using a new
13329 field "player->killed" was chosen for backwards compatibility, although
13330 clever use of the fields "player->active" etc. would probably also work */
13332 if (player->killed)
13336 player->killed = TRUE;
13338 /* remove accessible field at the player's position */
13339 Feld[jx][jy] = EL_EMPTY;
13341 /* deactivate shield (else Bang()/Explode() would not work right) */
13342 player->shield_normal_time_left = 0;
13343 player->shield_deadly_time_left = 0;
13346 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13347 player->killed, player->active, player->reanimated);
13353 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13354 player->killed, player->active, player->reanimated);
13357 if (player->reanimated) /* killed player may have been reanimated */
13358 player->killed = player->reanimated = FALSE;
13360 BuryPlayer(player);
13363 static void KillPlayerUnlessEnemyProtected(int x, int y)
13365 if (!PLAYER_ENEMY_PROTECTED(x, y))
13366 KillPlayer(PLAYERINFO(x, y));
13369 static void KillPlayerUnlessExplosionProtected(int x, int y)
13371 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13372 KillPlayer(PLAYERINFO(x, y));
13375 void BuryPlayer(struct PlayerInfo *player)
13377 int jx = player->jx, jy = player->jy;
13379 if (!player->active)
13382 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13383 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13385 player->GameOver = TRUE;
13386 RemovePlayer(player);
13389 void RemovePlayer(struct PlayerInfo *player)
13391 int jx = player->jx, jy = player->jy;
13392 int i, found = FALSE;
13394 player->present = FALSE;
13395 player->active = FALSE;
13397 if (!ExplodeField[jx][jy])
13398 StorePlayer[jx][jy] = 0;
13400 if (player->is_moving)
13401 TEST_DrawLevelField(player->last_jx, player->last_jy);
13403 for (i = 0; i < MAX_PLAYERS; i++)
13404 if (stored_player[i].active)
13408 AllPlayersGone = TRUE;
13414 void ExitPlayer(struct PlayerInfo *player)
13416 DrawPlayer(player); /* needed here only to cleanup last field */
13417 RemovePlayer(player);
13419 local_player->players_still_needed--;
13422 static void setFieldForSnapping(int x, int y, int element, int direction)
13424 struct ElementInfo *ei = &element_info[element];
13425 int direction_bit = MV_DIR_TO_BIT(direction);
13426 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13427 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13428 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13430 Feld[x][y] = EL_ELEMENT_SNAPPING;
13431 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13433 ResetGfxAnimation(x, y);
13435 GfxElement[x][y] = element;
13436 GfxAction[x][y] = action;
13437 GfxDir[x][y] = direction;
13438 GfxFrame[x][y] = -1;
13442 =============================================================================
13443 checkDiagonalPushing()
13444 -----------------------------------------------------------------------------
13445 check if diagonal input device direction results in pushing of object
13446 (by checking if the alternative direction is walkable, diggable, ...)
13447 =============================================================================
13450 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13451 int x, int y, int real_dx, int real_dy)
13453 int jx, jy, dx, dy, xx, yy;
13455 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13458 /* diagonal direction: check alternative direction */
13463 xx = jx + (dx == 0 ? real_dx : 0);
13464 yy = jy + (dy == 0 ? real_dy : 0);
13466 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13470 =============================================================================
13472 -----------------------------------------------------------------------------
13473 x, y: field next to player (non-diagonal) to try to dig to
13474 real_dx, real_dy: direction as read from input device (can be diagonal)
13475 =============================================================================
13478 static int DigField(struct PlayerInfo *player,
13479 int oldx, int oldy, int x, int y,
13480 int real_dx, int real_dy, int mode)
13482 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13483 boolean player_was_pushing = player->is_pushing;
13484 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13485 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13486 int jx = oldx, jy = oldy;
13487 int dx = x - jx, dy = y - jy;
13488 int nextx = x + dx, nexty = y + dy;
13489 int move_direction = (dx == -1 ? MV_LEFT :
13490 dx == +1 ? MV_RIGHT :
13492 dy == +1 ? MV_DOWN : MV_NONE);
13493 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13494 int dig_side = MV_DIR_OPPOSITE(move_direction);
13495 int old_element = Feld[jx][jy];
13496 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13499 if (is_player) /* function can also be called by EL_PENGUIN */
13501 if (player->MovPos == 0)
13503 player->is_digging = FALSE;
13504 player->is_collecting = FALSE;
13507 if (player->MovPos == 0) /* last pushing move finished */
13508 player->is_pushing = FALSE;
13510 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13512 player->is_switching = FALSE;
13513 player->push_delay = -1;
13515 return MP_NO_ACTION;
13519 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13520 old_element = Back[jx][jy];
13522 /* in case of element dropped at player position, check background */
13523 else if (Back[jx][jy] != EL_EMPTY &&
13524 game.engine_version >= VERSION_IDENT(2,2,0,0))
13525 old_element = Back[jx][jy];
13527 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13528 return MP_NO_ACTION; /* field has no opening in this direction */
13530 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13531 return MP_NO_ACTION; /* field has no opening in this direction */
13533 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13537 Feld[jx][jy] = player->artwork_element;
13538 InitMovingField(jx, jy, MV_DOWN);
13539 Store[jx][jy] = EL_ACID;
13540 ContinueMoving(jx, jy);
13541 BuryPlayer(player);
13543 return MP_DONT_RUN_INTO;
13546 if (player_can_move && DONT_RUN_INTO(element))
13548 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13550 return MP_DONT_RUN_INTO;
13553 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13554 return MP_NO_ACTION;
13556 collect_count = element_info[element].collect_count_initial;
13558 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13559 return MP_NO_ACTION;
13561 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13562 player_can_move = player_can_move_or_snap;
13564 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13565 game.engine_version >= VERSION_IDENT(2,2,0,0))
13567 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13568 player->index_bit, dig_side);
13569 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13570 player->index_bit, dig_side);
13572 if (element == EL_DC_LANDMINE)
13575 if (Feld[x][y] != element) /* field changed by snapping */
13578 return MP_NO_ACTION;
13581 if (player->gravity && is_player && !player->is_auto_moving &&
13582 canFallDown(player) && move_direction != MV_DOWN &&
13583 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13584 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13586 if (player_can_move &&
13587 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13589 int sound_element = SND_ELEMENT(element);
13590 int sound_action = ACTION_WALKING;
13592 if (IS_RND_GATE(element))
13594 if (!player->key[RND_GATE_NR(element)])
13595 return MP_NO_ACTION;
13597 else if (IS_RND_GATE_GRAY(element))
13599 if (!player->key[RND_GATE_GRAY_NR(element)])
13600 return MP_NO_ACTION;
13602 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13604 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13605 return MP_NO_ACTION;
13607 else if (element == EL_EXIT_OPEN ||
13608 element == EL_EM_EXIT_OPEN ||
13609 element == EL_EM_EXIT_OPENING ||
13610 element == EL_STEEL_EXIT_OPEN ||
13611 element == EL_EM_STEEL_EXIT_OPEN ||
13612 element == EL_EM_STEEL_EXIT_OPENING ||
13613 element == EL_SP_EXIT_OPEN ||
13614 element == EL_SP_EXIT_OPENING)
13616 sound_action = ACTION_PASSING; /* player is passing exit */
13618 else if (element == EL_EMPTY)
13620 sound_action = ACTION_MOVING; /* nothing to walk on */
13623 /* play sound from background or player, whatever is available */
13624 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13625 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13627 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13629 else if (player_can_move &&
13630 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13632 if (!ACCESS_FROM(element, opposite_direction))
13633 return MP_NO_ACTION; /* field not accessible from this direction */
13635 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13636 return MP_NO_ACTION;
13638 if (IS_EM_GATE(element))
13640 if (!player->key[EM_GATE_NR(element)])
13641 return MP_NO_ACTION;
13643 else if (IS_EM_GATE_GRAY(element))
13645 if (!player->key[EM_GATE_GRAY_NR(element)])
13646 return MP_NO_ACTION;
13648 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13650 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13651 return MP_NO_ACTION;
13653 else if (IS_EMC_GATE(element))
13655 if (!player->key[EMC_GATE_NR(element)])
13656 return MP_NO_ACTION;
13658 else if (IS_EMC_GATE_GRAY(element))
13660 if (!player->key[EMC_GATE_GRAY_NR(element)])
13661 return MP_NO_ACTION;
13663 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13665 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13666 return MP_NO_ACTION;
13668 else if (element == EL_DC_GATE_WHITE ||
13669 element == EL_DC_GATE_WHITE_GRAY ||
13670 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13672 if (player->num_white_keys == 0)
13673 return MP_NO_ACTION;
13675 player->num_white_keys--;
13677 else if (IS_SP_PORT(element))
13679 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13680 element == EL_SP_GRAVITY_PORT_RIGHT ||
13681 element == EL_SP_GRAVITY_PORT_UP ||
13682 element == EL_SP_GRAVITY_PORT_DOWN)
13683 player->gravity = !player->gravity;
13684 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13685 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13686 element == EL_SP_GRAVITY_ON_PORT_UP ||
13687 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13688 player->gravity = TRUE;
13689 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13690 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13691 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13692 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13693 player->gravity = FALSE;
13696 /* automatically move to the next field with double speed */
13697 player->programmed_action = move_direction;
13699 if (player->move_delay_reset_counter == 0)
13701 player->move_delay_reset_counter = 2; /* two double speed steps */
13703 DOUBLE_PLAYER_SPEED(player);
13706 PlayLevelSoundAction(x, y, ACTION_PASSING);
13708 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13712 if (mode != DF_SNAP)
13714 GfxElement[x][y] = GFX_ELEMENT(element);
13715 player->is_digging = TRUE;
13718 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13720 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13721 player->index_bit, dig_side);
13723 if (mode == DF_SNAP)
13725 if (level.block_snap_field)
13726 setFieldForSnapping(x, y, element, move_direction);
13728 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13730 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13731 player->index_bit, dig_side);
13734 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13738 if (is_player && mode != DF_SNAP)
13740 GfxElement[x][y] = element;
13741 player->is_collecting = TRUE;
13744 if (element == EL_SPEED_PILL)
13746 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13748 else if (element == EL_EXTRA_TIME && level.time > 0)
13750 TimeLeft += level.extra_time;
13752 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13754 DisplayGameControlValues();
13756 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13758 player->shield_normal_time_left += level.shield_normal_time;
13759 if (element == EL_SHIELD_DEADLY)
13760 player->shield_deadly_time_left += level.shield_deadly_time;
13762 else if (element == EL_DYNAMITE ||
13763 element == EL_EM_DYNAMITE ||
13764 element == EL_SP_DISK_RED)
13766 if (player->inventory_size < MAX_INVENTORY_SIZE)
13767 player->inventory_element[player->inventory_size++] = element;
13769 DrawGameDoorValues();
13771 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13773 player->dynabomb_count++;
13774 player->dynabombs_left++;
13776 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13778 player->dynabomb_size++;
13780 else if (element == EL_DYNABOMB_INCREASE_POWER)
13782 player->dynabomb_xl = TRUE;
13784 else if (IS_KEY(element))
13786 player->key[KEY_NR(element)] = TRUE;
13788 DrawGameDoorValues();
13790 else if (element == EL_DC_KEY_WHITE)
13792 player->num_white_keys++;
13794 /* display white keys? */
13795 /* DrawGameDoorValues(); */
13797 else if (IS_ENVELOPE(element))
13799 player->show_envelope = element;
13801 else if (element == EL_EMC_LENSES)
13803 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13805 RedrawAllInvisibleElementsForLenses();
13807 else if (element == EL_EMC_MAGNIFIER)
13809 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13811 RedrawAllInvisibleElementsForMagnifier();
13813 else if (IS_DROPPABLE(element) ||
13814 IS_THROWABLE(element)) /* can be collected and dropped */
13818 if (collect_count == 0)
13819 player->inventory_infinite_element = element;
13821 for (i = 0; i < collect_count; i++)
13822 if (player->inventory_size < MAX_INVENTORY_SIZE)
13823 player->inventory_element[player->inventory_size++] = element;
13825 DrawGameDoorValues();
13827 else if (collect_count > 0)
13829 local_player->gems_still_needed -= collect_count;
13830 if (local_player->gems_still_needed < 0)
13831 local_player->gems_still_needed = 0;
13833 game.snapshot.collected_item = TRUE;
13835 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13837 DisplayGameControlValues();
13840 RaiseScoreElement(element);
13841 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13844 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13845 player->index_bit, dig_side);
13847 if (mode == DF_SNAP)
13849 if (level.block_snap_field)
13850 setFieldForSnapping(x, y, element, move_direction);
13852 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13854 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13855 player->index_bit, dig_side);
13858 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13860 if (mode == DF_SNAP && element != EL_BD_ROCK)
13861 return MP_NO_ACTION;
13863 if (CAN_FALL(element) && dy)
13864 return MP_NO_ACTION;
13866 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13867 !(element == EL_SPRING && level.use_spring_bug))
13868 return MP_NO_ACTION;
13870 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13871 ((move_direction & MV_VERTICAL &&
13872 ((element_info[element].move_pattern & MV_LEFT &&
13873 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13874 (element_info[element].move_pattern & MV_RIGHT &&
13875 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13876 (move_direction & MV_HORIZONTAL &&
13877 ((element_info[element].move_pattern & MV_UP &&
13878 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13879 (element_info[element].move_pattern & MV_DOWN &&
13880 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13881 return MP_NO_ACTION;
13883 /* do not push elements already moving away faster than player */
13884 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13885 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13886 return MP_NO_ACTION;
13888 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13890 if (player->push_delay_value == -1 || !player_was_pushing)
13891 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13893 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13895 if (player->push_delay_value == -1)
13896 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13898 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13900 if (!player->is_pushing)
13901 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13904 player->is_pushing = TRUE;
13905 player->is_active = TRUE;
13907 if (!(IN_LEV_FIELD(nextx, nexty) &&
13908 (IS_FREE(nextx, nexty) ||
13909 (IS_SB_ELEMENT(element) &&
13910 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13911 (IS_CUSTOM_ELEMENT(element) &&
13912 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13913 return MP_NO_ACTION;
13915 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13916 return MP_NO_ACTION;
13918 if (player->push_delay == -1) /* new pushing; restart delay */
13919 player->push_delay = 0;
13921 if (player->push_delay < player->push_delay_value &&
13922 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13923 element != EL_SPRING && element != EL_BALLOON)
13925 /* make sure that there is no move delay before next try to push */
13926 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13927 player->move_delay = 0;
13929 return MP_NO_ACTION;
13932 if (IS_CUSTOM_ELEMENT(element) &&
13933 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13935 if (!DigFieldByCE(nextx, nexty, element))
13936 return MP_NO_ACTION;
13939 if (IS_SB_ELEMENT(element))
13941 if (element == EL_SOKOBAN_FIELD_FULL)
13943 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13944 local_player->sokobanfields_still_needed++;
13947 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13949 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13950 local_player->sokobanfields_still_needed--;
13953 Feld[x][y] = EL_SOKOBAN_OBJECT;
13955 if (Back[x][y] == Back[nextx][nexty])
13956 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13957 else if (Back[x][y] != 0)
13958 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13961 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13964 if (local_player->sokobanfields_still_needed == 0 &&
13965 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13967 local_player->players_still_needed = 0;
13969 PlayerWins(player);
13971 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13975 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13977 InitMovingField(x, y, move_direction);
13978 GfxAction[x][y] = ACTION_PUSHING;
13980 if (mode == DF_SNAP)
13981 ContinueMoving(x, y);
13983 MovPos[x][y] = (dx != 0 ? dx : dy);
13985 Pushed[x][y] = TRUE;
13986 Pushed[nextx][nexty] = TRUE;
13988 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13989 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13991 player->push_delay_value = -1; /* get new value later */
13993 /* check for element change _after_ element has been pushed */
13994 if (game.use_change_when_pushing_bug)
13996 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13997 player->index_bit, dig_side);
13998 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13999 player->index_bit, dig_side);
14002 else if (IS_SWITCHABLE(element))
14004 if (PLAYER_SWITCHING(player, x, y))
14006 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14007 player->index_bit, dig_side);
14012 player->is_switching = TRUE;
14013 player->switch_x = x;
14014 player->switch_y = y;
14016 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14018 if (element == EL_ROBOT_WHEEL)
14020 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14024 game.robot_wheel_active = TRUE;
14026 TEST_DrawLevelField(x, y);
14028 else if (element == EL_SP_TERMINAL)
14032 SCAN_PLAYFIELD(xx, yy)
14034 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14038 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14040 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14042 ResetGfxAnimation(xx, yy);
14043 TEST_DrawLevelField(xx, yy);
14047 else if (IS_BELT_SWITCH(element))
14049 ToggleBeltSwitch(x, y);
14051 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14052 element == EL_SWITCHGATE_SWITCH_DOWN ||
14053 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14054 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14056 ToggleSwitchgateSwitch(x, y);
14058 else if (element == EL_LIGHT_SWITCH ||
14059 element == EL_LIGHT_SWITCH_ACTIVE)
14061 ToggleLightSwitch(x, y);
14063 else if (element == EL_TIMEGATE_SWITCH ||
14064 element == EL_DC_TIMEGATE_SWITCH)
14066 ActivateTimegateSwitch(x, y);
14068 else if (element == EL_BALLOON_SWITCH_LEFT ||
14069 element == EL_BALLOON_SWITCH_RIGHT ||
14070 element == EL_BALLOON_SWITCH_UP ||
14071 element == EL_BALLOON_SWITCH_DOWN ||
14072 element == EL_BALLOON_SWITCH_NONE ||
14073 element == EL_BALLOON_SWITCH_ANY)
14075 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14076 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14077 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14078 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14079 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14082 else if (element == EL_LAMP)
14084 Feld[x][y] = EL_LAMP_ACTIVE;
14085 local_player->lights_still_needed--;
14087 ResetGfxAnimation(x, y);
14088 TEST_DrawLevelField(x, y);
14090 else if (element == EL_TIME_ORB_FULL)
14092 Feld[x][y] = EL_TIME_ORB_EMPTY;
14094 if (level.time > 0 || level.use_time_orb_bug)
14096 TimeLeft += level.time_orb_time;
14097 game.no_time_limit = FALSE;
14099 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14101 DisplayGameControlValues();
14104 ResetGfxAnimation(x, y);
14105 TEST_DrawLevelField(x, y);
14107 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14108 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14112 game.ball_state = !game.ball_state;
14114 SCAN_PLAYFIELD(xx, yy)
14116 int e = Feld[xx][yy];
14118 if (game.ball_state)
14120 if (e == EL_EMC_MAGIC_BALL)
14121 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14122 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14123 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14127 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14128 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14129 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14130 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14135 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14136 player->index_bit, dig_side);
14138 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14139 player->index_bit, dig_side);
14141 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14142 player->index_bit, dig_side);
14148 if (!PLAYER_SWITCHING(player, x, y))
14150 player->is_switching = TRUE;
14151 player->switch_x = x;
14152 player->switch_y = y;
14154 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14155 player->index_bit, dig_side);
14156 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14157 player->index_bit, dig_side);
14159 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14160 player->index_bit, dig_side);
14161 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14162 player->index_bit, dig_side);
14165 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14166 player->index_bit, dig_side);
14167 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14168 player->index_bit, dig_side);
14170 return MP_NO_ACTION;
14173 player->push_delay = -1;
14175 if (is_player) /* function can also be called by EL_PENGUIN */
14177 if (Feld[x][y] != element) /* really digged/collected something */
14179 player->is_collecting = !player->is_digging;
14180 player->is_active = TRUE;
14187 static boolean DigFieldByCE(int x, int y, int digging_element)
14189 int element = Feld[x][y];
14191 if (!IS_FREE(x, y))
14193 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14194 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14197 /* no element can dig solid indestructible elements */
14198 if (IS_INDESTRUCTIBLE(element) &&
14199 !IS_DIGGABLE(element) &&
14200 !IS_COLLECTIBLE(element))
14203 if (AmoebaNr[x][y] &&
14204 (element == EL_AMOEBA_FULL ||
14205 element == EL_BD_AMOEBA ||
14206 element == EL_AMOEBA_GROWING))
14208 AmoebaCnt[AmoebaNr[x][y]]--;
14209 AmoebaCnt2[AmoebaNr[x][y]]--;
14212 if (IS_MOVING(x, y))
14213 RemoveMovingField(x, y);
14217 TEST_DrawLevelField(x, y);
14220 /* if digged element was about to explode, prevent the explosion */
14221 ExplodeField[x][y] = EX_TYPE_NONE;
14223 PlayLevelSoundAction(x, y, action);
14226 Store[x][y] = EL_EMPTY;
14228 /* this makes it possible to leave the removed element again */
14229 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14230 Store[x][y] = element;
14235 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14237 int jx = player->jx, jy = player->jy;
14238 int x = jx + dx, y = jy + dy;
14239 int snap_direction = (dx == -1 ? MV_LEFT :
14240 dx == +1 ? MV_RIGHT :
14242 dy == +1 ? MV_DOWN : MV_NONE);
14243 boolean can_continue_snapping = (level.continuous_snapping &&
14244 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14246 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14249 if (!player->active || !IN_LEV_FIELD(x, y))
14257 if (player->MovPos == 0)
14258 player->is_pushing = FALSE;
14260 player->is_snapping = FALSE;
14262 if (player->MovPos == 0)
14264 player->is_moving = FALSE;
14265 player->is_digging = FALSE;
14266 player->is_collecting = FALSE;
14272 /* prevent snapping with already pressed snap key when not allowed */
14273 if (player->is_snapping && !can_continue_snapping)
14276 player->MovDir = snap_direction;
14278 if (player->MovPos == 0)
14280 player->is_moving = FALSE;
14281 player->is_digging = FALSE;
14282 player->is_collecting = FALSE;
14285 player->is_dropping = FALSE;
14286 player->is_dropping_pressed = FALSE;
14287 player->drop_pressed_delay = 0;
14289 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14292 player->is_snapping = TRUE;
14293 player->is_active = TRUE;
14295 if (player->MovPos == 0)
14297 player->is_moving = FALSE;
14298 player->is_digging = FALSE;
14299 player->is_collecting = FALSE;
14302 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14303 TEST_DrawLevelField(player->last_jx, player->last_jy);
14305 TEST_DrawLevelField(x, y);
14310 static boolean DropElement(struct PlayerInfo *player)
14312 int old_element, new_element;
14313 int dropx = player->jx, dropy = player->jy;
14314 int drop_direction = player->MovDir;
14315 int drop_side = drop_direction;
14316 int drop_element = get_next_dropped_element(player);
14318 /* do not drop an element on top of another element; when holding drop key
14319 pressed without moving, dropped element must move away before the next
14320 element can be dropped (this is especially important if the next element
14321 is dynamite, which can be placed on background for historical reasons) */
14322 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14325 if (IS_THROWABLE(drop_element))
14327 dropx += GET_DX_FROM_DIR(drop_direction);
14328 dropy += GET_DY_FROM_DIR(drop_direction);
14330 if (!IN_LEV_FIELD(dropx, dropy))
14334 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14335 new_element = drop_element; /* default: no change when dropping */
14337 /* check if player is active, not moving and ready to drop */
14338 if (!player->active || player->MovPos || player->drop_delay > 0)
14341 /* check if player has anything that can be dropped */
14342 if (new_element == EL_UNDEFINED)
14345 /* only set if player has anything that can be dropped */
14346 player->is_dropping_pressed = TRUE;
14348 /* check if drop key was pressed long enough for EM style dynamite */
14349 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14352 /* check if anything can be dropped at the current position */
14353 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14356 /* collected custom elements can only be dropped on empty fields */
14357 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14360 if (old_element != EL_EMPTY)
14361 Back[dropx][dropy] = old_element; /* store old element on this field */
14363 ResetGfxAnimation(dropx, dropy);
14364 ResetRandomAnimationValue(dropx, dropy);
14366 if (player->inventory_size > 0 ||
14367 player->inventory_infinite_element != EL_UNDEFINED)
14369 if (player->inventory_size > 0)
14371 player->inventory_size--;
14373 DrawGameDoorValues();
14375 if (new_element == EL_DYNAMITE)
14376 new_element = EL_DYNAMITE_ACTIVE;
14377 else if (new_element == EL_EM_DYNAMITE)
14378 new_element = EL_EM_DYNAMITE_ACTIVE;
14379 else if (new_element == EL_SP_DISK_RED)
14380 new_element = EL_SP_DISK_RED_ACTIVE;
14383 Feld[dropx][dropy] = new_element;
14385 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14386 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14387 el2img(Feld[dropx][dropy]), 0);
14389 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14391 /* needed if previous element just changed to "empty" in the last frame */
14392 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14394 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14395 player->index_bit, drop_side);
14396 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14398 player->index_bit, drop_side);
14400 TestIfElementTouchesCustomElement(dropx, dropy);
14402 else /* player is dropping a dyna bomb */
14404 player->dynabombs_left--;
14406 Feld[dropx][dropy] = new_element;
14408 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14409 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14410 el2img(Feld[dropx][dropy]), 0);
14412 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14415 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14416 InitField_WithBug1(dropx, dropy, FALSE);
14418 new_element = Feld[dropx][dropy]; /* element might have changed */
14420 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14421 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14423 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14424 MovDir[dropx][dropy] = drop_direction;
14426 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14428 /* do not cause impact style collision by dropping elements that can fall */
14429 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14432 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14433 player->is_dropping = TRUE;
14435 player->drop_pressed_delay = 0;
14436 player->is_dropping_pressed = FALSE;
14438 player->drop_x = dropx;
14439 player->drop_y = dropy;
14444 /* ------------------------------------------------------------------------- */
14445 /* game sound playing functions */
14446 /* ------------------------------------------------------------------------- */
14448 static int *loop_sound_frame = NULL;
14449 static int *loop_sound_volume = NULL;
14451 void InitPlayLevelSound()
14453 int num_sounds = getSoundListSize();
14455 checked_free(loop_sound_frame);
14456 checked_free(loop_sound_volume);
14458 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14459 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14462 static void PlayLevelSound(int x, int y, int nr)
14464 int sx = SCREENX(x), sy = SCREENY(y);
14465 int volume, stereo_position;
14466 int max_distance = 8;
14467 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14469 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14470 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14473 if (!IN_LEV_FIELD(x, y) ||
14474 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14475 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14478 volume = SOUND_MAX_VOLUME;
14480 if (!IN_SCR_FIELD(sx, sy))
14482 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14483 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14485 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14488 stereo_position = (SOUND_MAX_LEFT +
14489 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14490 (SCR_FIELDX + 2 * max_distance));
14492 if (IS_LOOP_SOUND(nr))
14494 /* This assures that quieter loop sounds do not overwrite louder ones,
14495 while restarting sound volume comparison with each new game frame. */
14497 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14500 loop_sound_volume[nr] = volume;
14501 loop_sound_frame[nr] = FrameCounter;
14504 PlaySoundExt(nr, volume, stereo_position, type);
14507 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14509 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14510 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14511 y < LEVELY(BY1) ? LEVELY(BY1) :
14512 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14516 static void PlayLevelSoundAction(int x, int y, int action)
14518 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14521 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14523 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14525 if (sound_effect != SND_UNDEFINED)
14526 PlayLevelSound(x, y, sound_effect);
14529 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14532 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14534 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14535 PlayLevelSound(x, y, sound_effect);
14538 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14540 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14542 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14543 PlayLevelSound(x, y, sound_effect);
14546 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14548 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14550 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14551 StopSound(sound_effect);
14554 static int getLevelMusicNr()
14556 if (levelset.music[level_nr] != MUS_UNDEFINED)
14557 return levelset.music[level_nr]; /* from config file */
14559 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14562 static void FadeLevelSounds()
14567 static void FadeLevelMusic()
14569 int music_nr = getLevelMusicNr();
14570 char *curr_music = getCurrentlyPlayingMusicFilename();
14571 char *next_music = getMusicInfoEntryFilename(music_nr);
14573 if (!strEqual(curr_music, next_music))
14577 void FadeLevelSoundsAndMusic()
14583 static void PlayLevelMusic()
14585 int music_nr = getLevelMusicNr();
14586 char *curr_music = getCurrentlyPlayingMusicFilename();
14587 char *next_music = getMusicInfoEntryFilename(music_nr);
14589 if (!strEqual(curr_music, next_music))
14590 PlayMusic(music_nr);
14593 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14595 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14596 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14597 int x = xx - 1 - offset;
14598 int y = yy - 1 - offset;
14603 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14607 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14611 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14615 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14619 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14623 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14627 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14630 case SAMPLE_android_clone:
14631 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14634 case SAMPLE_android_move:
14635 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14638 case SAMPLE_spring:
14639 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14643 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14647 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14650 case SAMPLE_eater_eat:
14651 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14655 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14658 case SAMPLE_collect:
14659 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14662 case SAMPLE_diamond:
14663 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14666 case SAMPLE_squash:
14667 /* !!! CHECK THIS !!! */
14669 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14671 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14675 case SAMPLE_wonderfall:
14676 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14680 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14684 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14688 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14692 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14696 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14700 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14703 case SAMPLE_wonder:
14704 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14708 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14711 case SAMPLE_exit_open:
14712 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14715 case SAMPLE_exit_leave:
14716 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14719 case SAMPLE_dynamite:
14720 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14724 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14728 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14732 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14740 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14744 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14748 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14753 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14755 int element = map_element_SP_to_RND(element_sp);
14756 int action = map_action_SP_to_RND(action_sp);
14757 int offset = (setup.sp_show_border_elements ? 0 : 1);
14758 int x = xx - offset;
14759 int y = yy - offset;
14761 PlayLevelSoundElementAction(x, y, element, action);
14764 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14766 int element = map_element_MM_to_RND(element_mm);
14767 int action = map_action_MM_to_RND(action_mm);
14769 int x = xx - offset;
14770 int y = yy - offset;
14772 if (!IS_MM_ELEMENT(element))
14773 element = EL_MM_DEFAULT;
14775 PlayLevelSoundElementAction(x, y, element, action);
14778 void PlaySound_MM(int sound_mm)
14780 int sound = map_sound_MM_to_RND(sound_mm);
14782 if (sound == SND_UNDEFINED)
14788 void PlaySoundLoop_MM(int sound_mm)
14790 int sound = map_sound_MM_to_RND(sound_mm);
14792 if (sound == SND_UNDEFINED)
14795 PlaySoundLoop(sound);
14798 void StopSound_MM(int sound_mm)
14800 int sound = map_sound_MM_to_RND(sound_mm);
14802 if (sound == SND_UNDEFINED)
14808 void RaiseScore(int value)
14810 local_player->score += value;
14812 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14814 DisplayGameControlValues();
14817 void RaiseScoreElement(int element)
14822 case EL_BD_DIAMOND:
14823 case EL_EMERALD_YELLOW:
14824 case EL_EMERALD_RED:
14825 case EL_EMERALD_PURPLE:
14826 case EL_SP_INFOTRON:
14827 RaiseScore(level.score[SC_EMERALD]);
14830 RaiseScore(level.score[SC_DIAMOND]);
14833 RaiseScore(level.score[SC_CRYSTAL]);
14836 RaiseScore(level.score[SC_PEARL]);
14839 case EL_BD_BUTTERFLY:
14840 case EL_SP_ELECTRON:
14841 RaiseScore(level.score[SC_BUG]);
14844 case EL_BD_FIREFLY:
14845 case EL_SP_SNIKSNAK:
14846 RaiseScore(level.score[SC_SPACESHIP]);
14849 case EL_DARK_YAMYAM:
14850 RaiseScore(level.score[SC_YAMYAM]);
14853 RaiseScore(level.score[SC_ROBOT]);
14856 RaiseScore(level.score[SC_PACMAN]);
14859 RaiseScore(level.score[SC_NUT]);
14862 case EL_EM_DYNAMITE:
14863 case EL_SP_DISK_RED:
14864 case EL_DYNABOMB_INCREASE_NUMBER:
14865 case EL_DYNABOMB_INCREASE_SIZE:
14866 case EL_DYNABOMB_INCREASE_POWER:
14867 RaiseScore(level.score[SC_DYNAMITE]);
14869 case EL_SHIELD_NORMAL:
14870 case EL_SHIELD_DEADLY:
14871 RaiseScore(level.score[SC_SHIELD]);
14873 case EL_EXTRA_TIME:
14874 RaiseScore(level.extra_time_score);
14888 case EL_DC_KEY_WHITE:
14889 RaiseScore(level.score[SC_KEY]);
14892 RaiseScore(element_info[element].collect_score);
14897 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14899 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14901 /* closing door required in case of envelope style request dialogs */
14903 CloseDoor(DOOR_CLOSE_1);
14905 if (network.enabled)
14906 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14910 FadeSkipNextFadeIn();
14912 SetGameStatus(GAME_MODE_MAIN);
14917 else /* continue playing the game */
14919 if (tape.playing && tape.deactivate_display)
14920 TapeDeactivateDisplayOff(TRUE);
14922 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14924 if (tape.playing && tape.deactivate_display)
14925 TapeDeactivateDisplayOn();
14929 void RequestQuitGame(boolean ask_if_really_quit)
14931 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14932 boolean skip_request = AllPlayersGone || quick_quit;
14934 RequestQuitGameExt(skip_request, quick_quit,
14935 "Do you really want to quit the game?");
14938 void RequestRestartGame(char *message)
14940 game.restart_game_message = NULL;
14942 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14944 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14948 SetGameStatus(GAME_MODE_MAIN);
14955 /* ------------------------------------------------------------------------- */
14956 /* random generator functions */
14957 /* ------------------------------------------------------------------------- */
14959 unsigned int InitEngineRandom_RND(int seed)
14961 game.num_random_calls = 0;
14963 return InitEngineRandom(seed);
14966 unsigned int RND(int max)
14970 game.num_random_calls++;
14972 return GetEngineRandom(max);
14979 /* ------------------------------------------------------------------------- */
14980 /* game engine snapshot handling functions */
14981 /* ------------------------------------------------------------------------- */
14983 struct EngineSnapshotInfo
14985 /* runtime values for custom element collect score */
14986 int collect_score[NUM_CUSTOM_ELEMENTS];
14988 /* runtime values for group element choice position */
14989 int choice_pos[NUM_GROUP_ELEMENTS];
14991 /* runtime values for belt position animations */
14992 int belt_graphic[4][NUM_BELT_PARTS];
14993 int belt_anim_mode[4][NUM_BELT_PARTS];
14996 static struct EngineSnapshotInfo engine_snapshot_rnd;
14997 static char *snapshot_level_identifier = NULL;
14998 static int snapshot_level_nr = -1;
15000 static void SaveEngineSnapshotValues_RND()
15002 static int belt_base_active_element[4] =
15004 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15005 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15006 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15007 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15011 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15013 int element = EL_CUSTOM_START + i;
15015 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15018 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15020 int element = EL_GROUP_START + i;
15022 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15025 for (i = 0; i < 4; i++)
15027 for (j = 0; j < NUM_BELT_PARTS; j++)
15029 int element = belt_base_active_element[i] + j;
15030 int graphic = el2img(element);
15031 int anim_mode = graphic_info[graphic].anim_mode;
15033 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15034 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15039 static void LoadEngineSnapshotValues_RND()
15041 unsigned int num_random_calls = game.num_random_calls;
15044 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15046 int element = EL_CUSTOM_START + i;
15048 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15051 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15053 int element = EL_GROUP_START + i;
15055 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15058 for (i = 0; i < 4; i++)
15060 for (j = 0; j < NUM_BELT_PARTS; j++)
15062 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15063 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15065 graphic_info[graphic].anim_mode = anim_mode;
15069 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15071 InitRND(tape.random_seed);
15072 for (i = 0; i < num_random_calls; i++)
15076 if (game.num_random_calls != num_random_calls)
15078 Error(ERR_INFO, "number of random calls out of sync");
15079 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15080 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15081 Error(ERR_EXIT, "this should not happen -- please debug");
15085 void FreeEngineSnapshotSingle()
15087 FreeSnapshotSingle();
15089 setString(&snapshot_level_identifier, NULL);
15090 snapshot_level_nr = -1;
15093 void FreeEngineSnapshotList()
15095 FreeSnapshotList();
15098 ListNode *SaveEngineSnapshotBuffers()
15100 ListNode *buffers = NULL;
15102 /* copy some special values to a structure better suited for the snapshot */
15104 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15105 SaveEngineSnapshotValues_RND();
15106 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15107 SaveEngineSnapshotValues_EM();
15108 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15109 SaveEngineSnapshotValues_SP(&buffers);
15110 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15111 SaveEngineSnapshotValues_MM(&buffers);
15113 /* save values stored in special snapshot structure */
15115 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15116 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15117 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15119 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15121 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15122 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15124 /* save further RND engine values */
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15127 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15128 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15132 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15139 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15142 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15149 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15171 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15191 ListNode *node = engine_snapshot_list_rnd;
15194 while (node != NULL)
15196 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15201 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15207 void SaveEngineSnapshotSingle()
15209 ListNode *buffers = SaveEngineSnapshotBuffers();
15211 /* finally save all snapshot buffers to single snapshot */
15212 SaveSnapshotSingle(buffers);
15214 /* save level identification information */
15215 setString(&snapshot_level_identifier, leveldir_current->identifier);
15216 snapshot_level_nr = level_nr;
15219 boolean CheckSaveEngineSnapshotToList()
15221 boolean save_snapshot =
15222 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15223 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15224 game.snapshot.changed_action) ||
15225 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15226 game.snapshot.collected_item));
15228 game.snapshot.changed_action = FALSE;
15229 game.snapshot.collected_item = FALSE;
15230 game.snapshot.save_snapshot = save_snapshot;
15232 return save_snapshot;
15235 void SaveEngineSnapshotToList()
15237 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15241 ListNode *buffers = SaveEngineSnapshotBuffers();
15243 /* finally save all snapshot buffers to snapshot list */
15244 SaveSnapshotToList(buffers);
15247 void SaveEngineSnapshotToListInitial()
15249 FreeEngineSnapshotList();
15251 SaveEngineSnapshotToList();
15254 void LoadEngineSnapshotValues()
15256 /* restore special values from snapshot structure */
15258 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15259 LoadEngineSnapshotValues_RND();
15260 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15261 LoadEngineSnapshotValues_EM();
15262 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15263 LoadEngineSnapshotValues_SP();
15264 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15265 LoadEngineSnapshotValues_MM();
15268 void LoadEngineSnapshotSingle()
15270 LoadSnapshotSingle();
15272 LoadEngineSnapshotValues();
15275 void LoadEngineSnapshot_Undo(int steps)
15277 LoadSnapshotFromList_Older(steps);
15279 LoadEngineSnapshotValues();
15282 void LoadEngineSnapshot_Redo(int steps)
15284 LoadSnapshotFromList_Newer(steps);
15286 LoadEngineSnapshotValues();
15289 boolean CheckEngineSnapshotSingle()
15291 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15292 snapshot_level_nr == level_nr);
15295 boolean CheckEngineSnapshotList()
15297 return CheckSnapshotList();
15301 /* ---------- new game button stuff ---------------------------------------- */
15308 boolean *setup_value;
15309 boolean allowed_on_tape;
15311 } gamebutton_info[NUM_GAME_BUTTONS] =
15314 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15315 GAME_CTRL_ID_STOP, NULL,
15319 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15320 GAME_CTRL_ID_PAUSE, NULL,
15324 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15325 GAME_CTRL_ID_PLAY, NULL,
15329 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15330 GAME_CTRL_ID_UNDO, NULL,
15334 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15335 GAME_CTRL_ID_REDO, NULL,
15339 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15340 GAME_CTRL_ID_SAVE, NULL,
15344 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15345 GAME_CTRL_ID_PAUSE2, NULL,
15349 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15350 GAME_CTRL_ID_LOAD, NULL,
15354 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15355 GAME_CTRL_ID_PANEL_STOP, NULL,
15359 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15360 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15361 FALSE, "pause game"
15364 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15365 GAME_CTRL_ID_PANEL_PLAY, NULL,
15369 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15370 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15371 TRUE, "background music on/off"
15374 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15375 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15376 TRUE, "sound loops on/off"
15379 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15380 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15381 TRUE, "normal sounds on/off"
15384 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15385 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15386 FALSE, "background music on/off"
15389 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15390 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15391 FALSE, "sound loops on/off"
15394 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15395 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15396 FALSE, "normal sounds on/off"
15400 void CreateGameButtons()
15404 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15406 int graphic = gamebutton_info[i].graphic;
15407 struct GraphicInfo *gfx = &graphic_info[graphic];
15408 struct XY *pos = gamebutton_info[i].pos;
15409 struct GadgetInfo *gi;
15412 unsigned int event_mask;
15413 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15414 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15415 int base_x = (on_tape ? VX : DX);
15416 int base_y = (on_tape ? VY : DY);
15417 int gd_x = gfx->src_x;
15418 int gd_y = gfx->src_y;
15419 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15420 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15421 int gd_xa = gfx->src_x + gfx->active_xoffset;
15422 int gd_ya = gfx->src_y + gfx->active_yoffset;
15423 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15424 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15427 if (gfx->bitmap == NULL)
15429 game_gadget[id] = NULL;
15434 if (id == GAME_CTRL_ID_STOP ||
15435 id == GAME_CTRL_ID_PANEL_STOP ||
15436 id == GAME_CTRL_ID_PLAY ||
15437 id == GAME_CTRL_ID_PANEL_PLAY ||
15438 id == GAME_CTRL_ID_SAVE ||
15439 id == GAME_CTRL_ID_LOAD)
15441 button_type = GD_TYPE_NORMAL_BUTTON;
15443 event_mask = GD_EVENT_RELEASED;
15445 else if (id == GAME_CTRL_ID_UNDO ||
15446 id == GAME_CTRL_ID_REDO)
15448 button_type = GD_TYPE_NORMAL_BUTTON;
15450 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15454 button_type = GD_TYPE_CHECK_BUTTON;
15455 checked = (gamebutton_info[i].setup_value != NULL ?
15456 *gamebutton_info[i].setup_value : FALSE);
15457 event_mask = GD_EVENT_PRESSED;
15460 gi = CreateGadget(GDI_CUSTOM_ID, id,
15461 GDI_IMAGE_ID, graphic,
15462 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15463 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15464 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15465 GDI_WIDTH, gfx->width,
15466 GDI_HEIGHT, gfx->height,
15467 GDI_TYPE, button_type,
15468 GDI_STATE, GD_BUTTON_UNPRESSED,
15469 GDI_CHECKED, checked,
15470 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15471 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15472 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15473 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15474 GDI_DIRECT_DRAW, FALSE,
15475 GDI_EVENT_MASK, event_mask,
15476 GDI_CALLBACK_ACTION, HandleGameButtons,
15480 Error(ERR_EXIT, "cannot create gadget");
15482 game_gadget[id] = gi;
15486 void FreeGameButtons()
15490 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15491 FreeGadget(game_gadget[i]);
15494 static void UnmapGameButtonsAtSamePosition(int id)
15498 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15500 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15501 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15502 UnmapGadget(game_gadget[i]);
15505 static void UnmapGameButtonsAtSamePosition_All()
15507 if (setup.show_snapshot_buttons)
15509 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15510 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15511 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15515 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15516 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15517 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15519 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15520 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15521 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15525 static void MapGameButtonsAtSamePosition(int id)
15529 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15531 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15532 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15533 MapGadget(game_gadget[i]);
15535 UnmapGameButtonsAtSamePosition_All();
15538 void MapUndoRedoButtons()
15540 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15541 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15543 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15544 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15546 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15549 void UnmapUndoRedoButtons()
15551 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15552 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15554 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15555 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15557 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15560 void MapGameButtonsExt(boolean on_tape)
15564 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15565 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15566 i != GAME_CTRL_ID_UNDO &&
15567 i != GAME_CTRL_ID_REDO)
15568 MapGadget(game_gadget[i]);
15570 UnmapGameButtonsAtSamePosition_All();
15572 RedrawGameButtons();
15575 void UnmapGameButtonsExt(boolean on_tape)
15579 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15580 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15581 UnmapGadget(game_gadget[i]);
15584 void RedrawGameButtonsExt(boolean on_tape)
15588 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15589 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15590 RedrawGadget(game_gadget[i]);
15592 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15593 redraw_mask &= ~REDRAW_ALL;
15596 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15601 gi->checked = state;
15604 void RedrawSoundButtonGadget(int id)
15606 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15607 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15608 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15609 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15610 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15611 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15614 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15615 RedrawGadget(game_gadget[id2]);
15618 void MapGameButtons()
15620 MapGameButtonsExt(FALSE);
15623 void UnmapGameButtons()
15625 UnmapGameButtonsExt(FALSE);
15628 void RedrawGameButtons()
15630 RedrawGameButtonsExt(FALSE);
15633 void MapGameButtonsOnTape()
15635 MapGameButtonsExt(TRUE);
15638 void UnmapGameButtonsOnTape()
15640 UnmapGameButtonsExt(TRUE);
15643 void RedrawGameButtonsOnTape()
15645 RedrawGameButtonsExt(TRUE);
15648 void GameUndoRedoExt()
15650 ClearPlayerAction();
15652 tape.pausing = TRUE;
15655 UpdateAndDisplayGameControlValues();
15657 DrawCompleteVideoDisplay();
15658 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15659 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15660 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15665 void GameUndo(int steps)
15667 if (!CheckEngineSnapshotList())
15670 LoadEngineSnapshot_Undo(steps);
15675 void GameRedo(int steps)
15677 if (!CheckEngineSnapshotList())
15680 LoadEngineSnapshot_Redo(steps);
15685 static void HandleGameButtonsExt(int id, int button)
15687 static boolean game_undo_executed = FALSE;
15688 int steps = BUTTON_STEPSIZE(button);
15689 boolean handle_game_buttons =
15690 (game_status == GAME_MODE_PLAYING ||
15691 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15693 if (!handle_game_buttons)
15698 case GAME_CTRL_ID_STOP:
15699 case GAME_CTRL_ID_PANEL_STOP:
15700 if (game_status == GAME_MODE_MAIN)
15706 RequestQuitGame(TRUE);
15710 case GAME_CTRL_ID_PAUSE:
15711 case GAME_CTRL_ID_PAUSE2:
15712 case GAME_CTRL_ID_PANEL_PAUSE:
15713 if (network.enabled && game_status == GAME_MODE_PLAYING)
15716 SendToServer_ContinuePlaying();
15718 SendToServer_PausePlaying();
15721 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15723 game_undo_executed = FALSE;
15727 case GAME_CTRL_ID_PLAY:
15728 case GAME_CTRL_ID_PANEL_PLAY:
15729 if (game_status == GAME_MODE_MAIN)
15731 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15733 else if (tape.pausing)
15735 if (network.enabled)
15736 SendToServer_ContinuePlaying();
15738 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15742 case GAME_CTRL_ID_UNDO:
15743 // Important: When using "save snapshot when collecting an item" mode,
15744 // load last (current) snapshot for first "undo" after pressing "pause"
15745 // (else the last-but-one snapshot would be loaded, because the snapshot
15746 // pointer already points to the last snapshot when pressing "pause",
15747 // which is fine for "every step/move" mode, but not for "every collect")
15748 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15749 !game_undo_executed)
15752 game_undo_executed = TRUE;
15757 case GAME_CTRL_ID_REDO:
15761 case GAME_CTRL_ID_SAVE:
15765 case GAME_CTRL_ID_LOAD:
15769 case SOUND_CTRL_ID_MUSIC:
15770 case SOUND_CTRL_ID_PANEL_MUSIC:
15771 if (setup.sound_music)
15773 setup.sound_music = FALSE;
15777 else if (audio.music_available)
15779 setup.sound = setup.sound_music = TRUE;
15781 SetAudioMode(setup.sound);
15783 if (game_status == GAME_MODE_PLAYING)
15787 RedrawSoundButtonGadget(id);
15791 case SOUND_CTRL_ID_LOOPS:
15792 case SOUND_CTRL_ID_PANEL_LOOPS:
15793 if (setup.sound_loops)
15794 setup.sound_loops = FALSE;
15795 else if (audio.loops_available)
15797 setup.sound = setup.sound_loops = TRUE;
15799 SetAudioMode(setup.sound);
15802 RedrawSoundButtonGadget(id);
15806 case SOUND_CTRL_ID_SIMPLE:
15807 case SOUND_CTRL_ID_PANEL_SIMPLE:
15808 if (setup.sound_simple)
15809 setup.sound_simple = FALSE;
15810 else if (audio.sound_available)
15812 setup.sound = setup.sound_simple = TRUE;
15814 SetAudioMode(setup.sound);
15817 RedrawSoundButtonGadget(id);
15826 static void HandleGameButtons(struct GadgetInfo *gi)
15828 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15831 void HandleSoundButtonKeys(Key key)
15833 if (key == setup.shortcut.sound_simple)
15834 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15835 else if (key == setup.shortcut.sound_loops)
15836 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15837 else if (key == setup.shortcut.sound_music)
15838 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);