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 SOUND_CTRL_ID_MUSIC 8
1017 #define SOUND_CTRL_ID_LOOPS 9
1018 #define SOUND_CTRL_ID_SIMPLE 10
1020 #define NUM_GAME_BUTTONS 11
1023 /* forward declaration for internal use */
1025 static void CreateField(int, int, int);
1027 static void ResetGfxAnimation(int, int);
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1053 static void HandleElementChange(int, int, int);
1054 static void ExecuteCustomElementAction(int, int, int, int);
1055 static boolean ChangeElement(int, int, int, int);
1057 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1058 #define CheckTriggeredElementChange(x, y, e, ev) \
1059 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1060 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1061 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1062 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1063 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1064 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1067 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1068 #define CheckElementChange(x, y, e, te, ev) \
1069 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1070 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1071 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1072 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1073 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1075 static void PlayLevelSound(int, int, int);
1076 static void PlayLevelSoundNearest(int, int, int);
1077 static void PlayLevelSoundAction(int, int, int);
1078 static void PlayLevelSoundElementAction(int, int, int, int);
1079 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1080 static void PlayLevelSoundActionIfLoop(int, int, int);
1081 static void StopLevelSoundActionIfLoop(int, int, int);
1082 static void PlayLevelMusic();
1083 static void FadeLevelSoundsAndMusic();
1085 static void HandleGameButtons(struct GadgetInfo *);
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1119 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1121 if (recursion_loop_detected) \
1124 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1126 recursion_loop_detected = TRUE; \
1127 recursion_loop_element = (e); \
1130 recursion_loop_depth++; \
1133 #define RECURSION_LOOP_DETECTION_END() \
1135 recursion_loop_depth--; \
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1142 static int map_player_action[MAX_PLAYERS];
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after */
1147 /* a specified time, eventually calling a function when changing */
1148 /* ------------------------------------------------------------------------- */
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1168 struct ChangingElementInfo
1173 void (*pre_change_function)(int x, int y);
1174 void (*change_function)(int x, int y);
1175 void (*post_change_function)(int x, int y);
1178 static struct ChangingElementInfo change_delay_list[] =
1213 EL_STEEL_EXIT_OPENING,
1221 EL_STEEL_EXIT_CLOSING,
1222 EL_STEEL_EXIT_CLOSED,
1245 EL_EM_STEEL_EXIT_OPENING,
1246 EL_EM_STEEL_EXIT_OPEN,
1253 EL_EM_STEEL_EXIT_CLOSING,
1277 EL_SWITCHGATE_OPENING,
1285 EL_SWITCHGATE_CLOSING,
1286 EL_SWITCHGATE_CLOSED,
1293 EL_TIMEGATE_OPENING,
1301 EL_TIMEGATE_CLOSING,
1310 EL_ACID_SPLASH_LEFT,
1318 EL_ACID_SPLASH_RIGHT,
1327 EL_SP_BUGGY_BASE_ACTIVATING,
1334 EL_SP_BUGGY_BASE_ACTIVATING,
1335 EL_SP_BUGGY_BASE_ACTIVE,
1342 EL_SP_BUGGY_BASE_ACTIVE,
1366 EL_ROBOT_WHEEL_ACTIVE,
1374 EL_TIMEGATE_SWITCH_ACTIVE,
1382 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1383 EL_DC_TIMEGATE_SWITCH,
1390 EL_EMC_MAGIC_BALL_ACTIVE,
1391 EL_EMC_MAGIC_BALL_ACTIVE,
1398 EL_EMC_SPRING_BUMPER_ACTIVE,
1399 EL_EMC_SPRING_BUMPER,
1406 EL_DIAGONAL_SHRINKING,
1414 EL_DIAGONAL_GROWING,
1435 int push_delay_fixed, push_delay_random;
1439 { EL_SPRING, 0, 0 },
1440 { EL_BALLOON, 0, 0 },
1442 { EL_SOKOBAN_OBJECT, 2, 0 },
1443 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1444 { EL_SATELLITE, 2, 0 },
1445 { EL_SP_DISK_YELLOW, 2, 0 },
1447 { EL_UNDEFINED, 0, 0 },
1455 move_stepsize_list[] =
1457 { EL_AMOEBA_DROP, 2 },
1458 { EL_AMOEBA_DROPPING, 2 },
1459 { EL_QUICKSAND_FILLING, 1 },
1460 { EL_QUICKSAND_EMPTYING, 1 },
1461 { EL_QUICKSAND_FAST_FILLING, 2 },
1462 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1463 { EL_MAGIC_WALL_FILLING, 2 },
1464 { EL_MAGIC_WALL_EMPTYING, 2 },
1465 { EL_BD_MAGIC_WALL_FILLING, 2 },
1466 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1467 { EL_DC_MAGIC_WALL_FILLING, 2 },
1468 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1470 { EL_UNDEFINED, 0 },
1478 collect_count_list[] =
1481 { EL_BD_DIAMOND, 1 },
1482 { EL_EMERALD_YELLOW, 1 },
1483 { EL_EMERALD_RED, 1 },
1484 { EL_EMERALD_PURPLE, 1 },
1486 { EL_SP_INFOTRON, 1 },
1490 { EL_UNDEFINED, 0 },
1498 access_direction_list[] =
1500 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1501 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1502 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1503 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1504 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1505 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1506 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1507 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1508 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1509 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1510 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1512 { EL_SP_PORT_LEFT, MV_RIGHT },
1513 { EL_SP_PORT_RIGHT, MV_LEFT },
1514 { EL_SP_PORT_UP, MV_DOWN },
1515 { EL_SP_PORT_DOWN, MV_UP },
1516 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1517 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1518 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1519 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1520 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1521 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1522 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1523 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1526 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1527 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1532 { EL_UNDEFINED, MV_NONE }
1535 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1537 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1538 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1539 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1540 IS_JUST_CHANGING(x, y))
1542 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1544 /* static variables for playfield scan mode (scanning forward or backward) */
1545 static int playfield_scan_start_x = 0;
1546 static int playfield_scan_start_y = 0;
1547 static int playfield_scan_delta_x = 1;
1548 static int playfield_scan_delta_y = 1;
1550 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1551 (y) >= 0 && (y) <= lev_fieldy - 1; \
1552 (y) += playfield_scan_delta_y) \
1553 for ((x) = playfield_scan_start_x; \
1554 (x) >= 0 && (x) <= lev_fieldx - 1; \
1555 (x) += playfield_scan_delta_x)
1558 void DEBUG_SetMaximumDynamite()
1562 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1563 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1564 local_player->inventory_element[local_player->inventory_size++] =
1569 static void InitPlayfieldScanModeVars()
1571 if (game.use_reverse_scan_direction)
1573 playfield_scan_start_x = lev_fieldx - 1;
1574 playfield_scan_start_y = lev_fieldy - 1;
1576 playfield_scan_delta_x = -1;
1577 playfield_scan_delta_y = -1;
1581 playfield_scan_start_x = 0;
1582 playfield_scan_start_y = 0;
1584 playfield_scan_delta_x = 1;
1585 playfield_scan_delta_y = 1;
1589 static void InitPlayfieldScanMode(int mode)
1591 game.use_reverse_scan_direction =
1592 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1594 InitPlayfieldScanModeVars();
1597 static int get_move_delay_from_stepsize(int move_stepsize)
1600 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1602 /* make sure that stepsize value is always a power of 2 */
1603 move_stepsize = (1 << log_2(move_stepsize));
1605 return TILEX / move_stepsize;
1608 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1611 int player_nr = player->index_nr;
1612 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1613 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1615 /* do no immediately change move delay -- the player might just be moving */
1616 player->move_delay_value_next = move_delay;
1618 /* information if player can move must be set separately */
1619 player->cannot_move = cannot_move;
1623 player->move_delay = game.initial_move_delay[player_nr];
1624 player->move_delay_value = game.initial_move_delay_value[player_nr];
1626 player->move_delay_value_next = -1;
1628 player->move_delay_reset_counter = 0;
1632 void GetPlayerConfig()
1634 GameFrameDelay = setup.game_frame_delay;
1636 if (!audio.sound_available)
1637 setup.sound_simple = FALSE;
1639 if (!audio.loops_available)
1640 setup.sound_loops = FALSE;
1642 if (!audio.music_available)
1643 setup.sound_music = FALSE;
1645 if (!video.fullscreen_available)
1646 setup.fullscreen = FALSE;
1648 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1650 SetAudioMode(setup.sound);
1653 int GetElementFromGroupElement(int element)
1655 if (IS_GROUP_ELEMENT(element))
1657 struct ElementGroupInfo *group = element_info[element].group;
1658 int last_anim_random_frame = gfx.anim_random_frame;
1661 if (group->choice_mode == ANIM_RANDOM)
1662 gfx.anim_random_frame = RND(group->num_elements_resolved);
1664 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1665 group->choice_mode, 0,
1668 if (group->choice_mode == ANIM_RANDOM)
1669 gfx.anim_random_frame = last_anim_random_frame;
1671 group->choice_pos++;
1673 element = group->element_resolved[element_pos];
1679 static void InitPlayerField(int x, int y, int element, boolean init_game)
1681 if (element == EL_SP_MURPHY)
1685 if (stored_player[0].present)
1687 Feld[x][y] = EL_SP_MURPHY_CLONE;
1693 stored_player[0].initial_element = element;
1694 stored_player[0].use_murphy = TRUE;
1696 if (!level.use_artwork_element[0])
1697 stored_player[0].artwork_element = EL_SP_MURPHY;
1700 Feld[x][y] = EL_PLAYER_1;
1706 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1707 int jx = player->jx, jy = player->jy;
1709 player->present = TRUE;
1711 player->block_last_field = (element == EL_SP_MURPHY ?
1712 level.sp_block_last_field :
1713 level.block_last_field);
1715 /* ---------- initialize player's last field block delay --------------- */
1717 /* always start with reliable default value (no adjustment needed) */
1718 player->block_delay_adjustment = 0;
1720 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1721 if (player->block_last_field && element == EL_SP_MURPHY)
1722 player->block_delay_adjustment = 1;
1724 /* special case 2: in game engines before 3.1.1, blocking was different */
1725 if (game.use_block_last_field_bug)
1726 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1728 if (!options.network || player->connected)
1730 player->active = TRUE;
1732 /* remove potentially duplicate players */
1733 if (StorePlayer[jx][jy] == Feld[x][y])
1734 StorePlayer[jx][jy] = 0;
1736 StorePlayer[x][y] = Feld[x][y];
1738 #if DEBUG_INIT_PLAYER
1741 printf("- player element %d activated", player->element_nr);
1742 printf(" (local player is %d and currently %s)\n",
1743 local_player->element_nr,
1744 local_player->active ? "active" : "not active");
1749 Feld[x][y] = EL_EMPTY;
1751 player->jx = player->last_jx = x;
1752 player->jy = player->last_jy = y;
1757 int player_nr = GET_PLAYER_NR(element);
1758 struct PlayerInfo *player = &stored_player[player_nr];
1760 if (player->active && player->killed)
1761 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1765 static void InitField(int x, int y, boolean init_game)
1767 int element = Feld[x][y];
1776 InitPlayerField(x, y, element, init_game);
1779 case EL_SOKOBAN_FIELD_PLAYER:
1780 element = Feld[x][y] = EL_PLAYER_1;
1781 InitField(x, y, init_game);
1783 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1784 InitField(x, y, init_game);
1787 case EL_SOKOBAN_FIELD_EMPTY:
1788 local_player->sokobanfields_still_needed++;
1792 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1793 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1794 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1795 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1796 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1797 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1798 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1800 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1801 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1810 case EL_SPACESHIP_RIGHT:
1811 case EL_SPACESHIP_UP:
1812 case EL_SPACESHIP_LEFT:
1813 case EL_SPACESHIP_DOWN:
1814 case EL_BD_BUTTERFLY:
1815 case EL_BD_BUTTERFLY_RIGHT:
1816 case EL_BD_BUTTERFLY_UP:
1817 case EL_BD_BUTTERFLY_LEFT:
1818 case EL_BD_BUTTERFLY_DOWN:
1820 case EL_BD_FIREFLY_RIGHT:
1821 case EL_BD_FIREFLY_UP:
1822 case EL_BD_FIREFLY_LEFT:
1823 case EL_BD_FIREFLY_DOWN:
1824 case EL_PACMAN_RIGHT:
1826 case EL_PACMAN_LEFT:
1827 case EL_PACMAN_DOWN:
1829 case EL_YAMYAM_LEFT:
1830 case EL_YAMYAM_RIGHT:
1832 case EL_YAMYAM_DOWN:
1833 case EL_DARK_YAMYAM:
1836 case EL_SP_SNIKSNAK:
1837 case EL_SP_ELECTRON:
1846 case EL_AMOEBA_FULL:
1851 case EL_AMOEBA_DROP:
1852 if (y == lev_fieldy - 1)
1854 Feld[x][y] = EL_AMOEBA_GROWING;
1855 Store[x][y] = EL_AMOEBA_WET;
1859 case EL_DYNAMITE_ACTIVE:
1860 case EL_SP_DISK_RED_ACTIVE:
1861 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1862 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1863 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1864 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1865 MovDelay[x][y] = 96;
1868 case EL_EM_DYNAMITE_ACTIVE:
1869 MovDelay[x][y] = 32;
1873 local_player->lights_still_needed++;
1877 local_player->friends_still_needed++;
1882 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1885 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1886 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1887 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1888 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1889 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1890 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1891 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1899 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1900 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1901 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1903 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1905 game.belt_dir[belt_nr] = belt_dir;
1906 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1908 else /* more than one switch -- set it like the first switch */
1910 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1915 case EL_LIGHT_SWITCH_ACTIVE:
1917 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1920 case EL_INVISIBLE_STEELWALL:
1921 case EL_INVISIBLE_WALL:
1922 case EL_INVISIBLE_SAND:
1923 if (game.light_time_left > 0 ||
1924 game.lenses_time_left > 0)
1925 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1928 case EL_EMC_MAGIC_BALL:
1929 if (game.ball_state)
1930 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1933 case EL_EMC_MAGIC_BALL_SWITCH:
1934 if (game.ball_state)
1935 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938 case EL_TRIGGER_PLAYER:
1939 case EL_TRIGGER_ELEMENT:
1940 case EL_TRIGGER_CE_VALUE:
1941 case EL_TRIGGER_CE_SCORE:
1943 case EL_ANY_ELEMENT:
1944 case EL_CURRENT_CE_VALUE:
1945 case EL_CURRENT_CE_SCORE:
1962 /* reference elements should not be used on the playfield */
1963 Feld[x][y] = EL_EMPTY;
1967 if (IS_CUSTOM_ELEMENT(element))
1969 if (CAN_MOVE(element))
1972 if (!element_info[element].use_last_ce_value || init_game)
1973 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1975 else if (IS_GROUP_ELEMENT(element))
1977 Feld[x][y] = GetElementFromGroupElement(element);
1979 InitField(x, y, init_game);
1986 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1989 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1991 InitField(x, y, init_game);
1993 /* not needed to call InitMovDir() -- already done by InitField()! */
1994 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1995 CAN_MOVE(Feld[x][y]))
1999 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2001 int old_element = Feld[x][y];
2003 InitField(x, y, init_game);
2005 /* not needed to call InitMovDir() -- already done by InitField()! */
2006 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2007 CAN_MOVE(old_element) &&
2008 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2011 /* this case is in fact a combination of not less than three bugs:
2012 first, it calls InitMovDir() for elements that can move, although this is
2013 already done by InitField(); then, it checks the element that was at this
2014 field _before_ the call to InitField() (which can change it); lastly, it
2015 was not called for "mole with direction" elements, which were treated as
2016 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2020 static int get_key_element_from_nr(int key_nr)
2022 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2023 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2024 EL_EM_KEY_1 : EL_KEY_1);
2026 return key_base_element + key_nr;
2029 static int get_next_dropped_element(struct PlayerInfo *player)
2031 return (player->inventory_size > 0 ?
2032 player->inventory_element[player->inventory_size - 1] :
2033 player->inventory_infinite_element != EL_UNDEFINED ?
2034 player->inventory_infinite_element :
2035 player->dynabombs_left > 0 ?
2036 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2040 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2042 /* pos >= 0: get element from bottom of the stack;
2043 pos < 0: get element from top of the stack */
2047 int min_inventory_size = -pos;
2048 int inventory_pos = player->inventory_size - min_inventory_size;
2049 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2051 return (player->inventory_size >= min_inventory_size ?
2052 player->inventory_element[inventory_pos] :
2053 player->inventory_infinite_element != EL_UNDEFINED ?
2054 player->inventory_infinite_element :
2055 player->dynabombs_left >= min_dynabombs_left ?
2056 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2061 int min_dynabombs_left = pos + 1;
2062 int min_inventory_size = pos + 1 - player->dynabombs_left;
2063 int inventory_pos = pos - player->dynabombs_left;
2065 return (player->inventory_infinite_element != EL_UNDEFINED ?
2066 player->inventory_infinite_element :
2067 player->dynabombs_left >= min_dynabombs_left ?
2068 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2069 player->inventory_size >= min_inventory_size ?
2070 player->inventory_element[inventory_pos] :
2075 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2077 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2078 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2081 if (gpo1->sort_priority != gpo2->sort_priority)
2082 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2084 compare_result = gpo1->nr - gpo2->nr;
2086 return compare_result;
2089 int getPlayerInventorySize(int player_nr)
2091 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2092 return level.native_em_level->ply[player_nr]->dynamite;
2093 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2094 return level.native_sp_level->game_sp->red_disk_count;
2096 return stored_player[player_nr].inventory_size;
2099 void InitGameControlValues()
2103 for (i = 0; game_panel_controls[i].nr != -1; i++)
2105 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2106 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2107 struct TextPosInfo *pos = gpc->pos;
2109 int type = gpc->type;
2113 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2114 Error(ERR_EXIT, "this should not happen -- please debug");
2117 /* force update of game controls after initialization */
2118 gpc->value = gpc->last_value = -1;
2119 gpc->frame = gpc->last_frame = -1;
2120 gpc->gfx_frame = -1;
2122 /* determine panel value width for later calculation of alignment */
2123 if (type == TYPE_INTEGER || type == TYPE_STRING)
2125 pos->width = pos->size * getFontWidth(pos->font);
2126 pos->height = getFontHeight(pos->font);
2128 else if (type == TYPE_ELEMENT)
2130 pos->width = pos->size;
2131 pos->height = pos->size;
2134 /* fill structure for game panel draw order */
2136 gpo->sort_priority = pos->sort_priority;
2139 /* sort game panel controls according to sort_priority and control number */
2140 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2141 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2144 void UpdatePlayfieldElementCount()
2146 boolean use_element_count = FALSE;
2149 /* first check if it is needed at all to calculate playfield element count */
2150 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2151 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2152 use_element_count = TRUE;
2154 if (!use_element_count)
2157 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2158 element_info[i].element_count = 0;
2160 SCAN_PLAYFIELD(x, y)
2162 element_info[Feld[x][y]].element_count++;
2165 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2166 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2167 if (IS_IN_GROUP(j, i))
2168 element_info[EL_GROUP_START + i].element_count +=
2169 element_info[j].element_count;
2172 void UpdateGameControlValues()
2175 int time = (local_player->LevelSolved ?
2176 local_player->LevelSolved_CountingTime :
2177 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2178 level.native_em_level->lev->time :
2179 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2180 level.native_sp_level->game_sp->time_played :
2181 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2182 game_mm.energy_left :
2183 game.no_time_limit ? TimePlayed : TimeLeft);
2184 int score = (local_player->LevelSolved ?
2185 local_player->LevelSolved_CountingScore :
2186 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 level.native_em_level->lev->score :
2188 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 level.native_sp_level->game_sp->score :
2190 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2192 local_player->score);
2193 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->required :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->infotrons_still_needed :
2197 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 game_mm.kettles_still_needed :
2199 local_player->gems_still_needed);
2200 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201 level.native_em_level->lev->required > 0 :
2202 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2204 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205 game_mm.kettles_still_needed > 0 ||
2206 game_mm.lights_still_needed > 0 :
2207 local_player->gems_still_needed > 0 ||
2208 local_player->sokobanfields_still_needed > 0 ||
2209 local_player->lights_still_needed > 0);
2210 int health = (local_player->LevelSolved ?
2211 local_player->LevelSolved_CountingHealth :
2212 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2213 MM_HEALTH(game_mm.laser_overload_value) :
2214 local_player->health);
2216 UpdatePlayfieldElementCount();
2218 /* update game panel control values */
2220 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2221 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2223 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2224 for (i = 0; i < MAX_NUM_KEYS; i++)
2225 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2226 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2227 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2229 if (game.centered_player_nr == -1)
2231 for (i = 0; i < MAX_PLAYERS; i++)
2233 /* only one player in Supaplex game engine */
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2237 for (k = 0; k < MAX_NUM_KEYS; k++)
2239 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241 if (level.native_em_level->ply[i]->keys & (1 << k))
2242 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243 get_key_element_from_nr(k);
2245 else if (stored_player[i].key[k])
2246 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247 get_key_element_from_nr(k);
2250 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2251 getPlayerInventorySize(i);
2253 if (stored_player[i].num_white_keys > 0)
2254 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2258 stored_player[i].num_white_keys;
2263 int player_nr = game.centered_player_nr;
2265 for (k = 0; k < MAX_NUM_KEYS; k++)
2267 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2269 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2270 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2271 get_key_element_from_nr(k);
2273 else if (stored_player[player_nr].key[k])
2274 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275 get_key_element_from_nr(k);
2278 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2279 getPlayerInventorySize(player_nr);
2281 if (stored_player[player_nr].num_white_keys > 0)
2282 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2284 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2285 stored_player[player_nr].num_white_keys;
2288 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2290 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2291 get_inventory_element_from_pos(local_player, i);
2292 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2293 get_inventory_element_from_pos(local_player, -i - 1);
2296 game_panel_controls[GAME_PANEL_SCORE].value = score;
2297 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2299 game_panel_controls[GAME_PANEL_TIME].value = time;
2301 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2302 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2303 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2305 if (game.no_time_limit)
2306 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2308 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2310 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2311 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2313 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2315 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2316 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2318 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2319 local_player->shield_normal_time_left;
2320 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2321 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2323 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2324 local_player->shield_deadly_time_left;
2326 game_panel_controls[GAME_PANEL_EXIT].value =
2327 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2329 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2330 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2331 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2332 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2333 EL_EMC_MAGIC_BALL_SWITCH);
2335 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2336 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2337 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2338 game.light_time_left;
2340 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2341 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2342 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2343 game.timegate_time_left;
2345 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2346 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2348 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2349 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2350 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2351 game.lenses_time_left;
2353 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2354 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2355 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2356 game.magnify_time_left;
2358 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2359 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2360 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2361 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2362 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2363 EL_BALLOON_SWITCH_NONE);
2365 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2366 local_player->dynabomb_count;
2367 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2368 local_player->dynabomb_size;
2369 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2370 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2372 game_panel_controls[GAME_PANEL_PENGUINS].value =
2373 local_player->friends_still_needed;
2375 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2376 local_player->sokobanfields_still_needed;
2377 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2378 local_player->sokobanfields_still_needed;
2380 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2381 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2383 for (i = 0; i < NUM_BELTS; i++)
2385 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2386 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2387 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2388 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2389 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2392 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2393 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2394 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2395 game.magic_wall_time_left;
2397 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398 local_player->gravity;
2400 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2403 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406 game.panel.element[i].id : EL_UNDEFINED);
2408 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411 element_info[game.panel.element_count[i].id].element_count : 0);
2413 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416 element_info[game.panel.ce_score[i].id].collect_score : 0);
2418 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421 element_info[game.panel.ce_score_element[i].id].collect_score :
2424 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2428 /* update game panel control frames */
2430 for (i = 0; game_panel_controls[i].nr != -1; i++)
2432 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2434 if (gpc->type == TYPE_ELEMENT)
2436 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2438 int last_anim_random_frame = gfx.anim_random_frame;
2439 int element = gpc->value;
2440 int graphic = el2panelimg(element);
2442 if (gpc->value != gpc->last_value)
2445 gpc->gfx_random = INIT_GFX_RANDOM();
2451 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2452 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2453 gpc->gfx_random = INIT_GFX_RANDOM();
2456 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2457 gfx.anim_random_frame = gpc->gfx_random;
2459 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2460 gpc->gfx_frame = element_info[element].collect_score;
2462 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2465 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2466 gfx.anim_random_frame = last_anim_random_frame;
2469 else if (gpc->type == TYPE_GRAPHIC)
2471 if (gpc->graphic != IMG_UNDEFINED)
2473 int last_anim_random_frame = gfx.anim_random_frame;
2474 int graphic = gpc->graphic;
2476 if (gpc->value != gpc->last_value)
2479 gpc->gfx_random = INIT_GFX_RANDOM();
2485 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2486 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2487 gpc->gfx_random = INIT_GFX_RANDOM();
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491 gfx.anim_random_frame = gpc->gfx_random;
2493 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2495 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496 gfx.anim_random_frame = last_anim_random_frame;
2502 void DisplayGameControlValues()
2504 boolean redraw_panel = FALSE;
2507 for (i = 0; game_panel_controls[i].nr != -1; i++)
2509 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2511 if (PANEL_DEACTIVATED(gpc->pos))
2514 if (gpc->value == gpc->last_value &&
2515 gpc->frame == gpc->last_frame)
2518 redraw_panel = TRUE;
2524 /* copy default game door content to main double buffer */
2526 /* !!! CHECK AGAIN !!! */
2527 SetPanelBackground();
2528 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2529 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2531 /* redraw game control buttons */
2532 RedrawGameButtons();
2534 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2536 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2538 int nr = game_panel_order[i].nr;
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2540 struct TextPosInfo *pos = gpc->pos;
2541 int type = gpc->type;
2542 int value = gpc->value;
2543 int frame = gpc->frame;
2544 int size = pos->size;
2545 int font = pos->font;
2546 boolean draw_masked = pos->draw_masked;
2547 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2549 if (PANEL_DEACTIVATED(pos))
2552 gpc->last_value = value;
2553 gpc->last_frame = frame;
2555 if (type == TYPE_INTEGER)
2557 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2558 nr == GAME_PANEL_TIME)
2560 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2562 if (use_dynamic_size) /* use dynamic number of digits */
2564 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2565 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2566 int size2 = size1 + 1;
2567 int font1 = pos->font;
2568 int font2 = pos->font_alt;
2570 size = (value < value_change ? size1 : size2);
2571 font = (value < value_change ? font1 : font2);
2575 /* correct text size if "digits" is zero or less */
2577 size = strlen(int2str(value, size));
2579 /* dynamically correct text alignment */
2580 pos->width = size * getFontWidth(font);
2582 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2583 int2str(value, size), font, mask_mode);
2585 else if (type == TYPE_ELEMENT)
2587 int element, graphic;
2591 int dst_x = PANEL_XPOS(pos);
2592 int dst_y = PANEL_YPOS(pos);
2594 if (value != EL_UNDEFINED && value != EL_EMPTY)
2597 graphic = el2panelimg(value);
2599 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2601 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2604 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2607 width = graphic_info[graphic].width * size / TILESIZE;
2608 height = graphic_info[graphic].height * size / TILESIZE;
2611 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2614 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2618 else if (type == TYPE_GRAPHIC)
2620 int graphic = gpc->graphic;
2621 int graphic_active = gpc->graphic_active;
2625 int dst_x = PANEL_XPOS(pos);
2626 int dst_y = PANEL_YPOS(pos);
2627 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2628 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2630 if (graphic != IMG_UNDEFINED && !skip)
2632 if (pos->style == STYLE_REVERSE)
2633 value = 100 - value;
2635 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2637 if (pos->direction & MV_HORIZONTAL)
2639 width = graphic_info[graphic_active].width * value / 100;
2640 height = graphic_info[graphic_active].height;
2642 if (pos->direction == MV_LEFT)
2644 src_x += graphic_info[graphic_active].width - width;
2645 dst_x += graphic_info[graphic_active].width - width;
2650 width = graphic_info[graphic_active].width;
2651 height = graphic_info[graphic_active].height * value / 100;
2653 if (pos->direction == MV_UP)
2655 src_y += graphic_info[graphic_active].height - height;
2656 dst_y += graphic_info[graphic_active].height - height;
2661 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2664 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2667 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2669 if (pos->direction & MV_HORIZONTAL)
2671 if (pos->direction == MV_RIGHT)
2678 dst_x = PANEL_XPOS(pos);
2681 width = graphic_info[graphic].width - width;
2685 if (pos->direction == MV_DOWN)
2692 dst_y = PANEL_YPOS(pos);
2695 height = graphic_info[graphic].height - height;
2699 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2702 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706 else if (type == TYPE_STRING)
2708 boolean active = (value != 0);
2709 char *state_normal = "off";
2710 char *state_active = "on";
2711 char *state = (active ? state_active : state_normal);
2712 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2713 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2714 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2715 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2717 if (nr == GAME_PANEL_GRAVITY_STATE)
2719 int font1 = pos->font; /* (used for normal state) */
2720 int font2 = pos->font_alt; /* (used for active state) */
2722 font = (active ? font2 : font1);
2731 /* don't truncate output if "chars" is zero or less */
2734 /* dynamically correct text alignment */
2735 pos->width = size * getFontWidth(font);
2738 s_cut = getStringCopyN(s, size);
2740 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2741 s_cut, font, mask_mode);
2747 redraw_mask |= REDRAW_DOOR_1;
2750 SetGameStatus(GAME_MODE_PLAYING);
2753 void UpdateAndDisplayGameControlValues()
2755 if (tape.deactivate_display)
2758 UpdateGameControlValues();
2759 DisplayGameControlValues();
2762 void UpdateGameDoorValues()
2764 UpdateGameControlValues();
2767 void DrawGameDoorValues()
2769 DisplayGameControlValues();
2774 =============================================================================
2776 -----------------------------------------------------------------------------
2777 initialize game engine due to level / tape version number
2778 =============================================================================
2781 static void InitGameEngine()
2783 int i, j, k, l, x, y;
2785 /* set game engine from tape file when re-playing, else from level file */
2786 game.engine_version = (tape.playing ? tape.engine_version :
2787 level.game_version);
2789 /* set single or multi-player game mode (needed for re-playing tapes) */
2790 game.team_mode = setup.team_mode;
2794 int num_players = 0;
2796 for (i = 0; i < MAX_PLAYERS; i++)
2797 if (tape.player_participates[i])
2800 /* multi-player tapes contain input data for more than one player */
2801 game.team_mode = (num_players > 1);
2804 /* ---------------------------------------------------------------------- */
2805 /* set flags for bugs and changes according to active game engine version */
2806 /* ---------------------------------------------------------------------- */
2809 Summary of bugfix/change:
2810 Fixed handling for custom elements that change when pushed by the player.
2812 Fixed/changed in version:
2816 Before 3.1.0, custom elements that "change when pushing" changed directly
2817 after the player started pushing them (until then handled in "DigField()").
2818 Since 3.1.0, these custom elements are not changed until the "pushing"
2819 move of the element is finished (now handled in "ContinueMoving()").
2821 Affected levels/tapes:
2822 The first condition is generally needed for all levels/tapes before version
2823 3.1.0, which might use the old behaviour before it was changed; known tapes
2824 that are affected are some tapes from the level set "Walpurgis Gardens" by
2826 The second condition is an exception from the above case and is needed for
2827 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2828 above (including some development versions of 3.1.0), but before it was
2829 known that this change would break tapes like the above and was fixed in
2830 3.1.1, so that the changed behaviour was active although the engine version
2831 while recording maybe was before 3.1.0. There is at least one tape that is
2832 affected by this exception, which is the tape for the one-level set "Bug
2833 Machine" by Juergen Bonhagen.
2836 game.use_change_when_pushing_bug =
2837 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2839 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2840 tape.game_version < VERSION_IDENT(3,1,1,0)));
2843 Summary of bugfix/change:
2844 Fixed handling for blocking the field the player leaves when moving.
2846 Fixed/changed in version:
2850 Before 3.1.1, when "block last field when moving" was enabled, the field
2851 the player is leaving when moving was blocked for the time of the move,
2852 and was directly unblocked afterwards. This resulted in the last field
2853 being blocked for exactly one less than the number of frames of one player
2854 move. Additionally, even when blocking was disabled, the last field was
2855 blocked for exactly one frame.
2856 Since 3.1.1, due to changes in player movement handling, the last field
2857 is not blocked at all when blocking is disabled. When blocking is enabled,
2858 the last field is blocked for exactly the number of frames of one player
2859 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2860 last field is blocked for exactly one more than the number of frames of
2863 Affected levels/tapes:
2864 (!!! yet to be determined -- probably many !!!)
2867 game.use_block_last_field_bug =
2868 (game.engine_version < VERSION_IDENT(3,1,1,0));
2870 game_em.use_single_button =
2871 (game.engine_version > VERSION_IDENT(4,0,0,2));
2873 game_em.use_snap_key_bug =
2874 (game.engine_version < VERSION_IDENT(4,0,1,0));
2876 /* ---------------------------------------------------------------------- */
2878 /* set maximal allowed number of custom element changes per game frame */
2879 game.max_num_changes_per_frame = 1;
2881 /* default scan direction: scan playfield from top/left to bottom/right */
2882 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2884 /* dynamically adjust element properties according to game engine version */
2885 InitElementPropertiesEngine(game.engine_version);
2888 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2889 printf(" tape version == %06d [%s] [file: %06d]\n",
2890 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2892 printf(" => game.engine_version == %06d\n", game.engine_version);
2895 /* ---------- initialize player's initial move delay --------------------- */
2897 /* dynamically adjust player properties according to level information */
2898 for (i = 0; i < MAX_PLAYERS; i++)
2899 game.initial_move_delay_value[i] =
2900 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2902 /* dynamically adjust player properties according to game engine version */
2903 for (i = 0; i < MAX_PLAYERS; i++)
2904 game.initial_move_delay[i] =
2905 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2906 game.initial_move_delay_value[i] : 0);
2908 /* ---------- initialize player's initial push delay --------------------- */
2910 /* dynamically adjust player properties according to game engine version */
2911 game.initial_push_delay_value =
2912 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2914 /* ---------- initialize changing elements ------------------------------- */
2916 /* initialize changing elements information */
2917 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2919 struct ElementInfo *ei = &element_info[i];
2921 /* this pointer might have been changed in the level editor */
2922 ei->change = &ei->change_page[0];
2924 if (!IS_CUSTOM_ELEMENT(i))
2926 ei->change->target_element = EL_EMPTY_SPACE;
2927 ei->change->delay_fixed = 0;
2928 ei->change->delay_random = 0;
2929 ei->change->delay_frames = 1;
2932 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2934 ei->has_change_event[j] = FALSE;
2936 ei->event_page_nr[j] = 0;
2937 ei->event_page[j] = &ei->change_page[0];
2941 /* add changing elements from pre-defined list */
2942 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2944 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2945 struct ElementInfo *ei = &element_info[ch_delay->element];
2947 ei->change->target_element = ch_delay->target_element;
2948 ei->change->delay_fixed = ch_delay->change_delay;
2950 ei->change->pre_change_function = ch_delay->pre_change_function;
2951 ei->change->change_function = ch_delay->change_function;
2952 ei->change->post_change_function = ch_delay->post_change_function;
2954 ei->change->can_change = TRUE;
2955 ei->change->can_change_or_has_action = TRUE;
2957 ei->has_change_event[CE_DELAY] = TRUE;
2959 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2960 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2963 /* ---------- initialize internal run-time variables --------------------- */
2965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2967 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2969 for (j = 0; j < ei->num_change_pages; j++)
2971 ei->change_page[j].can_change_or_has_action =
2972 (ei->change_page[j].can_change |
2973 ei->change_page[j].has_action);
2977 /* add change events from custom element configuration */
2978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2980 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2982 for (j = 0; j < ei->num_change_pages; j++)
2984 if (!ei->change_page[j].can_change_or_has_action)
2987 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2989 /* only add event page for the first page found with this event */
2990 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2992 ei->has_change_event[k] = TRUE;
2994 ei->event_page_nr[k] = j;
2995 ei->event_page[k] = &ei->change_page[j];
3001 /* ---------- initialize reference elements in change conditions --------- */
3003 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3005 int element = EL_CUSTOM_START + i;
3006 struct ElementInfo *ei = &element_info[element];
3008 for (j = 0; j < ei->num_change_pages; j++)
3010 int trigger_element = ei->change_page[j].initial_trigger_element;
3012 if (trigger_element >= EL_PREV_CE_8 &&
3013 trigger_element <= EL_NEXT_CE_8)
3014 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3016 ei->change_page[j].trigger_element = trigger_element;
3020 /* ---------- initialize run-time trigger player and element ------------- */
3022 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3024 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3026 for (j = 0; j < ei->num_change_pages; j++)
3028 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3029 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3030 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3031 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3032 ei->change_page[j].actual_trigger_ce_value = 0;
3033 ei->change_page[j].actual_trigger_ce_score = 0;
3037 /* ---------- initialize trigger events ---------------------------------- */
3039 /* initialize trigger events information */
3040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3042 trigger_events[i][j] = FALSE;
3044 /* add trigger events from element change event properties */
3045 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 struct ElementInfo *ei = &element_info[i];
3049 for (j = 0; j < ei->num_change_pages; j++)
3051 if (!ei->change_page[j].can_change_or_has_action)
3054 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3056 int trigger_element = ei->change_page[j].trigger_element;
3058 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3060 if (ei->change_page[j].has_event[k])
3062 if (IS_GROUP_ELEMENT(trigger_element))
3064 struct ElementGroupInfo *group =
3065 element_info[trigger_element].group;
3067 for (l = 0; l < group->num_elements_resolved; l++)
3068 trigger_events[group->element_resolved[l]][k] = TRUE;
3070 else if (trigger_element == EL_ANY_ELEMENT)
3071 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3072 trigger_events[l][k] = TRUE;
3074 trigger_events[trigger_element][k] = TRUE;
3081 /* ---------- initialize push delay -------------------------------------- */
3083 /* initialize push delay values to default */
3084 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3086 if (!IS_CUSTOM_ELEMENT(i))
3088 /* set default push delay values (corrected since version 3.0.7-1) */
3089 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3091 element_info[i].push_delay_fixed = 2;
3092 element_info[i].push_delay_random = 8;
3096 element_info[i].push_delay_fixed = 8;
3097 element_info[i].push_delay_random = 8;
3102 /* set push delay value for certain elements from pre-defined list */
3103 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3105 int e = push_delay_list[i].element;
3107 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3108 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3111 /* set push delay value for Supaplex elements for newer engine versions */
3112 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3114 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3116 if (IS_SP_ELEMENT(i))
3118 /* set SP push delay to just enough to push under a falling zonk */
3119 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3121 element_info[i].push_delay_fixed = delay;
3122 element_info[i].push_delay_random = 0;
3127 /* ---------- initialize move stepsize ----------------------------------- */
3129 /* initialize move stepsize values to default */
3130 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131 if (!IS_CUSTOM_ELEMENT(i))
3132 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3134 /* set move stepsize value for certain elements from pre-defined list */
3135 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3137 int e = move_stepsize_list[i].element;
3139 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3142 /* ---------- initialize collect score ----------------------------------- */
3144 /* initialize collect score values for custom elements from initial value */
3145 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3146 if (IS_CUSTOM_ELEMENT(i))
3147 element_info[i].collect_score = element_info[i].collect_score_initial;
3149 /* ---------- initialize collect count ----------------------------------- */
3151 /* initialize collect count values for non-custom elements */
3152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153 if (!IS_CUSTOM_ELEMENT(i))
3154 element_info[i].collect_count_initial = 0;
3156 /* add collect count values for all elements from pre-defined list */
3157 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3158 element_info[collect_count_list[i].element].collect_count_initial =
3159 collect_count_list[i].count;
3161 /* ---------- initialize access direction -------------------------------- */
3163 /* initialize access direction values to default (access from every side) */
3164 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165 if (!IS_CUSTOM_ELEMENT(i))
3166 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3168 /* set access direction value for certain elements from pre-defined list */
3169 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3170 element_info[access_direction_list[i].element].access_direction =
3171 access_direction_list[i].direction;
3173 /* ---------- initialize explosion content ------------------------------- */
3174 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3176 if (IS_CUSTOM_ELEMENT(i))
3179 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3181 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3183 element_info[i].content.e[x][y] =
3184 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3185 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3186 i == EL_PLAYER_3 ? EL_EMERALD :
3187 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3188 i == EL_MOLE ? EL_EMERALD_RED :
3189 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3190 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3191 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3192 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3193 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3194 i == EL_WALL_EMERALD ? EL_EMERALD :
3195 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3196 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3197 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3198 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3199 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3200 i == EL_WALL_PEARL ? EL_PEARL :
3201 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3206 /* ---------- initialize recursion detection ------------------------------ */
3207 recursion_loop_depth = 0;
3208 recursion_loop_detected = FALSE;
3209 recursion_loop_element = EL_UNDEFINED;
3211 /* ---------- initialize graphics engine ---------------------------------- */
3212 game.scroll_delay_value =
3213 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3214 setup.scroll_delay ? setup.scroll_delay_value : 0);
3215 game.scroll_delay_value =
3216 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3218 /* ---------- initialize game engine snapshots ---------------------------- */
3219 for (i = 0; i < MAX_PLAYERS; i++)
3220 game.snapshot.last_action[i] = 0;
3221 game.snapshot.changed_action = FALSE;
3222 game.snapshot.collected_item = FALSE;
3223 game.snapshot.mode =
3224 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3225 SNAPSHOT_MODE_EVERY_STEP :
3226 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3227 SNAPSHOT_MODE_EVERY_MOVE :
3228 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3229 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3230 game.snapshot.save_snapshot = FALSE;
3232 /* ---------- initialize level time for Supaplex engine ------------------- */
3233 /* Supaplex levels with time limit currently unsupported -- should be added */
3234 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3238 int get_num_special_action(int element, int action_first, int action_last)
3240 int num_special_action = 0;
3243 for (i = action_first; i <= action_last; i++)
3245 boolean found = FALSE;
3247 for (j = 0; j < NUM_DIRECTIONS; j++)
3248 if (el_act_dir2img(element, i, j) !=
3249 el_act_dir2img(element, ACTION_DEFAULT, j))
3253 num_special_action++;
3258 return num_special_action;
3263 =============================================================================
3265 -----------------------------------------------------------------------------
3266 initialize and start new game
3267 =============================================================================
3272 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3273 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3274 int fade_mask = REDRAW_FIELD;
3276 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3277 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3278 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3279 int initial_move_dir = MV_DOWN;
3282 // required here to update video display before fading (FIX THIS)
3283 DrawMaskedBorder(REDRAW_DOOR_2);
3285 if (!game.restart_level)
3286 CloseDoor(DOOR_CLOSE_1);
3288 SetGameStatus(GAME_MODE_PLAYING);
3290 if (level_editor_test_game)
3291 FadeSkipNextFadeIn();
3293 FadeSetEnterScreen();
3295 if (CheckIfGlobalBorderHasChanged())
3296 fade_mask = REDRAW_ALL;
3298 FadeLevelSoundsAndMusic();
3300 ExpireSoundLoops(TRUE);
3304 /* needed if different viewport properties defined for playing */
3305 ChangeViewportPropertiesIfNeeded();
3309 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3311 DrawCompleteVideoDisplay();
3314 InitGameControlValues();
3316 /* don't play tapes over network */
3317 network_playing = (options.network && !tape.playing);
3319 for (i = 0; i < MAX_PLAYERS; i++)
3321 struct PlayerInfo *player = &stored_player[i];
3323 player->index_nr = i;
3324 player->index_bit = (1 << i);
3325 player->element_nr = EL_PLAYER_1 + i;
3327 player->present = FALSE;
3328 player->active = FALSE;
3329 player->mapped = FALSE;
3331 player->killed = FALSE;
3332 player->reanimated = FALSE;
3335 player->effective_action = 0;
3336 player->programmed_action = 0;
3338 player->mouse_action.lx = 0;
3339 player->mouse_action.ly = 0;
3340 player->mouse_action.button = 0;
3342 player->effective_mouse_action.lx = 0;
3343 player->effective_mouse_action.ly = 0;
3344 player->effective_mouse_action.button = 0;
3347 player->score_final = 0;
3349 player->health = MAX_HEALTH;
3350 player->health_final = MAX_HEALTH;
3352 player->gems_still_needed = level.gems_needed;
3353 player->sokobanfields_still_needed = 0;
3354 player->lights_still_needed = 0;
3355 player->friends_still_needed = 0;
3357 for (j = 0; j < MAX_NUM_KEYS; j++)
3358 player->key[j] = FALSE;
3360 player->num_white_keys = 0;
3362 player->dynabomb_count = 0;
3363 player->dynabomb_size = 1;
3364 player->dynabombs_left = 0;
3365 player->dynabomb_xl = FALSE;
3367 player->MovDir = initial_move_dir;
3370 player->GfxDir = initial_move_dir;
3371 player->GfxAction = ACTION_DEFAULT;
3373 player->StepFrame = 0;
3375 player->initial_element = player->element_nr;
3376 player->artwork_element =
3377 (level.use_artwork_element[i] ? level.artwork_element[i] :
3378 player->element_nr);
3379 player->use_murphy = FALSE;
3381 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3382 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3384 player->gravity = level.initial_player_gravity[i];
3386 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3388 player->actual_frame_counter = 0;
3390 player->step_counter = 0;
3392 player->last_move_dir = initial_move_dir;
3394 player->is_active = FALSE;
3396 player->is_waiting = FALSE;
3397 player->is_moving = FALSE;
3398 player->is_auto_moving = FALSE;
3399 player->is_digging = FALSE;
3400 player->is_snapping = FALSE;
3401 player->is_collecting = FALSE;
3402 player->is_pushing = FALSE;
3403 player->is_switching = FALSE;
3404 player->is_dropping = FALSE;
3405 player->is_dropping_pressed = FALSE;
3407 player->is_bored = FALSE;
3408 player->is_sleeping = FALSE;
3410 player->was_waiting = TRUE;
3411 player->was_moving = FALSE;
3412 player->was_snapping = FALSE;
3413 player->was_dropping = FALSE;
3415 player->force_dropping = FALSE;
3417 player->frame_counter_bored = -1;
3418 player->frame_counter_sleeping = -1;
3420 player->anim_delay_counter = 0;
3421 player->post_delay_counter = 0;
3423 player->dir_waiting = initial_move_dir;
3424 player->action_waiting = ACTION_DEFAULT;
3425 player->last_action_waiting = ACTION_DEFAULT;
3426 player->special_action_bored = ACTION_DEFAULT;
3427 player->special_action_sleeping = ACTION_DEFAULT;
3429 player->switch_x = -1;
3430 player->switch_y = -1;
3432 player->drop_x = -1;
3433 player->drop_y = -1;
3435 player->show_envelope = 0;
3437 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3439 player->push_delay = -1; /* initialized when pushing starts */
3440 player->push_delay_value = game.initial_push_delay_value;
3442 player->drop_delay = 0;
3443 player->drop_pressed_delay = 0;
3445 player->last_jx = -1;
3446 player->last_jy = -1;
3450 player->shield_normal_time_left = 0;
3451 player->shield_deadly_time_left = 0;
3453 player->inventory_infinite_element = EL_UNDEFINED;
3454 player->inventory_size = 0;
3456 if (level.use_initial_inventory[i])
3458 for (j = 0; j < level.initial_inventory_size[i]; j++)
3460 int element = level.initial_inventory_content[i][j];
3461 int collect_count = element_info[element].collect_count_initial;
3464 if (!IS_CUSTOM_ELEMENT(element))
3467 if (collect_count == 0)
3468 player->inventory_infinite_element = element;
3470 for (k = 0; k < collect_count; k++)
3471 if (player->inventory_size < MAX_INVENTORY_SIZE)
3472 player->inventory_element[player->inventory_size++] = element;
3476 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3477 SnapField(player, 0, 0);
3479 player->LevelSolved = FALSE;
3480 player->GameOver = FALSE;
3482 player->LevelSolved_GameWon = FALSE;
3483 player->LevelSolved_GameEnd = FALSE;
3484 player->LevelSolved_PanelOff = FALSE;
3485 player->LevelSolved_SaveTape = FALSE;
3486 player->LevelSolved_SaveScore = FALSE;
3488 player->LevelSolved_CountingTime = 0;
3489 player->LevelSolved_CountingScore = 0;
3490 player->LevelSolved_CountingHealth = 0;
3492 map_player_action[i] = i;
3495 network_player_action_received = FALSE;
3497 #if defined(NETWORK_AVALIABLE)
3498 /* initial null action */
3499 if (network_playing)
3500 SendToServer_MovePlayer(MV_NONE);
3509 TimeLeft = level.time;
3512 ScreenMovDir = MV_NONE;
3516 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3518 AllPlayersGone = FALSE;
3520 game.no_time_limit = (level.time == 0);
3522 game.yamyam_content_nr = 0;
3523 game.robot_wheel_active = FALSE;
3524 game.magic_wall_active = FALSE;
3525 game.magic_wall_time_left = 0;
3526 game.light_time_left = 0;
3527 game.timegate_time_left = 0;
3528 game.switchgate_pos = 0;
3529 game.wind_direction = level.wind_direction_initial;
3531 game.lenses_time_left = 0;
3532 game.magnify_time_left = 0;
3534 game.ball_state = level.ball_state_initial;
3535 game.ball_content_nr = 0;
3537 game.envelope_active = FALSE;
3539 /* set focus to local player for network games, else to all players */
3540 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3541 game.centered_player_nr_next = game.centered_player_nr;
3542 game.set_centered_player = FALSE;
3544 if (network_playing && tape.recording)
3546 /* store client dependent player focus when recording network games */
3547 tape.centered_player_nr_next = game.centered_player_nr_next;
3548 tape.set_centered_player = TRUE;
3551 for (i = 0; i < NUM_BELTS; i++)
3553 game.belt_dir[i] = MV_NONE;
3554 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3557 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3558 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3560 #if DEBUG_INIT_PLAYER
3563 printf("Player status at level initialization:\n");
3567 SCAN_PLAYFIELD(x, y)
3569 Feld[x][y] = level.field[x][y];
3570 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3571 ChangeDelay[x][y] = 0;
3572 ChangePage[x][y] = -1;
3573 CustomValue[x][y] = 0; /* initialized in InitField() */
3574 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3576 WasJustMoving[x][y] = 0;
3577 WasJustFalling[x][y] = 0;
3578 CheckCollision[x][y] = 0;
3579 CheckImpact[x][y] = 0;
3581 Pushed[x][y] = FALSE;
3583 ChangeCount[x][y] = 0;
3584 ChangeEvent[x][y] = -1;
3586 ExplodePhase[x][y] = 0;
3587 ExplodeDelay[x][y] = 0;
3588 ExplodeField[x][y] = EX_TYPE_NONE;
3590 RunnerVisit[x][y] = 0;
3591 PlayerVisit[x][y] = 0;
3594 GfxRandom[x][y] = INIT_GFX_RANDOM();
3595 GfxElement[x][y] = EL_UNDEFINED;
3596 GfxAction[x][y] = ACTION_DEFAULT;
3597 GfxDir[x][y] = MV_NONE;
3598 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3601 SCAN_PLAYFIELD(x, y)
3603 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3605 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3607 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3610 InitField(x, y, TRUE);
3612 ResetGfxAnimation(x, y);
3617 for (i = 0; i < MAX_PLAYERS; i++)
3619 struct PlayerInfo *player = &stored_player[i];
3621 /* set number of special actions for bored and sleeping animation */
3622 player->num_special_action_bored =
3623 get_num_special_action(player->artwork_element,
3624 ACTION_BORING_1, ACTION_BORING_LAST);
3625 player->num_special_action_sleeping =
3626 get_num_special_action(player->artwork_element,
3627 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3630 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3631 emulate_sb ? EMU_SOKOBAN :
3632 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3634 /* initialize type of slippery elements */
3635 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637 if (!IS_CUSTOM_ELEMENT(i))
3639 /* default: elements slip down either to the left or right randomly */
3640 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3642 /* SP style elements prefer to slip down on the left side */
3643 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3644 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3646 /* BD style elements prefer to slip down on the left side */
3647 if (game.emulation == EMU_BOULDERDASH)
3648 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3652 /* initialize explosion and ignition delay */
3653 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3655 if (!IS_CUSTOM_ELEMENT(i))
3658 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3659 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3660 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3661 int last_phase = (num_phase + 1) * delay;
3662 int half_phase = (num_phase / 2) * delay;
3664 element_info[i].explosion_delay = last_phase - 1;
3665 element_info[i].ignition_delay = half_phase;
3667 if (i == EL_BLACK_ORB)
3668 element_info[i].ignition_delay = 1;
3672 /* correct non-moving belts to start moving left */
3673 for (i = 0; i < NUM_BELTS; i++)
3674 if (game.belt_dir[i] == MV_NONE)
3675 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3679 /* choose default local player */
3680 local_player = &stored_player[0];
3682 for (i = 0; i < MAX_PLAYERS; i++)
3683 stored_player[i].connected = FALSE;
3685 local_player->connected = TRUE;
3686 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3690 for (i = 0; i < MAX_PLAYERS; i++)
3691 stored_player[i].connected = tape.player_participates[i];
3693 else if (game.team_mode && !options.network)
3695 /* try to guess locally connected team mode players (needed for correct
3696 assignment of player figures from level to locally playing players) */
3698 for (i = 0; i < MAX_PLAYERS; i++)
3699 if (setup.input[i].use_joystick ||
3700 setup.input[i].key.left != KSYM_UNDEFINED)
3701 stored_player[i].connected = TRUE;
3704 #if DEBUG_INIT_PLAYER
3707 printf("Player status after level initialization:\n");
3709 for (i = 0; i < MAX_PLAYERS; i++)
3711 struct PlayerInfo *player = &stored_player[i];
3713 printf("- player %d: present == %d, connected == %d, active == %d",
3719 if (local_player == player)
3720 printf(" (local player)");
3727 #if DEBUG_INIT_PLAYER
3729 printf("Reassigning players ...\n");
3732 /* check if any connected player was not found in playfield */
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 struct PlayerInfo *player = &stored_player[i];
3737 if (player->connected && !player->present)
3739 struct PlayerInfo *field_player = NULL;
3741 #if DEBUG_INIT_PLAYER
3743 printf("- looking for field player for player %d ...\n", i + 1);
3746 /* assign first free player found that is present in the playfield */
3748 /* first try: look for unmapped playfield player that is not connected */
3749 for (j = 0; j < MAX_PLAYERS; j++)
3750 if (field_player == NULL &&
3751 stored_player[j].present &&
3752 !stored_player[j].mapped &&
3753 !stored_player[j].connected)
3754 field_player = &stored_player[j];
3756 /* second try: look for *any* unmapped playfield player */
3757 for (j = 0; j < MAX_PLAYERS; j++)
3758 if (field_player == NULL &&
3759 stored_player[j].present &&
3760 !stored_player[j].mapped)
3761 field_player = &stored_player[j];
3763 if (field_player != NULL)
3765 int jx = field_player->jx, jy = field_player->jy;
3767 #if DEBUG_INIT_PLAYER
3769 printf("- found player %d\n", field_player->index_nr + 1);
3772 player->present = FALSE;
3773 player->active = FALSE;
3775 field_player->present = TRUE;
3776 field_player->active = TRUE;
3779 player->initial_element = field_player->initial_element;
3780 player->artwork_element = field_player->artwork_element;
3782 player->block_last_field = field_player->block_last_field;
3783 player->block_delay_adjustment = field_player->block_delay_adjustment;
3786 StorePlayer[jx][jy] = field_player->element_nr;
3788 field_player->jx = field_player->last_jx = jx;
3789 field_player->jy = field_player->last_jy = jy;
3791 if (local_player == player)
3792 local_player = field_player;
3794 map_player_action[field_player->index_nr] = i;
3796 field_player->mapped = TRUE;
3798 #if DEBUG_INIT_PLAYER
3800 printf("- map_player_action[%d] == %d\n",
3801 field_player->index_nr + 1, i + 1);
3806 if (player->connected && player->present)
3807 player->mapped = TRUE;
3810 #if DEBUG_INIT_PLAYER
3813 printf("Player status after player assignment (first stage):\n");
3815 for (i = 0; i < MAX_PLAYERS; i++)
3817 struct PlayerInfo *player = &stored_player[i];
3819 printf("- player %d: present == %d, connected == %d, active == %d",
3825 if (local_player == player)
3826 printf(" (local player)");
3835 /* check if any connected player was not found in playfield */
3836 for (i = 0; i < MAX_PLAYERS; i++)
3838 struct PlayerInfo *player = &stored_player[i];
3840 if (player->connected && !player->present)
3842 for (j = 0; j < MAX_PLAYERS; j++)
3844 struct PlayerInfo *field_player = &stored_player[j];
3845 int jx = field_player->jx, jy = field_player->jy;
3847 /* assign first free player found that is present in the playfield */
3848 if (field_player->present && !field_player->connected)
3850 player->present = TRUE;
3851 player->active = TRUE;
3853 field_player->present = FALSE;
3854 field_player->active = FALSE;
3856 player->initial_element = field_player->initial_element;
3857 player->artwork_element = field_player->artwork_element;
3859 player->block_last_field = field_player->block_last_field;
3860 player->block_delay_adjustment = field_player->block_delay_adjustment;
3862 StorePlayer[jx][jy] = player->element_nr;
3864 player->jx = player->last_jx = jx;
3865 player->jy = player->last_jy = jy;
3875 printf("::: local_player->present == %d\n", local_player->present);
3880 /* when playing a tape, eliminate all players who do not participate */
3882 #if USE_NEW_PLAYER_ASSIGNMENTS
3884 if (!game.team_mode)
3886 for (i = 0; i < MAX_PLAYERS; i++)
3888 if (stored_player[i].active &&
3889 !tape.player_participates[map_player_action[i]])
3891 struct PlayerInfo *player = &stored_player[i];
3892 int jx = player->jx, jy = player->jy;
3894 #if DEBUG_INIT_PLAYER
3896 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3899 player->active = FALSE;
3900 StorePlayer[jx][jy] = 0;
3901 Feld[jx][jy] = EL_EMPTY;
3908 for (i = 0; i < MAX_PLAYERS; i++)
3910 if (stored_player[i].active &&
3911 !tape.player_participates[i])
3913 struct PlayerInfo *player = &stored_player[i];
3914 int jx = player->jx, jy = player->jy;
3916 player->active = FALSE;
3917 StorePlayer[jx][jy] = 0;
3918 Feld[jx][jy] = EL_EMPTY;
3923 else if (!options.network && !game.team_mode) /* && !tape.playing */
3925 /* when in single player mode, eliminate all but the first active player */
3927 for (i = 0; i < MAX_PLAYERS; i++)
3929 if (stored_player[i].active)
3931 for (j = i + 1; j < MAX_PLAYERS; j++)
3933 if (stored_player[j].active)
3935 struct PlayerInfo *player = &stored_player[j];
3936 int jx = player->jx, jy = player->jy;
3938 player->active = FALSE;
3939 player->present = FALSE;
3941 StorePlayer[jx][jy] = 0;
3942 Feld[jx][jy] = EL_EMPTY;
3949 /* when recording the game, store which players take part in the game */
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953 for (i = 0; i < MAX_PLAYERS; i++)
3954 if (stored_player[i].connected)
3955 tape.player_participates[i] = TRUE;
3957 for (i = 0; i < MAX_PLAYERS; i++)
3958 if (stored_player[i].active)
3959 tape.player_participates[i] = TRUE;
3963 #if DEBUG_INIT_PLAYER
3966 printf("Player status after player assignment (final stage):\n");
3968 for (i = 0; i < MAX_PLAYERS; i++)
3970 struct PlayerInfo *player = &stored_player[i];
3972 printf("- player %d: present == %d, connected == %d, active == %d",
3978 if (local_player == player)
3979 printf(" (local player)");
3986 if (BorderElement == EL_EMPTY)
3989 SBX_Right = lev_fieldx - SCR_FIELDX;
3991 SBY_Lower = lev_fieldy - SCR_FIELDY;
3996 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3998 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4001 if (full_lev_fieldx <= SCR_FIELDX)
4002 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4003 if (full_lev_fieldy <= SCR_FIELDY)
4004 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4006 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4008 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4011 /* if local player not found, look for custom element that might create
4012 the player (make some assumptions about the right custom element) */
4013 if (!local_player->present)
4015 int start_x = 0, start_y = 0;
4016 int found_rating = 0;
4017 int found_element = EL_UNDEFINED;
4018 int player_nr = local_player->index_nr;
4020 SCAN_PLAYFIELD(x, y)
4022 int element = Feld[x][y];
4027 if (level.use_start_element[player_nr] &&
4028 level.start_element[player_nr] == element &&
4035 found_element = element;
4038 if (!IS_CUSTOM_ELEMENT(element))
4041 if (CAN_CHANGE(element))
4043 for (i = 0; i < element_info[element].num_change_pages; i++)
4045 /* check for player created from custom element as single target */
4046 content = element_info[element].change_page[i].target_element;
4047 is_player = ELEM_IS_PLAYER(content);
4049 if (is_player && (found_rating < 3 ||
4050 (found_rating == 3 && element < found_element)))
4056 found_element = element;
4061 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4063 /* check for player created from custom element as explosion content */
4064 content = element_info[element].content.e[xx][yy];
4065 is_player = ELEM_IS_PLAYER(content);
4067 if (is_player && (found_rating < 2 ||
4068 (found_rating == 2 && element < found_element)))
4070 start_x = x + xx - 1;
4071 start_y = y + yy - 1;
4074 found_element = element;
4077 if (!CAN_CHANGE(element))
4080 for (i = 0; i < element_info[element].num_change_pages; i++)
4082 /* check for player created from custom element as extended target */
4084 element_info[element].change_page[i].target_content.e[xx][yy];
4086 is_player = ELEM_IS_PLAYER(content);
4088 if (is_player && (found_rating < 1 ||
4089 (found_rating == 1 && element < found_element)))
4091 start_x = x + xx - 1;
4092 start_y = y + yy - 1;
4095 found_element = element;
4101 scroll_x = SCROLL_POSITION_X(start_x);
4102 scroll_y = SCROLL_POSITION_Y(start_y);
4106 scroll_x = SCROLL_POSITION_X(local_player->jx);
4107 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4110 /* !!! FIX THIS (START) !!! */
4111 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4113 InitGameEngine_EM();
4115 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4117 InitGameEngine_SP();
4119 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4121 InitGameEngine_MM();
4125 DrawLevel(REDRAW_FIELD);
4128 /* after drawing the level, correct some elements */
4129 if (game.timegate_time_left == 0)
4130 CloseAllOpenTimegates();
4133 /* blit playfield from scroll buffer to normal back buffer for fading in */
4134 BlitScreenToBitmap(backbuffer);
4135 /* !!! FIX THIS (END) !!! */
4137 DrawMaskedBorder(fade_mask);
4142 // full screen redraw is required at this point in the following cases:
4143 // - special editor door undrawn when game was started from level editor
4144 // - drawing area (playfield) was changed and has to be removed completely
4145 redraw_mask = REDRAW_ALL;
4149 if (!game.restart_level)
4151 /* copy default game door content to main double buffer */
4153 /* !!! CHECK AGAIN !!! */
4154 SetPanelBackground();
4155 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4156 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4159 SetPanelBackground();
4160 SetDrawBackgroundMask(REDRAW_DOOR_1);
4162 UpdateAndDisplayGameControlValues();
4164 if (!game.restart_level)
4170 CreateGameButtons();
4172 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4173 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4174 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4179 /* copy actual game door content to door double buffer for OpenDoor() */
4180 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4182 OpenDoor(DOOR_OPEN_ALL);
4184 PlaySound(SND_GAME_STARTING);
4186 if (setup.sound_music)
4189 KeyboardAutoRepeatOffUnlessAutoplay();
4191 #if DEBUG_INIT_PLAYER
4194 printf("Player status (final):\n");
4196 for (i = 0; i < MAX_PLAYERS; i++)
4198 struct PlayerInfo *player = &stored_player[i];
4200 printf("- player %d: present == %d, connected == %d, active == %d",
4206 if (local_player == player)
4207 printf(" (local player)");
4220 if (!game.restart_level && !tape.playing)
4222 LevelStats_incPlayed(level_nr);
4224 SaveLevelSetup_SeriesInfo();
4227 game.restart_level = FALSE;
4229 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4230 InitGameActions_MM();
4232 SaveEngineSnapshotToListInitial();
4235 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4236 int actual_player_x, int actual_player_y)
4238 /* this is used for non-R'n'D game engines to update certain engine values */
4240 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4242 actual_player_x = correctLevelPosX_EM(actual_player_x);
4243 actual_player_y = correctLevelPosY_EM(actual_player_y);
4246 /* needed to determine if sounds are played within the visible screen area */
4247 scroll_x = actual_scroll_x;
4248 scroll_y = actual_scroll_y;
4250 /* needed to get player position for "follow finger" playing input method */
4251 local_player->jx = actual_player_x;
4252 local_player->jy = actual_player_y;
4255 void InitMovDir(int x, int y)
4257 int i, element = Feld[x][y];
4258 static int xy[4][2] =
4265 static int direction[3][4] =
4267 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4268 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4269 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4278 Feld[x][y] = EL_BUG;
4279 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4282 case EL_SPACESHIP_RIGHT:
4283 case EL_SPACESHIP_UP:
4284 case EL_SPACESHIP_LEFT:
4285 case EL_SPACESHIP_DOWN:
4286 Feld[x][y] = EL_SPACESHIP;
4287 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4290 case EL_BD_BUTTERFLY_RIGHT:
4291 case EL_BD_BUTTERFLY_UP:
4292 case EL_BD_BUTTERFLY_LEFT:
4293 case EL_BD_BUTTERFLY_DOWN:
4294 Feld[x][y] = EL_BD_BUTTERFLY;
4295 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4298 case EL_BD_FIREFLY_RIGHT:
4299 case EL_BD_FIREFLY_UP:
4300 case EL_BD_FIREFLY_LEFT:
4301 case EL_BD_FIREFLY_DOWN:
4302 Feld[x][y] = EL_BD_FIREFLY;
4303 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4306 case EL_PACMAN_RIGHT:
4308 case EL_PACMAN_LEFT:
4309 case EL_PACMAN_DOWN:
4310 Feld[x][y] = EL_PACMAN;
4311 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4314 case EL_YAMYAM_LEFT:
4315 case EL_YAMYAM_RIGHT:
4317 case EL_YAMYAM_DOWN:
4318 Feld[x][y] = EL_YAMYAM;
4319 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4322 case EL_SP_SNIKSNAK:
4323 MovDir[x][y] = MV_UP;
4326 case EL_SP_ELECTRON:
4327 MovDir[x][y] = MV_LEFT;
4334 Feld[x][y] = EL_MOLE;
4335 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4339 if (IS_CUSTOM_ELEMENT(element))
4341 struct ElementInfo *ei = &element_info[element];
4342 int move_direction_initial = ei->move_direction_initial;
4343 int move_pattern = ei->move_pattern;
4345 if (move_direction_initial == MV_START_PREVIOUS)
4347 if (MovDir[x][y] != MV_NONE)
4350 move_direction_initial = MV_START_AUTOMATIC;
4353 if (move_direction_initial == MV_START_RANDOM)
4354 MovDir[x][y] = 1 << RND(4);
4355 else if (move_direction_initial & MV_ANY_DIRECTION)
4356 MovDir[x][y] = move_direction_initial;
4357 else if (move_pattern == MV_ALL_DIRECTIONS ||
4358 move_pattern == MV_TURNING_LEFT ||
4359 move_pattern == MV_TURNING_RIGHT ||
4360 move_pattern == MV_TURNING_LEFT_RIGHT ||
4361 move_pattern == MV_TURNING_RIGHT_LEFT ||
4362 move_pattern == MV_TURNING_RANDOM)
4363 MovDir[x][y] = 1 << RND(4);
4364 else if (move_pattern == MV_HORIZONTAL)
4365 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4366 else if (move_pattern == MV_VERTICAL)
4367 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4368 else if (move_pattern & MV_ANY_DIRECTION)
4369 MovDir[x][y] = element_info[element].move_pattern;
4370 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4371 move_pattern == MV_ALONG_RIGHT_SIDE)
4373 /* use random direction as default start direction */
4374 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4375 MovDir[x][y] = 1 << RND(4);
4377 for (i = 0; i < NUM_DIRECTIONS; i++)
4379 int x1 = x + xy[i][0];
4380 int y1 = y + xy[i][1];
4382 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4384 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4385 MovDir[x][y] = direction[0][i];
4387 MovDir[x][y] = direction[1][i];
4396 MovDir[x][y] = 1 << RND(4);
4398 if (element != EL_BUG &&
4399 element != EL_SPACESHIP &&
4400 element != EL_BD_BUTTERFLY &&
4401 element != EL_BD_FIREFLY)
4404 for (i = 0; i < NUM_DIRECTIONS; i++)
4406 int x1 = x + xy[i][0];
4407 int y1 = y + xy[i][1];
4409 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4411 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4413 MovDir[x][y] = direction[0][i];
4416 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4417 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4419 MovDir[x][y] = direction[1][i];
4428 GfxDir[x][y] = MovDir[x][y];
4431 void InitAmoebaNr(int x, int y)
4434 int group_nr = AmoebeNachbarNr(x, y);
4438 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4440 if (AmoebaCnt[i] == 0)
4448 AmoebaNr[x][y] = group_nr;
4449 AmoebaCnt[group_nr]++;
4450 AmoebaCnt2[group_nr]++;
4453 static void PlayerWins(struct PlayerInfo *player)
4455 player->LevelSolved = TRUE;
4456 player->GameOver = TRUE;
4458 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4459 level.native_em_level->lev->score :
4460 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4463 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4464 MM_HEALTH(game_mm.laser_overload_value) :
4467 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4469 player->LevelSolved_CountingScore = player->score_final;
4470 player->LevelSolved_CountingHealth = player->health_final;
4475 static int time, time_final;
4476 static int score, score_final;
4477 static int health, health_final;
4478 static int game_over_delay_1 = 0;
4479 static int game_over_delay_2 = 0;
4480 static int game_over_delay_3 = 0;
4481 int game_over_delay_value_1 = 50;
4482 int game_over_delay_value_2 = 25;
4483 int game_over_delay_value_3 = 50;
4485 if (!local_player->LevelSolved_GameWon)
4489 /* do not start end game actions before the player stops moving (to exit) */
4490 if (local_player->MovPos)
4493 local_player->LevelSolved_GameWon = TRUE;
4494 local_player->LevelSolved_SaveTape = tape.recording;
4495 local_player->LevelSolved_SaveScore = !tape.playing;
4499 LevelStats_incSolved(level_nr);
4501 SaveLevelSetup_SeriesInfo();
4504 if (tape.auto_play) /* tape might already be stopped here */
4505 tape.auto_play_level_solved = TRUE;
4509 game_over_delay_1 = 0;
4510 game_over_delay_2 = 0;
4511 game_over_delay_3 = game_over_delay_value_3;
4513 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4514 score = score_final = local_player->score_final;
4515 health = health_final = local_player->health_final;
4517 if (level.score[SC_TIME_BONUS] > 0)
4522 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4524 else if (game.no_time_limit && TimePlayed < 999)
4527 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4530 game_over_delay_1 = game_over_delay_value_1;
4532 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4535 score_final += health * level.score[SC_TIME_BONUS];
4537 game_over_delay_2 = game_over_delay_value_2;
4540 local_player->score_final = score_final;
4541 local_player->health_final = health_final;
4544 if (level_editor_test_game)
4547 score = score_final;
4549 local_player->LevelSolved_CountingTime = time;
4550 local_player->LevelSolved_CountingScore = score;
4552 game_panel_controls[GAME_PANEL_TIME].value = time;
4553 game_panel_controls[GAME_PANEL_SCORE].value = score;
4555 DisplayGameControlValues();
4558 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4560 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4562 /* close exit door after last player */
4563 if ((AllPlayersGone &&
4564 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4565 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4566 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4567 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4568 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4570 int element = Feld[ExitX][ExitY];
4572 Feld[ExitX][ExitY] =
4573 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4574 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4575 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4576 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4577 EL_EM_STEEL_EXIT_CLOSING);
4579 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4582 /* player disappears */
4583 DrawLevelField(ExitX, ExitY);
4586 for (i = 0; i < MAX_PLAYERS; i++)
4588 struct PlayerInfo *player = &stored_player[i];
4590 if (player->present)
4592 RemovePlayer(player);
4594 /* player disappears */
4595 DrawLevelField(player->jx, player->jy);
4600 PlaySound(SND_GAME_WINNING);
4603 if (game_over_delay_1 > 0)
4605 game_over_delay_1--;
4610 if (time != time_final)
4612 int time_to_go = ABS(time_final - time);
4613 int time_count_dir = (time < time_final ? +1 : -1);
4614 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4616 time += time_count_steps * time_count_dir;
4617 score += time_count_steps * level.score[SC_TIME_BONUS];
4619 local_player->LevelSolved_CountingTime = time;
4620 local_player->LevelSolved_CountingScore = score;
4622 game_panel_controls[GAME_PANEL_TIME].value = time;
4623 game_panel_controls[GAME_PANEL_SCORE].value = score;
4625 DisplayGameControlValues();
4627 if (time == time_final)
4628 StopSound(SND_GAME_LEVELTIME_BONUS);
4629 else if (setup.sound_loops)
4630 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4632 PlaySound(SND_GAME_LEVELTIME_BONUS);
4637 if (game_over_delay_2 > 0)
4639 game_over_delay_2--;
4644 if (health != health_final)
4646 int health_count_dir = (health < health_final ? +1 : -1);
4648 health += health_count_dir;
4649 score += level.score[SC_TIME_BONUS];
4651 local_player->LevelSolved_CountingHealth = health;
4652 local_player->LevelSolved_CountingScore = score;
4654 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4655 game_panel_controls[GAME_PANEL_SCORE].value = score;
4657 DisplayGameControlValues();
4659 if (health == health_final)
4660 StopSound(SND_GAME_LEVELTIME_BONUS);
4661 else if (setup.sound_loops)
4662 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4664 PlaySound(SND_GAME_LEVELTIME_BONUS);
4669 local_player->LevelSolved_PanelOff = TRUE;
4671 if (game_over_delay_3 > 0)
4673 game_over_delay_3--;
4684 boolean raise_level = FALSE;
4686 local_player->LevelSolved_GameEnd = TRUE;
4688 if (!global.use_envelope_request)
4689 CloseDoor(DOOR_CLOSE_1);
4691 if (local_player->LevelSolved_SaveTape)
4693 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4696 CloseDoor(DOOR_CLOSE_ALL);
4698 if (level_editor_test_game)
4700 SetGameStatus(GAME_MODE_MAIN);
4707 if (!local_player->LevelSolved_SaveScore)
4709 SetGameStatus(GAME_MODE_MAIN);
4716 if (level_nr == leveldir_current->handicap_level)
4718 leveldir_current->handicap_level++;
4720 SaveLevelSetup_SeriesInfo();
4723 if (setup.increment_levels &&
4724 level_nr < leveldir_current->last_level)
4725 raise_level = TRUE; /* advance to next level */
4727 if ((hi_pos = NewHiScore()) >= 0)
4729 SetGameStatus(GAME_MODE_SCORES);
4731 DrawHallOfFame(hi_pos);
4741 SetGameStatus(GAME_MODE_MAIN);
4757 boolean one_score_entry_per_name = !program.many_scores_per_name;
4759 LoadScore(level_nr);
4761 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4762 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4765 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4767 if (local_player->score_final > highscore[k].Score)
4769 /* player has made it to the hall of fame */
4771 if (k < MAX_SCORE_ENTRIES - 1)
4773 int m = MAX_SCORE_ENTRIES - 1;
4775 if (one_score_entry_per_name)
4777 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4778 if (strEqual(setup.player_name, highscore[l].Name))
4781 if (m == k) /* player's new highscore overwrites his old one */
4785 for (l = m; l > k; l--)
4787 strcpy(highscore[l].Name, highscore[l - 1].Name);
4788 highscore[l].Score = highscore[l - 1].Score;
4794 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4795 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4796 highscore[k].Score = local_player->score_final;
4801 else if (one_score_entry_per_name &&
4802 !strncmp(setup.player_name, highscore[k].Name,
4803 MAX_PLAYER_NAME_LEN))
4804 break; /* player already there with a higher score */
4808 SaveScore(level_nr);
4813 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4815 int element = Feld[x][y];
4816 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4817 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4818 int horiz_move = (dx != 0);
4819 int sign = (horiz_move ? dx : dy);
4820 int step = sign * element_info[element].move_stepsize;
4822 /* special values for move stepsize for spring and things on conveyor belt */
4825 if (CAN_FALL(element) &&
4826 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4827 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4828 else if (element == EL_SPRING)
4829 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4835 inline static int getElementMoveStepsize(int x, int y)
4837 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4840 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4842 if (player->GfxAction != action || player->GfxDir != dir)
4844 player->GfxAction = action;
4845 player->GfxDir = dir;
4847 player->StepFrame = 0;
4851 static void ResetGfxFrame(int x, int y)
4853 // profiling showed that "autotest" spends 10~20% of its time in this function
4854 if (DrawingDeactivatedField())
4857 int element = Feld[x][y];
4858 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4860 if (graphic_info[graphic].anim_global_sync)
4861 GfxFrame[x][y] = FrameCounter;
4862 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4863 GfxFrame[x][y] = CustomValue[x][y];
4864 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4865 GfxFrame[x][y] = element_info[element].collect_score;
4866 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4867 GfxFrame[x][y] = ChangeDelay[x][y];
4870 static void ResetGfxAnimation(int x, int y)
4872 GfxAction[x][y] = ACTION_DEFAULT;
4873 GfxDir[x][y] = MovDir[x][y];
4876 ResetGfxFrame(x, y);
4879 static void ResetRandomAnimationValue(int x, int y)
4881 GfxRandom[x][y] = INIT_GFX_RANDOM();
4884 void InitMovingField(int x, int y, int direction)
4886 int element = Feld[x][y];
4887 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4888 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4891 boolean is_moving_before, is_moving_after;
4893 /* check if element was/is moving or being moved before/after mode change */
4894 is_moving_before = (WasJustMoving[x][y] != 0);
4895 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4897 /* reset animation only for moving elements which change direction of moving
4898 or which just started or stopped moving
4899 (else CEs with property "can move" / "not moving" are reset each frame) */
4900 if (is_moving_before != is_moving_after ||
4901 direction != MovDir[x][y])
4902 ResetGfxAnimation(x, y);
4904 MovDir[x][y] = direction;
4905 GfxDir[x][y] = direction;
4907 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4908 direction == MV_DOWN && CAN_FALL(element) ?
4909 ACTION_FALLING : ACTION_MOVING);
4911 /* this is needed for CEs with property "can move" / "not moving" */
4913 if (is_moving_after)
4915 if (Feld[newx][newy] == EL_EMPTY)
4916 Feld[newx][newy] = EL_BLOCKED;
4918 MovDir[newx][newy] = MovDir[x][y];
4920 CustomValue[newx][newy] = CustomValue[x][y];
4922 GfxFrame[newx][newy] = GfxFrame[x][y];
4923 GfxRandom[newx][newy] = GfxRandom[x][y];
4924 GfxAction[newx][newy] = GfxAction[x][y];
4925 GfxDir[newx][newy] = GfxDir[x][y];
4929 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4931 int direction = MovDir[x][y];
4932 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4933 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4939 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4941 int oldx = x, oldy = y;
4942 int direction = MovDir[x][y];
4944 if (direction == MV_LEFT)
4946 else if (direction == MV_RIGHT)
4948 else if (direction == MV_UP)
4950 else if (direction == MV_DOWN)
4953 *comes_from_x = oldx;
4954 *comes_from_y = oldy;
4957 int MovingOrBlocked2Element(int x, int y)
4959 int element = Feld[x][y];
4961 if (element == EL_BLOCKED)
4965 Blocked2Moving(x, y, &oldx, &oldy);
4966 return Feld[oldx][oldy];
4972 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4974 /* like MovingOrBlocked2Element(), but if element is moving
4975 and (x,y) is the field the moving element is just leaving,
4976 return EL_BLOCKED instead of the element value */
4977 int element = Feld[x][y];
4979 if (IS_MOVING(x, y))
4981 if (element == EL_BLOCKED)
4985 Blocked2Moving(x, y, &oldx, &oldy);
4986 return Feld[oldx][oldy];
4995 static void RemoveField(int x, int y)
4997 Feld[x][y] = EL_EMPTY;
5003 CustomValue[x][y] = 0;
5006 ChangeDelay[x][y] = 0;
5007 ChangePage[x][y] = -1;
5008 Pushed[x][y] = FALSE;
5010 GfxElement[x][y] = EL_UNDEFINED;
5011 GfxAction[x][y] = ACTION_DEFAULT;
5012 GfxDir[x][y] = MV_NONE;
5015 void RemoveMovingField(int x, int y)
5017 int oldx = x, oldy = y, newx = x, newy = y;
5018 int element = Feld[x][y];
5019 int next_element = EL_UNDEFINED;
5021 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5024 if (IS_MOVING(x, y))
5026 Moving2Blocked(x, y, &newx, &newy);
5028 if (Feld[newx][newy] != EL_BLOCKED)
5030 /* element is moving, but target field is not free (blocked), but
5031 already occupied by something different (example: acid pool);
5032 in this case, only remove the moving field, but not the target */
5034 RemoveField(oldx, oldy);
5036 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5038 TEST_DrawLevelField(oldx, oldy);
5043 else if (element == EL_BLOCKED)
5045 Blocked2Moving(x, y, &oldx, &oldy);
5046 if (!IS_MOVING(oldx, oldy))
5050 if (element == EL_BLOCKED &&
5051 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5052 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5053 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5054 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5055 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5056 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5057 next_element = get_next_element(Feld[oldx][oldy]);
5059 RemoveField(oldx, oldy);
5060 RemoveField(newx, newy);
5062 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5064 if (next_element != EL_UNDEFINED)
5065 Feld[oldx][oldy] = next_element;
5067 TEST_DrawLevelField(oldx, oldy);
5068 TEST_DrawLevelField(newx, newy);
5071 void DrawDynamite(int x, int y)
5073 int sx = SCREENX(x), sy = SCREENY(y);
5074 int graphic = el2img(Feld[x][y]);
5077 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5080 if (IS_WALKABLE_INSIDE(Back[x][y]))
5084 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5085 else if (Store[x][y])
5086 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5088 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5090 if (Back[x][y] || Store[x][y])
5091 DrawGraphicThruMask(sx, sy, graphic, frame);
5093 DrawGraphic(sx, sy, graphic, frame);
5096 void CheckDynamite(int x, int y)
5098 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5102 if (MovDelay[x][y] != 0)
5105 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5111 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5116 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5118 boolean num_checked_players = 0;
5121 for (i = 0; i < MAX_PLAYERS; i++)
5123 if (stored_player[i].active)
5125 int sx = stored_player[i].jx;
5126 int sy = stored_player[i].jy;
5128 if (num_checked_players == 0)
5135 *sx1 = MIN(*sx1, sx);
5136 *sy1 = MIN(*sy1, sy);
5137 *sx2 = MAX(*sx2, sx);
5138 *sy2 = MAX(*sy2, sy);
5141 num_checked_players++;
5146 static boolean checkIfAllPlayersFitToScreen_RND()
5148 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5150 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5152 return (sx2 - sx1 < SCR_FIELDX &&
5153 sy2 - sy1 < SCR_FIELDY);
5156 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5158 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5160 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5162 *sx = (sx1 + sx2) / 2;
5163 *sy = (sy1 + sy2) / 2;
5166 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5167 boolean center_screen, boolean quick_relocation)
5169 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5170 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5171 boolean no_delay = (tape.warp_forward);
5172 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5173 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5174 int new_scroll_x, new_scroll_y;
5176 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5178 /* case 1: quick relocation inside visible screen (without scrolling) */
5185 if (!level.shifted_relocation || center_screen)
5187 /* relocation _with_ centering of screen */
5189 new_scroll_x = SCROLL_POSITION_X(x);
5190 new_scroll_y = SCROLL_POSITION_Y(y);
5194 /* relocation _without_ centering of screen */
5196 int center_scroll_x = SCROLL_POSITION_X(old_x);
5197 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5198 int offset_x = x + (scroll_x - center_scroll_x);
5199 int offset_y = y + (scroll_y - center_scroll_y);
5201 /* for new screen position, apply previous offset to center position */
5202 new_scroll_x = SCROLL_POSITION_X(offset_x);
5203 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5206 if (quick_relocation)
5208 /* case 2: quick relocation (redraw without visible scrolling) */
5210 scroll_x = new_scroll_x;
5211 scroll_y = new_scroll_y;
5218 /* case 3: visible relocation (with scrolling to new position) */
5220 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5222 SetVideoFrameDelay(wait_delay_value);
5224 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5227 int fx = FX, fy = FY;
5229 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5230 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5232 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5238 fx += dx * TILEX / 2;
5239 fy += dy * TILEY / 2;
5241 ScrollLevel(dx, dy);
5244 /* scroll in two steps of half tile size to make things smoother */
5245 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5247 /* scroll second step to align at full tile size */
5248 BlitScreenToBitmap(window);
5254 SetVideoFrameDelay(frame_delay_value_old);
5257 void RelocatePlayer(int jx, int jy, int el_player_raw)
5259 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5260 int player_nr = GET_PLAYER_NR(el_player);
5261 struct PlayerInfo *player = &stored_player[player_nr];
5262 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5263 boolean no_delay = (tape.warp_forward);
5264 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5265 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5266 int old_jx = player->jx;
5267 int old_jy = player->jy;
5268 int old_element = Feld[old_jx][old_jy];
5269 int element = Feld[jx][jy];
5270 boolean player_relocated = (old_jx != jx || old_jy != jy);
5272 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5273 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5274 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5275 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5276 int leave_side_horiz = move_dir_horiz;
5277 int leave_side_vert = move_dir_vert;
5278 int enter_side = enter_side_horiz | enter_side_vert;
5279 int leave_side = leave_side_horiz | leave_side_vert;
5281 if (player->GameOver) /* do not reanimate dead player */
5284 if (!player_relocated) /* no need to relocate the player */
5287 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5289 RemoveField(jx, jy); /* temporarily remove newly placed player */
5290 DrawLevelField(jx, jy);
5293 if (player->present)
5295 while (player->MovPos)
5297 ScrollPlayer(player, SCROLL_GO_ON);
5298 ScrollScreen(NULL, SCROLL_GO_ON);
5300 AdvanceFrameAndPlayerCounters(player->index_nr);
5304 BackToFront_WithFrameDelay(wait_delay_value);
5307 DrawPlayer(player); /* needed here only to cleanup last field */
5308 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5310 player->is_moving = FALSE;
5313 if (IS_CUSTOM_ELEMENT(old_element))
5314 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5316 player->index_bit, leave_side);
5318 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5320 player->index_bit, leave_side);
5322 Feld[jx][jy] = el_player;
5323 InitPlayerField(jx, jy, el_player, TRUE);
5325 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5326 possible that the relocation target field did not contain a player element,
5327 but a walkable element, to which the new player was relocated -- in this
5328 case, restore that (already initialized!) element on the player field */
5329 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5331 Feld[jx][jy] = element; /* restore previously existing element */
5334 /* only visually relocate centered player */
5335 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5336 FALSE, level.instant_relocation);
5338 TestIfPlayerTouchesBadThing(jx, jy);
5339 TestIfPlayerTouchesCustomElement(jx, jy);
5341 if (IS_CUSTOM_ELEMENT(element))
5342 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5343 player->index_bit, enter_side);
5345 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5346 player->index_bit, enter_side);
5348 if (player->is_switching)
5350 /* ensure that relocation while still switching an element does not cause
5351 a new element to be treated as also switched directly after relocation
5352 (this is important for teleporter switches that teleport the player to
5353 a place where another teleporter switch is in the same direction, which
5354 would then incorrectly be treated as immediately switched before the
5355 direction key that caused the switch was released) */
5357 player->switch_x += jx - old_jx;
5358 player->switch_y += jy - old_jy;
5362 void Explode(int ex, int ey, int phase, int mode)
5368 /* !!! eliminate this variable !!! */
5369 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5371 if (game.explosions_delayed)
5373 ExplodeField[ex][ey] = mode;
5377 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5379 int center_element = Feld[ex][ey];
5380 int artwork_element, explosion_element; /* set these values later */
5382 /* remove things displayed in background while burning dynamite */
5383 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5386 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5388 /* put moving element to center field (and let it explode there) */
5389 center_element = MovingOrBlocked2Element(ex, ey);
5390 RemoveMovingField(ex, ey);
5391 Feld[ex][ey] = center_element;
5394 /* now "center_element" is finally determined -- set related values now */
5395 artwork_element = center_element; /* for custom player artwork */
5396 explosion_element = center_element; /* for custom player artwork */
5398 if (IS_PLAYER(ex, ey))
5400 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5402 artwork_element = stored_player[player_nr].artwork_element;
5404 if (level.use_explosion_element[player_nr])
5406 explosion_element = level.explosion_element[player_nr];
5407 artwork_element = explosion_element;
5411 if (mode == EX_TYPE_NORMAL ||
5412 mode == EX_TYPE_CENTER ||
5413 mode == EX_TYPE_CROSS)
5414 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5416 last_phase = element_info[explosion_element].explosion_delay + 1;
5418 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5420 int xx = x - ex + 1;
5421 int yy = y - ey + 1;
5424 if (!IN_LEV_FIELD(x, y) ||
5425 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5426 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5429 element = Feld[x][y];
5431 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5433 element = MovingOrBlocked2Element(x, y);
5435 if (!IS_EXPLOSION_PROOF(element))
5436 RemoveMovingField(x, y);
5439 /* indestructible elements can only explode in center (but not flames) */
5440 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5441 mode == EX_TYPE_BORDER)) ||
5442 element == EL_FLAMES)
5445 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5446 behaviour, for example when touching a yamyam that explodes to rocks
5447 with active deadly shield, a rock is created under the player !!! */
5448 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5450 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5451 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5452 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5454 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5457 if (IS_ACTIVE_BOMB(element))
5459 /* re-activate things under the bomb like gate or penguin */
5460 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5467 /* save walkable background elements while explosion on same tile */
5468 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5469 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5470 Back[x][y] = element;
5472 /* ignite explodable elements reached by other explosion */
5473 if (element == EL_EXPLOSION)
5474 element = Store2[x][y];
5476 if (AmoebaNr[x][y] &&
5477 (element == EL_AMOEBA_FULL ||
5478 element == EL_BD_AMOEBA ||
5479 element == EL_AMOEBA_GROWING))
5481 AmoebaCnt[AmoebaNr[x][y]]--;
5482 AmoebaCnt2[AmoebaNr[x][y]]--;
5487 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5489 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5491 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5493 if (PLAYERINFO(ex, ey)->use_murphy)
5494 Store[x][y] = EL_EMPTY;
5497 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5498 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5499 else if (ELEM_IS_PLAYER(center_element))
5500 Store[x][y] = EL_EMPTY;
5501 else if (center_element == EL_YAMYAM)
5502 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5503 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5504 Store[x][y] = element_info[center_element].content.e[xx][yy];
5506 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5507 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5508 otherwise) -- FIX THIS !!! */
5509 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5510 Store[x][y] = element_info[element].content.e[1][1];
5512 else if (!CAN_EXPLODE(element))
5513 Store[x][y] = element_info[element].content.e[1][1];
5516 Store[x][y] = EL_EMPTY;
5518 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5519 center_element == EL_AMOEBA_TO_DIAMOND)
5520 Store2[x][y] = element;
5522 Feld[x][y] = EL_EXPLOSION;
5523 GfxElement[x][y] = artwork_element;
5525 ExplodePhase[x][y] = 1;
5526 ExplodeDelay[x][y] = last_phase;
5531 if (center_element == EL_YAMYAM)
5532 game.yamyam_content_nr =
5533 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5545 GfxFrame[x][y] = 0; /* restart explosion animation */
5547 last_phase = ExplodeDelay[x][y];
5549 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5551 /* this can happen if the player leaves an explosion just in time */
5552 if (GfxElement[x][y] == EL_UNDEFINED)
5553 GfxElement[x][y] = EL_EMPTY;
5555 border_element = Store2[x][y];
5556 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5557 border_element = StorePlayer[x][y];
5559 if (phase == element_info[border_element].ignition_delay ||
5560 phase == last_phase)
5562 boolean border_explosion = FALSE;
5564 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5565 !PLAYER_EXPLOSION_PROTECTED(x, y))
5567 KillPlayerUnlessExplosionProtected(x, y);
5568 border_explosion = TRUE;
5570 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5572 Feld[x][y] = Store2[x][y];
5575 border_explosion = TRUE;
5577 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5579 AmoebeUmwandeln(x, y);
5581 border_explosion = TRUE;
5584 /* if an element just explodes due to another explosion (chain-reaction),
5585 do not immediately end the new explosion when it was the last frame of
5586 the explosion (as it would be done in the following "if"-statement!) */
5587 if (border_explosion && phase == last_phase)
5591 if (phase == last_phase)
5595 element = Feld[x][y] = Store[x][y];
5596 Store[x][y] = Store2[x][y] = 0;
5597 GfxElement[x][y] = EL_UNDEFINED;
5599 /* player can escape from explosions and might therefore be still alive */
5600 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5601 element <= EL_PLAYER_IS_EXPLODING_4)
5603 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5604 int explosion_element = EL_PLAYER_1 + player_nr;
5605 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5606 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5608 if (level.use_explosion_element[player_nr])
5609 explosion_element = level.explosion_element[player_nr];
5611 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5612 element_info[explosion_element].content.e[xx][yy]);
5615 /* restore probably existing indestructible background element */
5616 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5617 element = Feld[x][y] = Back[x][y];
5620 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5621 GfxDir[x][y] = MV_NONE;
5622 ChangeDelay[x][y] = 0;
5623 ChangePage[x][y] = -1;
5625 CustomValue[x][y] = 0;
5627 InitField_WithBug2(x, y, FALSE);
5629 TEST_DrawLevelField(x, y);
5631 TestIfElementTouchesCustomElement(x, y);
5633 if (GFX_CRUMBLED(element))
5634 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5636 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5637 StorePlayer[x][y] = 0;
5639 if (ELEM_IS_PLAYER(element))
5640 RelocatePlayer(x, y, element);
5642 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5644 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5645 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5648 TEST_DrawLevelFieldCrumbled(x, y);
5650 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5652 DrawLevelElement(x, y, Back[x][y]);
5653 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5655 else if (IS_WALKABLE_UNDER(Back[x][y]))
5657 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5658 DrawLevelElementThruMask(x, y, Back[x][y]);
5660 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5661 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5665 void DynaExplode(int ex, int ey)
5668 int dynabomb_element = Feld[ex][ey];
5669 int dynabomb_size = 1;
5670 boolean dynabomb_xl = FALSE;
5671 struct PlayerInfo *player;
5672 static int xy[4][2] =
5680 if (IS_ACTIVE_BOMB(dynabomb_element))
5682 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5683 dynabomb_size = player->dynabomb_size;
5684 dynabomb_xl = player->dynabomb_xl;
5685 player->dynabombs_left++;
5688 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5690 for (i = 0; i < NUM_DIRECTIONS; i++)
5692 for (j = 1; j <= dynabomb_size; j++)
5694 int x = ex + j * xy[i][0];
5695 int y = ey + j * xy[i][1];
5698 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5701 element = Feld[x][y];
5703 /* do not restart explosions of fields with active bombs */
5704 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5707 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5709 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5710 !IS_DIGGABLE(element) && !dynabomb_xl)
5716 void Bang(int x, int y)
5718 int element = MovingOrBlocked2Element(x, y);
5719 int explosion_type = EX_TYPE_NORMAL;
5721 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5723 struct PlayerInfo *player = PLAYERINFO(x, y);
5725 element = Feld[x][y] = player->initial_element;
5727 if (level.use_explosion_element[player->index_nr])
5729 int explosion_element = level.explosion_element[player->index_nr];
5731 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5732 explosion_type = EX_TYPE_CROSS;
5733 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5734 explosion_type = EX_TYPE_CENTER;
5742 case EL_BD_BUTTERFLY:
5745 case EL_DARK_YAMYAM:
5749 RaiseScoreElement(element);
5752 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5753 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5754 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5755 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5756 case EL_DYNABOMB_INCREASE_NUMBER:
5757 case EL_DYNABOMB_INCREASE_SIZE:
5758 case EL_DYNABOMB_INCREASE_POWER:
5759 explosion_type = EX_TYPE_DYNA;
5762 case EL_DC_LANDMINE:
5763 explosion_type = EX_TYPE_CENTER;
5768 case EL_LAMP_ACTIVE:
5769 case EL_AMOEBA_TO_DIAMOND:
5770 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5771 explosion_type = EX_TYPE_CENTER;
5775 if (element_info[element].explosion_type == EXPLODES_CROSS)
5776 explosion_type = EX_TYPE_CROSS;
5777 else if (element_info[element].explosion_type == EXPLODES_1X1)
5778 explosion_type = EX_TYPE_CENTER;
5782 if (explosion_type == EX_TYPE_DYNA)
5785 Explode(x, y, EX_PHASE_START, explosion_type);
5787 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5790 void SplashAcid(int x, int y)
5792 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5793 (!IN_LEV_FIELD(x - 1, y - 2) ||
5794 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5795 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5797 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5798 (!IN_LEV_FIELD(x + 1, y - 2) ||
5799 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5800 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5802 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5805 static void InitBeltMovement()
5807 static int belt_base_element[4] =
5809 EL_CONVEYOR_BELT_1_LEFT,
5810 EL_CONVEYOR_BELT_2_LEFT,
5811 EL_CONVEYOR_BELT_3_LEFT,
5812 EL_CONVEYOR_BELT_4_LEFT
5814 static int belt_base_active_element[4] =
5816 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5817 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5818 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5819 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5824 /* set frame order for belt animation graphic according to belt direction */
5825 for (i = 0; i < NUM_BELTS; i++)
5829 for (j = 0; j < NUM_BELT_PARTS; j++)
5831 int element = belt_base_active_element[belt_nr] + j;
5832 int graphic_1 = el2img(element);
5833 int graphic_2 = el2panelimg(element);
5835 if (game.belt_dir[i] == MV_LEFT)
5837 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5838 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5842 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5843 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5848 SCAN_PLAYFIELD(x, y)
5850 int element = Feld[x][y];
5852 for (i = 0; i < NUM_BELTS; i++)
5854 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5856 int e_belt_nr = getBeltNrFromBeltElement(element);
5859 if (e_belt_nr == belt_nr)
5861 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5863 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5870 static void ToggleBeltSwitch(int x, int y)
5872 static int belt_base_element[4] =
5874 EL_CONVEYOR_BELT_1_LEFT,
5875 EL_CONVEYOR_BELT_2_LEFT,
5876 EL_CONVEYOR_BELT_3_LEFT,
5877 EL_CONVEYOR_BELT_4_LEFT
5879 static int belt_base_active_element[4] =
5881 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5882 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5883 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5884 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5886 static int belt_base_switch_element[4] =
5888 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5889 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5890 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5891 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5893 static int belt_move_dir[4] =
5901 int element = Feld[x][y];
5902 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5903 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5904 int belt_dir = belt_move_dir[belt_dir_nr];
5907 if (!IS_BELT_SWITCH(element))
5910 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5911 game.belt_dir[belt_nr] = belt_dir;
5913 if (belt_dir_nr == 3)
5916 /* set frame order for belt animation graphic according to belt direction */
5917 for (i = 0; i < NUM_BELT_PARTS; i++)
5919 int element = belt_base_active_element[belt_nr] + i;
5920 int graphic_1 = el2img(element);
5921 int graphic_2 = el2panelimg(element);
5923 if (belt_dir == MV_LEFT)
5925 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5926 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5930 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5931 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5935 SCAN_PLAYFIELD(xx, yy)
5937 int element = Feld[xx][yy];
5939 if (IS_BELT_SWITCH(element))
5941 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5943 if (e_belt_nr == belt_nr)
5945 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5946 TEST_DrawLevelField(xx, yy);
5949 else if (IS_BELT(element) && belt_dir != MV_NONE)
5951 int e_belt_nr = getBeltNrFromBeltElement(element);
5953 if (e_belt_nr == belt_nr)
5955 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5957 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5958 TEST_DrawLevelField(xx, yy);
5961 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5963 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5965 if (e_belt_nr == belt_nr)
5967 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5969 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5970 TEST_DrawLevelField(xx, yy);
5976 static void ToggleSwitchgateSwitch(int x, int y)
5980 game.switchgate_pos = !game.switchgate_pos;
5982 SCAN_PLAYFIELD(xx, yy)
5984 int element = Feld[xx][yy];
5986 if (element == EL_SWITCHGATE_SWITCH_UP)
5988 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5989 TEST_DrawLevelField(xx, yy);
5991 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5993 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5994 TEST_DrawLevelField(xx, yy);
5996 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5998 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5999 TEST_DrawLevelField(xx, yy);
6001 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6003 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6004 TEST_DrawLevelField(xx, yy);
6006 else if (element == EL_SWITCHGATE_OPEN ||
6007 element == EL_SWITCHGATE_OPENING)
6009 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6011 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6013 else if (element == EL_SWITCHGATE_CLOSED ||
6014 element == EL_SWITCHGATE_CLOSING)
6016 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6018 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6023 static int getInvisibleActiveFromInvisibleElement(int element)
6025 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6026 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6027 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6031 static int getInvisibleFromInvisibleActiveElement(int element)
6033 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6034 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6035 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6039 static void RedrawAllLightSwitchesAndInvisibleElements()
6043 SCAN_PLAYFIELD(x, y)
6045 int element = Feld[x][y];
6047 if (element == EL_LIGHT_SWITCH &&
6048 game.light_time_left > 0)
6050 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6051 TEST_DrawLevelField(x, y);
6053 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6054 game.light_time_left == 0)
6056 Feld[x][y] = EL_LIGHT_SWITCH;
6057 TEST_DrawLevelField(x, y);
6059 else if (element == EL_EMC_DRIPPER &&
6060 game.light_time_left > 0)
6062 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6063 TEST_DrawLevelField(x, y);
6065 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6066 game.light_time_left == 0)
6068 Feld[x][y] = EL_EMC_DRIPPER;
6069 TEST_DrawLevelField(x, y);
6071 else if (element == EL_INVISIBLE_STEELWALL ||
6072 element == EL_INVISIBLE_WALL ||
6073 element == EL_INVISIBLE_SAND)
6075 if (game.light_time_left > 0)
6076 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6078 TEST_DrawLevelField(x, y);
6080 /* uncrumble neighbour fields, if needed */
6081 if (element == EL_INVISIBLE_SAND)
6082 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6084 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6085 element == EL_INVISIBLE_WALL_ACTIVE ||
6086 element == EL_INVISIBLE_SAND_ACTIVE)
6088 if (game.light_time_left == 0)
6089 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6091 TEST_DrawLevelField(x, y);
6093 /* re-crumble neighbour fields, if needed */
6094 if (element == EL_INVISIBLE_SAND)
6095 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6100 static void RedrawAllInvisibleElementsForLenses()
6104 SCAN_PLAYFIELD(x, y)
6106 int element = Feld[x][y];
6108 if (element == EL_EMC_DRIPPER &&
6109 game.lenses_time_left > 0)
6111 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6112 TEST_DrawLevelField(x, y);
6114 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6115 game.lenses_time_left == 0)
6117 Feld[x][y] = EL_EMC_DRIPPER;
6118 TEST_DrawLevelField(x, y);
6120 else if (element == EL_INVISIBLE_STEELWALL ||
6121 element == EL_INVISIBLE_WALL ||
6122 element == EL_INVISIBLE_SAND)
6124 if (game.lenses_time_left > 0)
6125 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6127 TEST_DrawLevelField(x, y);
6129 /* uncrumble neighbour fields, if needed */
6130 if (element == EL_INVISIBLE_SAND)
6131 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6133 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6134 element == EL_INVISIBLE_WALL_ACTIVE ||
6135 element == EL_INVISIBLE_SAND_ACTIVE)
6137 if (game.lenses_time_left == 0)
6138 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6140 TEST_DrawLevelField(x, y);
6142 /* re-crumble neighbour fields, if needed */
6143 if (element == EL_INVISIBLE_SAND)
6144 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6149 static void RedrawAllInvisibleElementsForMagnifier()
6153 SCAN_PLAYFIELD(x, y)
6155 int element = Feld[x][y];
6157 if (element == EL_EMC_FAKE_GRASS &&
6158 game.magnify_time_left > 0)
6160 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6161 TEST_DrawLevelField(x, y);
6163 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6164 game.magnify_time_left == 0)
6166 Feld[x][y] = EL_EMC_FAKE_GRASS;
6167 TEST_DrawLevelField(x, y);
6169 else if (IS_GATE_GRAY(element) &&
6170 game.magnify_time_left > 0)
6172 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6173 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6174 IS_EM_GATE_GRAY(element) ?
6175 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6176 IS_EMC_GATE_GRAY(element) ?
6177 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6178 IS_DC_GATE_GRAY(element) ?
6179 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6181 TEST_DrawLevelField(x, y);
6183 else if (IS_GATE_GRAY_ACTIVE(element) &&
6184 game.magnify_time_left == 0)
6186 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6187 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6188 IS_EM_GATE_GRAY_ACTIVE(element) ?
6189 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6190 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6191 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6192 IS_DC_GATE_GRAY_ACTIVE(element) ?
6193 EL_DC_GATE_WHITE_GRAY :
6195 TEST_DrawLevelField(x, y);
6200 static void ToggleLightSwitch(int x, int y)
6202 int element = Feld[x][y];
6204 game.light_time_left =
6205 (element == EL_LIGHT_SWITCH ?
6206 level.time_light * FRAMES_PER_SECOND : 0);
6208 RedrawAllLightSwitchesAndInvisibleElements();
6211 static void ActivateTimegateSwitch(int x, int y)
6215 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6217 SCAN_PLAYFIELD(xx, yy)
6219 int element = Feld[xx][yy];
6221 if (element == EL_TIMEGATE_CLOSED ||
6222 element == EL_TIMEGATE_CLOSING)
6224 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6225 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6229 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6231 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6232 TEST_DrawLevelField(xx, yy);
6238 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6239 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6242 void Impact(int x, int y)
6244 boolean last_line = (y == lev_fieldy - 1);
6245 boolean object_hit = FALSE;
6246 boolean impact = (last_line || object_hit);
6247 int element = Feld[x][y];
6248 int smashed = EL_STEELWALL;
6250 if (!last_line) /* check if element below was hit */
6252 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6255 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6256 MovDir[x][y + 1] != MV_DOWN ||
6257 MovPos[x][y + 1] <= TILEY / 2));
6259 /* do not smash moving elements that left the smashed field in time */
6260 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6261 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6264 #if USE_QUICKSAND_IMPACT_BUGFIX
6265 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6267 RemoveMovingField(x, y + 1);
6268 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6269 Feld[x][y + 2] = EL_ROCK;
6270 TEST_DrawLevelField(x, y + 2);
6275 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6277 RemoveMovingField(x, y + 1);
6278 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6279 Feld[x][y + 2] = EL_ROCK;
6280 TEST_DrawLevelField(x, y + 2);
6287 smashed = MovingOrBlocked2Element(x, y + 1);
6289 impact = (last_line || object_hit);
6292 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6294 SplashAcid(x, y + 1);
6298 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6299 /* only reset graphic animation if graphic really changes after impact */
6301 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6303 ResetGfxAnimation(x, y);
6304 TEST_DrawLevelField(x, y);
6307 if (impact && CAN_EXPLODE_IMPACT(element))
6312 else if (impact && element == EL_PEARL &&
6313 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6315 ResetGfxAnimation(x, y);
6317 Feld[x][y] = EL_PEARL_BREAKING;
6318 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6321 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6323 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6328 if (impact && element == EL_AMOEBA_DROP)
6330 if (object_hit && IS_PLAYER(x, y + 1))
6331 KillPlayerUnlessEnemyProtected(x, y + 1);
6332 else if (object_hit && smashed == EL_PENGUIN)
6336 Feld[x][y] = EL_AMOEBA_GROWING;
6337 Store[x][y] = EL_AMOEBA_WET;
6339 ResetRandomAnimationValue(x, y);
6344 if (object_hit) /* check which object was hit */
6346 if ((CAN_PASS_MAGIC_WALL(element) &&
6347 (smashed == EL_MAGIC_WALL ||
6348 smashed == EL_BD_MAGIC_WALL)) ||
6349 (CAN_PASS_DC_MAGIC_WALL(element) &&
6350 smashed == EL_DC_MAGIC_WALL))
6353 int activated_magic_wall =
6354 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6355 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6356 EL_DC_MAGIC_WALL_ACTIVE);
6358 /* activate magic wall / mill */
6359 SCAN_PLAYFIELD(xx, yy)
6361 if (Feld[xx][yy] == smashed)
6362 Feld[xx][yy] = activated_magic_wall;
6365 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6366 game.magic_wall_active = TRUE;
6368 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6369 SND_MAGIC_WALL_ACTIVATING :
6370 smashed == EL_BD_MAGIC_WALL ?
6371 SND_BD_MAGIC_WALL_ACTIVATING :
6372 SND_DC_MAGIC_WALL_ACTIVATING));
6375 if (IS_PLAYER(x, y + 1))
6377 if (CAN_SMASH_PLAYER(element))
6379 KillPlayerUnlessEnemyProtected(x, y + 1);
6383 else if (smashed == EL_PENGUIN)
6385 if (CAN_SMASH_PLAYER(element))
6391 else if (element == EL_BD_DIAMOND)
6393 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6399 else if (((element == EL_SP_INFOTRON ||
6400 element == EL_SP_ZONK) &&
6401 (smashed == EL_SP_SNIKSNAK ||
6402 smashed == EL_SP_ELECTRON ||
6403 smashed == EL_SP_DISK_ORANGE)) ||
6404 (element == EL_SP_INFOTRON &&
6405 smashed == EL_SP_DISK_YELLOW))
6410 else if (CAN_SMASH_EVERYTHING(element))
6412 if (IS_CLASSIC_ENEMY(smashed) ||
6413 CAN_EXPLODE_SMASHED(smashed))
6418 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6420 if (smashed == EL_LAMP ||
6421 smashed == EL_LAMP_ACTIVE)
6426 else if (smashed == EL_NUT)
6428 Feld[x][y + 1] = EL_NUT_BREAKING;
6429 PlayLevelSound(x, y, SND_NUT_BREAKING);
6430 RaiseScoreElement(EL_NUT);
6433 else if (smashed == EL_PEARL)
6435 ResetGfxAnimation(x, y);
6437 Feld[x][y + 1] = EL_PEARL_BREAKING;
6438 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6441 else if (smashed == EL_DIAMOND)
6443 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6444 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6447 else if (IS_BELT_SWITCH(smashed))
6449 ToggleBeltSwitch(x, y + 1);
6451 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6452 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6453 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6454 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6456 ToggleSwitchgateSwitch(x, y + 1);
6458 else if (smashed == EL_LIGHT_SWITCH ||
6459 smashed == EL_LIGHT_SWITCH_ACTIVE)
6461 ToggleLightSwitch(x, y + 1);
6465 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6467 CheckElementChangeBySide(x, y + 1, smashed, element,
6468 CE_SWITCHED, CH_SIDE_TOP);
6469 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6475 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6480 /* play sound of magic wall / mill */
6482 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6483 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6484 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6486 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6487 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6488 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6489 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6490 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6491 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6496 /* play sound of object that hits the ground */
6497 if (last_line || object_hit)
6498 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6501 inline static void TurnRoundExt(int x, int y)
6513 { 0, 0 }, { 0, 0 }, { 0, 0 },
6518 int left, right, back;
6522 { MV_DOWN, MV_UP, MV_RIGHT },
6523 { MV_UP, MV_DOWN, MV_LEFT },
6525 { MV_LEFT, MV_RIGHT, MV_DOWN },
6529 { MV_RIGHT, MV_LEFT, MV_UP }
6532 int element = Feld[x][y];
6533 int move_pattern = element_info[element].move_pattern;
6535 int old_move_dir = MovDir[x][y];
6536 int left_dir = turn[old_move_dir].left;
6537 int right_dir = turn[old_move_dir].right;
6538 int back_dir = turn[old_move_dir].back;
6540 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6541 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6542 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6543 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6545 int left_x = x + left_dx, left_y = y + left_dy;
6546 int right_x = x + right_dx, right_y = y + right_dy;
6547 int move_x = x + move_dx, move_y = y + move_dy;
6551 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6553 TestIfBadThingTouchesOtherBadThing(x, y);
6555 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6556 MovDir[x][y] = right_dir;
6557 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6558 MovDir[x][y] = left_dir;
6560 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6562 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6565 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6567 TestIfBadThingTouchesOtherBadThing(x, y);
6569 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6570 MovDir[x][y] = left_dir;
6571 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6572 MovDir[x][y] = right_dir;
6574 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6576 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6579 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6581 TestIfBadThingTouchesOtherBadThing(x, y);
6583 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6584 MovDir[x][y] = left_dir;
6585 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6586 MovDir[x][y] = right_dir;
6588 if (MovDir[x][y] != old_move_dir)
6591 else if (element == EL_YAMYAM)
6593 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6594 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6596 if (can_turn_left && can_turn_right)
6597 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6598 else if (can_turn_left)
6599 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6600 else if (can_turn_right)
6601 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6603 MovDir[x][y] = back_dir;
6605 MovDelay[x][y] = 16 + 16 * RND(3);
6607 else if (element == EL_DARK_YAMYAM)
6609 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6611 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6614 if (can_turn_left && can_turn_right)
6615 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6616 else if (can_turn_left)
6617 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6618 else if (can_turn_right)
6619 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6621 MovDir[x][y] = back_dir;
6623 MovDelay[x][y] = 16 + 16 * RND(3);
6625 else if (element == EL_PACMAN)
6627 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6628 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6630 if (can_turn_left && can_turn_right)
6631 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6632 else if (can_turn_left)
6633 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6634 else if (can_turn_right)
6635 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6637 MovDir[x][y] = back_dir;
6639 MovDelay[x][y] = 6 + RND(40);
6641 else if (element == EL_PIG)
6643 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6644 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6645 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6646 boolean should_turn_left, should_turn_right, should_move_on;
6648 int rnd = RND(rnd_value);
6650 should_turn_left = (can_turn_left &&
6652 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6653 y + back_dy + left_dy)));
6654 should_turn_right = (can_turn_right &&
6656 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6657 y + back_dy + right_dy)));
6658 should_move_on = (can_move_on &&
6661 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6662 y + move_dy + left_dy) ||
6663 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6664 y + move_dy + right_dy)));
6666 if (should_turn_left || should_turn_right || should_move_on)
6668 if (should_turn_left && should_turn_right && should_move_on)
6669 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6670 rnd < 2 * rnd_value / 3 ? right_dir :
6672 else if (should_turn_left && should_turn_right)
6673 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6674 else if (should_turn_left && should_move_on)
6675 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6676 else if (should_turn_right && should_move_on)
6677 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6678 else if (should_turn_left)
6679 MovDir[x][y] = left_dir;
6680 else if (should_turn_right)
6681 MovDir[x][y] = right_dir;
6682 else if (should_move_on)
6683 MovDir[x][y] = old_move_dir;
6685 else if (can_move_on && rnd > rnd_value / 8)
6686 MovDir[x][y] = old_move_dir;
6687 else if (can_turn_left && can_turn_right)
6688 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6689 else if (can_turn_left && rnd > rnd_value / 8)
6690 MovDir[x][y] = left_dir;
6691 else if (can_turn_right && rnd > rnd_value/8)
6692 MovDir[x][y] = right_dir;
6694 MovDir[x][y] = back_dir;
6696 xx = x + move_xy[MovDir[x][y]].dx;
6697 yy = y + move_xy[MovDir[x][y]].dy;
6699 if (!IN_LEV_FIELD(xx, yy) ||
6700 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6701 MovDir[x][y] = old_move_dir;
6705 else if (element == EL_DRAGON)
6707 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6708 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6709 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6711 int rnd = RND(rnd_value);
6713 if (can_move_on && rnd > rnd_value / 8)
6714 MovDir[x][y] = old_move_dir;
6715 else if (can_turn_left && can_turn_right)
6716 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6717 else if (can_turn_left && rnd > rnd_value / 8)
6718 MovDir[x][y] = left_dir;
6719 else if (can_turn_right && rnd > rnd_value / 8)
6720 MovDir[x][y] = right_dir;
6722 MovDir[x][y] = back_dir;
6724 xx = x + move_xy[MovDir[x][y]].dx;
6725 yy = y + move_xy[MovDir[x][y]].dy;
6727 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6728 MovDir[x][y] = old_move_dir;
6732 else if (element == EL_MOLE)
6734 boolean can_move_on =
6735 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6736 IS_AMOEBOID(Feld[move_x][move_y]) ||
6737 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6740 boolean can_turn_left =
6741 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6742 IS_AMOEBOID(Feld[left_x][left_y])));
6744 boolean can_turn_right =
6745 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6746 IS_AMOEBOID(Feld[right_x][right_y])));
6748 if (can_turn_left && can_turn_right)
6749 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6750 else if (can_turn_left)
6751 MovDir[x][y] = left_dir;
6753 MovDir[x][y] = right_dir;
6756 if (MovDir[x][y] != old_move_dir)
6759 else if (element == EL_BALLOON)
6761 MovDir[x][y] = game.wind_direction;
6764 else if (element == EL_SPRING)
6766 if (MovDir[x][y] & MV_HORIZONTAL)
6768 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6769 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6771 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6772 ResetGfxAnimation(move_x, move_y);
6773 TEST_DrawLevelField(move_x, move_y);
6775 MovDir[x][y] = back_dir;
6777 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6778 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6779 MovDir[x][y] = MV_NONE;
6784 else if (element == EL_ROBOT ||
6785 element == EL_SATELLITE ||
6786 element == EL_PENGUIN ||
6787 element == EL_EMC_ANDROID)
6789 int attr_x = -1, attr_y = -1;
6800 for (i = 0; i < MAX_PLAYERS; i++)
6802 struct PlayerInfo *player = &stored_player[i];
6803 int jx = player->jx, jy = player->jy;
6805 if (!player->active)
6809 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6817 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6818 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6819 game.engine_version < VERSION_IDENT(3,1,0,0)))
6825 if (element == EL_PENGUIN)
6828 static int xy[4][2] =
6836 for (i = 0; i < NUM_DIRECTIONS; i++)
6838 int ex = x + xy[i][0];
6839 int ey = y + xy[i][1];
6841 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6842 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6843 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6844 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6853 MovDir[x][y] = MV_NONE;
6855 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6856 else if (attr_x > x)
6857 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6859 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6860 else if (attr_y > y)
6861 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6863 if (element == EL_ROBOT)
6867 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6868 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6869 Moving2Blocked(x, y, &newx, &newy);
6871 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6872 MovDelay[x][y] = 8 + 8 * !RND(3);
6874 MovDelay[x][y] = 16;
6876 else if (element == EL_PENGUIN)
6882 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6884 boolean first_horiz = RND(2);
6885 int new_move_dir = MovDir[x][y];
6888 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6889 Moving2Blocked(x, y, &newx, &newy);
6891 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6895 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6896 Moving2Blocked(x, y, &newx, &newy);
6898 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6901 MovDir[x][y] = old_move_dir;
6905 else if (element == EL_SATELLITE)
6911 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6913 boolean first_horiz = RND(2);
6914 int new_move_dir = MovDir[x][y];
6917 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6918 Moving2Blocked(x, y, &newx, &newy);
6920 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6924 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6925 Moving2Blocked(x, y, &newx, &newy);
6927 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6930 MovDir[x][y] = old_move_dir;
6934 else if (element == EL_EMC_ANDROID)
6936 static int check_pos[16] =
6938 -1, /* 0 => (invalid) */
6939 7, /* 1 => MV_LEFT */
6940 3, /* 2 => MV_RIGHT */
6941 -1, /* 3 => (invalid) */
6943 0, /* 5 => MV_LEFT | MV_UP */
6944 2, /* 6 => MV_RIGHT | MV_UP */
6945 -1, /* 7 => (invalid) */
6946 5, /* 8 => MV_DOWN */
6947 6, /* 9 => MV_LEFT | MV_DOWN */
6948 4, /* 10 => MV_RIGHT | MV_DOWN */
6949 -1, /* 11 => (invalid) */
6950 -1, /* 12 => (invalid) */
6951 -1, /* 13 => (invalid) */
6952 -1, /* 14 => (invalid) */
6953 -1, /* 15 => (invalid) */
6961 { -1, -1, MV_LEFT | MV_UP },
6963 { +1, -1, MV_RIGHT | MV_UP },
6964 { +1, 0, MV_RIGHT },
6965 { +1, +1, MV_RIGHT | MV_DOWN },
6967 { -1, +1, MV_LEFT | MV_DOWN },
6970 int start_pos, check_order;
6971 boolean can_clone = FALSE;
6974 /* check if there is any free field around current position */
6975 for (i = 0; i < 8; i++)
6977 int newx = x + check_xy[i].dx;
6978 int newy = y + check_xy[i].dy;
6980 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6988 if (can_clone) /* randomly find an element to clone */
6992 start_pos = check_pos[RND(8)];
6993 check_order = (RND(2) ? -1 : +1);
6995 for (i = 0; i < 8; i++)
6997 int pos_raw = start_pos + i * check_order;
6998 int pos = (pos_raw + 8) % 8;
6999 int newx = x + check_xy[pos].dx;
7000 int newy = y + check_xy[pos].dy;
7002 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7004 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7005 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7007 Store[x][y] = Feld[newx][newy];
7016 if (can_clone) /* randomly find a direction to move */
7020 start_pos = check_pos[RND(8)];
7021 check_order = (RND(2) ? -1 : +1);
7023 for (i = 0; i < 8; i++)
7025 int pos_raw = start_pos + i * check_order;
7026 int pos = (pos_raw + 8) % 8;
7027 int newx = x + check_xy[pos].dx;
7028 int newy = y + check_xy[pos].dy;
7029 int new_move_dir = check_xy[pos].dir;
7031 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7033 MovDir[x][y] = new_move_dir;
7034 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7043 if (can_clone) /* cloning and moving successful */
7046 /* cannot clone -- try to move towards player */
7048 start_pos = check_pos[MovDir[x][y] & 0x0f];
7049 check_order = (RND(2) ? -1 : +1);
7051 for (i = 0; i < 3; i++)
7053 /* first check start_pos, then previous/next or (next/previous) pos */
7054 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7055 int pos = (pos_raw + 8) % 8;
7056 int newx = x + check_xy[pos].dx;
7057 int newy = y + check_xy[pos].dy;
7058 int new_move_dir = check_xy[pos].dir;
7060 if (IS_PLAYER(newx, newy))
7063 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7065 MovDir[x][y] = new_move_dir;
7066 MovDelay[x][y] = level.android_move_time * 8 + 1;
7073 else if (move_pattern == MV_TURNING_LEFT ||
7074 move_pattern == MV_TURNING_RIGHT ||
7075 move_pattern == MV_TURNING_LEFT_RIGHT ||
7076 move_pattern == MV_TURNING_RIGHT_LEFT ||
7077 move_pattern == MV_TURNING_RANDOM ||
7078 move_pattern == MV_ALL_DIRECTIONS)
7080 boolean can_turn_left =
7081 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7082 boolean can_turn_right =
7083 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7085 if (element_info[element].move_stepsize == 0) /* "not moving" */
7088 if (move_pattern == MV_TURNING_LEFT)
7089 MovDir[x][y] = left_dir;
7090 else if (move_pattern == MV_TURNING_RIGHT)
7091 MovDir[x][y] = right_dir;
7092 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7093 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7094 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7095 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7096 else if (move_pattern == MV_TURNING_RANDOM)
7097 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7098 can_turn_right && !can_turn_left ? right_dir :
7099 RND(2) ? left_dir : right_dir);
7100 else if (can_turn_left && can_turn_right)
7101 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7102 else if (can_turn_left)
7103 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7104 else if (can_turn_right)
7105 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7107 MovDir[x][y] = back_dir;
7109 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7111 else if (move_pattern == MV_HORIZONTAL ||
7112 move_pattern == MV_VERTICAL)
7114 if (move_pattern & old_move_dir)
7115 MovDir[x][y] = back_dir;
7116 else if (move_pattern == MV_HORIZONTAL)
7117 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7118 else if (move_pattern == MV_VERTICAL)
7119 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7121 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7123 else if (move_pattern & MV_ANY_DIRECTION)
7125 MovDir[x][y] = move_pattern;
7126 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128 else if (move_pattern & MV_WIND_DIRECTION)
7130 MovDir[x][y] = game.wind_direction;
7131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7135 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7136 MovDir[x][y] = left_dir;
7137 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7138 MovDir[x][y] = right_dir;
7140 if (MovDir[x][y] != old_move_dir)
7141 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7145 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7146 MovDir[x][y] = right_dir;
7147 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7148 MovDir[x][y] = left_dir;
7150 if (MovDir[x][y] != old_move_dir)
7151 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153 else if (move_pattern == MV_TOWARDS_PLAYER ||
7154 move_pattern == MV_AWAY_FROM_PLAYER)
7156 int attr_x = -1, attr_y = -1;
7158 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7169 for (i = 0; i < MAX_PLAYERS; i++)
7171 struct PlayerInfo *player = &stored_player[i];
7172 int jx = player->jx, jy = player->jy;
7174 if (!player->active)
7178 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7186 MovDir[x][y] = MV_NONE;
7188 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7189 else if (attr_x > x)
7190 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7192 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7193 else if (attr_y > y)
7194 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7196 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7198 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7200 boolean first_horiz = RND(2);
7201 int new_move_dir = MovDir[x][y];
7203 if (element_info[element].move_stepsize == 0) /* "not moving" */
7205 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7206 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7213 Moving2Blocked(x, y, &newx, &newy);
7215 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7219 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7220 Moving2Blocked(x, y, &newx, &newy);
7222 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7225 MovDir[x][y] = old_move_dir;
7228 else if (move_pattern == MV_WHEN_PUSHED ||
7229 move_pattern == MV_WHEN_DROPPED)
7231 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7232 MovDir[x][y] = MV_NONE;
7236 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7238 static int test_xy[7][2] =
7248 static int test_dir[7] =
7258 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7259 int move_preference = -1000000; /* start with very low preference */
7260 int new_move_dir = MV_NONE;
7261 int start_test = RND(4);
7264 for (i = 0; i < NUM_DIRECTIONS; i++)
7266 int move_dir = test_dir[start_test + i];
7267 int move_dir_preference;
7269 xx = x + test_xy[start_test + i][0];
7270 yy = y + test_xy[start_test + i][1];
7272 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7273 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7275 new_move_dir = move_dir;
7280 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7283 move_dir_preference = -1 * RunnerVisit[xx][yy];
7284 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7285 move_dir_preference = PlayerVisit[xx][yy];
7287 if (move_dir_preference > move_preference)
7289 /* prefer field that has not been visited for the longest time */
7290 move_preference = move_dir_preference;
7291 new_move_dir = move_dir;
7293 else if (move_dir_preference == move_preference &&
7294 move_dir == old_move_dir)
7296 /* prefer last direction when all directions are preferred equally */
7297 move_preference = move_dir_preference;
7298 new_move_dir = move_dir;
7302 MovDir[x][y] = new_move_dir;
7303 if (old_move_dir != new_move_dir)
7304 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7308 static void TurnRound(int x, int y)
7310 int direction = MovDir[x][y];
7314 GfxDir[x][y] = MovDir[x][y];
7316 if (direction != MovDir[x][y])
7320 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7322 ResetGfxFrame(x, y);
7325 static boolean JustBeingPushed(int x, int y)
7329 for (i = 0; i < MAX_PLAYERS; i++)
7331 struct PlayerInfo *player = &stored_player[i];
7333 if (player->active && player->is_pushing && player->MovPos)
7335 int next_jx = player->jx + (player->jx - player->last_jx);
7336 int next_jy = player->jy + (player->jy - player->last_jy);
7338 if (x == next_jx && y == next_jy)
7346 void StartMoving(int x, int y)
7348 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7349 int element = Feld[x][y];
7354 if (MovDelay[x][y] == 0)
7355 GfxAction[x][y] = ACTION_DEFAULT;
7357 if (CAN_FALL(element) && y < lev_fieldy - 1)
7359 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7360 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7361 if (JustBeingPushed(x, y))
7364 if (element == EL_QUICKSAND_FULL)
7366 if (IS_FREE(x, y + 1))
7368 InitMovingField(x, y, MV_DOWN);
7369 started_moving = TRUE;
7371 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7372 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7373 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7374 Store[x][y] = EL_ROCK;
7376 Store[x][y] = EL_ROCK;
7379 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7381 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7383 if (!MovDelay[x][y])
7385 MovDelay[x][y] = TILEY + 1;
7387 ResetGfxAnimation(x, y);
7388 ResetGfxAnimation(x, y + 1);
7393 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7394 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7401 Feld[x][y] = EL_QUICKSAND_EMPTY;
7402 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7403 Store[x][y + 1] = Store[x][y];
7406 PlayLevelSoundAction(x, y, ACTION_FILLING);
7408 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7410 if (!MovDelay[x][y])
7412 MovDelay[x][y] = TILEY + 1;
7414 ResetGfxAnimation(x, y);
7415 ResetGfxAnimation(x, y + 1);
7420 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7421 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7428 Feld[x][y] = EL_QUICKSAND_EMPTY;
7429 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7430 Store[x][y + 1] = Store[x][y];
7433 PlayLevelSoundAction(x, y, ACTION_FILLING);
7436 else if (element == EL_QUICKSAND_FAST_FULL)
7438 if (IS_FREE(x, y + 1))
7440 InitMovingField(x, y, MV_DOWN);
7441 started_moving = TRUE;
7443 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7444 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7445 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7446 Store[x][y] = EL_ROCK;
7448 Store[x][y] = EL_ROCK;
7451 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7453 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7455 if (!MovDelay[x][y])
7457 MovDelay[x][y] = TILEY + 1;
7459 ResetGfxAnimation(x, y);
7460 ResetGfxAnimation(x, y + 1);
7465 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7466 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7473 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7474 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7475 Store[x][y + 1] = Store[x][y];
7478 PlayLevelSoundAction(x, y, ACTION_FILLING);
7480 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7482 if (!MovDelay[x][y])
7484 MovDelay[x][y] = TILEY + 1;
7486 ResetGfxAnimation(x, y);
7487 ResetGfxAnimation(x, y + 1);
7492 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7493 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7500 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7501 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7502 Store[x][y + 1] = Store[x][y];
7505 PlayLevelSoundAction(x, y, ACTION_FILLING);
7508 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7509 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7511 InitMovingField(x, y, MV_DOWN);
7512 started_moving = TRUE;
7514 Feld[x][y] = EL_QUICKSAND_FILLING;
7515 Store[x][y] = element;
7517 PlayLevelSoundAction(x, y, ACTION_FILLING);
7519 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7520 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7522 InitMovingField(x, y, MV_DOWN);
7523 started_moving = TRUE;
7525 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7526 Store[x][y] = element;
7528 PlayLevelSoundAction(x, y, ACTION_FILLING);
7530 else if (element == EL_MAGIC_WALL_FULL)
7532 if (IS_FREE(x, y + 1))
7534 InitMovingField(x, y, MV_DOWN);
7535 started_moving = TRUE;
7537 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7538 Store[x][y] = EL_CHANGED(Store[x][y]);
7540 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7542 if (!MovDelay[x][y])
7543 MovDelay[x][y] = TILEY / 4 + 1;
7552 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7553 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7554 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7558 else if (element == EL_BD_MAGIC_WALL_FULL)
7560 if (IS_FREE(x, y + 1))
7562 InitMovingField(x, y, MV_DOWN);
7563 started_moving = TRUE;
7565 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7566 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7568 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7570 if (!MovDelay[x][y])
7571 MovDelay[x][y] = TILEY / 4 + 1;
7580 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7581 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7582 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7586 else if (element == EL_DC_MAGIC_WALL_FULL)
7588 if (IS_FREE(x, y + 1))
7590 InitMovingField(x, y, MV_DOWN);
7591 started_moving = TRUE;
7593 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7594 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7596 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7598 if (!MovDelay[x][y])
7599 MovDelay[x][y] = TILEY / 4 + 1;
7608 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7609 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7610 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7614 else if ((CAN_PASS_MAGIC_WALL(element) &&
7615 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7616 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7617 (CAN_PASS_DC_MAGIC_WALL(element) &&
7618 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7621 InitMovingField(x, y, MV_DOWN);
7622 started_moving = TRUE;
7625 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7626 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7627 EL_DC_MAGIC_WALL_FILLING);
7628 Store[x][y] = element;
7630 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7632 SplashAcid(x, y + 1);
7634 InitMovingField(x, y, MV_DOWN);
7635 started_moving = TRUE;
7637 Store[x][y] = EL_ACID;
7640 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7641 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7642 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7643 CAN_FALL(element) && WasJustFalling[x][y] &&
7644 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7646 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7647 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7648 (Feld[x][y + 1] == EL_BLOCKED)))
7650 /* this is needed for a special case not covered by calling "Impact()"
7651 from "ContinueMoving()": if an element moves to a tile directly below
7652 another element which was just falling on that tile (which was empty
7653 in the previous frame), the falling element above would just stop
7654 instead of smashing the element below (in previous version, the above
7655 element was just checked for "moving" instead of "falling", resulting
7656 in incorrect smashes caused by horizontal movement of the above
7657 element; also, the case of the player being the element to smash was
7658 simply not covered here... :-/ ) */
7660 CheckCollision[x][y] = 0;
7661 CheckImpact[x][y] = 0;
7665 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7667 if (MovDir[x][y] == MV_NONE)
7669 InitMovingField(x, y, MV_DOWN);
7670 started_moving = TRUE;
7673 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7675 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7676 MovDir[x][y] = MV_DOWN;
7678 InitMovingField(x, y, MV_DOWN);
7679 started_moving = TRUE;
7681 else if (element == EL_AMOEBA_DROP)
7683 Feld[x][y] = EL_AMOEBA_GROWING;
7684 Store[x][y] = EL_AMOEBA_WET;
7686 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7687 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7688 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7689 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7691 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7692 (IS_FREE(x - 1, y + 1) ||
7693 Feld[x - 1][y + 1] == EL_ACID));
7694 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7695 (IS_FREE(x + 1, y + 1) ||
7696 Feld[x + 1][y + 1] == EL_ACID));
7697 boolean can_fall_any = (can_fall_left || can_fall_right);
7698 boolean can_fall_both = (can_fall_left && can_fall_right);
7699 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7701 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7703 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7704 can_fall_right = FALSE;
7705 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7706 can_fall_left = FALSE;
7707 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7708 can_fall_right = FALSE;
7709 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7710 can_fall_left = FALSE;
7712 can_fall_any = (can_fall_left || can_fall_right);
7713 can_fall_both = FALSE;
7718 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7719 can_fall_right = FALSE; /* slip down on left side */
7721 can_fall_left = !(can_fall_right = RND(2));
7723 can_fall_both = FALSE;
7728 /* if not determined otherwise, prefer left side for slipping down */
7729 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7730 started_moving = TRUE;
7733 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7735 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7736 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7737 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7738 int belt_dir = game.belt_dir[belt_nr];
7740 if ((belt_dir == MV_LEFT && left_is_free) ||
7741 (belt_dir == MV_RIGHT && right_is_free))
7743 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7745 InitMovingField(x, y, belt_dir);
7746 started_moving = TRUE;
7748 Pushed[x][y] = TRUE;
7749 Pushed[nextx][y] = TRUE;
7751 GfxAction[x][y] = ACTION_DEFAULT;
7755 MovDir[x][y] = 0; /* if element was moving, stop it */
7760 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7761 if (CAN_MOVE(element) && !started_moving)
7763 int move_pattern = element_info[element].move_pattern;
7766 Moving2Blocked(x, y, &newx, &newy);
7768 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7771 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7772 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7774 WasJustMoving[x][y] = 0;
7775 CheckCollision[x][y] = 0;
7777 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7779 if (Feld[x][y] != element) /* element has changed */
7783 if (!MovDelay[x][y]) /* start new movement phase */
7785 /* all objects that can change their move direction after each step
7786 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7788 if (element != EL_YAMYAM &&
7789 element != EL_DARK_YAMYAM &&
7790 element != EL_PACMAN &&
7791 !(move_pattern & MV_ANY_DIRECTION) &&
7792 move_pattern != MV_TURNING_LEFT &&
7793 move_pattern != MV_TURNING_RIGHT &&
7794 move_pattern != MV_TURNING_LEFT_RIGHT &&
7795 move_pattern != MV_TURNING_RIGHT_LEFT &&
7796 move_pattern != MV_TURNING_RANDOM)
7800 if (MovDelay[x][y] && (element == EL_BUG ||
7801 element == EL_SPACESHIP ||
7802 element == EL_SP_SNIKSNAK ||
7803 element == EL_SP_ELECTRON ||
7804 element == EL_MOLE))
7805 TEST_DrawLevelField(x, y);
7809 if (MovDelay[x][y]) /* wait some time before next movement */
7813 if (element == EL_ROBOT ||
7814 element == EL_YAMYAM ||
7815 element == EL_DARK_YAMYAM)
7817 DrawLevelElementAnimationIfNeeded(x, y, element);
7818 PlayLevelSoundAction(x, y, ACTION_WAITING);
7820 else if (element == EL_SP_ELECTRON)
7821 DrawLevelElementAnimationIfNeeded(x, y, element);
7822 else if (element == EL_DRAGON)
7825 int dir = MovDir[x][y];
7826 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7827 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7828 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7829 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7830 dir == MV_UP ? IMG_FLAMES_1_UP :
7831 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7832 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7834 GfxAction[x][y] = ACTION_ATTACKING;
7836 if (IS_PLAYER(x, y))
7837 DrawPlayerField(x, y);
7839 TEST_DrawLevelField(x, y);
7841 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7843 for (i = 1; i <= 3; i++)
7845 int xx = x + i * dx;
7846 int yy = y + i * dy;
7847 int sx = SCREENX(xx);
7848 int sy = SCREENY(yy);
7849 int flame_graphic = graphic + (i - 1);
7851 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7856 int flamed = MovingOrBlocked2Element(xx, yy);
7858 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7861 RemoveMovingField(xx, yy);
7863 ChangeDelay[xx][yy] = 0;
7865 Feld[xx][yy] = EL_FLAMES;
7867 if (IN_SCR_FIELD(sx, sy))
7869 TEST_DrawLevelFieldCrumbled(xx, yy);
7870 DrawGraphic(sx, sy, flame_graphic, frame);
7875 if (Feld[xx][yy] == EL_FLAMES)
7876 Feld[xx][yy] = EL_EMPTY;
7877 TEST_DrawLevelField(xx, yy);
7882 if (MovDelay[x][y]) /* element still has to wait some time */
7884 PlayLevelSoundAction(x, y, ACTION_WAITING);
7890 /* now make next step */
7892 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7894 if (DONT_COLLIDE_WITH(element) &&
7895 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7896 !PLAYER_ENEMY_PROTECTED(newx, newy))
7898 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7903 else if (CAN_MOVE_INTO_ACID(element) &&
7904 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7905 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7906 (MovDir[x][y] == MV_DOWN ||
7907 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7909 SplashAcid(newx, newy);
7910 Store[x][y] = EL_ACID;
7912 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7914 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7915 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7916 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7917 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7920 TEST_DrawLevelField(x, y);
7922 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7923 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7924 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7926 local_player->friends_still_needed--;
7927 if (!local_player->friends_still_needed &&
7928 !local_player->GameOver && AllPlayersGone)
7929 PlayerWins(local_player);
7933 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7935 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7936 TEST_DrawLevelField(newx, newy);
7938 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7940 else if (!IS_FREE(newx, newy))
7942 GfxAction[x][y] = ACTION_WAITING;
7944 if (IS_PLAYER(x, y))
7945 DrawPlayerField(x, y);
7947 TEST_DrawLevelField(x, y);
7952 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7954 if (IS_FOOD_PIG(Feld[newx][newy]))
7956 if (IS_MOVING(newx, newy))
7957 RemoveMovingField(newx, newy);
7960 Feld[newx][newy] = EL_EMPTY;
7961 TEST_DrawLevelField(newx, newy);
7964 PlayLevelSound(x, y, SND_PIG_DIGGING);
7966 else if (!IS_FREE(newx, newy))
7968 if (IS_PLAYER(x, y))
7969 DrawPlayerField(x, y);
7971 TEST_DrawLevelField(x, y);
7976 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7978 if (Store[x][y] != EL_EMPTY)
7980 boolean can_clone = FALSE;
7983 /* check if element to clone is still there */
7984 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7986 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7994 /* cannot clone or target field not free anymore -- do not clone */
7995 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7996 Store[x][y] = EL_EMPTY;
7999 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8001 if (IS_MV_DIAGONAL(MovDir[x][y]))
8003 int diagonal_move_dir = MovDir[x][y];
8004 int stored = Store[x][y];
8005 int change_delay = 8;
8008 /* android is moving diagonally */
8010 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8012 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8013 GfxElement[x][y] = EL_EMC_ANDROID;
8014 GfxAction[x][y] = ACTION_SHRINKING;
8015 GfxDir[x][y] = diagonal_move_dir;
8016 ChangeDelay[x][y] = change_delay;
8018 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8021 DrawLevelGraphicAnimation(x, y, graphic);
8022 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8024 if (Feld[newx][newy] == EL_ACID)
8026 SplashAcid(newx, newy);
8031 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8033 Store[newx][newy] = EL_EMC_ANDROID;
8034 GfxElement[newx][newy] = EL_EMC_ANDROID;
8035 GfxAction[newx][newy] = ACTION_GROWING;
8036 GfxDir[newx][newy] = diagonal_move_dir;
8037 ChangeDelay[newx][newy] = change_delay;
8039 graphic = el_act_dir2img(GfxElement[newx][newy],
8040 GfxAction[newx][newy], GfxDir[newx][newy]);
8042 DrawLevelGraphicAnimation(newx, newy, graphic);
8043 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8049 Feld[newx][newy] = EL_EMPTY;
8050 TEST_DrawLevelField(newx, newy);
8052 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8055 else if (!IS_FREE(newx, newy))
8060 else if (IS_CUSTOM_ELEMENT(element) &&
8061 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8063 if (!DigFieldByCE(newx, newy, element))
8066 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8068 RunnerVisit[x][y] = FrameCounter;
8069 PlayerVisit[x][y] /= 8; /* expire player visit path */
8072 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8074 if (!IS_FREE(newx, newy))
8076 if (IS_PLAYER(x, y))
8077 DrawPlayerField(x, y);
8079 TEST_DrawLevelField(x, y);
8085 boolean wanna_flame = !RND(10);
8086 int dx = newx - x, dy = newy - y;
8087 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8088 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8089 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8090 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8091 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8092 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8095 IS_CLASSIC_ENEMY(element1) ||
8096 IS_CLASSIC_ENEMY(element2)) &&
8097 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8098 element1 != EL_FLAMES && element2 != EL_FLAMES)
8100 ResetGfxAnimation(x, y);
8101 GfxAction[x][y] = ACTION_ATTACKING;
8103 if (IS_PLAYER(x, y))
8104 DrawPlayerField(x, y);
8106 TEST_DrawLevelField(x, y);
8108 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8110 MovDelay[x][y] = 50;
8112 Feld[newx][newy] = EL_FLAMES;
8113 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8114 Feld[newx1][newy1] = EL_FLAMES;
8115 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8116 Feld[newx2][newy2] = EL_FLAMES;
8122 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8123 Feld[newx][newy] == EL_DIAMOND)
8125 if (IS_MOVING(newx, newy))
8126 RemoveMovingField(newx, newy);
8129 Feld[newx][newy] = EL_EMPTY;
8130 TEST_DrawLevelField(newx, newy);
8133 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8135 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8136 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8138 if (AmoebaNr[newx][newy])
8140 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8141 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8142 Feld[newx][newy] == EL_BD_AMOEBA)
8143 AmoebaCnt[AmoebaNr[newx][newy]]--;
8146 if (IS_MOVING(newx, newy))
8148 RemoveMovingField(newx, newy);
8152 Feld[newx][newy] = EL_EMPTY;
8153 TEST_DrawLevelField(newx, newy);
8156 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8158 else if ((element == EL_PACMAN || element == EL_MOLE)
8159 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8161 if (AmoebaNr[newx][newy])
8163 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8164 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8165 Feld[newx][newy] == EL_BD_AMOEBA)
8166 AmoebaCnt[AmoebaNr[newx][newy]]--;
8169 if (element == EL_MOLE)
8171 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8172 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8174 ResetGfxAnimation(x, y);
8175 GfxAction[x][y] = ACTION_DIGGING;
8176 TEST_DrawLevelField(x, y);
8178 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8180 return; /* wait for shrinking amoeba */
8182 else /* element == EL_PACMAN */
8184 Feld[newx][newy] = EL_EMPTY;
8185 TEST_DrawLevelField(newx, newy);
8186 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8189 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8190 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8191 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8193 /* wait for shrinking amoeba to completely disappear */
8196 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8198 /* object was running against a wall */
8202 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8203 DrawLevelElementAnimation(x, y, element);
8205 if (DONT_TOUCH(element))
8206 TestIfBadThingTouchesPlayer(x, y);
8211 InitMovingField(x, y, MovDir[x][y]);
8213 PlayLevelSoundAction(x, y, ACTION_MOVING);
8217 ContinueMoving(x, y);
8220 void ContinueMoving(int x, int y)
8222 int element = Feld[x][y];
8223 struct ElementInfo *ei = &element_info[element];
8224 int direction = MovDir[x][y];
8225 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8226 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8227 int newx = x + dx, newy = y + dy;
8228 int stored = Store[x][y];
8229 int stored_new = Store[newx][newy];
8230 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8231 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8232 boolean last_line = (newy == lev_fieldy - 1);
8234 MovPos[x][y] += getElementMoveStepsize(x, y);
8236 if (pushed_by_player) /* special case: moving object pushed by player */
8237 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8239 if (ABS(MovPos[x][y]) < TILEX)
8241 TEST_DrawLevelField(x, y);
8243 return; /* element is still moving */
8246 /* element reached destination field */
8248 Feld[x][y] = EL_EMPTY;
8249 Feld[newx][newy] = element;
8250 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8252 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8254 element = Feld[newx][newy] = EL_ACID;
8256 else if (element == EL_MOLE)
8258 Feld[x][y] = EL_SAND;
8260 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8262 else if (element == EL_QUICKSAND_FILLING)
8264 element = Feld[newx][newy] = get_next_element(element);
8265 Store[newx][newy] = Store[x][y];
8267 else if (element == EL_QUICKSAND_EMPTYING)
8269 Feld[x][y] = get_next_element(element);
8270 element = Feld[newx][newy] = Store[x][y];
8272 else if (element == EL_QUICKSAND_FAST_FILLING)
8274 element = Feld[newx][newy] = get_next_element(element);
8275 Store[newx][newy] = Store[x][y];
8277 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8279 Feld[x][y] = get_next_element(element);
8280 element = Feld[newx][newy] = Store[x][y];
8282 else if (element == EL_MAGIC_WALL_FILLING)
8284 element = Feld[newx][newy] = get_next_element(element);
8285 if (!game.magic_wall_active)
8286 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8287 Store[newx][newy] = Store[x][y];
8289 else if (element == EL_MAGIC_WALL_EMPTYING)
8291 Feld[x][y] = get_next_element(element);
8292 if (!game.magic_wall_active)
8293 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8294 element = Feld[newx][newy] = Store[x][y];
8296 InitField(newx, newy, FALSE);
8298 else if (element == EL_BD_MAGIC_WALL_FILLING)
8300 element = Feld[newx][newy] = get_next_element(element);
8301 if (!game.magic_wall_active)
8302 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8303 Store[newx][newy] = Store[x][y];
8305 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8307 Feld[x][y] = get_next_element(element);
8308 if (!game.magic_wall_active)
8309 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8310 element = Feld[newx][newy] = Store[x][y];
8312 InitField(newx, newy, FALSE);
8314 else if (element == EL_DC_MAGIC_WALL_FILLING)
8316 element = Feld[newx][newy] = get_next_element(element);
8317 if (!game.magic_wall_active)
8318 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8319 Store[newx][newy] = Store[x][y];
8321 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8323 Feld[x][y] = get_next_element(element);
8324 if (!game.magic_wall_active)
8325 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8326 element = Feld[newx][newy] = Store[x][y];
8328 InitField(newx, newy, FALSE);
8330 else if (element == EL_AMOEBA_DROPPING)
8332 Feld[x][y] = get_next_element(element);
8333 element = Feld[newx][newy] = Store[x][y];
8335 else if (element == EL_SOKOBAN_OBJECT)
8338 Feld[x][y] = Back[x][y];
8340 if (Back[newx][newy])
8341 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8343 Back[x][y] = Back[newx][newy] = 0;
8346 Store[x][y] = EL_EMPTY;
8351 MovDelay[newx][newy] = 0;
8353 if (CAN_CHANGE_OR_HAS_ACTION(element))
8355 /* copy element change control values to new field */
8356 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8357 ChangePage[newx][newy] = ChangePage[x][y];
8358 ChangeCount[newx][newy] = ChangeCount[x][y];
8359 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8362 CustomValue[newx][newy] = CustomValue[x][y];
8364 ChangeDelay[x][y] = 0;
8365 ChangePage[x][y] = -1;
8366 ChangeCount[x][y] = 0;
8367 ChangeEvent[x][y] = -1;
8369 CustomValue[x][y] = 0;
8371 /* copy animation control values to new field */
8372 GfxFrame[newx][newy] = GfxFrame[x][y];
8373 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8374 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8375 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8377 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8379 /* some elements can leave other elements behind after moving */
8380 if (ei->move_leave_element != EL_EMPTY &&
8381 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8382 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8384 int move_leave_element = ei->move_leave_element;
8386 /* this makes it possible to leave the removed element again */
8387 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8388 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8390 Feld[x][y] = move_leave_element;
8392 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8393 MovDir[x][y] = direction;
8395 InitField(x, y, FALSE);
8397 if (GFX_CRUMBLED(Feld[x][y]))
8398 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8400 if (ELEM_IS_PLAYER(move_leave_element))
8401 RelocatePlayer(x, y, move_leave_element);
8404 /* do this after checking for left-behind element */
8405 ResetGfxAnimation(x, y); /* reset animation values for old field */
8407 if (!CAN_MOVE(element) ||
8408 (CAN_FALL(element) && direction == MV_DOWN &&
8409 (element == EL_SPRING ||
8410 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8411 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8412 GfxDir[x][y] = MovDir[newx][newy] = 0;
8414 TEST_DrawLevelField(x, y);
8415 TEST_DrawLevelField(newx, newy);
8417 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8419 /* prevent pushed element from moving on in pushed direction */
8420 if (pushed_by_player && CAN_MOVE(element) &&
8421 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8422 !(element_info[element].move_pattern & direction))
8423 TurnRound(newx, newy);
8425 /* prevent elements on conveyor belt from moving on in last direction */
8426 if (pushed_by_conveyor && CAN_FALL(element) &&
8427 direction & MV_HORIZONTAL)
8428 MovDir[newx][newy] = 0;
8430 if (!pushed_by_player)
8432 int nextx = newx + dx, nexty = newy + dy;
8433 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8435 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8437 if (CAN_FALL(element) && direction == MV_DOWN)
8438 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8440 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8441 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8443 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8444 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8447 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8449 TestIfBadThingTouchesPlayer(newx, newy);
8450 TestIfBadThingTouchesFriend(newx, newy);
8452 if (!IS_CUSTOM_ELEMENT(element))
8453 TestIfBadThingTouchesOtherBadThing(newx, newy);
8455 else if (element == EL_PENGUIN)
8456 TestIfFriendTouchesBadThing(newx, newy);
8458 if (DONT_GET_HIT_BY(element))
8460 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8463 /* give the player one last chance (one more frame) to move away */
8464 if (CAN_FALL(element) && direction == MV_DOWN &&
8465 (last_line || (!IS_FREE(x, newy + 1) &&
8466 (!IS_PLAYER(x, newy + 1) ||
8467 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8470 if (pushed_by_player && !game.use_change_when_pushing_bug)
8472 int push_side = MV_DIR_OPPOSITE(direction);
8473 struct PlayerInfo *player = PLAYERINFO(x, y);
8475 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8476 player->index_bit, push_side);
8477 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8478 player->index_bit, push_side);
8481 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8482 MovDelay[newx][newy] = 1;
8484 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8486 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8487 TestIfElementHitsCustomElement(newx, newy, direction);
8488 TestIfPlayerTouchesCustomElement(newx, newy);
8489 TestIfElementTouchesCustomElement(newx, newy);
8491 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8492 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8493 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8494 MV_DIR_OPPOSITE(direction));
8497 int AmoebeNachbarNr(int ax, int ay)
8500 int element = Feld[ax][ay];
8502 static int xy[4][2] =
8510 for (i = 0; i < NUM_DIRECTIONS; i++)
8512 int x = ax + xy[i][0];
8513 int y = ay + xy[i][1];
8515 if (!IN_LEV_FIELD(x, y))
8518 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8519 group_nr = AmoebaNr[x][y];
8525 void AmoebenVereinigen(int ax, int ay)
8527 int i, x, y, xx, yy;
8528 int new_group_nr = AmoebaNr[ax][ay];
8529 static int xy[4][2] =
8537 if (new_group_nr == 0)
8540 for (i = 0; i < NUM_DIRECTIONS; i++)
8545 if (!IN_LEV_FIELD(x, y))
8548 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8549 Feld[x][y] == EL_BD_AMOEBA ||
8550 Feld[x][y] == EL_AMOEBA_DEAD) &&
8551 AmoebaNr[x][y] != new_group_nr)
8553 int old_group_nr = AmoebaNr[x][y];
8555 if (old_group_nr == 0)
8558 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8559 AmoebaCnt[old_group_nr] = 0;
8560 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8561 AmoebaCnt2[old_group_nr] = 0;
8563 SCAN_PLAYFIELD(xx, yy)
8565 if (AmoebaNr[xx][yy] == old_group_nr)
8566 AmoebaNr[xx][yy] = new_group_nr;
8572 void AmoebeUmwandeln(int ax, int ay)
8576 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8578 int group_nr = AmoebaNr[ax][ay];
8583 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8584 printf("AmoebeUmwandeln(): This should never happen!\n");
8589 SCAN_PLAYFIELD(x, y)
8591 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8594 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8598 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8599 SND_AMOEBA_TURNING_TO_GEM :
8600 SND_AMOEBA_TURNING_TO_ROCK));
8605 static int xy[4][2] =
8613 for (i = 0; i < NUM_DIRECTIONS; i++)
8618 if (!IN_LEV_FIELD(x, y))
8621 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8623 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8624 SND_AMOEBA_TURNING_TO_GEM :
8625 SND_AMOEBA_TURNING_TO_ROCK));
8632 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8635 int group_nr = AmoebaNr[ax][ay];
8636 boolean done = FALSE;
8641 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8642 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8647 SCAN_PLAYFIELD(x, y)
8649 if (AmoebaNr[x][y] == group_nr &&
8650 (Feld[x][y] == EL_AMOEBA_DEAD ||
8651 Feld[x][y] == EL_BD_AMOEBA ||
8652 Feld[x][y] == EL_AMOEBA_GROWING))
8655 Feld[x][y] = new_element;
8656 InitField(x, y, FALSE);
8657 TEST_DrawLevelField(x, y);
8663 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8664 SND_BD_AMOEBA_TURNING_TO_ROCK :
8665 SND_BD_AMOEBA_TURNING_TO_GEM));
8668 void AmoebeWaechst(int x, int y)
8670 static unsigned int sound_delay = 0;
8671 static unsigned int sound_delay_value = 0;
8673 if (!MovDelay[x][y]) /* start new growing cycle */
8677 if (DelayReached(&sound_delay, sound_delay_value))
8679 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8680 sound_delay_value = 30;
8684 if (MovDelay[x][y]) /* wait some time before growing bigger */
8687 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8689 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8690 6 - MovDelay[x][y]);
8692 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8695 if (!MovDelay[x][y])
8697 Feld[x][y] = Store[x][y];
8699 TEST_DrawLevelField(x, y);
8704 void AmoebaDisappearing(int x, int y)
8706 static unsigned int sound_delay = 0;
8707 static unsigned int sound_delay_value = 0;
8709 if (!MovDelay[x][y]) /* start new shrinking cycle */
8713 if (DelayReached(&sound_delay, sound_delay_value))
8714 sound_delay_value = 30;
8717 if (MovDelay[x][y]) /* wait some time before shrinking */
8720 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8722 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8723 6 - MovDelay[x][y]);
8725 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8728 if (!MovDelay[x][y])
8730 Feld[x][y] = EL_EMPTY;
8731 TEST_DrawLevelField(x, y);
8733 /* don't let mole enter this field in this cycle;
8734 (give priority to objects falling to this field from above) */
8740 void AmoebeAbleger(int ax, int ay)
8743 int element = Feld[ax][ay];
8744 int graphic = el2img(element);
8745 int newax = ax, neway = ay;
8746 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8747 static int xy[4][2] =
8755 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8757 Feld[ax][ay] = EL_AMOEBA_DEAD;
8758 TEST_DrawLevelField(ax, ay);
8762 if (IS_ANIMATED(graphic))
8763 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8765 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8766 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8768 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8771 if (MovDelay[ax][ay])
8775 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8778 int x = ax + xy[start][0];
8779 int y = ay + xy[start][1];
8781 if (!IN_LEV_FIELD(x, y))
8784 if (IS_FREE(x, y) ||
8785 CAN_GROW_INTO(Feld[x][y]) ||
8786 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8787 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8793 if (newax == ax && neway == ay)
8796 else /* normal or "filled" (BD style) amoeba */
8799 boolean waiting_for_player = FALSE;
8801 for (i = 0; i < NUM_DIRECTIONS; i++)
8803 int j = (start + i) % 4;
8804 int x = ax + xy[j][0];
8805 int y = ay + xy[j][1];
8807 if (!IN_LEV_FIELD(x, y))
8810 if (IS_FREE(x, y) ||
8811 CAN_GROW_INTO(Feld[x][y]) ||
8812 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8813 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8819 else if (IS_PLAYER(x, y))
8820 waiting_for_player = TRUE;
8823 if (newax == ax && neway == ay) /* amoeba cannot grow */
8825 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8827 Feld[ax][ay] = EL_AMOEBA_DEAD;
8828 TEST_DrawLevelField(ax, ay);
8829 AmoebaCnt[AmoebaNr[ax][ay]]--;
8831 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8833 if (element == EL_AMOEBA_FULL)
8834 AmoebeUmwandeln(ax, ay);
8835 else if (element == EL_BD_AMOEBA)
8836 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8841 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8843 /* amoeba gets larger by growing in some direction */
8845 int new_group_nr = AmoebaNr[ax][ay];
8848 if (new_group_nr == 0)
8850 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8851 printf("AmoebeAbleger(): This should never happen!\n");
8856 AmoebaNr[newax][neway] = new_group_nr;
8857 AmoebaCnt[new_group_nr]++;
8858 AmoebaCnt2[new_group_nr]++;
8860 /* if amoeba touches other amoeba(s) after growing, unify them */
8861 AmoebenVereinigen(newax, neway);
8863 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8865 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8871 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8872 (neway == lev_fieldy - 1 && newax != ax))
8874 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8875 Store[newax][neway] = element;
8877 else if (neway == ay || element == EL_EMC_DRIPPER)
8879 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8881 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8885 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8886 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8887 Store[ax][ay] = EL_AMOEBA_DROP;
8888 ContinueMoving(ax, ay);
8892 TEST_DrawLevelField(newax, neway);
8895 void Life(int ax, int ay)
8899 int element = Feld[ax][ay];
8900 int graphic = el2img(element);
8901 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8903 boolean changed = FALSE;
8905 if (IS_ANIMATED(graphic))
8906 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8911 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8912 MovDelay[ax][ay] = life_time;
8914 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8917 if (MovDelay[ax][ay])
8921 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8923 int xx = ax+x1, yy = ay+y1;
8926 if (!IN_LEV_FIELD(xx, yy))
8929 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8931 int x = xx+x2, y = yy+y2;
8933 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8936 if (((Feld[x][y] == element ||
8937 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8939 (IS_FREE(x, y) && Stop[x][y]))
8943 if (xx == ax && yy == ay) /* field in the middle */
8945 if (nachbarn < life_parameter[0] ||
8946 nachbarn > life_parameter[1])
8948 Feld[xx][yy] = EL_EMPTY;
8950 TEST_DrawLevelField(xx, yy);
8951 Stop[xx][yy] = TRUE;
8955 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8956 { /* free border field */
8957 if (nachbarn >= life_parameter[2] &&
8958 nachbarn <= life_parameter[3])
8960 Feld[xx][yy] = element;
8961 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8963 TEST_DrawLevelField(xx, yy);
8964 Stop[xx][yy] = TRUE;
8971 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8972 SND_GAME_OF_LIFE_GROWING);
8975 static void InitRobotWheel(int x, int y)
8977 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8980 static void RunRobotWheel(int x, int y)
8982 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8985 static void StopRobotWheel(int x, int y)
8987 if (ZX == x && ZY == y)
8991 game.robot_wheel_active = FALSE;
8995 static void InitTimegateWheel(int x, int y)
8997 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9000 static void RunTimegateWheel(int x, int y)
9002 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9005 static void InitMagicBallDelay(int x, int y)
9007 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9010 static void ActivateMagicBall(int bx, int by)
9014 if (level.ball_random)
9016 int pos_border = RND(8); /* select one of the eight border elements */
9017 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9018 int xx = pos_content % 3;
9019 int yy = pos_content / 3;
9024 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9025 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9029 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9031 int xx = x - bx + 1;
9032 int yy = y - by + 1;
9034 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9035 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9039 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9042 void CheckExit(int x, int y)
9044 if (local_player->gems_still_needed > 0 ||
9045 local_player->sokobanfields_still_needed > 0 ||
9046 local_player->lights_still_needed > 0)
9048 int element = Feld[x][y];
9049 int graphic = el2img(element);
9051 if (IS_ANIMATED(graphic))
9052 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9057 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9060 Feld[x][y] = EL_EXIT_OPENING;
9062 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9065 void CheckExitEM(int x, int y)
9067 if (local_player->gems_still_needed > 0 ||
9068 local_player->sokobanfields_still_needed > 0 ||
9069 local_player->lights_still_needed > 0)
9071 int element = Feld[x][y];
9072 int graphic = el2img(element);
9074 if (IS_ANIMATED(graphic))
9075 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9080 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9083 Feld[x][y] = EL_EM_EXIT_OPENING;
9085 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9088 void CheckExitSteel(int x, int y)
9090 if (local_player->gems_still_needed > 0 ||
9091 local_player->sokobanfields_still_needed > 0 ||
9092 local_player->lights_still_needed > 0)
9094 int element = Feld[x][y];
9095 int graphic = el2img(element);
9097 if (IS_ANIMATED(graphic))
9098 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9103 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9106 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9108 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9111 void CheckExitSteelEM(int x, int y)
9113 if (local_player->gems_still_needed > 0 ||
9114 local_player->sokobanfields_still_needed > 0 ||
9115 local_player->lights_still_needed > 0)
9117 int element = Feld[x][y];
9118 int graphic = el2img(element);
9120 if (IS_ANIMATED(graphic))
9121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9126 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9129 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9131 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9134 void CheckExitSP(int x, int y)
9136 if (local_player->gems_still_needed > 0)
9138 int element = Feld[x][y];
9139 int graphic = el2img(element);
9141 if (IS_ANIMATED(graphic))
9142 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9147 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9150 Feld[x][y] = EL_SP_EXIT_OPENING;
9152 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9155 static void CloseAllOpenTimegates()
9159 SCAN_PLAYFIELD(x, y)
9161 int element = Feld[x][y];
9163 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9165 Feld[x][y] = EL_TIMEGATE_CLOSING;
9167 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9172 void DrawTwinkleOnField(int x, int y)
9174 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9177 if (Feld[x][y] == EL_BD_DIAMOND)
9180 if (MovDelay[x][y] == 0) /* next animation frame */
9181 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9183 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9187 DrawLevelElementAnimation(x, y, Feld[x][y]);
9189 if (MovDelay[x][y] != 0)
9191 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9192 10 - MovDelay[x][y]);
9194 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9199 void MauerWaechst(int x, int y)
9203 if (!MovDelay[x][y]) /* next animation frame */
9204 MovDelay[x][y] = 3 * delay;
9206 if (MovDelay[x][y]) /* wait some time before next frame */
9210 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9212 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9213 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9215 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9218 if (!MovDelay[x][y])
9220 if (MovDir[x][y] == MV_LEFT)
9222 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9223 TEST_DrawLevelField(x - 1, y);
9225 else if (MovDir[x][y] == MV_RIGHT)
9227 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9228 TEST_DrawLevelField(x + 1, y);
9230 else if (MovDir[x][y] == MV_UP)
9232 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9233 TEST_DrawLevelField(x, y - 1);
9237 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9238 TEST_DrawLevelField(x, y + 1);
9241 Feld[x][y] = Store[x][y];
9243 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9244 TEST_DrawLevelField(x, y);
9249 void MauerAbleger(int ax, int ay)
9251 int element = Feld[ax][ay];
9252 int graphic = el2img(element);
9253 boolean oben_frei = FALSE, unten_frei = FALSE;
9254 boolean links_frei = FALSE, rechts_frei = FALSE;
9255 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9256 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9257 boolean new_wall = FALSE;
9259 if (IS_ANIMATED(graphic))
9260 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9262 if (!MovDelay[ax][ay]) /* start building new wall */
9263 MovDelay[ax][ay] = 6;
9265 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9268 if (MovDelay[ax][ay])
9272 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9274 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9276 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9278 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9281 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9282 element == EL_EXPANDABLE_WALL_ANY)
9286 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9287 Store[ax][ay-1] = element;
9288 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9289 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9290 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9291 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9296 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9297 Store[ax][ay+1] = element;
9298 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9299 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9300 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9301 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9306 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9307 element == EL_EXPANDABLE_WALL_ANY ||
9308 element == EL_EXPANDABLE_WALL ||
9309 element == EL_BD_EXPANDABLE_WALL)
9313 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9314 Store[ax-1][ay] = element;
9315 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9316 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9317 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9318 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9324 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9325 Store[ax+1][ay] = element;
9326 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9327 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9328 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9329 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9334 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9335 TEST_DrawLevelField(ax, ay);
9337 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9339 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9340 unten_massiv = TRUE;
9341 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9342 links_massiv = TRUE;
9343 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9344 rechts_massiv = TRUE;
9346 if (((oben_massiv && unten_massiv) ||
9347 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9348 element == EL_EXPANDABLE_WALL) &&
9349 ((links_massiv && rechts_massiv) ||
9350 element == EL_EXPANDABLE_WALL_VERTICAL))
9351 Feld[ax][ay] = EL_WALL;
9354 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9357 void MauerAblegerStahl(int ax, int ay)
9359 int element = Feld[ax][ay];
9360 int graphic = el2img(element);
9361 boolean oben_frei = FALSE, unten_frei = FALSE;
9362 boolean links_frei = FALSE, rechts_frei = FALSE;
9363 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9364 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9365 boolean new_wall = FALSE;
9367 if (IS_ANIMATED(graphic))
9368 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9370 if (!MovDelay[ax][ay]) /* start building new wall */
9371 MovDelay[ax][ay] = 6;
9373 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9376 if (MovDelay[ax][ay])
9380 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9382 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9384 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9386 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9389 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9390 element == EL_EXPANDABLE_STEELWALL_ANY)
9394 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9395 Store[ax][ay-1] = element;
9396 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9397 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9398 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9399 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9404 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9405 Store[ax][ay+1] = element;
9406 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9407 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9408 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9409 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9414 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9415 element == EL_EXPANDABLE_STEELWALL_ANY)
9419 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9420 Store[ax-1][ay] = element;
9421 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9422 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9423 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9424 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9430 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9431 Store[ax+1][ay] = element;
9432 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9433 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9434 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9435 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9440 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9442 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9443 unten_massiv = TRUE;
9444 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9445 links_massiv = TRUE;
9446 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9447 rechts_massiv = TRUE;
9449 if (((oben_massiv && unten_massiv) ||
9450 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9451 ((links_massiv && rechts_massiv) ||
9452 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9453 Feld[ax][ay] = EL_STEELWALL;
9456 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9459 void CheckForDragon(int x, int y)
9462 boolean dragon_found = FALSE;
9463 static int xy[4][2] =
9471 for (i = 0; i < NUM_DIRECTIONS; i++)
9473 for (j = 0; j < 4; j++)
9475 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9477 if (IN_LEV_FIELD(xx, yy) &&
9478 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9480 if (Feld[xx][yy] == EL_DRAGON)
9481 dragon_found = TRUE;
9490 for (i = 0; i < NUM_DIRECTIONS; i++)
9492 for (j = 0; j < 3; j++)
9494 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9496 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9498 Feld[xx][yy] = EL_EMPTY;
9499 TEST_DrawLevelField(xx, yy);
9508 static void InitBuggyBase(int x, int y)
9510 int element = Feld[x][y];
9511 int activating_delay = FRAMES_PER_SECOND / 4;
9514 (element == EL_SP_BUGGY_BASE ?
9515 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9516 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9518 element == EL_SP_BUGGY_BASE_ACTIVE ?
9519 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9522 static void WarnBuggyBase(int x, int y)
9525 static int xy[4][2] =
9533 for (i = 0; i < NUM_DIRECTIONS; i++)
9535 int xx = x + xy[i][0];
9536 int yy = y + xy[i][1];
9538 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9540 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9547 static void InitTrap(int x, int y)
9549 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9552 static void ActivateTrap(int x, int y)
9554 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9557 static void ChangeActiveTrap(int x, int y)
9559 int graphic = IMG_TRAP_ACTIVE;
9561 /* if new animation frame was drawn, correct crumbled sand border */
9562 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9563 TEST_DrawLevelFieldCrumbled(x, y);
9566 static int getSpecialActionElement(int element, int number, int base_element)
9568 return (element != EL_EMPTY ? element :
9569 number != -1 ? base_element + number - 1 :
9573 static int getModifiedActionNumber(int value_old, int operator, int operand,
9574 int value_min, int value_max)
9576 int value_new = (operator == CA_MODE_SET ? operand :
9577 operator == CA_MODE_ADD ? value_old + operand :
9578 operator == CA_MODE_SUBTRACT ? value_old - operand :
9579 operator == CA_MODE_MULTIPLY ? value_old * operand :
9580 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9581 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9584 return (value_new < value_min ? value_min :
9585 value_new > value_max ? value_max :
9589 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9591 struct ElementInfo *ei = &element_info[element];
9592 struct ElementChangeInfo *change = &ei->change_page[page];
9593 int target_element = change->target_element;
9594 int action_type = change->action_type;
9595 int action_mode = change->action_mode;
9596 int action_arg = change->action_arg;
9597 int action_element = change->action_element;
9600 if (!change->has_action)
9603 /* ---------- determine action paramater values -------------------------- */
9605 int level_time_value =
9606 (level.time > 0 ? TimeLeft :
9609 int action_arg_element_raw =
9610 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9611 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9612 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9613 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9614 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9615 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9616 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9618 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9620 int action_arg_direction =
9621 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9622 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9623 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9624 change->actual_trigger_side :
9625 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9626 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9629 int action_arg_number_min =
9630 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9633 int action_arg_number_max =
9634 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9635 action_type == CA_SET_LEVEL_GEMS ? 999 :
9636 action_type == CA_SET_LEVEL_TIME ? 9999 :
9637 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9638 action_type == CA_SET_CE_VALUE ? 9999 :
9639 action_type == CA_SET_CE_SCORE ? 9999 :
9642 int action_arg_number_reset =
9643 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9644 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9645 action_type == CA_SET_LEVEL_TIME ? level.time :
9646 action_type == CA_SET_LEVEL_SCORE ? 0 :
9647 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9648 action_type == CA_SET_CE_SCORE ? 0 :
9651 int action_arg_number =
9652 (action_arg <= CA_ARG_MAX ? action_arg :
9653 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9654 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9655 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9656 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9657 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9658 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9659 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9660 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9661 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9662 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9663 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9664 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9665 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9666 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9667 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9668 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9669 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9670 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9671 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9672 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9673 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9676 int action_arg_number_old =
9677 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9678 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9679 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9680 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9681 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9684 int action_arg_number_new =
9685 getModifiedActionNumber(action_arg_number_old,
9686 action_mode, action_arg_number,
9687 action_arg_number_min, action_arg_number_max);
9689 int trigger_player_bits =
9690 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9691 change->actual_trigger_player_bits : change->trigger_player);
9693 int action_arg_player_bits =
9694 (action_arg >= CA_ARG_PLAYER_1 &&
9695 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9696 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9697 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9700 /* ---------- execute action -------------------------------------------- */
9702 switch (action_type)
9709 /* ---------- level actions ------------------------------------------- */
9711 case CA_RESTART_LEVEL:
9713 game.restart_level = TRUE;
9718 case CA_SHOW_ENVELOPE:
9720 int element = getSpecialActionElement(action_arg_element,
9721 action_arg_number, EL_ENVELOPE_1);
9723 if (IS_ENVELOPE(element))
9724 local_player->show_envelope = element;
9729 case CA_SET_LEVEL_TIME:
9731 if (level.time > 0) /* only modify limited time value */
9733 TimeLeft = action_arg_number_new;
9735 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9737 DisplayGameControlValues();
9739 if (!TimeLeft && setup.time_limit)
9740 for (i = 0; i < MAX_PLAYERS; i++)
9741 KillPlayer(&stored_player[i]);
9747 case CA_SET_LEVEL_SCORE:
9749 local_player->score = action_arg_number_new;
9751 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9753 DisplayGameControlValues();
9758 case CA_SET_LEVEL_GEMS:
9760 local_player->gems_still_needed = action_arg_number_new;
9762 game.snapshot.collected_item = TRUE;
9764 game_panel_controls[GAME_PANEL_GEMS].value =
9765 local_player->gems_still_needed;
9767 DisplayGameControlValues();
9772 case CA_SET_LEVEL_WIND:
9774 game.wind_direction = action_arg_direction;
9779 case CA_SET_LEVEL_RANDOM_SEED:
9781 /* ensure that setting a new random seed while playing is predictable */
9782 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9787 /* ---------- player actions ------------------------------------------ */
9789 case CA_MOVE_PLAYER:
9791 /* automatically move to the next field in specified direction */
9792 for (i = 0; i < MAX_PLAYERS; i++)
9793 if (trigger_player_bits & (1 << i))
9794 stored_player[i].programmed_action = action_arg_direction;
9799 case CA_EXIT_PLAYER:
9801 for (i = 0; i < MAX_PLAYERS; i++)
9802 if (action_arg_player_bits & (1 << i))
9803 PlayerWins(&stored_player[i]);
9808 case CA_KILL_PLAYER:
9810 for (i = 0; i < MAX_PLAYERS; i++)
9811 if (action_arg_player_bits & (1 << i))
9812 KillPlayer(&stored_player[i]);
9817 case CA_SET_PLAYER_KEYS:
9819 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9820 int element = getSpecialActionElement(action_arg_element,
9821 action_arg_number, EL_KEY_1);
9823 if (IS_KEY(element))
9825 for (i = 0; i < MAX_PLAYERS; i++)
9827 if (trigger_player_bits & (1 << i))
9829 stored_player[i].key[KEY_NR(element)] = key_state;
9831 DrawGameDoorValues();
9839 case CA_SET_PLAYER_SPEED:
9841 for (i = 0; i < MAX_PLAYERS; i++)
9843 if (trigger_player_bits & (1 << i))
9845 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9847 if (action_arg == CA_ARG_SPEED_FASTER &&
9848 stored_player[i].cannot_move)
9850 action_arg_number = STEPSIZE_VERY_SLOW;
9852 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9853 action_arg == CA_ARG_SPEED_FASTER)
9855 action_arg_number = 2;
9856 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9859 else if (action_arg == CA_ARG_NUMBER_RESET)
9861 action_arg_number = level.initial_player_stepsize[i];
9865 getModifiedActionNumber(move_stepsize,
9868 action_arg_number_min,
9869 action_arg_number_max);
9871 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9878 case CA_SET_PLAYER_SHIELD:
9880 for (i = 0; i < MAX_PLAYERS; i++)
9882 if (trigger_player_bits & (1 << i))
9884 if (action_arg == CA_ARG_SHIELD_OFF)
9886 stored_player[i].shield_normal_time_left = 0;
9887 stored_player[i].shield_deadly_time_left = 0;
9889 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9891 stored_player[i].shield_normal_time_left = 999999;
9893 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9895 stored_player[i].shield_normal_time_left = 999999;
9896 stored_player[i].shield_deadly_time_left = 999999;
9904 case CA_SET_PLAYER_GRAVITY:
9906 for (i = 0; i < MAX_PLAYERS; i++)
9908 if (trigger_player_bits & (1 << i))
9910 stored_player[i].gravity =
9911 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9912 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9913 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9914 stored_player[i].gravity);
9921 case CA_SET_PLAYER_ARTWORK:
9923 for (i = 0; i < MAX_PLAYERS; i++)
9925 if (trigger_player_bits & (1 << i))
9927 int artwork_element = action_arg_element;
9929 if (action_arg == CA_ARG_ELEMENT_RESET)
9931 (level.use_artwork_element[i] ? level.artwork_element[i] :
9932 stored_player[i].element_nr);
9934 if (stored_player[i].artwork_element != artwork_element)
9935 stored_player[i].Frame = 0;
9937 stored_player[i].artwork_element = artwork_element;
9939 SetPlayerWaiting(&stored_player[i], FALSE);
9941 /* set number of special actions for bored and sleeping animation */
9942 stored_player[i].num_special_action_bored =
9943 get_num_special_action(artwork_element,
9944 ACTION_BORING_1, ACTION_BORING_LAST);
9945 stored_player[i].num_special_action_sleeping =
9946 get_num_special_action(artwork_element,
9947 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9954 case CA_SET_PLAYER_INVENTORY:
9956 for (i = 0; i < MAX_PLAYERS; i++)
9958 struct PlayerInfo *player = &stored_player[i];
9961 if (trigger_player_bits & (1 << i))
9963 int inventory_element = action_arg_element;
9965 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9966 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9967 action_arg == CA_ARG_ELEMENT_ACTION)
9969 int element = inventory_element;
9970 int collect_count = element_info[element].collect_count_initial;
9972 if (!IS_CUSTOM_ELEMENT(element))
9975 if (collect_count == 0)
9976 player->inventory_infinite_element = element;
9978 for (k = 0; k < collect_count; k++)
9979 if (player->inventory_size < MAX_INVENTORY_SIZE)
9980 player->inventory_element[player->inventory_size++] =
9983 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9984 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9985 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9987 if (player->inventory_infinite_element != EL_UNDEFINED &&
9988 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9989 action_arg_element_raw))
9990 player->inventory_infinite_element = EL_UNDEFINED;
9992 for (k = 0, j = 0; j < player->inventory_size; j++)
9994 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9995 action_arg_element_raw))
9996 player->inventory_element[k++] = player->inventory_element[j];
9999 player->inventory_size = k;
10001 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10003 if (player->inventory_size > 0)
10005 for (j = 0; j < player->inventory_size - 1; j++)
10006 player->inventory_element[j] = player->inventory_element[j + 1];
10008 player->inventory_size--;
10011 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10013 if (player->inventory_size > 0)
10014 player->inventory_size--;
10016 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10018 player->inventory_infinite_element = EL_UNDEFINED;
10019 player->inventory_size = 0;
10021 else if (action_arg == CA_ARG_INVENTORY_RESET)
10023 player->inventory_infinite_element = EL_UNDEFINED;
10024 player->inventory_size = 0;
10026 if (level.use_initial_inventory[i])
10028 for (j = 0; j < level.initial_inventory_size[i]; j++)
10030 int element = level.initial_inventory_content[i][j];
10031 int collect_count = element_info[element].collect_count_initial;
10033 if (!IS_CUSTOM_ELEMENT(element))
10036 if (collect_count == 0)
10037 player->inventory_infinite_element = element;
10039 for (k = 0; k < collect_count; k++)
10040 if (player->inventory_size < MAX_INVENTORY_SIZE)
10041 player->inventory_element[player->inventory_size++] =
10052 /* ---------- CE actions ---------------------------------------------- */
10054 case CA_SET_CE_VALUE:
10056 int last_ce_value = CustomValue[x][y];
10058 CustomValue[x][y] = action_arg_number_new;
10060 if (CustomValue[x][y] != last_ce_value)
10062 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10063 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10065 if (CustomValue[x][y] == 0)
10067 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10068 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10075 case CA_SET_CE_SCORE:
10077 int last_ce_score = ei->collect_score;
10079 ei->collect_score = action_arg_number_new;
10081 if (ei->collect_score != last_ce_score)
10083 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10084 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10086 if (ei->collect_score == 0)
10090 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10091 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10094 This is a very special case that seems to be a mixture between
10095 CheckElementChange() and CheckTriggeredElementChange(): while
10096 the first one only affects single elements that are triggered
10097 directly, the second one affects multiple elements in the playfield
10098 that are triggered indirectly by another element. This is a third
10099 case: Changing the CE score always affects multiple identical CEs,
10100 so every affected CE must be checked, not only the single CE for
10101 which the CE score was changed in the first place (as every instance
10102 of that CE shares the same CE score, and therefore also can change)!
10104 SCAN_PLAYFIELD(xx, yy)
10106 if (Feld[xx][yy] == element)
10107 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10108 CE_SCORE_GETS_ZERO);
10116 case CA_SET_CE_ARTWORK:
10118 int artwork_element = action_arg_element;
10119 boolean reset_frame = FALSE;
10122 if (action_arg == CA_ARG_ELEMENT_RESET)
10123 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10126 if (ei->gfx_element != artwork_element)
10127 reset_frame = TRUE;
10129 ei->gfx_element = artwork_element;
10131 SCAN_PLAYFIELD(xx, yy)
10133 if (Feld[xx][yy] == element)
10137 ResetGfxAnimation(xx, yy);
10138 ResetRandomAnimationValue(xx, yy);
10141 TEST_DrawLevelField(xx, yy);
10148 /* ---------- engine actions ------------------------------------------ */
10150 case CA_SET_ENGINE_SCAN_MODE:
10152 InitPlayfieldScanMode(action_arg);
10162 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10164 int old_element = Feld[x][y];
10165 int new_element = GetElementFromGroupElement(element);
10166 int previous_move_direction = MovDir[x][y];
10167 int last_ce_value = CustomValue[x][y];
10168 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10169 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10170 boolean add_player_onto_element = (new_element_is_player &&
10171 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10172 IS_WALKABLE(old_element));
10174 if (!add_player_onto_element)
10176 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10177 RemoveMovingField(x, y);
10181 Feld[x][y] = new_element;
10183 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10184 MovDir[x][y] = previous_move_direction;
10186 if (element_info[new_element].use_last_ce_value)
10187 CustomValue[x][y] = last_ce_value;
10189 InitField_WithBug1(x, y, FALSE);
10191 new_element = Feld[x][y]; /* element may have changed */
10193 ResetGfxAnimation(x, y);
10194 ResetRandomAnimationValue(x, y);
10196 TEST_DrawLevelField(x, y);
10198 if (GFX_CRUMBLED(new_element))
10199 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10202 /* check if element under the player changes from accessible to unaccessible
10203 (needed for special case of dropping element which then changes) */
10204 /* (must be checked after creating new element for walkable group elements) */
10205 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10206 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10213 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10214 if (new_element_is_player)
10215 RelocatePlayer(x, y, new_element);
10218 ChangeCount[x][y]++; /* count number of changes in the same frame */
10220 TestIfBadThingTouchesPlayer(x, y);
10221 TestIfPlayerTouchesCustomElement(x, y);
10222 TestIfElementTouchesCustomElement(x, y);
10225 static void CreateField(int x, int y, int element)
10227 CreateFieldExt(x, y, element, FALSE);
10230 static void CreateElementFromChange(int x, int y, int element)
10232 element = GET_VALID_RUNTIME_ELEMENT(element);
10234 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10236 int old_element = Feld[x][y];
10238 /* prevent changed element from moving in same engine frame
10239 unless both old and new element can either fall or move */
10240 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10241 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10245 CreateFieldExt(x, y, element, TRUE);
10248 static boolean ChangeElement(int x, int y, int element, int page)
10250 struct ElementInfo *ei = &element_info[element];
10251 struct ElementChangeInfo *change = &ei->change_page[page];
10252 int ce_value = CustomValue[x][y];
10253 int ce_score = ei->collect_score;
10254 int target_element;
10255 int old_element = Feld[x][y];
10257 /* always use default change event to prevent running into a loop */
10258 if (ChangeEvent[x][y] == -1)
10259 ChangeEvent[x][y] = CE_DELAY;
10261 if (ChangeEvent[x][y] == CE_DELAY)
10263 /* reset actual trigger element, trigger player and action element */
10264 change->actual_trigger_element = EL_EMPTY;
10265 change->actual_trigger_player = EL_EMPTY;
10266 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10267 change->actual_trigger_side = CH_SIDE_NONE;
10268 change->actual_trigger_ce_value = 0;
10269 change->actual_trigger_ce_score = 0;
10272 /* do not change elements more than a specified maximum number of changes */
10273 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10276 ChangeCount[x][y]++; /* count number of changes in the same frame */
10278 if (change->explode)
10285 if (change->use_target_content)
10287 boolean complete_replace = TRUE;
10288 boolean can_replace[3][3];
10291 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10294 boolean is_walkable;
10295 boolean is_diggable;
10296 boolean is_collectible;
10297 boolean is_removable;
10298 boolean is_destructible;
10299 int ex = x + xx - 1;
10300 int ey = y + yy - 1;
10301 int content_element = change->target_content.e[xx][yy];
10304 can_replace[xx][yy] = TRUE;
10306 if (ex == x && ey == y) /* do not check changing element itself */
10309 if (content_element == EL_EMPTY_SPACE)
10311 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10316 if (!IN_LEV_FIELD(ex, ey))
10318 can_replace[xx][yy] = FALSE;
10319 complete_replace = FALSE;
10326 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10327 e = MovingOrBlocked2Element(ex, ey);
10329 is_empty = (IS_FREE(ex, ey) ||
10330 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10332 is_walkable = (is_empty || IS_WALKABLE(e));
10333 is_diggable = (is_empty || IS_DIGGABLE(e));
10334 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10335 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10336 is_removable = (is_diggable || is_collectible);
10338 can_replace[xx][yy] =
10339 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10340 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10341 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10342 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10343 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10344 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10345 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10347 if (!can_replace[xx][yy])
10348 complete_replace = FALSE;
10351 if (!change->only_if_complete || complete_replace)
10353 boolean something_has_changed = FALSE;
10355 if (change->only_if_complete && change->use_random_replace &&
10356 RND(100) < change->random_percentage)
10359 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10361 int ex = x + xx - 1;
10362 int ey = y + yy - 1;
10363 int content_element;
10365 if (can_replace[xx][yy] && (!change->use_random_replace ||
10366 RND(100) < change->random_percentage))
10368 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10369 RemoveMovingField(ex, ey);
10371 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10373 content_element = change->target_content.e[xx][yy];
10374 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10375 ce_value, ce_score);
10377 CreateElementFromChange(ex, ey, target_element);
10379 something_has_changed = TRUE;
10381 /* for symmetry reasons, freeze newly created border elements */
10382 if (ex != x || ey != y)
10383 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10387 if (something_has_changed)
10389 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10390 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10396 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10397 ce_value, ce_score);
10399 if (element == EL_DIAGONAL_GROWING ||
10400 element == EL_DIAGONAL_SHRINKING)
10402 target_element = Store[x][y];
10404 Store[x][y] = EL_EMPTY;
10407 CreateElementFromChange(x, y, target_element);
10409 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10410 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10413 /* this uses direct change before indirect change */
10414 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10419 static void HandleElementChange(int x, int y, int page)
10421 int element = MovingOrBlocked2Element(x, y);
10422 struct ElementInfo *ei = &element_info[element];
10423 struct ElementChangeInfo *change = &ei->change_page[page];
10424 boolean handle_action_before_change = FALSE;
10427 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10428 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10431 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10432 x, y, element, element_info[element].token_name);
10433 printf("HandleElementChange(): This should never happen!\n");
10438 /* this can happen with classic bombs on walkable, changing elements */
10439 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10444 if (ChangeDelay[x][y] == 0) /* initialize element change */
10446 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10448 if (change->can_change)
10450 /* !!! not clear why graphic animation should be reset at all here !!! */
10451 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10452 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10455 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10457 When using an animation frame delay of 1 (this only happens with
10458 "sp_zonk.moving.left/right" in the classic graphics), the default
10459 (non-moving) animation shows wrong animation frames (while the
10460 moving animation, like "sp_zonk.moving.left/right", is correct,
10461 so this graphical bug never shows up with the classic graphics).
10462 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10463 be drawn instead of the correct frames 0,1,2,3. This is caused by
10464 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10465 an element change: First when the change delay ("ChangeDelay[][]")
10466 counter has reached zero after decrementing, then a second time in
10467 the next frame (after "GfxFrame[][]" was already incremented) when
10468 "ChangeDelay[][]" is reset to the initial delay value again.
10470 This causes frame 0 to be drawn twice, while the last frame won't
10471 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10473 As some animations may already be cleverly designed around this bug
10474 (at least the "Snake Bite" snake tail animation does this), it cannot
10475 simply be fixed here without breaking such existing animations.
10476 Unfortunately, it cannot easily be detected if a graphics set was
10477 designed "before" or "after" the bug was fixed. As a workaround,
10478 a new graphics set option "game.graphics_engine_version" was added
10479 to be able to specify the game's major release version for which the
10480 graphics set was designed, which can then be used to decide if the
10481 bugfix should be used (version 4 and above) or not (version 3 or
10482 below, or if no version was specified at all, as with old sets).
10484 (The wrong/fixed animation frames can be tested with the test level set
10485 "test_gfxframe" and level "000", which contains a specially prepared
10486 custom element at level position (x/y) == (11/9) which uses the zonk
10487 animation mentioned above. Using "game.graphics_engine_version: 4"
10488 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10489 This can also be seen from the debug output for this test element.)
10492 /* when a custom element is about to change (for example by change delay),
10493 do not reset graphic animation when the custom element is moving */
10494 if (game.graphics_engine_version < 4 &&
10497 ResetGfxAnimation(x, y);
10498 ResetRandomAnimationValue(x, y);
10501 if (change->pre_change_function)
10502 change->pre_change_function(x, y);
10506 ChangeDelay[x][y]--;
10508 if (ChangeDelay[x][y] != 0) /* continue element change */
10510 if (change->can_change)
10512 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10514 if (IS_ANIMATED(graphic))
10515 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10517 if (change->change_function)
10518 change->change_function(x, y);
10521 else /* finish element change */
10523 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10525 page = ChangePage[x][y];
10526 ChangePage[x][y] = -1;
10528 change = &ei->change_page[page];
10531 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10533 ChangeDelay[x][y] = 1; /* try change after next move step */
10534 ChangePage[x][y] = page; /* remember page to use for change */
10539 /* special case: set new level random seed before changing element */
10540 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10541 handle_action_before_change = TRUE;
10543 if (change->has_action && handle_action_before_change)
10544 ExecuteCustomElementAction(x, y, element, page);
10546 if (change->can_change)
10548 if (ChangeElement(x, y, element, page))
10550 if (change->post_change_function)
10551 change->post_change_function(x, y);
10555 if (change->has_action && !handle_action_before_change)
10556 ExecuteCustomElementAction(x, y, element, page);
10560 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10561 int trigger_element,
10563 int trigger_player,
10567 boolean change_done_any = FALSE;
10568 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10571 if (!(trigger_events[trigger_element][trigger_event]))
10574 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10576 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10578 int element = EL_CUSTOM_START + i;
10579 boolean change_done = FALSE;
10582 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10583 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10586 for (p = 0; p < element_info[element].num_change_pages; p++)
10588 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10590 if (change->can_change_or_has_action &&
10591 change->has_event[trigger_event] &&
10592 change->trigger_side & trigger_side &&
10593 change->trigger_player & trigger_player &&
10594 change->trigger_page & trigger_page_bits &&
10595 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10597 change->actual_trigger_element = trigger_element;
10598 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10599 change->actual_trigger_player_bits = trigger_player;
10600 change->actual_trigger_side = trigger_side;
10601 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10602 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10604 if ((change->can_change && !change_done) || change->has_action)
10608 SCAN_PLAYFIELD(x, y)
10610 if (Feld[x][y] == element)
10612 if (change->can_change && !change_done)
10614 /* if element already changed in this frame, not only prevent
10615 another element change (checked in ChangeElement()), but
10616 also prevent additional element actions for this element */
10618 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10619 !level.use_action_after_change_bug)
10622 ChangeDelay[x][y] = 1;
10623 ChangeEvent[x][y] = trigger_event;
10625 HandleElementChange(x, y, p);
10627 else if (change->has_action)
10629 /* if element already changed in this frame, not only prevent
10630 another element change (checked in ChangeElement()), but
10631 also prevent additional element actions for this element */
10633 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10634 !level.use_action_after_change_bug)
10637 ExecuteCustomElementAction(x, y, element, p);
10638 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10643 if (change->can_change)
10645 change_done = TRUE;
10646 change_done_any = TRUE;
10653 RECURSION_LOOP_DETECTION_END();
10655 return change_done_any;
10658 static boolean CheckElementChangeExt(int x, int y,
10660 int trigger_element,
10662 int trigger_player,
10665 boolean change_done = FALSE;
10668 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10669 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10672 if (Feld[x][y] == EL_BLOCKED)
10674 Blocked2Moving(x, y, &x, &y);
10675 element = Feld[x][y];
10678 /* check if element has already changed or is about to change after moving */
10679 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10680 Feld[x][y] != element) ||
10682 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10683 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10684 ChangePage[x][y] != -1)))
10687 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10689 for (p = 0; p < element_info[element].num_change_pages; p++)
10691 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10693 /* check trigger element for all events where the element that is checked
10694 for changing interacts with a directly adjacent element -- this is
10695 different to element changes that affect other elements to change on the
10696 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10697 boolean check_trigger_element =
10698 (trigger_event == CE_TOUCHING_X ||
10699 trigger_event == CE_HITTING_X ||
10700 trigger_event == CE_HIT_BY_X ||
10701 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10703 if (change->can_change_or_has_action &&
10704 change->has_event[trigger_event] &&
10705 change->trigger_side & trigger_side &&
10706 change->trigger_player & trigger_player &&
10707 (!check_trigger_element ||
10708 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10710 change->actual_trigger_element = trigger_element;
10711 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10712 change->actual_trigger_player_bits = trigger_player;
10713 change->actual_trigger_side = trigger_side;
10714 change->actual_trigger_ce_value = CustomValue[x][y];
10715 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10717 /* special case: trigger element not at (x,y) position for some events */
10718 if (check_trigger_element)
10730 { 0, 0 }, { 0, 0 }, { 0, 0 },
10734 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10735 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10737 change->actual_trigger_ce_value = CustomValue[xx][yy];
10738 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10741 if (change->can_change && !change_done)
10743 ChangeDelay[x][y] = 1;
10744 ChangeEvent[x][y] = trigger_event;
10746 HandleElementChange(x, y, p);
10748 change_done = TRUE;
10750 else if (change->has_action)
10752 ExecuteCustomElementAction(x, y, element, p);
10753 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10758 RECURSION_LOOP_DETECTION_END();
10760 return change_done;
10763 static void PlayPlayerSound(struct PlayerInfo *player)
10765 int jx = player->jx, jy = player->jy;
10766 int sound_element = player->artwork_element;
10767 int last_action = player->last_action_waiting;
10768 int action = player->action_waiting;
10770 if (player->is_waiting)
10772 if (action != last_action)
10773 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10775 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10779 if (action != last_action)
10780 StopSound(element_info[sound_element].sound[last_action]);
10782 if (last_action == ACTION_SLEEPING)
10783 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10787 static void PlayAllPlayersSound()
10791 for (i = 0; i < MAX_PLAYERS; i++)
10792 if (stored_player[i].active)
10793 PlayPlayerSound(&stored_player[i]);
10796 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10798 boolean last_waiting = player->is_waiting;
10799 int move_dir = player->MovDir;
10801 player->dir_waiting = move_dir;
10802 player->last_action_waiting = player->action_waiting;
10806 if (!last_waiting) /* not waiting -> waiting */
10808 player->is_waiting = TRUE;
10810 player->frame_counter_bored =
10812 game.player_boring_delay_fixed +
10813 GetSimpleRandom(game.player_boring_delay_random);
10814 player->frame_counter_sleeping =
10816 game.player_sleeping_delay_fixed +
10817 GetSimpleRandom(game.player_sleeping_delay_random);
10819 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10822 if (game.player_sleeping_delay_fixed +
10823 game.player_sleeping_delay_random > 0 &&
10824 player->anim_delay_counter == 0 &&
10825 player->post_delay_counter == 0 &&
10826 FrameCounter >= player->frame_counter_sleeping)
10827 player->is_sleeping = TRUE;
10828 else if (game.player_boring_delay_fixed +
10829 game.player_boring_delay_random > 0 &&
10830 FrameCounter >= player->frame_counter_bored)
10831 player->is_bored = TRUE;
10833 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10834 player->is_bored ? ACTION_BORING :
10837 if (player->is_sleeping && player->use_murphy)
10839 /* special case for sleeping Murphy when leaning against non-free tile */
10841 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10842 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10843 !IS_MOVING(player->jx - 1, player->jy)))
10844 move_dir = MV_LEFT;
10845 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10846 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10847 !IS_MOVING(player->jx + 1, player->jy)))
10848 move_dir = MV_RIGHT;
10850 player->is_sleeping = FALSE;
10852 player->dir_waiting = move_dir;
10855 if (player->is_sleeping)
10857 if (player->num_special_action_sleeping > 0)
10859 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10861 int last_special_action = player->special_action_sleeping;
10862 int num_special_action = player->num_special_action_sleeping;
10863 int special_action =
10864 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10865 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10866 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10867 last_special_action + 1 : ACTION_SLEEPING);
10868 int special_graphic =
10869 el_act_dir2img(player->artwork_element, special_action, move_dir);
10871 player->anim_delay_counter =
10872 graphic_info[special_graphic].anim_delay_fixed +
10873 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10874 player->post_delay_counter =
10875 graphic_info[special_graphic].post_delay_fixed +
10876 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10878 player->special_action_sleeping = special_action;
10881 if (player->anim_delay_counter > 0)
10883 player->action_waiting = player->special_action_sleeping;
10884 player->anim_delay_counter--;
10886 else if (player->post_delay_counter > 0)
10888 player->post_delay_counter--;
10892 else if (player->is_bored)
10894 if (player->num_special_action_bored > 0)
10896 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10898 int special_action =
10899 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10900 int special_graphic =
10901 el_act_dir2img(player->artwork_element, special_action, move_dir);
10903 player->anim_delay_counter =
10904 graphic_info[special_graphic].anim_delay_fixed +
10905 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10906 player->post_delay_counter =
10907 graphic_info[special_graphic].post_delay_fixed +
10908 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10910 player->special_action_bored = special_action;
10913 if (player->anim_delay_counter > 0)
10915 player->action_waiting = player->special_action_bored;
10916 player->anim_delay_counter--;
10918 else if (player->post_delay_counter > 0)
10920 player->post_delay_counter--;
10925 else if (last_waiting) /* waiting -> not waiting */
10927 player->is_waiting = FALSE;
10928 player->is_bored = FALSE;
10929 player->is_sleeping = FALSE;
10931 player->frame_counter_bored = -1;
10932 player->frame_counter_sleeping = -1;
10934 player->anim_delay_counter = 0;
10935 player->post_delay_counter = 0;
10937 player->dir_waiting = player->MovDir;
10938 player->action_waiting = ACTION_DEFAULT;
10940 player->special_action_bored = ACTION_DEFAULT;
10941 player->special_action_sleeping = ACTION_DEFAULT;
10945 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10947 if ((!player->is_moving && player->was_moving) ||
10948 (player->MovPos == 0 && player->was_moving) ||
10949 (player->is_snapping && !player->was_snapping) ||
10950 (player->is_dropping && !player->was_dropping))
10952 if (!CheckSaveEngineSnapshotToList())
10955 player->was_moving = FALSE;
10956 player->was_snapping = TRUE;
10957 player->was_dropping = TRUE;
10961 if (player->is_moving)
10962 player->was_moving = TRUE;
10964 if (!player->is_snapping)
10965 player->was_snapping = FALSE;
10967 if (!player->is_dropping)
10968 player->was_dropping = FALSE;
10972 static void CheckSingleStepMode(struct PlayerInfo *player)
10974 if (tape.single_step && tape.recording && !tape.pausing)
10976 /* as it is called "single step mode", just return to pause mode when the
10977 player stopped moving after one tile (or never starts moving at all) */
10978 if (!player->is_moving &&
10979 !player->is_pushing &&
10980 !player->is_dropping_pressed)
10982 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10983 SnapField(player, 0, 0); /* stop snapping */
10987 CheckSaveEngineSnapshot(player);
10990 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10992 int left = player_action & JOY_LEFT;
10993 int right = player_action & JOY_RIGHT;
10994 int up = player_action & JOY_UP;
10995 int down = player_action & JOY_DOWN;
10996 int button1 = player_action & JOY_BUTTON_1;
10997 int button2 = player_action & JOY_BUTTON_2;
10998 int dx = (left ? -1 : right ? 1 : 0);
10999 int dy = (up ? -1 : down ? 1 : 0);
11001 if (!player->active || tape.pausing)
11007 SnapField(player, dx, dy);
11011 DropElement(player);
11013 MovePlayer(player, dx, dy);
11016 CheckSingleStepMode(player);
11018 SetPlayerWaiting(player, FALSE);
11020 return player_action;
11024 /* no actions for this player (no input at player's configured device) */
11026 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11027 SnapField(player, 0, 0);
11028 CheckGravityMovementWhenNotMoving(player);
11030 if (player->MovPos == 0)
11031 SetPlayerWaiting(player, TRUE);
11033 if (player->MovPos == 0) /* needed for tape.playing */
11034 player->is_moving = FALSE;
11036 player->is_dropping = FALSE;
11037 player->is_dropping_pressed = FALSE;
11038 player->drop_pressed_delay = 0;
11040 CheckSingleStepMode(player);
11046 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11049 if (!tape.use_mouse)
11052 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11053 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11054 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11057 static void SetTapeActionFromMouseAction(byte *tape_action,
11058 struct MouseActionInfo *mouse_action)
11060 if (!tape.use_mouse)
11063 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11064 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11065 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11068 static void CheckLevelTime()
11072 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11073 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11075 if (level.native_em_level->lev->home == 0) /* all players at home */
11077 PlayerWins(local_player);
11079 AllPlayersGone = TRUE;
11081 level.native_em_level->lev->home = -1;
11084 if (level.native_em_level->ply[0]->alive == 0 &&
11085 level.native_em_level->ply[1]->alive == 0 &&
11086 level.native_em_level->ply[2]->alive == 0 &&
11087 level.native_em_level->ply[3]->alive == 0) /* all dead */
11088 AllPlayersGone = TRUE;
11090 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11092 if (game_sp.LevelSolved &&
11093 !game_sp.GameOver) /* game won */
11095 PlayerWins(local_player);
11097 game_sp.GameOver = TRUE;
11099 AllPlayersGone = TRUE;
11102 if (game_sp.GameOver) /* game lost */
11103 AllPlayersGone = TRUE;
11105 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11107 if (game_mm.level_solved &&
11108 !game_mm.game_over) /* game won */
11110 PlayerWins(local_player);
11112 game_mm.game_over = TRUE;
11114 AllPlayersGone = TRUE;
11117 if (game_mm.game_over) /* game lost */
11118 AllPlayersGone = TRUE;
11121 if (TimeFrames >= FRAMES_PER_SECOND)
11126 for (i = 0; i < MAX_PLAYERS; i++)
11128 struct PlayerInfo *player = &stored_player[i];
11130 if (SHIELD_ON(player))
11132 player->shield_normal_time_left--;
11134 if (player->shield_deadly_time_left > 0)
11135 player->shield_deadly_time_left--;
11139 if (!local_player->LevelSolved && !level.use_step_counter)
11147 if (TimeLeft <= 10 && setup.time_limit)
11148 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11150 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11151 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11153 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11155 if (!TimeLeft && setup.time_limit)
11157 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11158 level.native_em_level->lev->killed_out_of_time = TRUE;
11160 for (i = 0; i < MAX_PLAYERS; i++)
11161 KillPlayer(&stored_player[i]);
11164 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11166 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11169 level.native_em_level->lev->time =
11170 (game.no_time_limit ? TimePlayed : TimeLeft);
11173 if (tape.recording || tape.playing)
11174 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11177 if (tape.recording || tape.playing)
11178 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11180 UpdateAndDisplayGameControlValues();
11183 void AdvanceFrameAndPlayerCounters(int player_nr)
11187 /* advance frame counters (global frame counter and time frame counter) */
11191 /* advance player counters (counters for move delay, move animation etc.) */
11192 for (i = 0; i < MAX_PLAYERS; i++)
11194 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11195 int move_delay_value = stored_player[i].move_delay_value;
11196 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11198 if (!advance_player_counters) /* not all players may be affected */
11201 if (move_frames == 0) /* less than one move per game frame */
11203 int stepsize = TILEX / move_delay_value;
11204 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11205 int count = (stored_player[i].is_moving ?
11206 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11208 if (count % delay == 0)
11212 stored_player[i].Frame += move_frames;
11214 if (stored_player[i].MovPos != 0)
11215 stored_player[i].StepFrame += move_frames;
11217 if (stored_player[i].move_delay > 0)
11218 stored_player[i].move_delay--;
11220 /* due to bugs in previous versions, counter must count up, not down */
11221 if (stored_player[i].push_delay != -1)
11222 stored_player[i].push_delay++;
11224 if (stored_player[i].drop_delay > 0)
11225 stored_player[i].drop_delay--;
11227 if (stored_player[i].is_dropping_pressed)
11228 stored_player[i].drop_pressed_delay++;
11232 void StartGameActions(boolean init_network_game, boolean record_tape,
11235 unsigned int new_random_seed = InitRND(random_seed);
11238 TapeStartRecording(new_random_seed);
11240 #if defined(NETWORK_AVALIABLE)
11241 if (init_network_game)
11243 SendToServer_StartPlaying();
11252 void GameActionsExt()
11255 static unsigned int game_frame_delay = 0;
11257 unsigned int game_frame_delay_value;
11258 byte *recorded_player_action;
11259 byte summarized_player_action = 0;
11260 byte tape_action[MAX_PLAYERS];
11263 /* detect endless loops, caused by custom element programming */
11264 if (recursion_loop_detected && recursion_loop_depth == 0)
11266 char *message = getStringCat3("Internal Error! Element ",
11267 EL_NAME(recursion_loop_element),
11268 " caused endless loop! Quit the game?");
11270 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11271 EL_NAME(recursion_loop_element));
11273 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11275 recursion_loop_detected = FALSE; /* if game should be continued */
11282 if (game.restart_level)
11283 StartGameActions(options.network, setup.autorecord, level.random_seed);
11285 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11288 if (level.native_em_level->lev->home == 0) /* all players at home */
11290 PlayerWins(local_player);
11292 AllPlayersGone = TRUE;
11294 level.native_em_level->lev->home = -1;
11297 if (level.native_em_level->ply[0]->alive == 0 &&
11298 level.native_em_level->ply[1]->alive == 0 &&
11299 level.native_em_level->ply[2]->alive == 0 &&
11300 level.native_em_level->ply[3]->alive == 0) /* all dead */
11301 AllPlayersGone = TRUE;
11303 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11305 if (game_sp.LevelSolved &&
11306 !game_sp.GameOver) /* game won */
11308 PlayerWins(local_player);
11310 game_sp.GameOver = TRUE;
11312 AllPlayersGone = TRUE;
11315 if (game_sp.GameOver) /* game lost */
11316 AllPlayersGone = TRUE;
11318 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11320 if (game_mm.level_solved &&
11321 !game_mm.game_over) /* game won */
11323 PlayerWins(local_player);
11325 game_mm.game_over = TRUE;
11327 AllPlayersGone = TRUE;
11330 if (game_mm.game_over) /* game lost */
11331 AllPlayersGone = TRUE;
11334 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11337 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11340 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11343 game_frame_delay_value =
11344 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11346 if (tape.playing && tape.warp_forward && !tape.pausing)
11347 game_frame_delay_value = 0;
11349 SetVideoFrameDelay(game_frame_delay_value);
11353 /* ---------- main game synchronization point ---------- */
11355 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11357 printf("::: skip == %d\n", skip);
11360 /* ---------- main game synchronization point ---------- */
11362 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11366 if (network_playing && !network_player_action_received)
11368 /* try to get network player actions in time */
11370 #if defined(NETWORK_AVALIABLE)
11371 /* last chance to get network player actions without main loop delay */
11372 HandleNetworking();
11375 /* game was quit by network peer */
11376 if (game_status != GAME_MODE_PLAYING)
11379 if (!network_player_action_received)
11380 return; /* failed to get network player actions in time */
11382 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11388 /* at this point we know that we really continue executing the game */
11390 network_player_action_received = FALSE;
11392 /* when playing tape, read previously recorded player input from tape data */
11393 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11395 local_player->effective_mouse_action = local_player->mouse_action;
11397 if (recorded_player_action != NULL)
11398 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11399 recorded_player_action);
11401 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11405 if (tape.set_centered_player)
11407 game.centered_player_nr_next = tape.centered_player_nr_next;
11408 game.set_centered_player = TRUE;
11411 for (i = 0; i < MAX_PLAYERS; i++)
11413 summarized_player_action |= stored_player[i].action;
11415 if (!network_playing && (game.team_mode || tape.playing))
11416 stored_player[i].effective_action = stored_player[i].action;
11419 #if defined(NETWORK_AVALIABLE)
11420 if (network_playing)
11421 SendToServer_MovePlayer(summarized_player_action);
11424 // summarize all actions at local players mapped input device position
11425 // (this allows using different input devices in single player mode)
11426 if (!options.network && !game.team_mode)
11427 stored_player[map_player_action[local_player->index_nr]].effective_action =
11428 summarized_player_action;
11430 if (tape.recording &&
11432 setup.input_on_focus &&
11433 game.centered_player_nr != -1)
11435 for (i = 0; i < MAX_PLAYERS; i++)
11436 stored_player[i].effective_action =
11437 (i == game.centered_player_nr ? summarized_player_action : 0);
11440 if (recorded_player_action != NULL)
11441 for (i = 0; i < MAX_PLAYERS; i++)
11442 stored_player[i].effective_action = recorded_player_action[i];
11444 for (i = 0; i < MAX_PLAYERS; i++)
11446 tape_action[i] = stored_player[i].effective_action;
11448 /* (this may happen in the RND game engine if a player was not present on
11449 the playfield on level start, but appeared later from a custom element */
11450 if (setup.team_mode &&
11453 !tape.player_participates[i])
11454 tape.player_participates[i] = TRUE;
11457 SetTapeActionFromMouseAction(tape_action,
11458 &local_player->effective_mouse_action);
11460 /* only record actions from input devices, but not programmed actions */
11461 if (tape.recording)
11462 TapeRecordAction(tape_action);
11464 #if USE_NEW_PLAYER_ASSIGNMENTS
11465 // !!! also map player actions in single player mode !!!
11466 // if (game.team_mode)
11469 byte mapped_action[MAX_PLAYERS];
11471 #if DEBUG_PLAYER_ACTIONS
11473 for (i = 0; i < MAX_PLAYERS; i++)
11474 printf(" %d, ", stored_player[i].effective_action);
11477 for (i = 0; i < MAX_PLAYERS; i++)
11478 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11480 for (i = 0; i < MAX_PLAYERS; i++)
11481 stored_player[i].effective_action = mapped_action[i];
11483 #if DEBUG_PLAYER_ACTIONS
11485 for (i = 0; i < MAX_PLAYERS; i++)
11486 printf(" %d, ", stored_player[i].effective_action);
11490 #if DEBUG_PLAYER_ACTIONS
11494 for (i = 0; i < MAX_PLAYERS; i++)
11495 printf(" %d, ", stored_player[i].effective_action);
11501 for (i = 0; i < MAX_PLAYERS; i++)
11503 // allow engine snapshot in case of changed movement attempt
11504 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11505 (stored_player[i].effective_action & KEY_MOTION))
11506 game.snapshot.changed_action = TRUE;
11508 // allow engine snapshot in case of snapping/dropping attempt
11509 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11510 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11511 game.snapshot.changed_action = TRUE;
11513 game.snapshot.last_action[i] = stored_player[i].effective_action;
11516 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11518 GameActions_EM_Main();
11520 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11522 GameActions_SP_Main();
11524 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11526 GameActions_MM_Main();
11530 GameActions_RND_Main();
11533 BlitScreenToBitmap(backbuffer);
11537 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11539 if (global.show_frames_per_second)
11541 static unsigned int fps_counter = 0;
11542 static int fps_frames = 0;
11543 unsigned int fps_delay_ms = Counter() - fps_counter;
11547 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11549 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11552 fps_counter = Counter();
11554 /* always draw FPS to screen after FPS value was updated */
11555 redraw_mask |= REDRAW_FPS;
11558 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11559 if (GetDrawDeactivationMask() == REDRAW_NONE)
11560 redraw_mask |= REDRAW_FPS;
11564 static void GameActions_CheckSaveEngineSnapshot()
11566 if (!game.snapshot.save_snapshot)
11569 // clear flag for saving snapshot _before_ saving snapshot
11570 game.snapshot.save_snapshot = FALSE;
11572 SaveEngineSnapshotToList();
11579 GameActions_CheckSaveEngineSnapshot();
11582 void GameActions_EM_Main()
11584 byte effective_action[MAX_PLAYERS];
11585 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11588 for (i = 0; i < MAX_PLAYERS; i++)
11589 effective_action[i] = stored_player[i].effective_action;
11591 GameActions_EM(effective_action, warp_mode);
11594 void GameActions_SP_Main()
11596 byte effective_action[MAX_PLAYERS];
11597 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11600 for (i = 0; i < MAX_PLAYERS; i++)
11601 effective_action[i] = stored_player[i].effective_action;
11603 GameActions_SP(effective_action, warp_mode);
11605 for (i = 0; i < MAX_PLAYERS; i++)
11607 if (stored_player[i].force_dropping)
11608 stored_player[i].action |= KEY_BUTTON_DROP;
11610 stored_player[i].force_dropping = FALSE;
11614 void GameActions_MM_Main()
11616 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11618 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11621 void GameActions_RND_Main()
11626 void GameActions_RND()
11628 int magic_wall_x = 0, magic_wall_y = 0;
11629 int i, x, y, element, graphic, last_gfx_frame;
11631 InitPlayfieldScanModeVars();
11633 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11635 SCAN_PLAYFIELD(x, y)
11637 ChangeCount[x][y] = 0;
11638 ChangeEvent[x][y] = -1;
11642 if (game.set_centered_player)
11644 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11646 /* switching to "all players" only possible if all players fit to screen */
11647 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11649 game.centered_player_nr_next = game.centered_player_nr;
11650 game.set_centered_player = FALSE;
11653 /* do not switch focus to non-existing (or non-active) player */
11654 if (game.centered_player_nr_next >= 0 &&
11655 !stored_player[game.centered_player_nr_next].active)
11657 game.centered_player_nr_next = game.centered_player_nr;
11658 game.set_centered_player = FALSE;
11662 if (game.set_centered_player &&
11663 ScreenMovPos == 0) /* screen currently aligned at tile position */
11667 if (game.centered_player_nr_next == -1)
11669 setScreenCenteredToAllPlayers(&sx, &sy);
11673 sx = stored_player[game.centered_player_nr_next].jx;
11674 sy = stored_player[game.centered_player_nr_next].jy;
11677 game.centered_player_nr = game.centered_player_nr_next;
11678 game.set_centered_player = FALSE;
11680 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11681 DrawGameDoorValues();
11684 for (i = 0; i < MAX_PLAYERS; i++)
11686 int actual_player_action = stored_player[i].effective_action;
11689 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11690 - rnd_equinox_tetrachloride 048
11691 - rnd_equinox_tetrachloride_ii 096
11692 - rnd_emanuel_schmieg 002
11693 - doctor_sloan_ww 001, 020
11695 if (stored_player[i].MovPos == 0)
11696 CheckGravityMovement(&stored_player[i]);
11699 /* overwrite programmed action with tape action */
11700 if (stored_player[i].programmed_action)
11701 actual_player_action = stored_player[i].programmed_action;
11703 PlayerActions(&stored_player[i], actual_player_action);
11705 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11708 ScrollScreen(NULL, SCROLL_GO_ON);
11710 /* for backwards compatibility, the following code emulates a fixed bug that
11711 occured when pushing elements (causing elements that just made their last
11712 pushing step to already (if possible) make their first falling step in the
11713 same game frame, which is bad); this code is also needed to use the famous
11714 "spring push bug" which is used in older levels and might be wanted to be
11715 used also in newer levels, but in this case the buggy pushing code is only
11716 affecting the "spring" element and no other elements */
11718 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11720 for (i = 0; i < MAX_PLAYERS; i++)
11722 struct PlayerInfo *player = &stored_player[i];
11723 int x = player->jx;
11724 int y = player->jy;
11726 if (player->active && player->is_pushing && player->is_moving &&
11728 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11729 Feld[x][y] == EL_SPRING))
11731 ContinueMoving(x, y);
11733 /* continue moving after pushing (this is actually a bug) */
11734 if (!IS_MOVING(x, y))
11735 Stop[x][y] = FALSE;
11740 SCAN_PLAYFIELD(x, y)
11742 ChangeCount[x][y] = 0;
11743 ChangeEvent[x][y] = -1;
11745 /* this must be handled before main playfield loop */
11746 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11749 if (MovDelay[x][y] <= 0)
11753 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11756 if (MovDelay[x][y] <= 0)
11759 TEST_DrawLevelField(x, y);
11761 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11766 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11768 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11769 printf("GameActions(): This should never happen!\n");
11771 ChangePage[x][y] = -1;
11775 Stop[x][y] = FALSE;
11776 if (WasJustMoving[x][y] > 0)
11777 WasJustMoving[x][y]--;
11778 if (WasJustFalling[x][y] > 0)
11779 WasJustFalling[x][y]--;
11780 if (CheckCollision[x][y] > 0)
11781 CheckCollision[x][y]--;
11782 if (CheckImpact[x][y] > 0)
11783 CheckImpact[x][y]--;
11787 /* reset finished pushing action (not done in ContinueMoving() to allow
11788 continuous pushing animation for elements with zero push delay) */
11789 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11791 ResetGfxAnimation(x, y);
11792 TEST_DrawLevelField(x, y);
11796 if (IS_BLOCKED(x, y))
11800 Blocked2Moving(x, y, &oldx, &oldy);
11801 if (!IS_MOVING(oldx, oldy))
11803 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11804 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11805 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11806 printf("GameActions(): This should never happen!\n");
11812 SCAN_PLAYFIELD(x, y)
11814 element = Feld[x][y];
11815 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11816 last_gfx_frame = GfxFrame[x][y];
11818 ResetGfxFrame(x, y);
11820 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11821 DrawLevelGraphicAnimation(x, y, graphic);
11823 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11824 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11825 ResetRandomAnimationValue(x, y);
11827 SetRandomAnimationValue(x, y);
11829 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11831 if (IS_INACTIVE(element))
11833 if (IS_ANIMATED(graphic))
11834 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11839 /* this may take place after moving, so 'element' may have changed */
11840 if (IS_CHANGING(x, y) &&
11841 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11843 int page = element_info[element].event_page_nr[CE_DELAY];
11845 HandleElementChange(x, y, page);
11847 element = Feld[x][y];
11848 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11851 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11855 element = Feld[x][y];
11856 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11858 if (IS_ANIMATED(graphic) &&
11859 !IS_MOVING(x, y) &&
11861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11863 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11864 TEST_DrawTwinkleOnField(x, y);
11866 else if (element == EL_ACID)
11869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11871 else if ((element == EL_EXIT_OPEN ||
11872 element == EL_EM_EXIT_OPEN ||
11873 element == EL_SP_EXIT_OPEN ||
11874 element == EL_STEEL_EXIT_OPEN ||
11875 element == EL_EM_STEEL_EXIT_OPEN ||
11876 element == EL_SP_TERMINAL ||
11877 element == EL_SP_TERMINAL_ACTIVE ||
11878 element == EL_EXTRA_TIME ||
11879 element == EL_SHIELD_NORMAL ||
11880 element == EL_SHIELD_DEADLY) &&
11881 IS_ANIMATED(graphic))
11882 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11883 else if (IS_MOVING(x, y))
11884 ContinueMoving(x, y);
11885 else if (IS_ACTIVE_BOMB(element))
11886 CheckDynamite(x, y);
11887 else if (element == EL_AMOEBA_GROWING)
11888 AmoebeWaechst(x, y);
11889 else if (element == EL_AMOEBA_SHRINKING)
11890 AmoebaDisappearing(x, y);
11892 #if !USE_NEW_AMOEBA_CODE
11893 else if (IS_AMOEBALIVE(element))
11894 AmoebeAbleger(x, y);
11897 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11899 else if (element == EL_EXIT_CLOSED)
11901 else if (element == EL_EM_EXIT_CLOSED)
11903 else if (element == EL_STEEL_EXIT_CLOSED)
11904 CheckExitSteel(x, y);
11905 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11906 CheckExitSteelEM(x, y);
11907 else if (element == EL_SP_EXIT_CLOSED)
11909 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11910 element == EL_EXPANDABLE_STEELWALL_GROWING)
11911 MauerWaechst(x, y);
11912 else if (element == EL_EXPANDABLE_WALL ||
11913 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11914 element == EL_EXPANDABLE_WALL_VERTICAL ||
11915 element == EL_EXPANDABLE_WALL_ANY ||
11916 element == EL_BD_EXPANDABLE_WALL)
11917 MauerAbleger(x, y);
11918 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11919 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11920 element == EL_EXPANDABLE_STEELWALL_ANY)
11921 MauerAblegerStahl(x, y);
11922 else if (element == EL_FLAMES)
11923 CheckForDragon(x, y);
11924 else if (element == EL_EXPLOSION)
11925 ; /* drawing of correct explosion animation is handled separately */
11926 else if (element == EL_ELEMENT_SNAPPING ||
11927 element == EL_DIAGONAL_SHRINKING ||
11928 element == EL_DIAGONAL_GROWING)
11930 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11932 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11934 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11937 if (IS_BELT_ACTIVE(element))
11938 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11940 if (game.magic_wall_active)
11942 int jx = local_player->jx, jy = local_player->jy;
11944 /* play the element sound at the position nearest to the player */
11945 if ((element == EL_MAGIC_WALL_FULL ||
11946 element == EL_MAGIC_WALL_ACTIVE ||
11947 element == EL_MAGIC_WALL_EMPTYING ||
11948 element == EL_BD_MAGIC_WALL_FULL ||
11949 element == EL_BD_MAGIC_WALL_ACTIVE ||
11950 element == EL_BD_MAGIC_WALL_EMPTYING ||
11951 element == EL_DC_MAGIC_WALL_FULL ||
11952 element == EL_DC_MAGIC_WALL_ACTIVE ||
11953 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11954 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11962 #if USE_NEW_AMOEBA_CODE
11963 /* new experimental amoeba growth stuff */
11964 if (!(FrameCounter % 8))
11966 static unsigned int random = 1684108901;
11968 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11970 x = RND(lev_fieldx);
11971 y = RND(lev_fieldy);
11972 element = Feld[x][y];
11974 if (!IS_PLAYER(x,y) &&
11975 (element == EL_EMPTY ||
11976 CAN_GROW_INTO(element) ||
11977 element == EL_QUICKSAND_EMPTY ||
11978 element == EL_QUICKSAND_FAST_EMPTY ||
11979 element == EL_ACID_SPLASH_LEFT ||
11980 element == EL_ACID_SPLASH_RIGHT))
11982 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11983 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11984 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11985 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11986 Feld[x][y] = EL_AMOEBA_DROP;
11989 random = random * 129 + 1;
11994 game.explosions_delayed = FALSE;
11996 SCAN_PLAYFIELD(x, y)
11998 element = Feld[x][y];
12000 if (ExplodeField[x][y])
12001 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12002 else if (element == EL_EXPLOSION)
12003 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12005 ExplodeField[x][y] = EX_TYPE_NONE;
12008 game.explosions_delayed = TRUE;
12010 if (game.magic_wall_active)
12012 if (!(game.magic_wall_time_left % 4))
12014 int element = Feld[magic_wall_x][magic_wall_y];
12016 if (element == EL_BD_MAGIC_WALL_FULL ||
12017 element == EL_BD_MAGIC_WALL_ACTIVE ||
12018 element == EL_BD_MAGIC_WALL_EMPTYING)
12019 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12020 else if (element == EL_DC_MAGIC_WALL_FULL ||
12021 element == EL_DC_MAGIC_WALL_ACTIVE ||
12022 element == EL_DC_MAGIC_WALL_EMPTYING)
12023 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12025 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12028 if (game.magic_wall_time_left > 0)
12030 game.magic_wall_time_left--;
12032 if (!game.magic_wall_time_left)
12034 SCAN_PLAYFIELD(x, y)
12036 element = Feld[x][y];
12038 if (element == EL_MAGIC_WALL_ACTIVE ||
12039 element == EL_MAGIC_WALL_FULL)
12041 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12042 TEST_DrawLevelField(x, y);
12044 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12045 element == EL_BD_MAGIC_WALL_FULL)
12047 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12048 TEST_DrawLevelField(x, y);
12050 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12051 element == EL_DC_MAGIC_WALL_FULL)
12053 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12054 TEST_DrawLevelField(x, y);
12058 game.magic_wall_active = FALSE;
12063 if (game.light_time_left > 0)
12065 game.light_time_left--;
12067 if (game.light_time_left == 0)
12068 RedrawAllLightSwitchesAndInvisibleElements();
12071 if (game.timegate_time_left > 0)
12073 game.timegate_time_left--;
12075 if (game.timegate_time_left == 0)
12076 CloseAllOpenTimegates();
12079 if (game.lenses_time_left > 0)
12081 game.lenses_time_left--;
12083 if (game.lenses_time_left == 0)
12084 RedrawAllInvisibleElementsForLenses();
12087 if (game.magnify_time_left > 0)
12089 game.magnify_time_left--;
12091 if (game.magnify_time_left == 0)
12092 RedrawAllInvisibleElementsForMagnifier();
12095 for (i = 0; i < MAX_PLAYERS; i++)
12097 struct PlayerInfo *player = &stored_player[i];
12099 if (SHIELD_ON(player))
12101 if (player->shield_deadly_time_left)
12102 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12103 else if (player->shield_normal_time_left)
12104 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12108 #if USE_DELAYED_GFX_REDRAW
12109 SCAN_PLAYFIELD(x, y)
12111 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12113 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12114 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12116 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12117 DrawLevelField(x, y);
12119 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12120 DrawLevelFieldCrumbled(x, y);
12122 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12123 DrawLevelFieldCrumbledNeighbours(x, y);
12125 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12126 DrawTwinkleOnField(x, y);
12129 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12134 PlayAllPlayersSound();
12136 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12138 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12140 local_player->show_envelope = 0;
12143 /* use random number generator in every frame to make it less predictable */
12144 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12148 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12150 int min_x = x, min_y = y, max_x = x, max_y = y;
12153 for (i = 0; i < MAX_PLAYERS; i++)
12155 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12157 if (!stored_player[i].active || &stored_player[i] == player)
12160 min_x = MIN(min_x, jx);
12161 min_y = MIN(min_y, jy);
12162 max_x = MAX(max_x, jx);
12163 max_y = MAX(max_y, jy);
12166 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12169 static boolean AllPlayersInVisibleScreen()
12173 for (i = 0; i < MAX_PLAYERS; i++)
12175 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12177 if (!stored_player[i].active)
12180 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12187 void ScrollLevel(int dx, int dy)
12189 int scroll_offset = 2 * TILEX_VAR;
12192 BlitBitmap(drawto_field, drawto_field,
12193 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12194 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12195 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12196 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12197 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12198 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12202 x = (dx == 1 ? BX1 : BX2);
12203 for (y = BY1; y <= BY2; y++)
12204 DrawScreenField(x, y);
12209 y = (dy == 1 ? BY1 : BY2);
12210 for (x = BX1; x <= BX2; x++)
12211 DrawScreenField(x, y);
12214 redraw_mask |= REDRAW_FIELD;
12217 static boolean canFallDown(struct PlayerInfo *player)
12219 int jx = player->jx, jy = player->jy;
12221 return (IN_LEV_FIELD(jx, jy + 1) &&
12222 (IS_FREE(jx, jy + 1) ||
12223 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12224 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12225 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12228 static boolean canPassField(int x, int y, int move_dir)
12230 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12231 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12232 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12233 int nextx = x + dx;
12234 int nexty = y + dy;
12235 int element = Feld[x][y];
12237 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12238 !CAN_MOVE(element) &&
12239 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12240 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12241 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12244 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12246 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12247 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12248 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12252 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12253 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12254 (IS_DIGGABLE(Feld[newx][newy]) ||
12255 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12256 canPassField(newx, newy, move_dir)));
12259 static void CheckGravityMovement(struct PlayerInfo *player)
12261 if (player->gravity && !player->programmed_action)
12263 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12264 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12265 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12266 int jx = player->jx, jy = player->jy;
12267 boolean player_is_moving_to_valid_field =
12268 (!player_is_snapping &&
12269 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12270 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12271 boolean player_can_fall_down = canFallDown(player);
12273 if (player_can_fall_down &&
12274 !player_is_moving_to_valid_field)
12275 player->programmed_action = MV_DOWN;
12279 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12281 return CheckGravityMovement(player);
12283 if (player->gravity && !player->programmed_action)
12285 int jx = player->jx, jy = player->jy;
12286 boolean field_under_player_is_free =
12287 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12288 boolean player_is_standing_on_valid_field =
12289 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12290 (IS_WALKABLE(Feld[jx][jy]) &&
12291 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12293 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12294 player->programmed_action = MV_DOWN;
12299 MovePlayerOneStep()
12300 -----------------------------------------------------------------------------
12301 dx, dy: direction (non-diagonal) to try to move the player to
12302 real_dx, real_dy: direction as read from input device (can be diagonal)
12305 boolean MovePlayerOneStep(struct PlayerInfo *player,
12306 int dx, int dy, int real_dx, int real_dy)
12308 int jx = player->jx, jy = player->jy;
12309 int new_jx = jx + dx, new_jy = jy + dy;
12311 boolean player_can_move = !player->cannot_move;
12313 if (!player->active || (!dx && !dy))
12314 return MP_NO_ACTION;
12316 player->MovDir = (dx < 0 ? MV_LEFT :
12317 dx > 0 ? MV_RIGHT :
12319 dy > 0 ? MV_DOWN : MV_NONE);
12321 if (!IN_LEV_FIELD(new_jx, new_jy))
12322 return MP_NO_ACTION;
12324 if (!player_can_move)
12326 if (player->MovPos == 0)
12328 player->is_moving = FALSE;
12329 player->is_digging = FALSE;
12330 player->is_collecting = FALSE;
12331 player->is_snapping = FALSE;
12332 player->is_pushing = FALSE;
12336 if (!options.network && game.centered_player_nr == -1 &&
12337 !AllPlayersInSight(player, new_jx, new_jy))
12338 return MP_NO_ACTION;
12340 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12341 if (can_move != MP_MOVING)
12344 /* check if DigField() has caused relocation of the player */
12345 if (player->jx != jx || player->jy != jy)
12346 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12348 StorePlayer[jx][jy] = 0;
12349 player->last_jx = jx;
12350 player->last_jy = jy;
12351 player->jx = new_jx;
12352 player->jy = new_jy;
12353 StorePlayer[new_jx][new_jy] = player->element_nr;
12355 if (player->move_delay_value_next != -1)
12357 player->move_delay_value = player->move_delay_value_next;
12358 player->move_delay_value_next = -1;
12362 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12364 player->step_counter++;
12366 PlayerVisit[jx][jy] = FrameCounter;
12368 player->is_moving = TRUE;
12371 /* should better be called in MovePlayer(), but this breaks some tapes */
12372 ScrollPlayer(player, SCROLL_INIT);
12378 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12380 int jx = player->jx, jy = player->jy;
12381 int old_jx = jx, old_jy = jy;
12382 int moved = MP_NO_ACTION;
12384 if (!player->active)
12389 if (player->MovPos == 0)
12391 player->is_moving = FALSE;
12392 player->is_digging = FALSE;
12393 player->is_collecting = FALSE;
12394 player->is_snapping = FALSE;
12395 player->is_pushing = FALSE;
12401 if (player->move_delay > 0)
12404 player->move_delay = -1; /* set to "uninitialized" value */
12406 /* store if player is automatically moved to next field */
12407 player->is_auto_moving = (player->programmed_action != MV_NONE);
12409 /* remove the last programmed player action */
12410 player->programmed_action = 0;
12412 if (player->MovPos)
12414 /* should only happen if pre-1.2 tape recordings are played */
12415 /* this is only for backward compatibility */
12417 int original_move_delay_value = player->move_delay_value;
12420 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12424 /* scroll remaining steps with finest movement resolution */
12425 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12427 while (player->MovPos)
12429 ScrollPlayer(player, SCROLL_GO_ON);
12430 ScrollScreen(NULL, SCROLL_GO_ON);
12432 AdvanceFrameAndPlayerCounters(player->index_nr);
12435 BackToFront_WithFrameDelay(0);
12438 player->move_delay_value = original_move_delay_value;
12441 player->is_active = FALSE;
12443 if (player->last_move_dir & MV_HORIZONTAL)
12445 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12446 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12450 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12451 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12454 if (!moved && !player->is_active)
12456 player->is_moving = FALSE;
12457 player->is_digging = FALSE;
12458 player->is_collecting = FALSE;
12459 player->is_snapping = FALSE;
12460 player->is_pushing = FALSE;
12466 if (moved & MP_MOVING && !ScreenMovPos &&
12467 (player->index_nr == game.centered_player_nr ||
12468 game.centered_player_nr == -1))
12470 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12471 int offset = game.scroll_delay_value;
12473 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12475 /* actual player has left the screen -- scroll in that direction */
12476 if (jx != old_jx) /* player has moved horizontally */
12477 scroll_x += (jx - old_jx);
12478 else /* player has moved vertically */
12479 scroll_y += (jy - old_jy);
12483 if (jx != old_jx) /* player has moved horizontally */
12485 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12486 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12487 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12489 /* don't scroll over playfield boundaries */
12490 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12491 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12493 /* don't scroll more than one field at a time */
12494 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12496 /* don't scroll against the player's moving direction */
12497 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12498 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12499 scroll_x = old_scroll_x;
12501 else /* player has moved vertically */
12503 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12504 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12505 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12507 /* don't scroll over playfield boundaries */
12508 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12509 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12511 /* don't scroll more than one field at a time */
12512 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12514 /* don't scroll against the player's moving direction */
12515 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12516 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12517 scroll_y = old_scroll_y;
12521 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12523 if (!options.network && game.centered_player_nr == -1 &&
12524 !AllPlayersInVisibleScreen())
12526 scroll_x = old_scroll_x;
12527 scroll_y = old_scroll_y;
12531 ScrollScreen(player, SCROLL_INIT);
12532 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12537 player->StepFrame = 0;
12539 if (moved & MP_MOVING)
12541 if (old_jx != jx && old_jy == jy)
12542 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12543 else if (old_jx == jx && old_jy != jy)
12544 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12546 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12548 player->last_move_dir = player->MovDir;
12549 player->is_moving = TRUE;
12550 player->is_snapping = FALSE;
12551 player->is_switching = FALSE;
12552 player->is_dropping = FALSE;
12553 player->is_dropping_pressed = FALSE;
12554 player->drop_pressed_delay = 0;
12557 /* should better be called here than above, but this breaks some tapes */
12558 ScrollPlayer(player, SCROLL_INIT);
12563 CheckGravityMovementWhenNotMoving(player);
12565 player->is_moving = FALSE;
12567 /* at this point, the player is allowed to move, but cannot move right now
12568 (e.g. because of something blocking the way) -- ensure that the player
12569 is also allowed to move in the next frame (in old versions before 3.1.1,
12570 the player was forced to wait again for eight frames before next try) */
12572 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12573 player->move_delay = 0; /* allow direct movement in the next frame */
12576 if (player->move_delay == -1) /* not yet initialized by DigField() */
12577 player->move_delay = player->move_delay_value;
12579 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12581 TestIfPlayerTouchesBadThing(jx, jy);
12582 TestIfPlayerTouchesCustomElement(jx, jy);
12585 if (!player->active)
12586 RemovePlayer(player);
12591 void ScrollPlayer(struct PlayerInfo *player, int mode)
12593 int jx = player->jx, jy = player->jy;
12594 int last_jx = player->last_jx, last_jy = player->last_jy;
12595 int move_stepsize = TILEX / player->move_delay_value;
12597 if (!player->active)
12600 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12603 if (mode == SCROLL_INIT)
12605 player->actual_frame_counter = FrameCounter;
12606 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12608 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12609 Feld[last_jx][last_jy] == EL_EMPTY)
12611 int last_field_block_delay = 0; /* start with no blocking at all */
12612 int block_delay_adjustment = player->block_delay_adjustment;
12614 /* if player blocks last field, add delay for exactly one move */
12615 if (player->block_last_field)
12617 last_field_block_delay += player->move_delay_value;
12619 /* when blocking enabled, prevent moving up despite gravity */
12620 if (player->gravity && player->MovDir == MV_UP)
12621 block_delay_adjustment = -1;
12624 /* add block delay adjustment (also possible when not blocking) */
12625 last_field_block_delay += block_delay_adjustment;
12627 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12628 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12631 if (player->MovPos != 0) /* player has not yet reached destination */
12634 else if (!FrameReached(&player->actual_frame_counter, 1))
12637 if (player->MovPos != 0)
12639 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12640 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12642 /* before DrawPlayer() to draw correct player graphic for this case */
12643 if (player->MovPos == 0)
12644 CheckGravityMovement(player);
12647 if (player->MovPos == 0) /* player reached destination field */
12649 if (player->move_delay_reset_counter > 0)
12651 player->move_delay_reset_counter--;
12653 if (player->move_delay_reset_counter == 0)
12655 /* continue with normal speed after quickly moving through gate */
12656 HALVE_PLAYER_SPEED(player);
12658 /* be able to make the next move without delay */
12659 player->move_delay = 0;
12663 player->last_jx = jx;
12664 player->last_jy = jy;
12666 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12667 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12668 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12669 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12670 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12671 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12672 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12673 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12675 DrawPlayer(player); /* needed here only to cleanup last field */
12676 RemovePlayer(player);
12678 if (local_player->friends_still_needed == 0 ||
12679 IS_SP_ELEMENT(Feld[jx][jy]))
12680 PlayerWins(player);
12683 /* this breaks one level: "machine", level 000 */
12685 int move_direction = player->MovDir;
12686 int enter_side = MV_DIR_OPPOSITE(move_direction);
12687 int leave_side = move_direction;
12688 int old_jx = last_jx;
12689 int old_jy = last_jy;
12690 int old_element = Feld[old_jx][old_jy];
12691 int new_element = Feld[jx][jy];
12693 if (IS_CUSTOM_ELEMENT(old_element))
12694 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12696 player->index_bit, leave_side);
12698 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12699 CE_PLAYER_LEAVES_X,
12700 player->index_bit, leave_side);
12702 if (IS_CUSTOM_ELEMENT(new_element))
12703 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12704 player->index_bit, enter_side);
12706 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12707 CE_PLAYER_ENTERS_X,
12708 player->index_bit, enter_side);
12710 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12711 CE_MOVE_OF_X, move_direction);
12714 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12716 TestIfPlayerTouchesBadThing(jx, jy);
12717 TestIfPlayerTouchesCustomElement(jx, jy);
12719 /* needed because pushed element has not yet reached its destination,
12720 so it would trigger a change event at its previous field location */
12721 if (!player->is_pushing)
12722 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12724 if (!player->active)
12725 RemovePlayer(player);
12728 if (!local_player->LevelSolved && level.use_step_counter)
12738 if (TimeLeft <= 10 && setup.time_limit)
12739 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12741 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12743 DisplayGameControlValues();
12745 if (!TimeLeft && setup.time_limit)
12746 for (i = 0; i < MAX_PLAYERS; i++)
12747 KillPlayer(&stored_player[i]);
12749 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12751 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12753 DisplayGameControlValues();
12757 if (tape.single_step && tape.recording && !tape.pausing &&
12758 !player->programmed_action)
12759 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12761 if (!player->programmed_action)
12762 CheckSaveEngineSnapshot(player);
12766 void ScrollScreen(struct PlayerInfo *player, int mode)
12768 static unsigned int screen_frame_counter = 0;
12770 if (mode == SCROLL_INIT)
12772 /* set scrolling step size according to actual player's moving speed */
12773 ScrollStepSize = TILEX / player->move_delay_value;
12775 screen_frame_counter = FrameCounter;
12776 ScreenMovDir = player->MovDir;
12777 ScreenMovPos = player->MovPos;
12778 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12781 else if (!FrameReached(&screen_frame_counter, 1))
12786 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12787 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12788 redraw_mask |= REDRAW_FIELD;
12791 ScreenMovDir = MV_NONE;
12794 void TestIfPlayerTouchesCustomElement(int x, int y)
12796 static int xy[4][2] =
12803 static int trigger_sides[4][2] =
12805 /* center side border side */
12806 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12807 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12808 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12809 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12811 static int touch_dir[4] =
12813 MV_LEFT | MV_RIGHT,
12818 int center_element = Feld[x][y]; /* should always be non-moving! */
12821 for (i = 0; i < NUM_DIRECTIONS; i++)
12823 int xx = x + xy[i][0];
12824 int yy = y + xy[i][1];
12825 int center_side = trigger_sides[i][0];
12826 int border_side = trigger_sides[i][1];
12827 int border_element;
12829 if (!IN_LEV_FIELD(xx, yy))
12832 if (IS_PLAYER(x, y)) /* player found at center element */
12834 struct PlayerInfo *player = PLAYERINFO(x, y);
12836 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12837 border_element = Feld[xx][yy]; /* may be moving! */
12838 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12839 border_element = Feld[xx][yy];
12840 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12841 border_element = MovingOrBlocked2Element(xx, yy);
12843 continue; /* center and border element do not touch */
12845 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12846 player->index_bit, border_side);
12847 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12848 CE_PLAYER_TOUCHES_X,
12849 player->index_bit, border_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(x, y)->initial_element;
12858 CheckElementChangeBySide(xx, yy, border_element, player_element,
12859 CE_TOUCHING_X, border_side);
12862 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12864 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12866 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12868 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12869 continue; /* center and border element do not touch */
12872 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12873 player->index_bit, center_side);
12874 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12875 CE_PLAYER_TOUCHES_X,
12876 player->index_bit, center_side);
12879 /* use player element that is initially defined in the level playfield,
12880 not the player element that corresponds to the runtime player number
12881 (example: a level that contains EL_PLAYER_3 as the only player would
12882 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12883 int player_element = PLAYERINFO(xx, yy)->initial_element;
12885 CheckElementChangeBySide(x, y, center_element, player_element,
12886 CE_TOUCHING_X, center_side);
12894 void TestIfElementTouchesCustomElement(int x, int y)
12896 static int xy[4][2] =
12903 static int trigger_sides[4][2] =
12905 /* center side border side */
12906 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12907 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12908 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12909 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12911 static int touch_dir[4] =
12913 MV_LEFT | MV_RIGHT,
12918 boolean change_center_element = FALSE;
12919 int center_element = Feld[x][y]; /* should always be non-moving! */
12920 int border_element_old[NUM_DIRECTIONS];
12923 for (i = 0; i < NUM_DIRECTIONS; i++)
12925 int xx = x + xy[i][0];
12926 int yy = y + xy[i][1];
12927 int border_element;
12929 border_element_old[i] = -1;
12931 if (!IN_LEV_FIELD(xx, yy))
12934 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12935 border_element = Feld[xx][yy]; /* may be moving! */
12936 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12937 border_element = Feld[xx][yy];
12938 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12939 border_element = MovingOrBlocked2Element(xx, yy);
12941 continue; /* center and border element do not touch */
12943 border_element_old[i] = border_element;
12946 for (i = 0; i < NUM_DIRECTIONS; i++)
12948 int xx = x + xy[i][0];
12949 int yy = y + xy[i][1];
12950 int center_side = trigger_sides[i][0];
12951 int border_element = border_element_old[i];
12953 if (border_element == -1)
12956 /* check for change of border element */
12957 CheckElementChangeBySide(xx, yy, border_element, center_element,
12958 CE_TOUCHING_X, center_side);
12960 /* (center element cannot be player, so we dont have to check this here) */
12963 for (i = 0; i < NUM_DIRECTIONS; i++)
12965 int xx = x + xy[i][0];
12966 int yy = y + xy[i][1];
12967 int border_side = trigger_sides[i][1];
12968 int border_element = border_element_old[i];
12970 if (border_element == -1)
12973 /* check for change of center element (but change it only once) */
12974 if (!change_center_element)
12975 change_center_element =
12976 CheckElementChangeBySide(x, y, center_element, border_element,
12977 CE_TOUCHING_X, border_side);
12979 if (IS_PLAYER(xx, yy))
12981 /* use player element that is initially defined in the level playfield,
12982 not the player element that corresponds to the runtime player number
12983 (example: a level that contains EL_PLAYER_3 as the only player would
12984 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12985 int player_element = PLAYERINFO(xx, yy)->initial_element;
12987 CheckElementChangeBySide(x, y, center_element, player_element,
12988 CE_TOUCHING_X, border_side);
12993 void TestIfElementHitsCustomElement(int x, int y, int direction)
12995 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12996 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12997 int hitx = x + dx, hity = y + dy;
12998 int hitting_element = Feld[x][y];
12999 int touched_element;
13001 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13004 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13005 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13007 if (IN_LEV_FIELD(hitx, hity))
13009 int opposite_direction = MV_DIR_OPPOSITE(direction);
13010 int hitting_side = direction;
13011 int touched_side = opposite_direction;
13012 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13013 MovDir[hitx][hity] != direction ||
13014 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13020 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13021 CE_HITTING_X, touched_side);
13023 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13024 CE_HIT_BY_X, hitting_side);
13026 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13027 CE_HIT_BY_SOMETHING, opposite_direction);
13029 if (IS_PLAYER(hitx, hity))
13031 /* use player element that is initially defined in the level playfield,
13032 not the player element that corresponds to the runtime player number
13033 (example: a level that contains EL_PLAYER_3 as the only player would
13034 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13035 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13037 CheckElementChangeBySide(x, y, hitting_element, player_element,
13038 CE_HITTING_X, touched_side);
13043 /* "hitting something" is also true when hitting the playfield border */
13044 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13045 CE_HITTING_SOMETHING, direction);
13048 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13050 int i, kill_x = -1, kill_y = -1;
13052 int bad_element = -1;
13053 static int test_xy[4][2] =
13060 static int test_dir[4] =
13068 for (i = 0; i < NUM_DIRECTIONS; i++)
13070 int test_x, test_y, test_move_dir, test_element;
13072 test_x = good_x + test_xy[i][0];
13073 test_y = good_y + test_xy[i][1];
13075 if (!IN_LEV_FIELD(test_x, test_y))
13079 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13081 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13083 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13084 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13086 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13087 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13091 bad_element = test_element;
13097 if (kill_x != -1 || kill_y != -1)
13099 if (IS_PLAYER(good_x, good_y))
13101 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13103 if (player->shield_deadly_time_left > 0 &&
13104 !IS_INDESTRUCTIBLE(bad_element))
13105 Bang(kill_x, kill_y);
13106 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13107 KillPlayer(player);
13110 Bang(good_x, good_y);
13114 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13116 int i, kill_x = -1, kill_y = -1;
13117 int bad_element = Feld[bad_x][bad_y];
13118 static int test_xy[4][2] =
13125 static int touch_dir[4] =
13127 MV_LEFT | MV_RIGHT,
13132 static int test_dir[4] =
13140 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13143 for (i = 0; i < NUM_DIRECTIONS; i++)
13145 int test_x, test_y, test_move_dir, test_element;
13147 test_x = bad_x + test_xy[i][0];
13148 test_y = bad_y + test_xy[i][1];
13150 if (!IN_LEV_FIELD(test_x, test_y))
13154 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13156 test_element = Feld[test_x][test_y];
13158 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13159 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13161 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13162 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13164 /* good thing is player or penguin that does not move away */
13165 if (IS_PLAYER(test_x, test_y))
13167 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13169 if (bad_element == EL_ROBOT && player->is_moving)
13170 continue; /* robot does not kill player if he is moving */
13172 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13174 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13175 continue; /* center and border element do not touch */
13183 else if (test_element == EL_PENGUIN)
13193 if (kill_x != -1 || kill_y != -1)
13195 if (IS_PLAYER(kill_x, kill_y))
13197 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13199 if (player->shield_deadly_time_left > 0 &&
13200 !IS_INDESTRUCTIBLE(bad_element))
13201 Bang(bad_x, bad_y);
13202 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13203 KillPlayer(player);
13206 Bang(kill_x, kill_y);
13210 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13212 int bad_element = Feld[bad_x][bad_y];
13213 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13214 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13215 int test_x = bad_x + dx, test_y = bad_y + dy;
13216 int test_move_dir, test_element;
13217 int kill_x = -1, kill_y = -1;
13219 if (!IN_LEV_FIELD(test_x, test_y))
13223 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13225 test_element = Feld[test_x][test_y];
13227 if (test_move_dir != bad_move_dir)
13229 /* good thing can be player or penguin that does not move away */
13230 if (IS_PLAYER(test_x, test_y))
13232 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13234 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13235 player as being hit when he is moving towards the bad thing, because
13236 the "get hit by" condition would be lost after the player stops) */
13237 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13238 return; /* player moves away from bad thing */
13243 else if (test_element == EL_PENGUIN)
13250 if (kill_x != -1 || kill_y != -1)
13252 if (IS_PLAYER(kill_x, kill_y))
13254 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13256 if (player->shield_deadly_time_left > 0 &&
13257 !IS_INDESTRUCTIBLE(bad_element))
13258 Bang(bad_x, bad_y);
13259 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13260 KillPlayer(player);
13263 Bang(kill_x, kill_y);
13267 void TestIfPlayerTouchesBadThing(int x, int y)
13269 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13272 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13274 TestIfGoodThingHitsBadThing(x, y, move_dir);
13277 void TestIfBadThingTouchesPlayer(int x, int y)
13279 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13282 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13284 TestIfBadThingHitsGoodThing(x, y, move_dir);
13287 void TestIfFriendTouchesBadThing(int x, int y)
13289 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13292 void TestIfBadThingTouchesFriend(int x, int y)
13294 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13297 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13299 int i, kill_x = bad_x, kill_y = bad_y;
13300 static int xy[4][2] =
13308 for (i = 0; i < NUM_DIRECTIONS; i++)
13312 x = bad_x + xy[i][0];
13313 y = bad_y + xy[i][1];
13314 if (!IN_LEV_FIELD(x, y))
13317 element = Feld[x][y];
13318 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13319 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13327 if (kill_x != bad_x || kill_y != bad_y)
13328 Bang(bad_x, bad_y);
13331 void KillPlayer(struct PlayerInfo *player)
13333 int jx = player->jx, jy = player->jy;
13335 if (!player->active)
13339 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13340 player->killed, player->active, player->reanimated);
13343 /* the following code was introduced to prevent an infinite loop when calling
13345 -> CheckTriggeredElementChangeExt()
13346 -> ExecuteCustomElementAction()
13348 -> (infinitely repeating the above sequence of function calls)
13349 which occurs when killing the player while having a CE with the setting
13350 "kill player X when explosion of <player X>"; the solution using a new
13351 field "player->killed" was chosen for backwards compatibility, although
13352 clever use of the fields "player->active" etc. would probably also work */
13354 if (player->killed)
13358 player->killed = TRUE;
13360 /* remove accessible field at the player's position */
13361 Feld[jx][jy] = EL_EMPTY;
13363 /* deactivate shield (else Bang()/Explode() would not work right) */
13364 player->shield_normal_time_left = 0;
13365 player->shield_deadly_time_left = 0;
13368 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13369 player->killed, player->active, player->reanimated);
13375 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13376 player->killed, player->active, player->reanimated);
13379 if (player->reanimated) /* killed player may have been reanimated */
13380 player->killed = player->reanimated = FALSE;
13382 BuryPlayer(player);
13385 static void KillPlayerUnlessEnemyProtected(int x, int y)
13387 if (!PLAYER_ENEMY_PROTECTED(x, y))
13388 KillPlayer(PLAYERINFO(x, y));
13391 static void KillPlayerUnlessExplosionProtected(int x, int y)
13393 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13394 KillPlayer(PLAYERINFO(x, y));
13397 void BuryPlayer(struct PlayerInfo *player)
13399 int jx = player->jx, jy = player->jy;
13401 if (!player->active)
13404 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13405 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13407 player->GameOver = TRUE;
13408 RemovePlayer(player);
13411 void RemovePlayer(struct PlayerInfo *player)
13413 int jx = player->jx, jy = player->jy;
13414 int i, found = FALSE;
13416 player->present = FALSE;
13417 player->active = FALSE;
13419 if (!ExplodeField[jx][jy])
13420 StorePlayer[jx][jy] = 0;
13422 if (player->is_moving)
13423 TEST_DrawLevelField(player->last_jx, player->last_jy);
13425 for (i = 0; i < MAX_PLAYERS; i++)
13426 if (stored_player[i].active)
13430 AllPlayersGone = TRUE;
13436 static void setFieldForSnapping(int x, int y, int element, int direction)
13438 struct ElementInfo *ei = &element_info[element];
13439 int direction_bit = MV_DIR_TO_BIT(direction);
13440 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13441 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13442 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13444 Feld[x][y] = EL_ELEMENT_SNAPPING;
13445 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13447 ResetGfxAnimation(x, y);
13449 GfxElement[x][y] = element;
13450 GfxAction[x][y] = action;
13451 GfxDir[x][y] = direction;
13452 GfxFrame[x][y] = -1;
13456 =============================================================================
13457 checkDiagonalPushing()
13458 -----------------------------------------------------------------------------
13459 check if diagonal input device direction results in pushing of object
13460 (by checking if the alternative direction is walkable, diggable, ...)
13461 =============================================================================
13464 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13465 int x, int y, int real_dx, int real_dy)
13467 int jx, jy, dx, dy, xx, yy;
13469 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13472 /* diagonal direction: check alternative direction */
13477 xx = jx + (dx == 0 ? real_dx : 0);
13478 yy = jy + (dy == 0 ? real_dy : 0);
13480 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13484 =============================================================================
13486 -----------------------------------------------------------------------------
13487 x, y: field next to player (non-diagonal) to try to dig to
13488 real_dx, real_dy: direction as read from input device (can be diagonal)
13489 =============================================================================
13492 static int DigField(struct PlayerInfo *player,
13493 int oldx, int oldy, int x, int y,
13494 int real_dx, int real_dy, int mode)
13496 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13497 boolean player_was_pushing = player->is_pushing;
13498 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13499 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13500 int jx = oldx, jy = oldy;
13501 int dx = x - jx, dy = y - jy;
13502 int nextx = x + dx, nexty = y + dy;
13503 int move_direction = (dx == -1 ? MV_LEFT :
13504 dx == +1 ? MV_RIGHT :
13506 dy == +1 ? MV_DOWN : MV_NONE);
13507 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13508 int dig_side = MV_DIR_OPPOSITE(move_direction);
13509 int old_element = Feld[jx][jy];
13510 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13513 if (is_player) /* function can also be called by EL_PENGUIN */
13515 if (player->MovPos == 0)
13517 player->is_digging = FALSE;
13518 player->is_collecting = FALSE;
13521 if (player->MovPos == 0) /* last pushing move finished */
13522 player->is_pushing = FALSE;
13524 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13526 player->is_switching = FALSE;
13527 player->push_delay = -1;
13529 return MP_NO_ACTION;
13533 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13534 old_element = Back[jx][jy];
13536 /* in case of element dropped at player position, check background */
13537 else if (Back[jx][jy] != EL_EMPTY &&
13538 game.engine_version >= VERSION_IDENT(2,2,0,0))
13539 old_element = Back[jx][jy];
13541 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13542 return MP_NO_ACTION; /* field has no opening in this direction */
13544 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13545 return MP_NO_ACTION; /* field has no opening in this direction */
13547 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13551 Feld[jx][jy] = player->artwork_element;
13552 InitMovingField(jx, jy, MV_DOWN);
13553 Store[jx][jy] = EL_ACID;
13554 ContinueMoving(jx, jy);
13555 BuryPlayer(player);
13557 return MP_DONT_RUN_INTO;
13560 if (player_can_move && DONT_RUN_INTO(element))
13562 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13564 return MP_DONT_RUN_INTO;
13567 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13568 return MP_NO_ACTION;
13570 collect_count = element_info[element].collect_count_initial;
13572 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13573 return MP_NO_ACTION;
13575 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13576 player_can_move = player_can_move_or_snap;
13578 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13579 game.engine_version >= VERSION_IDENT(2,2,0,0))
13581 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13582 player->index_bit, dig_side);
13583 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13584 player->index_bit, dig_side);
13586 if (element == EL_DC_LANDMINE)
13589 if (Feld[x][y] != element) /* field changed by snapping */
13592 return MP_NO_ACTION;
13595 if (player->gravity && is_player && !player->is_auto_moving &&
13596 canFallDown(player) && move_direction != MV_DOWN &&
13597 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13598 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13600 if (player_can_move &&
13601 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13603 int sound_element = SND_ELEMENT(element);
13604 int sound_action = ACTION_WALKING;
13606 if (IS_RND_GATE(element))
13608 if (!player->key[RND_GATE_NR(element)])
13609 return MP_NO_ACTION;
13611 else if (IS_RND_GATE_GRAY(element))
13613 if (!player->key[RND_GATE_GRAY_NR(element)])
13614 return MP_NO_ACTION;
13616 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13618 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13619 return MP_NO_ACTION;
13621 else if (element == EL_EXIT_OPEN ||
13622 element == EL_EM_EXIT_OPEN ||
13623 element == EL_EM_EXIT_OPENING ||
13624 element == EL_STEEL_EXIT_OPEN ||
13625 element == EL_EM_STEEL_EXIT_OPEN ||
13626 element == EL_EM_STEEL_EXIT_OPENING ||
13627 element == EL_SP_EXIT_OPEN ||
13628 element == EL_SP_EXIT_OPENING)
13630 sound_action = ACTION_PASSING; /* player is passing exit */
13632 else if (element == EL_EMPTY)
13634 sound_action = ACTION_MOVING; /* nothing to walk on */
13637 /* play sound from background or player, whatever is available */
13638 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13639 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13641 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13643 else if (player_can_move &&
13644 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13646 if (!ACCESS_FROM(element, opposite_direction))
13647 return MP_NO_ACTION; /* field not accessible from this direction */
13649 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13650 return MP_NO_ACTION;
13652 if (IS_EM_GATE(element))
13654 if (!player->key[EM_GATE_NR(element)])
13655 return MP_NO_ACTION;
13657 else if (IS_EM_GATE_GRAY(element))
13659 if (!player->key[EM_GATE_GRAY_NR(element)])
13660 return MP_NO_ACTION;
13662 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13664 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13665 return MP_NO_ACTION;
13667 else if (IS_EMC_GATE(element))
13669 if (!player->key[EMC_GATE_NR(element)])
13670 return MP_NO_ACTION;
13672 else if (IS_EMC_GATE_GRAY(element))
13674 if (!player->key[EMC_GATE_GRAY_NR(element)])
13675 return MP_NO_ACTION;
13677 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13679 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13680 return MP_NO_ACTION;
13682 else if (element == EL_DC_GATE_WHITE ||
13683 element == EL_DC_GATE_WHITE_GRAY ||
13684 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13686 if (player->num_white_keys == 0)
13687 return MP_NO_ACTION;
13689 player->num_white_keys--;
13691 else if (IS_SP_PORT(element))
13693 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13694 element == EL_SP_GRAVITY_PORT_RIGHT ||
13695 element == EL_SP_GRAVITY_PORT_UP ||
13696 element == EL_SP_GRAVITY_PORT_DOWN)
13697 player->gravity = !player->gravity;
13698 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13699 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13700 element == EL_SP_GRAVITY_ON_PORT_UP ||
13701 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13702 player->gravity = TRUE;
13703 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13704 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13705 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13706 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13707 player->gravity = FALSE;
13710 /* automatically move to the next field with double speed */
13711 player->programmed_action = move_direction;
13713 if (player->move_delay_reset_counter == 0)
13715 player->move_delay_reset_counter = 2; /* two double speed steps */
13717 DOUBLE_PLAYER_SPEED(player);
13720 PlayLevelSoundAction(x, y, ACTION_PASSING);
13722 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13726 if (mode != DF_SNAP)
13728 GfxElement[x][y] = GFX_ELEMENT(element);
13729 player->is_digging = TRUE;
13732 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13735 player->index_bit, dig_side);
13737 if (mode == DF_SNAP)
13739 if (level.block_snap_field)
13740 setFieldForSnapping(x, y, element, move_direction);
13742 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13745 player->index_bit, dig_side);
13748 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13752 if (is_player && mode != DF_SNAP)
13754 GfxElement[x][y] = element;
13755 player->is_collecting = TRUE;
13758 if (element == EL_SPEED_PILL)
13760 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13762 else if (element == EL_EXTRA_TIME && level.time > 0)
13764 TimeLeft += level.extra_time;
13766 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13768 DisplayGameControlValues();
13770 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13772 player->shield_normal_time_left += level.shield_normal_time;
13773 if (element == EL_SHIELD_DEADLY)
13774 player->shield_deadly_time_left += level.shield_deadly_time;
13776 else if (element == EL_DYNAMITE ||
13777 element == EL_EM_DYNAMITE ||
13778 element == EL_SP_DISK_RED)
13780 if (player->inventory_size < MAX_INVENTORY_SIZE)
13781 player->inventory_element[player->inventory_size++] = element;
13783 DrawGameDoorValues();
13785 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13787 player->dynabomb_count++;
13788 player->dynabombs_left++;
13790 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13792 player->dynabomb_size++;
13794 else if (element == EL_DYNABOMB_INCREASE_POWER)
13796 player->dynabomb_xl = TRUE;
13798 else if (IS_KEY(element))
13800 player->key[KEY_NR(element)] = TRUE;
13802 DrawGameDoorValues();
13804 else if (element == EL_DC_KEY_WHITE)
13806 player->num_white_keys++;
13808 /* display white keys? */
13809 /* DrawGameDoorValues(); */
13811 else if (IS_ENVELOPE(element))
13813 player->show_envelope = element;
13815 else if (element == EL_EMC_LENSES)
13817 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13819 RedrawAllInvisibleElementsForLenses();
13821 else if (element == EL_EMC_MAGNIFIER)
13823 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13825 RedrawAllInvisibleElementsForMagnifier();
13827 else if (IS_DROPPABLE(element) ||
13828 IS_THROWABLE(element)) /* can be collected and dropped */
13832 if (collect_count == 0)
13833 player->inventory_infinite_element = element;
13835 for (i = 0; i < collect_count; i++)
13836 if (player->inventory_size < MAX_INVENTORY_SIZE)
13837 player->inventory_element[player->inventory_size++] = element;
13839 DrawGameDoorValues();
13841 else if (collect_count > 0)
13843 local_player->gems_still_needed -= collect_count;
13844 if (local_player->gems_still_needed < 0)
13845 local_player->gems_still_needed = 0;
13847 game.snapshot.collected_item = TRUE;
13849 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13851 DisplayGameControlValues();
13854 RaiseScoreElement(element);
13855 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13858 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13859 player->index_bit, dig_side);
13861 if (mode == DF_SNAP)
13863 if (level.block_snap_field)
13864 setFieldForSnapping(x, y, element, move_direction);
13866 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13868 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13869 player->index_bit, dig_side);
13872 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13874 if (mode == DF_SNAP && element != EL_BD_ROCK)
13875 return MP_NO_ACTION;
13877 if (CAN_FALL(element) && dy)
13878 return MP_NO_ACTION;
13880 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13881 !(element == EL_SPRING && level.use_spring_bug))
13882 return MP_NO_ACTION;
13884 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13885 ((move_direction & MV_VERTICAL &&
13886 ((element_info[element].move_pattern & MV_LEFT &&
13887 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13888 (element_info[element].move_pattern & MV_RIGHT &&
13889 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13890 (move_direction & MV_HORIZONTAL &&
13891 ((element_info[element].move_pattern & MV_UP &&
13892 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13893 (element_info[element].move_pattern & MV_DOWN &&
13894 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13895 return MP_NO_ACTION;
13897 /* do not push elements already moving away faster than player */
13898 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13899 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13900 return MP_NO_ACTION;
13902 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13904 if (player->push_delay_value == -1 || !player_was_pushing)
13905 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13907 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13909 if (player->push_delay_value == -1)
13910 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13912 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13914 if (!player->is_pushing)
13915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13918 player->is_pushing = TRUE;
13919 player->is_active = TRUE;
13921 if (!(IN_LEV_FIELD(nextx, nexty) &&
13922 (IS_FREE(nextx, nexty) ||
13923 (IS_SB_ELEMENT(element) &&
13924 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13925 (IS_CUSTOM_ELEMENT(element) &&
13926 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13927 return MP_NO_ACTION;
13929 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13930 return MP_NO_ACTION;
13932 if (player->push_delay == -1) /* new pushing; restart delay */
13933 player->push_delay = 0;
13935 if (player->push_delay < player->push_delay_value &&
13936 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13937 element != EL_SPRING && element != EL_BALLOON)
13939 /* make sure that there is no move delay before next try to push */
13940 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13941 player->move_delay = 0;
13943 return MP_NO_ACTION;
13946 if (IS_CUSTOM_ELEMENT(element) &&
13947 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13949 if (!DigFieldByCE(nextx, nexty, element))
13950 return MP_NO_ACTION;
13953 if (IS_SB_ELEMENT(element))
13955 if (element == EL_SOKOBAN_FIELD_FULL)
13957 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13958 local_player->sokobanfields_still_needed++;
13961 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13963 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13964 local_player->sokobanfields_still_needed--;
13967 Feld[x][y] = EL_SOKOBAN_OBJECT;
13969 if (Back[x][y] == Back[nextx][nexty])
13970 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13971 else if (Back[x][y] != 0)
13972 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13975 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13978 if (local_player->sokobanfields_still_needed == 0 &&
13979 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13981 PlayerWins(player);
13983 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13987 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13989 InitMovingField(x, y, move_direction);
13990 GfxAction[x][y] = ACTION_PUSHING;
13992 if (mode == DF_SNAP)
13993 ContinueMoving(x, y);
13995 MovPos[x][y] = (dx != 0 ? dx : dy);
13997 Pushed[x][y] = TRUE;
13998 Pushed[nextx][nexty] = TRUE;
14000 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14001 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14003 player->push_delay_value = -1; /* get new value later */
14005 /* check for element change _after_ element has been pushed */
14006 if (game.use_change_when_pushing_bug)
14008 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14009 player->index_bit, dig_side);
14010 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14011 player->index_bit, dig_side);
14014 else if (IS_SWITCHABLE(element))
14016 if (PLAYER_SWITCHING(player, x, y))
14018 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14019 player->index_bit, dig_side);
14024 player->is_switching = TRUE;
14025 player->switch_x = x;
14026 player->switch_y = y;
14028 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14030 if (element == EL_ROBOT_WHEEL)
14032 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14036 game.robot_wheel_active = TRUE;
14038 TEST_DrawLevelField(x, y);
14040 else if (element == EL_SP_TERMINAL)
14044 SCAN_PLAYFIELD(xx, yy)
14046 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14050 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14052 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14054 ResetGfxAnimation(xx, yy);
14055 TEST_DrawLevelField(xx, yy);
14059 else if (IS_BELT_SWITCH(element))
14061 ToggleBeltSwitch(x, y);
14063 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14064 element == EL_SWITCHGATE_SWITCH_DOWN ||
14065 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14066 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14068 ToggleSwitchgateSwitch(x, y);
14070 else if (element == EL_LIGHT_SWITCH ||
14071 element == EL_LIGHT_SWITCH_ACTIVE)
14073 ToggleLightSwitch(x, y);
14075 else if (element == EL_TIMEGATE_SWITCH ||
14076 element == EL_DC_TIMEGATE_SWITCH)
14078 ActivateTimegateSwitch(x, y);
14080 else if (element == EL_BALLOON_SWITCH_LEFT ||
14081 element == EL_BALLOON_SWITCH_RIGHT ||
14082 element == EL_BALLOON_SWITCH_UP ||
14083 element == EL_BALLOON_SWITCH_DOWN ||
14084 element == EL_BALLOON_SWITCH_NONE ||
14085 element == EL_BALLOON_SWITCH_ANY)
14087 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14088 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14089 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14090 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14091 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14094 else if (element == EL_LAMP)
14096 Feld[x][y] = EL_LAMP_ACTIVE;
14097 local_player->lights_still_needed--;
14099 ResetGfxAnimation(x, y);
14100 TEST_DrawLevelField(x, y);
14102 else if (element == EL_TIME_ORB_FULL)
14104 Feld[x][y] = EL_TIME_ORB_EMPTY;
14106 if (level.time > 0 || level.use_time_orb_bug)
14108 TimeLeft += level.time_orb_time;
14109 game.no_time_limit = FALSE;
14111 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14113 DisplayGameControlValues();
14116 ResetGfxAnimation(x, y);
14117 TEST_DrawLevelField(x, y);
14119 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14120 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14124 game.ball_state = !game.ball_state;
14126 SCAN_PLAYFIELD(xx, yy)
14128 int e = Feld[xx][yy];
14130 if (game.ball_state)
14132 if (e == EL_EMC_MAGIC_BALL)
14133 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14134 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14135 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14139 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14140 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14141 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14142 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14147 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14148 player->index_bit, dig_side);
14150 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14151 player->index_bit, dig_side);
14153 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14154 player->index_bit, dig_side);
14160 if (!PLAYER_SWITCHING(player, x, y))
14162 player->is_switching = TRUE;
14163 player->switch_x = x;
14164 player->switch_y = y;
14166 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14167 player->index_bit, dig_side);
14168 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14169 player->index_bit, dig_side);
14171 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14172 player->index_bit, dig_side);
14173 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14174 player->index_bit, dig_side);
14177 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14178 player->index_bit, dig_side);
14179 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14180 player->index_bit, dig_side);
14182 return MP_NO_ACTION;
14185 player->push_delay = -1;
14187 if (is_player) /* function can also be called by EL_PENGUIN */
14189 if (Feld[x][y] != element) /* really digged/collected something */
14191 player->is_collecting = !player->is_digging;
14192 player->is_active = TRUE;
14199 static boolean DigFieldByCE(int x, int y, int digging_element)
14201 int element = Feld[x][y];
14203 if (!IS_FREE(x, y))
14205 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14206 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14209 /* no element can dig solid indestructible elements */
14210 if (IS_INDESTRUCTIBLE(element) &&
14211 !IS_DIGGABLE(element) &&
14212 !IS_COLLECTIBLE(element))
14215 if (AmoebaNr[x][y] &&
14216 (element == EL_AMOEBA_FULL ||
14217 element == EL_BD_AMOEBA ||
14218 element == EL_AMOEBA_GROWING))
14220 AmoebaCnt[AmoebaNr[x][y]]--;
14221 AmoebaCnt2[AmoebaNr[x][y]]--;
14224 if (IS_MOVING(x, y))
14225 RemoveMovingField(x, y);
14229 TEST_DrawLevelField(x, y);
14232 /* if digged element was about to explode, prevent the explosion */
14233 ExplodeField[x][y] = EX_TYPE_NONE;
14235 PlayLevelSoundAction(x, y, action);
14238 Store[x][y] = EL_EMPTY;
14240 /* this makes it possible to leave the removed element again */
14241 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14242 Store[x][y] = element;
14247 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14249 int jx = player->jx, jy = player->jy;
14250 int x = jx + dx, y = jy + dy;
14251 int snap_direction = (dx == -1 ? MV_LEFT :
14252 dx == +1 ? MV_RIGHT :
14254 dy == +1 ? MV_DOWN : MV_NONE);
14255 boolean can_continue_snapping = (level.continuous_snapping &&
14256 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14258 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14261 if (!player->active || !IN_LEV_FIELD(x, y))
14269 if (player->MovPos == 0)
14270 player->is_pushing = FALSE;
14272 player->is_snapping = FALSE;
14274 if (player->MovPos == 0)
14276 player->is_moving = FALSE;
14277 player->is_digging = FALSE;
14278 player->is_collecting = FALSE;
14284 /* prevent snapping with already pressed snap key when not allowed */
14285 if (player->is_snapping && !can_continue_snapping)
14288 player->MovDir = snap_direction;
14290 if (player->MovPos == 0)
14292 player->is_moving = FALSE;
14293 player->is_digging = FALSE;
14294 player->is_collecting = FALSE;
14297 player->is_dropping = FALSE;
14298 player->is_dropping_pressed = FALSE;
14299 player->drop_pressed_delay = 0;
14301 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14304 player->is_snapping = TRUE;
14305 player->is_active = TRUE;
14307 if (player->MovPos == 0)
14309 player->is_moving = FALSE;
14310 player->is_digging = FALSE;
14311 player->is_collecting = FALSE;
14314 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14315 TEST_DrawLevelField(player->last_jx, player->last_jy);
14317 TEST_DrawLevelField(x, y);
14322 static boolean DropElement(struct PlayerInfo *player)
14324 int old_element, new_element;
14325 int dropx = player->jx, dropy = player->jy;
14326 int drop_direction = player->MovDir;
14327 int drop_side = drop_direction;
14328 int drop_element = get_next_dropped_element(player);
14330 /* do not drop an element on top of another element; when holding drop key
14331 pressed without moving, dropped element must move away before the next
14332 element can be dropped (this is especially important if the next element
14333 is dynamite, which can be placed on background for historical reasons) */
14334 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14337 if (IS_THROWABLE(drop_element))
14339 dropx += GET_DX_FROM_DIR(drop_direction);
14340 dropy += GET_DY_FROM_DIR(drop_direction);
14342 if (!IN_LEV_FIELD(dropx, dropy))
14346 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14347 new_element = drop_element; /* default: no change when dropping */
14349 /* check if player is active, not moving and ready to drop */
14350 if (!player->active || player->MovPos || player->drop_delay > 0)
14353 /* check if player has anything that can be dropped */
14354 if (new_element == EL_UNDEFINED)
14357 /* only set if player has anything that can be dropped */
14358 player->is_dropping_pressed = TRUE;
14360 /* check if drop key was pressed long enough for EM style dynamite */
14361 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14364 /* check if anything can be dropped at the current position */
14365 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14368 /* collected custom elements can only be dropped on empty fields */
14369 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14372 if (old_element != EL_EMPTY)
14373 Back[dropx][dropy] = old_element; /* store old element on this field */
14375 ResetGfxAnimation(dropx, dropy);
14376 ResetRandomAnimationValue(dropx, dropy);
14378 if (player->inventory_size > 0 ||
14379 player->inventory_infinite_element != EL_UNDEFINED)
14381 if (player->inventory_size > 0)
14383 player->inventory_size--;
14385 DrawGameDoorValues();
14387 if (new_element == EL_DYNAMITE)
14388 new_element = EL_DYNAMITE_ACTIVE;
14389 else if (new_element == EL_EM_DYNAMITE)
14390 new_element = EL_EM_DYNAMITE_ACTIVE;
14391 else if (new_element == EL_SP_DISK_RED)
14392 new_element = EL_SP_DISK_RED_ACTIVE;
14395 Feld[dropx][dropy] = new_element;
14397 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14398 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14399 el2img(Feld[dropx][dropy]), 0);
14401 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14403 /* needed if previous element just changed to "empty" in the last frame */
14404 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14406 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14407 player->index_bit, drop_side);
14408 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14410 player->index_bit, drop_side);
14412 TestIfElementTouchesCustomElement(dropx, dropy);
14414 else /* player is dropping a dyna bomb */
14416 player->dynabombs_left--;
14418 Feld[dropx][dropy] = new_element;
14420 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14421 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14422 el2img(Feld[dropx][dropy]), 0);
14424 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14427 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14428 InitField_WithBug1(dropx, dropy, FALSE);
14430 new_element = Feld[dropx][dropy]; /* element might have changed */
14432 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14433 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14435 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14436 MovDir[dropx][dropy] = drop_direction;
14438 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14440 /* do not cause impact style collision by dropping elements that can fall */
14441 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14444 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14445 player->is_dropping = TRUE;
14447 player->drop_pressed_delay = 0;
14448 player->is_dropping_pressed = FALSE;
14450 player->drop_x = dropx;
14451 player->drop_y = dropy;
14456 /* ------------------------------------------------------------------------- */
14457 /* game sound playing functions */
14458 /* ------------------------------------------------------------------------- */
14460 static int *loop_sound_frame = NULL;
14461 static int *loop_sound_volume = NULL;
14463 void InitPlayLevelSound()
14465 int num_sounds = getSoundListSize();
14467 checked_free(loop_sound_frame);
14468 checked_free(loop_sound_volume);
14470 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14471 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14474 static void PlayLevelSound(int x, int y, int nr)
14476 int sx = SCREENX(x), sy = SCREENY(y);
14477 int volume, stereo_position;
14478 int max_distance = 8;
14479 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14481 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14482 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14485 if (!IN_LEV_FIELD(x, y) ||
14486 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14487 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14490 volume = SOUND_MAX_VOLUME;
14492 if (!IN_SCR_FIELD(sx, sy))
14494 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14495 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14497 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14500 stereo_position = (SOUND_MAX_LEFT +
14501 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14502 (SCR_FIELDX + 2 * max_distance));
14504 if (IS_LOOP_SOUND(nr))
14506 /* This assures that quieter loop sounds do not overwrite louder ones,
14507 while restarting sound volume comparison with each new game frame. */
14509 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14512 loop_sound_volume[nr] = volume;
14513 loop_sound_frame[nr] = FrameCounter;
14516 PlaySoundExt(nr, volume, stereo_position, type);
14519 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14521 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14522 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14523 y < LEVELY(BY1) ? LEVELY(BY1) :
14524 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14528 static void PlayLevelSoundAction(int x, int y, int action)
14530 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14533 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14535 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14537 if (sound_effect != SND_UNDEFINED)
14538 PlayLevelSound(x, y, sound_effect);
14541 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14544 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14546 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14547 PlayLevelSound(x, y, sound_effect);
14550 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14552 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14554 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14555 PlayLevelSound(x, y, sound_effect);
14558 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14560 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14562 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14563 StopSound(sound_effect);
14566 static int getLevelMusicNr()
14568 if (levelset.music[level_nr] != MUS_UNDEFINED)
14569 return levelset.music[level_nr]; /* from config file */
14571 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14574 static void FadeLevelSounds()
14579 static void FadeLevelMusic()
14581 int music_nr = getLevelMusicNr();
14582 char *curr_music = getCurrentlyPlayingMusicFilename();
14583 char *next_music = getMusicInfoEntryFilename(music_nr);
14585 if (!strEqual(curr_music, next_music))
14589 void FadeLevelSoundsAndMusic()
14595 static void PlayLevelMusic()
14597 int music_nr = getLevelMusicNr();
14598 char *curr_music = getCurrentlyPlayingMusicFilename();
14599 char *next_music = getMusicInfoEntryFilename(music_nr);
14601 if (!strEqual(curr_music, next_music))
14602 PlayMusic(music_nr);
14605 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14607 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14608 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14609 int x = xx - 1 - offset;
14610 int y = yy - 1 - offset;
14615 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14619 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14623 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14627 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14631 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14635 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14639 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14642 case SAMPLE_android_clone:
14643 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14646 case SAMPLE_android_move:
14647 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14650 case SAMPLE_spring:
14651 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14655 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14659 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14662 case SAMPLE_eater_eat:
14663 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14667 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14670 case SAMPLE_collect:
14671 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14674 case SAMPLE_diamond:
14675 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14678 case SAMPLE_squash:
14679 /* !!! CHECK THIS !!! */
14681 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14683 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14687 case SAMPLE_wonderfall:
14688 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14692 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14696 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14700 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14704 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14708 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14715 case SAMPLE_wonder:
14716 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14720 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14723 case SAMPLE_exit_open:
14724 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14727 case SAMPLE_exit_leave:
14728 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14731 case SAMPLE_dynamite:
14732 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14736 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14740 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14744 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14748 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14752 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14756 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14760 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14765 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14767 int element = map_element_SP_to_RND(element_sp);
14768 int action = map_action_SP_to_RND(action_sp);
14769 int offset = (setup.sp_show_border_elements ? 0 : 1);
14770 int x = xx - offset;
14771 int y = yy - offset;
14773 PlayLevelSoundElementAction(x, y, element, action);
14776 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14778 int element = map_element_MM_to_RND(element_mm);
14779 int action = map_action_MM_to_RND(action_mm);
14781 int x = xx - offset;
14782 int y = yy - offset;
14784 if (!IS_MM_ELEMENT(element))
14785 element = EL_MM_DEFAULT;
14787 PlayLevelSoundElementAction(x, y, element, action);
14790 void PlaySound_MM(int sound_mm)
14792 int sound = map_sound_MM_to_RND(sound_mm);
14794 if (sound == SND_UNDEFINED)
14800 void PlaySoundLoop_MM(int sound_mm)
14802 int sound = map_sound_MM_to_RND(sound_mm);
14804 if (sound == SND_UNDEFINED)
14807 PlaySoundLoop(sound);
14810 void StopSound_MM(int sound_mm)
14812 int sound = map_sound_MM_to_RND(sound_mm);
14814 if (sound == SND_UNDEFINED)
14820 void RaiseScore(int value)
14822 local_player->score += value;
14824 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14826 DisplayGameControlValues();
14829 void RaiseScoreElement(int element)
14834 case EL_BD_DIAMOND:
14835 case EL_EMERALD_YELLOW:
14836 case EL_EMERALD_RED:
14837 case EL_EMERALD_PURPLE:
14838 case EL_SP_INFOTRON:
14839 RaiseScore(level.score[SC_EMERALD]);
14842 RaiseScore(level.score[SC_DIAMOND]);
14845 RaiseScore(level.score[SC_CRYSTAL]);
14848 RaiseScore(level.score[SC_PEARL]);
14851 case EL_BD_BUTTERFLY:
14852 case EL_SP_ELECTRON:
14853 RaiseScore(level.score[SC_BUG]);
14856 case EL_BD_FIREFLY:
14857 case EL_SP_SNIKSNAK:
14858 RaiseScore(level.score[SC_SPACESHIP]);
14861 case EL_DARK_YAMYAM:
14862 RaiseScore(level.score[SC_YAMYAM]);
14865 RaiseScore(level.score[SC_ROBOT]);
14868 RaiseScore(level.score[SC_PACMAN]);
14871 RaiseScore(level.score[SC_NUT]);
14874 case EL_EM_DYNAMITE:
14875 case EL_SP_DISK_RED:
14876 case EL_DYNABOMB_INCREASE_NUMBER:
14877 case EL_DYNABOMB_INCREASE_SIZE:
14878 case EL_DYNABOMB_INCREASE_POWER:
14879 RaiseScore(level.score[SC_DYNAMITE]);
14881 case EL_SHIELD_NORMAL:
14882 case EL_SHIELD_DEADLY:
14883 RaiseScore(level.score[SC_SHIELD]);
14885 case EL_EXTRA_TIME:
14886 RaiseScore(level.extra_time_score);
14900 case EL_DC_KEY_WHITE:
14901 RaiseScore(level.score[SC_KEY]);
14904 RaiseScore(element_info[element].collect_score);
14909 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14911 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14913 /* closing door required in case of envelope style request dialogs */
14915 CloseDoor(DOOR_CLOSE_1);
14917 #if defined(NETWORK_AVALIABLE)
14918 if (options.network)
14919 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14924 FadeSkipNextFadeIn();
14926 SetGameStatus(GAME_MODE_MAIN);
14931 else /* continue playing the game */
14933 if (tape.playing && tape.deactivate_display)
14934 TapeDeactivateDisplayOff(TRUE);
14936 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14938 if (tape.playing && tape.deactivate_display)
14939 TapeDeactivateDisplayOn();
14943 void RequestQuitGame(boolean ask_if_really_quit)
14945 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14946 boolean skip_request = AllPlayersGone || quick_quit;
14948 RequestQuitGameExt(skip_request, quick_quit,
14949 "Do you really want to quit the game?");
14953 /* ------------------------------------------------------------------------- */
14954 /* random generator functions */
14955 /* ------------------------------------------------------------------------- */
14957 unsigned int InitEngineRandom_RND(int seed)
14959 game.num_random_calls = 0;
14961 return InitEngineRandom(seed);
14964 unsigned int RND(int max)
14968 game.num_random_calls++;
14970 return GetEngineRandom(max);
14977 /* ------------------------------------------------------------------------- */
14978 /* game engine snapshot handling functions */
14979 /* ------------------------------------------------------------------------- */
14981 struct EngineSnapshotInfo
14983 /* runtime values for custom element collect score */
14984 int collect_score[NUM_CUSTOM_ELEMENTS];
14986 /* runtime values for group element choice position */
14987 int choice_pos[NUM_GROUP_ELEMENTS];
14989 /* runtime values for belt position animations */
14990 int belt_graphic[4][NUM_BELT_PARTS];
14991 int belt_anim_mode[4][NUM_BELT_PARTS];
14994 static struct EngineSnapshotInfo engine_snapshot_rnd;
14995 static char *snapshot_level_identifier = NULL;
14996 static int snapshot_level_nr = -1;
14998 static void SaveEngineSnapshotValues_RND()
15000 static int belt_base_active_element[4] =
15002 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15003 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15004 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15005 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15011 int element = EL_CUSTOM_START + i;
15013 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15016 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15018 int element = EL_GROUP_START + i;
15020 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15023 for (i = 0; i < 4; i++)
15025 for (j = 0; j < NUM_BELT_PARTS; j++)
15027 int element = belt_base_active_element[i] + j;
15028 int graphic = el2img(element);
15029 int anim_mode = graphic_info[graphic].anim_mode;
15031 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15032 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15037 static void LoadEngineSnapshotValues_RND()
15039 unsigned int num_random_calls = game.num_random_calls;
15042 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15044 int element = EL_CUSTOM_START + i;
15046 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15049 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15051 int element = EL_GROUP_START + i;
15053 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15056 for (i = 0; i < 4; i++)
15058 for (j = 0; j < NUM_BELT_PARTS; j++)
15060 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15061 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15063 graphic_info[graphic].anim_mode = anim_mode;
15067 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15069 InitRND(tape.random_seed);
15070 for (i = 0; i < num_random_calls; i++)
15074 if (game.num_random_calls != num_random_calls)
15076 Error(ERR_INFO, "number of random calls out of sync");
15077 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15078 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15079 Error(ERR_EXIT, "this should not happen -- please debug");
15083 void FreeEngineSnapshotSingle()
15085 FreeSnapshotSingle();
15087 setString(&snapshot_level_identifier, NULL);
15088 snapshot_level_nr = -1;
15091 void FreeEngineSnapshotList()
15093 FreeSnapshotList();
15096 ListNode *SaveEngineSnapshotBuffers()
15098 ListNode *buffers = NULL;
15100 /* copy some special values to a structure better suited for the snapshot */
15102 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15103 SaveEngineSnapshotValues_RND();
15104 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15105 SaveEngineSnapshotValues_EM();
15106 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15107 SaveEngineSnapshotValues_SP(&buffers);
15108 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15109 SaveEngineSnapshotValues_MM(&buffers);
15111 /* save values stored in special snapshot structure */
15113 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15114 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15115 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15116 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15117 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15119 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15122 /* save further RND engine values */
15124 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15125 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15126 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15128 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15129 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15130 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15131 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15133 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15134 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15135 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15136 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15137 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15139 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15140 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15148 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15150 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15154 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15159 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15165 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15169 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15176 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15189 ListNode *node = engine_snapshot_list_rnd;
15192 while (node != NULL)
15194 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15199 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15205 void SaveEngineSnapshotSingle()
15207 ListNode *buffers = SaveEngineSnapshotBuffers();
15209 /* finally save all snapshot buffers to single snapshot */
15210 SaveSnapshotSingle(buffers);
15212 /* save level identification information */
15213 setString(&snapshot_level_identifier, leveldir_current->identifier);
15214 snapshot_level_nr = level_nr;
15217 boolean CheckSaveEngineSnapshotToList()
15219 boolean save_snapshot =
15220 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15221 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15222 game.snapshot.changed_action) ||
15223 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15224 game.snapshot.collected_item));
15226 game.snapshot.changed_action = FALSE;
15227 game.snapshot.collected_item = FALSE;
15228 game.snapshot.save_snapshot = save_snapshot;
15230 return save_snapshot;
15233 void SaveEngineSnapshotToList()
15235 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15239 ListNode *buffers = SaveEngineSnapshotBuffers();
15241 /* finally save all snapshot buffers to snapshot list */
15242 SaveSnapshotToList(buffers);
15245 void SaveEngineSnapshotToListInitial()
15247 FreeEngineSnapshotList();
15249 SaveEngineSnapshotToList();
15252 void LoadEngineSnapshotValues()
15254 /* restore special values from snapshot structure */
15256 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15257 LoadEngineSnapshotValues_RND();
15258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15259 LoadEngineSnapshotValues_EM();
15260 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15261 LoadEngineSnapshotValues_SP();
15262 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15263 LoadEngineSnapshotValues_MM();
15266 void LoadEngineSnapshotSingle()
15268 LoadSnapshotSingle();
15270 LoadEngineSnapshotValues();
15273 void LoadEngineSnapshot_Undo(int steps)
15275 LoadSnapshotFromList_Older(steps);
15277 LoadEngineSnapshotValues();
15280 void LoadEngineSnapshot_Redo(int steps)
15282 LoadSnapshotFromList_Newer(steps);
15284 LoadEngineSnapshotValues();
15287 boolean CheckEngineSnapshotSingle()
15289 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15290 snapshot_level_nr == level_nr);
15293 boolean CheckEngineSnapshotList()
15295 return CheckSnapshotList();
15299 /* ---------- new game button stuff ---------------------------------------- */
15307 } gamebutton_info[NUM_GAME_BUTTONS] =
15310 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15311 GAME_CTRL_ID_STOP, "stop game"
15314 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15315 GAME_CTRL_ID_PAUSE, "pause game"
15318 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15319 GAME_CTRL_ID_PLAY, "play game"
15322 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15323 GAME_CTRL_ID_UNDO, "undo step"
15326 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15327 GAME_CTRL_ID_REDO, "redo step"
15330 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15331 GAME_CTRL_ID_SAVE, "save game"
15334 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15335 GAME_CTRL_ID_PAUSE2, "pause game"
15338 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15339 GAME_CTRL_ID_LOAD, "load game"
15342 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15343 SOUND_CTRL_ID_MUSIC, "background music on/off"
15346 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15347 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15350 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15351 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15355 void CreateGameButtons()
15359 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15361 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15362 struct XY *pos = gamebutton_info[i].pos;
15363 struct GadgetInfo *gi;
15366 unsigned int event_mask;
15367 int base_x = (tape.show_game_buttons ? VX : DX);
15368 int base_y = (tape.show_game_buttons ? VY : DY);
15369 int gd_x = gfx->src_x;
15370 int gd_y = gfx->src_y;
15371 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15372 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15373 int gd_xa = gfx->src_x + gfx->active_xoffset;
15374 int gd_ya = gfx->src_y + gfx->active_yoffset;
15375 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15376 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15379 if (gfx->bitmap == NULL)
15381 game_gadget[id] = NULL;
15386 if (id == GAME_CTRL_ID_STOP ||
15387 id == GAME_CTRL_ID_PLAY ||
15388 id == GAME_CTRL_ID_SAVE ||
15389 id == GAME_CTRL_ID_LOAD)
15391 button_type = GD_TYPE_NORMAL_BUTTON;
15393 event_mask = GD_EVENT_RELEASED;
15395 else if (id == GAME_CTRL_ID_UNDO ||
15396 id == GAME_CTRL_ID_REDO)
15398 button_type = GD_TYPE_NORMAL_BUTTON;
15400 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15404 button_type = GD_TYPE_CHECK_BUTTON;
15406 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15407 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15408 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15409 event_mask = GD_EVENT_PRESSED;
15412 gi = CreateGadget(GDI_CUSTOM_ID, id,
15413 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15414 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15415 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15416 GDI_WIDTH, gfx->width,
15417 GDI_HEIGHT, gfx->height,
15418 GDI_TYPE, button_type,
15419 GDI_STATE, GD_BUTTON_UNPRESSED,
15420 GDI_CHECKED, checked,
15421 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15422 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15423 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15424 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15425 GDI_DIRECT_DRAW, FALSE,
15426 GDI_EVENT_MASK, event_mask,
15427 GDI_CALLBACK_ACTION, HandleGameButtons,
15431 Error(ERR_EXIT, "cannot create gadget");
15433 game_gadget[id] = gi;
15437 void FreeGameButtons()
15441 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15442 FreeGadget(game_gadget[i]);
15445 static void UnmapGameButtonsAtSamePosition(int id)
15449 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15451 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15452 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15453 UnmapGadget(game_gadget[i]);
15456 static void UnmapGameButtonsAtSamePosition_All()
15458 if (setup.show_snapshot_buttons)
15460 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15461 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15462 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15466 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15467 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15468 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15472 static void MapGameButtonsAtSamePosition(int id)
15476 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15478 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15479 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15480 MapGadget(game_gadget[i]);
15482 UnmapGameButtonsAtSamePosition_All();
15485 void MapUndoRedoButtons()
15487 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15488 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15490 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15491 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15493 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15496 void UnmapUndoRedoButtons()
15498 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15499 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15501 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15502 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15504 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15507 void MapGameButtons()
15511 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15512 if (i != GAME_CTRL_ID_UNDO &&
15513 i != GAME_CTRL_ID_REDO)
15514 MapGadget(game_gadget[i]);
15516 UnmapGameButtonsAtSamePosition_All();
15518 RedrawGameButtons();
15521 void UnmapGameButtons()
15525 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15526 UnmapGadget(game_gadget[i]);
15529 void RedrawGameButtons()
15533 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15534 RedrawGadget(game_gadget[i]);
15536 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15537 redraw_mask &= ~REDRAW_ALL;
15540 void GameUndoRedoExt()
15542 ClearPlayerAction();
15544 tape.pausing = TRUE;
15547 UpdateAndDisplayGameControlValues();
15549 DrawCompleteVideoDisplay();
15550 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15551 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15552 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15557 void GameUndo(int steps)
15559 if (!CheckEngineSnapshotList())
15562 LoadEngineSnapshot_Undo(steps);
15567 void GameRedo(int steps)
15569 if (!CheckEngineSnapshotList())
15572 LoadEngineSnapshot_Redo(steps);
15577 static void HandleGameButtonsExt(int id, int button)
15579 static boolean game_undo_executed = FALSE;
15580 int steps = BUTTON_STEPSIZE(button);
15581 boolean handle_game_buttons =
15582 (game_status == GAME_MODE_PLAYING ||
15583 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15585 if (!handle_game_buttons)
15590 case GAME_CTRL_ID_STOP:
15591 if (game_status == GAME_MODE_MAIN)
15597 RequestQuitGame(TRUE);
15601 case GAME_CTRL_ID_PAUSE:
15602 case GAME_CTRL_ID_PAUSE2:
15603 if (options.network && game_status == GAME_MODE_PLAYING)
15605 #if defined(NETWORK_AVALIABLE)
15607 SendToServer_ContinuePlaying();
15609 SendToServer_PausePlaying();
15613 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15615 game_undo_executed = FALSE;
15619 case GAME_CTRL_ID_PLAY:
15620 if (game_status == GAME_MODE_MAIN)
15622 StartGameActions(options.network, setup.autorecord, level.random_seed);
15624 else if (tape.pausing)
15626 #if defined(NETWORK_AVALIABLE)
15627 if (options.network)
15628 SendToServer_ContinuePlaying();
15631 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15635 case GAME_CTRL_ID_UNDO:
15636 // Important: When using "save snapshot when collecting an item" mode,
15637 // load last (current) snapshot for first "undo" after pressing "pause"
15638 // (else the last-but-one snapshot would be loaded, because the snapshot
15639 // pointer already points to the last snapshot when pressing "pause",
15640 // which is fine for "every step/move" mode, but not for "every collect")
15641 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15642 !game_undo_executed)
15645 game_undo_executed = TRUE;
15650 case GAME_CTRL_ID_REDO:
15654 case GAME_CTRL_ID_SAVE:
15658 case GAME_CTRL_ID_LOAD:
15662 case SOUND_CTRL_ID_MUSIC:
15663 if (setup.sound_music)
15665 setup.sound_music = FALSE;
15669 else if (audio.music_available)
15671 setup.sound = setup.sound_music = TRUE;
15673 SetAudioMode(setup.sound);
15679 case SOUND_CTRL_ID_LOOPS:
15680 if (setup.sound_loops)
15681 setup.sound_loops = FALSE;
15682 else if (audio.loops_available)
15684 setup.sound = setup.sound_loops = TRUE;
15686 SetAudioMode(setup.sound);
15690 case SOUND_CTRL_ID_SIMPLE:
15691 if (setup.sound_simple)
15692 setup.sound_simple = FALSE;
15693 else if (audio.sound_available)
15695 setup.sound = setup.sound_simple = TRUE;
15697 SetAudioMode(setup.sound);
15706 static void HandleGameButtons(struct GadgetInfo *gi)
15708 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15711 void HandleSoundButtonKeys(Key key)
15714 if (key == setup.shortcut.sound_simple)
15715 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15716 else if (key == setup.shortcut.sound_loops)
15717 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15718 else if (key == setup.shortcut.sound_music)
15719 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);