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->friends_still_needed = 0;
3397 for (j = 0; j < MAX_NUM_KEYS; j++)
3398 player->key[j] = FALSE;
3400 player->num_white_keys = 0;
3402 player->dynabomb_count = 0;
3403 player->dynabomb_size = 1;
3404 player->dynabombs_left = 0;
3405 player->dynabomb_xl = FALSE;
3407 player->MovDir = initial_move_dir;
3410 player->GfxDir = initial_move_dir;
3411 player->GfxAction = ACTION_DEFAULT;
3413 player->StepFrame = 0;
3415 player->initial_element = player->element_nr;
3416 player->artwork_element =
3417 (level.use_artwork_element[i] ? level.artwork_element[i] :
3418 player->element_nr);
3419 player->use_murphy = FALSE;
3421 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3422 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3424 player->gravity = level.initial_player_gravity[i];
3426 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3428 player->actual_frame_counter = 0;
3430 player->step_counter = 0;
3432 player->last_move_dir = initial_move_dir;
3434 player->is_active = FALSE;
3436 player->is_waiting = FALSE;
3437 player->is_moving = FALSE;
3438 player->is_auto_moving = FALSE;
3439 player->is_digging = FALSE;
3440 player->is_snapping = FALSE;
3441 player->is_collecting = FALSE;
3442 player->is_pushing = FALSE;
3443 player->is_switching = FALSE;
3444 player->is_dropping = FALSE;
3445 player->is_dropping_pressed = FALSE;
3447 player->is_bored = FALSE;
3448 player->is_sleeping = FALSE;
3450 player->was_waiting = TRUE;
3451 player->was_moving = FALSE;
3452 player->was_snapping = FALSE;
3453 player->was_dropping = FALSE;
3455 player->force_dropping = FALSE;
3457 player->frame_counter_bored = -1;
3458 player->frame_counter_sleeping = -1;
3460 player->anim_delay_counter = 0;
3461 player->post_delay_counter = 0;
3463 player->dir_waiting = initial_move_dir;
3464 player->action_waiting = ACTION_DEFAULT;
3465 player->last_action_waiting = ACTION_DEFAULT;
3466 player->special_action_bored = ACTION_DEFAULT;
3467 player->special_action_sleeping = ACTION_DEFAULT;
3469 player->switch_x = -1;
3470 player->switch_y = -1;
3472 player->drop_x = -1;
3473 player->drop_y = -1;
3475 player->show_envelope = 0;
3477 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3479 player->push_delay = -1; /* initialized when pushing starts */
3480 player->push_delay_value = game.initial_push_delay_value;
3482 player->drop_delay = 0;
3483 player->drop_pressed_delay = 0;
3485 player->last_jx = -1;
3486 player->last_jy = -1;
3490 player->shield_normal_time_left = 0;
3491 player->shield_deadly_time_left = 0;
3493 player->inventory_infinite_element = EL_UNDEFINED;
3494 player->inventory_size = 0;
3496 if (level.use_initial_inventory[i])
3498 for (j = 0; j < level.initial_inventory_size[i]; j++)
3500 int element = level.initial_inventory_content[i][j];
3501 int collect_count = element_info[element].collect_count_initial;
3504 if (!IS_CUSTOM_ELEMENT(element))
3507 if (collect_count == 0)
3508 player->inventory_infinite_element = element;
3510 for (k = 0; k < collect_count; k++)
3511 if (player->inventory_size < MAX_INVENTORY_SIZE)
3512 player->inventory_element[player->inventory_size++] = element;
3516 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3517 SnapField(player, 0, 0);
3519 player->LevelSolved = FALSE;
3520 player->GameOver = FALSE;
3522 player->LevelSolved_GameWon = FALSE;
3523 player->LevelSolved_GameEnd = FALSE;
3524 player->LevelSolved_PanelOff = FALSE;
3525 player->LevelSolved_SaveTape = FALSE;
3526 player->LevelSolved_SaveScore = FALSE;
3528 player->LevelSolved_CountingTime = 0;
3529 player->LevelSolved_CountingScore = 0;
3530 player->LevelSolved_CountingHealth = 0;
3532 map_player_action[i] = i;
3535 network_player_action_received = FALSE;
3537 /* initial null action */
3538 if (network_playing)
3539 SendToServer_MovePlayer(MV_NONE);
3547 TimeLeft = level.time;
3550 ScreenMovDir = MV_NONE;
3554 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3556 AllPlayersGone = FALSE;
3558 game.no_time_limit = (level.time == 0);
3560 game.yamyam_content_nr = 0;
3561 game.robot_wheel_active = FALSE;
3562 game.magic_wall_active = FALSE;
3563 game.magic_wall_time_left = 0;
3564 game.light_time_left = 0;
3565 game.timegate_time_left = 0;
3566 game.switchgate_pos = 0;
3567 game.wind_direction = level.wind_direction_initial;
3569 game.lenses_time_left = 0;
3570 game.magnify_time_left = 0;
3572 game.ball_state = level.ball_state_initial;
3573 game.ball_content_nr = 0;
3575 game.envelope_active = FALSE;
3577 for (i = 0; i < NUM_BELTS; i++)
3579 game.belt_dir[i] = MV_NONE;
3580 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3583 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3584 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3586 #if DEBUG_INIT_PLAYER
3587 DebugPrintPlayerStatus("Player status at level initialization");
3590 SCAN_PLAYFIELD(x, y)
3592 Feld[x][y] = level.field[x][y];
3593 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3594 ChangeDelay[x][y] = 0;
3595 ChangePage[x][y] = -1;
3596 CustomValue[x][y] = 0; /* initialized in InitField() */
3597 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3599 WasJustMoving[x][y] = 0;
3600 WasJustFalling[x][y] = 0;
3601 CheckCollision[x][y] = 0;
3602 CheckImpact[x][y] = 0;
3604 Pushed[x][y] = FALSE;
3606 ChangeCount[x][y] = 0;
3607 ChangeEvent[x][y] = -1;
3609 ExplodePhase[x][y] = 0;
3610 ExplodeDelay[x][y] = 0;
3611 ExplodeField[x][y] = EX_TYPE_NONE;
3613 RunnerVisit[x][y] = 0;
3614 PlayerVisit[x][y] = 0;
3617 GfxRandom[x][y] = INIT_GFX_RANDOM();
3618 GfxElement[x][y] = EL_UNDEFINED;
3619 GfxAction[x][y] = ACTION_DEFAULT;
3620 GfxDir[x][y] = MV_NONE;
3621 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3624 SCAN_PLAYFIELD(x, y)
3626 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3628 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3630 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3633 InitField(x, y, TRUE);
3635 ResetGfxAnimation(x, y);
3640 for (i = 0; i < MAX_PLAYERS; i++)
3642 struct PlayerInfo *player = &stored_player[i];
3644 /* set number of special actions for bored and sleeping animation */
3645 player->num_special_action_bored =
3646 get_num_special_action(player->artwork_element,
3647 ACTION_BORING_1, ACTION_BORING_LAST);
3648 player->num_special_action_sleeping =
3649 get_num_special_action(player->artwork_element,
3650 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3653 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3654 emulate_sb ? EMU_SOKOBAN :
3655 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3657 /* initialize type of slippery elements */
3658 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3660 if (!IS_CUSTOM_ELEMENT(i))
3662 /* default: elements slip down either to the left or right randomly */
3663 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3665 /* SP style elements prefer to slip down on the left side */
3666 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3667 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3669 /* BD style elements prefer to slip down on the left side */
3670 if (game.emulation == EMU_BOULDERDASH)
3671 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675 /* initialize explosion and ignition delay */
3676 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3678 if (!IS_CUSTOM_ELEMENT(i))
3681 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3682 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3683 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3684 int last_phase = (num_phase + 1) * delay;
3685 int half_phase = (num_phase / 2) * delay;
3687 element_info[i].explosion_delay = last_phase - 1;
3688 element_info[i].ignition_delay = half_phase;
3690 if (i == EL_BLACK_ORB)
3691 element_info[i].ignition_delay = 1;
3695 /* correct non-moving belts to start moving left */
3696 for (i = 0; i < NUM_BELTS; i++)
3697 if (game.belt_dir[i] == MV_NONE)
3698 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3700 #if USE_NEW_PLAYER_ASSIGNMENTS
3701 for (i = 0; i < MAX_PLAYERS; i++)
3703 stored_player[i].connected = FALSE;
3705 /* in network game mode, the local player might not be the first player */
3706 if (stored_player[i].connected_locally)
3707 local_player = &stored_player[i];
3710 if (!network.enabled)
3711 local_player->connected = TRUE;
3715 for (i = 0; i < MAX_PLAYERS; i++)
3716 stored_player[i].connected = tape.player_participates[i];
3718 else if (network.enabled)
3720 /* add team mode players connected over the network (needed for correct
3721 assignment of player figures from level to locally playing players) */
3723 for (i = 0; i < MAX_PLAYERS; i++)
3724 if (stored_player[i].connected_network)
3725 stored_player[i].connected = TRUE;
3727 else if (game.team_mode)
3729 /* try to guess locally connected team mode players (needed for correct
3730 assignment of player figures from level to locally playing players) */
3732 for (i = 0; i < MAX_PLAYERS; i++)
3733 if (setup.input[i].use_joystick ||
3734 setup.input[i].key.left != KSYM_UNDEFINED)
3735 stored_player[i].connected = TRUE;
3738 #if DEBUG_INIT_PLAYER
3739 DebugPrintPlayerStatus("Player status after level initialization");
3742 #if DEBUG_INIT_PLAYER
3744 printf("Reassigning players ...\n");
3747 /* check if any connected player was not found in playfield */
3748 for (i = 0; i < MAX_PLAYERS; i++)
3750 struct PlayerInfo *player = &stored_player[i];
3752 if (player->connected && !player->present)
3754 struct PlayerInfo *field_player = NULL;
3756 #if DEBUG_INIT_PLAYER
3758 printf("- looking for field player for player %d ...\n", i + 1);
3761 /* assign first free player found that is present in the playfield */
3763 /* first try: look for unmapped playfield player that is not connected */
3764 for (j = 0; j < MAX_PLAYERS; j++)
3765 if (field_player == NULL &&
3766 stored_player[j].present &&
3767 !stored_player[j].mapped &&
3768 !stored_player[j].connected)
3769 field_player = &stored_player[j];
3771 /* second try: look for *any* unmapped playfield player */
3772 for (j = 0; j < MAX_PLAYERS; j++)
3773 if (field_player == NULL &&
3774 stored_player[j].present &&
3775 !stored_player[j].mapped)
3776 field_player = &stored_player[j];
3778 if (field_player != NULL)
3780 int jx = field_player->jx, jy = field_player->jy;
3782 #if DEBUG_INIT_PLAYER
3784 printf("- found player %d\n", field_player->index_nr + 1);
3787 player->present = FALSE;
3788 player->active = FALSE;
3790 field_player->present = TRUE;
3791 field_player->active = TRUE;
3794 player->initial_element = field_player->initial_element;
3795 player->artwork_element = field_player->artwork_element;
3797 player->block_last_field = field_player->block_last_field;
3798 player->block_delay_adjustment = field_player->block_delay_adjustment;
3801 StorePlayer[jx][jy] = field_player->element_nr;
3803 field_player->jx = field_player->last_jx = jx;
3804 field_player->jy = field_player->last_jy = jy;
3806 if (local_player == player)
3807 local_player = field_player;
3809 map_player_action[field_player->index_nr] = i;
3811 field_player->mapped = TRUE;
3813 #if DEBUG_INIT_PLAYER
3815 printf("- map_player_action[%d] == %d\n",
3816 field_player->index_nr + 1, i + 1);
3821 if (player->connected && player->present)
3822 player->mapped = TRUE;
3825 #if DEBUG_INIT_PLAYER
3826 DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3831 /* check if any connected player was not found in playfield */
3832 for (i = 0; i < MAX_PLAYERS; i++)
3834 struct PlayerInfo *player = &stored_player[i];
3836 if (player->connected && !player->present)
3838 for (j = 0; j < MAX_PLAYERS; j++)
3840 struct PlayerInfo *field_player = &stored_player[j];
3841 int jx = field_player->jx, jy = field_player->jy;
3843 /* assign first free player found that is present in the playfield */
3844 if (field_player->present && !field_player->connected)
3846 player->present = TRUE;
3847 player->active = TRUE;
3849 field_player->present = FALSE;
3850 field_player->active = FALSE;
3852 player->initial_element = field_player->initial_element;
3853 player->artwork_element = field_player->artwork_element;
3855 player->block_last_field = field_player->block_last_field;
3856 player->block_delay_adjustment = field_player->block_delay_adjustment;
3858 StorePlayer[jx][jy] = player->element_nr;
3860 player->jx = player->last_jx = jx;
3861 player->jy = player->last_jy = jy;
3871 printf("::: local_player->present == %d\n", local_player->present);
3874 /* set focus to local player for network games, else to all players */
3875 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3876 game.centered_player_nr_next = game.centered_player_nr;
3877 game.set_centered_player = FALSE;
3879 if (network_playing && tape.recording)
3881 /* store client dependent player focus when recording network games */
3882 tape.centered_player_nr_next = game.centered_player_nr_next;
3883 tape.set_centered_player = TRUE;
3888 /* when playing a tape, eliminate all players who do not participate */
3890 #if USE_NEW_PLAYER_ASSIGNMENTS
3892 if (!game.team_mode)
3894 for (i = 0; i < MAX_PLAYERS; i++)
3896 if (stored_player[i].active &&
3897 !tape.player_participates[map_player_action[i]])
3899 struct PlayerInfo *player = &stored_player[i];
3900 int jx = player->jx, jy = player->jy;
3902 #if DEBUG_INIT_PLAYER
3904 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3907 player->active = FALSE;
3908 StorePlayer[jx][jy] = 0;
3909 Feld[jx][jy] = EL_EMPTY;
3916 for (i = 0; i < MAX_PLAYERS; i++)
3918 if (stored_player[i].active &&
3919 !tape.player_participates[i])
3921 struct PlayerInfo *player = &stored_player[i];
3922 int jx = player->jx, jy = player->jy;
3924 player->active = FALSE;
3925 StorePlayer[jx][jy] = 0;
3926 Feld[jx][jy] = EL_EMPTY;
3931 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3933 /* when in single player mode, eliminate all but the local player */
3935 for (i = 0; i < MAX_PLAYERS; i++)
3937 struct PlayerInfo *player = &stored_player[i];
3939 if (player->active && player != local_player)
3941 int jx = player->jx, jy = player->jy;
3943 player->active = FALSE;
3944 player->present = FALSE;
3946 StorePlayer[jx][jy] = 0;
3947 Feld[jx][jy] = EL_EMPTY;
3952 /* when recording the game, store which players take part in the game */
3955 #if USE_NEW_PLAYER_ASSIGNMENTS
3956 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].connected)
3958 tape.player_participates[i] = TRUE;
3960 for (i = 0; i < MAX_PLAYERS; i++)
3961 if (stored_player[i].active)
3962 tape.player_participates[i] = TRUE;
3966 #if DEBUG_INIT_PLAYER
3967 DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3970 if (BorderElement == EL_EMPTY)
3973 SBX_Right = lev_fieldx - SCR_FIELDX;
3975 SBY_Lower = lev_fieldy - SCR_FIELDY;
3980 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3982 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3985 if (full_lev_fieldx <= SCR_FIELDX)
3986 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3987 if (full_lev_fieldy <= SCR_FIELDY)
3988 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3990 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3992 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3995 /* if local player not found, look for custom element that might create
3996 the player (make some assumptions about the right custom element) */
3997 if (!local_player->present)
3999 int start_x = 0, start_y = 0;
4000 int found_rating = 0;
4001 int found_element = EL_UNDEFINED;
4002 int player_nr = local_player->index_nr;
4004 SCAN_PLAYFIELD(x, y)
4006 int element = Feld[x][y];
4011 if (level.use_start_element[player_nr] &&
4012 level.start_element[player_nr] == element &&
4019 found_element = element;
4022 if (!IS_CUSTOM_ELEMENT(element))
4025 if (CAN_CHANGE(element))
4027 for (i = 0; i < element_info[element].num_change_pages; i++)
4029 /* check for player created from custom element as single target */
4030 content = element_info[element].change_page[i].target_element;
4031 is_player = ELEM_IS_PLAYER(content);
4033 if (is_player && (found_rating < 3 ||
4034 (found_rating == 3 && element < found_element)))
4040 found_element = element;
4045 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4047 /* check for player created from custom element as explosion content */
4048 content = element_info[element].content.e[xx][yy];
4049 is_player = ELEM_IS_PLAYER(content);
4051 if (is_player && (found_rating < 2 ||
4052 (found_rating == 2 && element < found_element)))
4054 start_x = x + xx - 1;
4055 start_y = y + yy - 1;
4058 found_element = element;
4061 if (!CAN_CHANGE(element))
4064 for (i = 0; i < element_info[element].num_change_pages; i++)
4066 /* check for player created from custom element as extended target */
4068 element_info[element].change_page[i].target_content.e[xx][yy];
4070 is_player = ELEM_IS_PLAYER(content);
4072 if (is_player && (found_rating < 1 ||
4073 (found_rating == 1 && element < found_element)))
4075 start_x = x + xx - 1;
4076 start_y = y + yy - 1;
4079 found_element = element;
4085 scroll_x = SCROLL_POSITION_X(start_x);
4086 scroll_y = SCROLL_POSITION_Y(start_y);
4090 scroll_x = SCROLL_POSITION_X(local_player->jx);
4091 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4094 /* !!! FIX THIS (START) !!! */
4095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4097 InitGameEngine_EM();
4099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4101 InitGameEngine_SP();
4103 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4105 InitGameEngine_MM();
4109 DrawLevel(REDRAW_FIELD);
4112 /* after drawing the level, correct some elements */
4113 if (game.timegate_time_left == 0)
4114 CloseAllOpenTimegates();
4117 /* blit playfield from scroll buffer to normal back buffer for fading in */
4118 BlitScreenToBitmap(backbuffer);
4119 /* !!! FIX THIS (END) !!! */
4121 DrawMaskedBorder(fade_mask);
4126 // full screen redraw is required at this point in the following cases:
4127 // - special editor door undrawn when game was started from level editor
4128 // - drawing area (playfield) was changed and has to be removed completely
4129 redraw_mask = REDRAW_ALL;
4133 if (!game.restart_level)
4135 /* copy default game door content to main double buffer */
4137 /* !!! CHECK AGAIN !!! */
4138 SetPanelBackground();
4139 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4140 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4143 SetPanelBackground();
4144 SetDrawBackgroundMask(REDRAW_DOOR_1);
4146 UpdateAndDisplayGameControlValues();
4148 if (!game.restart_level)
4154 CreateGameButtons();
4159 /* copy actual game door content to door double buffer for OpenDoor() */
4160 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4162 OpenDoor(DOOR_OPEN_ALL);
4164 KeyboardAutoRepeatOffUnlessAutoplay();
4166 #if DEBUG_INIT_PLAYER
4167 DebugPrintPlayerStatus("Player status (final)");
4176 if (!game.restart_level && !tape.playing)
4178 LevelStats_incPlayed(level_nr);
4180 SaveLevelSetup_SeriesInfo();
4183 game.restart_level = FALSE;
4184 game.restart_game_message = NULL;
4186 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4187 InitGameActions_MM();
4189 SaveEngineSnapshotToListInitial();
4191 if (!game.restart_level)
4193 PlaySound(SND_GAME_STARTING);
4195 if (setup.sound_music)
4200 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4201 int actual_player_x, int actual_player_y)
4203 /* this is used for non-R'n'D game engines to update certain engine values */
4205 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4207 actual_player_x = correctLevelPosX_EM(actual_player_x);
4208 actual_player_y = correctLevelPosY_EM(actual_player_y);
4211 /* needed to determine if sounds are played within the visible screen area */
4212 scroll_x = actual_scroll_x;
4213 scroll_y = actual_scroll_y;
4215 /* needed to get player position for "follow finger" playing input method */
4216 local_player->jx = actual_player_x;
4217 local_player->jy = actual_player_y;
4220 void InitMovDir(int x, int y)
4222 int i, element = Feld[x][y];
4223 static int xy[4][2] =
4230 static int direction[3][4] =
4232 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4233 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4234 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4243 Feld[x][y] = EL_BUG;
4244 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4247 case EL_SPACESHIP_RIGHT:
4248 case EL_SPACESHIP_UP:
4249 case EL_SPACESHIP_LEFT:
4250 case EL_SPACESHIP_DOWN:
4251 Feld[x][y] = EL_SPACESHIP;
4252 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4255 case EL_BD_BUTTERFLY_RIGHT:
4256 case EL_BD_BUTTERFLY_UP:
4257 case EL_BD_BUTTERFLY_LEFT:
4258 case EL_BD_BUTTERFLY_DOWN:
4259 Feld[x][y] = EL_BD_BUTTERFLY;
4260 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4263 case EL_BD_FIREFLY_RIGHT:
4264 case EL_BD_FIREFLY_UP:
4265 case EL_BD_FIREFLY_LEFT:
4266 case EL_BD_FIREFLY_DOWN:
4267 Feld[x][y] = EL_BD_FIREFLY;
4268 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4271 case EL_PACMAN_RIGHT:
4273 case EL_PACMAN_LEFT:
4274 case EL_PACMAN_DOWN:
4275 Feld[x][y] = EL_PACMAN;
4276 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4279 case EL_YAMYAM_LEFT:
4280 case EL_YAMYAM_RIGHT:
4282 case EL_YAMYAM_DOWN:
4283 Feld[x][y] = EL_YAMYAM;
4284 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4287 case EL_SP_SNIKSNAK:
4288 MovDir[x][y] = MV_UP;
4291 case EL_SP_ELECTRON:
4292 MovDir[x][y] = MV_LEFT;
4299 Feld[x][y] = EL_MOLE;
4300 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4304 if (IS_CUSTOM_ELEMENT(element))
4306 struct ElementInfo *ei = &element_info[element];
4307 int move_direction_initial = ei->move_direction_initial;
4308 int move_pattern = ei->move_pattern;
4310 if (move_direction_initial == MV_START_PREVIOUS)
4312 if (MovDir[x][y] != MV_NONE)
4315 move_direction_initial = MV_START_AUTOMATIC;
4318 if (move_direction_initial == MV_START_RANDOM)
4319 MovDir[x][y] = 1 << RND(4);
4320 else if (move_direction_initial & MV_ANY_DIRECTION)
4321 MovDir[x][y] = move_direction_initial;
4322 else if (move_pattern == MV_ALL_DIRECTIONS ||
4323 move_pattern == MV_TURNING_LEFT ||
4324 move_pattern == MV_TURNING_RIGHT ||
4325 move_pattern == MV_TURNING_LEFT_RIGHT ||
4326 move_pattern == MV_TURNING_RIGHT_LEFT ||
4327 move_pattern == MV_TURNING_RANDOM)
4328 MovDir[x][y] = 1 << RND(4);
4329 else if (move_pattern == MV_HORIZONTAL)
4330 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4331 else if (move_pattern == MV_VERTICAL)
4332 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4333 else if (move_pattern & MV_ANY_DIRECTION)
4334 MovDir[x][y] = element_info[element].move_pattern;
4335 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4336 move_pattern == MV_ALONG_RIGHT_SIDE)
4338 /* use random direction as default start direction */
4339 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4340 MovDir[x][y] = 1 << RND(4);
4342 for (i = 0; i < NUM_DIRECTIONS; i++)
4344 int x1 = x + xy[i][0];
4345 int y1 = y + xy[i][1];
4347 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4349 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4350 MovDir[x][y] = direction[0][i];
4352 MovDir[x][y] = direction[1][i];
4361 MovDir[x][y] = 1 << RND(4);
4363 if (element != EL_BUG &&
4364 element != EL_SPACESHIP &&
4365 element != EL_BD_BUTTERFLY &&
4366 element != EL_BD_FIREFLY)
4369 for (i = 0; i < NUM_DIRECTIONS; i++)
4371 int x1 = x + xy[i][0];
4372 int y1 = y + xy[i][1];
4374 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4376 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4378 MovDir[x][y] = direction[0][i];
4381 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4382 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4384 MovDir[x][y] = direction[1][i];
4393 GfxDir[x][y] = MovDir[x][y];
4396 void InitAmoebaNr(int x, int y)
4399 int group_nr = AmoebeNachbarNr(x, y);
4403 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4405 if (AmoebaCnt[i] == 0)
4413 AmoebaNr[x][y] = group_nr;
4414 AmoebaCnt[group_nr]++;
4415 AmoebaCnt2[group_nr]++;
4418 static void PlayerWins(struct PlayerInfo *player)
4420 player->LevelSolved = TRUE;
4421 player->GameOver = TRUE;
4423 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4424 level.native_em_level->lev->score :
4425 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4428 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4429 MM_HEALTH(game_mm.laser_overload_value) :
4432 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4434 player->LevelSolved_CountingScore = player->score_final;
4435 player->LevelSolved_CountingHealth = player->health_final;
4440 static int time_count_steps;
4441 static int time, time_final;
4442 static int score, score_final;
4443 static int health, health_final;
4444 static int game_over_delay_1 = 0;
4445 static int game_over_delay_2 = 0;
4446 static int game_over_delay_3 = 0;
4447 int game_over_delay_value_1 = 50;
4448 int game_over_delay_value_2 = 25;
4449 int game_over_delay_value_3 = 50;
4451 if (!local_player->LevelSolved_GameWon)
4455 /* do not start end game actions before the player stops moving (to exit) */
4456 if (local_player->MovPos)
4459 local_player->LevelSolved_GameWon = TRUE;
4460 local_player->LevelSolved_SaveTape = tape.recording;
4461 local_player->LevelSolved_SaveScore = !tape.playing;
4465 LevelStats_incSolved(level_nr);
4467 SaveLevelSetup_SeriesInfo();
4470 if (tape.auto_play) /* tape might already be stopped here */
4471 tape.auto_play_level_solved = TRUE;
4475 game_over_delay_1 = 0;
4476 game_over_delay_2 = 0;
4477 game_over_delay_3 = game_over_delay_value_3;
4479 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4480 score = score_final = local_player->score_final;
4481 health = health_final = local_player->health_final;
4483 if (level.score[SC_TIME_BONUS] > 0)
4488 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4490 else if (game.no_time_limit && TimePlayed < 999)
4493 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4496 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4498 game_over_delay_1 = game_over_delay_value_1;
4500 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4503 score_final += health * level.score[SC_TIME_BONUS];
4505 game_over_delay_2 = game_over_delay_value_2;
4508 local_player->score_final = score_final;
4509 local_player->health_final = health_final;
4512 if (level_editor_test_game)
4515 score = score_final;
4517 local_player->LevelSolved_CountingTime = time;
4518 local_player->LevelSolved_CountingScore = score;
4520 game_panel_controls[GAME_PANEL_TIME].value = time;
4521 game_panel_controls[GAME_PANEL_SCORE].value = score;
4523 DisplayGameControlValues();
4526 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4528 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4530 /* close exit door after last player */
4531 if ((AllPlayersGone &&
4532 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4533 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4534 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4535 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4536 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4538 int element = Feld[ExitX][ExitY];
4540 Feld[ExitX][ExitY] =
4541 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4542 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4543 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4544 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4545 EL_EM_STEEL_EXIT_CLOSING);
4547 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4550 /* player disappears */
4551 DrawLevelField(ExitX, ExitY);
4554 for (i = 0; i < MAX_PLAYERS; i++)
4556 struct PlayerInfo *player = &stored_player[i];
4558 if (player->present)
4560 RemovePlayer(player);
4562 /* player disappears */
4563 DrawLevelField(player->jx, player->jy);
4568 PlaySound(SND_GAME_WINNING);
4571 if (game_over_delay_1 > 0)
4573 game_over_delay_1--;
4578 if (time != time_final)
4580 int time_to_go = ABS(time_final - time);
4581 int time_count_dir = (time < time_final ? +1 : -1);
4583 if (time_to_go < time_count_steps)
4584 time_count_steps = 1;
4586 time += time_count_steps * time_count_dir;
4587 score += time_count_steps * level.score[SC_TIME_BONUS];
4589 local_player->LevelSolved_CountingTime = time;
4590 local_player->LevelSolved_CountingScore = score;
4592 game_panel_controls[GAME_PANEL_TIME].value = time;
4593 game_panel_controls[GAME_PANEL_SCORE].value = score;
4595 DisplayGameControlValues();
4597 if (time == time_final)
4598 StopSound(SND_GAME_LEVELTIME_BONUS);
4599 else if (setup.sound_loops)
4600 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4602 PlaySound(SND_GAME_LEVELTIME_BONUS);
4607 if (game_over_delay_2 > 0)
4609 game_over_delay_2--;
4614 if (health != health_final)
4616 int health_count_dir = (health < health_final ? +1 : -1);
4618 health += health_count_dir;
4619 score += level.score[SC_TIME_BONUS];
4621 local_player->LevelSolved_CountingHealth = health;
4622 local_player->LevelSolved_CountingScore = score;
4624 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4625 game_panel_controls[GAME_PANEL_SCORE].value = score;
4627 DisplayGameControlValues();
4629 if (health == health_final)
4630 StopSound(SND_GAME_LEVELTIME_BONUS);
4631 else if (setup.sound_loops)
4632 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4634 PlaySound(SND_GAME_LEVELTIME_BONUS);
4639 local_player->LevelSolved_PanelOff = TRUE;
4641 if (game_over_delay_3 > 0)
4643 game_over_delay_3--;
4654 int last_level_nr = level_nr;
4656 local_player->LevelSolved_GameEnd = TRUE;
4658 if (local_player->LevelSolved_SaveTape)
4660 /* make sure that request dialog to save tape does not open door again */
4661 if (!global.use_envelope_request)
4662 CloseDoor(DOOR_CLOSE_1);
4664 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4667 /* if no tape is to be saved, close both doors simultaneously */
4668 CloseDoor(DOOR_CLOSE_ALL);
4670 if (level_editor_test_game)
4672 SetGameStatus(GAME_MODE_MAIN);
4679 if (!local_player->LevelSolved_SaveScore)
4681 SetGameStatus(GAME_MODE_MAIN);
4688 if (level_nr == leveldir_current->handicap_level)
4690 leveldir_current->handicap_level++;
4692 SaveLevelSetup_SeriesInfo();
4695 if (setup.increment_levels &&
4696 level_nr < leveldir_current->last_level)
4698 level_nr++; /* advance to next level */
4699 TapeErase(); /* start with empty tape */
4701 if (setup.auto_play_next_level)
4703 LoadLevel(level_nr);
4705 SaveLevelSetup_SeriesInfo();
4709 hi_pos = NewHiScore(last_level_nr);
4711 if (hi_pos >= 0 && !setup.skip_scores_after_game)
4713 SetGameStatus(GAME_MODE_SCORES);
4715 DrawHallOfFame(last_level_nr, hi_pos);
4717 else if (!setup.auto_play_next_level || !setup.increment_levels)
4719 SetGameStatus(GAME_MODE_MAIN);
4725 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4729 int NewHiScore(int level_nr)
4733 boolean one_score_entry_per_name = !program.many_scores_per_name;
4735 LoadScore(level_nr);
4737 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4738 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4741 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4743 if (local_player->score_final > highscore[k].Score)
4745 /* player has made it to the hall of fame */
4747 if (k < MAX_SCORE_ENTRIES - 1)
4749 int m = MAX_SCORE_ENTRIES - 1;
4751 if (one_score_entry_per_name)
4753 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4754 if (strEqual(setup.player_name, highscore[l].Name))
4757 if (m == k) /* player's new highscore overwrites his old one */
4761 for (l = m; l > k; l--)
4763 strcpy(highscore[l].Name, highscore[l - 1].Name);
4764 highscore[l].Score = highscore[l - 1].Score;
4770 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4771 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4772 highscore[k].Score = local_player->score_final;
4777 else if (one_score_entry_per_name &&
4778 !strncmp(setup.player_name, highscore[k].Name,
4779 MAX_PLAYER_NAME_LEN))
4780 break; /* player already there with a higher score */
4784 SaveScore(level_nr);
4789 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4791 int element = Feld[x][y];
4792 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4793 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4794 int horiz_move = (dx != 0);
4795 int sign = (horiz_move ? dx : dy);
4796 int step = sign * element_info[element].move_stepsize;
4798 /* special values for move stepsize for spring and things on conveyor belt */
4801 if (CAN_FALL(element) &&
4802 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4803 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4804 else if (element == EL_SPRING)
4805 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4811 inline static int getElementMoveStepsize(int x, int y)
4813 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4816 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4818 if (player->GfxAction != action || player->GfxDir != dir)
4820 player->GfxAction = action;
4821 player->GfxDir = dir;
4823 player->StepFrame = 0;
4827 static void ResetGfxFrame(int x, int y)
4829 // profiling showed that "autotest" spends 10~20% of its time in this function
4830 if (DrawingDeactivatedField())
4833 int element = Feld[x][y];
4834 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4836 if (graphic_info[graphic].anim_global_sync)
4837 GfxFrame[x][y] = FrameCounter;
4838 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4839 GfxFrame[x][y] = CustomValue[x][y];
4840 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4841 GfxFrame[x][y] = element_info[element].collect_score;
4842 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4843 GfxFrame[x][y] = ChangeDelay[x][y];
4846 static void ResetGfxAnimation(int x, int y)
4848 GfxAction[x][y] = ACTION_DEFAULT;
4849 GfxDir[x][y] = MovDir[x][y];
4852 ResetGfxFrame(x, y);
4855 static void ResetRandomAnimationValue(int x, int y)
4857 GfxRandom[x][y] = INIT_GFX_RANDOM();
4860 void InitMovingField(int x, int y, int direction)
4862 int element = Feld[x][y];
4863 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4864 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4867 boolean is_moving_before, is_moving_after;
4869 /* check if element was/is moving or being moved before/after mode change */
4870 is_moving_before = (WasJustMoving[x][y] != 0);
4871 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4873 /* reset animation only for moving elements which change direction of moving
4874 or which just started or stopped moving
4875 (else CEs with property "can move" / "not moving" are reset each frame) */
4876 if (is_moving_before != is_moving_after ||
4877 direction != MovDir[x][y])
4878 ResetGfxAnimation(x, y);
4880 MovDir[x][y] = direction;
4881 GfxDir[x][y] = direction;
4883 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4884 direction == MV_DOWN && CAN_FALL(element) ?
4885 ACTION_FALLING : ACTION_MOVING);
4887 /* this is needed for CEs with property "can move" / "not moving" */
4889 if (is_moving_after)
4891 if (Feld[newx][newy] == EL_EMPTY)
4892 Feld[newx][newy] = EL_BLOCKED;
4894 MovDir[newx][newy] = MovDir[x][y];
4896 CustomValue[newx][newy] = CustomValue[x][y];
4898 GfxFrame[newx][newy] = GfxFrame[x][y];
4899 GfxRandom[newx][newy] = GfxRandom[x][y];
4900 GfxAction[newx][newy] = GfxAction[x][y];
4901 GfxDir[newx][newy] = GfxDir[x][y];
4905 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4907 int direction = MovDir[x][y];
4908 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4909 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4915 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4917 int oldx = x, oldy = y;
4918 int direction = MovDir[x][y];
4920 if (direction == MV_LEFT)
4922 else if (direction == MV_RIGHT)
4924 else if (direction == MV_UP)
4926 else if (direction == MV_DOWN)
4929 *comes_from_x = oldx;
4930 *comes_from_y = oldy;
4933 int MovingOrBlocked2Element(int x, int y)
4935 int element = Feld[x][y];
4937 if (element == EL_BLOCKED)
4941 Blocked2Moving(x, y, &oldx, &oldy);
4942 return Feld[oldx][oldy];
4948 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4950 /* like MovingOrBlocked2Element(), but if element is moving
4951 and (x,y) is the field the moving element is just leaving,
4952 return EL_BLOCKED instead of the element value */
4953 int element = Feld[x][y];
4955 if (IS_MOVING(x, y))
4957 if (element == EL_BLOCKED)
4961 Blocked2Moving(x, y, &oldx, &oldy);
4962 return Feld[oldx][oldy];
4971 static void RemoveField(int x, int y)
4973 Feld[x][y] = EL_EMPTY;
4979 CustomValue[x][y] = 0;
4982 ChangeDelay[x][y] = 0;
4983 ChangePage[x][y] = -1;
4984 Pushed[x][y] = FALSE;
4986 GfxElement[x][y] = EL_UNDEFINED;
4987 GfxAction[x][y] = ACTION_DEFAULT;
4988 GfxDir[x][y] = MV_NONE;
4991 void RemoveMovingField(int x, int y)
4993 int oldx = x, oldy = y, newx = x, newy = y;
4994 int element = Feld[x][y];
4995 int next_element = EL_UNDEFINED;
4997 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5000 if (IS_MOVING(x, y))
5002 Moving2Blocked(x, y, &newx, &newy);
5004 if (Feld[newx][newy] != EL_BLOCKED)
5006 /* element is moving, but target field is not free (blocked), but
5007 already occupied by something different (example: acid pool);
5008 in this case, only remove the moving field, but not the target */
5010 RemoveField(oldx, oldy);
5012 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5014 TEST_DrawLevelField(oldx, oldy);
5019 else if (element == EL_BLOCKED)
5021 Blocked2Moving(x, y, &oldx, &oldy);
5022 if (!IS_MOVING(oldx, oldy))
5026 if (element == EL_BLOCKED &&
5027 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5028 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5029 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5030 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5031 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5032 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5033 next_element = get_next_element(Feld[oldx][oldy]);
5035 RemoveField(oldx, oldy);
5036 RemoveField(newx, newy);
5038 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5040 if (next_element != EL_UNDEFINED)
5041 Feld[oldx][oldy] = next_element;
5043 TEST_DrawLevelField(oldx, oldy);
5044 TEST_DrawLevelField(newx, newy);
5047 void DrawDynamite(int x, int y)
5049 int sx = SCREENX(x), sy = SCREENY(y);
5050 int graphic = el2img(Feld[x][y]);
5053 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5056 if (IS_WALKABLE_INSIDE(Back[x][y]))
5060 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5061 else if (Store[x][y])
5062 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5064 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5066 if (Back[x][y] || Store[x][y])
5067 DrawGraphicThruMask(sx, sy, graphic, frame);
5069 DrawGraphic(sx, sy, graphic, frame);
5072 void CheckDynamite(int x, int y)
5074 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5078 if (MovDelay[x][y] != 0)
5081 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5087 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5092 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5094 boolean num_checked_players = 0;
5097 for (i = 0; i < MAX_PLAYERS; i++)
5099 if (stored_player[i].active)
5101 int sx = stored_player[i].jx;
5102 int sy = stored_player[i].jy;
5104 if (num_checked_players == 0)
5111 *sx1 = MIN(*sx1, sx);
5112 *sy1 = MIN(*sy1, sy);
5113 *sx2 = MAX(*sx2, sx);
5114 *sy2 = MAX(*sy2, sy);
5117 num_checked_players++;
5122 static boolean checkIfAllPlayersFitToScreen_RND()
5124 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5126 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5128 return (sx2 - sx1 < SCR_FIELDX &&
5129 sy2 - sy1 < SCR_FIELDY);
5132 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5134 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5136 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5138 *sx = (sx1 + sx2) / 2;
5139 *sy = (sy1 + sy2) / 2;
5142 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5143 boolean center_screen, boolean quick_relocation)
5145 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5146 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5147 boolean no_delay = (tape.warp_forward);
5148 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5149 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5150 int new_scroll_x, new_scroll_y;
5152 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5154 /* case 1: quick relocation inside visible screen (without scrolling) */
5161 if (!level.shifted_relocation || center_screen)
5163 /* relocation _with_ centering of screen */
5165 new_scroll_x = SCROLL_POSITION_X(x);
5166 new_scroll_y = SCROLL_POSITION_Y(y);
5170 /* relocation _without_ centering of screen */
5172 int center_scroll_x = SCROLL_POSITION_X(old_x);
5173 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5174 int offset_x = x + (scroll_x - center_scroll_x);
5175 int offset_y = y + (scroll_y - center_scroll_y);
5177 /* for new screen position, apply previous offset to center position */
5178 new_scroll_x = SCROLL_POSITION_X(offset_x);
5179 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5182 if (quick_relocation)
5184 /* case 2: quick relocation (redraw without visible scrolling) */
5186 scroll_x = new_scroll_x;
5187 scroll_y = new_scroll_y;
5194 /* case 3: visible relocation (with scrolling to new position) */
5196 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5198 SetVideoFrameDelay(wait_delay_value);
5200 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5203 int fx = FX, fy = FY;
5205 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5206 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5208 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5214 fx += dx * TILEX / 2;
5215 fy += dy * TILEY / 2;
5217 ScrollLevel(dx, dy);
5220 /* scroll in two steps of half tile size to make things smoother */
5221 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5223 /* scroll second step to align at full tile size */
5224 BlitScreenToBitmap(window);
5230 SetVideoFrameDelay(frame_delay_value_old);
5233 void RelocatePlayer(int jx, int jy, int el_player_raw)
5235 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5236 int player_nr = GET_PLAYER_NR(el_player);
5237 struct PlayerInfo *player = &stored_player[player_nr];
5238 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5239 boolean no_delay = (tape.warp_forward);
5240 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5241 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5242 int old_jx = player->jx;
5243 int old_jy = player->jy;
5244 int old_element = Feld[old_jx][old_jy];
5245 int element = Feld[jx][jy];
5246 boolean player_relocated = (old_jx != jx || old_jy != jy);
5248 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5249 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5250 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5251 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5252 int leave_side_horiz = move_dir_horiz;
5253 int leave_side_vert = move_dir_vert;
5254 int enter_side = enter_side_horiz | enter_side_vert;
5255 int leave_side = leave_side_horiz | leave_side_vert;
5257 if (player->GameOver) /* do not reanimate dead player */
5260 if (!player_relocated) /* no need to relocate the player */
5263 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5265 RemoveField(jx, jy); /* temporarily remove newly placed player */
5266 DrawLevelField(jx, jy);
5269 if (player->present)
5271 while (player->MovPos)
5273 ScrollPlayer(player, SCROLL_GO_ON);
5274 ScrollScreen(NULL, SCROLL_GO_ON);
5276 AdvanceFrameAndPlayerCounters(player->index_nr);
5280 BackToFront_WithFrameDelay(wait_delay_value);
5283 DrawPlayer(player); /* needed here only to cleanup last field */
5284 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5286 player->is_moving = FALSE;
5289 if (IS_CUSTOM_ELEMENT(old_element))
5290 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5292 player->index_bit, leave_side);
5294 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5296 player->index_bit, leave_side);
5298 Feld[jx][jy] = el_player;
5299 InitPlayerField(jx, jy, el_player, TRUE);
5301 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5302 possible that the relocation target field did not contain a player element,
5303 but a walkable element, to which the new player was relocated -- in this
5304 case, restore that (already initialized!) element on the player field */
5305 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5307 Feld[jx][jy] = element; /* restore previously existing element */
5310 /* only visually relocate centered player */
5311 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5312 FALSE, level.instant_relocation);
5314 TestIfPlayerTouchesBadThing(jx, jy);
5315 TestIfPlayerTouchesCustomElement(jx, jy);
5317 if (IS_CUSTOM_ELEMENT(element))
5318 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5319 player->index_bit, enter_side);
5321 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5322 player->index_bit, enter_side);
5324 if (player->is_switching)
5326 /* ensure that relocation while still switching an element does not cause
5327 a new element to be treated as also switched directly after relocation
5328 (this is important for teleporter switches that teleport the player to
5329 a place where another teleporter switch is in the same direction, which
5330 would then incorrectly be treated as immediately switched before the
5331 direction key that caused the switch was released) */
5333 player->switch_x += jx - old_jx;
5334 player->switch_y += jy - old_jy;
5338 void Explode(int ex, int ey, int phase, int mode)
5344 /* !!! eliminate this variable !!! */
5345 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5347 if (game.explosions_delayed)
5349 ExplodeField[ex][ey] = mode;
5353 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5355 int center_element = Feld[ex][ey];
5356 int artwork_element, explosion_element; /* set these values later */
5358 /* remove things displayed in background while burning dynamite */
5359 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5362 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5364 /* put moving element to center field (and let it explode there) */
5365 center_element = MovingOrBlocked2Element(ex, ey);
5366 RemoveMovingField(ex, ey);
5367 Feld[ex][ey] = center_element;
5370 /* now "center_element" is finally determined -- set related values now */
5371 artwork_element = center_element; /* for custom player artwork */
5372 explosion_element = center_element; /* for custom player artwork */
5374 if (IS_PLAYER(ex, ey))
5376 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5378 artwork_element = stored_player[player_nr].artwork_element;
5380 if (level.use_explosion_element[player_nr])
5382 explosion_element = level.explosion_element[player_nr];
5383 artwork_element = explosion_element;
5387 if (mode == EX_TYPE_NORMAL ||
5388 mode == EX_TYPE_CENTER ||
5389 mode == EX_TYPE_CROSS)
5390 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5392 last_phase = element_info[explosion_element].explosion_delay + 1;
5394 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5396 int xx = x - ex + 1;
5397 int yy = y - ey + 1;
5400 if (!IN_LEV_FIELD(x, y) ||
5401 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5402 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5405 element = Feld[x][y];
5407 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5409 element = MovingOrBlocked2Element(x, y);
5411 if (!IS_EXPLOSION_PROOF(element))
5412 RemoveMovingField(x, y);
5415 /* indestructible elements can only explode in center (but not flames) */
5416 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5417 mode == EX_TYPE_BORDER)) ||
5418 element == EL_FLAMES)
5421 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5422 behaviour, for example when touching a yamyam that explodes to rocks
5423 with active deadly shield, a rock is created under the player !!! */
5424 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5426 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5427 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5428 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5430 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5433 if (IS_ACTIVE_BOMB(element))
5435 /* re-activate things under the bomb like gate or penguin */
5436 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5443 /* save walkable background elements while explosion on same tile */
5444 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5445 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5446 Back[x][y] = element;
5448 /* ignite explodable elements reached by other explosion */
5449 if (element == EL_EXPLOSION)
5450 element = Store2[x][y];
5452 if (AmoebaNr[x][y] &&
5453 (element == EL_AMOEBA_FULL ||
5454 element == EL_BD_AMOEBA ||
5455 element == EL_AMOEBA_GROWING))
5457 AmoebaCnt[AmoebaNr[x][y]]--;
5458 AmoebaCnt2[AmoebaNr[x][y]]--;
5463 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5465 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5467 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5469 if (PLAYERINFO(ex, ey)->use_murphy)
5470 Store[x][y] = EL_EMPTY;
5473 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5474 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5475 else if (ELEM_IS_PLAYER(center_element))
5476 Store[x][y] = EL_EMPTY;
5477 else if (center_element == EL_YAMYAM)
5478 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5479 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5480 Store[x][y] = element_info[center_element].content.e[xx][yy];
5482 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5483 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5484 otherwise) -- FIX THIS !!! */
5485 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5486 Store[x][y] = element_info[element].content.e[1][1];
5488 else if (!CAN_EXPLODE(element))
5489 Store[x][y] = element_info[element].content.e[1][1];
5492 Store[x][y] = EL_EMPTY;
5494 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5495 center_element == EL_AMOEBA_TO_DIAMOND)
5496 Store2[x][y] = element;
5498 Feld[x][y] = EL_EXPLOSION;
5499 GfxElement[x][y] = artwork_element;
5501 ExplodePhase[x][y] = 1;
5502 ExplodeDelay[x][y] = last_phase;
5507 if (center_element == EL_YAMYAM)
5508 game.yamyam_content_nr =
5509 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5521 GfxFrame[x][y] = 0; /* restart explosion animation */
5523 last_phase = ExplodeDelay[x][y];
5525 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5527 /* this can happen if the player leaves an explosion just in time */
5528 if (GfxElement[x][y] == EL_UNDEFINED)
5529 GfxElement[x][y] = EL_EMPTY;
5531 border_element = Store2[x][y];
5532 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5533 border_element = StorePlayer[x][y];
5535 if (phase == element_info[border_element].ignition_delay ||
5536 phase == last_phase)
5538 boolean border_explosion = FALSE;
5540 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5541 !PLAYER_EXPLOSION_PROTECTED(x, y))
5543 KillPlayerUnlessExplosionProtected(x, y);
5544 border_explosion = TRUE;
5546 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5548 Feld[x][y] = Store2[x][y];
5551 border_explosion = TRUE;
5553 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5555 AmoebeUmwandeln(x, y);
5557 border_explosion = TRUE;
5560 /* if an element just explodes due to another explosion (chain-reaction),
5561 do not immediately end the new explosion when it was the last frame of
5562 the explosion (as it would be done in the following "if"-statement!) */
5563 if (border_explosion && phase == last_phase)
5567 if (phase == last_phase)
5571 element = Feld[x][y] = Store[x][y];
5572 Store[x][y] = Store2[x][y] = 0;
5573 GfxElement[x][y] = EL_UNDEFINED;
5575 /* player can escape from explosions and might therefore be still alive */
5576 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5577 element <= EL_PLAYER_IS_EXPLODING_4)
5579 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5580 int explosion_element = EL_PLAYER_1 + player_nr;
5581 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5582 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5584 if (level.use_explosion_element[player_nr])
5585 explosion_element = level.explosion_element[player_nr];
5587 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5588 element_info[explosion_element].content.e[xx][yy]);
5591 /* restore probably existing indestructible background element */
5592 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5593 element = Feld[x][y] = Back[x][y];
5596 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5597 GfxDir[x][y] = MV_NONE;
5598 ChangeDelay[x][y] = 0;
5599 ChangePage[x][y] = -1;
5601 CustomValue[x][y] = 0;
5603 InitField_WithBug2(x, y, FALSE);
5605 TEST_DrawLevelField(x, y);
5607 TestIfElementTouchesCustomElement(x, y);
5609 if (GFX_CRUMBLED(element))
5610 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5612 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5613 StorePlayer[x][y] = 0;
5615 if (ELEM_IS_PLAYER(element))
5616 RelocatePlayer(x, y, element);
5618 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5620 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5621 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5624 TEST_DrawLevelFieldCrumbled(x, y);
5626 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5628 DrawLevelElement(x, y, Back[x][y]);
5629 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5631 else if (IS_WALKABLE_UNDER(Back[x][y]))
5633 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5634 DrawLevelElementThruMask(x, y, Back[x][y]);
5636 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5637 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5641 void DynaExplode(int ex, int ey)
5644 int dynabomb_element = Feld[ex][ey];
5645 int dynabomb_size = 1;
5646 boolean dynabomb_xl = FALSE;
5647 struct PlayerInfo *player;
5648 static int xy[4][2] =
5656 if (IS_ACTIVE_BOMB(dynabomb_element))
5658 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5659 dynabomb_size = player->dynabomb_size;
5660 dynabomb_xl = player->dynabomb_xl;
5661 player->dynabombs_left++;
5664 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5666 for (i = 0; i < NUM_DIRECTIONS; i++)
5668 for (j = 1; j <= dynabomb_size; j++)
5670 int x = ex + j * xy[i][0];
5671 int y = ey + j * xy[i][1];
5674 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5677 element = Feld[x][y];
5679 /* do not restart explosions of fields with active bombs */
5680 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5683 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5685 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5686 !IS_DIGGABLE(element) && !dynabomb_xl)
5692 void Bang(int x, int y)
5694 int element = MovingOrBlocked2Element(x, y);
5695 int explosion_type = EX_TYPE_NORMAL;
5697 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5699 struct PlayerInfo *player = PLAYERINFO(x, y);
5701 element = Feld[x][y] = player->initial_element;
5703 if (level.use_explosion_element[player->index_nr])
5705 int explosion_element = level.explosion_element[player->index_nr];
5707 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5708 explosion_type = EX_TYPE_CROSS;
5709 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5710 explosion_type = EX_TYPE_CENTER;
5718 case EL_BD_BUTTERFLY:
5721 case EL_DARK_YAMYAM:
5725 RaiseScoreElement(element);
5728 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5729 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5730 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5731 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5732 case EL_DYNABOMB_INCREASE_NUMBER:
5733 case EL_DYNABOMB_INCREASE_SIZE:
5734 case EL_DYNABOMB_INCREASE_POWER:
5735 explosion_type = EX_TYPE_DYNA;
5738 case EL_DC_LANDMINE:
5739 explosion_type = EX_TYPE_CENTER;
5744 case EL_LAMP_ACTIVE:
5745 case EL_AMOEBA_TO_DIAMOND:
5746 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5747 explosion_type = EX_TYPE_CENTER;
5751 if (element_info[element].explosion_type == EXPLODES_CROSS)
5752 explosion_type = EX_TYPE_CROSS;
5753 else if (element_info[element].explosion_type == EXPLODES_1X1)
5754 explosion_type = EX_TYPE_CENTER;
5758 if (explosion_type == EX_TYPE_DYNA)
5761 Explode(x, y, EX_PHASE_START, explosion_type);
5763 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5766 void SplashAcid(int x, int y)
5768 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5769 (!IN_LEV_FIELD(x - 1, y - 2) ||
5770 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5771 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
5778 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5781 static void InitBeltMovement()
5783 static int belt_base_element[4] =
5785 EL_CONVEYOR_BELT_1_LEFT,
5786 EL_CONVEYOR_BELT_2_LEFT,
5787 EL_CONVEYOR_BELT_3_LEFT,
5788 EL_CONVEYOR_BELT_4_LEFT
5790 static int belt_base_active_element[4] =
5792 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5793 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5794 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5795 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5800 /* set frame order for belt animation graphic according to belt direction */
5801 for (i = 0; i < NUM_BELTS; i++)
5805 for (j = 0; j < NUM_BELT_PARTS; j++)
5807 int element = belt_base_active_element[belt_nr] + j;
5808 int graphic_1 = el2img(element);
5809 int graphic_2 = el2panelimg(element);
5811 if (game.belt_dir[i] == MV_LEFT)
5813 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5814 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5818 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5819 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5824 SCAN_PLAYFIELD(x, y)
5826 int element = Feld[x][y];
5828 for (i = 0; i < NUM_BELTS; i++)
5830 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5832 int e_belt_nr = getBeltNrFromBeltElement(element);
5835 if (e_belt_nr == belt_nr)
5837 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5839 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5846 static void ToggleBeltSwitch(int x, int y)
5848 static int belt_base_element[4] =
5850 EL_CONVEYOR_BELT_1_LEFT,
5851 EL_CONVEYOR_BELT_2_LEFT,
5852 EL_CONVEYOR_BELT_3_LEFT,
5853 EL_CONVEYOR_BELT_4_LEFT
5855 static int belt_base_active_element[4] =
5857 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5858 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5859 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5860 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5862 static int belt_base_switch_element[4] =
5864 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5865 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5866 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5867 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5869 static int belt_move_dir[4] =
5877 int element = Feld[x][y];
5878 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5879 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5880 int belt_dir = belt_move_dir[belt_dir_nr];
5883 if (!IS_BELT_SWITCH(element))
5886 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5887 game.belt_dir[belt_nr] = belt_dir;
5889 if (belt_dir_nr == 3)
5892 /* set frame order for belt animation graphic according to belt direction */
5893 for (i = 0; i < NUM_BELT_PARTS; i++)
5895 int element = belt_base_active_element[belt_nr] + i;
5896 int graphic_1 = el2img(element);
5897 int graphic_2 = el2panelimg(element);
5899 if (belt_dir == MV_LEFT)
5901 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5906 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5907 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5911 SCAN_PLAYFIELD(xx, yy)
5913 int element = Feld[xx][yy];
5915 if (IS_BELT_SWITCH(element))
5917 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5919 if (e_belt_nr == belt_nr)
5921 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5922 TEST_DrawLevelField(xx, yy);
5925 else if (IS_BELT(element) && belt_dir != MV_NONE)
5927 int e_belt_nr = getBeltNrFromBeltElement(element);
5929 if (e_belt_nr == belt_nr)
5931 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5933 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5934 TEST_DrawLevelField(xx, yy);
5937 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5939 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5941 if (e_belt_nr == belt_nr)
5943 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5945 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5946 TEST_DrawLevelField(xx, yy);
5952 static void ToggleSwitchgateSwitch(int x, int y)
5956 game.switchgate_pos = !game.switchgate_pos;
5958 SCAN_PLAYFIELD(xx, yy)
5960 int element = Feld[xx][yy];
5962 if (element == EL_SWITCHGATE_SWITCH_UP)
5964 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5965 TEST_DrawLevelField(xx, yy);
5967 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5969 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5970 TEST_DrawLevelField(xx, yy);
5972 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5974 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5975 TEST_DrawLevelField(xx, yy);
5977 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5979 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5980 TEST_DrawLevelField(xx, yy);
5982 else if (element == EL_SWITCHGATE_OPEN ||
5983 element == EL_SWITCHGATE_OPENING)
5985 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5987 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5989 else if (element == EL_SWITCHGATE_CLOSED ||
5990 element == EL_SWITCHGATE_CLOSING)
5992 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5994 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5999 static int getInvisibleActiveFromInvisibleElement(int element)
6001 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6002 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6003 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6007 static int getInvisibleFromInvisibleActiveElement(int element)
6009 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6010 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6011 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6015 static void RedrawAllLightSwitchesAndInvisibleElements()
6019 SCAN_PLAYFIELD(x, y)
6021 int element = Feld[x][y];
6023 if (element == EL_LIGHT_SWITCH &&
6024 game.light_time_left > 0)
6026 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6027 TEST_DrawLevelField(x, y);
6029 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6030 game.light_time_left == 0)
6032 Feld[x][y] = EL_LIGHT_SWITCH;
6033 TEST_DrawLevelField(x, y);
6035 else if (element == EL_EMC_DRIPPER &&
6036 game.light_time_left > 0)
6038 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6039 TEST_DrawLevelField(x, y);
6041 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6042 game.light_time_left == 0)
6044 Feld[x][y] = EL_EMC_DRIPPER;
6045 TEST_DrawLevelField(x, y);
6047 else if (element == EL_INVISIBLE_STEELWALL ||
6048 element == EL_INVISIBLE_WALL ||
6049 element == EL_INVISIBLE_SAND)
6051 if (game.light_time_left > 0)
6052 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6054 TEST_DrawLevelField(x, y);
6056 /* uncrumble neighbour fields, if needed */
6057 if (element == EL_INVISIBLE_SAND)
6058 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6060 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6061 element == EL_INVISIBLE_WALL_ACTIVE ||
6062 element == EL_INVISIBLE_SAND_ACTIVE)
6064 if (game.light_time_left == 0)
6065 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6067 TEST_DrawLevelField(x, y);
6069 /* re-crumble neighbour fields, if needed */
6070 if (element == EL_INVISIBLE_SAND)
6071 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076 static void RedrawAllInvisibleElementsForLenses()
6080 SCAN_PLAYFIELD(x, y)
6082 int element = Feld[x][y];
6084 if (element == EL_EMC_DRIPPER &&
6085 game.lenses_time_left > 0)
6087 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088 TEST_DrawLevelField(x, y);
6090 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091 game.lenses_time_left == 0)
6093 Feld[x][y] = EL_EMC_DRIPPER;
6094 TEST_DrawLevelField(x, y);
6096 else if (element == EL_INVISIBLE_STEELWALL ||
6097 element == EL_INVISIBLE_WALL ||
6098 element == EL_INVISIBLE_SAND)
6100 if (game.lenses_time_left > 0)
6101 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6103 TEST_DrawLevelField(x, y);
6105 /* uncrumble neighbour fields, if needed */
6106 if (element == EL_INVISIBLE_SAND)
6107 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6109 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110 element == EL_INVISIBLE_WALL_ACTIVE ||
6111 element == EL_INVISIBLE_SAND_ACTIVE)
6113 if (game.lenses_time_left == 0)
6114 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6116 TEST_DrawLevelField(x, y);
6118 /* re-crumble neighbour fields, if needed */
6119 if (element == EL_INVISIBLE_SAND)
6120 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6125 static void RedrawAllInvisibleElementsForMagnifier()
6129 SCAN_PLAYFIELD(x, y)
6131 int element = Feld[x][y];
6133 if (element == EL_EMC_FAKE_GRASS &&
6134 game.magnify_time_left > 0)
6136 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6137 TEST_DrawLevelField(x, y);
6139 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6140 game.magnify_time_left == 0)
6142 Feld[x][y] = EL_EMC_FAKE_GRASS;
6143 TEST_DrawLevelField(x, y);
6145 else if (IS_GATE_GRAY(element) &&
6146 game.magnify_time_left > 0)
6148 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6149 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6150 IS_EM_GATE_GRAY(element) ?
6151 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6152 IS_EMC_GATE_GRAY(element) ?
6153 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6154 IS_DC_GATE_GRAY(element) ?
6155 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6157 TEST_DrawLevelField(x, y);
6159 else if (IS_GATE_GRAY_ACTIVE(element) &&
6160 game.magnify_time_left == 0)
6162 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6163 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6164 IS_EM_GATE_GRAY_ACTIVE(element) ?
6165 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6166 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6167 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6168 IS_DC_GATE_GRAY_ACTIVE(element) ?
6169 EL_DC_GATE_WHITE_GRAY :
6171 TEST_DrawLevelField(x, y);
6176 static void ToggleLightSwitch(int x, int y)
6178 int element = Feld[x][y];
6180 game.light_time_left =
6181 (element == EL_LIGHT_SWITCH ?
6182 level.time_light * FRAMES_PER_SECOND : 0);
6184 RedrawAllLightSwitchesAndInvisibleElements();
6187 static void ActivateTimegateSwitch(int x, int y)
6191 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6193 SCAN_PLAYFIELD(xx, yy)
6195 int element = Feld[xx][yy];
6197 if (element == EL_TIMEGATE_CLOSED ||
6198 element == EL_TIMEGATE_CLOSING)
6200 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6201 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6205 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6207 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6208 TEST_DrawLevelField(xx, yy);
6214 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6215 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6218 void Impact(int x, int y)
6220 boolean last_line = (y == lev_fieldy - 1);
6221 boolean object_hit = FALSE;
6222 boolean impact = (last_line || object_hit);
6223 int element = Feld[x][y];
6224 int smashed = EL_STEELWALL;
6226 if (!last_line) /* check if element below was hit */
6228 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6231 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6232 MovDir[x][y + 1] != MV_DOWN ||
6233 MovPos[x][y + 1] <= TILEY / 2));
6235 /* do not smash moving elements that left the smashed field in time */
6236 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6237 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6240 #if USE_QUICKSAND_IMPACT_BUGFIX
6241 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6243 RemoveMovingField(x, y + 1);
6244 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6245 Feld[x][y + 2] = EL_ROCK;
6246 TEST_DrawLevelField(x, y + 2);
6251 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6253 RemoveMovingField(x, y + 1);
6254 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6255 Feld[x][y + 2] = EL_ROCK;
6256 TEST_DrawLevelField(x, y + 2);
6263 smashed = MovingOrBlocked2Element(x, y + 1);
6265 impact = (last_line || object_hit);
6268 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6270 SplashAcid(x, y + 1);
6274 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6275 /* only reset graphic animation if graphic really changes after impact */
6277 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6279 ResetGfxAnimation(x, y);
6280 TEST_DrawLevelField(x, y);
6283 if (impact && CAN_EXPLODE_IMPACT(element))
6288 else if (impact && element == EL_PEARL &&
6289 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6291 ResetGfxAnimation(x, y);
6293 Feld[x][y] = EL_PEARL_BREAKING;
6294 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6297 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6299 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6304 if (impact && element == EL_AMOEBA_DROP)
6306 if (object_hit && IS_PLAYER(x, y + 1))
6307 KillPlayerUnlessEnemyProtected(x, y + 1);
6308 else if (object_hit && smashed == EL_PENGUIN)
6312 Feld[x][y] = EL_AMOEBA_GROWING;
6313 Store[x][y] = EL_AMOEBA_WET;
6315 ResetRandomAnimationValue(x, y);
6320 if (object_hit) /* check which object was hit */
6322 if ((CAN_PASS_MAGIC_WALL(element) &&
6323 (smashed == EL_MAGIC_WALL ||
6324 smashed == EL_BD_MAGIC_WALL)) ||
6325 (CAN_PASS_DC_MAGIC_WALL(element) &&
6326 smashed == EL_DC_MAGIC_WALL))
6329 int activated_magic_wall =
6330 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6331 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6332 EL_DC_MAGIC_WALL_ACTIVE);
6334 /* activate magic wall / mill */
6335 SCAN_PLAYFIELD(xx, yy)
6337 if (Feld[xx][yy] == smashed)
6338 Feld[xx][yy] = activated_magic_wall;
6341 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6342 game.magic_wall_active = TRUE;
6344 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6345 SND_MAGIC_WALL_ACTIVATING :
6346 smashed == EL_BD_MAGIC_WALL ?
6347 SND_BD_MAGIC_WALL_ACTIVATING :
6348 SND_DC_MAGIC_WALL_ACTIVATING));
6351 if (IS_PLAYER(x, y + 1))
6353 if (CAN_SMASH_PLAYER(element))
6355 KillPlayerUnlessEnemyProtected(x, y + 1);
6359 else if (smashed == EL_PENGUIN)
6361 if (CAN_SMASH_PLAYER(element))
6367 else if (element == EL_BD_DIAMOND)
6369 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6375 else if (((element == EL_SP_INFOTRON ||
6376 element == EL_SP_ZONK) &&
6377 (smashed == EL_SP_SNIKSNAK ||
6378 smashed == EL_SP_ELECTRON ||
6379 smashed == EL_SP_DISK_ORANGE)) ||
6380 (element == EL_SP_INFOTRON &&
6381 smashed == EL_SP_DISK_YELLOW))
6386 else if (CAN_SMASH_EVERYTHING(element))
6388 if (IS_CLASSIC_ENEMY(smashed) ||
6389 CAN_EXPLODE_SMASHED(smashed))
6394 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6396 if (smashed == EL_LAMP ||
6397 smashed == EL_LAMP_ACTIVE)
6402 else if (smashed == EL_NUT)
6404 Feld[x][y + 1] = EL_NUT_BREAKING;
6405 PlayLevelSound(x, y, SND_NUT_BREAKING);
6406 RaiseScoreElement(EL_NUT);
6409 else if (smashed == EL_PEARL)
6411 ResetGfxAnimation(x, y);
6413 Feld[x][y + 1] = EL_PEARL_BREAKING;
6414 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6417 else if (smashed == EL_DIAMOND)
6419 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6420 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6423 else if (IS_BELT_SWITCH(smashed))
6425 ToggleBeltSwitch(x, y + 1);
6427 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6428 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6429 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6430 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6432 ToggleSwitchgateSwitch(x, y + 1);
6434 else if (smashed == EL_LIGHT_SWITCH ||
6435 smashed == EL_LIGHT_SWITCH_ACTIVE)
6437 ToggleLightSwitch(x, y + 1);
6441 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6443 CheckElementChangeBySide(x, y + 1, smashed, element,
6444 CE_SWITCHED, CH_SIDE_TOP);
6445 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6451 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6456 /* play sound of magic wall / mill */
6458 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6459 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6460 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6462 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6463 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6464 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6465 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6466 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6467 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6472 /* play sound of object that hits the ground */
6473 if (last_line || object_hit)
6474 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6477 inline static void TurnRoundExt(int x, int y)
6489 { 0, 0 }, { 0, 0 }, { 0, 0 },
6494 int left, right, back;
6498 { MV_DOWN, MV_UP, MV_RIGHT },
6499 { MV_UP, MV_DOWN, MV_LEFT },
6501 { MV_LEFT, MV_RIGHT, MV_DOWN },
6505 { MV_RIGHT, MV_LEFT, MV_UP }
6508 int element = Feld[x][y];
6509 int move_pattern = element_info[element].move_pattern;
6511 int old_move_dir = MovDir[x][y];
6512 int left_dir = turn[old_move_dir].left;
6513 int right_dir = turn[old_move_dir].right;
6514 int back_dir = turn[old_move_dir].back;
6516 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6517 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6518 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6519 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6521 int left_x = x + left_dx, left_y = y + left_dy;
6522 int right_x = x + right_dx, right_y = y + right_dy;
6523 int move_x = x + move_dx, move_y = y + move_dy;
6527 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6529 TestIfBadThingTouchesOtherBadThing(x, y);
6531 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6532 MovDir[x][y] = right_dir;
6533 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6534 MovDir[x][y] = left_dir;
6536 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6538 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6541 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6543 TestIfBadThingTouchesOtherBadThing(x, y);
6545 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6546 MovDir[x][y] = left_dir;
6547 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6548 MovDir[x][y] = right_dir;
6550 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6552 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6555 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6557 TestIfBadThingTouchesOtherBadThing(x, y);
6559 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6560 MovDir[x][y] = left_dir;
6561 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6562 MovDir[x][y] = right_dir;
6564 if (MovDir[x][y] != old_move_dir)
6567 else if (element == EL_YAMYAM)
6569 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6570 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6572 if (can_turn_left && can_turn_right)
6573 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6574 else if (can_turn_left)
6575 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6576 else if (can_turn_right)
6577 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6579 MovDir[x][y] = back_dir;
6581 MovDelay[x][y] = 16 + 16 * RND(3);
6583 else if (element == EL_DARK_YAMYAM)
6585 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6587 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6590 if (can_turn_left && can_turn_right)
6591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6592 else if (can_turn_left)
6593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6594 else if (can_turn_right)
6595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6597 MovDir[x][y] = back_dir;
6599 MovDelay[x][y] = 16 + 16 * RND(3);
6601 else if (element == EL_PACMAN)
6603 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6604 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6606 if (can_turn_left && can_turn_right)
6607 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608 else if (can_turn_left)
6609 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610 else if (can_turn_right)
6611 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6613 MovDir[x][y] = back_dir;
6615 MovDelay[x][y] = 6 + RND(40);
6617 else if (element == EL_PIG)
6619 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6620 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6621 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6622 boolean should_turn_left, should_turn_right, should_move_on;
6624 int rnd = RND(rnd_value);
6626 should_turn_left = (can_turn_left &&
6628 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6629 y + back_dy + left_dy)));
6630 should_turn_right = (can_turn_right &&
6632 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6633 y + back_dy + right_dy)));
6634 should_move_on = (can_move_on &&
6637 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6638 y + move_dy + left_dy) ||
6639 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6640 y + move_dy + right_dy)));
6642 if (should_turn_left || should_turn_right || should_move_on)
6644 if (should_turn_left && should_turn_right && should_move_on)
6645 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6646 rnd < 2 * rnd_value / 3 ? right_dir :
6648 else if (should_turn_left && should_turn_right)
6649 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6650 else if (should_turn_left && should_move_on)
6651 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6652 else if (should_turn_right && should_move_on)
6653 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6654 else if (should_turn_left)
6655 MovDir[x][y] = left_dir;
6656 else if (should_turn_right)
6657 MovDir[x][y] = right_dir;
6658 else if (should_move_on)
6659 MovDir[x][y] = old_move_dir;
6661 else if (can_move_on && rnd > rnd_value / 8)
6662 MovDir[x][y] = old_move_dir;
6663 else if (can_turn_left && can_turn_right)
6664 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6665 else if (can_turn_left && rnd > rnd_value / 8)
6666 MovDir[x][y] = left_dir;
6667 else if (can_turn_right && rnd > rnd_value/8)
6668 MovDir[x][y] = right_dir;
6670 MovDir[x][y] = back_dir;
6672 xx = x + move_xy[MovDir[x][y]].dx;
6673 yy = y + move_xy[MovDir[x][y]].dy;
6675 if (!IN_LEV_FIELD(xx, yy) ||
6676 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6677 MovDir[x][y] = old_move_dir;
6681 else if (element == EL_DRAGON)
6683 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6684 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6685 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6687 int rnd = RND(rnd_value);
6689 if (can_move_on && rnd > rnd_value / 8)
6690 MovDir[x][y] = old_move_dir;
6691 else if (can_turn_left && can_turn_right)
6692 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6693 else if (can_turn_left && rnd > rnd_value / 8)
6694 MovDir[x][y] = left_dir;
6695 else if (can_turn_right && rnd > rnd_value / 8)
6696 MovDir[x][y] = right_dir;
6698 MovDir[x][y] = back_dir;
6700 xx = x + move_xy[MovDir[x][y]].dx;
6701 yy = y + move_xy[MovDir[x][y]].dy;
6703 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6704 MovDir[x][y] = old_move_dir;
6708 else if (element == EL_MOLE)
6710 boolean can_move_on =
6711 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6712 IS_AMOEBOID(Feld[move_x][move_y]) ||
6713 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6716 boolean can_turn_left =
6717 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6718 IS_AMOEBOID(Feld[left_x][left_y])));
6720 boolean can_turn_right =
6721 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6722 IS_AMOEBOID(Feld[right_x][right_y])));
6724 if (can_turn_left && can_turn_right)
6725 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6726 else if (can_turn_left)
6727 MovDir[x][y] = left_dir;
6729 MovDir[x][y] = right_dir;
6732 if (MovDir[x][y] != old_move_dir)
6735 else if (element == EL_BALLOON)
6737 MovDir[x][y] = game.wind_direction;
6740 else if (element == EL_SPRING)
6742 if (MovDir[x][y] & MV_HORIZONTAL)
6744 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6745 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6747 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6748 ResetGfxAnimation(move_x, move_y);
6749 TEST_DrawLevelField(move_x, move_y);
6751 MovDir[x][y] = back_dir;
6753 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6754 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6755 MovDir[x][y] = MV_NONE;
6760 else if (element == EL_ROBOT ||
6761 element == EL_SATELLITE ||
6762 element == EL_PENGUIN ||
6763 element == EL_EMC_ANDROID)
6765 int attr_x = -1, attr_y = -1;
6776 for (i = 0; i < MAX_PLAYERS; i++)
6778 struct PlayerInfo *player = &stored_player[i];
6779 int jx = player->jx, jy = player->jy;
6781 if (!player->active)
6785 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6793 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6794 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6795 game.engine_version < VERSION_IDENT(3,1,0,0)))
6801 if (element == EL_PENGUIN)
6804 static int xy[4][2] =
6812 for (i = 0; i < NUM_DIRECTIONS; i++)
6814 int ex = x + xy[i][0];
6815 int ey = y + xy[i][1];
6817 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6818 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6819 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6820 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6829 MovDir[x][y] = MV_NONE;
6831 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6832 else if (attr_x > x)
6833 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6835 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6836 else if (attr_y > y)
6837 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6839 if (element == EL_ROBOT)
6843 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6844 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6845 Moving2Blocked(x, y, &newx, &newy);
6847 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6848 MovDelay[x][y] = 8 + 8 * !RND(3);
6850 MovDelay[x][y] = 16;
6852 else if (element == EL_PENGUIN)
6858 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6860 boolean first_horiz = RND(2);
6861 int new_move_dir = MovDir[x][y];
6864 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6865 Moving2Blocked(x, y, &newx, &newy);
6867 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6871 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6872 Moving2Blocked(x, y, &newx, &newy);
6874 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6877 MovDir[x][y] = old_move_dir;
6881 else if (element == EL_SATELLITE)
6887 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6889 boolean first_horiz = RND(2);
6890 int new_move_dir = MovDir[x][y];
6893 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894 Moving2Blocked(x, y, &newx, &newy);
6896 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6900 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6901 Moving2Blocked(x, y, &newx, &newy);
6903 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6906 MovDir[x][y] = old_move_dir;
6910 else if (element == EL_EMC_ANDROID)
6912 static int check_pos[16] =
6914 -1, /* 0 => (invalid) */
6915 7, /* 1 => MV_LEFT */
6916 3, /* 2 => MV_RIGHT */
6917 -1, /* 3 => (invalid) */
6919 0, /* 5 => MV_LEFT | MV_UP */
6920 2, /* 6 => MV_RIGHT | MV_UP */
6921 -1, /* 7 => (invalid) */
6922 5, /* 8 => MV_DOWN */
6923 6, /* 9 => MV_LEFT | MV_DOWN */
6924 4, /* 10 => MV_RIGHT | MV_DOWN */
6925 -1, /* 11 => (invalid) */
6926 -1, /* 12 => (invalid) */
6927 -1, /* 13 => (invalid) */
6928 -1, /* 14 => (invalid) */
6929 -1, /* 15 => (invalid) */
6937 { -1, -1, MV_LEFT | MV_UP },
6939 { +1, -1, MV_RIGHT | MV_UP },
6940 { +1, 0, MV_RIGHT },
6941 { +1, +1, MV_RIGHT | MV_DOWN },
6943 { -1, +1, MV_LEFT | MV_DOWN },
6946 int start_pos, check_order;
6947 boolean can_clone = FALSE;
6950 /* check if there is any free field around current position */
6951 for (i = 0; i < 8; i++)
6953 int newx = x + check_xy[i].dx;
6954 int newy = y + check_xy[i].dy;
6956 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6964 if (can_clone) /* randomly find an element to clone */
6968 start_pos = check_pos[RND(8)];
6969 check_order = (RND(2) ? -1 : +1);
6971 for (i = 0; i < 8; i++)
6973 int pos_raw = start_pos + i * check_order;
6974 int pos = (pos_raw + 8) % 8;
6975 int newx = x + check_xy[pos].dx;
6976 int newy = y + check_xy[pos].dy;
6978 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6980 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6981 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6983 Store[x][y] = Feld[newx][newy];
6992 if (can_clone) /* randomly find a direction to move */
6996 start_pos = check_pos[RND(8)];
6997 check_order = (RND(2) ? -1 : +1);
6999 for (i = 0; i < 8; i++)
7001 int pos_raw = start_pos + i * check_order;
7002 int pos = (pos_raw + 8) % 8;
7003 int newx = x + check_xy[pos].dx;
7004 int newy = y + check_xy[pos].dy;
7005 int new_move_dir = check_xy[pos].dir;
7007 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7009 MovDir[x][y] = new_move_dir;
7010 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7019 if (can_clone) /* cloning and moving successful */
7022 /* cannot clone -- try to move towards player */
7024 start_pos = check_pos[MovDir[x][y] & 0x0f];
7025 check_order = (RND(2) ? -1 : +1);
7027 for (i = 0; i < 3; i++)
7029 /* first check start_pos, then previous/next or (next/previous) pos */
7030 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7031 int pos = (pos_raw + 8) % 8;
7032 int newx = x + check_xy[pos].dx;
7033 int newy = y + check_xy[pos].dy;
7034 int new_move_dir = check_xy[pos].dir;
7036 if (IS_PLAYER(newx, newy))
7039 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7041 MovDir[x][y] = new_move_dir;
7042 MovDelay[x][y] = level.android_move_time * 8 + 1;
7049 else if (move_pattern == MV_TURNING_LEFT ||
7050 move_pattern == MV_TURNING_RIGHT ||
7051 move_pattern == MV_TURNING_LEFT_RIGHT ||
7052 move_pattern == MV_TURNING_RIGHT_LEFT ||
7053 move_pattern == MV_TURNING_RANDOM ||
7054 move_pattern == MV_ALL_DIRECTIONS)
7056 boolean can_turn_left =
7057 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7058 boolean can_turn_right =
7059 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7061 if (element_info[element].move_stepsize == 0) /* "not moving" */
7064 if (move_pattern == MV_TURNING_LEFT)
7065 MovDir[x][y] = left_dir;
7066 else if (move_pattern == MV_TURNING_RIGHT)
7067 MovDir[x][y] = right_dir;
7068 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7069 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7070 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7071 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7072 else if (move_pattern == MV_TURNING_RANDOM)
7073 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7074 can_turn_right && !can_turn_left ? right_dir :
7075 RND(2) ? left_dir : right_dir);
7076 else if (can_turn_left && can_turn_right)
7077 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7078 else if (can_turn_left)
7079 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7080 else if (can_turn_right)
7081 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7083 MovDir[x][y] = back_dir;
7085 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7087 else if (move_pattern == MV_HORIZONTAL ||
7088 move_pattern == MV_VERTICAL)
7090 if (move_pattern & old_move_dir)
7091 MovDir[x][y] = back_dir;
7092 else if (move_pattern == MV_HORIZONTAL)
7093 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7094 else if (move_pattern == MV_VERTICAL)
7095 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7097 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7099 else if (move_pattern & MV_ANY_DIRECTION)
7101 MovDir[x][y] = move_pattern;
7102 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7104 else if (move_pattern & MV_WIND_DIRECTION)
7106 MovDir[x][y] = game.wind_direction;
7107 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7109 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7111 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7112 MovDir[x][y] = left_dir;
7113 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7114 MovDir[x][y] = right_dir;
7116 if (MovDir[x][y] != old_move_dir)
7117 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7119 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7121 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7122 MovDir[x][y] = right_dir;
7123 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7124 MovDir[x][y] = left_dir;
7126 if (MovDir[x][y] != old_move_dir)
7127 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7129 else if (move_pattern == MV_TOWARDS_PLAYER ||
7130 move_pattern == MV_AWAY_FROM_PLAYER)
7132 int attr_x = -1, attr_y = -1;
7134 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7145 for (i = 0; i < MAX_PLAYERS; i++)
7147 struct PlayerInfo *player = &stored_player[i];
7148 int jx = player->jx, jy = player->jy;
7150 if (!player->active)
7154 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7162 MovDir[x][y] = MV_NONE;
7164 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7165 else if (attr_x > x)
7166 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7168 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7169 else if (attr_y > y)
7170 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7176 boolean first_horiz = RND(2);
7177 int new_move_dir = MovDir[x][y];
7179 if (element_info[element].move_stepsize == 0) /* "not moving" */
7181 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7182 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7188 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7189 Moving2Blocked(x, y, &newx, &newy);
7191 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7195 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196 Moving2Blocked(x, y, &newx, &newy);
7198 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7201 MovDir[x][y] = old_move_dir;
7204 else if (move_pattern == MV_WHEN_PUSHED ||
7205 move_pattern == MV_WHEN_DROPPED)
7207 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7208 MovDir[x][y] = MV_NONE;
7212 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7214 static int test_xy[7][2] =
7224 static int test_dir[7] =
7234 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7235 int move_preference = -1000000; /* start with very low preference */
7236 int new_move_dir = MV_NONE;
7237 int start_test = RND(4);
7240 for (i = 0; i < NUM_DIRECTIONS; i++)
7242 int move_dir = test_dir[start_test + i];
7243 int move_dir_preference;
7245 xx = x + test_xy[start_test + i][0];
7246 yy = y + test_xy[start_test + i][1];
7248 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7249 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7251 new_move_dir = move_dir;
7256 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7259 move_dir_preference = -1 * RunnerVisit[xx][yy];
7260 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7261 move_dir_preference = PlayerVisit[xx][yy];
7263 if (move_dir_preference > move_preference)
7265 /* prefer field that has not been visited for the longest time */
7266 move_preference = move_dir_preference;
7267 new_move_dir = move_dir;
7269 else if (move_dir_preference == move_preference &&
7270 move_dir == old_move_dir)
7272 /* prefer last direction when all directions are preferred equally */
7273 move_preference = move_dir_preference;
7274 new_move_dir = move_dir;
7278 MovDir[x][y] = new_move_dir;
7279 if (old_move_dir != new_move_dir)
7280 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7284 static void TurnRound(int x, int y)
7286 int direction = MovDir[x][y];
7290 GfxDir[x][y] = MovDir[x][y];
7292 if (direction != MovDir[x][y])
7296 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7298 ResetGfxFrame(x, y);
7301 static boolean JustBeingPushed(int x, int y)
7305 for (i = 0; i < MAX_PLAYERS; i++)
7307 struct PlayerInfo *player = &stored_player[i];
7309 if (player->active && player->is_pushing && player->MovPos)
7311 int next_jx = player->jx + (player->jx - player->last_jx);
7312 int next_jy = player->jy + (player->jy - player->last_jy);
7314 if (x == next_jx && y == next_jy)
7322 void StartMoving(int x, int y)
7324 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7325 int element = Feld[x][y];
7330 if (MovDelay[x][y] == 0)
7331 GfxAction[x][y] = ACTION_DEFAULT;
7333 if (CAN_FALL(element) && y < lev_fieldy - 1)
7335 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7336 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7337 if (JustBeingPushed(x, y))
7340 if (element == EL_QUICKSAND_FULL)
7342 if (IS_FREE(x, y + 1))
7344 InitMovingField(x, y, MV_DOWN);
7345 started_moving = TRUE;
7347 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7348 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7349 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7350 Store[x][y] = EL_ROCK;
7352 Store[x][y] = EL_ROCK;
7355 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7357 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7359 if (!MovDelay[x][y])
7361 MovDelay[x][y] = TILEY + 1;
7363 ResetGfxAnimation(x, y);
7364 ResetGfxAnimation(x, y + 1);
7369 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7370 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7377 Feld[x][y] = EL_QUICKSAND_EMPTY;
7378 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7379 Store[x][y + 1] = Store[x][y];
7382 PlayLevelSoundAction(x, y, ACTION_FILLING);
7384 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7386 if (!MovDelay[x][y])
7388 MovDelay[x][y] = TILEY + 1;
7390 ResetGfxAnimation(x, y);
7391 ResetGfxAnimation(x, y + 1);
7396 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7397 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7404 Feld[x][y] = EL_QUICKSAND_EMPTY;
7405 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406 Store[x][y + 1] = Store[x][y];
7409 PlayLevelSoundAction(x, y, ACTION_FILLING);
7412 else if (element == EL_QUICKSAND_FAST_FULL)
7414 if (IS_FREE(x, y + 1))
7416 InitMovingField(x, y, MV_DOWN);
7417 started_moving = TRUE;
7419 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7420 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7421 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7422 Store[x][y] = EL_ROCK;
7424 Store[x][y] = EL_ROCK;
7427 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7429 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7431 if (!MovDelay[x][y])
7433 MovDelay[x][y] = TILEY + 1;
7435 ResetGfxAnimation(x, y);
7436 ResetGfxAnimation(x, y + 1);
7441 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7442 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7449 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7450 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7451 Store[x][y + 1] = Store[x][y];
7454 PlayLevelSoundAction(x, y, ACTION_FILLING);
7456 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7458 if (!MovDelay[x][y])
7460 MovDelay[x][y] = TILEY + 1;
7462 ResetGfxAnimation(x, y);
7463 ResetGfxAnimation(x, y + 1);
7468 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7469 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7476 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7477 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7478 Store[x][y + 1] = Store[x][y];
7481 PlayLevelSoundAction(x, y, ACTION_FILLING);
7484 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7485 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7487 InitMovingField(x, y, MV_DOWN);
7488 started_moving = TRUE;
7490 Feld[x][y] = EL_QUICKSAND_FILLING;
7491 Store[x][y] = element;
7493 PlayLevelSoundAction(x, y, ACTION_FILLING);
7495 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7496 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7498 InitMovingField(x, y, MV_DOWN);
7499 started_moving = TRUE;
7501 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7502 Store[x][y] = element;
7504 PlayLevelSoundAction(x, y, ACTION_FILLING);
7506 else if (element == EL_MAGIC_WALL_FULL)
7508 if (IS_FREE(x, y + 1))
7510 InitMovingField(x, y, MV_DOWN);
7511 started_moving = TRUE;
7513 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7514 Store[x][y] = EL_CHANGED(Store[x][y]);
7516 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7518 if (!MovDelay[x][y])
7519 MovDelay[x][y] = TILEY / 4 + 1;
7528 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7529 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7530 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7534 else if (element == EL_BD_MAGIC_WALL_FULL)
7536 if (IS_FREE(x, y + 1))
7538 InitMovingField(x, y, MV_DOWN);
7539 started_moving = TRUE;
7541 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7542 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7544 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7546 if (!MovDelay[x][y])
7547 MovDelay[x][y] = TILEY / 4 + 1;
7556 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7557 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7558 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7562 else if (element == EL_DC_MAGIC_WALL_FULL)
7564 if (IS_FREE(x, y + 1))
7566 InitMovingField(x, y, MV_DOWN);
7567 started_moving = TRUE;
7569 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7570 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7572 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7574 if (!MovDelay[x][y])
7575 MovDelay[x][y] = TILEY / 4 + 1;
7584 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7585 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7586 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7590 else if ((CAN_PASS_MAGIC_WALL(element) &&
7591 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7592 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7593 (CAN_PASS_DC_MAGIC_WALL(element) &&
7594 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7597 InitMovingField(x, y, MV_DOWN);
7598 started_moving = TRUE;
7601 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7602 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7603 EL_DC_MAGIC_WALL_FILLING);
7604 Store[x][y] = element;
7606 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7608 SplashAcid(x, y + 1);
7610 InitMovingField(x, y, MV_DOWN);
7611 started_moving = TRUE;
7613 Store[x][y] = EL_ACID;
7616 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7617 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7618 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7619 CAN_FALL(element) && WasJustFalling[x][y] &&
7620 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7622 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7623 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7624 (Feld[x][y + 1] == EL_BLOCKED)))
7626 /* this is needed for a special case not covered by calling "Impact()"
7627 from "ContinueMoving()": if an element moves to a tile directly below
7628 another element which was just falling on that tile (which was empty
7629 in the previous frame), the falling element above would just stop
7630 instead of smashing the element below (in previous version, the above
7631 element was just checked for "moving" instead of "falling", resulting
7632 in incorrect smashes caused by horizontal movement of the above
7633 element; also, the case of the player being the element to smash was
7634 simply not covered here... :-/ ) */
7636 CheckCollision[x][y] = 0;
7637 CheckImpact[x][y] = 0;
7641 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7643 if (MovDir[x][y] == MV_NONE)
7645 InitMovingField(x, y, MV_DOWN);
7646 started_moving = TRUE;
7649 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7651 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7652 MovDir[x][y] = MV_DOWN;
7654 InitMovingField(x, y, MV_DOWN);
7655 started_moving = TRUE;
7657 else if (element == EL_AMOEBA_DROP)
7659 Feld[x][y] = EL_AMOEBA_GROWING;
7660 Store[x][y] = EL_AMOEBA_WET;
7662 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7663 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7664 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7665 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7667 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7668 (IS_FREE(x - 1, y + 1) ||
7669 Feld[x - 1][y + 1] == EL_ACID));
7670 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7671 (IS_FREE(x + 1, y + 1) ||
7672 Feld[x + 1][y + 1] == EL_ACID));
7673 boolean can_fall_any = (can_fall_left || can_fall_right);
7674 boolean can_fall_both = (can_fall_left && can_fall_right);
7675 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7677 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7679 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7680 can_fall_right = FALSE;
7681 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7682 can_fall_left = FALSE;
7683 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7684 can_fall_right = FALSE;
7685 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7686 can_fall_left = FALSE;
7688 can_fall_any = (can_fall_left || can_fall_right);
7689 can_fall_both = FALSE;
7694 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7695 can_fall_right = FALSE; /* slip down on left side */
7697 can_fall_left = !(can_fall_right = RND(2));
7699 can_fall_both = FALSE;
7704 /* if not determined otherwise, prefer left side for slipping down */
7705 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7706 started_moving = TRUE;
7709 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7711 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7712 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7713 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7714 int belt_dir = game.belt_dir[belt_nr];
7716 if ((belt_dir == MV_LEFT && left_is_free) ||
7717 (belt_dir == MV_RIGHT && right_is_free))
7719 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7721 InitMovingField(x, y, belt_dir);
7722 started_moving = TRUE;
7724 Pushed[x][y] = TRUE;
7725 Pushed[nextx][y] = TRUE;
7727 GfxAction[x][y] = ACTION_DEFAULT;
7731 MovDir[x][y] = 0; /* if element was moving, stop it */
7736 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7737 if (CAN_MOVE(element) && !started_moving)
7739 int move_pattern = element_info[element].move_pattern;
7742 Moving2Blocked(x, y, &newx, &newy);
7744 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7747 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7748 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7750 WasJustMoving[x][y] = 0;
7751 CheckCollision[x][y] = 0;
7753 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7755 if (Feld[x][y] != element) /* element has changed */
7759 if (!MovDelay[x][y]) /* start new movement phase */
7761 /* all objects that can change their move direction after each step
7762 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7764 if (element != EL_YAMYAM &&
7765 element != EL_DARK_YAMYAM &&
7766 element != EL_PACMAN &&
7767 !(move_pattern & MV_ANY_DIRECTION) &&
7768 move_pattern != MV_TURNING_LEFT &&
7769 move_pattern != MV_TURNING_RIGHT &&
7770 move_pattern != MV_TURNING_LEFT_RIGHT &&
7771 move_pattern != MV_TURNING_RIGHT_LEFT &&
7772 move_pattern != MV_TURNING_RANDOM)
7776 if (MovDelay[x][y] && (element == EL_BUG ||
7777 element == EL_SPACESHIP ||
7778 element == EL_SP_SNIKSNAK ||
7779 element == EL_SP_ELECTRON ||
7780 element == EL_MOLE))
7781 TEST_DrawLevelField(x, y);
7785 if (MovDelay[x][y]) /* wait some time before next movement */
7789 if (element == EL_ROBOT ||
7790 element == EL_YAMYAM ||
7791 element == EL_DARK_YAMYAM)
7793 DrawLevelElementAnimationIfNeeded(x, y, element);
7794 PlayLevelSoundAction(x, y, ACTION_WAITING);
7796 else if (element == EL_SP_ELECTRON)
7797 DrawLevelElementAnimationIfNeeded(x, y, element);
7798 else if (element == EL_DRAGON)
7801 int dir = MovDir[x][y];
7802 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7803 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7804 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7805 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7806 dir == MV_UP ? IMG_FLAMES_1_UP :
7807 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7808 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7810 GfxAction[x][y] = ACTION_ATTACKING;
7812 if (IS_PLAYER(x, y))
7813 DrawPlayerField(x, y);
7815 TEST_DrawLevelField(x, y);
7817 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7819 for (i = 1; i <= 3; i++)
7821 int xx = x + i * dx;
7822 int yy = y + i * dy;
7823 int sx = SCREENX(xx);
7824 int sy = SCREENY(yy);
7825 int flame_graphic = graphic + (i - 1);
7827 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7832 int flamed = MovingOrBlocked2Element(xx, yy);
7834 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7837 RemoveMovingField(xx, yy);
7839 ChangeDelay[xx][yy] = 0;
7841 Feld[xx][yy] = EL_FLAMES;
7843 if (IN_SCR_FIELD(sx, sy))
7845 TEST_DrawLevelFieldCrumbled(xx, yy);
7846 DrawGraphic(sx, sy, flame_graphic, frame);
7851 if (Feld[xx][yy] == EL_FLAMES)
7852 Feld[xx][yy] = EL_EMPTY;
7853 TEST_DrawLevelField(xx, yy);
7858 if (MovDelay[x][y]) /* element still has to wait some time */
7860 PlayLevelSoundAction(x, y, ACTION_WAITING);
7866 /* now make next step */
7868 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7870 if (DONT_COLLIDE_WITH(element) &&
7871 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7872 !PLAYER_ENEMY_PROTECTED(newx, newy))
7874 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7879 else if (CAN_MOVE_INTO_ACID(element) &&
7880 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7881 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7882 (MovDir[x][y] == MV_DOWN ||
7883 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7885 SplashAcid(newx, newy);
7886 Store[x][y] = EL_ACID;
7888 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7890 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7891 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7892 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7893 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7896 TEST_DrawLevelField(x, y);
7898 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7899 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7900 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7902 local_player->friends_still_needed--;
7903 if (!local_player->friends_still_needed &&
7904 !local_player->GameOver && AllPlayersGone)
7905 PlayerWins(local_player);
7909 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7911 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7912 TEST_DrawLevelField(newx, newy);
7914 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7916 else if (!IS_FREE(newx, newy))
7918 GfxAction[x][y] = ACTION_WAITING;
7920 if (IS_PLAYER(x, y))
7921 DrawPlayerField(x, y);
7923 TEST_DrawLevelField(x, y);
7928 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7930 if (IS_FOOD_PIG(Feld[newx][newy]))
7932 if (IS_MOVING(newx, newy))
7933 RemoveMovingField(newx, newy);
7936 Feld[newx][newy] = EL_EMPTY;
7937 TEST_DrawLevelField(newx, newy);
7940 PlayLevelSound(x, y, SND_PIG_DIGGING);
7942 else if (!IS_FREE(newx, newy))
7944 if (IS_PLAYER(x, y))
7945 DrawPlayerField(x, y);
7947 TEST_DrawLevelField(x, y);
7952 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7954 if (Store[x][y] != EL_EMPTY)
7956 boolean can_clone = FALSE;
7959 /* check if element to clone is still there */
7960 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7962 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7970 /* cannot clone or target field not free anymore -- do not clone */
7971 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7972 Store[x][y] = EL_EMPTY;
7975 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7977 if (IS_MV_DIAGONAL(MovDir[x][y]))
7979 int diagonal_move_dir = MovDir[x][y];
7980 int stored = Store[x][y];
7981 int change_delay = 8;
7984 /* android is moving diagonally */
7986 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7988 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7989 GfxElement[x][y] = EL_EMC_ANDROID;
7990 GfxAction[x][y] = ACTION_SHRINKING;
7991 GfxDir[x][y] = diagonal_move_dir;
7992 ChangeDelay[x][y] = change_delay;
7994 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7997 DrawLevelGraphicAnimation(x, y, graphic);
7998 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8000 if (Feld[newx][newy] == EL_ACID)
8002 SplashAcid(newx, newy);
8007 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8009 Store[newx][newy] = EL_EMC_ANDROID;
8010 GfxElement[newx][newy] = EL_EMC_ANDROID;
8011 GfxAction[newx][newy] = ACTION_GROWING;
8012 GfxDir[newx][newy] = diagonal_move_dir;
8013 ChangeDelay[newx][newy] = change_delay;
8015 graphic = el_act_dir2img(GfxElement[newx][newy],
8016 GfxAction[newx][newy], GfxDir[newx][newy]);
8018 DrawLevelGraphicAnimation(newx, newy, graphic);
8019 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8025 Feld[newx][newy] = EL_EMPTY;
8026 TEST_DrawLevelField(newx, newy);
8028 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8031 else if (!IS_FREE(newx, newy))
8036 else if (IS_CUSTOM_ELEMENT(element) &&
8037 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8039 if (!DigFieldByCE(newx, newy, element))
8042 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8044 RunnerVisit[x][y] = FrameCounter;
8045 PlayerVisit[x][y] /= 8; /* expire player visit path */
8048 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8050 if (!IS_FREE(newx, newy))
8052 if (IS_PLAYER(x, y))
8053 DrawPlayerField(x, y);
8055 TEST_DrawLevelField(x, y);
8061 boolean wanna_flame = !RND(10);
8062 int dx = newx - x, dy = newy - y;
8063 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8064 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8065 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8066 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8067 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8068 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8071 IS_CLASSIC_ENEMY(element1) ||
8072 IS_CLASSIC_ENEMY(element2)) &&
8073 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8074 element1 != EL_FLAMES && element2 != EL_FLAMES)
8076 ResetGfxAnimation(x, y);
8077 GfxAction[x][y] = ACTION_ATTACKING;
8079 if (IS_PLAYER(x, y))
8080 DrawPlayerField(x, y);
8082 TEST_DrawLevelField(x, y);
8084 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8086 MovDelay[x][y] = 50;
8088 Feld[newx][newy] = EL_FLAMES;
8089 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8090 Feld[newx1][newy1] = EL_FLAMES;
8091 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8092 Feld[newx2][newy2] = EL_FLAMES;
8098 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8099 Feld[newx][newy] == EL_DIAMOND)
8101 if (IS_MOVING(newx, newy))
8102 RemoveMovingField(newx, newy);
8105 Feld[newx][newy] = EL_EMPTY;
8106 TEST_DrawLevelField(newx, newy);
8109 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8111 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8112 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8114 if (AmoebaNr[newx][newy])
8116 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8117 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8118 Feld[newx][newy] == EL_BD_AMOEBA)
8119 AmoebaCnt[AmoebaNr[newx][newy]]--;
8122 if (IS_MOVING(newx, newy))
8124 RemoveMovingField(newx, newy);
8128 Feld[newx][newy] = EL_EMPTY;
8129 TEST_DrawLevelField(newx, newy);
8132 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8134 else if ((element == EL_PACMAN || element == EL_MOLE)
8135 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8137 if (AmoebaNr[newx][newy])
8139 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141 Feld[newx][newy] == EL_BD_AMOEBA)
8142 AmoebaCnt[AmoebaNr[newx][newy]]--;
8145 if (element == EL_MOLE)
8147 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8148 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8150 ResetGfxAnimation(x, y);
8151 GfxAction[x][y] = ACTION_DIGGING;
8152 TEST_DrawLevelField(x, y);
8154 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8156 return; /* wait for shrinking amoeba */
8158 else /* element == EL_PACMAN */
8160 Feld[newx][newy] = EL_EMPTY;
8161 TEST_DrawLevelField(newx, newy);
8162 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8165 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8166 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8167 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8169 /* wait for shrinking amoeba to completely disappear */
8172 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8174 /* object was running against a wall */
8178 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8179 DrawLevelElementAnimation(x, y, element);
8181 if (DONT_TOUCH(element))
8182 TestIfBadThingTouchesPlayer(x, y);
8187 InitMovingField(x, y, MovDir[x][y]);
8189 PlayLevelSoundAction(x, y, ACTION_MOVING);
8193 ContinueMoving(x, y);
8196 void ContinueMoving(int x, int y)
8198 int element = Feld[x][y];
8199 struct ElementInfo *ei = &element_info[element];
8200 int direction = MovDir[x][y];
8201 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8202 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8203 int newx = x + dx, newy = y + dy;
8204 int stored = Store[x][y];
8205 int stored_new = Store[newx][newy];
8206 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8207 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8208 boolean last_line = (newy == lev_fieldy - 1);
8210 MovPos[x][y] += getElementMoveStepsize(x, y);
8212 if (pushed_by_player) /* special case: moving object pushed by player */
8213 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8215 if (ABS(MovPos[x][y]) < TILEX)
8217 TEST_DrawLevelField(x, y);
8219 return; /* element is still moving */
8222 /* element reached destination field */
8224 Feld[x][y] = EL_EMPTY;
8225 Feld[newx][newy] = element;
8226 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8228 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8230 element = Feld[newx][newy] = EL_ACID;
8232 else if (element == EL_MOLE)
8234 Feld[x][y] = EL_SAND;
8236 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8238 else if (element == EL_QUICKSAND_FILLING)
8240 element = Feld[newx][newy] = get_next_element(element);
8241 Store[newx][newy] = Store[x][y];
8243 else if (element == EL_QUICKSAND_EMPTYING)
8245 Feld[x][y] = get_next_element(element);
8246 element = Feld[newx][newy] = Store[x][y];
8248 else if (element == EL_QUICKSAND_FAST_FILLING)
8250 element = Feld[newx][newy] = get_next_element(element);
8251 Store[newx][newy] = Store[x][y];
8253 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8255 Feld[x][y] = get_next_element(element);
8256 element = Feld[newx][newy] = Store[x][y];
8258 else if (element == EL_MAGIC_WALL_FILLING)
8260 element = Feld[newx][newy] = get_next_element(element);
8261 if (!game.magic_wall_active)
8262 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8263 Store[newx][newy] = Store[x][y];
8265 else if (element == EL_MAGIC_WALL_EMPTYING)
8267 Feld[x][y] = get_next_element(element);
8268 if (!game.magic_wall_active)
8269 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8270 element = Feld[newx][newy] = Store[x][y];
8272 InitField(newx, newy, FALSE);
8274 else if (element == EL_BD_MAGIC_WALL_FILLING)
8276 element = Feld[newx][newy] = get_next_element(element);
8277 if (!game.magic_wall_active)
8278 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8279 Store[newx][newy] = Store[x][y];
8281 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8283 Feld[x][y] = get_next_element(element);
8284 if (!game.magic_wall_active)
8285 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8286 element = Feld[newx][newy] = Store[x][y];
8288 InitField(newx, newy, FALSE);
8290 else if (element == EL_DC_MAGIC_WALL_FILLING)
8292 element = Feld[newx][newy] = get_next_element(element);
8293 if (!game.magic_wall_active)
8294 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8295 Store[newx][newy] = Store[x][y];
8297 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8299 Feld[x][y] = get_next_element(element);
8300 if (!game.magic_wall_active)
8301 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8302 element = Feld[newx][newy] = Store[x][y];
8304 InitField(newx, newy, FALSE);
8306 else if (element == EL_AMOEBA_DROPPING)
8308 Feld[x][y] = get_next_element(element);
8309 element = Feld[newx][newy] = Store[x][y];
8311 else if (element == EL_SOKOBAN_OBJECT)
8314 Feld[x][y] = Back[x][y];
8316 if (Back[newx][newy])
8317 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8319 Back[x][y] = Back[newx][newy] = 0;
8322 Store[x][y] = EL_EMPTY;
8327 MovDelay[newx][newy] = 0;
8329 if (CAN_CHANGE_OR_HAS_ACTION(element))
8331 /* copy element change control values to new field */
8332 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8333 ChangePage[newx][newy] = ChangePage[x][y];
8334 ChangeCount[newx][newy] = ChangeCount[x][y];
8335 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8338 CustomValue[newx][newy] = CustomValue[x][y];
8340 ChangeDelay[x][y] = 0;
8341 ChangePage[x][y] = -1;
8342 ChangeCount[x][y] = 0;
8343 ChangeEvent[x][y] = -1;
8345 CustomValue[x][y] = 0;
8347 /* copy animation control values to new field */
8348 GfxFrame[newx][newy] = GfxFrame[x][y];
8349 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8350 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8351 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8353 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8355 /* some elements can leave other elements behind after moving */
8356 if (ei->move_leave_element != EL_EMPTY &&
8357 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8358 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8360 int move_leave_element = ei->move_leave_element;
8362 /* this makes it possible to leave the removed element again */
8363 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8364 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8366 Feld[x][y] = move_leave_element;
8368 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8369 MovDir[x][y] = direction;
8371 InitField(x, y, FALSE);
8373 if (GFX_CRUMBLED(Feld[x][y]))
8374 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8376 if (ELEM_IS_PLAYER(move_leave_element))
8377 RelocatePlayer(x, y, move_leave_element);
8380 /* do this after checking for left-behind element */
8381 ResetGfxAnimation(x, y); /* reset animation values for old field */
8383 if (!CAN_MOVE(element) ||
8384 (CAN_FALL(element) && direction == MV_DOWN &&
8385 (element == EL_SPRING ||
8386 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8387 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8388 GfxDir[x][y] = MovDir[newx][newy] = 0;
8390 TEST_DrawLevelField(x, y);
8391 TEST_DrawLevelField(newx, newy);
8393 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8395 /* prevent pushed element from moving on in pushed direction */
8396 if (pushed_by_player && CAN_MOVE(element) &&
8397 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8398 !(element_info[element].move_pattern & direction))
8399 TurnRound(newx, newy);
8401 /* prevent elements on conveyor belt from moving on in last direction */
8402 if (pushed_by_conveyor && CAN_FALL(element) &&
8403 direction & MV_HORIZONTAL)
8404 MovDir[newx][newy] = 0;
8406 if (!pushed_by_player)
8408 int nextx = newx + dx, nexty = newy + dy;
8409 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8411 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8413 if (CAN_FALL(element) && direction == MV_DOWN)
8414 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8416 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8417 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8419 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8420 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8423 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8425 TestIfBadThingTouchesPlayer(newx, newy);
8426 TestIfBadThingTouchesFriend(newx, newy);
8428 if (!IS_CUSTOM_ELEMENT(element))
8429 TestIfBadThingTouchesOtherBadThing(newx, newy);
8431 else if (element == EL_PENGUIN)
8432 TestIfFriendTouchesBadThing(newx, newy);
8434 if (DONT_GET_HIT_BY(element))
8436 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8439 /* give the player one last chance (one more frame) to move away */
8440 if (CAN_FALL(element) && direction == MV_DOWN &&
8441 (last_line || (!IS_FREE(x, newy + 1) &&
8442 (!IS_PLAYER(x, newy + 1) ||
8443 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8446 if (pushed_by_player && !game.use_change_when_pushing_bug)
8448 int push_side = MV_DIR_OPPOSITE(direction);
8449 struct PlayerInfo *player = PLAYERINFO(x, y);
8451 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8452 player->index_bit, push_side);
8453 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8454 player->index_bit, push_side);
8457 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8458 MovDelay[newx][newy] = 1;
8460 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8462 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8463 TestIfElementHitsCustomElement(newx, newy, direction);
8464 TestIfPlayerTouchesCustomElement(newx, newy);
8465 TestIfElementTouchesCustomElement(newx, newy);
8467 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8468 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8469 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8470 MV_DIR_OPPOSITE(direction));
8473 int AmoebeNachbarNr(int ax, int ay)
8476 int element = Feld[ax][ay];
8478 static int xy[4][2] =
8486 for (i = 0; i < NUM_DIRECTIONS; i++)
8488 int x = ax + xy[i][0];
8489 int y = ay + xy[i][1];
8491 if (!IN_LEV_FIELD(x, y))
8494 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8495 group_nr = AmoebaNr[x][y];
8501 void AmoebenVereinigen(int ax, int ay)
8503 int i, x, y, xx, yy;
8504 int new_group_nr = AmoebaNr[ax][ay];
8505 static int xy[4][2] =
8513 if (new_group_nr == 0)
8516 for (i = 0; i < NUM_DIRECTIONS; i++)
8521 if (!IN_LEV_FIELD(x, y))
8524 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8525 Feld[x][y] == EL_BD_AMOEBA ||
8526 Feld[x][y] == EL_AMOEBA_DEAD) &&
8527 AmoebaNr[x][y] != new_group_nr)
8529 int old_group_nr = AmoebaNr[x][y];
8531 if (old_group_nr == 0)
8534 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8535 AmoebaCnt[old_group_nr] = 0;
8536 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8537 AmoebaCnt2[old_group_nr] = 0;
8539 SCAN_PLAYFIELD(xx, yy)
8541 if (AmoebaNr[xx][yy] == old_group_nr)
8542 AmoebaNr[xx][yy] = new_group_nr;
8548 void AmoebeUmwandeln(int ax, int ay)
8552 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8554 int group_nr = AmoebaNr[ax][ay];
8559 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8560 printf("AmoebeUmwandeln(): This should never happen!\n");
8565 SCAN_PLAYFIELD(x, y)
8567 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8570 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8574 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8575 SND_AMOEBA_TURNING_TO_GEM :
8576 SND_AMOEBA_TURNING_TO_ROCK));
8581 static int xy[4][2] =
8589 for (i = 0; i < NUM_DIRECTIONS; i++)
8594 if (!IN_LEV_FIELD(x, y))
8597 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8599 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8600 SND_AMOEBA_TURNING_TO_GEM :
8601 SND_AMOEBA_TURNING_TO_ROCK));
8608 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8611 int group_nr = AmoebaNr[ax][ay];
8612 boolean done = FALSE;
8617 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8618 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8623 SCAN_PLAYFIELD(x, y)
8625 if (AmoebaNr[x][y] == group_nr &&
8626 (Feld[x][y] == EL_AMOEBA_DEAD ||
8627 Feld[x][y] == EL_BD_AMOEBA ||
8628 Feld[x][y] == EL_AMOEBA_GROWING))
8631 Feld[x][y] = new_element;
8632 InitField(x, y, FALSE);
8633 TEST_DrawLevelField(x, y);
8639 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8640 SND_BD_AMOEBA_TURNING_TO_ROCK :
8641 SND_BD_AMOEBA_TURNING_TO_GEM));
8644 void AmoebeWaechst(int x, int y)
8646 static unsigned int sound_delay = 0;
8647 static unsigned int sound_delay_value = 0;
8649 if (!MovDelay[x][y]) /* start new growing cycle */
8653 if (DelayReached(&sound_delay, sound_delay_value))
8655 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8656 sound_delay_value = 30;
8660 if (MovDelay[x][y]) /* wait some time before growing bigger */
8663 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8665 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8666 6 - MovDelay[x][y]);
8668 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8671 if (!MovDelay[x][y])
8673 Feld[x][y] = Store[x][y];
8675 TEST_DrawLevelField(x, y);
8680 void AmoebaDisappearing(int x, int y)
8682 static unsigned int sound_delay = 0;
8683 static unsigned int sound_delay_value = 0;
8685 if (!MovDelay[x][y]) /* start new shrinking cycle */
8689 if (DelayReached(&sound_delay, sound_delay_value))
8690 sound_delay_value = 30;
8693 if (MovDelay[x][y]) /* wait some time before shrinking */
8696 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8698 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8699 6 - MovDelay[x][y]);
8701 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8704 if (!MovDelay[x][y])
8706 Feld[x][y] = EL_EMPTY;
8707 TEST_DrawLevelField(x, y);
8709 /* don't let mole enter this field in this cycle;
8710 (give priority to objects falling to this field from above) */
8716 void AmoebeAbleger(int ax, int ay)
8719 int element = Feld[ax][ay];
8720 int graphic = el2img(element);
8721 int newax = ax, neway = ay;
8722 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8723 static int xy[4][2] =
8731 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8733 Feld[ax][ay] = EL_AMOEBA_DEAD;
8734 TEST_DrawLevelField(ax, ay);
8738 if (IS_ANIMATED(graphic))
8739 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8741 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8742 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8744 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8747 if (MovDelay[ax][ay])
8751 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8754 int x = ax + xy[start][0];
8755 int y = ay + xy[start][1];
8757 if (!IN_LEV_FIELD(x, y))
8760 if (IS_FREE(x, y) ||
8761 CAN_GROW_INTO(Feld[x][y]) ||
8762 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8763 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8769 if (newax == ax && neway == ay)
8772 else /* normal or "filled" (BD style) amoeba */
8775 boolean waiting_for_player = FALSE;
8777 for (i = 0; i < NUM_DIRECTIONS; i++)
8779 int j = (start + i) % 4;
8780 int x = ax + xy[j][0];
8781 int y = ay + xy[j][1];
8783 if (!IN_LEV_FIELD(x, y))
8786 if (IS_FREE(x, y) ||
8787 CAN_GROW_INTO(Feld[x][y]) ||
8788 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8789 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8795 else if (IS_PLAYER(x, y))
8796 waiting_for_player = TRUE;
8799 if (newax == ax && neway == ay) /* amoeba cannot grow */
8801 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8803 Feld[ax][ay] = EL_AMOEBA_DEAD;
8804 TEST_DrawLevelField(ax, ay);
8805 AmoebaCnt[AmoebaNr[ax][ay]]--;
8807 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8809 if (element == EL_AMOEBA_FULL)
8810 AmoebeUmwandeln(ax, ay);
8811 else if (element == EL_BD_AMOEBA)
8812 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8817 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8819 /* amoeba gets larger by growing in some direction */
8821 int new_group_nr = AmoebaNr[ax][ay];
8824 if (new_group_nr == 0)
8826 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8827 printf("AmoebeAbleger(): This should never happen!\n");
8832 AmoebaNr[newax][neway] = new_group_nr;
8833 AmoebaCnt[new_group_nr]++;
8834 AmoebaCnt2[new_group_nr]++;
8836 /* if amoeba touches other amoeba(s) after growing, unify them */
8837 AmoebenVereinigen(newax, neway);
8839 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8841 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8847 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8848 (neway == lev_fieldy - 1 && newax != ax))
8850 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8851 Store[newax][neway] = element;
8853 else if (neway == ay || element == EL_EMC_DRIPPER)
8855 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8857 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8861 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8862 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8863 Store[ax][ay] = EL_AMOEBA_DROP;
8864 ContinueMoving(ax, ay);
8868 TEST_DrawLevelField(newax, neway);
8871 void Life(int ax, int ay)
8875 int element = Feld[ax][ay];
8876 int graphic = el2img(element);
8877 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8879 boolean changed = FALSE;
8881 if (IS_ANIMATED(graphic))
8882 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8887 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8888 MovDelay[ax][ay] = life_time;
8890 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8893 if (MovDelay[ax][ay])
8897 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8899 int xx = ax+x1, yy = ay+y1;
8902 if (!IN_LEV_FIELD(xx, yy))
8905 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8907 int x = xx+x2, y = yy+y2;
8909 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8912 if (((Feld[x][y] == element ||
8913 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8915 (IS_FREE(x, y) && Stop[x][y]))
8919 if (xx == ax && yy == ay) /* field in the middle */
8921 if (nachbarn < life_parameter[0] ||
8922 nachbarn > life_parameter[1])
8924 Feld[xx][yy] = EL_EMPTY;
8926 TEST_DrawLevelField(xx, yy);
8927 Stop[xx][yy] = TRUE;
8931 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8932 { /* free border field */
8933 if (nachbarn >= life_parameter[2] &&
8934 nachbarn <= life_parameter[3])
8936 Feld[xx][yy] = element;
8937 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8939 TEST_DrawLevelField(xx, yy);
8940 Stop[xx][yy] = TRUE;
8947 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8948 SND_GAME_OF_LIFE_GROWING);
8951 static void InitRobotWheel(int x, int y)
8953 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8956 static void RunRobotWheel(int x, int y)
8958 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8961 static void StopRobotWheel(int x, int y)
8963 if (ZX == x && ZY == y)
8967 game.robot_wheel_active = FALSE;
8971 static void InitTimegateWheel(int x, int y)
8973 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8976 static void RunTimegateWheel(int x, int y)
8978 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8981 static void InitMagicBallDelay(int x, int y)
8983 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8986 static void ActivateMagicBall(int bx, int by)
8990 if (level.ball_random)
8992 int pos_border = RND(8); /* select one of the eight border elements */
8993 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8994 int xx = pos_content % 3;
8995 int yy = pos_content / 3;
9000 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9001 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9005 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9007 int xx = x - bx + 1;
9008 int yy = y - by + 1;
9010 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9011 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9015 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9018 void CheckExit(int x, int y)
9020 if (local_player->gems_still_needed > 0 ||
9021 local_player->sokobanfields_still_needed > 0 ||
9022 local_player->lights_still_needed > 0)
9024 int element = Feld[x][y];
9025 int graphic = el2img(element);
9027 if (IS_ANIMATED(graphic))
9028 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9033 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9036 Feld[x][y] = EL_EXIT_OPENING;
9038 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9041 void CheckExitEM(int x, int y)
9043 if (local_player->gems_still_needed > 0 ||
9044 local_player->sokobanfields_still_needed > 0 ||
9045 local_player->lights_still_needed > 0)
9047 int element = Feld[x][y];
9048 int graphic = el2img(element);
9050 if (IS_ANIMATED(graphic))
9051 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9056 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9059 Feld[x][y] = EL_EM_EXIT_OPENING;
9061 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9064 void CheckExitSteel(int x, int y)
9066 if (local_player->gems_still_needed > 0 ||
9067 local_player->sokobanfields_still_needed > 0 ||
9068 local_player->lights_still_needed > 0)
9070 int element = Feld[x][y];
9071 int graphic = el2img(element);
9073 if (IS_ANIMATED(graphic))
9074 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9079 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9082 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9084 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9087 void CheckExitSteelEM(int x, int y)
9089 if (local_player->gems_still_needed > 0 ||
9090 local_player->sokobanfields_still_needed > 0 ||
9091 local_player->lights_still_needed > 0)
9093 int element = Feld[x][y];
9094 int graphic = el2img(element);
9096 if (IS_ANIMATED(graphic))
9097 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9102 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9105 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9107 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9110 void CheckExitSP(int x, int y)
9112 if (local_player->gems_still_needed > 0)
9114 int element = Feld[x][y];
9115 int graphic = el2img(element);
9117 if (IS_ANIMATED(graphic))
9118 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9126 Feld[x][y] = EL_SP_EXIT_OPENING;
9128 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9131 static void CloseAllOpenTimegates()
9135 SCAN_PLAYFIELD(x, y)
9137 int element = Feld[x][y];
9139 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9141 Feld[x][y] = EL_TIMEGATE_CLOSING;
9143 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9148 void DrawTwinkleOnField(int x, int y)
9150 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9153 if (Feld[x][y] == EL_BD_DIAMOND)
9156 if (MovDelay[x][y] == 0) /* next animation frame */
9157 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9159 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9163 DrawLevelElementAnimation(x, y, Feld[x][y]);
9165 if (MovDelay[x][y] != 0)
9167 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9168 10 - MovDelay[x][y]);
9170 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9175 void MauerWaechst(int x, int y)
9179 if (!MovDelay[x][y]) /* next animation frame */
9180 MovDelay[x][y] = 3 * delay;
9182 if (MovDelay[x][y]) /* wait some time before next frame */
9186 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9188 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9189 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9191 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9194 if (!MovDelay[x][y])
9196 if (MovDir[x][y] == MV_LEFT)
9198 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9199 TEST_DrawLevelField(x - 1, y);
9201 else if (MovDir[x][y] == MV_RIGHT)
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_UP)
9208 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9209 TEST_DrawLevelField(x, y - 1);
9213 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9214 TEST_DrawLevelField(x, y + 1);
9217 Feld[x][y] = Store[x][y];
9219 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9220 TEST_DrawLevelField(x, y);
9225 void MauerAbleger(int ax, int ay)
9227 int element = Feld[ax][ay];
9228 int graphic = el2img(element);
9229 boolean oben_frei = FALSE, unten_frei = FALSE;
9230 boolean links_frei = FALSE, rechts_frei = FALSE;
9231 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9232 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9233 boolean new_wall = FALSE;
9235 if (IS_ANIMATED(graphic))
9236 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9238 if (!MovDelay[ax][ay]) /* start building new wall */
9239 MovDelay[ax][ay] = 6;
9241 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9244 if (MovDelay[ax][ay])
9248 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9250 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9252 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9254 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9257 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9258 element == EL_EXPANDABLE_WALL_ANY)
9262 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9263 Store[ax][ay-1] = element;
9264 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9265 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9266 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9267 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9272 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9273 Store[ax][ay+1] = element;
9274 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9275 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9276 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9277 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9282 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9283 element == EL_EXPANDABLE_WALL_ANY ||
9284 element == EL_EXPANDABLE_WALL ||
9285 element == EL_BD_EXPANDABLE_WALL)
9289 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9290 Store[ax-1][ay] = element;
9291 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9292 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9293 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9294 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9300 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9301 Store[ax+1][ay] = element;
9302 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9303 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9304 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9305 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9310 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9311 TEST_DrawLevelField(ax, ay);
9313 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9315 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9316 unten_massiv = TRUE;
9317 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9318 links_massiv = TRUE;
9319 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9320 rechts_massiv = TRUE;
9322 if (((oben_massiv && unten_massiv) ||
9323 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9324 element == EL_EXPANDABLE_WALL) &&
9325 ((links_massiv && rechts_massiv) ||
9326 element == EL_EXPANDABLE_WALL_VERTICAL))
9327 Feld[ax][ay] = EL_WALL;
9330 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9333 void MauerAblegerStahl(int ax, int ay)
9335 int element = Feld[ax][ay];
9336 int graphic = el2img(element);
9337 boolean oben_frei = FALSE, unten_frei = FALSE;
9338 boolean links_frei = FALSE, rechts_frei = FALSE;
9339 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9340 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9341 boolean new_wall = FALSE;
9343 if (IS_ANIMATED(graphic))
9344 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9346 if (!MovDelay[ax][ay]) /* start building new wall */
9347 MovDelay[ax][ay] = 6;
9349 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9352 if (MovDelay[ax][ay])
9356 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9358 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9360 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9362 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9365 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9366 element == EL_EXPANDABLE_STEELWALL_ANY)
9370 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9371 Store[ax][ay-1] = element;
9372 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9373 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9374 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9375 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9380 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9381 Store[ax][ay+1] = element;
9382 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9383 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9384 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9385 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9390 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9391 element == EL_EXPANDABLE_STEELWALL_ANY)
9395 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9396 Store[ax-1][ay] = element;
9397 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9398 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9399 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9400 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9406 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9407 Store[ax+1][ay] = element;
9408 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9409 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9410 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9411 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9416 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9418 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419 unten_massiv = TRUE;
9420 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421 links_massiv = TRUE;
9422 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423 rechts_massiv = TRUE;
9425 if (((oben_massiv && unten_massiv) ||
9426 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9427 ((links_massiv && rechts_massiv) ||
9428 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9429 Feld[ax][ay] = EL_STEELWALL;
9432 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9435 void CheckForDragon(int x, int y)
9438 boolean dragon_found = FALSE;
9439 static int xy[4][2] =
9447 for (i = 0; i < NUM_DIRECTIONS; i++)
9449 for (j = 0; j < 4; j++)
9451 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9453 if (IN_LEV_FIELD(xx, yy) &&
9454 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9456 if (Feld[xx][yy] == EL_DRAGON)
9457 dragon_found = TRUE;
9466 for (i = 0; i < NUM_DIRECTIONS; i++)
9468 for (j = 0; j < 3; j++)
9470 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9472 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9474 Feld[xx][yy] = EL_EMPTY;
9475 TEST_DrawLevelField(xx, yy);
9484 static void InitBuggyBase(int x, int y)
9486 int element = Feld[x][y];
9487 int activating_delay = FRAMES_PER_SECOND / 4;
9490 (element == EL_SP_BUGGY_BASE ?
9491 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9492 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9494 element == EL_SP_BUGGY_BASE_ACTIVE ?
9495 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9498 static void WarnBuggyBase(int x, int y)
9501 static int xy[4][2] =
9509 for (i = 0; i < NUM_DIRECTIONS; i++)
9511 int xx = x + xy[i][0];
9512 int yy = y + xy[i][1];
9514 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9516 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9523 static void InitTrap(int x, int y)
9525 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9528 static void ActivateTrap(int x, int y)
9530 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9533 static void ChangeActiveTrap(int x, int y)
9535 int graphic = IMG_TRAP_ACTIVE;
9537 /* if new animation frame was drawn, correct crumbled sand border */
9538 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9539 TEST_DrawLevelFieldCrumbled(x, y);
9542 static int getSpecialActionElement(int element, int number, int base_element)
9544 return (element != EL_EMPTY ? element :
9545 number != -1 ? base_element + number - 1 :
9549 static int getModifiedActionNumber(int value_old, int operator, int operand,
9550 int value_min, int value_max)
9552 int value_new = (operator == CA_MODE_SET ? operand :
9553 operator == CA_MODE_ADD ? value_old + operand :
9554 operator == CA_MODE_SUBTRACT ? value_old - operand :
9555 operator == CA_MODE_MULTIPLY ? value_old * operand :
9556 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9557 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9560 return (value_new < value_min ? value_min :
9561 value_new > value_max ? value_max :
9565 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9567 struct ElementInfo *ei = &element_info[element];
9568 struct ElementChangeInfo *change = &ei->change_page[page];
9569 int target_element = change->target_element;
9570 int action_type = change->action_type;
9571 int action_mode = change->action_mode;
9572 int action_arg = change->action_arg;
9573 int action_element = change->action_element;
9576 if (!change->has_action)
9579 /* ---------- determine action paramater values -------------------------- */
9581 int level_time_value =
9582 (level.time > 0 ? TimeLeft :
9585 int action_arg_element_raw =
9586 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9587 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9588 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9589 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9590 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9591 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9592 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9594 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9596 int action_arg_direction =
9597 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9598 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9599 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9600 change->actual_trigger_side :
9601 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9602 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9605 int action_arg_number_min =
9606 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9609 int action_arg_number_max =
9610 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9611 action_type == CA_SET_LEVEL_GEMS ? 999 :
9612 action_type == CA_SET_LEVEL_TIME ? 9999 :
9613 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9614 action_type == CA_SET_CE_VALUE ? 9999 :
9615 action_type == CA_SET_CE_SCORE ? 9999 :
9618 int action_arg_number_reset =
9619 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9620 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9621 action_type == CA_SET_LEVEL_TIME ? level.time :
9622 action_type == CA_SET_LEVEL_SCORE ? 0 :
9623 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9624 action_type == CA_SET_CE_SCORE ? 0 :
9627 int action_arg_number =
9628 (action_arg <= CA_ARG_MAX ? action_arg :
9629 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9630 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9631 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9632 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9633 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9634 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9635 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9636 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9637 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9638 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9639 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9640 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9641 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9642 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9643 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9644 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9645 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9646 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9647 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9648 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9649 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9652 int action_arg_number_old =
9653 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9654 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9655 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9656 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9657 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9660 int action_arg_number_new =
9661 getModifiedActionNumber(action_arg_number_old,
9662 action_mode, action_arg_number,
9663 action_arg_number_min, action_arg_number_max);
9665 int trigger_player_bits =
9666 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9667 change->actual_trigger_player_bits : change->trigger_player);
9669 int action_arg_player_bits =
9670 (action_arg >= CA_ARG_PLAYER_1 &&
9671 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9672 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9673 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9676 /* ---------- execute action -------------------------------------------- */
9678 switch (action_type)
9685 /* ---------- level actions ------------------------------------------- */
9687 case CA_RESTART_LEVEL:
9689 game.restart_level = TRUE;
9694 case CA_SHOW_ENVELOPE:
9696 int element = getSpecialActionElement(action_arg_element,
9697 action_arg_number, EL_ENVELOPE_1);
9699 if (IS_ENVELOPE(element))
9700 local_player->show_envelope = element;
9705 case CA_SET_LEVEL_TIME:
9707 if (level.time > 0) /* only modify limited time value */
9709 TimeLeft = action_arg_number_new;
9711 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9713 DisplayGameControlValues();
9715 if (!TimeLeft && setup.time_limit)
9716 for (i = 0; i < MAX_PLAYERS; i++)
9717 KillPlayer(&stored_player[i]);
9723 case CA_SET_LEVEL_SCORE:
9725 local_player->score = action_arg_number_new;
9727 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9729 DisplayGameControlValues();
9734 case CA_SET_LEVEL_GEMS:
9736 local_player->gems_still_needed = action_arg_number_new;
9738 game.snapshot.collected_item = TRUE;
9740 game_panel_controls[GAME_PANEL_GEMS].value =
9741 local_player->gems_still_needed;
9743 DisplayGameControlValues();
9748 case CA_SET_LEVEL_WIND:
9750 game.wind_direction = action_arg_direction;
9755 case CA_SET_LEVEL_RANDOM_SEED:
9757 /* ensure that setting a new random seed while playing is predictable */
9758 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9763 /* ---------- player actions ------------------------------------------ */
9765 case CA_MOVE_PLAYER:
9767 /* automatically move to the next field in specified direction */
9768 for (i = 0; i < MAX_PLAYERS; i++)
9769 if (trigger_player_bits & (1 << i))
9770 stored_player[i].programmed_action = action_arg_direction;
9775 case CA_EXIT_PLAYER:
9777 for (i = 0; i < MAX_PLAYERS; i++)
9778 if (action_arg_player_bits & (1 << i))
9779 ExitPlayer(&stored_player[i]);
9782 PlayerWins(local_player);
9787 case CA_KILL_PLAYER:
9789 for (i = 0; i < MAX_PLAYERS; i++)
9790 if (action_arg_player_bits & (1 << i))
9791 KillPlayer(&stored_player[i]);
9796 case CA_SET_PLAYER_KEYS:
9798 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9799 int element = getSpecialActionElement(action_arg_element,
9800 action_arg_number, EL_KEY_1);
9802 if (IS_KEY(element))
9804 for (i = 0; i < MAX_PLAYERS; i++)
9806 if (trigger_player_bits & (1 << i))
9808 stored_player[i].key[KEY_NR(element)] = key_state;
9810 DrawGameDoorValues();
9818 case CA_SET_PLAYER_SPEED:
9820 for (i = 0; i < MAX_PLAYERS; i++)
9822 if (trigger_player_bits & (1 << i))
9824 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9826 if (action_arg == CA_ARG_SPEED_FASTER &&
9827 stored_player[i].cannot_move)
9829 action_arg_number = STEPSIZE_VERY_SLOW;
9831 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9832 action_arg == CA_ARG_SPEED_FASTER)
9834 action_arg_number = 2;
9835 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9838 else if (action_arg == CA_ARG_NUMBER_RESET)
9840 action_arg_number = level.initial_player_stepsize[i];
9844 getModifiedActionNumber(move_stepsize,
9847 action_arg_number_min,
9848 action_arg_number_max);
9850 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9857 case CA_SET_PLAYER_SHIELD:
9859 for (i = 0; i < MAX_PLAYERS; i++)
9861 if (trigger_player_bits & (1 << i))
9863 if (action_arg == CA_ARG_SHIELD_OFF)
9865 stored_player[i].shield_normal_time_left = 0;
9866 stored_player[i].shield_deadly_time_left = 0;
9868 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9870 stored_player[i].shield_normal_time_left = 999999;
9872 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9874 stored_player[i].shield_normal_time_left = 999999;
9875 stored_player[i].shield_deadly_time_left = 999999;
9883 case CA_SET_PLAYER_GRAVITY:
9885 for (i = 0; i < MAX_PLAYERS; i++)
9887 if (trigger_player_bits & (1 << i))
9889 stored_player[i].gravity =
9890 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9891 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9892 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9893 stored_player[i].gravity);
9900 case CA_SET_PLAYER_ARTWORK:
9902 for (i = 0; i < MAX_PLAYERS; i++)
9904 if (trigger_player_bits & (1 << i))
9906 int artwork_element = action_arg_element;
9908 if (action_arg == CA_ARG_ELEMENT_RESET)
9910 (level.use_artwork_element[i] ? level.artwork_element[i] :
9911 stored_player[i].element_nr);
9913 if (stored_player[i].artwork_element != artwork_element)
9914 stored_player[i].Frame = 0;
9916 stored_player[i].artwork_element = artwork_element;
9918 SetPlayerWaiting(&stored_player[i], FALSE);
9920 /* set number of special actions for bored and sleeping animation */
9921 stored_player[i].num_special_action_bored =
9922 get_num_special_action(artwork_element,
9923 ACTION_BORING_1, ACTION_BORING_LAST);
9924 stored_player[i].num_special_action_sleeping =
9925 get_num_special_action(artwork_element,
9926 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9933 case CA_SET_PLAYER_INVENTORY:
9935 for (i = 0; i < MAX_PLAYERS; i++)
9937 struct PlayerInfo *player = &stored_player[i];
9940 if (trigger_player_bits & (1 << i))
9942 int inventory_element = action_arg_element;
9944 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9945 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9946 action_arg == CA_ARG_ELEMENT_ACTION)
9948 int element = inventory_element;
9949 int collect_count = element_info[element].collect_count_initial;
9951 if (!IS_CUSTOM_ELEMENT(element))
9954 if (collect_count == 0)
9955 player->inventory_infinite_element = element;
9957 for (k = 0; k < collect_count; k++)
9958 if (player->inventory_size < MAX_INVENTORY_SIZE)
9959 player->inventory_element[player->inventory_size++] =
9962 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9963 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9964 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9966 if (player->inventory_infinite_element != EL_UNDEFINED &&
9967 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9968 action_arg_element_raw))
9969 player->inventory_infinite_element = EL_UNDEFINED;
9971 for (k = 0, j = 0; j < player->inventory_size; j++)
9973 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9974 action_arg_element_raw))
9975 player->inventory_element[k++] = player->inventory_element[j];
9978 player->inventory_size = k;
9980 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9982 if (player->inventory_size > 0)
9984 for (j = 0; j < player->inventory_size - 1; j++)
9985 player->inventory_element[j] = player->inventory_element[j + 1];
9987 player->inventory_size--;
9990 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9992 if (player->inventory_size > 0)
9993 player->inventory_size--;
9995 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9997 player->inventory_infinite_element = EL_UNDEFINED;
9998 player->inventory_size = 0;
10000 else if (action_arg == CA_ARG_INVENTORY_RESET)
10002 player->inventory_infinite_element = EL_UNDEFINED;
10003 player->inventory_size = 0;
10005 if (level.use_initial_inventory[i])
10007 for (j = 0; j < level.initial_inventory_size[i]; j++)
10009 int element = level.initial_inventory_content[i][j];
10010 int collect_count = element_info[element].collect_count_initial;
10012 if (!IS_CUSTOM_ELEMENT(element))
10015 if (collect_count == 0)
10016 player->inventory_infinite_element = element;
10018 for (k = 0; k < collect_count; k++)
10019 if (player->inventory_size < MAX_INVENTORY_SIZE)
10020 player->inventory_element[player->inventory_size++] =
10031 /* ---------- CE actions ---------------------------------------------- */
10033 case CA_SET_CE_VALUE:
10035 int last_ce_value = CustomValue[x][y];
10037 CustomValue[x][y] = action_arg_number_new;
10039 if (CustomValue[x][y] != last_ce_value)
10041 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10042 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10044 if (CustomValue[x][y] == 0)
10046 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10047 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10054 case CA_SET_CE_SCORE:
10056 int last_ce_score = ei->collect_score;
10058 ei->collect_score = action_arg_number_new;
10060 if (ei->collect_score != last_ce_score)
10062 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10063 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10065 if (ei->collect_score == 0)
10069 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10070 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10073 This is a very special case that seems to be a mixture between
10074 CheckElementChange() and CheckTriggeredElementChange(): while
10075 the first one only affects single elements that are triggered
10076 directly, the second one affects multiple elements in the playfield
10077 that are triggered indirectly by another element. This is a third
10078 case: Changing the CE score always affects multiple identical CEs,
10079 so every affected CE must be checked, not only the single CE for
10080 which the CE score was changed in the first place (as every instance
10081 of that CE shares the same CE score, and therefore also can change)!
10083 SCAN_PLAYFIELD(xx, yy)
10085 if (Feld[xx][yy] == element)
10086 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10087 CE_SCORE_GETS_ZERO);
10095 case CA_SET_CE_ARTWORK:
10097 int artwork_element = action_arg_element;
10098 boolean reset_frame = FALSE;
10101 if (action_arg == CA_ARG_ELEMENT_RESET)
10102 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10105 if (ei->gfx_element != artwork_element)
10106 reset_frame = TRUE;
10108 ei->gfx_element = artwork_element;
10110 SCAN_PLAYFIELD(xx, yy)
10112 if (Feld[xx][yy] == element)
10116 ResetGfxAnimation(xx, yy);
10117 ResetRandomAnimationValue(xx, yy);
10120 TEST_DrawLevelField(xx, yy);
10127 /* ---------- engine actions ------------------------------------------ */
10129 case CA_SET_ENGINE_SCAN_MODE:
10131 InitPlayfieldScanMode(action_arg);
10141 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10143 int old_element = Feld[x][y];
10144 int new_element = GetElementFromGroupElement(element);
10145 int previous_move_direction = MovDir[x][y];
10146 int last_ce_value = CustomValue[x][y];
10147 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10148 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10149 boolean add_player_onto_element = (new_element_is_player &&
10150 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10151 IS_WALKABLE(old_element));
10153 if (!add_player_onto_element)
10155 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10156 RemoveMovingField(x, y);
10160 Feld[x][y] = new_element;
10162 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10163 MovDir[x][y] = previous_move_direction;
10165 if (element_info[new_element].use_last_ce_value)
10166 CustomValue[x][y] = last_ce_value;
10168 InitField_WithBug1(x, y, FALSE);
10170 new_element = Feld[x][y]; /* element may have changed */
10172 ResetGfxAnimation(x, y);
10173 ResetRandomAnimationValue(x, y);
10175 TEST_DrawLevelField(x, y);
10177 if (GFX_CRUMBLED(new_element))
10178 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10181 /* check if element under the player changes from accessible to unaccessible
10182 (needed for special case of dropping element which then changes) */
10183 /* (must be checked after creating new element for walkable group elements) */
10184 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10185 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10192 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10193 if (new_element_is_player)
10194 RelocatePlayer(x, y, new_element);
10197 ChangeCount[x][y]++; /* count number of changes in the same frame */
10199 TestIfBadThingTouchesPlayer(x, y);
10200 TestIfPlayerTouchesCustomElement(x, y);
10201 TestIfElementTouchesCustomElement(x, y);
10204 static void CreateField(int x, int y, int element)
10206 CreateFieldExt(x, y, element, FALSE);
10209 static void CreateElementFromChange(int x, int y, int element)
10211 element = GET_VALID_RUNTIME_ELEMENT(element);
10213 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10215 int old_element = Feld[x][y];
10217 /* prevent changed element from moving in same engine frame
10218 unless both old and new element can either fall or move */
10219 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10220 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10224 CreateFieldExt(x, y, element, TRUE);
10227 static boolean ChangeElement(int x, int y, int element, int page)
10229 struct ElementInfo *ei = &element_info[element];
10230 struct ElementChangeInfo *change = &ei->change_page[page];
10231 int ce_value = CustomValue[x][y];
10232 int ce_score = ei->collect_score;
10233 int target_element;
10234 int old_element = Feld[x][y];
10236 /* always use default change event to prevent running into a loop */
10237 if (ChangeEvent[x][y] == -1)
10238 ChangeEvent[x][y] = CE_DELAY;
10240 if (ChangeEvent[x][y] == CE_DELAY)
10242 /* reset actual trigger element, trigger player and action element */
10243 change->actual_trigger_element = EL_EMPTY;
10244 change->actual_trigger_player = EL_EMPTY;
10245 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10246 change->actual_trigger_side = CH_SIDE_NONE;
10247 change->actual_trigger_ce_value = 0;
10248 change->actual_trigger_ce_score = 0;
10251 /* do not change elements more than a specified maximum number of changes */
10252 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10255 ChangeCount[x][y]++; /* count number of changes in the same frame */
10257 if (change->explode)
10264 if (change->use_target_content)
10266 boolean complete_replace = TRUE;
10267 boolean can_replace[3][3];
10270 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10273 boolean is_walkable;
10274 boolean is_diggable;
10275 boolean is_collectible;
10276 boolean is_removable;
10277 boolean is_destructible;
10278 int ex = x + xx - 1;
10279 int ey = y + yy - 1;
10280 int content_element = change->target_content.e[xx][yy];
10283 can_replace[xx][yy] = TRUE;
10285 if (ex == x && ey == y) /* do not check changing element itself */
10288 if (content_element == EL_EMPTY_SPACE)
10290 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10295 if (!IN_LEV_FIELD(ex, ey))
10297 can_replace[xx][yy] = FALSE;
10298 complete_replace = FALSE;
10305 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10306 e = MovingOrBlocked2Element(ex, ey);
10308 is_empty = (IS_FREE(ex, ey) ||
10309 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10311 is_walkable = (is_empty || IS_WALKABLE(e));
10312 is_diggable = (is_empty || IS_DIGGABLE(e));
10313 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10314 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10315 is_removable = (is_diggable || is_collectible);
10317 can_replace[xx][yy] =
10318 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10319 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10320 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10321 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10322 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10323 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10324 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10326 if (!can_replace[xx][yy])
10327 complete_replace = FALSE;
10330 if (!change->only_if_complete || complete_replace)
10332 boolean something_has_changed = FALSE;
10334 if (change->only_if_complete && change->use_random_replace &&
10335 RND(100) < change->random_percentage)
10338 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10340 int ex = x + xx - 1;
10341 int ey = y + yy - 1;
10342 int content_element;
10344 if (can_replace[xx][yy] && (!change->use_random_replace ||
10345 RND(100) < change->random_percentage))
10347 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10348 RemoveMovingField(ex, ey);
10350 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10352 content_element = change->target_content.e[xx][yy];
10353 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10354 ce_value, ce_score);
10356 CreateElementFromChange(ex, ey, target_element);
10358 something_has_changed = TRUE;
10360 /* for symmetry reasons, freeze newly created border elements */
10361 if (ex != x || ey != y)
10362 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10366 if (something_has_changed)
10368 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10369 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10375 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10376 ce_value, ce_score);
10378 if (element == EL_DIAGONAL_GROWING ||
10379 element == EL_DIAGONAL_SHRINKING)
10381 target_element = Store[x][y];
10383 Store[x][y] = EL_EMPTY;
10386 CreateElementFromChange(x, y, target_element);
10388 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10389 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10392 /* this uses direct change before indirect change */
10393 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10398 static void HandleElementChange(int x, int y, int page)
10400 int element = MovingOrBlocked2Element(x, y);
10401 struct ElementInfo *ei = &element_info[element];
10402 struct ElementChangeInfo *change = &ei->change_page[page];
10403 boolean handle_action_before_change = FALSE;
10406 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10407 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10410 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10411 x, y, element, element_info[element].token_name);
10412 printf("HandleElementChange(): This should never happen!\n");
10417 /* this can happen with classic bombs on walkable, changing elements */
10418 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10423 if (ChangeDelay[x][y] == 0) /* initialize element change */
10425 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10427 if (change->can_change)
10429 /* !!! not clear why graphic animation should be reset at all here !!! */
10430 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10431 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10434 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10436 When using an animation frame delay of 1 (this only happens with
10437 "sp_zonk.moving.left/right" in the classic graphics), the default
10438 (non-moving) animation shows wrong animation frames (while the
10439 moving animation, like "sp_zonk.moving.left/right", is correct,
10440 so this graphical bug never shows up with the classic graphics).
10441 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10442 be drawn instead of the correct frames 0,1,2,3. This is caused by
10443 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10444 an element change: First when the change delay ("ChangeDelay[][]")
10445 counter has reached zero after decrementing, then a second time in
10446 the next frame (after "GfxFrame[][]" was already incremented) when
10447 "ChangeDelay[][]" is reset to the initial delay value again.
10449 This causes frame 0 to be drawn twice, while the last frame won't
10450 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10452 As some animations may already be cleverly designed around this bug
10453 (at least the "Snake Bite" snake tail animation does this), it cannot
10454 simply be fixed here without breaking such existing animations.
10455 Unfortunately, it cannot easily be detected if a graphics set was
10456 designed "before" or "after" the bug was fixed. As a workaround,
10457 a new graphics set option "game.graphics_engine_version" was added
10458 to be able to specify the game's major release version for which the
10459 graphics set was designed, which can then be used to decide if the
10460 bugfix should be used (version 4 and above) or not (version 3 or
10461 below, or if no version was specified at all, as with old sets).
10463 (The wrong/fixed animation frames can be tested with the test level set
10464 "test_gfxframe" and level "000", which contains a specially prepared
10465 custom element at level position (x/y) == (11/9) which uses the zonk
10466 animation mentioned above. Using "game.graphics_engine_version: 4"
10467 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10468 This can also be seen from the debug output for this test element.)
10471 /* when a custom element is about to change (for example by change delay),
10472 do not reset graphic animation when the custom element is moving */
10473 if (game.graphics_engine_version < 4 &&
10476 ResetGfxAnimation(x, y);
10477 ResetRandomAnimationValue(x, y);
10480 if (change->pre_change_function)
10481 change->pre_change_function(x, y);
10485 ChangeDelay[x][y]--;
10487 if (ChangeDelay[x][y] != 0) /* continue element change */
10489 if (change->can_change)
10491 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10493 if (IS_ANIMATED(graphic))
10494 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10496 if (change->change_function)
10497 change->change_function(x, y);
10500 else /* finish element change */
10502 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10504 page = ChangePage[x][y];
10505 ChangePage[x][y] = -1;
10507 change = &ei->change_page[page];
10510 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10512 ChangeDelay[x][y] = 1; /* try change after next move step */
10513 ChangePage[x][y] = page; /* remember page to use for change */
10518 /* special case: set new level random seed before changing element */
10519 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10520 handle_action_before_change = TRUE;
10522 if (change->has_action && handle_action_before_change)
10523 ExecuteCustomElementAction(x, y, element, page);
10525 if (change->can_change)
10527 if (ChangeElement(x, y, element, page))
10529 if (change->post_change_function)
10530 change->post_change_function(x, y);
10534 if (change->has_action && !handle_action_before_change)
10535 ExecuteCustomElementAction(x, y, element, page);
10539 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10540 int trigger_element,
10542 int trigger_player,
10546 boolean change_done_any = FALSE;
10547 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10550 if (!(trigger_events[trigger_element][trigger_event]))
10553 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10557 int element = EL_CUSTOM_START + i;
10558 boolean change_done = FALSE;
10561 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10562 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10565 for (p = 0; p < element_info[element].num_change_pages; p++)
10567 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10569 if (change->can_change_or_has_action &&
10570 change->has_event[trigger_event] &&
10571 change->trigger_side & trigger_side &&
10572 change->trigger_player & trigger_player &&
10573 change->trigger_page & trigger_page_bits &&
10574 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10576 change->actual_trigger_element = trigger_element;
10577 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10578 change->actual_trigger_player_bits = trigger_player;
10579 change->actual_trigger_side = trigger_side;
10580 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10581 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10583 if ((change->can_change && !change_done) || change->has_action)
10587 SCAN_PLAYFIELD(x, y)
10589 if (Feld[x][y] == element)
10591 if (change->can_change && !change_done)
10593 /* if element already changed in this frame, not only prevent
10594 another element change (checked in ChangeElement()), but
10595 also prevent additional element actions for this element */
10597 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10598 !level.use_action_after_change_bug)
10601 ChangeDelay[x][y] = 1;
10602 ChangeEvent[x][y] = trigger_event;
10604 HandleElementChange(x, y, p);
10606 else if (change->has_action)
10608 /* if element already changed in this frame, not only prevent
10609 another element change (checked in ChangeElement()), but
10610 also prevent additional element actions for this element */
10612 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10613 !level.use_action_after_change_bug)
10616 ExecuteCustomElementAction(x, y, element, p);
10617 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10622 if (change->can_change)
10624 change_done = TRUE;
10625 change_done_any = TRUE;
10632 RECURSION_LOOP_DETECTION_END();
10634 return change_done_any;
10637 static boolean CheckElementChangeExt(int x, int y,
10639 int trigger_element,
10641 int trigger_player,
10644 boolean change_done = FALSE;
10647 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10648 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10651 if (Feld[x][y] == EL_BLOCKED)
10653 Blocked2Moving(x, y, &x, &y);
10654 element = Feld[x][y];
10657 /* check if element has already changed or is about to change after moving */
10658 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10659 Feld[x][y] != element) ||
10661 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10662 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10663 ChangePage[x][y] != -1)))
10666 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10668 for (p = 0; p < element_info[element].num_change_pages; p++)
10670 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10672 /* check trigger element for all events where the element that is checked
10673 for changing interacts with a directly adjacent element -- this is
10674 different to element changes that affect other elements to change on the
10675 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10676 boolean check_trigger_element =
10677 (trigger_event == CE_TOUCHING_X ||
10678 trigger_event == CE_HITTING_X ||
10679 trigger_event == CE_HIT_BY_X ||
10680 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10682 if (change->can_change_or_has_action &&
10683 change->has_event[trigger_event] &&
10684 change->trigger_side & trigger_side &&
10685 change->trigger_player & trigger_player &&
10686 (!check_trigger_element ||
10687 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10689 change->actual_trigger_element = trigger_element;
10690 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10691 change->actual_trigger_player_bits = trigger_player;
10692 change->actual_trigger_side = trigger_side;
10693 change->actual_trigger_ce_value = CustomValue[x][y];
10694 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10696 /* special case: trigger element not at (x,y) position for some events */
10697 if (check_trigger_element)
10709 { 0, 0 }, { 0, 0 }, { 0, 0 },
10713 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10714 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10716 change->actual_trigger_ce_value = CustomValue[xx][yy];
10717 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10720 if (change->can_change && !change_done)
10722 ChangeDelay[x][y] = 1;
10723 ChangeEvent[x][y] = trigger_event;
10725 HandleElementChange(x, y, p);
10727 change_done = TRUE;
10729 else if (change->has_action)
10731 ExecuteCustomElementAction(x, y, element, p);
10732 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10737 RECURSION_LOOP_DETECTION_END();
10739 return change_done;
10742 static void PlayPlayerSound(struct PlayerInfo *player)
10744 int jx = player->jx, jy = player->jy;
10745 int sound_element = player->artwork_element;
10746 int last_action = player->last_action_waiting;
10747 int action = player->action_waiting;
10749 if (player->is_waiting)
10751 if (action != last_action)
10752 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10754 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10758 if (action != last_action)
10759 StopSound(element_info[sound_element].sound[last_action]);
10761 if (last_action == ACTION_SLEEPING)
10762 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10766 static void PlayAllPlayersSound()
10770 for (i = 0; i < MAX_PLAYERS; i++)
10771 if (stored_player[i].active)
10772 PlayPlayerSound(&stored_player[i]);
10775 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10777 boolean last_waiting = player->is_waiting;
10778 int move_dir = player->MovDir;
10780 player->dir_waiting = move_dir;
10781 player->last_action_waiting = player->action_waiting;
10785 if (!last_waiting) /* not waiting -> waiting */
10787 player->is_waiting = TRUE;
10789 player->frame_counter_bored =
10791 game.player_boring_delay_fixed +
10792 GetSimpleRandom(game.player_boring_delay_random);
10793 player->frame_counter_sleeping =
10795 game.player_sleeping_delay_fixed +
10796 GetSimpleRandom(game.player_sleeping_delay_random);
10798 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10801 if (game.player_sleeping_delay_fixed +
10802 game.player_sleeping_delay_random > 0 &&
10803 player->anim_delay_counter == 0 &&
10804 player->post_delay_counter == 0 &&
10805 FrameCounter >= player->frame_counter_sleeping)
10806 player->is_sleeping = TRUE;
10807 else if (game.player_boring_delay_fixed +
10808 game.player_boring_delay_random > 0 &&
10809 FrameCounter >= player->frame_counter_bored)
10810 player->is_bored = TRUE;
10812 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10813 player->is_bored ? ACTION_BORING :
10816 if (player->is_sleeping && player->use_murphy)
10818 /* special case for sleeping Murphy when leaning against non-free tile */
10820 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10821 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10822 !IS_MOVING(player->jx - 1, player->jy)))
10823 move_dir = MV_LEFT;
10824 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10825 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10826 !IS_MOVING(player->jx + 1, player->jy)))
10827 move_dir = MV_RIGHT;
10829 player->is_sleeping = FALSE;
10831 player->dir_waiting = move_dir;
10834 if (player->is_sleeping)
10836 if (player->num_special_action_sleeping > 0)
10838 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10840 int last_special_action = player->special_action_sleeping;
10841 int num_special_action = player->num_special_action_sleeping;
10842 int special_action =
10843 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10844 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10845 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10846 last_special_action + 1 : ACTION_SLEEPING);
10847 int special_graphic =
10848 el_act_dir2img(player->artwork_element, special_action, move_dir);
10850 player->anim_delay_counter =
10851 graphic_info[special_graphic].anim_delay_fixed +
10852 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10853 player->post_delay_counter =
10854 graphic_info[special_graphic].post_delay_fixed +
10855 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10857 player->special_action_sleeping = special_action;
10860 if (player->anim_delay_counter > 0)
10862 player->action_waiting = player->special_action_sleeping;
10863 player->anim_delay_counter--;
10865 else if (player->post_delay_counter > 0)
10867 player->post_delay_counter--;
10871 else if (player->is_bored)
10873 if (player->num_special_action_bored > 0)
10875 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10877 int special_action =
10878 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10879 int special_graphic =
10880 el_act_dir2img(player->artwork_element, special_action, move_dir);
10882 player->anim_delay_counter =
10883 graphic_info[special_graphic].anim_delay_fixed +
10884 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10885 player->post_delay_counter =
10886 graphic_info[special_graphic].post_delay_fixed +
10887 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10889 player->special_action_bored = special_action;
10892 if (player->anim_delay_counter > 0)
10894 player->action_waiting = player->special_action_bored;
10895 player->anim_delay_counter--;
10897 else if (player->post_delay_counter > 0)
10899 player->post_delay_counter--;
10904 else if (last_waiting) /* waiting -> not waiting */
10906 player->is_waiting = FALSE;
10907 player->is_bored = FALSE;
10908 player->is_sleeping = FALSE;
10910 player->frame_counter_bored = -1;
10911 player->frame_counter_sleeping = -1;
10913 player->anim_delay_counter = 0;
10914 player->post_delay_counter = 0;
10916 player->dir_waiting = player->MovDir;
10917 player->action_waiting = ACTION_DEFAULT;
10919 player->special_action_bored = ACTION_DEFAULT;
10920 player->special_action_sleeping = ACTION_DEFAULT;
10924 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10926 if ((!player->is_moving && player->was_moving) ||
10927 (player->MovPos == 0 && player->was_moving) ||
10928 (player->is_snapping && !player->was_snapping) ||
10929 (player->is_dropping && !player->was_dropping))
10931 if (!CheckSaveEngineSnapshotToList())
10934 player->was_moving = FALSE;
10935 player->was_snapping = TRUE;
10936 player->was_dropping = TRUE;
10940 if (player->is_moving)
10941 player->was_moving = TRUE;
10943 if (!player->is_snapping)
10944 player->was_snapping = FALSE;
10946 if (!player->is_dropping)
10947 player->was_dropping = FALSE;
10951 static void CheckSingleStepMode(struct PlayerInfo *player)
10953 if (tape.single_step && tape.recording && !tape.pausing)
10955 /* as it is called "single step mode", just return to pause mode when the
10956 player stopped moving after one tile (or never starts moving at all) */
10957 if (!player->is_moving &&
10958 !player->is_pushing &&
10959 !player->is_dropping_pressed)
10961 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10962 SnapField(player, 0, 0); /* stop snapping */
10966 CheckSaveEngineSnapshot(player);
10969 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10971 int left = player_action & JOY_LEFT;
10972 int right = player_action & JOY_RIGHT;
10973 int up = player_action & JOY_UP;
10974 int down = player_action & JOY_DOWN;
10975 int button1 = player_action & JOY_BUTTON_1;
10976 int button2 = player_action & JOY_BUTTON_2;
10977 int dx = (left ? -1 : right ? 1 : 0);
10978 int dy = (up ? -1 : down ? 1 : 0);
10980 if (!player->active || tape.pausing)
10986 SnapField(player, dx, dy);
10990 DropElement(player);
10992 MovePlayer(player, dx, dy);
10995 CheckSingleStepMode(player);
10997 SetPlayerWaiting(player, FALSE);
10999 return player_action;
11003 /* no actions for this player (no input at player's configured device) */
11005 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11006 SnapField(player, 0, 0);
11007 CheckGravityMovementWhenNotMoving(player);
11009 if (player->MovPos == 0)
11010 SetPlayerWaiting(player, TRUE);
11012 if (player->MovPos == 0) /* needed for tape.playing */
11013 player->is_moving = FALSE;
11015 player->is_dropping = FALSE;
11016 player->is_dropping_pressed = FALSE;
11017 player->drop_pressed_delay = 0;
11019 CheckSingleStepMode(player);
11025 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11028 if (!tape.use_mouse)
11031 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11032 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11033 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11036 static void SetTapeActionFromMouseAction(byte *tape_action,
11037 struct MouseActionInfo *mouse_action)
11039 if (!tape.use_mouse)
11042 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11043 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11044 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11047 static void CheckLevelTime()
11051 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11052 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11054 if (level.native_em_level->lev->home == 0) /* all players at home */
11056 PlayerWins(local_player);
11058 AllPlayersGone = TRUE;
11060 level.native_em_level->lev->home = -1;
11063 if (level.native_em_level->ply[0]->alive == 0 &&
11064 level.native_em_level->ply[1]->alive == 0 &&
11065 level.native_em_level->ply[2]->alive == 0 &&
11066 level.native_em_level->ply[3]->alive == 0) /* all dead */
11067 AllPlayersGone = TRUE;
11069 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11071 if (game_sp.LevelSolved &&
11072 !game_sp.GameOver) /* game won */
11074 PlayerWins(local_player);
11076 game_sp.GameOver = TRUE;
11078 AllPlayersGone = TRUE;
11081 if (game_sp.GameOver) /* game lost */
11082 AllPlayersGone = TRUE;
11084 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11086 if (game_mm.level_solved &&
11087 !game_mm.game_over) /* game won */
11089 PlayerWins(local_player);
11091 game_mm.game_over = TRUE;
11093 AllPlayersGone = TRUE;
11096 if (game_mm.game_over) /* game lost */
11097 AllPlayersGone = TRUE;
11100 if (TimeFrames >= FRAMES_PER_SECOND)
11105 for (i = 0; i < MAX_PLAYERS; i++)
11107 struct PlayerInfo *player = &stored_player[i];
11109 if (SHIELD_ON(player))
11111 player->shield_normal_time_left--;
11113 if (player->shield_deadly_time_left > 0)
11114 player->shield_deadly_time_left--;
11118 if (!local_player->LevelSolved && !level.use_step_counter)
11126 if (TimeLeft <= 10 && setup.time_limit)
11127 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11129 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11130 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11132 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11134 if (!TimeLeft && setup.time_limit)
11136 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11137 level.native_em_level->lev->killed_out_of_time = TRUE;
11139 for (i = 0; i < MAX_PLAYERS; i++)
11140 KillPlayer(&stored_player[i]);
11143 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11145 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11148 level.native_em_level->lev->time =
11149 (game.no_time_limit ? TimePlayed : TimeLeft);
11152 if (tape.recording || tape.playing)
11153 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11156 if (tape.recording || tape.playing)
11157 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11159 UpdateAndDisplayGameControlValues();
11162 void AdvanceFrameAndPlayerCounters(int player_nr)
11166 /* advance frame counters (global frame counter and time frame counter) */
11170 /* advance player counters (counters for move delay, move animation etc.) */
11171 for (i = 0; i < MAX_PLAYERS; i++)
11173 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11174 int move_delay_value = stored_player[i].move_delay_value;
11175 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11177 if (!advance_player_counters) /* not all players may be affected */
11180 if (move_frames == 0) /* less than one move per game frame */
11182 int stepsize = TILEX / move_delay_value;
11183 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11184 int count = (stored_player[i].is_moving ?
11185 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11187 if (count % delay == 0)
11191 stored_player[i].Frame += move_frames;
11193 if (stored_player[i].MovPos != 0)
11194 stored_player[i].StepFrame += move_frames;
11196 if (stored_player[i].move_delay > 0)
11197 stored_player[i].move_delay--;
11199 /* due to bugs in previous versions, counter must count up, not down */
11200 if (stored_player[i].push_delay != -1)
11201 stored_player[i].push_delay++;
11203 if (stored_player[i].drop_delay > 0)
11204 stored_player[i].drop_delay--;
11206 if (stored_player[i].is_dropping_pressed)
11207 stored_player[i].drop_pressed_delay++;
11211 void StartGameActions(boolean init_network_game, boolean record_tape,
11214 unsigned int new_random_seed = InitRND(random_seed);
11217 TapeStartRecording(new_random_seed);
11219 if (init_network_game)
11221 SendToServer_StartPlaying();
11229 void GameActionsExt()
11232 static unsigned int game_frame_delay = 0;
11234 unsigned int game_frame_delay_value;
11235 byte *recorded_player_action;
11236 byte summarized_player_action = 0;
11237 byte tape_action[MAX_PLAYERS];
11240 /* detect endless loops, caused by custom element programming */
11241 if (recursion_loop_detected && recursion_loop_depth == 0)
11243 char *message = getStringCat3("Internal Error! Element ",
11244 EL_NAME(recursion_loop_element),
11245 " caused endless loop! Quit the game?");
11247 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11248 EL_NAME(recursion_loop_element));
11250 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11252 recursion_loop_detected = FALSE; /* if game should be continued */
11259 if (game.restart_level)
11260 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11262 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11263 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11265 if (level.native_em_level->lev->home == 0) /* all players at home */
11267 PlayerWins(local_player);
11269 AllPlayersGone = TRUE;
11271 level.native_em_level->lev->home = -1;
11274 if (level.native_em_level->ply[0]->alive == 0 &&
11275 level.native_em_level->ply[1]->alive == 0 &&
11276 level.native_em_level->ply[2]->alive == 0 &&
11277 level.native_em_level->ply[3]->alive == 0) /* all dead */
11278 AllPlayersGone = TRUE;
11280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11282 if (game_sp.LevelSolved &&
11283 !game_sp.GameOver) /* game won */
11285 PlayerWins(local_player);
11287 game_sp.GameOver = TRUE;
11289 AllPlayersGone = TRUE;
11292 if (game_sp.GameOver) /* game lost */
11293 AllPlayersGone = TRUE;
11295 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11297 if (game_mm.level_solved &&
11298 !game_mm.game_over) /* game won */
11300 PlayerWins(local_player);
11302 game_mm.game_over = TRUE;
11304 AllPlayersGone = TRUE;
11307 if (game_mm.game_over) /* game lost */
11308 AllPlayersGone = TRUE;
11311 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11314 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11317 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11320 game_frame_delay_value =
11321 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11323 if (tape.playing && tape.warp_forward && !tape.pausing)
11324 game_frame_delay_value = 0;
11326 SetVideoFrameDelay(game_frame_delay_value);
11330 /* ---------- main game synchronization point ---------- */
11332 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11334 printf("::: skip == %d\n", skip);
11337 /* ---------- main game synchronization point ---------- */
11339 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11343 if (network_playing && !network_player_action_received)
11345 /* try to get network player actions in time */
11347 /* last chance to get network player actions without main loop delay */
11348 HandleNetworking();
11350 /* game was quit by network peer */
11351 if (game_status != GAME_MODE_PLAYING)
11354 if (!network_player_action_received)
11355 return; /* failed to get network player actions in time */
11357 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11363 /* at this point we know that we really continue executing the game */
11365 network_player_action_received = FALSE;
11367 /* when playing tape, read previously recorded player input from tape data */
11368 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11370 local_player->effective_mouse_action = local_player->mouse_action;
11372 if (recorded_player_action != NULL)
11373 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11374 recorded_player_action);
11376 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11380 if (tape.set_centered_player)
11382 game.centered_player_nr_next = tape.centered_player_nr_next;
11383 game.set_centered_player = TRUE;
11386 for (i = 0; i < MAX_PLAYERS; i++)
11388 summarized_player_action |= stored_player[i].action;
11390 if (!network_playing && (game.team_mode || tape.playing))
11391 stored_player[i].effective_action = stored_player[i].action;
11394 if (network_playing)
11395 SendToServer_MovePlayer(summarized_player_action);
11397 // summarize all actions at local players mapped input device position
11398 // (this allows using different input devices in single player mode)
11399 if (!network.enabled && !game.team_mode)
11400 stored_player[map_player_action[local_player->index_nr]].effective_action =
11401 summarized_player_action;
11403 if (tape.recording &&
11405 setup.input_on_focus &&
11406 game.centered_player_nr != -1)
11408 for (i = 0; i < MAX_PLAYERS; i++)
11409 stored_player[i].effective_action =
11410 (i == game.centered_player_nr ? summarized_player_action : 0);
11413 if (recorded_player_action != NULL)
11414 for (i = 0; i < MAX_PLAYERS; i++)
11415 stored_player[i].effective_action = recorded_player_action[i];
11417 for (i = 0; i < MAX_PLAYERS; i++)
11419 tape_action[i] = stored_player[i].effective_action;
11421 /* (this may happen in the RND game engine if a player was not present on
11422 the playfield on level start, but appeared later from a custom element */
11423 if (setup.team_mode &&
11426 !tape.player_participates[i])
11427 tape.player_participates[i] = TRUE;
11430 SetTapeActionFromMouseAction(tape_action,
11431 &local_player->effective_mouse_action);
11433 /* only record actions from input devices, but not programmed actions */
11434 if (tape.recording)
11435 TapeRecordAction(tape_action);
11437 #if USE_NEW_PLAYER_ASSIGNMENTS
11438 // !!! also map player actions in single player mode !!!
11439 // if (game.team_mode)
11442 byte mapped_action[MAX_PLAYERS];
11444 #if DEBUG_PLAYER_ACTIONS
11446 for (i = 0; i < MAX_PLAYERS; i++)
11447 printf(" %d, ", stored_player[i].effective_action);
11450 for (i = 0; i < MAX_PLAYERS; i++)
11451 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11453 for (i = 0; i < MAX_PLAYERS; i++)
11454 stored_player[i].effective_action = mapped_action[i];
11456 #if DEBUG_PLAYER_ACTIONS
11458 for (i = 0; i < MAX_PLAYERS; i++)
11459 printf(" %d, ", stored_player[i].effective_action);
11463 #if DEBUG_PLAYER_ACTIONS
11467 for (i = 0; i < MAX_PLAYERS; i++)
11468 printf(" %d, ", stored_player[i].effective_action);
11474 for (i = 0; i < MAX_PLAYERS; i++)
11476 // allow engine snapshot in case of changed movement attempt
11477 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11478 (stored_player[i].effective_action & KEY_MOTION))
11479 game.snapshot.changed_action = TRUE;
11481 // allow engine snapshot in case of snapping/dropping attempt
11482 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11483 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11484 game.snapshot.changed_action = TRUE;
11486 game.snapshot.last_action[i] = stored_player[i].effective_action;
11489 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11491 GameActions_EM_Main();
11493 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11495 GameActions_SP_Main();
11497 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11499 GameActions_MM_Main();
11503 GameActions_RND_Main();
11506 BlitScreenToBitmap(backbuffer);
11510 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11512 if (global.show_frames_per_second)
11514 static unsigned int fps_counter = 0;
11515 static int fps_frames = 0;
11516 unsigned int fps_delay_ms = Counter() - fps_counter;
11520 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11522 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11525 fps_counter = Counter();
11527 /* always draw FPS to screen after FPS value was updated */
11528 redraw_mask |= REDRAW_FPS;
11531 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11532 if (GetDrawDeactivationMask() == REDRAW_NONE)
11533 redraw_mask |= REDRAW_FPS;
11537 static void GameActions_CheckSaveEngineSnapshot()
11539 if (!game.snapshot.save_snapshot)
11542 // clear flag for saving snapshot _before_ saving snapshot
11543 game.snapshot.save_snapshot = FALSE;
11545 SaveEngineSnapshotToList();
11552 GameActions_CheckSaveEngineSnapshot();
11555 void GameActions_EM_Main()
11557 byte effective_action[MAX_PLAYERS];
11558 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11561 for (i = 0; i < MAX_PLAYERS; i++)
11562 effective_action[i] = stored_player[i].effective_action;
11564 GameActions_EM(effective_action, warp_mode);
11567 void GameActions_SP_Main()
11569 byte effective_action[MAX_PLAYERS];
11570 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11573 for (i = 0; i < MAX_PLAYERS; i++)
11574 effective_action[i] = stored_player[i].effective_action;
11576 GameActions_SP(effective_action, warp_mode);
11578 for (i = 0; i < MAX_PLAYERS; i++)
11580 if (stored_player[i].force_dropping)
11581 stored_player[i].action |= KEY_BUTTON_DROP;
11583 stored_player[i].force_dropping = FALSE;
11587 void GameActions_MM_Main()
11589 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11591 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11594 void GameActions_RND_Main()
11599 void GameActions_RND()
11601 int magic_wall_x = 0, magic_wall_y = 0;
11602 int i, x, y, element, graphic, last_gfx_frame;
11604 InitPlayfieldScanModeVars();
11606 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11608 SCAN_PLAYFIELD(x, y)
11610 ChangeCount[x][y] = 0;
11611 ChangeEvent[x][y] = -1;
11615 if (game.set_centered_player)
11617 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11619 /* switching to "all players" only possible if all players fit to screen */
11620 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11622 game.centered_player_nr_next = game.centered_player_nr;
11623 game.set_centered_player = FALSE;
11626 /* do not switch focus to non-existing (or non-active) player */
11627 if (game.centered_player_nr_next >= 0 &&
11628 !stored_player[game.centered_player_nr_next].active)
11630 game.centered_player_nr_next = game.centered_player_nr;
11631 game.set_centered_player = FALSE;
11635 if (game.set_centered_player &&
11636 ScreenMovPos == 0) /* screen currently aligned at tile position */
11640 if (game.centered_player_nr_next == -1)
11642 setScreenCenteredToAllPlayers(&sx, &sy);
11646 sx = stored_player[game.centered_player_nr_next].jx;
11647 sy = stored_player[game.centered_player_nr_next].jy;
11650 game.centered_player_nr = game.centered_player_nr_next;
11651 game.set_centered_player = FALSE;
11653 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11654 DrawGameDoorValues();
11657 for (i = 0; i < MAX_PLAYERS; i++)
11659 int actual_player_action = stored_player[i].effective_action;
11662 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11663 - rnd_equinox_tetrachloride 048
11664 - rnd_equinox_tetrachloride_ii 096
11665 - rnd_emanuel_schmieg 002
11666 - doctor_sloan_ww 001, 020
11668 if (stored_player[i].MovPos == 0)
11669 CheckGravityMovement(&stored_player[i]);
11672 /* overwrite programmed action with tape action */
11673 if (stored_player[i].programmed_action)
11674 actual_player_action = stored_player[i].programmed_action;
11676 PlayerActions(&stored_player[i], actual_player_action);
11678 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11681 ScrollScreen(NULL, SCROLL_GO_ON);
11683 /* for backwards compatibility, the following code emulates a fixed bug that
11684 occured when pushing elements (causing elements that just made their last
11685 pushing step to already (if possible) make their first falling step in the
11686 same game frame, which is bad); this code is also needed to use the famous
11687 "spring push bug" which is used in older levels and might be wanted to be
11688 used also in newer levels, but in this case the buggy pushing code is only
11689 affecting the "spring" element and no other elements */
11691 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11693 for (i = 0; i < MAX_PLAYERS; i++)
11695 struct PlayerInfo *player = &stored_player[i];
11696 int x = player->jx;
11697 int y = player->jy;
11699 if (player->active && player->is_pushing && player->is_moving &&
11701 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11702 Feld[x][y] == EL_SPRING))
11704 ContinueMoving(x, y);
11706 /* continue moving after pushing (this is actually a bug) */
11707 if (!IS_MOVING(x, y))
11708 Stop[x][y] = FALSE;
11713 SCAN_PLAYFIELD(x, y)
11715 ChangeCount[x][y] = 0;
11716 ChangeEvent[x][y] = -1;
11718 /* this must be handled before main playfield loop */
11719 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11722 if (MovDelay[x][y] <= 0)
11726 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11729 if (MovDelay[x][y] <= 0)
11732 TEST_DrawLevelField(x, y);
11734 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11739 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11741 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11742 printf("GameActions(): This should never happen!\n");
11744 ChangePage[x][y] = -1;
11748 Stop[x][y] = FALSE;
11749 if (WasJustMoving[x][y] > 0)
11750 WasJustMoving[x][y]--;
11751 if (WasJustFalling[x][y] > 0)
11752 WasJustFalling[x][y]--;
11753 if (CheckCollision[x][y] > 0)
11754 CheckCollision[x][y]--;
11755 if (CheckImpact[x][y] > 0)
11756 CheckImpact[x][y]--;
11760 /* reset finished pushing action (not done in ContinueMoving() to allow
11761 continuous pushing animation for elements with zero push delay) */
11762 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11764 ResetGfxAnimation(x, y);
11765 TEST_DrawLevelField(x, y);
11769 if (IS_BLOCKED(x, y))
11773 Blocked2Moving(x, y, &oldx, &oldy);
11774 if (!IS_MOVING(oldx, oldy))
11776 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11777 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11778 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11779 printf("GameActions(): This should never happen!\n");
11785 SCAN_PLAYFIELD(x, y)
11787 element = Feld[x][y];
11788 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11789 last_gfx_frame = GfxFrame[x][y];
11791 ResetGfxFrame(x, y);
11793 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11794 DrawLevelGraphicAnimation(x, y, graphic);
11796 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11797 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11798 ResetRandomAnimationValue(x, y);
11800 SetRandomAnimationValue(x, y);
11802 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11804 if (IS_INACTIVE(element))
11806 if (IS_ANIMATED(graphic))
11807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11812 /* this may take place after moving, so 'element' may have changed */
11813 if (IS_CHANGING(x, y) &&
11814 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11816 int page = element_info[element].event_page_nr[CE_DELAY];
11818 HandleElementChange(x, y, page);
11820 element = Feld[x][y];
11821 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11824 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11828 element = Feld[x][y];
11829 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11831 if (IS_ANIMATED(graphic) &&
11832 !IS_MOVING(x, y) &&
11834 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11836 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11837 TEST_DrawTwinkleOnField(x, y);
11839 else if (element == EL_ACID)
11842 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11844 else if ((element == EL_EXIT_OPEN ||
11845 element == EL_EM_EXIT_OPEN ||
11846 element == EL_SP_EXIT_OPEN ||
11847 element == EL_STEEL_EXIT_OPEN ||
11848 element == EL_EM_STEEL_EXIT_OPEN ||
11849 element == EL_SP_TERMINAL ||
11850 element == EL_SP_TERMINAL_ACTIVE ||
11851 element == EL_EXTRA_TIME ||
11852 element == EL_SHIELD_NORMAL ||
11853 element == EL_SHIELD_DEADLY) &&
11854 IS_ANIMATED(graphic))
11855 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11856 else if (IS_MOVING(x, y))
11857 ContinueMoving(x, y);
11858 else if (IS_ACTIVE_BOMB(element))
11859 CheckDynamite(x, y);
11860 else if (element == EL_AMOEBA_GROWING)
11861 AmoebeWaechst(x, y);
11862 else if (element == EL_AMOEBA_SHRINKING)
11863 AmoebaDisappearing(x, y);
11865 #if !USE_NEW_AMOEBA_CODE
11866 else if (IS_AMOEBALIVE(element))
11867 AmoebeAbleger(x, y);
11870 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11872 else if (element == EL_EXIT_CLOSED)
11874 else if (element == EL_EM_EXIT_CLOSED)
11876 else if (element == EL_STEEL_EXIT_CLOSED)
11877 CheckExitSteel(x, y);
11878 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11879 CheckExitSteelEM(x, y);
11880 else if (element == EL_SP_EXIT_CLOSED)
11882 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11883 element == EL_EXPANDABLE_STEELWALL_GROWING)
11884 MauerWaechst(x, y);
11885 else if (element == EL_EXPANDABLE_WALL ||
11886 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11887 element == EL_EXPANDABLE_WALL_VERTICAL ||
11888 element == EL_EXPANDABLE_WALL_ANY ||
11889 element == EL_BD_EXPANDABLE_WALL)
11890 MauerAbleger(x, y);
11891 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11892 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11893 element == EL_EXPANDABLE_STEELWALL_ANY)
11894 MauerAblegerStahl(x, y);
11895 else if (element == EL_FLAMES)
11896 CheckForDragon(x, y);
11897 else if (element == EL_EXPLOSION)
11898 ; /* drawing of correct explosion animation is handled separately */
11899 else if (element == EL_ELEMENT_SNAPPING ||
11900 element == EL_DIAGONAL_SHRINKING ||
11901 element == EL_DIAGONAL_GROWING)
11903 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11905 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11907 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11908 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11910 if (IS_BELT_ACTIVE(element))
11911 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11913 if (game.magic_wall_active)
11915 int jx = local_player->jx, jy = local_player->jy;
11917 /* play the element sound at the position nearest to the player */
11918 if ((element == EL_MAGIC_WALL_FULL ||
11919 element == EL_MAGIC_WALL_ACTIVE ||
11920 element == EL_MAGIC_WALL_EMPTYING ||
11921 element == EL_BD_MAGIC_WALL_FULL ||
11922 element == EL_BD_MAGIC_WALL_ACTIVE ||
11923 element == EL_BD_MAGIC_WALL_EMPTYING ||
11924 element == EL_DC_MAGIC_WALL_FULL ||
11925 element == EL_DC_MAGIC_WALL_ACTIVE ||
11926 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11927 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11935 #if USE_NEW_AMOEBA_CODE
11936 /* new experimental amoeba growth stuff */
11937 if (!(FrameCounter % 8))
11939 static unsigned int random = 1684108901;
11941 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11943 x = RND(lev_fieldx);
11944 y = RND(lev_fieldy);
11945 element = Feld[x][y];
11947 if (!IS_PLAYER(x,y) &&
11948 (element == EL_EMPTY ||
11949 CAN_GROW_INTO(element) ||
11950 element == EL_QUICKSAND_EMPTY ||
11951 element == EL_QUICKSAND_FAST_EMPTY ||
11952 element == EL_ACID_SPLASH_LEFT ||
11953 element == EL_ACID_SPLASH_RIGHT))
11955 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11956 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11957 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11958 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11959 Feld[x][y] = EL_AMOEBA_DROP;
11962 random = random * 129 + 1;
11967 game.explosions_delayed = FALSE;
11969 SCAN_PLAYFIELD(x, y)
11971 element = Feld[x][y];
11973 if (ExplodeField[x][y])
11974 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11975 else if (element == EL_EXPLOSION)
11976 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11978 ExplodeField[x][y] = EX_TYPE_NONE;
11981 game.explosions_delayed = TRUE;
11983 if (game.magic_wall_active)
11985 if (!(game.magic_wall_time_left % 4))
11987 int element = Feld[magic_wall_x][magic_wall_y];
11989 if (element == EL_BD_MAGIC_WALL_FULL ||
11990 element == EL_BD_MAGIC_WALL_ACTIVE ||
11991 element == EL_BD_MAGIC_WALL_EMPTYING)
11992 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11993 else if (element == EL_DC_MAGIC_WALL_FULL ||
11994 element == EL_DC_MAGIC_WALL_ACTIVE ||
11995 element == EL_DC_MAGIC_WALL_EMPTYING)
11996 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11998 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12001 if (game.magic_wall_time_left > 0)
12003 game.magic_wall_time_left--;
12005 if (!game.magic_wall_time_left)
12007 SCAN_PLAYFIELD(x, y)
12009 element = Feld[x][y];
12011 if (element == EL_MAGIC_WALL_ACTIVE ||
12012 element == EL_MAGIC_WALL_FULL)
12014 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12015 TEST_DrawLevelField(x, y);
12017 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12018 element == EL_BD_MAGIC_WALL_FULL)
12020 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12021 TEST_DrawLevelField(x, y);
12023 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12024 element == EL_DC_MAGIC_WALL_FULL)
12026 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12027 TEST_DrawLevelField(x, y);
12031 game.magic_wall_active = FALSE;
12036 if (game.light_time_left > 0)
12038 game.light_time_left--;
12040 if (game.light_time_left == 0)
12041 RedrawAllLightSwitchesAndInvisibleElements();
12044 if (game.timegate_time_left > 0)
12046 game.timegate_time_left--;
12048 if (game.timegate_time_left == 0)
12049 CloseAllOpenTimegates();
12052 if (game.lenses_time_left > 0)
12054 game.lenses_time_left--;
12056 if (game.lenses_time_left == 0)
12057 RedrawAllInvisibleElementsForLenses();
12060 if (game.magnify_time_left > 0)
12062 game.magnify_time_left--;
12064 if (game.magnify_time_left == 0)
12065 RedrawAllInvisibleElementsForMagnifier();
12068 for (i = 0; i < MAX_PLAYERS; i++)
12070 struct PlayerInfo *player = &stored_player[i];
12072 if (SHIELD_ON(player))
12074 if (player->shield_deadly_time_left)
12075 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12076 else if (player->shield_normal_time_left)
12077 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12081 #if USE_DELAYED_GFX_REDRAW
12082 SCAN_PLAYFIELD(x, y)
12084 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12086 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12087 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12089 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12090 DrawLevelField(x, y);
12092 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12093 DrawLevelFieldCrumbled(x, y);
12095 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12096 DrawLevelFieldCrumbledNeighbours(x, y);
12098 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12099 DrawTwinkleOnField(x, y);
12102 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12107 PlayAllPlayersSound();
12109 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12111 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12113 local_player->show_envelope = 0;
12116 /* use random number generator in every frame to make it less predictable */
12117 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12121 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12123 int min_x = x, min_y = y, max_x = x, max_y = y;
12126 for (i = 0; i < MAX_PLAYERS; i++)
12128 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12130 if (!stored_player[i].active || &stored_player[i] == player)
12133 min_x = MIN(min_x, jx);
12134 min_y = MIN(min_y, jy);
12135 max_x = MAX(max_x, jx);
12136 max_y = MAX(max_y, jy);
12139 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12142 static boolean AllPlayersInVisibleScreen()
12146 for (i = 0; i < MAX_PLAYERS; i++)
12148 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12150 if (!stored_player[i].active)
12153 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12160 void ScrollLevel(int dx, int dy)
12162 int scroll_offset = 2 * TILEX_VAR;
12165 BlitBitmap(drawto_field, drawto_field,
12166 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12167 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12168 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12169 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12170 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12171 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12175 x = (dx == 1 ? BX1 : BX2);
12176 for (y = BY1; y <= BY2; y++)
12177 DrawScreenField(x, y);
12182 y = (dy == 1 ? BY1 : BY2);
12183 for (x = BX1; x <= BX2; x++)
12184 DrawScreenField(x, y);
12187 redraw_mask |= REDRAW_FIELD;
12190 static boolean canFallDown(struct PlayerInfo *player)
12192 int jx = player->jx, jy = player->jy;
12194 return (IN_LEV_FIELD(jx, jy + 1) &&
12195 (IS_FREE(jx, jy + 1) ||
12196 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12197 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12198 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12201 static boolean canPassField(int x, int y, int move_dir)
12203 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12204 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12205 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12206 int nextx = x + dx;
12207 int nexty = y + dy;
12208 int element = Feld[x][y];
12210 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12211 !CAN_MOVE(element) &&
12212 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12213 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12214 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12217 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12219 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12220 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12221 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12225 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12226 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12227 (IS_DIGGABLE(Feld[newx][newy]) ||
12228 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12229 canPassField(newx, newy, move_dir)));
12232 static void CheckGravityMovement(struct PlayerInfo *player)
12234 if (player->gravity && !player->programmed_action)
12236 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12237 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12238 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12239 int jx = player->jx, jy = player->jy;
12240 boolean player_is_moving_to_valid_field =
12241 (!player_is_snapping &&
12242 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12243 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12244 boolean player_can_fall_down = canFallDown(player);
12246 if (player_can_fall_down &&
12247 !player_is_moving_to_valid_field)
12248 player->programmed_action = MV_DOWN;
12252 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12254 return CheckGravityMovement(player);
12256 if (player->gravity && !player->programmed_action)
12258 int jx = player->jx, jy = player->jy;
12259 boolean field_under_player_is_free =
12260 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12261 boolean player_is_standing_on_valid_field =
12262 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12263 (IS_WALKABLE(Feld[jx][jy]) &&
12264 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12266 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12267 player->programmed_action = MV_DOWN;
12272 MovePlayerOneStep()
12273 -----------------------------------------------------------------------------
12274 dx, dy: direction (non-diagonal) to try to move the player to
12275 real_dx, real_dy: direction as read from input device (can be diagonal)
12278 boolean MovePlayerOneStep(struct PlayerInfo *player,
12279 int dx, int dy, int real_dx, int real_dy)
12281 int jx = player->jx, jy = player->jy;
12282 int new_jx = jx + dx, new_jy = jy + dy;
12284 boolean player_can_move = !player->cannot_move;
12286 if (!player->active || (!dx && !dy))
12287 return MP_NO_ACTION;
12289 player->MovDir = (dx < 0 ? MV_LEFT :
12290 dx > 0 ? MV_RIGHT :
12292 dy > 0 ? MV_DOWN : MV_NONE);
12294 if (!IN_LEV_FIELD(new_jx, new_jy))
12295 return MP_NO_ACTION;
12297 if (!player_can_move)
12299 if (player->MovPos == 0)
12301 player->is_moving = FALSE;
12302 player->is_digging = FALSE;
12303 player->is_collecting = FALSE;
12304 player->is_snapping = FALSE;
12305 player->is_pushing = FALSE;
12309 if (!network.enabled && game.centered_player_nr == -1 &&
12310 !AllPlayersInSight(player, new_jx, new_jy))
12311 return MP_NO_ACTION;
12313 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12314 if (can_move != MP_MOVING)
12317 /* check if DigField() has caused relocation of the player */
12318 if (player->jx != jx || player->jy != jy)
12319 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12321 StorePlayer[jx][jy] = 0;
12322 player->last_jx = jx;
12323 player->last_jy = jy;
12324 player->jx = new_jx;
12325 player->jy = new_jy;
12326 StorePlayer[new_jx][new_jy] = player->element_nr;
12328 if (player->move_delay_value_next != -1)
12330 player->move_delay_value = player->move_delay_value_next;
12331 player->move_delay_value_next = -1;
12335 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12337 player->step_counter++;
12339 PlayerVisit[jx][jy] = FrameCounter;
12341 player->is_moving = TRUE;
12344 /* should better be called in MovePlayer(), but this breaks some tapes */
12345 ScrollPlayer(player, SCROLL_INIT);
12351 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12353 int jx = player->jx, jy = player->jy;
12354 int old_jx = jx, old_jy = jy;
12355 int moved = MP_NO_ACTION;
12357 if (!player->active)
12362 if (player->MovPos == 0)
12364 player->is_moving = FALSE;
12365 player->is_digging = FALSE;
12366 player->is_collecting = FALSE;
12367 player->is_snapping = FALSE;
12368 player->is_pushing = FALSE;
12374 if (player->move_delay > 0)
12377 player->move_delay = -1; /* set to "uninitialized" value */
12379 /* store if player is automatically moved to next field */
12380 player->is_auto_moving = (player->programmed_action != MV_NONE);
12382 /* remove the last programmed player action */
12383 player->programmed_action = 0;
12385 if (player->MovPos)
12387 /* should only happen if pre-1.2 tape recordings are played */
12388 /* this is only for backward compatibility */
12390 int original_move_delay_value = player->move_delay_value;
12393 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12397 /* scroll remaining steps with finest movement resolution */
12398 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12400 while (player->MovPos)
12402 ScrollPlayer(player, SCROLL_GO_ON);
12403 ScrollScreen(NULL, SCROLL_GO_ON);
12405 AdvanceFrameAndPlayerCounters(player->index_nr);
12408 BackToFront_WithFrameDelay(0);
12411 player->move_delay_value = original_move_delay_value;
12414 player->is_active = FALSE;
12416 if (player->last_move_dir & MV_HORIZONTAL)
12418 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12419 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12423 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12424 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12427 if (!moved && !player->is_active)
12429 player->is_moving = FALSE;
12430 player->is_digging = FALSE;
12431 player->is_collecting = FALSE;
12432 player->is_snapping = FALSE;
12433 player->is_pushing = FALSE;
12439 if (moved & MP_MOVING && !ScreenMovPos &&
12440 (player->index_nr == game.centered_player_nr ||
12441 game.centered_player_nr == -1))
12443 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12444 int offset = game.scroll_delay_value;
12446 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12448 /* actual player has left the screen -- scroll in that direction */
12449 if (jx != old_jx) /* player has moved horizontally */
12450 scroll_x += (jx - old_jx);
12451 else /* player has moved vertically */
12452 scroll_y += (jy - old_jy);
12456 if (jx != old_jx) /* player has moved horizontally */
12458 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12459 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12460 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12462 /* don't scroll over playfield boundaries */
12463 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12464 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12466 /* don't scroll more than one field at a time */
12467 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12469 /* don't scroll against the player's moving direction */
12470 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12471 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12472 scroll_x = old_scroll_x;
12474 else /* player has moved vertically */
12476 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12477 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12478 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12480 /* don't scroll over playfield boundaries */
12481 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12482 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12484 /* don't scroll more than one field at a time */
12485 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12487 /* don't scroll against the player's moving direction */
12488 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12489 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12490 scroll_y = old_scroll_y;
12494 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12496 if (!network.enabled && game.centered_player_nr == -1 &&
12497 !AllPlayersInVisibleScreen())
12499 scroll_x = old_scroll_x;
12500 scroll_y = old_scroll_y;
12504 ScrollScreen(player, SCROLL_INIT);
12505 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12510 player->StepFrame = 0;
12512 if (moved & MP_MOVING)
12514 if (old_jx != jx && old_jy == jy)
12515 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12516 else if (old_jx == jx && old_jy != jy)
12517 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12519 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12521 player->last_move_dir = player->MovDir;
12522 player->is_moving = TRUE;
12523 player->is_snapping = FALSE;
12524 player->is_switching = FALSE;
12525 player->is_dropping = FALSE;
12526 player->is_dropping_pressed = FALSE;
12527 player->drop_pressed_delay = 0;
12530 /* should better be called here than above, but this breaks some tapes */
12531 ScrollPlayer(player, SCROLL_INIT);
12536 CheckGravityMovementWhenNotMoving(player);
12538 player->is_moving = FALSE;
12540 /* at this point, the player is allowed to move, but cannot move right now
12541 (e.g. because of something blocking the way) -- ensure that the player
12542 is also allowed to move in the next frame (in old versions before 3.1.1,
12543 the player was forced to wait again for eight frames before next try) */
12545 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12546 player->move_delay = 0; /* allow direct movement in the next frame */
12549 if (player->move_delay == -1) /* not yet initialized by DigField() */
12550 player->move_delay = player->move_delay_value;
12552 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12554 TestIfPlayerTouchesBadThing(jx, jy);
12555 TestIfPlayerTouchesCustomElement(jx, jy);
12558 if (!player->active)
12559 RemovePlayer(player);
12564 void ScrollPlayer(struct PlayerInfo *player, int mode)
12566 int jx = player->jx, jy = player->jy;
12567 int last_jx = player->last_jx, last_jy = player->last_jy;
12568 int move_stepsize = TILEX / player->move_delay_value;
12570 if (!player->active)
12573 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12576 if (mode == SCROLL_INIT)
12578 player->actual_frame_counter = FrameCounter;
12579 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12581 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12582 Feld[last_jx][last_jy] == EL_EMPTY)
12584 int last_field_block_delay = 0; /* start with no blocking at all */
12585 int block_delay_adjustment = player->block_delay_adjustment;
12587 /* if player blocks last field, add delay for exactly one move */
12588 if (player->block_last_field)
12590 last_field_block_delay += player->move_delay_value;
12592 /* when blocking enabled, prevent moving up despite gravity */
12593 if (player->gravity && player->MovDir == MV_UP)
12594 block_delay_adjustment = -1;
12597 /* add block delay adjustment (also possible when not blocking) */
12598 last_field_block_delay += block_delay_adjustment;
12600 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12601 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12604 if (player->MovPos != 0) /* player has not yet reached destination */
12607 else if (!FrameReached(&player->actual_frame_counter, 1))
12610 if (player->MovPos != 0)
12612 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12613 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12615 /* before DrawPlayer() to draw correct player graphic for this case */
12616 if (player->MovPos == 0)
12617 CheckGravityMovement(player);
12620 if (player->MovPos == 0) /* player reached destination field */
12622 if (player->move_delay_reset_counter > 0)
12624 player->move_delay_reset_counter--;
12626 if (player->move_delay_reset_counter == 0)
12628 /* continue with normal speed after quickly moving through gate */
12629 HALVE_PLAYER_SPEED(player);
12631 /* be able to make the next move without delay */
12632 player->move_delay = 0;
12636 player->last_jx = jx;
12637 player->last_jy = jy;
12639 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12640 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12641 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12642 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12643 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12644 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12645 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12646 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12648 ExitPlayer(player);
12650 if ((local_player->friends_still_needed == 0 ||
12651 IS_SP_ELEMENT(Feld[jx][jy])) &&
12653 PlayerWins(local_player);
12656 /* this breaks one level: "machine", level 000 */
12658 int move_direction = player->MovDir;
12659 int enter_side = MV_DIR_OPPOSITE(move_direction);
12660 int leave_side = move_direction;
12661 int old_jx = last_jx;
12662 int old_jy = last_jy;
12663 int old_element = Feld[old_jx][old_jy];
12664 int new_element = Feld[jx][jy];
12666 if (IS_CUSTOM_ELEMENT(old_element))
12667 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12669 player->index_bit, leave_side);
12671 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12672 CE_PLAYER_LEAVES_X,
12673 player->index_bit, leave_side);
12675 if (IS_CUSTOM_ELEMENT(new_element))
12676 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12677 player->index_bit, enter_side);
12679 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12680 CE_PLAYER_ENTERS_X,
12681 player->index_bit, enter_side);
12683 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12684 CE_MOVE_OF_X, move_direction);
12687 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12689 TestIfPlayerTouchesBadThing(jx, jy);
12690 TestIfPlayerTouchesCustomElement(jx, jy);
12692 /* needed because pushed element has not yet reached its destination,
12693 so it would trigger a change event at its previous field location */
12694 if (!player->is_pushing)
12695 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12697 if (!player->active)
12698 RemovePlayer(player);
12701 if (!local_player->LevelSolved && level.use_step_counter)
12711 if (TimeLeft <= 10 && setup.time_limit)
12712 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12714 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12716 DisplayGameControlValues();
12718 if (!TimeLeft && setup.time_limit)
12719 for (i = 0; i < MAX_PLAYERS; i++)
12720 KillPlayer(&stored_player[i]);
12722 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12724 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12726 DisplayGameControlValues();
12730 if (tape.single_step && tape.recording && !tape.pausing &&
12731 !player->programmed_action)
12732 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12734 if (!player->programmed_action)
12735 CheckSaveEngineSnapshot(player);
12739 void ScrollScreen(struct PlayerInfo *player, int mode)
12741 static unsigned int screen_frame_counter = 0;
12743 if (mode == SCROLL_INIT)
12745 /* set scrolling step size according to actual player's moving speed */
12746 ScrollStepSize = TILEX / player->move_delay_value;
12748 screen_frame_counter = FrameCounter;
12749 ScreenMovDir = player->MovDir;
12750 ScreenMovPos = player->MovPos;
12751 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12754 else if (!FrameReached(&screen_frame_counter, 1))
12759 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12760 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12761 redraw_mask |= REDRAW_FIELD;
12764 ScreenMovDir = MV_NONE;
12767 void TestIfPlayerTouchesCustomElement(int x, int y)
12769 static int xy[4][2] =
12776 static int trigger_sides[4][2] =
12778 /* center side border side */
12779 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12780 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12781 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12782 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12784 static int touch_dir[4] =
12786 MV_LEFT | MV_RIGHT,
12791 int center_element = Feld[x][y]; /* should always be non-moving! */
12794 for (i = 0; i < NUM_DIRECTIONS; i++)
12796 int xx = x + xy[i][0];
12797 int yy = y + xy[i][1];
12798 int center_side = trigger_sides[i][0];
12799 int border_side = trigger_sides[i][1];
12800 int border_element;
12802 if (!IN_LEV_FIELD(xx, yy))
12805 if (IS_PLAYER(x, y)) /* player found at center element */
12807 struct PlayerInfo *player = PLAYERINFO(x, y);
12809 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12810 border_element = Feld[xx][yy]; /* may be moving! */
12811 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12812 border_element = Feld[xx][yy];
12813 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12814 border_element = MovingOrBlocked2Element(xx, yy);
12816 continue; /* center and border element do not touch */
12818 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12819 player->index_bit, border_side);
12820 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12821 CE_PLAYER_TOUCHES_X,
12822 player->index_bit, border_side);
12825 /* use player element that is initially defined in the level playfield,
12826 not the player element that corresponds to the runtime player number
12827 (example: a level that contains EL_PLAYER_3 as the only player would
12828 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12829 int player_element = PLAYERINFO(x, y)->initial_element;
12831 CheckElementChangeBySide(xx, yy, border_element, player_element,
12832 CE_TOUCHING_X, border_side);
12835 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12837 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12839 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12841 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12842 continue; /* center and border element do not touch */
12845 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12846 player->index_bit, center_side);
12847 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12848 CE_PLAYER_TOUCHES_X,
12849 player->index_bit, center_side);
12852 /* use player element that is initially defined in the level playfield,
12853 not the player element that corresponds to the runtime player number
12854 (example: a level that contains EL_PLAYER_3 as the only player would
12855 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12856 int player_element = PLAYERINFO(xx, yy)->initial_element;
12858 CheckElementChangeBySide(x, y, center_element, player_element,
12859 CE_TOUCHING_X, center_side);
12867 void TestIfElementTouchesCustomElement(int x, int y)
12869 static int xy[4][2] =
12876 static int trigger_sides[4][2] =
12878 /* center side border side */
12879 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12880 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12881 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12882 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12884 static int touch_dir[4] =
12886 MV_LEFT | MV_RIGHT,
12891 boolean change_center_element = FALSE;
12892 int center_element = Feld[x][y]; /* should always be non-moving! */
12893 int border_element_old[NUM_DIRECTIONS];
12896 for (i = 0; i < NUM_DIRECTIONS; i++)
12898 int xx = x + xy[i][0];
12899 int yy = y + xy[i][1];
12900 int border_element;
12902 border_element_old[i] = -1;
12904 if (!IN_LEV_FIELD(xx, yy))
12907 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12908 border_element = Feld[xx][yy]; /* may be moving! */
12909 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12910 border_element = Feld[xx][yy];
12911 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12912 border_element = MovingOrBlocked2Element(xx, yy);
12914 continue; /* center and border element do not touch */
12916 border_element_old[i] = border_element;
12919 for (i = 0; i < NUM_DIRECTIONS; i++)
12921 int xx = x + xy[i][0];
12922 int yy = y + xy[i][1];
12923 int center_side = trigger_sides[i][0];
12924 int border_element = border_element_old[i];
12926 if (border_element == -1)
12929 /* check for change of border element */
12930 CheckElementChangeBySide(xx, yy, border_element, center_element,
12931 CE_TOUCHING_X, center_side);
12933 /* (center element cannot be player, so we dont have to check this here) */
12936 for (i = 0; i < NUM_DIRECTIONS; i++)
12938 int xx = x + xy[i][0];
12939 int yy = y + xy[i][1];
12940 int border_side = trigger_sides[i][1];
12941 int border_element = border_element_old[i];
12943 if (border_element == -1)
12946 /* check for change of center element (but change it only once) */
12947 if (!change_center_element)
12948 change_center_element =
12949 CheckElementChangeBySide(x, y, center_element, border_element,
12950 CE_TOUCHING_X, border_side);
12952 if (IS_PLAYER(xx, yy))
12954 /* use player element that is initially defined in the level playfield,
12955 not the player element that corresponds to the runtime player number
12956 (example: a level that contains EL_PLAYER_3 as the only player would
12957 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12958 int player_element = PLAYERINFO(xx, yy)->initial_element;
12960 CheckElementChangeBySide(x, y, center_element, player_element,
12961 CE_TOUCHING_X, border_side);
12966 void TestIfElementHitsCustomElement(int x, int y, int direction)
12968 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12969 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12970 int hitx = x + dx, hity = y + dy;
12971 int hitting_element = Feld[x][y];
12972 int touched_element;
12974 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12977 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12978 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12980 if (IN_LEV_FIELD(hitx, hity))
12982 int opposite_direction = MV_DIR_OPPOSITE(direction);
12983 int hitting_side = direction;
12984 int touched_side = opposite_direction;
12985 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12986 MovDir[hitx][hity] != direction ||
12987 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12993 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12994 CE_HITTING_X, touched_side);
12996 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12997 CE_HIT_BY_X, hitting_side);
12999 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13000 CE_HIT_BY_SOMETHING, opposite_direction);
13002 if (IS_PLAYER(hitx, hity))
13004 /* use player element that is initially defined in the level playfield,
13005 not the player element that corresponds to the runtime player number
13006 (example: a level that contains EL_PLAYER_3 as the only player would
13007 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13008 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13010 CheckElementChangeBySide(x, y, hitting_element, player_element,
13011 CE_HITTING_X, touched_side);
13016 /* "hitting something" is also true when hitting the playfield border */
13017 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13018 CE_HITTING_SOMETHING, direction);
13021 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13023 int i, kill_x = -1, kill_y = -1;
13025 int bad_element = -1;
13026 static int test_xy[4][2] =
13033 static int test_dir[4] =
13041 for (i = 0; i < NUM_DIRECTIONS; i++)
13043 int test_x, test_y, test_move_dir, test_element;
13045 test_x = good_x + test_xy[i][0];
13046 test_y = good_y + test_xy[i][1];
13048 if (!IN_LEV_FIELD(test_x, test_y))
13052 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13054 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13056 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13057 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13059 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13060 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13064 bad_element = test_element;
13070 if (kill_x != -1 || kill_y != -1)
13072 if (IS_PLAYER(good_x, good_y))
13074 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13076 if (player->shield_deadly_time_left > 0 &&
13077 !IS_INDESTRUCTIBLE(bad_element))
13078 Bang(kill_x, kill_y);
13079 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13080 KillPlayer(player);
13083 Bang(good_x, good_y);
13087 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13089 int i, kill_x = -1, kill_y = -1;
13090 int bad_element = Feld[bad_x][bad_y];
13091 static int test_xy[4][2] =
13098 static int touch_dir[4] =
13100 MV_LEFT | MV_RIGHT,
13105 static int test_dir[4] =
13113 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13116 for (i = 0; i < NUM_DIRECTIONS; i++)
13118 int test_x, test_y, test_move_dir, test_element;
13120 test_x = bad_x + test_xy[i][0];
13121 test_y = bad_y + test_xy[i][1];
13123 if (!IN_LEV_FIELD(test_x, test_y))
13127 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13129 test_element = Feld[test_x][test_y];
13131 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13132 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13134 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13135 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13137 /* good thing is player or penguin that does not move away */
13138 if (IS_PLAYER(test_x, test_y))
13140 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13142 if (bad_element == EL_ROBOT && player->is_moving)
13143 continue; /* robot does not kill player if he is moving */
13145 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13147 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13148 continue; /* center and border element do not touch */
13156 else if (test_element == EL_PENGUIN)
13166 if (kill_x != -1 || kill_y != -1)
13168 if (IS_PLAYER(kill_x, kill_y))
13170 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13172 if (player->shield_deadly_time_left > 0 &&
13173 !IS_INDESTRUCTIBLE(bad_element))
13174 Bang(bad_x, bad_y);
13175 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13176 KillPlayer(player);
13179 Bang(kill_x, kill_y);
13183 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13185 int bad_element = Feld[bad_x][bad_y];
13186 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13187 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13188 int test_x = bad_x + dx, test_y = bad_y + dy;
13189 int test_move_dir, test_element;
13190 int kill_x = -1, kill_y = -1;
13192 if (!IN_LEV_FIELD(test_x, test_y))
13196 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13198 test_element = Feld[test_x][test_y];
13200 if (test_move_dir != bad_move_dir)
13202 /* good thing can be player or penguin that does not move away */
13203 if (IS_PLAYER(test_x, test_y))
13205 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13207 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13208 player as being hit when he is moving towards the bad thing, because
13209 the "get hit by" condition would be lost after the player stops) */
13210 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13211 return; /* player moves away from bad thing */
13216 else if (test_element == EL_PENGUIN)
13223 if (kill_x != -1 || kill_y != -1)
13225 if (IS_PLAYER(kill_x, kill_y))
13227 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13229 if (player->shield_deadly_time_left > 0 &&
13230 !IS_INDESTRUCTIBLE(bad_element))
13231 Bang(bad_x, bad_y);
13232 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13233 KillPlayer(player);
13236 Bang(kill_x, kill_y);
13240 void TestIfPlayerTouchesBadThing(int x, int y)
13242 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13245 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13247 TestIfGoodThingHitsBadThing(x, y, move_dir);
13250 void TestIfBadThingTouchesPlayer(int x, int y)
13252 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13255 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13257 TestIfBadThingHitsGoodThing(x, y, move_dir);
13260 void TestIfFriendTouchesBadThing(int x, int y)
13262 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13265 void TestIfBadThingTouchesFriend(int x, int y)
13267 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13270 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13272 int i, kill_x = bad_x, kill_y = bad_y;
13273 static int xy[4][2] =
13281 for (i = 0; i < NUM_DIRECTIONS; i++)
13285 x = bad_x + xy[i][0];
13286 y = bad_y + xy[i][1];
13287 if (!IN_LEV_FIELD(x, y))
13290 element = Feld[x][y];
13291 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13292 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13300 if (kill_x != bad_x || kill_y != bad_y)
13301 Bang(bad_x, bad_y);
13304 void KillPlayer(struct PlayerInfo *player)
13306 int jx = player->jx, jy = player->jy;
13308 if (!player->active)
13312 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13313 player->killed, player->active, player->reanimated);
13316 /* the following code was introduced to prevent an infinite loop when calling
13318 -> CheckTriggeredElementChangeExt()
13319 -> ExecuteCustomElementAction()
13321 -> (infinitely repeating the above sequence of function calls)
13322 which occurs when killing the player while having a CE with the setting
13323 "kill player X when explosion of <player X>"; the solution using a new
13324 field "player->killed" was chosen for backwards compatibility, although
13325 clever use of the fields "player->active" etc. would probably also work */
13327 if (player->killed)
13331 player->killed = TRUE;
13333 /* remove accessible field at the player's position */
13334 Feld[jx][jy] = EL_EMPTY;
13336 /* deactivate shield (else Bang()/Explode() would not work right) */
13337 player->shield_normal_time_left = 0;
13338 player->shield_deadly_time_left = 0;
13341 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13342 player->killed, player->active, player->reanimated);
13348 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13349 player->killed, player->active, player->reanimated);
13352 if (player->reanimated) /* killed player may have been reanimated */
13353 player->killed = player->reanimated = FALSE;
13355 BuryPlayer(player);
13358 static void KillPlayerUnlessEnemyProtected(int x, int y)
13360 if (!PLAYER_ENEMY_PROTECTED(x, y))
13361 KillPlayer(PLAYERINFO(x, y));
13364 static void KillPlayerUnlessExplosionProtected(int x, int y)
13366 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13367 KillPlayer(PLAYERINFO(x, y));
13370 void BuryPlayer(struct PlayerInfo *player)
13372 int jx = player->jx, jy = player->jy;
13374 if (!player->active)
13377 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13378 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13380 player->GameOver = TRUE;
13381 RemovePlayer(player);
13384 void RemovePlayer(struct PlayerInfo *player)
13386 int jx = player->jx, jy = player->jy;
13387 int i, found = FALSE;
13389 player->present = FALSE;
13390 player->active = FALSE;
13392 if (!ExplodeField[jx][jy])
13393 StorePlayer[jx][jy] = 0;
13395 if (player->is_moving)
13396 TEST_DrawLevelField(player->last_jx, player->last_jy);
13398 for (i = 0; i < MAX_PLAYERS; i++)
13399 if (stored_player[i].active)
13403 AllPlayersGone = TRUE;
13409 void ExitPlayer(struct PlayerInfo *player)
13411 DrawPlayer(player); /* needed here only to cleanup last field */
13412 RemovePlayer(player);
13415 static void setFieldForSnapping(int x, int y, int element, int direction)
13417 struct ElementInfo *ei = &element_info[element];
13418 int direction_bit = MV_DIR_TO_BIT(direction);
13419 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13420 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13421 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13423 Feld[x][y] = EL_ELEMENT_SNAPPING;
13424 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13426 ResetGfxAnimation(x, y);
13428 GfxElement[x][y] = element;
13429 GfxAction[x][y] = action;
13430 GfxDir[x][y] = direction;
13431 GfxFrame[x][y] = -1;
13435 =============================================================================
13436 checkDiagonalPushing()
13437 -----------------------------------------------------------------------------
13438 check if diagonal input device direction results in pushing of object
13439 (by checking if the alternative direction is walkable, diggable, ...)
13440 =============================================================================
13443 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13444 int x, int y, int real_dx, int real_dy)
13446 int jx, jy, dx, dy, xx, yy;
13448 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13451 /* diagonal direction: check alternative direction */
13456 xx = jx + (dx == 0 ? real_dx : 0);
13457 yy = jy + (dy == 0 ? real_dy : 0);
13459 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13463 =============================================================================
13465 -----------------------------------------------------------------------------
13466 x, y: field next to player (non-diagonal) to try to dig to
13467 real_dx, real_dy: direction as read from input device (can be diagonal)
13468 =============================================================================
13471 static int DigField(struct PlayerInfo *player,
13472 int oldx, int oldy, int x, int y,
13473 int real_dx, int real_dy, int mode)
13475 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13476 boolean player_was_pushing = player->is_pushing;
13477 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13478 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13479 int jx = oldx, jy = oldy;
13480 int dx = x - jx, dy = y - jy;
13481 int nextx = x + dx, nexty = y + dy;
13482 int move_direction = (dx == -1 ? MV_LEFT :
13483 dx == +1 ? MV_RIGHT :
13485 dy == +1 ? MV_DOWN : MV_NONE);
13486 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13487 int dig_side = MV_DIR_OPPOSITE(move_direction);
13488 int old_element = Feld[jx][jy];
13489 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13492 if (is_player) /* function can also be called by EL_PENGUIN */
13494 if (player->MovPos == 0)
13496 player->is_digging = FALSE;
13497 player->is_collecting = FALSE;
13500 if (player->MovPos == 0) /* last pushing move finished */
13501 player->is_pushing = FALSE;
13503 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13505 player->is_switching = FALSE;
13506 player->push_delay = -1;
13508 return MP_NO_ACTION;
13512 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13513 old_element = Back[jx][jy];
13515 /* in case of element dropped at player position, check background */
13516 else if (Back[jx][jy] != EL_EMPTY &&
13517 game.engine_version >= VERSION_IDENT(2,2,0,0))
13518 old_element = Back[jx][jy];
13520 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13521 return MP_NO_ACTION; /* field has no opening in this direction */
13523 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13524 return MP_NO_ACTION; /* field has no opening in this direction */
13526 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13530 Feld[jx][jy] = player->artwork_element;
13531 InitMovingField(jx, jy, MV_DOWN);
13532 Store[jx][jy] = EL_ACID;
13533 ContinueMoving(jx, jy);
13534 BuryPlayer(player);
13536 return MP_DONT_RUN_INTO;
13539 if (player_can_move && DONT_RUN_INTO(element))
13541 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13543 return MP_DONT_RUN_INTO;
13546 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13547 return MP_NO_ACTION;
13549 collect_count = element_info[element].collect_count_initial;
13551 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13552 return MP_NO_ACTION;
13554 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13555 player_can_move = player_can_move_or_snap;
13557 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13558 game.engine_version >= VERSION_IDENT(2,2,0,0))
13560 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13561 player->index_bit, dig_side);
13562 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13563 player->index_bit, dig_side);
13565 if (element == EL_DC_LANDMINE)
13568 if (Feld[x][y] != element) /* field changed by snapping */
13571 return MP_NO_ACTION;
13574 if (player->gravity && is_player && !player->is_auto_moving &&
13575 canFallDown(player) && move_direction != MV_DOWN &&
13576 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13577 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13579 if (player_can_move &&
13580 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13582 int sound_element = SND_ELEMENT(element);
13583 int sound_action = ACTION_WALKING;
13585 if (IS_RND_GATE(element))
13587 if (!player->key[RND_GATE_NR(element)])
13588 return MP_NO_ACTION;
13590 else if (IS_RND_GATE_GRAY(element))
13592 if (!player->key[RND_GATE_GRAY_NR(element)])
13593 return MP_NO_ACTION;
13595 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13597 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13598 return MP_NO_ACTION;
13600 else if (element == EL_EXIT_OPEN ||
13601 element == EL_EM_EXIT_OPEN ||
13602 element == EL_EM_EXIT_OPENING ||
13603 element == EL_STEEL_EXIT_OPEN ||
13604 element == EL_EM_STEEL_EXIT_OPEN ||
13605 element == EL_EM_STEEL_EXIT_OPENING ||
13606 element == EL_SP_EXIT_OPEN ||
13607 element == EL_SP_EXIT_OPENING)
13609 sound_action = ACTION_PASSING; /* player is passing exit */
13611 else if (element == EL_EMPTY)
13613 sound_action = ACTION_MOVING; /* nothing to walk on */
13616 /* play sound from background or player, whatever is available */
13617 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13618 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13620 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13622 else if (player_can_move &&
13623 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13625 if (!ACCESS_FROM(element, opposite_direction))
13626 return MP_NO_ACTION; /* field not accessible from this direction */
13628 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13629 return MP_NO_ACTION;
13631 if (IS_EM_GATE(element))
13633 if (!player->key[EM_GATE_NR(element)])
13634 return MP_NO_ACTION;
13636 else if (IS_EM_GATE_GRAY(element))
13638 if (!player->key[EM_GATE_GRAY_NR(element)])
13639 return MP_NO_ACTION;
13641 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13643 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13644 return MP_NO_ACTION;
13646 else if (IS_EMC_GATE(element))
13648 if (!player->key[EMC_GATE_NR(element)])
13649 return MP_NO_ACTION;
13651 else if (IS_EMC_GATE_GRAY(element))
13653 if (!player->key[EMC_GATE_GRAY_NR(element)])
13654 return MP_NO_ACTION;
13656 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13658 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13659 return MP_NO_ACTION;
13661 else if (element == EL_DC_GATE_WHITE ||
13662 element == EL_DC_GATE_WHITE_GRAY ||
13663 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13665 if (player->num_white_keys == 0)
13666 return MP_NO_ACTION;
13668 player->num_white_keys--;
13670 else if (IS_SP_PORT(element))
13672 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13673 element == EL_SP_GRAVITY_PORT_RIGHT ||
13674 element == EL_SP_GRAVITY_PORT_UP ||
13675 element == EL_SP_GRAVITY_PORT_DOWN)
13676 player->gravity = !player->gravity;
13677 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13678 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13679 element == EL_SP_GRAVITY_ON_PORT_UP ||
13680 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13681 player->gravity = TRUE;
13682 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13683 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13684 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13685 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13686 player->gravity = FALSE;
13689 /* automatically move to the next field with double speed */
13690 player->programmed_action = move_direction;
13692 if (player->move_delay_reset_counter == 0)
13694 player->move_delay_reset_counter = 2; /* two double speed steps */
13696 DOUBLE_PLAYER_SPEED(player);
13699 PlayLevelSoundAction(x, y, ACTION_PASSING);
13701 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13705 if (mode != DF_SNAP)
13707 GfxElement[x][y] = GFX_ELEMENT(element);
13708 player->is_digging = TRUE;
13711 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13713 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13714 player->index_bit, dig_side);
13716 if (mode == DF_SNAP)
13718 if (level.block_snap_field)
13719 setFieldForSnapping(x, y, element, move_direction);
13721 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13723 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13724 player->index_bit, dig_side);
13727 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13731 if (is_player && mode != DF_SNAP)
13733 GfxElement[x][y] = element;
13734 player->is_collecting = TRUE;
13737 if (element == EL_SPEED_PILL)
13739 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13741 else if (element == EL_EXTRA_TIME && level.time > 0)
13743 TimeLeft += level.extra_time;
13745 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13747 DisplayGameControlValues();
13749 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13751 player->shield_normal_time_left += level.shield_normal_time;
13752 if (element == EL_SHIELD_DEADLY)
13753 player->shield_deadly_time_left += level.shield_deadly_time;
13755 else if (element == EL_DYNAMITE ||
13756 element == EL_EM_DYNAMITE ||
13757 element == EL_SP_DISK_RED)
13759 if (player->inventory_size < MAX_INVENTORY_SIZE)
13760 player->inventory_element[player->inventory_size++] = element;
13762 DrawGameDoorValues();
13764 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13766 player->dynabomb_count++;
13767 player->dynabombs_left++;
13769 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13771 player->dynabomb_size++;
13773 else if (element == EL_DYNABOMB_INCREASE_POWER)
13775 player->dynabomb_xl = TRUE;
13777 else if (IS_KEY(element))
13779 player->key[KEY_NR(element)] = TRUE;
13781 DrawGameDoorValues();
13783 else if (element == EL_DC_KEY_WHITE)
13785 player->num_white_keys++;
13787 /* display white keys? */
13788 /* DrawGameDoorValues(); */
13790 else if (IS_ENVELOPE(element))
13792 player->show_envelope = element;
13794 else if (element == EL_EMC_LENSES)
13796 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13798 RedrawAllInvisibleElementsForLenses();
13800 else if (element == EL_EMC_MAGNIFIER)
13802 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13804 RedrawAllInvisibleElementsForMagnifier();
13806 else if (IS_DROPPABLE(element) ||
13807 IS_THROWABLE(element)) /* can be collected and dropped */
13811 if (collect_count == 0)
13812 player->inventory_infinite_element = element;
13814 for (i = 0; i < collect_count; i++)
13815 if (player->inventory_size < MAX_INVENTORY_SIZE)
13816 player->inventory_element[player->inventory_size++] = element;
13818 DrawGameDoorValues();
13820 else if (collect_count > 0)
13822 local_player->gems_still_needed -= collect_count;
13823 if (local_player->gems_still_needed < 0)
13824 local_player->gems_still_needed = 0;
13826 game.snapshot.collected_item = TRUE;
13828 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13830 DisplayGameControlValues();
13833 RaiseScoreElement(element);
13834 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13837 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13838 player->index_bit, dig_side);
13840 if (mode == DF_SNAP)
13842 if (level.block_snap_field)
13843 setFieldForSnapping(x, y, element, move_direction);
13845 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13847 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13848 player->index_bit, dig_side);
13851 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13853 if (mode == DF_SNAP && element != EL_BD_ROCK)
13854 return MP_NO_ACTION;
13856 if (CAN_FALL(element) && dy)
13857 return MP_NO_ACTION;
13859 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13860 !(element == EL_SPRING && level.use_spring_bug))
13861 return MP_NO_ACTION;
13863 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13864 ((move_direction & MV_VERTICAL &&
13865 ((element_info[element].move_pattern & MV_LEFT &&
13866 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13867 (element_info[element].move_pattern & MV_RIGHT &&
13868 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13869 (move_direction & MV_HORIZONTAL &&
13870 ((element_info[element].move_pattern & MV_UP &&
13871 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13872 (element_info[element].move_pattern & MV_DOWN &&
13873 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13874 return MP_NO_ACTION;
13876 /* do not push elements already moving away faster than player */
13877 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13878 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13879 return MP_NO_ACTION;
13881 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13883 if (player->push_delay_value == -1 || !player_was_pushing)
13884 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13886 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13888 if (player->push_delay_value == -1)
13889 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13891 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13893 if (!player->is_pushing)
13894 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13897 player->is_pushing = TRUE;
13898 player->is_active = TRUE;
13900 if (!(IN_LEV_FIELD(nextx, nexty) &&
13901 (IS_FREE(nextx, nexty) ||
13902 (IS_SB_ELEMENT(element) &&
13903 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13904 (IS_CUSTOM_ELEMENT(element) &&
13905 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13906 return MP_NO_ACTION;
13908 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13909 return MP_NO_ACTION;
13911 if (player->push_delay == -1) /* new pushing; restart delay */
13912 player->push_delay = 0;
13914 if (player->push_delay < player->push_delay_value &&
13915 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13916 element != EL_SPRING && element != EL_BALLOON)
13918 /* make sure that there is no move delay before next try to push */
13919 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13920 player->move_delay = 0;
13922 return MP_NO_ACTION;
13925 if (IS_CUSTOM_ELEMENT(element) &&
13926 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13928 if (!DigFieldByCE(nextx, nexty, element))
13929 return MP_NO_ACTION;
13932 if (IS_SB_ELEMENT(element))
13934 if (element == EL_SOKOBAN_FIELD_FULL)
13936 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13937 local_player->sokobanfields_still_needed++;
13940 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13942 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13943 local_player->sokobanfields_still_needed--;
13946 Feld[x][y] = EL_SOKOBAN_OBJECT;
13948 if (Back[x][y] == Back[nextx][nexty])
13949 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13950 else if (Back[x][y] != 0)
13951 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13954 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13957 if (local_player->sokobanfields_still_needed == 0 &&
13958 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13960 PlayerWins(player);
13962 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13966 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13968 InitMovingField(x, y, move_direction);
13969 GfxAction[x][y] = ACTION_PUSHING;
13971 if (mode == DF_SNAP)
13972 ContinueMoving(x, y);
13974 MovPos[x][y] = (dx != 0 ? dx : dy);
13976 Pushed[x][y] = TRUE;
13977 Pushed[nextx][nexty] = TRUE;
13979 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13980 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13982 player->push_delay_value = -1; /* get new value later */
13984 /* check for element change _after_ element has been pushed */
13985 if (game.use_change_when_pushing_bug)
13987 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13988 player->index_bit, dig_side);
13989 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13990 player->index_bit, dig_side);
13993 else if (IS_SWITCHABLE(element))
13995 if (PLAYER_SWITCHING(player, x, y))
13997 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13998 player->index_bit, dig_side);
14003 player->is_switching = TRUE;
14004 player->switch_x = x;
14005 player->switch_y = y;
14007 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14009 if (element == EL_ROBOT_WHEEL)
14011 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14015 game.robot_wheel_active = TRUE;
14017 TEST_DrawLevelField(x, y);
14019 else if (element == EL_SP_TERMINAL)
14023 SCAN_PLAYFIELD(xx, yy)
14025 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14029 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14031 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14033 ResetGfxAnimation(xx, yy);
14034 TEST_DrawLevelField(xx, yy);
14038 else if (IS_BELT_SWITCH(element))
14040 ToggleBeltSwitch(x, y);
14042 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14043 element == EL_SWITCHGATE_SWITCH_DOWN ||
14044 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14045 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14047 ToggleSwitchgateSwitch(x, y);
14049 else if (element == EL_LIGHT_SWITCH ||
14050 element == EL_LIGHT_SWITCH_ACTIVE)
14052 ToggleLightSwitch(x, y);
14054 else if (element == EL_TIMEGATE_SWITCH ||
14055 element == EL_DC_TIMEGATE_SWITCH)
14057 ActivateTimegateSwitch(x, y);
14059 else if (element == EL_BALLOON_SWITCH_LEFT ||
14060 element == EL_BALLOON_SWITCH_RIGHT ||
14061 element == EL_BALLOON_SWITCH_UP ||
14062 element == EL_BALLOON_SWITCH_DOWN ||
14063 element == EL_BALLOON_SWITCH_NONE ||
14064 element == EL_BALLOON_SWITCH_ANY)
14066 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14067 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14068 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14069 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14070 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14073 else if (element == EL_LAMP)
14075 Feld[x][y] = EL_LAMP_ACTIVE;
14076 local_player->lights_still_needed--;
14078 ResetGfxAnimation(x, y);
14079 TEST_DrawLevelField(x, y);
14081 else if (element == EL_TIME_ORB_FULL)
14083 Feld[x][y] = EL_TIME_ORB_EMPTY;
14085 if (level.time > 0 || level.use_time_orb_bug)
14087 TimeLeft += level.time_orb_time;
14088 game.no_time_limit = FALSE;
14090 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14092 DisplayGameControlValues();
14095 ResetGfxAnimation(x, y);
14096 TEST_DrawLevelField(x, y);
14098 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14099 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14103 game.ball_state = !game.ball_state;
14105 SCAN_PLAYFIELD(xx, yy)
14107 int e = Feld[xx][yy];
14109 if (game.ball_state)
14111 if (e == EL_EMC_MAGIC_BALL)
14112 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14113 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14114 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14118 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14119 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14120 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14121 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14126 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14127 player->index_bit, dig_side);
14129 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14130 player->index_bit, dig_side);
14132 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14133 player->index_bit, dig_side);
14139 if (!PLAYER_SWITCHING(player, x, y))
14141 player->is_switching = TRUE;
14142 player->switch_x = x;
14143 player->switch_y = y;
14145 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14146 player->index_bit, dig_side);
14147 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14148 player->index_bit, dig_side);
14150 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14151 player->index_bit, dig_side);
14152 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14153 player->index_bit, dig_side);
14156 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14157 player->index_bit, dig_side);
14158 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14159 player->index_bit, dig_side);
14161 return MP_NO_ACTION;
14164 player->push_delay = -1;
14166 if (is_player) /* function can also be called by EL_PENGUIN */
14168 if (Feld[x][y] != element) /* really digged/collected something */
14170 player->is_collecting = !player->is_digging;
14171 player->is_active = TRUE;
14178 static boolean DigFieldByCE(int x, int y, int digging_element)
14180 int element = Feld[x][y];
14182 if (!IS_FREE(x, y))
14184 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14185 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14188 /* no element can dig solid indestructible elements */
14189 if (IS_INDESTRUCTIBLE(element) &&
14190 !IS_DIGGABLE(element) &&
14191 !IS_COLLECTIBLE(element))
14194 if (AmoebaNr[x][y] &&
14195 (element == EL_AMOEBA_FULL ||
14196 element == EL_BD_AMOEBA ||
14197 element == EL_AMOEBA_GROWING))
14199 AmoebaCnt[AmoebaNr[x][y]]--;
14200 AmoebaCnt2[AmoebaNr[x][y]]--;
14203 if (IS_MOVING(x, y))
14204 RemoveMovingField(x, y);
14208 TEST_DrawLevelField(x, y);
14211 /* if digged element was about to explode, prevent the explosion */
14212 ExplodeField[x][y] = EX_TYPE_NONE;
14214 PlayLevelSoundAction(x, y, action);
14217 Store[x][y] = EL_EMPTY;
14219 /* this makes it possible to leave the removed element again */
14220 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14221 Store[x][y] = element;
14226 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14228 int jx = player->jx, jy = player->jy;
14229 int x = jx + dx, y = jy + dy;
14230 int snap_direction = (dx == -1 ? MV_LEFT :
14231 dx == +1 ? MV_RIGHT :
14233 dy == +1 ? MV_DOWN : MV_NONE);
14234 boolean can_continue_snapping = (level.continuous_snapping &&
14235 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14237 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14240 if (!player->active || !IN_LEV_FIELD(x, y))
14248 if (player->MovPos == 0)
14249 player->is_pushing = FALSE;
14251 player->is_snapping = FALSE;
14253 if (player->MovPos == 0)
14255 player->is_moving = FALSE;
14256 player->is_digging = FALSE;
14257 player->is_collecting = FALSE;
14263 /* prevent snapping with already pressed snap key when not allowed */
14264 if (player->is_snapping && !can_continue_snapping)
14267 player->MovDir = snap_direction;
14269 if (player->MovPos == 0)
14271 player->is_moving = FALSE;
14272 player->is_digging = FALSE;
14273 player->is_collecting = FALSE;
14276 player->is_dropping = FALSE;
14277 player->is_dropping_pressed = FALSE;
14278 player->drop_pressed_delay = 0;
14280 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14283 player->is_snapping = TRUE;
14284 player->is_active = TRUE;
14286 if (player->MovPos == 0)
14288 player->is_moving = FALSE;
14289 player->is_digging = FALSE;
14290 player->is_collecting = FALSE;
14293 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14294 TEST_DrawLevelField(player->last_jx, player->last_jy);
14296 TEST_DrawLevelField(x, y);
14301 static boolean DropElement(struct PlayerInfo *player)
14303 int old_element, new_element;
14304 int dropx = player->jx, dropy = player->jy;
14305 int drop_direction = player->MovDir;
14306 int drop_side = drop_direction;
14307 int drop_element = get_next_dropped_element(player);
14309 /* do not drop an element on top of another element; when holding drop key
14310 pressed without moving, dropped element must move away before the next
14311 element can be dropped (this is especially important if the next element
14312 is dynamite, which can be placed on background for historical reasons) */
14313 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14316 if (IS_THROWABLE(drop_element))
14318 dropx += GET_DX_FROM_DIR(drop_direction);
14319 dropy += GET_DY_FROM_DIR(drop_direction);
14321 if (!IN_LEV_FIELD(dropx, dropy))
14325 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14326 new_element = drop_element; /* default: no change when dropping */
14328 /* check if player is active, not moving and ready to drop */
14329 if (!player->active || player->MovPos || player->drop_delay > 0)
14332 /* check if player has anything that can be dropped */
14333 if (new_element == EL_UNDEFINED)
14336 /* only set if player has anything that can be dropped */
14337 player->is_dropping_pressed = TRUE;
14339 /* check if drop key was pressed long enough for EM style dynamite */
14340 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14343 /* check if anything can be dropped at the current position */
14344 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14347 /* collected custom elements can only be dropped on empty fields */
14348 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14351 if (old_element != EL_EMPTY)
14352 Back[dropx][dropy] = old_element; /* store old element on this field */
14354 ResetGfxAnimation(dropx, dropy);
14355 ResetRandomAnimationValue(dropx, dropy);
14357 if (player->inventory_size > 0 ||
14358 player->inventory_infinite_element != EL_UNDEFINED)
14360 if (player->inventory_size > 0)
14362 player->inventory_size--;
14364 DrawGameDoorValues();
14366 if (new_element == EL_DYNAMITE)
14367 new_element = EL_DYNAMITE_ACTIVE;
14368 else if (new_element == EL_EM_DYNAMITE)
14369 new_element = EL_EM_DYNAMITE_ACTIVE;
14370 else if (new_element == EL_SP_DISK_RED)
14371 new_element = EL_SP_DISK_RED_ACTIVE;
14374 Feld[dropx][dropy] = new_element;
14376 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14377 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14378 el2img(Feld[dropx][dropy]), 0);
14380 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14382 /* needed if previous element just changed to "empty" in the last frame */
14383 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14385 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14386 player->index_bit, drop_side);
14387 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14389 player->index_bit, drop_side);
14391 TestIfElementTouchesCustomElement(dropx, dropy);
14393 else /* player is dropping a dyna bomb */
14395 player->dynabombs_left--;
14397 Feld[dropx][dropy] = new_element;
14399 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14400 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14401 el2img(Feld[dropx][dropy]), 0);
14403 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14406 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14407 InitField_WithBug1(dropx, dropy, FALSE);
14409 new_element = Feld[dropx][dropy]; /* element might have changed */
14411 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14412 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14414 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14415 MovDir[dropx][dropy] = drop_direction;
14417 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14419 /* do not cause impact style collision by dropping elements that can fall */
14420 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14423 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14424 player->is_dropping = TRUE;
14426 player->drop_pressed_delay = 0;
14427 player->is_dropping_pressed = FALSE;
14429 player->drop_x = dropx;
14430 player->drop_y = dropy;
14435 /* ------------------------------------------------------------------------- */
14436 /* game sound playing functions */
14437 /* ------------------------------------------------------------------------- */
14439 static int *loop_sound_frame = NULL;
14440 static int *loop_sound_volume = NULL;
14442 void InitPlayLevelSound()
14444 int num_sounds = getSoundListSize();
14446 checked_free(loop_sound_frame);
14447 checked_free(loop_sound_volume);
14449 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14450 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14453 static void PlayLevelSound(int x, int y, int nr)
14455 int sx = SCREENX(x), sy = SCREENY(y);
14456 int volume, stereo_position;
14457 int max_distance = 8;
14458 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14460 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14461 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14464 if (!IN_LEV_FIELD(x, y) ||
14465 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14466 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14469 volume = SOUND_MAX_VOLUME;
14471 if (!IN_SCR_FIELD(sx, sy))
14473 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14474 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14476 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14479 stereo_position = (SOUND_MAX_LEFT +
14480 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14481 (SCR_FIELDX + 2 * max_distance));
14483 if (IS_LOOP_SOUND(nr))
14485 /* This assures that quieter loop sounds do not overwrite louder ones,
14486 while restarting sound volume comparison with each new game frame. */
14488 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14491 loop_sound_volume[nr] = volume;
14492 loop_sound_frame[nr] = FrameCounter;
14495 PlaySoundExt(nr, volume, stereo_position, type);
14498 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14500 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14501 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14502 y < LEVELY(BY1) ? LEVELY(BY1) :
14503 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14507 static void PlayLevelSoundAction(int x, int y, int action)
14509 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14512 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14514 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14516 if (sound_effect != SND_UNDEFINED)
14517 PlayLevelSound(x, y, sound_effect);
14520 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14523 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14525 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14526 PlayLevelSound(x, y, sound_effect);
14529 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14531 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14533 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14534 PlayLevelSound(x, y, sound_effect);
14537 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14539 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14541 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14542 StopSound(sound_effect);
14545 static int getLevelMusicNr()
14547 if (levelset.music[level_nr] != MUS_UNDEFINED)
14548 return levelset.music[level_nr]; /* from config file */
14550 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14553 static void FadeLevelSounds()
14558 static void FadeLevelMusic()
14560 int music_nr = getLevelMusicNr();
14561 char *curr_music = getCurrentlyPlayingMusicFilename();
14562 char *next_music = getMusicInfoEntryFilename(music_nr);
14564 if (!strEqual(curr_music, next_music))
14568 void FadeLevelSoundsAndMusic()
14574 static void PlayLevelMusic()
14576 int music_nr = getLevelMusicNr();
14577 char *curr_music = getCurrentlyPlayingMusicFilename();
14578 char *next_music = getMusicInfoEntryFilename(music_nr);
14580 if (!strEqual(curr_music, next_music))
14581 PlayMusic(music_nr);
14584 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14586 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14587 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14588 int x = xx - 1 - offset;
14589 int y = yy - 1 - offset;
14594 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14598 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14602 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14606 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14610 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14614 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14618 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14621 case SAMPLE_android_clone:
14622 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14625 case SAMPLE_android_move:
14626 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14629 case SAMPLE_spring:
14630 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14634 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14638 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14641 case SAMPLE_eater_eat:
14642 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14646 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14649 case SAMPLE_collect:
14650 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14653 case SAMPLE_diamond:
14654 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14657 case SAMPLE_squash:
14658 /* !!! CHECK THIS !!! */
14660 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14662 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14666 case SAMPLE_wonderfall:
14667 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14671 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14675 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14679 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14683 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14687 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14691 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14694 case SAMPLE_wonder:
14695 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14699 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14702 case SAMPLE_exit_open:
14703 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14706 case SAMPLE_exit_leave:
14707 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14710 case SAMPLE_dynamite:
14711 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14715 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14723 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14727 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14731 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14735 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14739 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14744 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14746 int element = map_element_SP_to_RND(element_sp);
14747 int action = map_action_SP_to_RND(action_sp);
14748 int offset = (setup.sp_show_border_elements ? 0 : 1);
14749 int x = xx - offset;
14750 int y = yy - offset;
14752 PlayLevelSoundElementAction(x, y, element, action);
14755 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14757 int element = map_element_MM_to_RND(element_mm);
14758 int action = map_action_MM_to_RND(action_mm);
14760 int x = xx - offset;
14761 int y = yy - offset;
14763 if (!IS_MM_ELEMENT(element))
14764 element = EL_MM_DEFAULT;
14766 PlayLevelSoundElementAction(x, y, element, action);
14769 void PlaySound_MM(int sound_mm)
14771 int sound = map_sound_MM_to_RND(sound_mm);
14773 if (sound == SND_UNDEFINED)
14779 void PlaySoundLoop_MM(int sound_mm)
14781 int sound = map_sound_MM_to_RND(sound_mm);
14783 if (sound == SND_UNDEFINED)
14786 PlaySoundLoop(sound);
14789 void StopSound_MM(int sound_mm)
14791 int sound = map_sound_MM_to_RND(sound_mm);
14793 if (sound == SND_UNDEFINED)
14799 void RaiseScore(int value)
14801 local_player->score += value;
14803 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14805 DisplayGameControlValues();
14808 void RaiseScoreElement(int element)
14813 case EL_BD_DIAMOND:
14814 case EL_EMERALD_YELLOW:
14815 case EL_EMERALD_RED:
14816 case EL_EMERALD_PURPLE:
14817 case EL_SP_INFOTRON:
14818 RaiseScore(level.score[SC_EMERALD]);
14821 RaiseScore(level.score[SC_DIAMOND]);
14824 RaiseScore(level.score[SC_CRYSTAL]);
14827 RaiseScore(level.score[SC_PEARL]);
14830 case EL_BD_BUTTERFLY:
14831 case EL_SP_ELECTRON:
14832 RaiseScore(level.score[SC_BUG]);
14835 case EL_BD_FIREFLY:
14836 case EL_SP_SNIKSNAK:
14837 RaiseScore(level.score[SC_SPACESHIP]);
14840 case EL_DARK_YAMYAM:
14841 RaiseScore(level.score[SC_YAMYAM]);
14844 RaiseScore(level.score[SC_ROBOT]);
14847 RaiseScore(level.score[SC_PACMAN]);
14850 RaiseScore(level.score[SC_NUT]);
14853 case EL_EM_DYNAMITE:
14854 case EL_SP_DISK_RED:
14855 case EL_DYNABOMB_INCREASE_NUMBER:
14856 case EL_DYNABOMB_INCREASE_SIZE:
14857 case EL_DYNABOMB_INCREASE_POWER:
14858 RaiseScore(level.score[SC_DYNAMITE]);
14860 case EL_SHIELD_NORMAL:
14861 case EL_SHIELD_DEADLY:
14862 RaiseScore(level.score[SC_SHIELD]);
14864 case EL_EXTRA_TIME:
14865 RaiseScore(level.extra_time_score);
14879 case EL_DC_KEY_WHITE:
14880 RaiseScore(level.score[SC_KEY]);
14883 RaiseScore(element_info[element].collect_score);
14888 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14890 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14892 /* closing door required in case of envelope style request dialogs */
14894 CloseDoor(DOOR_CLOSE_1);
14896 if (network.enabled)
14897 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14901 FadeSkipNextFadeIn();
14903 SetGameStatus(GAME_MODE_MAIN);
14908 else /* continue playing the game */
14910 if (tape.playing && tape.deactivate_display)
14911 TapeDeactivateDisplayOff(TRUE);
14913 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14915 if (tape.playing && tape.deactivate_display)
14916 TapeDeactivateDisplayOn();
14920 void RequestQuitGame(boolean ask_if_really_quit)
14922 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14923 boolean skip_request = AllPlayersGone || quick_quit;
14925 RequestQuitGameExt(skip_request, quick_quit,
14926 "Do you really want to quit the game?");
14929 void RequestRestartGame(char *message)
14931 game.restart_game_message = NULL;
14933 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14935 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14939 SetGameStatus(GAME_MODE_MAIN);
14946 /* ------------------------------------------------------------------------- */
14947 /* random generator functions */
14948 /* ------------------------------------------------------------------------- */
14950 unsigned int InitEngineRandom_RND(int seed)
14952 game.num_random_calls = 0;
14954 return InitEngineRandom(seed);
14957 unsigned int RND(int max)
14961 game.num_random_calls++;
14963 return GetEngineRandom(max);
14970 /* ------------------------------------------------------------------------- */
14971 /* game engine snapshot handling functions */
14972 /* ------------------------------------------------------------------------- */
14974 struct EngineSnapshotInfo
14976 /* runtime values for custom element collect score */
14977 int collect_score[NUM_CUSTOM_ELEMENTS];
14979 /* runtime values for group element choice position */
14980 int choice_pos[NUM_GROUP_ELEMENTS];
14982 /* runtime values for belt position animations */
14983 int belt_graphic[4][NUM_BELT_PARTS];
14984 int belt_anim_mode[4][NUM_BELT_PARTS];
14987 static struct EngineSnapshotInfo engine_snapshot_rnd;
14988 static char *snapshot_level_identifier = NULL;
14989 static int snapshot_level_nr = -1;
14991 static void SaveEngineSnapshotValues_RND()
14993 static int belt_base_active_element[4] =
14995 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14996 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14997 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14998 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15002 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15004 int element = EL_CUSTOM_START + i;
15006 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15009 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15011 int element = EL_GROUP_START + i;
15013 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15016 for (i = 0; i < 4; i++)
15018 for (j = 0; j < NUM_BELT_PARTS; j++)
15020 int element = belt_base_active_element[i] + j;
15021 int graphic = el2img(element);
15022 int anim_mode = graphic_info[graphic].anim_mode;
15024 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15025 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15030 static void LoadEngineSnapshotValues_RND()
15032 unsigned int num_random_calls = game.num_random_calls;
15035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15037 int element = EL_CUSTOM_START + i;
15039 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15042 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15044 int element = EL_GROUP_START + i;
15046 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15049 for (i = 0; i < 4; i++)
15051 for (j = 0; j < NUM_BELT_PARTS; j++)
15053 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15054 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15056 graphic_info[graphic].anim_mode = anim_mode;
15060 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15062 InitRND(tape.random_seed);
15063 for (i = 0; i < num_random_calls; i++)
15067 if (game.num_random_calls != num_random_calls)
15069 Error(ERR_INFO, "number of random calls out of sync");
15070 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15071 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15072 Error(ERR_EXIT, "this should not happen -- please debug");
15076 void FreeEngineSnapshotSingle()
15078 FreeSnapshotSingle();
15080 setString(&snapshot_level_identifier, NULL);
15081 snapshot_level_nr = -1;
15084 void FreeEngineSnapshotList()
15086 FreeSnapshotList();
15089 ListNode *SaveEngineSnapshotBuffers()
15091 ListNode *buffers = NULL;
15093 /* copy some special values to a structure better suited for the snapshot */
15095 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15096 SaveEngineSnapshotValues_RND();
15097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15098 SaveEngineSnapshotValues_EM();
15099 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15100 SaveEngineSnapshotValues_SP(&buffers);
15101 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15102 SaveEngineSnapshotValues_MM(&buffers);
15104 /* save values stored in special snapshot structure */
15106 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15107 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15108 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15109 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15110 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15111 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15112 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15113 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15115 /* save further RND engine values */
15117 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15119 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15121 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15122 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15123 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15124 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15127 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15128 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15129 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15132 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15134 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15138 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15144 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15146 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15149 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15182 ListNode *node = engine_snapshot_list_rnd;
15185 while (node != NULL)
15187 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15192 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15198 void SaveEngineSnapshotSingle()
15200 ListNode *buffers = SaveEngineSnapshotBuffers();
15202 /* finally save all snapshot buffers to single snapshot */
15203 SaveSnapshotSingle(buffers);
15205 /* save level identification information */
15206 setString(&snapshot_level_identifier, leveldir_current->identifier);
15207 snapshot_level_nr = level_nr;
15210 boolean CheckSaveEngineSnapshotToList()
15212 boolean save_snapshot =
15213 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15214 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15215 game.snapshot.changed_action) ||
15216 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15217 game.snapshot.collected_item));
15219 game.snapshot.changed_action = FALSE;
15220 game.snapshot.collected_item = FALSE;
15221 game.snapshot.save_snapshot = save_snapshot;
15223 return save_snapshot;
15226 void SaveEngineSnapshotToList()
15228 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15232 ListNode *buffers = SaveEngineSnapshotBuffers();
15234 /* finally save all snapshot buffers to snapshot list */
15235 SaveSnapshotToList(buffers);
15238 void SaveEngineSnapshotToListInitial()
15240 FreeEngineSnapshotList();
15242 SaveEngineSnapshotToList();
15245 void LoadEngineSnapshotValues()
15247 /* restore special values from snapshot structure */
15249 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15250 LoadEngineSnapshotValues_RND();
15251 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15252 LoadEngineSnapshotValues_EM();
15253 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15254 LoadEngineSnapshotValues_SP();
15255 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15256 LoadEngineSnapshotValues_MM();
15259 void LoadEngineSnapshotSingle()
15261 LoadSnapshotSingle();
15263 LoadEngineSnapshotValues();
15266 void LoadEngineSnapshot_Undo(int steps)
15268 LoadSnapshotFromList_Older(steps);
15270 LoadEngineSnapshotValues();
15273 void LoadEngineSnapshot_Redo(int steps)
15275 LoadSnapshotFromList_Newer(steps);
15277 LoadEngineSnapshotValues();
15280 boolean CheckEngineSnapshotSingle()
15282 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15283 snapshot_level_nr == level_nr);
15286 boolean CheckEngineSnapshotList()
15288 return CheckSnapshotList();
15292 /* ---------- new game button stuff ---------------------------------------- */
15299 boolean *setup_value;
15300 boolean allowed_on_tape;
15302 } gamebutton_info[NUM_GAME_BUTTONS] =
15305 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15306 GAME_CTRL_ID_STOP, NULL,
15310 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15311 GAME_CTRL_ID_PAUSE, NULL,
15315 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15316 GAME_CTRL_ID_PLAY, NULL,
15320 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15321 GAME_CTRL_ID_UNDO, NULL,
15325 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15326 GAME_CTRL_ID_REDO, NULL,
15330 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15331 GAME_CTRL_ID_SAVE, NULL,
15335 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15336 GAME_CTRL_ID_PAUSE2, NULL,
15340 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15341 GAME_CTRL_ID_LOAD, NULL,
15345 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15346 GAME_CTRL_ID_PANEL_STOP, NULL,
15350 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15351 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15352 FALSE, "pause game"
15355 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15356 GAME_CTRL_ID_PANEL_PLAY, NULL,
15360 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15361 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15362 TRUE, "background music on/off"
15365 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15366 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15367 TRUE, "sound loops on/off"
15370 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15371 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15372 TRUE, "normal sounds on/off"
15375 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15376 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15377 FALSE, "background music on/off"
15380 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15381 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15382 FALSE, "sound loops on/off"
15385 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15386 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15387 FALSE, "normal sounds on/off"
15391 void CreateGameButtons()
15395 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15397 int graphic = gamebutton_info[i].graphic;
15398 struct GraphicInfo *gfx = &graphic_info[graphic];
15399 struct XY *pos = gamebutton_info[i].pos;
15400 struct GadgetInfo *gi;
15403 unsigned int event_mask;
15404 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15405 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15406 int base_x = (on_tape ? VX : DX);
15407 int base_y = (on_tape ? VY : DY);
15408 int gd_x = gfx->src_x;
15409 int gd_y = gfx->src_y;
15410 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15411 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15412 int gd_xa = gfx->src_x + gfx->active_xoffset;
15413 int gd_ya = gfx->src_y + gfx->active_yoffset;
15414 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15415 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15418 if (gfx->bitmap == NULL)
15420 game_gadget[id] = NULL;
15425 if (id == GAME_CTRL_ID_STOP ||
15426 id == GAME_CTRL_ID_PANEL_STOP ||
15427 id == GAME_CTRL_ID_PLAY ||
15428 id == GAME_CTRL_ID_PANEL_PLAY ||
15429 id == GAME_CTRL_ID_SAVE ||
15430 id == GAME_CTRL_ID_LOAD)
15432 button_type = GD_TYPE_NORMAL_BUTTON;
15434 event_mask = GD_EVENT_RELEASED;
15436 else if (id == GAME_CTRL_ID_UNDO ||
15437 id == GAME_CTRL_ID_REDO)
15439 button_type = GD_TYPE_NORMAL_BUTTON;
15441 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15445 button_type = GD_TYPE_CHECK_BUTTON;
15446 checked = (gamebutton_info[i].setup_value != NULL ?
15447 *gamebutton_info[i].setup_value : FALSE);
15448 event_mask = GD_EVENT_PRESSED;
15451 gi = CreateGadget(GDI_CUSTOM_ID, id,
15452 GDI_IMAGE_ID, graphic,
15453 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15454 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15455 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15456 GDI_WIDTH, gfx->width,
15457 GDI_HEIGHT, gfx->height,
15458 GDI_TYPE, button_type,
15459 GDI_STATE, GD_BUTTON_UNPRESSED,
15460 GDI_CHECKED, checked,
15461 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15462 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15463 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15464 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15465 GDI_DIRECT_DRAW, FALSE,
15466 GDI_EVENT_MASK, event_mask,
15467 GDI_CALLBACK_ACTION, HandleGameButtons,
15471 Error(ERR_EXIT, "cannot create gadget");
15473 game_gadget[id] = gi;
15477 void FreeGameButtons()
15481 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15482 FreeGadget(game_gadget[i]);
15485 static void UnmapGameButtonsAtSamePosition(int id)
15489 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15491 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15492 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15493 UnmapGadget(game_gadget[i]);
15496 static void UnmapGameButtonsAtSamePosition_All()
15498 if (setup.show_snapshot_buttons)
15500 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15501 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15502 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15506 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15507 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15508 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15510 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15511 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15512 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15516 static void MapGameButtonsAtSamePosition(int id)
15520 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15522 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15523 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15524 MapGadget(game_gadget[i]);
15526 UnmapGameButtonsAtSamePosition_All();
15529 void MapUndoRedoButtons()
15531 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15532 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15534 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15535 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15537 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15540 void UnmapUndoRedoButtons()
15542 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15543 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15545 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15546 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15548 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15551 void MapGameButtonsExt(boolean on_tape)
15555 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15556 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15557 i != GAME_CTRL_ID_UNDO &&
15558 i != GAME_CTRL_ID_REDO)
15559 MapGadget(game_gadget[i]);
15561 UnmapGameButtonsAtSamePosition_All();
15563 RedrawGameButtons();
15566 void UnmapGameButtonsExt(boolean on_tape)
15570 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15571 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15572 UnmapGadget(game_gadget[i]);
15575 void RedrawGameButtonsExt(boolean on_tape)
15579 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15580 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15581 RedrawGadget(game_gadget[i]);
15583 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15584 redraw_mask &= ~REDRAW_ALL;
15587 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15592 gi->checked = state;
15595 void RedrawSoundButtonGadget(int id)
15597 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15598 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15599 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15600 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15601 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15602 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15605 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15606 RedrawGadget(game_gadget[id2]);
15609 void MapGameButtons()
15611 MapGameButtonsExt(FALSE);
15614 void UnmapGameButtons()
15616 UnmapGameButtonsExt(FALSE);
15619 void RedrawGameButtons()
15621 RedrawGameButtonsExt(FALSE);
15624 void MapGameButtonsOnTape()
15626 MapGameButtonsExt(TRUE);
15629 void UnmapGameButtonsOnTape()
15631 UnmapGameButtonsExt(TRUE);
15634 void RedrawGameButtonsOnTape()
15636 RedrawGameButtonsExt(TRUE);
15639 void GameUndoRedoExt()
15641 ClearPlayerAction();
15643 tape.pausing = TRUE;
15646 UpdateAndDisplayGameControlValues();
15648 DrawCompleteVideoDisplay();
15649 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15650 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15651 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15656 void GameUndo(int steps)
15658 if (!CheckEngineSnapshotList())
15661 LoadEngineSnapshot_Undo(steps);
15666 void GameRedo(int steps)
15668 if (!CheckEngineSnapshotList())
15671 LoadEngineSnapshot_Redo(steps);
15676 static void HandleGameButtonsExt(int id, int button)
15678 static boolean game_undo_executed = FALSE;
15679 int steps = BUTTON_STEPSIZE(button);
15680 boolean handle_game_buttons =
15681 (game_status == GAME_MODE_PLAYING ||
15682 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15684 if (!handle_game_buttons)
15689 case GAME_CTRL_ID_STOP:
15690 case GAME_CTRL_ID_PANEL_STOP:
15691 if (game_status == GAME_MODE_MAIN)
15697 RequestQuitGame(TRUE);
15701 case GAME_CTRL_ID_PAUSE:
15702 case GAME_CTRL_ID_PAUSE2:
15703 case GAME_CTRL_ID_PANEL_PAUSE:
15704 if (network.enabled && game_status == GAME_MODE_PLAYING)
15707 SendToServer_ContinuePlaying();
15709 SendToServer_PausePlaying();
15712 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15714 game_undo_executed = FALSE;
15718 case GAME_CTRL_ID_PLAY:
15719 case GAME_CTRL_ID_PANEL_PLAY:
15720 if (game_status == GAME_MODE_MAIN)
15722 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15724 else if (tape.pausing)
15726 if (network.enabled)
15727 SendToServer_ContinuePlaying();
15729 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15733 case GAME_CTRL_ID_UNDO:
15734 // Important: When using "save snapshot when collecting an item" mode,
15735 // load last (current) snapshot for first "undo" after pressing "pause"
15736 // (else the last-but-one snapshot would be loaded, because the snapshot
15737 // pointer already points to the last snapshot when pressing "pause",
15738 // which is fine for "every step/move" mode, but not for "every collect")
15739 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15740 !game_undo_executed)
15743 game_undo_executed = TRUE;
15748 case GAME_CTRL_ID_REDO:
15752 case GAME_CTRL_ID_SAVE:
15756 case GAME_CTRL_ID_LOAD:
15760 case SOUND_CTRL_ID_MUSIC:
15761 case SOUND_CTRL_ID_PANEL_MUSIC:
15762 if (setup.sound_music)
15764 setup.sound_music = FALSE;
15768 else if (audio.music_available)
15770 setup.sound = setup.sound_music = TRUE;
15772 SetAudioMode(setup.sound);
15774 if (game_status == GAME_MODE_PLAYING)
15778 RedrawSoundButtonGadget(id);
15782 case SOUND_CTRL_ID_LOOPS:
15783 case SOUND_CTRL_ID_PANEL_LOOPS:
15784 if (setup.sound_loops)
15785 setup.sound_loops = FALSE;
15786 else if (audio.loops_available)
15788 setup.sound = setup.sound_loops = TRUE;
15790 SetAudioMode(setup.sound);
15793 RedrawSoundButtonGadget(id);
15797 case SOUND_CTRL_ID_SIMPLE:
15798 case SOUND_CTRL_ID_PANEL_SIMPLE:
15799 if (setup.sound_simple)
15800 setup.sound_simple = FALSE;
15801 else if (audio.sound_available)
15803 setup.sound = setup.sound_simple = TRUE;
15805 SetAudioMode(setup.sound);
15808 RedrawSoundButtonGadget(id);
15817 static void HandleGameButtons(struct GadgetInfo *gi)
15819 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15822 void HandleSoundButtonKeys(Key key)
15824 if (key == setup.shortcut.sound_simple)
15825 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15826 else if (key == setup.shortcut.sound_loops)
15827 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15828 else if (key == setup.shortcut.sound_music)
15829 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);