1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
91 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
96 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p) ((p).y)
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME (PANEL_XPOS(game.panel.time))
117 #define YY_TIME (PANEL_YPOS(game.panel.time))
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1 (DX + XX_LEVEL1)
121 #define DX_LEVEL2 (DX + XX_LEVEL2)
122 #define DX_LEVEL (DX + XX_LEVEL)
123 #define DY_LEVEL (DY + YY_LEVEL)
124 #define DX_EMERALDS (DX + XX_EMERALDS)
125 #define DY_EMERALDS (DY + YY_EMERALDS)
126 #define DX_DYNAMITE (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE (DY + YY_DYNAMITE)
128 #define DX_KEYS (DX + XX_KEYS)
129 #define DY_KEYS (DY + YY_KEYS)
130 #define DX_SCORE (DX + XX_SCORE)
131 #define DY_SCORE (DY + YY_SCORE)
132 #define DX_TIME1 (DX + XX_TIME1)
133 #define DX_TIME2 (DX + XX_TIME2)
134 #define DX_TIME (DX + XX_TIME)
135 #define DY_TIME (DY + YY_TIME)
138 /* game panel display and control definitions */
140 #define GAME_CONTROL_LEVEL 0
141 #define GAME_CONTROL_GEMS 1
142 #define GAME_CONTROL_INVENTORY 2
143 #define GAME_CONTROL_KEYS 3
144 #define GAME_CONTROL_SCORE 4
145 #define GAME_CONTROL_TIME 5
149 #define GAME_CONTROL_LEVELS 1
150 #define GAME_CONTROL_SCORES 2
151 #define GAME_CONTROL_EDITOR 3
152 #define GAME_CONTROL_INFO 4
153 #define GAME_CONTROL_GAME 5
154 #define GAME_CONTROL_SETUP 6
155 #define GAME_CONTROL_QUIT 7
156 #define GAME_CONTROL_PREV_LEVEL 8
157 #define GAME_CONTROL_NEXT_LEVEL 9
158 #define GAME_CONTROL_CURRENT_LEVEL 10
159 #define GAME_CONTROL_FIRST_LEVEL 11
160 #define GAME_CONTROL_LAST_LEVEL 12
161 #define GAME_CONTROL_LEVEL_INFO_1 13
162 #define GAME_CONTROL_LEVEL_INFO_2 14
163 #define GAME_CONTROL_LEVEL_NAME 15
164 #define GAME_CONTROL_LEVEL_AUTHOR 16
165 #define GAME_CONTROL_LEVEL_YEAR 17
166 #define GAME_CONTROL_LEVEL_IMPORTED_FROM 18
167 #define GAME_CONTROL_LEVEL_IMPORTED_BY 19
168 #define GAME_CONTROL_LEVEL_TESTED_BY 20
169 #define GAME_CONTROL_TITLE_1 21
170 #define GAME_CONTROL_TITLE_2 22
171 #define GAME_CONTROL_TITLE_3 23
173 static char str_game_text_name[10];
174 static char str_game_text_current_level[10];
175 static char str_game_text_first_level[10];
176 static char str_game_text_last_level[10];
178 static char *game_text_name = str_game_text_name;
179 static char *game_text_current_level = str_game_text_current_level;
180 static char *game_text_first_level = str_game_text_first_level;
181 static char *game_text_last_level = str_game_text_last_level;
182 static char *game_text_levels = "Levelset";
183 static char *game_text_scores = "Hall Of Fame";
184 static char *game_text_editor = "Level Creator";
185 static char *game_text_info = "Info Screen";
186 static char *game_text_game = "Start Game";
187 static char *game_text_setup = "Setup";
188 static char *game_text_quit = "Quit";
189 static char *game_text_level_name = level.name;
190 static char *game_text_level_author = level.author;
191 static char *game_text_level_year = NULL;
192 static char *game_text_level_imported_from = NULL;
193 static char *game_text_level_imported_by = NULL;
194 static char *game_text_level_tested_by = NULL;
195 static char *game_text_title_1 = PROGRAM_TITLE_STRING;
196 static char *game_text_title_2 = PROGRAM_COPYRIGHT_STRING;
197 static char *game_text_title_3 = PROGRAM_GAME_BY_STRING;
199 struct GameControlInfo
203 struct MenuPosInfo *pos_button;
206 struct TextPosInfo *pos_text;
209 struct TextPosInfo *pos_input;
213 static struct GameControlInfo game_controls[] =
217 &menu.game.button.name, IMG_MENU_BUTTON,
218 &menu.game.text.name, &game_text_name,
219 &menu.game.input.name, &setup.player_name,
223 &menu.game.button.levels, IMG_MENU_BUTTON_ENTER_MENU,
224 &menu.game.text.levels, &game_text_levels,
229 &menu.game.button.scores, IMG_MENU_BUTTON,
230 &menu.game.text.scores, &game_text_scores,
235 &menu.game.button.editor, IMG_MENU_BUTTON,
236 &menu.game.text.editor, &game_text_editor,
241 &menu.game.button.info, IMG_MENU_BUTTON_ENTER_MENU,
242 &menu.game.text.info, &game_text_info,
247 &menu.game.button.game, IMG_MENU_BUTTON,
248 &menu.game.text.game, &game_text_game,
253 &menu.game.button.setup, IMG_MENU_BUTTON_ENTER_MENU,
254 &menu.game.text.setup, &game_text_setup,
259 &menu.game.button.quit, IMG_MENU_BUTTON,
260 &menu.game.text.quit, &game_text_quit,
264 /* (these two buttons are real gadgets) */
266 GAME_CONTROL_PREV_LEVEL,
267 &menu.game.button.prev_level, IMG_MENU_BUTTON_PREV_LEVEL,
272 GAME_CONTROL_NEXT_LEVEL,
273 &menu.game.button.next_level, IMG_MENU_BUTTON_NEXT_LEVEL,
279 GAME_CONTROL_CURRENT_LEVEL,
281 &menu.game.text.current_level, &game_text_current_level,
285 GAME_CONTROL_FIRST_LEVEL,
287 &menu.game.text.first_level, &game_text_first_level,
291 GAME_CONTROL_LAST_LEVEL,
293 &menu.game.text.last_level, &game_text_last_level,
297 GAME_CONTROL_LEVEL_INFO_1,
299 &menu.game.text.level_info_1, NULL,
303 GAME_CONTROL_LEVEL_INFO_2,
305 &menu.game.text.level_info_2, NULL,
309 GAME_CONTROL_LEVEL_NAME,
311 &menu.game.text.level_name, &game_text_level_name,
315 GAME_CONTROL_LEVEL_AUTHOR,
317 &menu.game.text.level_author, &game_text_level_author,
321 GAME_CONTROL_LEVEL_YEAR,
323 &menu.game.text.level_year, &game_text_level_year,
327 GAME_CONTROL_LEVEL_IMPORTED_FROM,
329 &menu.game.text.level_imported_from, &game_text_level_imported_from,
333 GAME_CONTROL_LEVEL_IMPORTED_BY,
335 &menu.game.text.level_imported_by, &game_text_level_imported_by,
339 GAME_CONTROL_LEVEL_TESTED_BY,
341 &menu.game.text.level_tested_by, &game_text_level_tested_by,
345 GAME_CONTROL_TITLE_1,
347 &menu.game.text.title_1, &game_text_title_1,
351 GAME_CONTROL_TITLE_2,
353 &menu.game.text.title_2, &game_text_title_2,
357 GAME_CONTROL_TITLE_3,
359 &menu.game.text.title_3, &game_text_title_3,
373 /* values for delayed check of falling and moving elements and for collision */
374 #define CHECK_DELAY_MOVING 3
375 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
376 #define CHECK_DELAY_COLLISION 2
377 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
379 /* values for initial player move delay (initial delay counter value) */
380 #define INITIAL_MOVE_DELAY_OFF -1
381 #define INITIAL_MOVE_DELAY_ON 0
383 /* values for player movement speed (which is in fact a delay value) */
384 #define MOVE_DELAY_MIN_SPEED 32
385 #define MOVE_DELAY_NORMAL_SPEED 8
386 #define MOVE_DELAY_HIGH_SPEED 4
387 #define MOVE_DELAY_MAX_SPEED 1
389 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
390 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
392 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
393 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
395 /* values for other actions */
396 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
397 #define MOVE_STEPSIZE_MIN (1)
398 #define MOVE_STEPSIZE_MAX (TILEX)
400 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
401 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
403 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
405 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
406 RND(element_info[e].push_delay_random))
407 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
408 RND(element_info[e].drop_delay_random))
409 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
410 RND(element_info[e].move_delay_random))
411 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
412 (element_info[e].move_delay_random))
413 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
414 RND(element_info[e].ce_value_random_initial))
415 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
416 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
417 RND((c)->delay_random * (c)->delay_frames))
418 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
419 RND((c)->delay_random))
422 #define GET_VALID_RUNTIME_ELEMENT(e) \
423 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
425 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
426 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
427 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
428 (be) + (e) - EL_SELF)
430 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
431 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
432 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
433 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
434 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
435 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
436 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
437 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
438 RESOLVED_REFERENCE_ELEMENT(be, e) : \
441 #define CAN_GROW_INTO(e) \
442 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
444 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
445 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
448 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
449 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
450 (CAN_MOVE_INTO_ACID(e) && \
451 Feld[x][y] == EL_ACID) || \
454 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
455 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
456 (CAN_MOVE_INTO_ACID(e) && \
457 Feld[x][y] == EL_ACID) || \
460 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
461 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
463 (CAN_MOVE_INTO_ACID(e) && \
464 Feld[x][y] == EL_ACID) || \
465 (DONT_COLLIDE_WITH(e) && \
467 !PLAYER_ENEMY_PROTECTED(x, y))))
469 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
470 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
472 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
473 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
475 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
476 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
478 #define ANDROID_CAN_CLONE_FIELD(x, y) \
479 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
480 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
482 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
483 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
485 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
486 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
488 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
489 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
491 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
492 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
494 #define PIG_CAN_ENTER_FIELD(e, x, y) \
495 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
497 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
498 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
499 Feld[x][y] == EL_EM_EXIT_OPEN || \
500 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
501 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
502 IS_FOOD_PENGUIN(Feld[x][y])))
503 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
504 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
506 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
507 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
509 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
510 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
512 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
513 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
514 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
516 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
518 #define CE_ENTER_FIELD_COND(e, x, y) \
519 (!IS_PLAYER(x, y) && \
520 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
522 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
523 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
525 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
526 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
528 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
529 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
530 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
531 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
533 /* game button identifiers */
534 #define GAME_CTRL_ID_STOP 0
535 #define GAME_CTRL_ID_PAUSE 1
536 #define GAME_CTRL_ID_PLAY 2
537 #define SOUND_CTRL_ID_MUSIC 3
538 #define SOUND_CTRL_ID_LOOPS 4
539 #define SOUND_CTRL_ID_SIMPLE 5
541 #define NUM_GAME_BUTTONS 6
544 /* forward declaration for internal use */
546 static void CreateField(int, int, int);
548 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
549 static void AdvanceFrameAndPlayerCounters(int);
551 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
552 static boolean MovePlayer(struct PlayerInfo *, int, int);
553 static void ScrollPlayer(struct PlayerInfo *, int);
554 static void ScrollScreen(struct PlayerInfo *, int);
556 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
558 static void InitBeltMovement(void);
559 static void CloseAllOpenTimegates(void);
560 static void CheckGravityMovement(struct PlayerInfo *);
561 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
562 static void KillPlayerUnlessEnemyProtected(int, int);
563 static void KillPlayerUnlessExplosionProtected(int, int);
565 static void TestIfPlayerTouchesCustomElement(int, int);
566 static void TestIfElementTouchesCustomElement(int, int);
567 static void TestIfElementHitsCustomElement(int, int, int);
569 static void TestIfElementSmashesCustomElement(int, int, int);
572 static void HandleElementChange(int, int, int);
573 static void ExecuteCustomElementAction(int, int, int, int);
574 static boolean ChangeElement(int, int, int, int);
576 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
577 #define CheckTriggeredElementChange(x, y, e, ev) \
578 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
579 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
580 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
581 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
582 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
583 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
584 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
586 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
587 #define CheckElementChange(x, y, e, te, ev) \
588 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
589 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
590 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
591 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
592 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
594 static void PlayLevelSound(int, int, int);
595 static void PlayLevelSoundNearest(int, int, int);
596 static void PlayLevelSoundAction(int, int, int);
597 static void PlayLevelSoundElementAction(int, int, int, int);
598 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
599 static void PlayLevelSoundActionIfLoop(int, int, int);
600 static void StopLevelSoundActionIfLoop(int, int, int);
601 static void PlayLevelMusic();
603 static void MapGameButtons();
604 static void HandleGameButtons(struct GadgetInfo *);
606 int AmoebeNachbarNr(int, int);
607 void AmoebeUmwandeln(int, int);
608 void ContinueMoving(int, int);
610 void InitMovDir(int, int);
611 void InitAmoebaNr(int, int);
612 int NewHiScore(void);
614 void TestIfGoodThingHitsBadThing(int, int, int);
615 void TestIfBadThingHitsGoodThing(int, int, int);
616 void TestIfPlayerTouchesBadThing(int, int);
617 void TestIfPlayerRunsIntoBadThing(int, int, int);
618 void TestIfBadThingTouchesPlayer(int, int);
619 void TestIfBadThingRunsIntoPlayer(int, int, int);
620 void TestIfFriendTouchesBadThing(int, int);
621 void TestIfBadThingTouchesFriend(int, int);
622 void TestIfBadThingTouchesOtherBadThing(int, int);
624 void KillPlayer(struct PlayerInfo *);
625 void BuryPlayer(struct PlayerInfo *);
626 void RemovePlayer(struct PlayerInfo *);
628 boolean SnapField(struct PlayerInfo *, int, int);
629 boolean DropElement(struct PlayerInfo *);
631 static int getInvisibleActiveFromInvisibleElement(int);
632 static int getInvisibleFromInvisibleActiveElement(int);
634 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
636 /* for detection of endless loops, caused by custom element programming */
637 /* (using maximal playfield width x 10 is just a rough approximation) */
638 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
640 #define RECURSION_LOOP_DETECTION_START(e, rc) \
642 if (recursion_loop_detected) \
645 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
647 recursion_loop_detected = TRUE; \
648 recursion_loop_element = (e); \
651 recursion_loop_depth++; \
654 #define RECURSION_LOOP_DETECTION_END() \
656 recursion_loop_depth--; \
659 static int recursion_loop_depth;
660 static boolean recursion_loop_detected;
661 static boolean recursion_loop_element;
664 /* ------------------------------------------------------------------------- */
665 /* definition of elements that automatically change to other elements after */
666 /* a specified time, eventually calling a function when changing */
667 /* ------------------------------------------------------------------------- */
669 /* forward declaration for changer functions */
670 static void InitBuggyBase(int, int);
671 static void WarnBuggyBase(int, int);
673 static void InitTrap(int, int);
674 static void ActivateTrap(int, int);
675 static void ChangeActiveTrap(int, int);
677 static void InitRobotWheel(int, int);
678 static void RunRobotWheel(int, int);
679 static void StopRobotWheel(int, int);
681 static void InitTimegateWheel(int, int);
682 static void RunTimegateWheel(int, int);
684 static void InitMagicBallDelay(int, int);
685 static void ActivateMagicBall(int, int);
687 struct ChangingElementInfo
692 void (*pre_change_function)(int x, int y);
693 void (*change_function)(int x, int y);
694 void (*post_change_function)(int x, int y);
697 static struct ChangingElementInfo change_delay_list[] =
732 EL_STEEL_EXIT_OPENING,
740 EL_STEEL_EXIT_CLOSING,
741 EL_STEEL_EXIT_CLOSED,
768 EL_EM_STEEL_EXIT_OPENING,
769 EL_EM_STEEL_EXIT_OPEN,
776 EL_EM_STEEL_EXIT_CLOSING,
780 EL_EM_STEEL_EXIT_CLOSED,
804 EL_SWITCHGATE_OPENING,
812 EL_SWITCHGATE_CLOSING,
813 EL_SWITCHGATE_CLOSED,
845 EL_ACID_SPLASH_RIGHT,
854 EL_SP_BUGGY_BASE_ACTIVATING,
861 EL_SP_BUGGY_BASE_ACTIVATING,
862 EL_SP_BUGGY_BASE_ACTIVE,
869 EL_SP_BUGGY_BASE_ACTIVE,
893 EL_ROBOT_WHEEL_ACTIVE,
901 EL_TIMEGATE_SWITCH_ACTIVE,
909 EL_DC_TIMEGATE_SWITCH_ACTIVE,
910 EL_DC_TIMEGATE_SWITCH,
917 EL_EMC_MAGIC_BALL_ACTIVE,
918 EL_EMC_MAGIC_BALL_ACTIVE,
925 EL_EMC_SPRING_BUMPER_ACTIVE,
926 EL_EMC_SPRING_BUMPER,
933 EL_DIAGONAL_SHRINKING,
962 int push_delay_fixed, push_delay_random;
967 { EL_BALLOON, 0, 0 },
969 { EL_SOKOBAN_OBJECT, 2, 0 },
970 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
971 { EL_SATELLITE, 2, 0 },
972 { EL_SP_DISK_YELLOW, 2, 0 },
974 { EL_UNDEFINED, 0, 0 },
982 move_stepsize_list[] =
984 { EL_AMOEBA_DROP, 2 },
985 { EL_AMOEBA_DROPPING, 2 },
986 { EL_QUICKSAND_FILLING, 1 },
987 { EL_QUICKSAND_EMPTYING, 1 },
988 { EL_QUICKSAND_FAST_FILLING, 2 },
989 { EL_QUICKSAND_FAST_EMPTYING, 2 },
990 { EL_MAGIC_WALL_FILLING, 2 },
991 { EL_MAGIC_WALL_EMPTYING, 2 },
992 { EL_BD_MAGIC_WALL_FILLING, 2 },
993 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
994 { EL_DC_MAGIC_WALL_FILLING, 2 },
995 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1005 collect_count_list[] =
1008 { EL_BD_DIAMOND, 1 },
1009 { EL_EMERALD_YELLOW, 1 },
1010 { EL_EMERALD_RED, 1 },
1011 { EL_EMERALD_PURPLE, 1 },
1013 { EL_SP_INFOTRON, 1 },
1017 { EL_UNDEFINED, 0 },
1025 access_direction_list[] =
1027 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1028 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1029 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1030 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1031 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1032 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1033 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1034 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1035 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1036 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1037 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1039 { EL_SP_PORT_LEFT, MV_RIGHT },
1040 { EL_SP_PORT_RIGHT, MV_LEFT },
1041 { EL_SP_PORT_UP, MV_DOWN },
1042 { EL_SP_PORT_DOWN, MV_UP },
1043 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1044 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1045 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1046 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1047 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1048 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1049 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1050 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1051 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1052 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1053 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1054 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1055 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1056 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1057 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1059 { EL_UNDEFINED, MV_NONE }
1062 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1064 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1065 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1066 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1067 IS_JUST_CHANGING(x, y))
1069 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1071 /* static variables for playfield scan mode (scanning forward or backward) */
1072 static int playfield_scan_start_x = 0;
1073 static int playfield_scan_start_y = 0;
1074 static int playfield_scan_delta_x = 1;
1075 static int playfield_scan_delta_y = 1;
1077 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1078 (y) >= 0 && (y) <= lev_fieldy - 1; \
1079 (y) += playfield_scan_delta_y) \
1080 for ((x) = playfield_scan_start_x; \
1081 (x) >= 0 && (x) <= lev_fieldx - 1; \
1082 (x) += playfield_scan_delta_x)
1085 void DEBUG_SetMaximumDynamite()
1089 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1090 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1091 local_player->inventory_element[local_player->inventory_size++] =
1096 static void InitPlayfieldScanModeVars()
1098 if (game.use_reverse_scan_direction)
1100 playfield_scan_start_x = lev_fieldx - 1;
1101 playfield_scan_start_y = lev_fieldy - 1;
1103 playfield_scan_delta_x = -1;
1104 playfield_scan_delta_y = -1;
1108 playfield_scan_start_x = 0;
1109 playfield_scan_start_y = 0;
1111 playfield_scan_delta_x = 1;
1112 playfield_scan_delta_y = 1;
1116 static void InitPlayfieldScanMode(int mode)
1118 game.use_reverse_scan_direction =
1119 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1121 InitPlayfieldScanModeVars();
1124 static int get_move_delay_from_stepsize(int move_stepsize)
1127 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1129 /* make sure that stepsize value is always a power of 2 */
1130 move_stepsize = (1 << log_2(move_stepsize));
1132 return TILEX / move_stepsize;
1135 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1138 int player_nr = player->index_nr;
1139 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1140 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1142 /* do no immediately change move delay -- the player might just be moving */
1143 player->move_delay_value_next = move_delay;
1145 /* information if player can move must be set separately */
1146 player->cannot_move = cannot_move;
1150 player->move_delay = game.initial_move_delay[player_nr];
1151 player->move_delay_value = game.initial_move_delay_value[player_nr];
1153 player->move_delay_value_next = -1;
1155 player->move_delay_reset_counter = 0;
1159 void GetPlayerConfig()
1161 GameFrameDelay = setup.game_frame_delay;
1163 if (!audio.sound_available)
1164 setup.sound_simple = FALSE;
1166 if (!audio.loops_available)
1167 setup.sound_loops = FALSE;
1169 if (!audio.music_available)
1170 setup.sound_music = FALSE;
1172 if (!video.fullscreen_available)
1173 setup.fullscreen = FALSE;
1175 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1177 SetAudioMode(setup.sound);
1181 int GetElementFromGroupElement(int element)
1183 if (IS_GROUP_ELEMENT(element))
1185 struct ElementGroupInfo *group = element_info[element].group;
1186 int last_anim_random_frame = gfx.anim_random_frame;
1189 if (group->choice_mode == ANIM_RANDOM)
1190 gfx.anim_random_frame = RND(group->num_elements_resolved);
1192 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1193 group->choice_mode, 0,
1196 if (group->choice_mode == ANIM_RANDOM)
1197 gfx.anim_random_frame = last_anim_random_frame;
1199 group->choice_pos++;
1201 element = group->element_resolved[element_pos];
1207 static void InitPlayerField(int x, int y, int element, boolean init_game)
1209 if (element == EL_SP_MURPHY)
1213 if (stored_player[0].present)
1215 Feld[x][y] = EL_SP_MURPHY_CLONE;
1221 stored_player[0].use_murphy = TRUE;
1223 if (!level.use_artwork_element[0])
1224 stored_player[0].artwork_element = EL_SP_MURPHY;
1227 Feld[x][y] = EL_PLAYER_1;
1233 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1234 int jx = player->jx, jy = player->jy;
1236 player->present = TRUE;
1238 player->block_last_field = (element == EL_SP_MURPHY ?
1239 level.sp_block_last_field :
1240 level.block_last_field);
1242 /* ---------- initialize player's last field block delay --------------- */
1244 /* always start with reliable default value (no adjustment needed) */
1245 player->block_delay_adjustment = 0;
1247 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1248 if (player->block_last_field && element == EL_SP_MURPHY)
1249 player->block_delay_adjustment = 1;
1251 /* special case 2: in game engines before 3.1.1, blocking was different */
1252 if (game.use_block_last_field_bug)
1253 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1255 if (!options.network || player->connected)
1257 player->active = TRUE;
1259 /* remove potentially duplicate players */
1260 if (StorePlayer[jx][jy] == Feld[x][y])
1261 StorePlayer[jx][jy] = 0;
1263 StorePlayer[x][y] = Feld[x][y];
1267 printf("Player %d activated.\n", player->element_nr);
1268 printf("[Local player is %d and currently %s.]\n",
1269 local_player->element_nr,
1270 local_player->active ? "active" : "not active");
1274 Feld[x][y] = EL_EMPTY;
1276 player->jx = player->last_jx = x;
1277 player->jy = player->last_jy = y;
1281 static void InitField(int x, int y, boolean init_game)
1283 int element = Feld[x][y];
1292 InitPlayerField(x, y, element, init_game);
1295 case EL_SOKOBAN_FIELD_PLAYER:
1296 element = Feld[x][y] = EL_PLAYER_1;
1297 InitField(x, y, init_game);
1299 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1300 InitField(x, y, init_game);
1303 case EL_SOKOBAN_FIELD_EMPTY:
1304 local_player->sokobanfields_still_needed++;
1308 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1309 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1310 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1311 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1312 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1313 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1314 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1315 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1316 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1317 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1326 case EL_SPACESHIP_RIGHT:
1327 case EL_SPACESHIP_UP:
1328 case EL_SPACESHIP_LEFT:
1329 case EL_SPACESHIP_DOWN:
1330 case EL_BD_BUTTERFLY:
1331 case EL_BD_BUTTERFLY_RIGHT:
1332 case EL_BD_BUTTERFLY_UP:
1333 case EL_BD_BUTTERFLY_LEFT:
1334 case EL_BD_BUTTERFLY_DOWN:
1336 case EL_BD_FIREFLY_RIGHT:
1337 case EL_BD_FIREFLY_UP:
1338 case EL_BD_FIREFLY_LEFT:
1339 case EL_BD_FIREFLY_DOWN:
1340 case EL_PACMAN_RIGHT:
1342 case EL_PACMAN_LEFT:
1343 case EL_PACMAN_DOWN:
1345 case EL_YAMYAM_LEFT:
1346 case EL_YAMYAM_RIGHT:
1348 case EL_YAMYAM_DOWN:
1349 case EL_DARK_YAMYAM:
1352 case EL_SP_SNIKSNAK:
1353 case EL_SP_ELECTRON:
1362 case EL_AMOEBA_FULL:
1367 case EL_AMOEBA_DROP:
1368 if (y == lev_fieldy - 1)
1370 Feld[x][y] = EL_AMOEBA_GROWING;
1371 Store[x][y] = EL_AMOEBA_WET;
1375 case EL_DYNAMITE_ACTIVE:
1376 case EL_SP_DISK_RED_ACTIVE:
1377 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1378 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1379 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1380 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1381 MovDelay[x][y] = 96;
1384 case EL_EM_DYNAMITE_ACTIVE:
1385 MovDelay[x][y] = 32;
1389 local_player->lights_still_needed++;
1393 local_player->friends_still_needed++;
1398 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1401 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1402 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1403 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1404 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1405 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1406 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1407 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1408 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1409 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1410 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1411 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1412 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1415 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1416 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1417 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1419 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1421 game.belt_dir[belt_nr] = belt_dir;
1422 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1424 else /* more than one switch -- set it like the first switch */
1426 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1431 #if !USE_BOTH_SWITCHGATE_SWITCHES
1432 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1434 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1437 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1439 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1443 case EL_LIGHT_SWITCH_ACTIVE:
1445 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1448 case EL_INVISIBLE_STEELWALL:
1449 case EL_INVISIBLE_WALL:
1450 case EL_INVISIBLE_SAND:
1451 if (game.light_time_left > 0 ||
1452 game.lenses_time_left > 0)
1453 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1456 case EL_EMC_MAGIC_BALL:
1457 if (game.ball_state)
1458 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1461 case EL_EMC_MAGIC_BALL_SWITCH:
1462 if (game.ball_state)
1463 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1467 if (IS_CUSTOM_ELEMENT(element))
1469 if (CAN_MOVE(element))
1472 #if USE_NEW_CUSTOM_VALUE
1473 if (!element_info[element].use_last_ce_value || init_game)
1474 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1477 else if (IS_GROUP_ELEMENT(element))
1479 Feld[x][y] = GetElementFromGroupElement(element);
1481 InitField(x, y, init_game);
1488 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1491 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1493 InitField(x, y, init_game);
1495 /* not needed to call InitMovDir() -- already done by InitField()! */
1496 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1497 CAN_MOVE(Feld[x][y]))
1501 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1503 int old_element = Feld[x][y];
1505 InitField(x, y, init_game);
1507 /* not needed to call InitMovDir() -- already done by InitField()! */
1508 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1509 CAN_MOVE(old_element) &&
1510 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1513 /* this case is in fact a combination of not less than three bugs:
1514 first, it calls InitMovDir() for elements that can move, although this is
1515 already done by InitField(); then, it checks the element that was at this
1516 field _before_ the call to InitField() (which can change it); lastly, it
1517 was not called for "mole with direction" elements, which were treated as
1518 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1524 void DrawGameValue_Emeralds(int value)
1526 struct TextPosInfo *pos = &game.panel.gems;
1528 int font_nr = pos->font;
1530 int font_nr = FONT_TEXT_2;
1532 int font_width = getFontWidth(font_nr);
1533 int chars = pos->chars;
1535 if (PANEL_DEACTIVATED(pos))
1538 pos->width = chars * font_width;
1540 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1543 void DrawGameValue_Dynamite(int value)
1545 struct TextPosInfo *pos = &game.panel.inventory;
1547 int font_nr = pos->font;
1549 int font_nr = FONT_TEXT_2;
1551 int font_width = getFontWidth(font_nr);
1552 int chars = pos->chars;
1554 if (PANEL_DEACTIVATED(pos))
1557 pos->width = chars * font_width;
1559 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1562 void DrawGameValue_Score(int value)
1564 struct TextPosInfo *pos = &game.panel.score;
1566 int font_nr = pos->font;
1568 int font_nr = FONT_TEXT_2;
1570 int font_width = getFontWidth(font_nr);
1571 int chars = pos->chars;
1573 if (PANEL_DEACTIVATED(pos))
1576 pos->width = chars * font_width;
1578 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1581 void DrawGameValue_Time(int value)
1583 struct TextPosInfo *pos = &game.panel.time;
1584 static int last_value = -1;
1587 int chars = pos->chars;
1589 int font1_nr = pos->font;
1590 int font2_nr = pos->font_alt;
1592 int font1_nr = FONT_TEXT_2;
1593 int font2_nr = FONT_TEXT_1;
1595 int font_nr = font1_nr;
1596 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1598 if (PANEL_DEACTIVATED(pos))
1601 if (use_dynamic_chars) /* use dynamic number of chars */
1603 chars = (value < 1000 ? chars1 : chars2);
1604 font_nr = (value < 1000 ? font1_nr : font2_nr);
1607 /* clear background if value just changed its size (dynamic chars only) */
1608 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1610 int width1 = chars1 * getFontWidth(font1_nr);
1611 int width2 = chars2 * getFontWidth(font2_nr);
1612 int max_width = MAX(width1, width2);
1613 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1615 pos->width = max_width;
1617 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1618 max_width, max_height);
1621 pos->width = chars * getFontWidth(font_nr);
1623 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1628 void DrawGameValue_Level(int value)
1630 struct TextPosInfo *pos = &game.panel.level;
1633 int chars = pos->chars;
1635 int font1_nr = pos->font;
1636 int font2_nr = pos->font_alt;
1638 int font1_nr = FONT_TEXT_2;
1639 int font2_nr = FONT_TEXT_1;
1641 int font_nr = font1_nr;
1642 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1644 if (PANEL_DEACTIVATED(pos))
1647 if (use_dynamic_chars) /* use dynamic number of chars */
1649 chars = (level_nr < 100 ? chars1 : chars2);
1650 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1653 pos->width = chars * getFontWidth(font_nr);
1655 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1658 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1660 struct TextPosInfo *pos = &game.panel.keys;
1661 int base_key_graphic = EL_KEY_1;
1664 if (PANEL_DEACTIVATED(pos))
1667 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1668 base_key_graphic = EL_EM_KEY_1;
1670 pos->width = 4 * MINI_TILEX;
1672 /* currently only 4 of 8 possible keys are displayed */
1673 for (i = 0; i < STD_NUM_KEYS; i++)
1675 int src_x = DOOR_GFX_PAGEX5 + 18;
1676 int src_y = DOOR_GFX_PAGEY1 + 123;
1677 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1678 int dst_y = PANEL_YPOS(pos);
1681 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1683 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1684 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1690 void DrawGameValue_Emeralds(int value)
1692 int font_nr = FONT_TEXT_2;
1693 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1695 if (PANEL_DEACTIVATED(game.panel.gems))
1698 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1701 void DrawGameValue_Dynamite(int value)
1703 int font_nr = FONT_TEXT_2;
1704 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1706 if (PANEL_DEACTIVATED(game.panel.inventory))
1709 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1712 void DrawGameValue_Score(int value)
1714 int font_nr = FONT_TEXT_2;
1715 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1717 if (PANEL_DEACTIVATED(game.panel.score))
1720 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1723 void DrawGameValue_Time(int value)
1725 int font1_nr = FONT_TEXT_2;
1727 int font2_nr = FONT_TEXT_1;
1729 int font2_nr = FONT_LEVEL_NUMBER;
1731 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1732 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1734 if (PANEL_DEACTIVATED(game.panel.time))
1737 /* clear background if value just changed its size */
1738 if (value == 999 || value == 1000)
1739 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1742 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1744 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1747 void DrawGameValue_Level(int value)
1749 int font1_nr = FONT_TEXT_2;
1751 int font2_nr = FONT_TEXT_1;
1753 int font2_nr = FONT_LEVEL_NUMBER;
1756 if (PANEL_DEACTIVATED(game.panel.level))
1760 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1762 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1765 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1767 int base_key_graphic = EL_KEY_1;
1770 if (PANEL_DEACTIVATED(game.panel.keys))
1773 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1774 base_key_graphic = EL_EM_KEY_1;
1776 /* currently only 4 of 8 possible keys are displayed */
1777 for (i = 0; i < STD_NUM_KEYS; i++)
1779 int x = XX_KEYS + i * MINI_TILEX;
1783 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1785 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1786 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1792 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1795 int key[MAX_NUM_KEYS];
1798 /* prevent EM engine from updating time/score values parallel to GameWon() */
1799 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1800 local_player->LevelSolved)
1803 for (i = 0; i < MAX_NUM_KEYS; i++)
1804 key[i] = key_bits & (1 << i);
1806 DrawGameValue_Level(level_nr);
1808 DrawGameValue_Emeralds(emeralds);
1809 DrawGameValue_Dynamite(dynamite);
1810 DrawGameValue_Score(score);
1811 DrawGameValue_Time(time);
1813 DrawGameValue_Keys(key);
1816 void DrawGameDoorValues()
1818 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1819 int dynamite_value = 0;
1820 int score_value = (local_player->LevelSolved ? local_player->score_final :
1821 local_player->score);
1822 int gems_value = local_player->gems_still_needed;
1826 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1828 DrawGameDoorValues_EM();
1833 if (game.centered_player_nr == -1)
1835 for (i = 0; i < MAX_PLAYERS; i++)
1837 for (j = 0; j < MAX_NUM_KEYS; j++)
1838 if (stored_player[i].key[j])
1839 key_bits |= (1 << j);
1841 dynamite_value += stored_player[i].inventory_size;
1846 int player_nr = game.centered_player_nr;
1848 for (i = 0; i < MAX_NUM_KEYS; i++)
1849 if (stored_player[player_nr].key[i])
1850 key_bits |= (1 << i);
1852 dynamite_value = stored_player[player_nr].inventory_size;
1855 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1861 =============================================================================
1863 -----------------------------------------------------------------------------
1864 initialize game engine due to level / tape version number
1865 =============================================================================
1868 static void InitGameEngine()
1870 int i, j, k, l, x, y;
1872 /* set game engine from tape file when re-playing, else from level file */
1873 game.engine_version = (tape.playing ? tape.engine_version :
1874 level.game_version);
1876 /* ---------------------------------------------------------------------- */
1877 /* set flags for bugs and changes according to active game engine version */
1878 /* ---------------------------------------------------------------------- */
1881 Summary of bugfix/change:
1882 Fixed handling for custom elements that change when pushed by the player.
1884 Fixed/changed in version:
1888 Before 3.1.0, custom elements that "change when pushing" changed directly
1889 after the player started pushing them (until then handled in "DigField()").
1890 Since 3.1.0, these custom elements are not changed until the "pushing"
1891 move of the element is finished (now handled in "ContinueMoving()").
1893 Affected levels/tapes:
1894 The first condition is generally needed for all levels/tapes before version
1895 3.1.0, which might use the old behaviour before it was changed; known tapes
1896 that are affected are some tapes from the level set "Walpurgis Gardens" by
1898 The second condition is an exception from the above case and is needed for
1899 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1900 above (including some development versions of 3.1.0), but before it was
1901 known that this change would break tapes like the above and was fixed in
1902 3.1.1, so that the changed behaviour was active although the engine version
1903 while recording maybe was before 3.1.0. There is at least one tape that is
1904 affected by this exception, which is the tape for the one-level set "Bug
1905 Machine" by Juergen Bonhagen.
1908 game.use_change_when_pushing_bug =
1909 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1911 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1912 tape.game_version < VERSION_IDENT(3,1,1,0)));
1915 Summary of bugfix/change:
1916 Fixed handling for blocking the field the player leaves when moving.
1918 Fixed/changed in version:
1922 Before 3.1.1, when "block last field when moving" was enabled, the field
1923 the player is leaving when moving was blocked for the time of the move,
1924 and was directly unblocked afterwards. This resulted in the last field
1925 being blocked for exactly one less than the number of frames of one player
1926 move. Additionally, even when blocking was disabled, the last field was
1927 blocked for exactly one frame.
1928 Since 3.1.1, due to changes in player movement handling, the last field
1929 is not blocked at all when blocking is disabled. When blocking is enabled,
1930 the last field is blocked for exactly the number of frames of one player
1931 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1932 last field is blocked for exactly one more than the number of frames of
1935 Affected levels/tapes:
1936 (!!! yet to be determined -- probably many !!!)
1939 game.use_block_last_field_bug =
1940 (game.engine_version < VERSION_IDENT(3,1,1,0));
1943 Summary of bugfix/change:
1944 Changed behaviour of CE changes with multiple changes per single frame.
1946 Fixed/changed in version:
1950 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1951 This resulted in race conditions where CEs seem to behave strange in some
1952 situations (where triggered CE changes were just skipped because there was
1953 already a CE change on that tile in the playfield in that engine frame).
1954 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1955 (The number of changes per frame must be limited in any case, because else
1956 it is easily possible to define CE changes that would result in an infinite
1957 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1958 should be set large enough so that it would only be reached in cases where
1959 the corresponding CE change conditions run into a loop. Therefore, it seems
1960 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1961 maximal number of change pages for custom elements.)
1963 Affected levels/tapes:
1967 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1968 game.max_num_changes_per_frame = 1;
1970 game.max_num_changes_per_frame =
1971 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1974 /* ---------------------------------------------------------------------- */
1976 /* default scan direction: scan playfield from top/left to bottom/right */
1977 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1979 /* dynamically adjust element properties according to game engine version */
1980 InitElementPropertiesEngine(game.engine_version);
1983 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1984 printf(" tape version == %06d [%s] [file: %06d]\n",
1985 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1987 printf(" => game.engine_version == %06d\n", game.engine_version);
1990 /* ---------- initialize player's initial move delay --------------------- */
1992 /* dynamically adjust player properties according to level information */
1993 for (i = 0; i < MAX_PLAYERS; i++)
1994 game.initial_move_delay_value[i] =
1995 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1997 /* dynamically adjust player properties according to game engine version */
1998 for (i = 0; i < MAX_PLAYERS; i++)
1999 game.initial_move_delay[i] =
2000 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2001 game.initial_move_delay_value[i] : 0);
2003 /* ---------- initialize player's initial push delay --------------------- */
2005 /* dynamically adjust player properties according to game engine version */
2006 game.initial_push_delay_value =
2007 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2009 /* ---------- initialize changing elements ------------------------------- */
2011 /* initialize changing elements information */
2012 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2014 struct ElementInfo *ei = &element_info[i];
2016 /* this pointer might have been changed in the level editor */
2017 ei->change = &ei->change_page[0];
2019 if (!IS_CUSTOM_ELEMENT(i))
2021 ei->change->target_element = EL_EMPTY_SPACE;
2022 ei->change->delay_fixed = 0;
2023 ei->change->delay_random = 0;
2024 ei->change->delay_frames = 1;
2027 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2029 ei->has_change_event[j] = FALSE;
2031 ei->event_page_nr[j] = 0;
2032 ei->event_page[j] = &ei->change_page[0];
2036 /* add changing elements from pre-defined list */
2037 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2039 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2040 struct ElementInfo *ei = &element_info[ch_delay->element];
2042 ei->change->target_element = ch_delay->target_element;
2043 ei->change->delay_fixed = ch_delay->change_delay;
2045 ei->change->pre_change_function = ch_delay->pre_change_function;
2046 ei->change->change_function = ch_delay->change_function;
2047 ei->change->post_change_function = ch_delay->post_change_function;
2049 ei->change->can_change = TRUE;
2050 ei->change->can_change_or_has_action = TRUE;
2052 ei->has_change_event[CE_DELAY] = TRUE;
2054 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2055 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2058 /* ---------- initialize internal run-time variables ------------- */
2060 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2062 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2064 for (j = 0; j < ei->num_change_pages; j++)
2066 ei->change_page[j].can_change_or_has_action =
2067 (ei->change_page[j].can_change |
2068 ei->change_page[j].has_action);
2072 /* add change events from custom element configuration */
2073 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2075 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2077 for (j = 0; j < ei->num_change_pages; j++)
2079 if (!ei->change_page[j].can_change_or_has_action)
2082 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2084 /* only add event page for the first page found with this event */
2085 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2087 ei->has_change_event[k] = TRUE;
2089 ei->event_page_nr[k] = j;
2090 ei->event_page[k] = &ei->change_page[j];
2096 /* ---------- initialize run-time trigger player and element ------------- */
2098 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2100 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2102 for (j = 0; j < ei->num_change_pages; j++)
2104 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2105 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2106 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2107 ei->change_page[j].actual_trigger_ce_value = 0;
2108 ei->change_page[j].actual_trigger_ce_score = 0;
2112 /* ---------- initialize trigger events ---------------------------------- */
2114 /* initialize trigger events information */
2115 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2116 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2117 trigger_events[i][j] = FALSE;
2119 /* add trigger events from element change event properties */
2120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122 struct ElementInfo *ei = &element_info[i];
2124 for (j = 0; j < ei->num_change_pages; j++)
2126 if (!ei->change_page[j].can_change_or_has_action)
2129 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2131 int trigger_element = ei->change_page[j].trigger_element;
2133 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2135 if (ei->change_page[j].has_event[k])
2137 if (IS_GROUP_ELEMENT(trigger_element))
2139 struct ElementGroupInfo *group =
2140 element_info[trigger_element].group;
2142 for (l = 0; l < group->num_elements_resolved; l++)
2143 trigger_events[group->element_resolved[l]][k] = TRUE;
2145 else if (trigger_element == EL_ANY_ELEMENT)
2146 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2147 trigger_events[l][k] = TRUE;
2149 trigger_events[trigger_element][k] = TRUE;
2156 /* ---------- initialize push delay -------------------------------------- */
2158 /* initialize push delay values to default */
2159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2161 if (!IS_CUSTOM_ELEMENT(i))
2163 /* set default push delay values (corrected since version 3.0.7-1) */
2164 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2166 element_info[i].push_delay_fixed = 2;
2167 element_info[i].push_delay_random = 8;
2171 element_info[i].push_delay_fixed = 8;
2172 element_info[i].push_delay_random = 8;
2177 /* set push delay value for certain elements from pre-defined list */
2178 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2180 int e = push_delay_list[i].element;
2182 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2183 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2186 /* set push delay value for Supaplex elements for newer engine versions */
2187 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2189 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2191 if (IS_SP_ELEMENT(i))
2193 /* set SP push delay to just enough to push under a falling zonk */
2194 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2196 element_info[i].push_delay_fixed = delay;
2197 element_info[i].push_delay_random = 0;
2202 /* ---------- initialize move stepsize ----------------------------------- */
2204 /* initialize move stepsize values to default */
2205 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206 if (!IS_CUSTOM_ELEMENT(i))
2207 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2209 /* set move stepsize value for certain elements from pre-defined list */
2210 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2212 int e = move_stepsize_list[i].element;
2214 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2217 /* ---------- initialize collect score ----------------------------------- */
2219 /* initialize collect score values for custom elements from initial value */
2220 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2221 if (IS_CUSTOM_ELEMENT(i))
2222 element_info[i].collect_score = element_info[i].collect_score_initial;
2224 /* ---------- initialize collect count ----------------------------------- */
2226 /* initialize collect count values for non-custom elements */
2227 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2228 if (!IS_CUSTOM_ELEMENT(i))
2229 element_info[i].collect_count_initial = 0;
2231 /* add collect count values for all elements from pre-defined list */
2232 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2233 element_info[collect_count_list[i].element].collect_count_initial =
2234 collect_count_list[i].count;
2236 /* ---------- initialize access direction -------------------------------- */
2238 /* initialize access direction values to default (access from every side) */
2239 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2240 if (!IS_CUSTOM_ELEMENT(i))
2241 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2243 /* set access direction value for certain elements from pre-defined list */
2244 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2245 element_info[access_direction_list[i].element].access_direction =
2246 access_direction_list[i].direction;
2248 /* ---------- initialize explosion content ------------------------------- */
2249 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2251 if (IS_CUSTOM_ELEMENT(i))
2254 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2256 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2258 element_info[i].content.e[x][y] =
2259 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2260 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2261 i == EL_PLAYER_3 ? EL_EMERALD :
2262 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2263 i == EL_MOLE ? EL_EMERALD_RED :
2264 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2265 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2266 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2267 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2268 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2269 i == EL_WALL_EMERALD ? EL_EMERALD :
2270 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2271 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2272 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2273 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2274 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2275 i == EL_WALL_PEARL ? EL_PEARL :
2276 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2281 /* ---------- initialize recursion detection ------------------------------ */
2282 recursion_loop_depth = 0;
2283 recursion_loop_detected = FALSE;
2284 recursion_loop_element = EL_UNDEFINED;
2287 int get_num_special_action(int element, int action_first, int action_last)
2289 int num_special_action = 0;
2292 for (i = action_first; i <= action_last; i++)
2294 boolean found = FALSE;
2296 for (j = 0; j < NUM_DIRECTIONS; j++)
2297 if (el_act_dir2img(element, i, j) !=
2298 el_act_dir2img(element, ACTION_DEFAULT, j))
2302 num_special_action++;
2307 return num_special_action;
2312 =============================================================================
2314 -----------------------------------------------------------------------------
2315 initialize and start new game
2316 =============================================================================
2321 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2322 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2323 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2324 boolean do_fading = (game_status == GAME_MODE_MAIN);
2327 game_status = GAME_MODE_PLAYING;
2331 /* don't play tapes over network */
2332 network_playing = (options.network && !tape.playing);
2334 for (i = 0; i < MAX_PLAYERS; i++)
2336 struct PlayerInfo *player = &stored_player[i];
2338 player->index_nr = i;
2339 player->index_bit = (1 << i);
2340 player->element_nr = EL_PLAYER_1 + i;
2342 player->present = FALSE;
2343 player->active = FALSE;
2344 player->killed = FALSE;
2347 player->effective_action = 0;
2348 player->programmed_action = 0;
2351 player->score_final = 0;
2353 player->gems_still_needed = level.gems_needed;
2354 player->sokobanfields_still_needed = 0;
2355 player->lights_still_needed = 0;
2356 player->friends_still_needed = 0;
2358 for (j = 0; j < MAX_NUM_KEYS; j++)
2359 player->key[j] = FALSE;
2361 player->num_white_keys = 0;
2363 player->dynabomb_count = 0;
2364 player->dynabomb_size = 1;
2365 player->dynabombs_left = 0;
2366 player->dynabomb_xl = FALSE;
2368 player->MovDir = MV_NONE;
2371 player->GfxDir = MV_NONE;
2372 player->GfxAction = ACTION_DEFAULT;
2374 player->StepFrame = 0;
2376 player->use_murphy = FALSE;
2377 player->artwork_element =
2378 (level.use_artwork_element[i] ? level.artwork_element[i] :
2379 player->element_nr);
2381 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2382 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2384 player->gravity = level.initial_player_gravity[i];
2386 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2388 player->actual_frame_counter = 0;
2390 player->step_counter = 0;
2392 player->last_move_dir = MV_NONE;
2394 player->is_active = FALSE;
2396 player->is_waiting = FALSE;
2397 player->is_moving = FALSE;
2398 player->is_auto_moving = FALSE;
2399 player->is_digging = FALSE;
2400 player->is_snapping = FALSE;
2401 player->is_collecting = FALSE;
2402 player->is_pushing = FALSE;
2403 player->is_switching = FALSE;
2404 player->is_dropping = FALSE;
2405 player->is_dropping_pressed = FALSE;
2407 player->is_bored = FALSE;
2408 player->is_sleeping = FALSE;
2410 player->frame_counter_bored = -1;
2411 player->frame_counter_sleeping = -1;
2413 player->anim_delay_counter = 0;
2414 player->post_delay_counter = 0;
2416 player->dir_waiting = MV_NONE;
2417 player->action_waiting = ACTION_DEFAULT;
2418 player->last_action_waiting = ACTION_DEFAULT;
2419 player->special_action_bored = ACTION_DEFAULT;
2420 player->special_action_sleeping = ACTION_DEFAULT;
2422 player->switch_x = -1;
2423 player->switch_y = -1;
2425 player->drop_x = -1;
2426 player->drop_y = -1;
2428 player->show_envelope = 0;
2430 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2432 player->push_delay = -1; /* initialized when pushing starts */
2433 player->push_delay_value = game.initial_push_delay_value;
2435 player->drop_delay = 0;
2436 player->drop_pressed_delay = 0;
2438 player->last_jx = -1;
2439 player->last_jy = -1;
2443 player->shield_normal_time_left = 0;
2444 player->shield_deadly_time_left = 0;
2446 player->inventory_infinite_element = EL_UNDEFINED;
2447 player->inventory_size = 0;
2449 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2450 SnapField(player, 0, 0);
2452 player->LevelSolved = FALSE;
2453 player->GameOver = FALSE;
2455 player->LevelSolved_GameWon = FALSE;
2456 player->LevelSolved_GameEnd = FALSE;
2457 player->LevelSolved_PanelOff = FALSE;
2458 player->LevelSolved_SaveTape = FALSE;
2459 player->LevelSolved_SaveScore = FALSE;
2462 network_player_action_received = FALSE;
2464 #if defined(NETWORK_AVALIABLE)
2465 /* initial null action */
2466 if (network_playing)
2467 SendToServer_MovePlayer(MV_NONE);
2476 TimeLeft = level.time;
2479 ScreenMovDir = MV_NONE;
2483 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2485 AllPlayersGone = FALSE;
2487 game.yamyam_content_nr = 0;
2488 game.magic_wall_active = FALSE;
2489 game.magic_wall_time_left = 0;
2490 game.light_time_left = 0;
2491 game.timegate_time_left = 0;
2492 game.switchgate_pos = 0;
2493 game.wind_direction = level.wind_direction_initial;
2495 #if !USE_PLAYER_GRAVITY
2496 game.gravity = FALSE;
2497 game.explosions_delayed = TRUE;
2500 game.lenses_time_left = 0;
2501 game.magnify_time_left = 0;
2503 game.ball_state = level.ball_state_initial;
2504 game.ball_content_nr = 0;
2506 game.envelope_active = FALSE;
2508 /* set focus to local player for network games, else to all players */
2509 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2510 game.centered_player_nr_next = game.centered_player_nr;
2511 game.set_centered_player = FALSE;
2513 if (network_playing && tape.recording)
2515 /* store client dependent player focus when recording network games */
2516 tape.centered_player_nr_next = game.centered_player_nr_next;
2517 tape.set_centered_player = TRUE;
2520 for (i = 0; i < NUM_BELTS; i++)
2522 game.belt_dir[i] = MV_NONE;
2523 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2526 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2527 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2529 SCAN_PLAYFIELD(x, y)
2531 Feld[x][y] = level.field[x][y];
2532 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2533 ChangeDelay[x][y] = 0;
2534 ChangePage[x][y] = -1;
2535 #if USE_NEW_CUSTOM_VALUE
2536 CustomValue[x][y] = 0; /* initialized in InitField() */
2538 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2540 WasJustMoving[x][y] = 0;
2541 WasJustFalling[x][y] = 0;
2542 CheckCollision[x][y] = 0;
2543 CheckImpact[x][y] = 0;
2545 Pushed[x][y] = FALSE;
2547 ChangeCount[x][y] = 0;
2548 ChangeEvent[x][y] = -1;
2550 ExplodePhase[x][y] = 0;
2551 ExplodeDelay[x][y] = 0;
2552 ExplodeField[x][y] = EX_TYPE_NONE;
2554 RunnerVisit[x][y] = 0;
2555 PlayerVisit[x][y] = 0;
2558 GfxRandom[x][y] = INIT_GFX_RANDOM();
2559 GfxElement[x][y] = EL_UNDEFINED;
2560 GfxAction[x][y] = ACTION_DEFAULT;
2561 GfxDir[x][y] = MV_NONE;
2564 SCAN_PLAYFIELD(x, y)
2566 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2568 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2570 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2573 InitField(x, y, TRUE);
2578 for (i = 0; i < MAX_PLAYERS; i++)
2580 struct PlayerInfo *player = &stored_player[i];
2582 /* set number of special actions for bored and sleeping animation */
2583 player->num_special_action_bored =
2584 get_num_special_action(player->artwork_element,
2585 ACTION_BORING_1, ACTION_BORING_LAST);
2586 player->num_special_action_sleeping =
2587 get_num_special_action(player->artwork_element,
2588 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2591 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2592 emulate_sb ? EMU_SOKOBAN :
2593 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2595 #if USE_NEW_ALL_SLIPPERY
2596 /* initialize type of slippery elements */
2597 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2599 if (!IS_CUSTOM_ELEMENT(i))
2601 /* default: elements slip down either to the left or right randomly */
2602 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2604 /* SP style elements prefer to slip down on the left side */
2605 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2606 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2608 /* BD style elements prefer to slip down on the left side */
2609 if (game.emulation == EMU_BOULDERDASH)
2610 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2615 /* initialize explosion and ignition delay */
2616 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2618 if (!IS_CUSTOM_ELEMENT(i))
2621 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2622 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2623 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2624 int last_phase = (num_phase + 1) * delay;
2625 int half_phase = (num_phase / 2) * delay;
2627 element_info[i].explosion_delay = last_phase - 1;
2628 element_info[i].ignition_delay = half_phase;
2630 if (i == EL_BLACK_ORB)
2631 element_info[i].ignition_delay = 1;
2635 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2636 element_info[i].explosion_delay = 1;
2638 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2639 element_info[i].ignition_delay = 1;
2643 /* correct non-moving belts to start moving left */
2644 for (i = 0; i < NUM_BELTS; i++)
2645 if (game.belt_dir[i] == MV_NONE)
2646 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2648 /* check if any connected player was not found in playfield */
2649 for (i = 0; i < MAX_PLAYERS; i++)
2651 struct PlayerInfo *player = &stored_player[i];
2653 if (player->connected && !player->present)
2655 for (j = 0; j < MAX_PLAYERS; j++)
2657 struct PlayerInfo *some_player = &stored_player[j];
2658 int jx = some_player->jx, jy = some_player->jy;
2660 /* assign first free player found that is present in the playfield */
2661 if (some_player->present && !some_player->connected)
2663 player->present = TRUE;
2664 player->active = TRUE;
2666 some_player->present = FALSE;
2667 some_player->active = FALSE;
2669 player->artwork_element = some_player->artwork_element;
2671 player->block_last_field = some_player->block_last_field;
2672 player->block_delay_adjustment = some_player->block_delay_adjustment;
2674 StorePlayer[jx][jy] = player->element_nr;
2675 player->jx = player->last_jx = jx;
2676 player->jy = player->last_jy = jy;
2686 /* when playing a tape, eliminate all players who do not participate */
2688 for (i = 0; i < MAX_PLAYERS; i++)
2690 if (stored_player[i].active && !tape.player_participates[i])
2692 struct PlayerInfo *player = &stored_player[i];
2693 int jx = player->jx, jy = player->jy;
2695 player->active = FALSE;
2696 StorePlayer[jx][jy] = 0;
2697 Feld[jx][jy] = EL_EMPTY;
2701 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2703 /* when in single player mode, eliminate all but the first active player */
2705 for (i = 0; i < MAX_PLAYERS; i++)
2707 if (stored_player[i].active)
2709 for (j = i + 1; j < MAX_PLAYERS; j++)
2711 if (stored_player[j].active)
2713 struct PlayerInfo *player = &stored_player[j];
2714 int jx = player->jx, jy = player->jy;
2716 player->active = FALSE;
2717 player->present = FALSE;
2719 StorePlayer[jx][jy] = 0;
2720 Feld[jx][jy] = EL_EMPTY;
2727 /* when recording the game, store which players take part in the game */
2730 for (i = 0; i < MAX_PLAYERS; i++)
2731 if (stored_player[i].active)
2732 tape.player_participates[i] = TRUE;
2737 for (i = 0; i < MAX_PLAYERS; i++)
2739 struct PlayerInfo *player = &stored_player[i];
2741 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2746 if (local_player == player)
2747 printf("Player %d is local player.\n", i+1);
2751 if (BorderElement == EL_EMPTY)
2754 SBX_Right = lev_fieldx - SCR_FIELDX;
2756 SBY_Lower = lev_fieldy - SCR_FIELDY;
2761 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2763 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2766 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2767 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2769 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2770 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2772 /* if local player not found, look for custom element that might create
2773 the player (make some assumptions about the right custom element) */
2774 if (!local_player->present)
2776 int start_x = 0, start_y = 0;
2777 int found_rating = 0;
2778 int found_element = EL_UNDEFINED;
2779 int player_nr = local_player->index_nr;
2781 SCAN_PLAYFIELD(x, y)
2783 int element = Feld[x][y];
2788 if (level.use_start_element[player_nr] &&
2789 level.start_element[player_nr] == element &&
2796 found_element = element;
2799 if (!IS_CUSTOM_ELEMENT(element))
2802 if (CAN_CHANGE(element))
2804 for (i = 0; i < element_info[element].num_change_pages; i++)
2806 /* check for player created from custom element as single target */
2807 content = element_info[element].change_page[i].target_element;
2808 is_player = ELEM_IS_PLAYER(content);
2810 if (is_player && (found_rating < 3 || element < found_element))
2816 found_element = element;
2821 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2823 /* check for player created from custom element as explosion content */
2824 content = element_info[element].content.e[xx][yy];
2825 is_player = ELEM_IS_PLAYER(content);
2827 if (is_player && (found_rating < 2 || element < found_element))
2829 start_x = x + xx - 1;
2830 start_y = y + yy - 1;
2833 found_element = element;
2836 if (!CAN_CHANGE(element))
2839 for (i = 0; i < element_info[element].num_change_pages; i++)
2841 /* check for player created from custom element as extended target */
2843 element_info[element].change_page[i].target_content.e[xx][yy];
2845 is_player = ELEM_IS_PLAYER(content);
2847 if (is_player && (found_rating < 1 || element < found_element))
2849 start_x = x + xx - 1;
2850 start_y = y + yy - 1;
2853 found_element = element;
2859 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2860 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2863 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2864 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2869 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2870 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2871 local_player->jx - MIDPOSX);
2873 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2874 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2875 local_player->jy - MIDPOSY);
2880 if (!game.restart_level)
2881 CloseDoor(DOOR_CLOSE_1);
2884 FadeOut(REDRAW_FIELD);
2886 /* !!! FIX THIS (START) !!! */
2887 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2889 InitGameEngine_EM();
2891 /* blit playfield from scroll buffer to normal back buffer for fading in */
2892 BlitScreenToBitmap_EM(backbuffer);
2899 /* after drawing the level, correct some elements */
2900 if (game.timegate_time_left == 0)
2901 CloseAllOpenTimegates();
2903 /* blit playfield from scroll buffer to normal back buffer for fading in */
2904 if (setup.soft_scrolling)
2905 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2907 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2909 /* !!! FIX THIS (END) !!! */
2912 FadeIn(REDRAW_FIELD);
2916 if (!game.restart_level)
2918 /* copy default game door content to main double buffer */
2919 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2920 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2923 SetPanelBackground();
2924 SetDrawBackgroundMask(REDRAW_DOOR_1);
2926 DrawGameDoorValues();
2928 if (!game.restart_level)
2932 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2933 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2934 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2938 /* copy actual game door content to door double buffer for OpenDoor() */
2939 BlitBitmap(drawto, bitmap_db_door,
2940 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2942 OpenDoor(DOOR_OPEN_ALL);
2944 PlaySound(SND_GAME_STARTING);
2946 if (setup.sound_music)
2949 KeyboardAutoRepeatOffUnlessAutoplay();
2953 for (i = 0; i < MAX_PLAYERS; i++)
2954 printf("Player %d %sactive.\n",
2955 i + 1, (stored_player[i].active ? "" : "not "));
2966 game.restart_level = FALSE;
2969 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2971 /* this is used for non-R'n'D game engines to update certain engine values */
2973 /* needed to determine if sounds are played within the visible screen area */
2974 scroll_x = actual_scroll_x;
2975 scroll_y = actual_scroll_y;
2978 void InitMovDir(int x, int y)
2980 int i, element = Feld[x][y];
2981 static int xy[4][2] =
2988 static int direction[3][4] =
2990 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2991 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2992 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3001 Feld[x][y] = EL_BUG;
3002 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3005 case EL_SPACESHIP_RIGHT:
3006 case EL_SPACESHIP_UP:
3007 case EL_SPACESHIP_LEFT:
3008 case EL_SPACESHIP_DOWN:
3009 Feld[x][y] = EL_SPACESHIP;
3010 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3013 case EL_BD_BUTTERFLY_RIGHT:
3014 case EL_BD_BUTTERFLY_UP:
3015 case EL_BD_BUTTERFLY_LEFT:
3016 case EL_BD_BUTTERFLY_DOWN:
3017 Feld[x][y] = EL_BD_BUTTERFLY;
3018 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3021 case EL_BD_FIREFLY_RIGHT:
3022 case EL_BD_FIREFLY_UP:
3023 case EL_BD_FIREFLY_LEFT:
3024 case EL_BD_FIREFLY_DOWN:
3025 Feld[x][y] = EL_BD_FIREFLY;
3026 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3029 case EL_PACMAN_RIGHT:
3031 case EL_PACMAN_LEFT:
3032 case EL_PACMAN_DOWN:
3033 Feld[x][y] = EL_PACMAN;
3034 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3037 case EL_YAMYAM_LEFT:
3038 case EL_YAMYAM_RIGHT:
3040 case EL_YAMYAM_DOWN:
3041 Feld[x][y] = EL_YAMYAM;
3042 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3045 case EL_SP_SNIKSNAK:
3046 MovDir[x][y] = MV_UP;
3049 case EL_SP_ELECTRON:
3050 MovDir[x][y] = MV_LEFT;
3057 Feld[x][y] = EL_MOLE;
3058 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3062 if (IS_CUSTOM_ELEMENT(element))
3064 struct ElementInfo *ei = &element_info[element];
3065 int move_direction_initial = ei->move_direction_initial;
3066 int move_pattern = ei->move_pattern;
3068 if (move_direction_initial == MV_START_PREVIOUS)
3070 if (MovDir[x][y] != MV_NONE)
3073 move_direction_initial = MV_START_AUTOMATIC;
3076 if (move_direction_initial == MV_START_RANDOM)
3077 MovDir[x][y] = 1 << RND(4);
3078 else if (move_direction_initial & MV_ANY_DIRECTION)
3079 MovDir[x][y] = move_direction_initial;
3080 else if (move_pattern == MV_ALL_DIRECTIONS ||
3081 move_pattern == MV_TURNING_LEFT ||
3082 move_pattern == MV_TURNING_RIGHT ||
3083 move_pattern == MV_TURNING_LEFT_RIGHT ||
3084 move_pattern == MV_TURNING_RIGHT_LEFT ||
3085 move_pattern == MV_TURNING_RANDOM)
3086 MovDir[x][y] = 1 << RND(4);
3087 else if (move_pattern == MV_HORIZONTAL)
3088 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3089 else if (move_pattern == MV_VERTICAL)
3090 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3091 else if (move_pattern & MV_ANY_DIRECTION)
3092 MovDir[x][y] = element_info[element].move_pattern;
3093 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3094 move_pattern == MV_ALONG_RIGHT_SIDE)
3096 /* use random direction as default start direction */
3097 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3098 MovDir[x][y] = 1 << RND(4);
3100 for (i = 0; i < NUM_DIRECTIONS; i++)
3102 int x1 = x + xy[i][0];
3103 int y1 = y + xy[i][1];
3105 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3107 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3108 MovDir[x][y] = direction[0][i];
3110 MovDir[x][y] = direction[1][i];
3119 MovDir[x][y] = 1 << RND(4);
3121 if (element != EL_BUG &&
3122 element != EL_SPACESHIP &&
3123 element != EL_BD_BUTTERFLY &&
3124 element != EL_BD_FIREFLY)
3127 for (i = 0; i < NUM_DIRECTIONS; i++)
3129 int x1 = x + xy[i][0];
3130 int y1 = y + xy[i][1];
3132 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3134 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3136 MovDir[x][y] = direction[0][i];
3139 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3140 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3142 MovDir[x][y] = direction[1][i];
3151 GfxDir[x][y] = MovDir[x][y];
3154 void InitAmoebaNr(int x, int y)
3157 int group_nr = AmoebeNachbarNr(x, y);
3161 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3163 if (AmoebaCnt[i] == 0)
3171 AmoebaNr[x][y] = group_nr;
3172 AmoebaCnt[group_nr]++;
3173 AmoebaCnt2[group_nr]++;
3176 static void PlayerWins(struct PlayerInfo *player)
3178 player->LevelSolved = TRUE;
3179 player->GameOver = TRUE;
3181 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3182 level.native_em_level->lev->score : player->score);
3187 static int time, time_final;
3188 static int score, score_final;
3189 static int game_over_delay_1 = 0;
3190 static int game_over_delay_2 = 0;
3191 int game_over_delay_value_1 = 50;
3192 int game_over_delay_value_2 = 50;
3194 if (!local_player->LevelSolved_GameWon)
3198 /* do not start end game actions before the player stops moving (to exit) */
3199 if (local_player->MovPos)
3202 local_player->LevelSolved_GameWon = TRUE;
3203 local_player->LevelSolved_SaveTape = tape.recording;
3204 local_player->LevelSolved_SaveScore = !tape.playing;
3206 if (tape.auto_play) /* tape might already be stopped here */
3207 tape.auto_play_level_solved = TRUE;
3213 game_over_delay_1 = game_over_delay_value_1;
3214 game_over_delay_2 = game_over_delay_value_2;
3216 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3217 score = score_final = local_player->score_final;
3222 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3224 else if (level.time == 0 && TimePlayed < 999)
3227 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3230 local_player->score_final = score_final;
3232 if (level_editor_test_game)
3235 score = score_final;
3237 DrawGameValue_Time(time);
3238 DrawGameValue_Score(score);
3241 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3243 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3245 /* close exit door after last player */
3246 if ((AllPlayersGone &&
3247 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3248 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3249 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3250 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3251 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3253 int element = Feld[ExitX][ExitY];
3256 if (element == EL_EM_EXIT_OPEN ||
3257 element == EL_EM_STEEL_EXIT_OPEN)
3264 Feld[ExitX][ExitY] =
3265 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3266 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3267 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3268 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3269 EL_EM_STEEL_EXIT_CLOSING);
3271 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3275 /* player disappears */
3276 DrawLevelField(ExitX, ExitY);
3279 for (i = 0; i < MAX_PLAYERS; i++)
3281 struct PlayerInfo *player = &stored_player[i];
3283 if (player->present)
3285 RemovePlayer(player);
3287 /* player disappears */
3288 DrawLevelField(player->jx, player->jy);
3293 PlaySound(SND_GAME_WINNING);
3296 if (game_over_delay_1 > 0)
3298 game_over_delay_1--;
3303 if (time != time_final)
3305 int time_to_go = ABS(time_final - time);
3306 int time_count_dir = (time < time_final ? +1 : -1);
3307 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3309 time += time_count_steps * time_count_dir;
3310 score += time_count_steps * level.score[SC_TIME_BONUS];
3312 DrawGameValue_Time(time);
3313 DrawGameValue_Score(score);
3315 if (time == time_final)
3316 StopSound(SND_GAME_LEVELTIME_BONUS);
3317 else if (setup.sound_loops)
3318 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3320 PlaySound(SND_GAME_LEVELTIME_BONUS);
3325 local_player->LevelSolved_PanelOff = TRUE;
3327 if (game_over_delay_2 > 0)
3329 game_over_delay_2--;
3342 boolean raise_level = FALSE;
3344 local_player->LevelSolved_GameEnd = TRUE;
3346 CloseDoor(DOOR_CLOSE_1);
3348 if (local_player->LevelSolved_SaveTape)
3355 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3357 SaveTape(tape.level_nr); /* ask to save tape */
3361 if (level_editor_test_game)
3363 game_status = GAME_MODE_MAIN;
3370 if (!local_player->LevelSolved_SaveScore)
3372 FadeOut(REDRAW_FIELD);
3374 game_status = GAME_MODE_MAIN;
3376 DrawAndFadeInMainMenu(REDRAW_FIELD);
3381 if (level_nr == leveldir_current->handicap_level)
3383 leveldir_current->handicap_level++;
3384 SaveLevelSetup_SeriesInfo();
3387 if (level_nr < leveldir_current->last_level)
3388 raise_level = TRUE; /* advance to next level */
3390 if ((hi_pos = NewHiScore()) >= 0)
3392 game_status = GAME_MODE_SCORES;
3394 DrawHallOfFame(hi_pos);
3404 FadeOut(REDRAW_FIELD);
3406 game_status = GAME_MODE_MAIN;
3414 DrawAndFadeInMainMenu(REDRAW_FIELD);
3423 LoadScore(level_nr);
3425 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3426 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3429 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3431 if (local_player->score_final > highscore[k].Score)
3433 /* player has made it to the hall of fame */
3435 if (k < MAX_SCORE_ENTRIES - 1)
3437 int m = MAX_SCORE_ENTRIES - 1;
3440 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3441 if (strEqual(setup.player_name, highscore[l].Name))
3443 if (m == k) /* player's new highscore overwrites his old one */
3447 for (l = m; l > k; l--)
3449 strcpy(highscore[l].Name, highscore[l - 1].Name);
3450 highscore[l].Score = highscore[l - 1].Score;
3457 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3458 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3459 highscore[k].Score = local_player->score_final;
3465 else if (!strncmp(setup.player_name, highscore[k].Name,
3466 MAX_PLAYER_NAME_LEN))
3467 break; /* player already there with a higher score */
3473 SaveScore(level_nr);
3478 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3480 int element = Feld[x][y];
3481 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3482 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3483 int horiz_move = (dx != 0);
3484 int sign = (horiz_move ? dx : dy);
3485 int step = sign * element_info[element].move_stepsize;
3487 /* special values for move stepsize for spring and things on conveyor belt */
3490 if (CAN_FALL(element) &&
3491 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3492 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3493 else if (element == EL_SPRING)
3494 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3500 inline static int getElementMoveStepsize(int x, int y)
3502 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3505 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3507 if (player->GfxAction != action || player->GfxDir != dir)
3510 printf("Player frame reset! (%d => %d, %d => %d)\n",
3511 player->GfxAction, action, player->GfxDir, dir);
3514 player->GfxAction = action;
3515 player->GfxDir = dir;
3517 player->StepFrame = 0;
3521 #if USE_GFX_RESET_GFX_ANIMATION
3522 static void ResetGfxFrame(int x, int y, boolean redraw)
3524 int element = Feld[x][y];
3525 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3526 int last_gfx_frame = GfxFrame[x][y];
3528 if (graphic_info[graphic].anim_global_sync)
3529 GfxFrame[x][y] = FrameCounter;
3530 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3531 GfxFrame[x][y] = CustomValue[x][y];
3532 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3533 GfxFrame[x][y] = element_info[element].collect_score;
3534 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3535 GfxFrame[x][y] = ChangeDelay[x][y];
3537 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3538 DrawLevelGraphicAnimation(x, y, graphic);
3542 static void ResetGfxAnimation(int x, int y)
3544 GfxAction[x][y] = ACTION_DEFAULT;
3545 GfxDir[x][y] = MovDir[x][y];
3548 #if USE_GFX_RESET_GFX_ANIMATION
3549 ResetGfxFrame(x, y, FALSE);
3553 static void ResetRandomAnimationValue(int x, int y)
3555 GfxRandom[x][y] = INIT_GFX_RANDOM();
3558 void InitMovingField(int x, int y, int direction)
3560 int element = Feld[x][y];
3561 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3562 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3565 boolean is_moving_before, is_moving_after;
3567 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3570 /* check if element was/is moving or being moved before/after mode change */
3573 is_moving_before = (WasJustMoving[x][y] != 0);
3575 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3576 is_moving_before = WasJustMoving[x][y];
3579 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3581 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3583 /* reset animation only for moving elements which change direction of moving
3584 or which just started or stopped moving
3585 (else CEs with property "can move" / "not moving" are reset each frame) */
3586 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3588 if (is_moving_before != is_moving_after ||
3589 direction != MovDir[x][y])
3590 ResetGfxAnimation(x, y);
3592 if ((is_moving_before || is_moving_after) && !continues_moving)
3593 ResetGfxAnimation(x, y);
3596 if (!continues_moving)
3597 ResetGfxAnimation(x, y);
3600 MovDir[x][y] = direction;
3601 GfxDir[x][y] = direction;
3603 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3604 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3605 direction == MV_DOWN && CAN_FALL(element) ?
3606 ACTION_FALLING : ACTION_MOVING);
3608 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3609 ACTION_FALLING : ACTION_MOVING);
3612 /* this is needed for CEs with property "can move" / "not moving" */
3614 if (is_moving_after)
3616 if (Feld[newx][newy] == EL_EMPTY)
3617 Feld[newx][newy] = EL_BLOCKED;
3619 MovDir[newx][newy] = MovDir[x][y];
3621 #if USE_NEW_CUSTOM_VALUE
3622 CustomValue[newx][newy] = CustomValue[x][y];
3625 GfxFrame[newx][newy] = GfxFrame[x][y];
3626 GfxRandom[newx][newy] = GfxRandom[x][y];
3627 GfxAction[newx][newy] = GfxAction[x][y];
3628 GfxDir[newx][newy] = GfxDir[x][y];
3632 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3634 int direction = MovDir[x][y];
3635 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3636 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3642 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3644 int oldx = x, oldy = y;
3645 int direction = MovDir[x][y];
3647 if (direction == MV_LEFT)
3649 else if (direction == MV_RIGHT)
3651 else if (direction == MV_UP)
3653 else if (direction == MV_DOWN)
3656 *comes_from_x = oldx;
3657 *comes_from_y = oldy;
3660 int MovingOrBlocked2Element(int x, int y)
3662 int element = Feld[x][y];
3664 if (element == EL_BLOCKED)
3668 Blocked2Moving(x, y, &oldx, &oldy);
3669 return Feld[oldx][oldy];
3675 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3677 /* like MovingOrBlocked2Element(), but if element is moving
3678 and (x,y) is the field the moving element is just leaving,
3679 return EL_BLOCKED instead of the element value */
3680 int element = Feld[x][y];
3682 if (IS_MOVING(x, y))
3684 if (element == EL_BLOCKED)
3688 Blocked2Moving(x, y, &oldx, &oldy);
3689 return Feld[oldx][oldy];
3698 static void RemoveField(int x, int y)
3700 Feld[x][y] = EL_EMPTY;
3706 #if USE_NEW_CUSTOM_VALUE
3707 CustomValue[x][y] = 0;
3711 ChangeDelay[x][y] = 0;
3712 ChangePage[x][y] = -1;
3713 Pushed[x][y] = FALSE;
3716 ExplodeField[x][y] = EX_TYPE_NONE;
3719 GfxElement[x][y] = EL_UNDEFINED;
3720 GfxAction[x][y] = ACTION_DEFAULT;
3721 GfxDir[x][y] = MV_NONE;
3724 void RemoveMovingField(int x, int y)
3726 int oldx = x, oldy = y, newx = x, newy = y;
3727 int element = Feld[x][y];
3728 int next_element = EL_UNDEFINED;
3730 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3733 if (IS_MOVING(x, y))
3735 Moving2Blocked(x, y, &newx, &newy);
3737 if (Feld[newx][newy] != EL_BLOCKED)
3739 /* element is moving, but target field is not free (blocked), but
3740 already occupied by something different (example: acid pool);
3741 in this case, only remove the moving field, but not the target */
3743 RemoveField(oldx, oldy);
3745 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3747 DrawLevelField(oldx, oldy);
3752 else if (element == EL_BLOCKED)
3754 Blocked2Moving(x, y, &oldx, &oldy);
3755 if (!IS_MOVING(oldx, oldy))
3759 if (element == EL_BLOCKED &&
3760 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3761 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3762 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3763 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3764 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3765 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3766 next_element = get_next_element(Feld[oldx][oldy]);
3768 RemoveField(oldx, oldy);
3769 RemoveField(newx, newy);
3771 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3773 if (next_element != EL_UNDEFINED)
3774 Feld[oldx][oldy] = next_element;
3776 DrawLevelField(oldx, oldy);
3777 DrawLevelField(newx, newy);
3780 void DrawDynamite(int x, int y)
3782 int sx = SCREENX(x), sy = SCREENY(y);
3783 int graphic = el2img(Feld[x][y]);
3786 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3789 if (IS_WALKABLE_INSIDE(Back[x][y]))
3793 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3794 else if (Store[x][y])
3795 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3797 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3799 if (Back[x][y] || Store[x][y])
3800 DrawGraphicThruMask(sx, sy, graphic, frame);
3802 DrawGraphic(sx, sy, graphic, frame);
3805 void CheckDynamite(int x, int y)
3807 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3811 if (MovDelay[x][y] != 0)
3814 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3820 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3825 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3827 boolean num_checked_players = 0;
3830 for (i = 0; i < MAX_PLAYERS; i++)
3832 if (stored_player[i].active)
3834 int sx = stored_player[i].jx;
3835 int sy = stored_player[i].jy;
3837 if (num_checked_players == 0)
3844 *sx1 = MIN(*sx1, sx);
3845 *sy1 = MIN(*sy1, sy);
3846 *sx2 = MAX(*sx2, sx);
3847 *sy2 = MAX(*sy2, sy);
3850 num_checked_players++;
3855 static boolean checkIfAllPlayersFitToScreen_RND()
3857 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3859 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3861 return (sx2 - sx1 < SCR_FIELDX &&
3862 sy2 - sy1 < SCR_FIELDY);
3865 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3867 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3869 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3871 *sx = (sx1 + sx2) / 2;
3872 *sy = (sy1 + sy2) / 2;
3875 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3876 boolean center_screen, boolean quick_relocation)
3878 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3879 boolean no_delay = (tape.warp_forward);
3880 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3881 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3883 if (quick_relocation)
3885 int offset = (setup.scroll_delay ? 3 : 0);
3887 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3891 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3892 x > SBX_Right + MIDPOSX ? SBX_Right :
3895 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3896 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3901 /* quick relocation (without scrolling), but do not center screen */
3903 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3904 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3907 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3911 int offset_x = x + (scroll_x - center_scroll_x);
3912 int offset_y = y + (scroll_y - center_scroll_y);
3914 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3915 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3916 offset_x - MIDPOSX);
3918 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3919 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3920 offset_y - MIDPOSY);
3925 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3926 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3927 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3929 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3930 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3931 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3933 /* don't scroll over playfield boundaries */
3934 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3935 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3937 /* don't scroll over playfield boundaries */
3938 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3939 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3942 RedrawPlayfield(TRUE, 0,0,0,0);
3946 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3947 x > SBX_Right + MIDPOSX ? SBX_Right :
3950 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3951 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3954 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3956 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3959 int fx = FX, fy = FY;
3961 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3962 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3964 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3970 fx += dx * TILEX / 2;
3971 fy += dy * TILEY / 2;
3973 ScrollLevel(dx, dy);
3976 /* scroll in two steps of half tile size to make things smoother */
3977 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3979 Delay(wait_delay_value);
3981 /* scroll second step to align at full tile size */
3983 Delay(wait_delay_value);
3988 Delay(wait_delay_value);
3992 void RelocatePlayer(int jx, int jy, int el_player_raw)
3994 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3995 int player_nr = GET_PLAYER_NR(el_player);
3996 struct PlayerInfo *player = &stored_player[player_nr];
3997 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3998 boolean no_delay = (tape.warp_forward);
3999 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4000 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4001 int old_jx = player->jx;
4002 int old_jy = player->jy;
4003 int old_element = Feld[old_jx][old_jy];
4004 int element = Feld[jx][jy];
4005 boolean player_relocated = (old_jx != jx || old_jy != jy);
4007 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4008 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4009 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4010 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4011 int leave_side_horiz = move_dir_horiz;
4012 int leave_side_vert = move_dir_vert;
4013 int enter_side = enter_side_horiz | enter_side_vert;
4014 int leave_side = leave_side_horiz | leave_side_vert;
4016 if (player->GameOver) /* do not reanimate dead player */
4019 if (!player_relocated) /* no need to relocate the player */
4022 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4024 RemoveField(jx, jy); /* temporarily remove newly placed player */
4025 DrawLevelField(jx, jy);
4028 if (player->present)
4030 while (player->MovPos)
4032 ScrollPlayer(player, SCROLL_GO_ON);
4033 ScrollScreen(NULL, SCROLL_GO_ON);
4035 AdvanceFrameAndPlayerCounters(player->index_nr);
4040 Delay(wait_delay_value);
4043 DrawPlayer(player); /* needed here only to cleanup last field */
4044 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4046 player->is_moving = FALSE;
4049 if (IS_CUSTOM_ELEMENT(old_element))
4050 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4052 player->index_bit, leave_side);
4054 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4056 player->index_bit, leave_side);
4058 Feld[jx][jy] = el_player;
4059 InitPlayerField(jx, jy, el_player, TRUE);
4061 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4063 Feld[jx][jy] = element;
4064 InitField(jx, jy, FALSE);
4067 /* only visually relocate centered player */
4068 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4069 FALSE, level.instant_relocation);
4071 TestIfPlayerTouchesBadThing(jx, jy);
4072 TestIfPlayerTouchesCustomElement(jx, jy);
4074 if (IS_CUSTOM_ELEMENT(element))
4075 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4076 player->index_bit, enter_side);
4078 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4079 player->index_bit, enter_side);
4082 void Explode(int ex, int ey, int phase, int mode)
4088 /* !!! eliminate this variable !!! */
4089 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4091 if (game.explosions_delayed)
4093 ExplodeField[ex][ey] = mode;
4097 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4099 int center_element = Feld[ex][ey];
4100 int artwork_element, explosion_element; /* set these values later */
4103 /* --- This is only really needed (and now handled) in "Impact()". --- */
4104 /* do not explode moving elements that left the explode field in time */
4105 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4106 center_element == EL_EMPTY &&
4107 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4112 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4113 if (mode == EX_TYPE_NORMAL ||
4114 mode == EX_TYPE_CENTER ||
4115 mode == EX_TYPE_CROSS)
4116 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4119 /* remove things displayed in background while burning dynamite */
4120 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4123 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4125 /* put moving element to center field (and let it explode there) */
4126 center_element = MovingOrBlocked2Element(ex, ey);
4127 RemoveMovingField(ex, ey);
4128 Feld[ex][ey] = center_element;
4131 /* now "center_element" is finally determined -- set related values now */
4132 artwork_element = center_element; /* for custom player artwork */
4133 explosion_element = center_element; /* for custom player artwork */
4135 if (IS_PLAYER(ex, ey))
4137 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4139 artwork_element = stored_player[player_nr].artwork_element;
4141 if (level.use_explosion_element[player_nr])
4143 explosion_element = level.explosion_element[player_nr];
4144 artwork_element = explosion_element;
4149 if (mode == EX_TYPE_NORMAL ||
4150 mode == EX_TYPE_CENTER ||
4151 mode == EX_TYPE_CROSS)
4152 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4155 last_phase = element_info[explosion_element].explosion_delay + 1;
4157 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4159 int xx = x - ex + 1;
4160 int yy = y - ey + 1;
4163 if (!IN_LEV_FIELD(x, y) ||
4164 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4165 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4168 element = Feld[x][y];
4170 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4172 element = MovingOrBlocked2Element(x, y);
4174 if (!IS_EXPLOSION_PROOF(element))
4175 RemoveMovingField(x, y);
4178 /* indestructible elements can only explode in center (but not flames) */
4179 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4180 mode == EX_TYPE_BORDER)) ||
4181 element == EL_FLAMES)
4184 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4185 behaviour, for example when touching a yamyam that explodes to rocks
4186 with active deadly shield, a rock is created under the player !!! */
4187 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4189 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4190 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4191 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4193 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4196 if (IS_ACTIVE_BOMB(element))
4198 /* re-activate things under the bomb like gate or penguin */
4199 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4206 /* save walkable background elements while explosion on same tile */
4207 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4208 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4209 Back[x][y] = element;
4211 /* ignite explodable elements reached by other explosion */
4212 if (element == EL_EXPLOSION)
4213 element = Store2[x][y];
4215 if (AmoebaNr[x][y] &&
4216 (element == EL_AMOEBA_FULL ||
4217 element == EL_BD_AMOEBA ||
4218 element == EL_AMOEBA_GROWING))
4220 AmoebaCnt[AmoebaNr[x][y]]--;
4221 AmoebaCnt2[AmoebaNr[x][y]]--;
4226 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4228 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4230 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4232 if (PLAYERINFO(ex, ey)->use_murphy)
4233 Store[x][y] = EL_EMPTY;
4236 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4237 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4238 else if (ELEM_IS_PLAYER(center_element))
4239 Store[x][y] = EL_EMPTY;
4240 else if (center_element == EL_YAMYAM)
4241 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4242 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4243 Store[x][y] = element_info[center_element].content.e[xx][yy];
4245 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4246 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4247 otherwise) -- FIX THIS !!! */
4248 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4249 Store[x][y] = element_info[element].content.e[1][1];
4251 else if (!CAN_EXPLODE(element))
4252 Store[x][y] = element_info[element].content.e[1][1];
4255 Store[x][y] = EL_EMPTY;
4257 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4258 center_element == EL_AMOEBA_TO_DIAMOND)
4259 Store2[x][y] = element;
4261 Feld[x][y] = EL_EXPLOSION;
4262 GfxElement[x][y] = artwork_element;
4264 ExplodePhase[x][y] = 1;
4265 ExplodeDelay[x][y] = last_phase;
4270 if (center_element == EL_YAMYAM)
4271 game.yamyam_content_nr =
4272 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4284 GfxFrame[x][y] = 0; /* restart explosion animation */
4286 last_phase = ExplodeDelay[x][y];
4288 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4292 /* activate this even in non-DEBUG version until cause for crash in
4293 getGraphicAnimationFrame() (see below) is found and eliminated */
4299 /* this can happen if the player leaves an explosion just in time */
4300 if (GfxElement[x][y] == EL_UNDEFINED)
4301 GfxElement[x][y] = EL_EMPTY;
4303 if (GfxElement[x][y] == EL_UNDEFINED)
4306 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4307 printf("Explode(): This should never happen!\n");
4310 GfxElement[x][y] = EL_EMPTY;
4316 border_element = Store2[x][y];
4317 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4318 border_element = StorePlayer[x][y];
4320 if (phase == element_info[border_element].ignition_delay ||
4321 phase == last_phase)
4323 boolean border_explosion = FALSE;
4325 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4326 !PLAYER_EXPLOSION_PROTECTED(x, y))
4328 KillPlayerUnlessExplosionProtected(x, y);
4329 border_explosion = TRUE;
4331 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4333 Feld[x][y] = Store2[x][y];
4336 border_explosion = TRUE;
4338 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4340 AmoebeUmwandeln(x, y);
4342 border_explosion = TRUE;
4345 /* if an element just explodes due to another explosion (chain-reaction),
4346 do not immediately end the new explosion when it was the last frame of
4347 the explosion (as it would be done in the following "if"-statement!) */
4348 if (border_explosion && phase == last_phase)
4352 if (phase == last_phase)
4356 element = Feld[x][y] = Store[x][y];
4357 Store[x][y] = Store2[x][y] = 0;
4358 GfxElement[x][y] = EL_UNDEFINED;
4360 /* player can escape from explosions and might therefore be still alive */
4361 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4362 element <= EL_PLAYER_IS_EXPLODING_4)
4364 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4365 int explosion_element = EL_PLAYER_1 + player_nr;
4366 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4367 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4369 if (level.use_explosion_element[player_nr])
4370 explosion_element = level.explosion_element[player_nr];
4372 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4373 element_info[explosion_element].content.e[xx][yy]);
4376 /* restore probably existing indestructible background element */
4377 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4378 element = Feld[x][y] = Back[x][y];
4381 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4382 GfxDir[x][y] = MV_NONE;
4383 ChangeDelay[x][y] = 0;
4384 ChangePage[x][y] = -1;
4386 #if USE_NEW_CUSTOM_VALUE
4387 CustomValue[x][y] = 0;
4390 InitField_WithBug2(x, y, FALSE);
4392 DrawLevelField(x, y);
4394 TestIfElementTouchesCustomElement(x, y);
4396 if (GFX_CRUMBLED(element))
4397 DrawLevelFieldCrumbledSandNeighbours(x, y);
4399 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4400 StorePlayer[x][y] = 0;
4402 if (ELEM_IS_PLAYER(element))
4403 RelocatePlayer(x, y, element);
4405 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4407 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4408 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4411 DrawLevelFieldCrumbledSand(x, y);
4413 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4415 DrawLevelElement(x, y, Back[x][y]);
4416 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4418 else if (IS_WALKABLE_UNDER(Back[x][y]))
4420 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4421 DrawLevelElementThruMask(x, y, Back[x][y]);
4423 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4424 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4428 void DynaExplode(int ex, int ey)
4431 int dynabomb_element = Feld[ex][ey];
4432 int dynabomb_size = 1;
4433 boolean dynabomb_xl = FALSE;
4434 struct PlayerInfo *player;
4435 static int xy[4][2] =
4443 if (IS_ACTIVE_BOMB(dynabomb_element))
4445 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4446 dynabomb_size = player->dynabomb_size;
4447 dynabomb_xl = player->dynabomb_xl;
4448 player->dynabombs_left++;
4451 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4453 for (i = 0; i < NUM_DIRECTIONS; i++)
4455 for (j = 1; j <= dynabomb_size; j++)
4457 int x = ex + j * xy[i][0];
4458 int y = ey + j * xy[i][1];
4461 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4464 element = Feld[x][y];
4466 /* do not restart explosions of fields with active bombs */
4467 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4470 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4472 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4473 !IS_DIGGABLE(element) && !dynabomb_xl)
4479 void Bang(int x, int y)
4481 int element = MovingOrBlocked2Element(x, y);
4482 int explosion_type = EX_TYPE_NORMAL;
4484 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4486 struct PlayerInfo *player = PLAYERINFO(x, y);
4488 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4489 player->element_nr);
4491 if (level.use_explosion_element[player->index_nr])
4493 int explosion_element = level.explosion_element[player->index_nr];
4495 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4496 explosion_type = EX_TYPE_CROSS;
4497 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4498 explosion_type = EX_TYPE_CENTER;
4506 case EL_BD_BUTTERFLY:
4509 case EL_DARK_YAMYAM:
4513 RaiseScoreElement(element);
4516 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4517 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4518 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4519 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4520 case EL_DYNABOMB_INCREASE_NUMBER:
4521 case EL_DYNABOMB_INCREASE_SIZE:
4522 case EL_DYNABOMB_INCREASE_POWER:
4523 explosion_type = EX_TYPE_DYNA;
4526 case EL_DC_LANDMINE:
4528 case EL_EM_EXIT_OPEN:
4529 case EL_EM_STEEL_EXIT_OPEN:
4531 explosion_type = EX_TYPE_CENTER;
4536 case EL_LAMP_ACTIVE:
4537 case EL_AMOEBA_TO_DIAMOND:
4538 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4539 explosion_type = EX_TYPE_CENTER;
4543 if (element_info[element].explosion_type == EXPLODES_CROSS)
4544 explosion_type = EX_TYPE_CROSS;
4545 else if (element_info[element].explosion_type == EXPLODES_1X1)
4546 explosion_type = EX_TYPE_CENTER;
4550 if (explosion_type == EX_TYPE_DYNA)
4553 Explode(x, y, EX_PHASE_START, explosion_type);
4555 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4558 void SplashAcid(int x, int y)
4560 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4561 (!IN_LEV_FIELD(x - 1, y - 2) ||
4562 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4563 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4565 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4566 (!IN_LEV_FIELD(x + 1, y - 2) ||
4567 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4568 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4570 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4573 static void InitBeltMovement()
4575 static int belt_base_element[4] =
4577 EL_CONVEYOR_BELT_1_LEFT,
4578 EL_CONVEYOR_BELT_2_LEFT,
4579 EL_CONVEYOR_BELT_3_LEFT,
4580 EL_CONVEYOR_BELT_4_LEFT
4582 static int belt_base_active_element[4] =
4584 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4585 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4586 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4587 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4592 /* set frame order for belt animation graphic according to belt direction */
4593 for (i = 0; i < NUM_BELTS; i++)
4597 for (j = 0; j < NUM_BELT_PARTS; j++)
4599 int element = belt_base_active_element[belt_nr] + j;
4600 int graphic = el2img(element);
4602 if (game.belt_dir[i] == MV_LEFT)
4603 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4605 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4609 SCAN_PLAYFIELD(x, y)
4611 int element = Feld[x][y];
4613 for (i = 0; i < NUM_BELTS; i++)
4615 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4617 int e_belt_nr = getBeltNrFromBeltElement(element);
4620 if (e_belt_nr == belt_nr)
4622 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4624 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4631 static void ToggleBeltSwitch(int x, int y)
4633 static int belt_base_element[4] =
4635 EL_CONVEYOR_BELT_1_LEFT,
4636 EL_CONVEYOR_BELT_2_LEFT,
4637 EL_CONVEYOR_BELT_3_LEFT,
4638 EL_CONVEYOR_BELT_4_LEFT
4640 static int belt_base_active_element[4] =
4642 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4643 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4644 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4645 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4647 static int belt_base_switch_element[4] =
4649 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4650 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4651 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4652 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4654 static int belt_move_dir[4] =
4662 int element = Feld[x][y];
4663 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4664 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4665 int belt_dir = belt_move_dir[belt_dir_nr];
4668 if (!IS_BELT_SWITCH(element))
4671 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4672 game.belt_dir[belt_nr] = belt_dir;
4674 if (belt_dir_nr == 3)
4677 /* set frame order for belt animation graphic according to belt direction */
4678 for (i = 0; i < NUM_BELT_PARTS; i++)
4680 int element = belt_base_active_element[belt_nr] + i;
4681 int graphic = el2img(element);
4683 if (belt_dir == MV_LEFT)
4684 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4686 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4689 SCAN_PLAYFIELD(xx, yy)
4691 int element = Feld[xx][yy];
4693 if (IS_BELT_SWITCH(element))
4695 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4697 if (e_belt_nr == belt_nr)
4699 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4700 DrawLevelField(xx, yy);
4703 else if (IS_BELT(element) && belt_dir != MV_NONE)
4705 int e_belt_nr = getBeltNrFromBeltElement(element);
4707 if (e_belt_nr == belt_nr)
4709 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4711 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4712 DrawLevelField(xx, yy);
4715 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4717 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4719 if (e_belt_nr == belt_nr)
4721 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4723 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4724 DrawLevelField(xx, yy);
4730 static void ToggleSwitchgateSwitch(int x, int y)
4734 game.switchgate_pos = !game.switchgate_pos;
4736 SCAN_PLAYFIELD(xx, yy)
4738 int element = Feld[xx][yy];
4740 #if !USE_BOTH_SWITCHGATE_SWITCHES
4741 if (element == EL_SWITCHGATE_SWITCH_UP ||
4742 element == EL_SWITCHGATE_SWITCH_DOWN)
4744 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4745 DrawLevelField(xx, yy);
4747 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4748 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4750 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4751 DrawLevelField(xx, yy);
4754 if (element == EL_SWITCHGATE_SWITCH_UP)
4756 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4757 DrawLevelField(xx, yy);
4759 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4761 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4762 DrawLevelField(xx, yy);
4764 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4766 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4767 DrawLevelField(xx, yy);
4769 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4771 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4772 DrawLevelField(xx, yy);
4775 else if (element == EL_SWITCHGATE_OPEN ||
4776 element == EL_SWITCHGATE_OPENING)
4778 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4780 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4782 else if (element == EL_SWITCHGATE_CLOSED ||
4783 element == EL_SWITCHGATE_CLOSING)
4785 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4787 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4792 static int getInvisibleActiveFromInvisibleElement(int element)
4794 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4795 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4796 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4800 static int getInvisibleFromInvisibleActiveElement(int element)
4802 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4803 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4804 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4808 static void RedrawAllLightSwitchesAndInvisibleElements()
4812 SCAN_PLAYFIELD(x, y)
4814 int element = Feld[x][y];
4816 if (element == EL_LIGHT_SWITCH &&
4817 game.light_time_left > 0)
4819 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4820 DrawLevelField(x, y);
4822 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4823 game.light_time_left == 0)
4825 Feld[x][y] = EL_LIGHT_SWITCH;
4826 DrawLevelField(x, y);
4828 else if (element == EL_EMC_DRIPPER &&
4829 game.light_time_left > 0)
4831 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4832 DrawLevelField(x, y);
4834 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4835 game.light_time_left == 0)
4837 Feld[x][y] = EL_EMC_DRIPPER;
4838 DrawLevelField(x, y);
4840 else if (element == EL_INVISIBLE_STEELWALL ||
4841 element == EL_INVISIBLE_WALL ||
4842 element == EL_INVISIBLE_SAND)
4844 if (game.light_time_left > 0)
4845 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4847 DrawLevelField(x, y);
4849 /* uncrumble neighbour fields, if needed */
4850 if (element == EL_INVISIBLE_SAND)
4851 DrawLevelFieldCrumbledSandNeighbours(x, y);
4853 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4854 element == EL_INVISIBLE_WALL_ACTIVE ||
4855 element == EL_INVISIBLE_SAND_ACTIVE)
4857 if (game.light_time_left == 0)
4858 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4860 DrawLevelField(x, y);
4862 /* re-crumble neighbour fields, if needed */
4863 if (element == EL_INVISIBLE_SAND)
4864 DrawLevelFieldCrumbledSandNeighbours(x, y);
4869 static void RedrawAllInvisibleElementsForLenses()
4873 SCAN_PLAYFIELD(x, y)
4875 int element = Feld[x][y];
4877 if (element == EL_EMC_DRIPPER &&
4878 game.lenses_time_left > 0)
4880 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4881 DrawLevelField(x, y);
4883 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4884 game.lenses_time_left == 0)
4886 Feld[x][y] = EL_EMC_DRIPPER;
4887 DrawLevelField(x, y);
4889 else if (element == EL_INVISIBLE_STEELWALL ||
4890 element == EL_INVISIBLE_WALL ||
4891 element == EL_INVISIBLE_SAND)
4893 if (game.lenses_time_left > 0)
4894 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4896 DrawLevelField(x, y);
4898 /* uncrumble neighbour fields, if needed */
4899 if (element == EL_INVISIBLE_SAND)
4900 DrawLevelFieldCrumbledSandNeighbours(x, y);
4902 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4903 element == EL_INVISIBLE_WALL_ACTIVE ||
4904 element == EL_INVISIBLE_SAND_ACTIVE)
4906 if (game.lenses_time_left == 0)
4907 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4909 DrawLevelField(x, y);
4911 /* re-crumble neighbour fields, if needed */
4912 if (element == EL_INVISIBLE_SAND)
4913 DrawLevelFieldCrumbledSandNeighbours(x, y);
4918 static void RedrawAllInvisibleElementsForMagnifier()
4922 SCAN_PLAYFIELD(x, y)
4924 int element = Feld[x][y];
4926 if (element == EL_EMC_FAKE_GRASS &&
4927 game.magnify_time_left > 0)
4929 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4930 DrawLevelField(x, y);
4932 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4933 game.magnify_time_left == 0)
4935 Feld[x][y] = EL_EMC_FAKE_GRASS;
4936 DrawLevelField(x, y);
4938 else if (IS_GATE_GRAY(element) &&
4939 game.magnify_time_left > 0)
4941 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4942 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4943 IS_EM_GATE_GRAY(element) ?
4944 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4945 IS_EMC_GATE_GRAY(element) ?
4946 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4948 DrawLevelField(x, y);
4950 else if (IS_GATE_GRAY_ACTIVE(element) &&
4951 game.magnify_time_left == 0)
4953 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4954 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4955 IS_EM_GATE_GRAY_ACTIVE(element) ?
4956 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4957 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4958 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4960 DrawLevelField(x, y);
4965 static void ToggleLightSwitch(int x, int y)
4967 int element = Feld[x][y];
4969 game.light_time_left =
4970 (element == EL_LIGHT_SWITCH ?
4971 level.time_light * FRAMES_PER_SECOND : 0);
4973 RedrawAllLightSwitchesAndInvisibleElements();
4976 static void ActivateTimegateSwitch(int x, int y)
4980 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4982 SCAN_PLAYFIELD(xx, yy)
4984 int element = Feld[xx][yy];
4986 if (element == EL_TIMEGATE_CLOSED ||
4987 element == EL_TIMEGATE_CLOSING)
4989 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4990 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4994 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4996 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4997 DrawLevelField(xx, yy);
5004 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5005 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5007 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5011 void Impact(int x, int y)
5013 boolean last_line = (y == lev_fieldy - 1);
5014 boolean object_hit = FALSE;
5015 boolean impact = (last_line || object_hit);
5016 int element = Feld[x][y];
5017 int smashed = EL_STEELWALL;
5019 if (!last_line) /* check if element below was hit */
5021 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5024 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5025 MovDir[x][y + 1] != MV_DOWN ||
5026 MovPos[x][y + 1] <= TILEY / 2));
5028 /* do not smash moving elements that left the smashed field in time */
5029 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5030 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5033 #if USE_QUICKSAND_IMPACT_BUGFIX
5034 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5036 RemoveMovingField(x, y + 1);
5037 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5038 Feld[x][y + 2] = EL_ROCK;
5039 DrawLevelField(x, y + 2);
5044 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5046 RemoveMovingField(x, y + 1);
5047 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5048 Feld[x][y + 2] = EL_ROCK;
5049 DrawLevelField(x, y + 2);
5056 smashed = MovingOrBlocked2Element(x, y + 1);
5058 impact = (last_line || object_hit);
5061 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5063 SplashAcid(x, y + 1);
5067 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5068 /* only reset graphic animation if graphic really changes after impact */
5070 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5072 ResetGfxAnimation(x, y);
5073 DrawLevelField(x, y);
5076 if (impact && CAN_EXPLODE_IMPACT(element))
5081 else if (impact && element == EL_PEARL &&
5082 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5084 ResetGfxAnimation(x, y);
5086 Feld[x][y] = EL_PEARL_BREAKING;
5087 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5090 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5092 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5097 if (impact && element == EL_AMOEBA_DROP)
5099 if (object_hit && IS_PLAYER(x, y + 1))
5100 KillPlayerUnlessEnemyProtected(x, y + 1);
5101 else if (object_hit && smashed == EL_PENGUIN)
5105 Feld[x][y] = EL_AMOEBA_GROWING;
5106 Store[x][y] = EL_AMOEBA_WET;
5108 ResetRandomAnimationValue(x, y);
5113 if (object_hit) /* check which object was hit */
5115 if ((CAN_PASS_MAGIC_WALL(element) &&
5116 (smashed == EL_MAGIC_WALL ||
5117 smashed == EL_BD_MAGIC_WALL)) ||
5118 (CAN_PASS_DC_MAGIC_WALL(element) &&
5119 smashed == EL_DC_MAGIC_WALL))
5122 int activated_magic_wall =
5123 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5124 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5125 EL_DC_MAGIC_WALL_ACTIVE);
5127 /* activate magic wall / mill */
5128 SCAN_PLAYFIELD(xx, yy)
5130 if (Feld[xx][yy] == smashed)
5131 Feld[xx][yy] = activated_magic_wall;
5134 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5135 game.magic_wall_active = TRUE;
5137 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5138 SND_MAGIC_WALL_ACTIVATING :
5139 smashed == EL_BD_MAGIC_WALL ?
5140 SND_BD_MAGIC_WALL_ACTIVATING :
5141 SND_DC_MAGIC_WALL_ACTIVATING));
5144 if (IS_PLAYER(x, y + 1))
5146 if (CAN_SMASH_PLAYER(element))
5148 KillPlayerUnlessEnemyProtected(x, y + 1);
5152 else if (smashed == EL_PENGUIN)
5154 if (CAN_SMASH_PLAYER(element))
5160 else if (element == EL_BD_DIAMOND)
5162 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5168 else if (((element == EL_SP_INFOTRON ||
5169 element == EL_SP_ZONK) &&
5170 (smashed == EL_SP_SNIKSNAK ||
5171 smashed == EL_SP_ELECTRON ||
5172 smashed == EL_SP_DISK_ORANGE)) ||
5173 (element == EL_SP_INFOTRON &&
5174 smashed == EL_SP_DISK_YELLOW))
5179 else if (CAN_SMASH_EVERYTHING(element))
5181 if (IS_CLASSIC_ENEMY(smashed) ||
5182 CAN_EXPLODE_SMASHED(smashed))
5187 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5189 if (smashed == EL_LAMP ||
5190 smashed == EL_LAMP_ACTIVE)
5195 else if (smashed == EL_NUT)
5197 Feld[x][y + 1] = EL_NUT_BREAKING;
5198 PlayLevelSound(x, y, SND_NUT_BREAKING);
5199 RaiseScoreElement(EL_NUT);
5202 else if (smashed == EL_PEARL)
5204 ResetGfxAnimation(x, y);
5206 Feld[x][y + 1] = EL_PEARL_BREAKING;
5207 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5210 else if (smashed == EL_DIAMOND)
5212 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5213 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5216 else if (IS_BELT_SWITCH(smashed))
5218 ToggleBeltSwitch(x, y + 1);
5220 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5221 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5222 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5223 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5225 ToggleSwitchgateSwitch(x, y + 1);
5227 else if (smashed == EL_LIGHT_SWITCH ||
5228 smashed == EL_LIGHT_SWITCH_ACTIVE)
5230 ToggleLightSwitch(x, y + 1);
5235 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5238 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5240 CheckElementChangeBySide(x, y + 1, smashed, element,
5241 CE_SWITCHED, CH_SIDE_TOP);
5242 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5248 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5253 /* play sound of magic wall / mill */
5255 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5256 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5257 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5259 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5260 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5261 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5262 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5263 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5264 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5269 /* play sound of object that hits the ground */
5270 if (last_line || object_hit)
5271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5274 inline static void TurnRoundExt(int x, int y)
5286 { 0, 0 }, { 0, 0 }, { 0, 0 },
5291 int left, right, back;
5295 { MV_DOWN, MV_UP, MV_RIGHT },
5296 { MV_UP, MV_DOWN, MV_LEFT },
5298 { MV_LEFT, MV_RIGHT, MV_DOWN },
5302 { MV_RIGHT, MV_LEFT, MV_UP }
5305 int element = Feld[x][y];
5306 int move_pattern = element_info[element].move_pattern;
5308 int old_move_dir = MovDir[x][y];
5309 int left_dir = turn[old_move_dir].left;
5310 int right_dir = turn[old_move_dir].right;
5311 int back_dir = turn[old_move_dir].back;
5313 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5314 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5315 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5316 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5318 int left_x = x + left_dx, left_y = y + left_dy;
5319 int right_x = x + right_dx, right_y = y + right_dy;
5320 int move_x = x + move_dx, move_y = y + move_dy;
5324 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5326 TestIfBadThingTouchesOtherBadThing(x, y);
5328 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5329 MovDir[x][y] = right_dir;
5330 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5331 MovDir[x][y] = left_dir;
5333 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5335 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5338 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5340 TestIfBadThingTouchesOtherBadThing(x, y);
5342 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5343 MovDir[x][y] = left_dir;
5344 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5345 MovDir[x][y] = right_dir;
5347 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5349 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5352 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5354 TestIfBadThingTouchesOtherBadThing(x, y);
5356 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5357 MovDir[x][y] = left_dir;
5358 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5359 MovDir[x][y] = right_dir;
5361 if (MovDir[x][y] != old_move_dir)
5364 else if (element == EL_YAMYAM)
5366 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5367 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5369 if (can_turn_left && can_turn_right)
5370 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5371 else if (can_turn_left)
5372 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5373 else if (can_turn_right)
5374 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5376 MovDir[x][y] = back_dir;
5378 MovDelay[x][y] = 16 + 16 * RND(3);
5380 else if (element == EL_DARK_YAMYAM)
5382 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5384 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5387 if (can_turn_left && can_turn_right)
5388 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5389 else if (can_turn_left)
5390 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5391 else if (can_turn_right)
5392 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5394 MovDir[x][y] = back_dir;
5396 MovDelay[x][y] = 16 + 16 * RND(3);
5398 else if (element == EL_PACMAN)
5400 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5401 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5403 if (can_turn_left && can_turn_right)
5404 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5405 else if (can_turn_left)
5406 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5407 else if (can_turn_right)
5408 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5410 MovDir[x][y] = back_dir;
5412 MovDelay[x][y] = 6 + RND(40);
5414 else if (element == EL_PIG)
5416 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5417 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5418 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5419 boolean should_turn_left, should_turn_right, should_move_on;
5421 int rnd = RND(rnd_value);
5423 should_turn_left = (can_turn_left &&
5425 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5426 y + back_dy + left_dy)));
5427 should_turn_right = (can_turn_right &&
5429 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5430 y + back_dy + right_dy)));
5431 should_move_on = (can_move_on &&
5434 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5435 y + move_dy + left_dy) ||
5436 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5437 y + move_dy + right_dy)));
5439 if (should_turn_left || should_turn_right || should_move_on)
5441 if (should_turn_left && should_turn_right && should_move_on)
5442 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5443 rnd < 2 * rnd_value / 3 ? right_dir :
5445 else if (should_turn_left && should_turn_right)
5446 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5447 else if (should_turn_left && should_move_on)
5448 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5449 else if (should_turn_right && should_move_on)
5450 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5451 else if (should_turn_left)
5452 MovDir[x][y] = left_dir;
5453 else if (should_turn_right)
5454 MovDir[x][y] = right_dir;
5455 else if (should_move_on)
5456 MovDir[x][y] = old_move_dir;
5458 else if (can_move_on && rnd > rnd_value / 8)
5459 MovDir[x][y] = old_move_dir;
5460 else if (can_turn_left && can_turn_right)
5461 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5462 else if (can_turn_left && rnd > rnd_value / 8)
5463 MovDir[x][y] = left_dir;
5464 else if (can_turn_right && rnd > rnd_value/8)
5465 MovDir[x][y] = right_dir;
5467 MovDir[x][y] = back_dir;
5469 xx = x + move_xy[MovDir[x][y]].dx;
5470 yy = y + move_xy[MovDir[x][y]].dy;
5472 if (!IN_LEV_FIELD(xx, yy) ||
5473 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5474 MovDir[x][y] = old_move_dir;
5478 else if (element == EL_DRAGON)
5480 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5481 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5482 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5484 int rnd = RND(rnd_value);
5486 if (can_move_on && rnd > rnd_value / 8)
5487 MovDir[x][y] = old_move_dir;
5488 else if (can_turn_left && can_turn_right)
5489 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5490 else if (can_turn_left && rnd > rnd_value / 8)
5491 MovDir[x][y] = left_dir;
5492 else if (can_turn_right && rnd > rnd_value / 8)
5493 MovDir[x][y] = right_dir;
5495 MovDir[x][y] = back_dir;
5497 xx = x + move_xy[MovDir[x][y]].dx;
5498 yy = y + move_xy[MovDir[x][y]].dy;
5500 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5501 MovDir[x][y] = old_move_dir;
5505 else if (element == EL_MOLE)
5507 boolean can_move_on =
5508 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5509 IS_AMOEBOID(Feld[move_x][move_y]) ||
5510 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5513 boolean can_turn_left =
5514 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5515 IS_AMOEBOID(Feld[left_x][left_y])));
5517 boolean can_turn_right =
5518 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5519 IS_AMOEBOID(Feld[right_x][right_y])));
5521 if (can_turn_left && can_turn_right)
5522 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5523 else if (can_turn_left)
5524 MovDir[x][y] = left_dir;
5526 MovDir[x][y] = right_dir;
5529 if (MovDir[x][y] != old_move_dir)
5532 else if (element == EL_BALLOON)
5534 MovDir[x][y] = game.wind_direction;
5537 else if (element == EL_SPRING)
5539 #if USE_NEW_SPRING_BUMPER
5540 if (MovDir[x][y] & MV_HORIZONTAL)
5542 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5543 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5545 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5546 ResetGfxAnimation(move_x, move_y);
5547 DrawLevelField(move_x, move_y);
5549 MovDir[x][y] = back_dir;
5551 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5552 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5553 MovDir[x][y] = MV_NONE;
5556 if (MovDir[x][y] & MV_HORIZONTAL &&
5557 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5558 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5559 MovDir[x][y] = MV_NONE;
5564 else if (element == EL_ROBOT ||
5565 element == EL_SATELLITE ||
5566 element == EL_PENGUIN ||
5567 element == EL_EMC_ANDROID)
5569 int attr_x = -1, attr_y = -1;
5580 for (i = 0; i < MAX_PLAYERS; i++)
5582 struct PlayerInfo *player = &stored_player[i];
5583 int jx = player->jx, jy = player->jy;
5585 if (!player->active)
5589 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5597 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5598 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5599 game.engine_version < VERSION_IDENT(3,1,0,0)))
5605 if (element == EL_PENGUIN)
5608 static int xy[4][2] =
5616 for (i = 0; i < NUM_DIRECTIONS; i++)
5618 int ex = x + xy[i][0];
5619 int ey = y + xy[i][1];
5621 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5622 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5623 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5624 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5633 MovDir[x][y] = MV_NONE;
5635 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5636 else if (attr_x > x)
5637 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5639 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5640 else if (attr_y > y)
5641 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5643 if (element == EL_ROBOT)
5647 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5648 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5649 Moving2Blocked(x, y, &newx, &newy);
5651 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5652 MovDelay[x][y] = 8 + 8 * !RND(3);
5654 MovDelay[x][y] = 16;
5656 else if (element == EL_PENGUIN)
5662 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5664 boolean first_horiz = RND(2);
5665 int new_move_dir = MovDir[x][y];
5668 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5669 Moving2Blocked(x, y, &newx, &newy);
5671 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5675 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5676 Moving2Blocked(x, y, &newx, &newy);
5678 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5681 MovDir[x][y] = old_move_dir;
5685 else if (element == EL_SATELLITE)
5691 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5693 boolean first_horiz = RND(2);
5694 int new_move_dir = MovDir[x][y];
5697 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5698 Moving2Blocked(x, y, &newx, &newy);
5700 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5704 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5705 Moving2Blocked(x, y, &newx, &newy);
5707 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5710 MovDir[x][y] = old_move_dir;
5714 else if (element == EL_EMC_ANDROID)
5716 static int check_pos[16] =
5718 -1, /* 0 => (invalid) */
5719 7, /* 1 => MV_LEFT */
5720 3, /* 2 => MV_RIGHT */
5721 -1, /* 3 => (invalid) */
5723 0, /* 5 => MV_LEFT | MV_UP */
5724 2, /* 6 => MV_RIGHT | MV_UP */
5725 -1, /* 7 => (invalid) */
5726 5, /* 8 => MV_DOWN */
5727 6, /* 9 => MV_LEFT | MV_DOWN */
5728 4, /* 10 => MV_RIGHT | MV_DOWN */
5729 -1, /* 11 => (invalid) */
5730 -1, /* 12 => (invalid) */
5731 -1, /* 13 => (invalid) */
5732 -1, /* 14 => (invalid) */
5733 -1, /* 15 => (invalid) */
5741 { -1, -1, MV_LEFT | MV_UP },
5743 { +1, -1, MV_RIGHT | MV_UP },
5744 { +1, 0, MV_RIGHT },
5745 { +1, +1, MV_RIGHT | MV_DOWN },
5747 { -1, +1, MV_LEFT | MV_DOWN },
5750 int start_pos, check_order;
5751 boolean can_clone = FALSE;
5754 /* check if there is any free field around current position */
5755 for (i = 0; i < 8; i++)
5757 int newx = x + check_xy[i].dx;
5758 int newy = y + check_xy[i].dy;
5760 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5768 if (can_clone) /* randomly find an element to clone */
5772 start_pos = check_pos[RND(8)];
5773 check_order = (RND(2) ? -1 : +1);
5775 for (i = 0; i < 8; i++)
5777 int pos_raw = start_pos + i * check_order;
5778 int pos = (pos_raw + 8) % 8;
5779 int newx = x + check_xy[pos].dx;
5780 int newy = y + check_xy[pos].dy;
5782 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5784 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5785 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5787 Store[x][y] = Feld[newx][newy];
5796 if (can_clone) /* randomly find a direction to move */
5800 start_pos = check_pos[RND(8)];
5801 check_order = (RND(2) ? -1 : +1);
5803 for (i = 0; i < 8; i++)
5805 int pos_raw = start_pos + i * check_order;
5806 int pos = (pos_raw + 8) % 8;
5807 int newx = x + check_xy[pos].dx;
5808 int newy = y + check_xy[pos].dy;
5809 int new_move_dir = check_xy[pos].dir;
5811 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5813 MovDir[x][y] = new_move_dir;
5814 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5823 if (can_clone) /* cloning and moving successful */
5826 /* cannot clone -- try to move towards player */
5828 start_pos = check_pos[MovDir[x][y] & 0x0f];
5829 check_order = (RND(2) ? -1 : +1);
5831 for (i = 0; i < 3; i++)
5833 /* first check start_pos, then previous/next or (next/previous) pos */
5834 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5835 int pos = (pos_raw + 8) % 8;
5836 int newx = x + check_xy[pos].dx;
5837 int newy = y + check_xy[pos].dy;
5838 int new_move_dir = check_xy[pos].dir;
5840 if (IS_PLAYER(newx, newy))
5843 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5845 MovDir[x][y] = new_move_dir;
5846 MovDelay[x][y] = level.android_move_time * 8 + 1;
5853 else if (move_pattern == MV_TURNING_LEFT ||
5854 move_pattern == MV_TURNING_RIGHT ||
5855 move_pattern == MV_TURNING_LEFT_RIGHT ||
5856 move_pattern == MV_TURNING_RIGHT_LEFT ||
5857 move_pattern == MV_TURNING_RANDOM ||
5858 move_pattern == MV_ALL_DIRECTIONS)
5860 boolean can_turn_left =
5861 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5862 boolean can_turn_right =
5863 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5865 if (element_info[element].move_stepsize == 0) /* "not moving" */
5868 if (move_pattern == MV_TURNING_LEFT)
5869 MovDir[x][y] = left_dir;
5870 else if (move_pattern == MV_TURNING_RIGHT)
5871 MovDir[x][y] = right_dir;
5872 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5873 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5874 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5875 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5876 else if (move_pattern == MV_TURNING_RANDOM)
5877 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5878 can_turn_right && !can_turn_left ? right_dir :
5879 RND(2) ? left_dir : right_dir);
5880 else if (can_turn_left && can_turn_right)
5881 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5882 else if (can_turn_left)
5883 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5884 else if (can_turn_right)
5885 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5887 MovDir[x][y] = back_dir;
5889 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5891 else if (move_pattern == MV_HORIZONTAL ||
5892 move_pattern == MV_VERTICAL)
5894 if (move_pattern & old_move_dir)
5895 MovDir[x][y] = back_dir;
5896 else if (move_pattern == MV_HORIZONTAL)
5897 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5898 else if (move_pattern == MV_VERTICAL)
5899 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5901 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5903 else if (move_pattern & MV_ANY_DIRECTION)
5905 MovDir[x][y] = move_pattern;
5906 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5908 else if (move_pattern & MV_WIND_DIRECTION)
5910 MovDir[x][y] = game.wind_direction;
5911 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5913 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5915 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5916 MovDir[x][y] = left_dir;
5917 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5918 MovDir[x][y] = right_dir;
5920 if (MovDir[x][y] != old_move_dir)
5921 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5923 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5925 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5926 MovDir[x][y] = right_dir;
5927 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5928 MovDir[x][y] = left_dir;
5930 if (MovDir[x][y] != old_move_dir)
5931 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5933 else if (move_pattern == MV_TOWARDS_PLAYER ||
5934 move_pattern == MV_AWAY_FROM_PLAYER)
5936 int attr_x = -1, attr_y = -1;
5938 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5949 for (i = 0; i < MAX_PLAYERS; i++)
5951 struct PlayerInfo *player = &stored_player[i];
5952 int jx = player->jx, jy = player->jy;
5954 if (!player->active)
5958 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5966 MovDir[x][y] = MV_NONE;
5968 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5969 else if (attr_x > x)
5970 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5972 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5973 else if (attr_y > y)
5974 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5976 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5978 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5980 boolean first_horiz = RND(2);
5981 int new_move_dir = MovDir[x][y];
5983 if (element_info[element].move_stepsize == 0) /* "not moving" */
5985 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5986 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5992 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5993 Moving2Blocked(x, y, &newx, &newy);
5995 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5999 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6000 Moving2Blocked(x, y, &newx, &newy);
6002 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6005 MovDir[x][y] = old_move_dir;
6008 else if (move_pattern == MV_WHEN_PUSHED ||
6009 move_pattern == MV_WHEN_DROPPED)
6011 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6012 MovDir[x][y] = MV_NONE;
6016 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6018 static int test_xy[7][2] =
6028 static int test_dir[7] =
6038 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6039 int move_preference = -1000000; /* start with very low preference */
6040 int new_move_dir = MV_NONE;
6041 int start_test = RND(4);
6044 for (i = 0; i < NUM_DIRECTIONS; i++)
6046 int move_dir = test_dir[start_test + i];
6047 int move_dir_preference;
6049 xx = x + test_xy[start_test + i][0];
6050 yy = y + test_xy[start_test + i][1];
6052 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6053 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6055 new_move_dir = move_dir;
6060 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6063 move_dir_preference = -1 * RunnerVisit[xx][yy];
6064 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6065 move_dir_preference = PlayerVisit[xx][yy];
6067 if (move_dir_preference > move_preference)
6069 /* prefer field that has not been visited for the longest time */
6070 move_preference = move_dir_preference;
6071 new_move_dir = move_dir;
6073 else if (move_dir_preference == move_preference &&
6074 move_dir == old_move_dir)
6076 /* prefer last direction when all directions are preferred equally */
6077 move_preference = move_dir_preference;
6078 new_move_dir = move_dir;
6082 MovDir[x][y] = new_move_dir;
6083 if (old_move_dir != new_move_dir)
6084 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6088 static void TurnRound(int x, int y)
6090 int direction = MovDir[x][y];
6094 GfxDir[x][y] = MovDir[x][y];
6096 if (direction != MovDir[x][y])
6100 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6102 ResetGfxFrame(x, y, FALSE);
6105 static boolean JustBeingPushed(int x, int y)
6109 for (i = 0; i < MAX_PLAYERS; i++)
6111 struct PlayerInfo *player = &stored_player[i];
6113 if (player->active && player->is_pushing && player->MovPos)
6115 int next_jx = player->jx + (player->jx - player->last_jx);
6116 int next_jy = player->jy + (player->jy - player->last_jy);
6118 if (x == next_jx && y == next_jy)
6126 void StartMoving(int x, int y)
6128 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6129 int element = Feld[x][y];
6134 if (MovDelay[x][y] == 0)
6135 GfxAction[x][y] = ACTION_DEFAULT;
6137 if (CAN_FALL(element) && y < lev_fieldy - 1)
6139 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6140 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6141 if (JustBeingPushed(x, y))
6144 if (element == EL_QUICKSAND_FULL)
6146 if (IS_FREE(x, y + 1))
6148 InitMovingField(x, y, MV_DOWN);
6149 started_moving = TRUE;
6151 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6152 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6153 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6154 Store[x][y] = EL_ROCK;
6156 Store[x][y] = EL_ROCK;
6159 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6161 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6163 if (!MovDelay[x][y])
6164 MovDelay[x][y] = TILEY + 1;
6173 Feld[x][y] = EL_QUICKSAND_EMPTY;
6174 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6175 Store[x][y + 1] = Store[x][y];
6178 PlayLevelSoundAction(x, y, ACTION_FILLING);
6181 else if (element == EL_QUICKSAND_FAST_FULL)
6183 if (IS_FREE(x, y + 1))
6185 InitMovingField(x, y, MV_DOWN);
6186 started_moving = TRUE;
6188 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6189 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6190 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6191 Store[x][y] = EL_ROCK;
6193 Store[x][y] = EL_ROCK;
6196 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6198 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6200 if (!MovDelay[x][y])
6201 MovDelay[x][y] = TILEY + 1;
6210 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6211 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6212 Store[x][y + 1] = Store[x][y];
6215 PlayLevelSoundAction(x, y, ACTION_FILLING);
6218 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6219 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6221 InitMovingField(x, y, MV_DOWN);
6222 started_moving = TRUE;
6224 Feld[x][y] = EL_QUICKSAND_FILLING;
6225 Store[x][y] = element;
6227 PlayLevelSoundAction(x, y, ACTION_FILLING);
6229 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6230 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6232 InitMovingField(x, y, MV_DOWN);
6233 started_moving = TRUE;
6235 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6236 Store[x][y] = element;
6238 PlayLevelSoundAction(x, y, ACTION_FILLING);
6240 else if (element == EL_MAGIC_WALL_FULL)
6242 if (IS_FREE(x, y + 1))
6244 InitMovingField(x, y, MV_DOWN);
6245 started_moving = TRUE;
6247 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6248 Store[x][y] = EL_CHANGED(Store[x][y]);
6250 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6252 if (!MovDelay[x][y])
6253 MovDelay[x][y] = TILEY/4 + 1;
6262 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6263 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6264 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6268 else if (element == EL_BD_MAGIC_WALL_FULL)
6270 if (IS_FREE(x, y + 1))
6272 InitMovingField(x, y, MV_DOWN);
6273 started_moving = TRUE;
6275 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6276 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6278 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6280 if (!MovDelay[x][y])
6281 MovDelay[x][y] = TILEY/4 + 1;
6290 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6291 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6292 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6296 else if (element == EL_DC_MAGIC_WALL_FULL)
6298 if (IS_FREE(x, y + 1))
6300 InitMovingField(x, y, MV_DOWN);
6301 started_moving = TRUE;
6303 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6304 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6306 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6308 if (!MovDelay[x][y])
6309 MovDelay[x][y] = TILEY/4 + 1;
6318 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6319 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6320 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6324 else if ((CAN_PASS_MAGIC_WALL(element) &&
6325 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6326 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6327 (CAN_PASS_DC_MAGIC_WALL(element) &&
6328 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6331 InitMovingField(x, y, MV_DOWN);
6332 started_moving = TRUE;
6335 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6336 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6337 EL_DC_MAGIC_WALL_FILLING);
6338 Store[x][y] = element;
6340 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6342 SplashAcid(x, y + 1);
6344 InitMovingField(x, y, MV_DOWN);
6345 started_moving = TRUE;
6347 Store[x][y] = EL_ACID;
6350 #if USE_FIX_IMPACT_COLLISION
6351 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6352 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6354 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6355 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6357 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6358 CAN_FALL(element) && WasJustFalling[x][y] &&
6359 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6361 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6362 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6363 (Feld[x][y + 1] == EL_BLOCKED)))
6365 /* this is needed for a special case not covered by calling "Impact()"
6366 from "ContinueMoving()": if an element moves to a tile directly below
6367 another element which was just falling on that tile (which was empty
6368 in the previous frame), the falling element above would just stop
6369 instead of smashing the element below (in previous version, the above
6370 element was just checked for "moving" instead of "falling", resulting
6371 in incorrect smashes caused by horizontal movement of the above
6372 element; also, the case of the player being the element to smash was
6373 simply not covered here... :-/ ) */
6375 CheckCollision[x][y] = 0;
6376 CheckImpact[x][y] = 0;
6380 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6382 if (MovDir[x][y] == MV_NONE)
6384 InitMovingField(x, y, MV_DOWN);
6385 started_moving = TRUE;
6388 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6390 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6391 MovDir[x][y] = MV_DOWN;
6393 InitMovingField(x, y, MV_DOWN);
6394 started_moving = TRUE;
6396 else if (element == EL_AMOEBA_DROP)
6398 Feld[x][y] = EL_AMOEBA_GROWING;
6399 Store[x][y] = EL_AMOEBA_WET;
6401 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6402 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6403 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6404 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6406 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6407 (IS_FREE(x - 1, y + 1) ||
6408 Feld[x - 1][y + 1] == EL_ACID));
6409 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6410 (IS_FREE(x + 1, y + 1) ||
6411 Feld[x + 1][y + 1] == EL_ACID));
6412 boolean can_fall_any = (can_fall_left || can_fall_right);
6413 boolean can_fall_both = (can_fall_left && can_fall_right);
6414 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6416 #if USE_NEW_ALL_SLIPPERY
6417 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6419 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6420 can_fall_right = FALSE;
6421 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6422 can_fall_left = FALSE;
6423 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6424 can_fall_right = FALSE;
6425 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6426 can_fall_left = FALSE;
6428 can_fall_any = (can_fall_left || can_fall_right);
6429 can_fall_both = FALSE;
6432 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6434 if (slippery_type == SLIPPERY_ONLY_LEFT)
6435 can_fall_right = FALSE;
6436 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6437 can_fall_left = FALSE;
6438 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6439 can_fall_right = FALSE;
6440 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6441 can_fall_left = FALSE;
6443 can_fall_any = (can_fall_left || can_fall_right);
6444 can_fall_both = (can_fall_left && can_fall_right);
6448 #if USE_NEW_ALL_SLIPPERY
6450 #if USE_NEW_SP_SLIPPERY
6451 /* !!! better use the same properties as for custom elements here !!! */
6452 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6453 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6455 can_fall_right = FALSE; /* slip down on left side */
6456 can_fall_both = FALSE;
6461 #if USE_NEW_ALL_SLIPPERY
6464 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6465 can_fall_right = FALSE; /* slip down on left side */
6467 can_fall_left = !(can_fall_right = RND(2));
6469 can_fall_both = FALSE;
6474 if (game.emulation == EMU_BOULDERDASH ||
6475 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6476 can_fall_right = FALSE; /* slip down on left side */
6478 can_fall_left = !(can_fall_right = RND(2));
6480 can_fall_both = FALSE;
6486 /* if not determined otherwise, prefer left side for slipping down */
6487 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6488 started_moving = TRUE;
6492 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6494 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6497 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6498 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6499 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6500 int belt_dir = game.belt_dir[belt_nr];
6502 if ((belt_dir == MV_LEFT && left_is_free) ||
6503 (belt_dir == MV_RIGHT && right_is_free))
6505 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6507 InitMovingField(x, y, belt_dir);
6508 started_moving = TRUE;
6510 Pushed[x][y] = TRUE;
6511 Pushed[nextx][y] = TRUE;
6513 GfxAction[x][y] = ACTION_DEFAULT;
6517 MovDir[x][y] = 0; /* if element was moving, stop it */
6522 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6524 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6526 if (CAN_MOVE(element) && !started_moving)
6529 int move_pattern = element_info[element].move_pattern;
6534 if (MovDir[x][y] == MV_NONE)
6536 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6537 x, y, element, element_info[element].token_name);
6538 printf("StartMoving(): This should never happen!\n");
6543 Moving2Blocked(x, y, &newx, &newy);
6545 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6548 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6549 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6551 WasJustMoving[x][y] = 0;
6552 CheckCollision[x][y] = 0;
6554 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6556 if (Feld[x][y] != element) /* element has changed */
6560 if (!MovDelay[x][y]) /* start new movement phase */
6562 /* all objects that can change their move direction after each step
6563 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6565 if (element != EL_YAMYAM &&
6566 element != EL_DARK_YAMYAM &&
6567 element != EL_PACMAN &&
6568 !(move_pattern & MV_ANY_DIRECTION) &&
6569 move_pattern != MV_TURNING_LEFT &&
6570 move_pattern != MV_TURNING_RIGHT &&
6571 move_pattern != MV_TURNING_LEFT_RIGHT &&
6572 move_pattern != MV_TURNING_RIGHT_LEFT &&
6573 move_pattern != MV_TURNING_RANDOM)
6577 if (MovDelay[x][y] && (element == EL_BUG ||
6578 element == EL_SPACESHIP ||
6579 element == EL_SP_SNIKSNAK ||
6580 element == EL_SP_ELECTRON ||
6581 element == EL_MOLE))
6582 DrawLevelField(x, y);
6586 if (MovDelay[x][y]) /* wait some time before next movement */
6590 if (element == EL_ROBOT ||
6591 element == EL_YAMYAM ||
6592 element == EL_DARK_YAMYAM)
6594 DrawLevelElementAnimationIfNeeded(x, y, element);
6595 PlayLevelSoundAction(x, y, ACTION_WAITING);
6597 else if (element == EL_SP_ELECTRON)
6598 DrawLevelElementAnimationIfNeeded(x, y, element);
6599 else if (element == EL_DRAGON)
6602 int dir = MovDir[x][y];
6603 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6604 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6605 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6606 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6607 dir == MV_UP ? IMG_FLAMES_1_UP :
6608 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6609 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6611 GfxAction[x][y] = ACTION_ATTACKING;
6613 if (IS_PLAYER(x, y))
6614 DrawPlayerField(x, y);
6616 DrawLevelField(x, y);
6618 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6620 for (i = 1; i <= 3; i++)
6622 int xx = x + i * dx;
6623 int yy = y + i * dy;
6624 int sx = SCREENX(xx);
6625 int sy = SCREENY(yy);
6626 int flame_graphic = graphic + (i - 1);
6628 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6633 int flamed = MovingOrBlocked2Element(xx, yy);
6637 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6639 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6640 RemoveMovingField(xx, yy);
6642 RemoveField(xx, yy);
6644 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6647 RemoveMovingField(xx, yy);
6650 ChangeDelay[xx][yy] = 0;
6652 Feld[xx][yy] = EL_FLAMES;
6654 if (IN_SCR_FIELD(sx, sy))
6656 DrawLevelFieldCrumbledSand(xx, yy);
6657 DrawGraphic(sx, sy, flame_graphic, frame);
6662 if (Feld[xx][yy] == EL_FLAMES)
6663 Feld[xx][yy] = EL_EMPTY;
6664 DrawLevelField(xx, yy);
6669 if (MovDelay[x][y]) /* element still has to wait some time */
6671 PlayLevelSoundAction(x, y, ACTION_WAITING);
6677 /* now make next step */
6679 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6681 if (DONT_COLLIDE_WITH(element) &&
6682 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6683 !PLAYER_ENEMY_PROTECTED(newx, newy))
6685 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6690 else if (CAN_MOVE_INTO_ACID(element) &&
6691 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6692 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6693 (MovDir[x][y] == MV_DOWN ||
6694 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6696 SplashAcid(newx, newy);
6697 Store[x][y] = EL_ACID;
6699 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6701 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6702 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6703 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6704 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6707 DrawLevelField(x, y);
6709 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6710 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6711 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6713 local_player->friends_still_needed--;
6714 if (!local_player->friends_still_needed &&
6715 !local_player->GameOver && AllPlayersGone)
6716 PlayerWins(local_player);
6720 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6722 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6723 DrawLevelField(newx, newy);
6725 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6727 else if (!IS_FREE(newx, newy))
6729 GfxAction[x][y] = ACTION_WAITING;
6731 if (IS_PLAYER(x, y))
6732 DrawPlayerField(x, y);
6734 DrawLevelField(x, y);
6739 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6741 if (IS_FOOD_PIG(Feld[newx][newy]))
6743 if (IS_MOVING(newx, newy))
6744 RemoveMovingField(newx, newy);
6747 Feld[newx][newy] = EL_EMPTY;
6748 DrawLevelField(newx, newy);
6751 PlayLevelSound(x, y, SND_PIG_DIGGING);
6753 else if (!IS_FREE(newx, newy))
6755 if (IS_PLAYER(x, y))
6756 DrawPlayerField(x, y);
6758 DrawLevelField(x, y);
6763 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6765 if (Store[x][y] != EL_EMPTY)
6767 boolean can_clone = FALSE;
6770 /* check if element to clone is still there */
6771 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6773 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6781 /* cannot clone or target field not free anymore -- do not clone */
6782 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6783 Store[x][y] = EL_EMPTY;
6786 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6788 if (IS_MV_DIAGONAL(MovDir[x][y]))
6790 int diagonal_move_dir = MovDir[x][y];
6791 int stored = Store[x][y];
6792 int change_delay = 8;
6795 /* android is moving diagonally */
6797 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6799 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6800 GfxElement[x][y] = EL_EMC_ANDROID;
6801 GfxAction[x][y] = ACTION_SHRINKING;
6802 GfxDir[x][y] = diagonal_move_dir;
6803 ChangeDelay[x][y] = change_delay;
6805 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6808 DrawLevelGraphicAnimation(x, y, graphic);
6809 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6811 if (Feld[newx][newy] == EL_ACID)
6813 SplashAcid(newx, newy);
6818 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6820 Store[newx][newy] = EL_EMC_ANDROID;
6821 GfxElement[newx][newy] = EL_EMC_ANDROID;
6822 GfxAction[newx][newy] = ACTION_GROWING;
6823 GfxDir[newx][newy] = diagonal_move_dir;
6824 ChangeDelay[newx][newy] = change_delay;
6826 graphic = el_act_dir2img(GfxElement[newx][newy],
6827 GfxAction[newx][newy], GfxDir[newx][newy]);
6829 DrawLevelGraphicAnimation(newx, newy, graphic);
6830 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6836 Feld[newx][newy] = EL_EMPTY;
6837 DrawLevelField(newx, newy);
6839 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6842 else if (!IS_FREE(newx, newy))
6845 if (IS_PLAYER(x, y))
6846 DrawPlayerField(x, y);
6848 DrawLevelField(x, y);
6854 else if (IS_CUSTOM_ELEMENT(element) &&
6855 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6857 int new_element = Feld[newx][newy];
6859 if (!IS_FREE(newx, newy))
6861 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6862 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6865 /* no element can dig solid indestructible elements */
6866 if (IS_INDESTRUCTIBLE(new_element) &&
6867 !IS_DIGGABLE(new_element) &&
6868 !IS_COLLECTIBLE(new_element))
6871 if (AmoebaNr[newx][newy] &&
6872 (new_element == EL_AMOEBA_FULL ||
6873 new_element == EL_BD_AMOEBA ||
6874 new_element == EL_AMOEBA_GROWING))
6876 AmoebaCnt[AmoebaNr[newx][newy]]--;
6877 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6880 if (IS_MOVING(newx, newy))
6881 RemoveMovingField(newx, newy);
6884 RemoveField(newx, newy);
6885 DrawLevelField(newx, newy);
6888 /* if digged element was about to explode, prevent the explosion */
6889 ExplodeField[newx][newy] = EX_TYPE_NONE;
6891 PlayLevelSoundAction(x, y, action);
6894 Store[newx][newy] = EL_EMPTY;
6896 /* this makes it possible to leave the removed element again */
6897 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6898 Store[newx][newy] = new_element;
6900 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6902 int move_leave_element = element_info[element].move_leave_element;
6904 /* this makes it possible to leave the removed element again */
6905 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6906 new_element : move_leave_element);
6910 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6912 RunnerVisit[x][y] = FrameCounter;
6913 PlayerVisit[x][y] /= 8; /* expire player visit path */
6916 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6918 if (!IS_FREE(newx, newy))
6920 if (IS_PLAYER(x, y))
6921 DrawPlayerField(x, y);
6923 DrawLevelField(x, y);
6929 boolean wanna_flame = !RND(10);
6930 int dx = newx - x, dy = newy - y;
6931 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6932 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6933 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6934 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6935 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6936 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6939 IS_CLASSIC_ENEMY(element1) ||
6940 IS_CLASSIC_ENEMY(element2)) &&
6941 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6942 element1 != EL_FLAMES && element2 != EL_FLAMES)
6944 ResetGfxAnimation(x, y);
6945 GfxAction[x][y] = ACTION_ATTACKING;
6947 if (IS_PLAYER(x, y))
6948 DrawPlayerField(x, y);
6950 DrawLevelField(x, y);
6952 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6954 MovDelay[x][y] = 50;
6958 RemoveField(newx, newy);
6960 Feld[newx][newy] = EL_FLAMES;
6961 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6964 RemoveField(newx1, newy1);
6966 Feld[newx1][newy1] = EL_FLAMES;
6968 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6971 RemoveField(newx2, newy2);
6973 Feld[newx2][newy2] = EL_FLAMES;
6980 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6981 Feld[newx][newy] == EL_DIAMOND)
6983 if (IS_MOVING(newx, newy))
6984 RemoveMovingField(newx, newy);
6987 Feld[newx][newy] = EL_EMPTY;
6988 DrawLevelField(newx, newy);
6991 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6993 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6994 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6996 if (AmoebaNr[newx][newy])
6998 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6999 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7000 Feld[newx][newy] == EL_BD_AMOEBA)
7001 AmoebaCnt[AmoebaNr[newx][newy]]--;
7006 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7008 RemoveMovingField(newx, newy);
7011 if (IS_MOVING(newx, newy))
7013 RemoveMovingField(newx, newy);
7018 Feld[newx][newy] = EL_EMPTY;
7019 DrawLevelField(newx, newy);
7022 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7024 else if ((element == EL_PACMAN || element == EL_MOLE)
7025 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7027 if (AmoebaNr[newx][newy])
7029 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7030 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7031 Feld[newx][newy] == EL_BD_AMOEBA)
7032 AmoebaCnt[AmoebaNr[newx][newy]]--;
7035 if (element == EL_MOLE)
7037 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7038 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7040 ResetGfxAnimation(x, y);
7041 GfxAction[x][y] = ACTION_DIGGING;
7042 DrawLevelField(x, y);
7044 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7046 return; /* wait for shrinking amoeba */
7048 else /* element == EL_PACMAN */
7050 Feld[newx][newy] = EL_EMPTY;
7051 DrawLevelField(newx, newy);
7052 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7055 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7056 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7057 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7059 /* wait for shrinking amoeba to completely disappear */
7062 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7064 /* object was running against a wall */
7069 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7070 if (move_pattern & MV_ANY_DIRECTION &&
7071 move_pattern == MovDir[x][y])
7073 int blocking_element =
7074 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7076 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7079 element = Feld[x][y]; /* element might have changed */
7083 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7084 DrawLevelElementAnimation(x, y, element);
7086 if (DONT_TOUCH(element))
7087 TestIfBadThingTouchesPlayer(x, y);
7092 InitMovingField(x, y, MovDir[x][y]);
7094 PlayLevelSoundAction(x, y, ACTION_MOVING);
7098 ContinueMoving(x, y);
7101 void ContinueMoving(int x, int y)
7103 int element = Feld[x][y];
7104 struct ElementInfo *ei = &element_info[element];
7105 int direction = MovDir[x][y];
7106 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7107 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7108 int newx = x + dx, newy = y + dy;
7109 int stored = Store[x][y];
7110 int stored_new = Store[newx][newy];
7111 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7112 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7113 boolean last_line = (newy == lev_fieldy - 1);
7115 MovPos[x][y] += getElementMoveStepsize(x, y);
7117 if (pushed_by_player) /* special case: moving object pushed by player */
7118 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7120 if (ABS(MovPos[x][y]) < TILEX)
7123 int ee = Feld[x][y];
7124 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7125 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7127 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7128 x, y, ABS(MovPos[x][y]),
7130 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7133 DrawLevelField(x, y);
7135 return; /* element is still moving */
7138 /* element reached destination field */
7140 Feld[x][y] = EL_EMPTY;
7141 Feld[newx][newy] = element;
7142 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7144 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7146 element = Feld[newx][newy] = EL_ACID;
7148 else if (element == EL_MOLE)
7150 Feld[x][y] = EL_SAND;
7152 DrawLevelFieldCrumbledSandNeighbours(x, y);
7154 else if (element == EL_QUICKSAND_FILLING)
7156 element = Feld[newx][newy] = get_next_element(element);
7157 Store[newx][newy] = Store[x][y];
7159 else if (element == EL_QUICKSAND_EMPTYING)
7161 Feld[x][y] = get_next_element(element);
7162 element = Feld[newx][newy] = Store[x][y];
7164 else if (element == EL_QUICKSAND_FAST_FILLING)
7166 element = Feld[newx][newy] = get_next_element(element);
7167 Store[newx][newy] = Store[x][y];
7169 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7171 Feld[x][y] = get_next_element(element);
7172 element = Feld[newx][newy] = Store[x][y];
7174 else if (element == EL_MAGIC_WALL_FILLING)
7176 element = Feld[newx][newy] = get_next_element(element);
7177 if (!game.magic_wall_active)
7178 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7179 Store[newx][newy] = Store[x][y];
7181 else if (element == EL_MAGIC_WALL_EMPTYING)
7183 Feld[x][y] = get_next_element(element);
7184 if (!game.magic_wall_active)
7185 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7186 element = Feld[newx][newy] = Store[x][y];
7188 #if USE_NEW_CUSTOM_VALUE
7189 InitField(newx, newy, FALSE);
7192 else if (element == EL_BD_MAGIC_WALL_FILLING)
7194 element = Feld[newx][newy] = get_next_element(element);
7195 if (!game.magic_wall_active)
7196 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7197 Store[newx][newy] = Store[x][y];
7199 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7201 Feld[x][y] = get_next_element(element);
7202 if (!game.magic_wall_active)
7203 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7204 element = Feld[newx][newy] = Store[x][y];
7206 #if USE_NEW_CUSTOM_VALUE
7207 InitField(newx, newy, FALSE);
7210 else if (element == EL_DC_MAGIC_WALL_FILLING)
7212 element = Feld[newx][newy] = get_next_element(element);
7213 if (!game.magic_wall_active)
7214 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7215 Store[newx][newy] = Store[x][y];
7217 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7219 Feld[x][y] = get_next_element(element);
7220 if (!game.magic_wall_active)
7221 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7222 element = Feld[newx][newy] = Store[x][y];
7224 #if USE_NEW_CUSTOM_VALUE
7225 InitField(newx, newy, FALSE);
7228 else if (element == EL_AMOEBA_DROPPING)
7230 Feld[x][y] = get_next_element(element);
7231 element = Feld[newx][newy] = Store[x][y];
7233 else if (element == EL_SOKOBAN_OBJECT)
7236 Feld[x][y] = Back[x][y];
7238 if (Back[newx][newy])
7239 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7241 Back[x][y] = Back[newx][newy] = 0;
7244 Store[x][y] = EL_EMPTY;
7249 MovDelay[newx][newy] = 0;
7251 if (CAN_CHANGE_OR_HAS_ACTION(element))
7253 /* copy element change control values to new field */
7254 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7255 ChangePage[newx][newy] = ChangePage[x][y];
7256 ChangeCount[newx][newy] = ChangeCount[x][y];
7257 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7260 #if USE_NEW_CUSTOM_VALUE
7261 CustomValue[newx][newy] = CustomValue[x][y];
7264 ChangeDelay[x][y] = 0;
7265 ChangePage[x][y] = -1;
7266 ChangeCount[x][y] = 0;
7267 ChangeEvent[x][y] = -1;
7269 #if USE_NEW_CUSTOM_VALUE
7270 CustomValue[x][y] = 0;
7273 /* copy animation control values to new field */
7274 GfxFrame[newx][newy] = GfxFrame[x][y];
7275 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7276 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7277 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7279 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7281 /* some elements can leave other elements behind after moving */
7283 if (ei->move_leave_element != EL_EMPTY &&
7284 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7285 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7287 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7288 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7289 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7292 int move_leave_element = ei->move_leave_element;
7296 /* this makes it possible to leave the removed element again */
7297 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7298 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7300 /* this makes it possible to leave the removed element again */
7301 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7302 move_leave_element = stored;
7305 /* this makes it possible to leave the removed element again */
7306 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7307 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7308 move_leave_element = stored;
7311 Feld[x][y] = move_leave_element;
7313 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7314 MovDir[x][y] = direction;
7316 InitField(x, y, FALSE);
7318 if (GFX_CRUMBLED(Feld[x][y]))
7319 DrawLevelFieldCrumbledSandNeighbours(x, y);
7321 if (ELEM_IS_PLAYER(move_leave_element))
7322 RelocatePlayer(x, y, move_leave_element);
7325 /* do this after checking for left-behind element */
7326 ResetGfxAnimation(x, y); /* reset animation values for old field */
7328 if (!CAN_MOVE(element) ||
7329 (CAN_FALL(element) && direction == MV_DOWN &&
7330 (element == EL_SPRING ||
7331 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7332 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7333 GfxDir[x][y] = MovDir[newx][newy] = 0;
7335 DrawLevelField(x, y);
7336 DrawLevelField(newx, newy);
7338 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7340 /* prevent pushed element from moving on in pushed direction */
7341 if (pushed_by_player && CAN_MOVE(element) &&
7342 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7343 !(element_info[element].move_pattern & direction))
7344 TurnRound(newx, newy);
7346 /* prevent elements on conveyor belt from moving on in last direction */
7347 if (pushed_by_conveyor && CAN_FALL(element) &&
7348 direction & MV_HORIZONTAL)
7349 MovDir[newx][newy] = 0;
7351 if (!pushed_by_player)
7353 int nextx = newx + dx, nexty = newy + dy;
7354 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7356 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7358 if (CAN_FALL(element) && direction == MV_DOWN)
7359 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7361 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7362 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7364 #if USE_FIX_IMPACT_COLLISION
7365 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7366 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7370 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7372 TestIfBadThingTouchesPlayer(newx, newy);
7373 TestIfBadThingTouchesFriend(newx, newy);
7375 if (!IS_CUSTOM_ELEMENT(element))
7376 TestIfBadThingTouchesOtherBadThing(newx, newy);
7378 else if (element == EL_PENGUIN)
7379 TestIfFriendTouchesBadThing(newx, newy);
7381 /* give the player one last chance (one more frame) to move away */
7382 if (CAN_FALL(element) && direction == MV_DOWN &&
7383 (last_line || (!IS_FREE(x, newy + 1) &&
7384 (!IS_PLAYER(x, newy + 1) ||
7385 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7388 if (pushed_by_player && !game.use_change_when_pushing_bug)
7390 int push_side = MV_DIR_OPPOSITE(direction);
7391 struct PlayerInfo *player = PLAYERINFO(x, y);
7393 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7394 player->index_bit, push_side);
7395 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7396 player->index_bit, push_side);
7399 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7400 MovDelay[newx][newy] = 1;
7402 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7404 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7407 if (ChangePage[newx][newy] != -1) /* delayed change */
7409 int page = ChangePage[newx][newy];
7410 struct ElementChangeInfo *change = &ei->change_page[page];
7412 ChangePage[newx][newy] = -1;
7414 if (change->can_change)
7416 if (ChangeElement(newx, newy, element, page))
7418 if (change->post_change_function)
7419 change->post_change_function(newx, newy);
7423 if (change->has_action)
7424 ExecuteCustomElementAction(newx, newy, element, page);
7428 TestIfElementHitsCustomElement(newx, newy, direction);
7429 TestIfPlayerTouchesCustomElement(newx, newy);
7430 TestIfElementTouchesCustomElement(newx, newy);
7432 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7433 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7434 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7435 MV_DIR_OPPOSITE(direction));
7438 int AmoebeNachbarNr(int ax, int ay)
7441 int element = Feld[ax][ay];
7443 static int xy[4][2] =
7451 for (i = 0; i < NUM_DIRECTIONS; i++)
7453 int x = ax + xy[i][0];
7454 int y = ay + xy[i][1];
7456 if (!IN_LEV_FIELD(x, y))
7459 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7460 group_nr = AmoebaNr[x][y];
7466 void AmoebenVereinigen(int ax, int ay)
7468 int i, x, y, xx, yy;
7469 int new_group_nr = AmoebaNr[ax][ay];
7470 static int xy[4][2] =
7478 if (new_group_nr == 0)
7481 for (i = 0; i < NUM_DIRECTIONS; i++)
7486 if (!IN_LEV_FIELD(x, y))
7489 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7490 Feld[x][y] == EL_BD_AMOEBA ||
7491 Feld[x][y] == EL_AMOEBA_DEAD) &&
7492 AmoebaNr[x][y] != new_group_nr)
7494 int old_group_nr = AmoebaNr[x][y];
7496 if (old_group_nr == 0)
7499 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7500 AmoebaCnt[old_group_nr] = 0;
7501 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7502 AmoebaCnt2[old_group_nr] = 0;
7504 SCAN_PLAYFIELD(xx, yy)
7506 if (AmoebaNr[xx][yy] == old_group_nr)
7507 AmoebaNr[xx][yy] = new_group_nr;
7513 void AmoebeUmwandeln(int ax, int ay)
7517 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7519 int group_nr = AmoebaNr[ax][ay];
7524 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7525 printf("AmoebeUmwandeln(): This should never happen!\n");
7530 SCAN_PLAYFIELD(x, y)
7532 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7535 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7539 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7540 SND_AMOEBA_TURNING_TO_GEM :
7541 SND_AMOEBA_TURNING_TO_ROCK));
7546 static int xy[4][2] =
7554 for (i = 0; i < NUM_DIRECTIONS; i++)
7559 if (!IN_LEV_FIELD(x, y))
7562 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7564 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7565 SND_AMOEBA_TURNING_TO_GEM :
7566 SND_AMOEBA_TURNING_TO_ROCK));
7573 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7576 int group_nr = AmoebaNr[ax][ay];
7577 boolean done = FALSE;
7582 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7583 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7588 SCAN_PLAYFIELD(x, y)
7590 if (AmoebaNr[x][y] == group_nr &&
7591 (Feld[x][y] == EL_AMOEBA_DEAD ||
7592 Feld[x][y] == EL_BD_AMOEBA ||
7593 Feld[x][y] == EL_AMOEBA_GROWING))
7596 Feld[x][y] = new_element;
7597 InitField(x, y, FALSE);
7598 DrawLevelField(x, y);
7604 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7605 SND_BD_AMOEBA_TURNING_TO_ROCK :
7606 SND_BD_AMOEBA_TURNING_TO_GEM));
7609 void AmoebeWaechst(int x, int y)
7611 static unsigned long sound_delay = 0;
7612 static unsigned long sound_delay_value = 0;
7614 if (!MovDelay[x][y]) /* start new growing cycle */
7618 if (DelayReached(&sound_delay, sound_delay_value))
7620 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7621 sound_delay_value = 30;
7625 if (MovDelay[x][y]) /* wait some time before growing bigger */
7628 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7630 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7631 6 - MovDelay[x][y]);
7633 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7636 if (!MovDelay[x][y])
7638 Feld[x][y] = Store[x][y];
7640 DrawLevelField(x, y);
7645 void AmoebaDisappearing(int x, int y)
7647 static unsigned long sound_delay = 0;
7648 static unsigned long sound_delay_value = 0;
7650 if (!MovDelay[x][y]) /* start new shrinking cycle */
7654 if (DelayReached(&sound_delay, sound_delay_value))
7655 sound_delay_value = 30;
7658 if (MovDelay[x][y]) /* wait some time before shrinking */
7661 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7663 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7664 6 - MovDelay[x][y]);
7666 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7669 if (!MovDelay[x][y])
7671 Feld[x][y] = EL_EMPTY;
7672 DrawLevelField(x, y);
7674 /* don't let mole enter this field in this cycle;
7675 (give priority to objects falling to this field from above) */
7681 void AmoebeAbleger(int ax, int ay)
7684 int element = Feld[ax][ay];
7685 int graphic = el2img(element);
7686 int newax = ax, neway = ay;
7687 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7688 static int xy[4][2] =
7696 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7698 Feld[ax][ay] = EL_AMOEBA_DEAD;
7699 DrawLevelField(ax, ay);
7703 if (IS_ANIMATED(graphic))
7704 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7706 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7707 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7709 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7712 if (MovDelay[ax][ay])
7716 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7719 int x = ax + xy[start][0];
7720 int y = ay + xy[start][1];
7722 if (!IN_LEV_FIELD(x, y))
7725 if (IS_FREE(x, y) ||
7726 CAN_GROW_INTO(Feld[x][y]) ||
7727 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7728 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7734 if (newax == ax && neway == ay)
7737 else /* normal or "filled" (BD style) amoeba */
7740 boolean waiting_for_player = FALSE;
7742 for (i = 0; i < NUM_DIRECTIONS; i++)
7744 int j = (start + i) % 4;
7745 int x = ax + xy[j][0];
7746 int y = ay + xy[j][1];
7748 if (!IN_LEV_FIELD(x, y))
7751 if (IS_FREE(x, y) ||
7752 CAN_GROW_INTO(Feld[x][y]) ||
7753 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7754 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7760 else if (IS_PLAYER(x, y))
7761 waiting_for_player = TRUE;
7764 if (newax == ax && neway == ay) /* amoeba cannot grow */
7766 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7768 Feld[ax][ay] = EL_AMOEBA_DEAD;
7769 DrawLevelField(ax, ay);
7770 AmoebaCnt[AmoebaNr[ax][ay]]--;
7772 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7774 if (element == EL_AMOEBA_FULL)
7775 AmoebeUmwandeln(ax, ay);
7776 else if (element == EL_BD_AMOEBA)
7777 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7782 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7784 /* amoeba gets larger by growing in some direction */
7786 int new_group_nr = AmoebaNr[ax][ay];
7789 if (new_group_nr == 0)
7791 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7792 printf("AmoebeAbleger(): This should never happen!\n");
7797 AmoebaNr[newax][neway] = new_group_nr;
7798 AmoebaCnt[new_group_nr]++;
7799 AmoebaCnt2[new_group_nr]++;
7801 /* if amoeba touches other amoeba(s) after growing, unify them */
7802 AmoebenVereinigen(newax, neway);
7804 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7806 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7812 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7813 (neway == lev_fieldy - 1 && newax != ax))
7815 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7816 Store[newax][neway] = element;
7818 else if (neway == ay || element == EL_EMC_DRIPPER)
7820 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7822 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7826 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7827 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7828 Store[ax][ay] = EL_AMOEBA_DROP;
7829 ContinueMoving(ax, ay);
7833 DrawLevelField(newax, neway);
7836 void Life(int ax, int ay)
7840 int element = Feld[ax][ay];
7841 int graphic = el2img(element);
7842 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7844 boolean changed = FALSE;
7846 if (IS_ANIMATED(graphic))
7847 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7852 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7853 MovDelay[ax][ay] = life_time;
7855 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7858 if (MovDelay[ax][ay])
7862 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7864 int xx = ax+x1, yy = ay+y1;
7867 if (!IN_LEV_FIELD(xx, yy))
7870 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7872 int x = xx+x2, y = yy+y2;
7874 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7877 if (((Feld[x][y] == element ||
7878 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7880 (IS_FREE(x, y) && Stop[x][y]))
7884 if (xx == ax && yy == ay) /* field in the middle */
7886 if (nachbarn < life_parameter[0] ||
7887 nachbarn > life_parameter[1])
7889 Feld[xx][yy] = EL_EMPTY;
7891 DrawLevelField(xx, yy);
7892 Stop[xx][yy] = TRUE;
7896 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7897 { /* free border field */
7898 if (nachbarn >= life_parameter[2] &&
7899 nachbarn <= life_parameter[3])
7901 Feld[xx][yy] = element;
7902 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7904 DrawLevelField(xx, yy);
7905 Stop[xx][yy] = TRUE;
7912 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7913 SND_GAME_OF_LIFE_GROWING);
7916 static void InitRobotWheel(int x, int y)
7918 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7921 static void RunRobotWheel(int x, int y)
7923 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7926 static void StopRobotWheel(int x, int y)
7928 if (ZX == x && ZY == y)
7932 static void InitTimegateWheel(int x, int y)
7934 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7937 static void RunTimegateWheel(int x, int y)
7939 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7942 static void InitMagicBallDelay(int x, int y)
7945 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7947 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7951 static void ActivateMagicBall(int bx, int by)
7955 if (level.ball_random)
7957 int pos_border = RND(8); /* select one of the eight border elements */
7958 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7959 int xx = pos_content % 3;
7960 int yy = pos_content / 3;
7965 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7966 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7970 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7972 int xx = x - bx + 1;
7973 int yy = y - by + 1;
7975 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7976 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7980 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7983 void CheckExit(int x, int y)
7985 if (local_player->gems_still_needed > 0 ||
7986 local_player->sokobanfields_still_needed > 0 ||
7987 local_player->lights_still_needed > 0)
7989 int element = Feld[x][y];
7990 int graphic = el2img(element);
7992 if (IS_ANIMATED(graphic))
7993 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7998 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8001 Feld[x][y] = EL_EXIT_OPENING;
8003 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8006 void CheckExitEM(int x, int y)
8008 if (local_player->gems_still_needed > 0 ||
8009 local_player->sokobanfields_still_needed > 0 ||
8010 local_player->lights_still_needed > 0)
8012 int element = Feld[x][y];
8013 int graphic = el2img(element);
8015 if (IS_ANIMATED(graphic))
8016 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8021 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8024 Feld[x][y] = EL_EM_EXIT_OPENING;
8026 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8029 void CheckExitSteel(int x, int y)
8031 if (local_player->gems_still_needed > 0 ||
8032 local_player->sokobanfields_still_needed > 0 ||
8033 local_player->lights_still_needed > 0)
8035 int element = Feld[x][y];
8036 int graphic = el2img(element);
8038 if (IS_ANIMATED(graphic))
8039 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8044 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8047 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8049 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8052 void CheckExitSteelEM(int x, int y)
8054 if (local_player->gems_still_needed > 0 ||
8055 local_player->sokobanfields_still_needed > 0 ||
8056 local_player->lights_still_needed > 0)
8058 int element = Feld[x][y];
8059 int graphic = el2img(element);
8061 if (IS_ANIMATED(graphic))
8062 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8067 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8070 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8072 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8075 void CheckExitSP(int x, int y)
8077 if (local_player->gems_still_needed > 0)
8079 int element = Feld[x][y];
8080 int graphic = el2img(element);
8082 if (IS_ANIMATED(graphic))
8083 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8088 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8091 Feld[x][y] = EL_SP_EXIT_OPENING;
8093 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8096 static void CloseAllOpenTimegates()
8100 SCAN_PLAYFIELD(x, y)
8102 int element = Feld[x][y];
8104 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8106 Feld[x][y] = EL_TIMEGATE_CLOSING;
8108 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8113 void DrawTwinkleOnField(int x, int y)
8115 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8118 if (Feld[x][y] == EL_BD_DIAMOND)
8121 if (MovDelay[x][y] == 0) /* next animation frame */
8122 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8124 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8128 if (setup.direct_draw && MovDelay[x][y])
8129 SetDrawtoField(DRAW_BUFFERED);
8131 DrawLevelElementAnimation(x, y, Feld[x][y]);
8133 if (MovDelay[x][y] != 0)
8135 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8136 10 - MovDelay[x][y]);
8138 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8140 if (setup.direct_draw)
8144 dest_x = FX + SCREENX(x) * TILEX;
8145 dest_y = FY + SCREENY(y) * TILEY;
8147 BlitBitmap(drawto_field, window,
8148 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8149 SetDrawtoField(DRAW_DIRECT);
8155 void MauerWaechst(int x, int y)
8159 if (!MovDelay[x][y]) /* next animation frame */
8160 MovDelay[x][y] = 3 * delay;
8162 if (MovDelay[x][y]) /* wait some time before next frame */
8166 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8168 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8169 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8171 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8174 if (!MovDelay[x][y])
8176 if (MovDir[x][y] == MV_LEFT)
8178 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8179 DrawLevelField(x - 1, y);
8181 else if (MovDir[x][y] == MV_RIGHT)
8183 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8184 DrawLevelField(x + 1, y);
8186 else if (MovDir[x][y] == MV_UP)
8188 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8189 DrawLevelField(x, y - 1);
8193 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8194 DrawLevelField(x, y + 1);
8197 Feld[x][y] = Store[x][y];
8199 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8200 DrawLevelField(x, y);
8205 void MauerAbleger(int ax, int ay)
8207 int element = Feld[ax][ay];
8208 int graphic = el2img(element);
8209 boolean oben_frei = FALSE, unten_frei = FALSE;
8210 boolean links_frei = FALSE, rechts_frei = FALSE;
8211 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8212 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8213 boolean new_wall = FALSE;
8215 if (IS_ANIMATED(graphic))
8216 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8218 if (!MovDelay[ax][ay]) /* start building new wall */
8219 MovDelay[ax][ay] = 6;
8221 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8224 if (MovDelay[ax][ay])
8228 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8230 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8232 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8234 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8237 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8238 element == EL_EXPANDABLE_WALL_ANY)
8242 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8243 Store[ax][ay-1] = element;
8244 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8245 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8246 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8247 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8252 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8253 Store[ax][ay+1] = element;
8254 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8255 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8256 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8257 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8262 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8263 element == EL_EXPANDABLE_WALL_ANY ||
8264 element == EL_EXPANDABLE_WALL ||
8265 element == EL_BD_EXPANDABLE_WALL)
8269 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8270 Store[ax-1][ay] = element;
8271 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8272 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8273 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8274 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8280 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8281 Store[ax+1][ay] = element;
8282 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8283 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8284 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8285 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8290 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8291 DrawLevelField(ax, ay);
8293 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8295 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8296 unten_massiv = TRUE;
8297 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8298 links_massiv = TRUE;
8299 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8300 rechts_massiv = TRUE;
8302 if (((oben_massiv && unten_massiv) ||
8303 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8304 element == EL_EXPANDABLE_WALL) &&
8305 ((links_massiv && rechts_massiv) ||
8306 element == EL_EXPANDABLE_WALL_VERTICAL))
8307 Feld[ax][ay] = EL_WALL;
8310 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8313 void MauerAblegerStahl(int ax, int ay)
8315 int element = Feld[ax][ay];
8316 int graphic = el2img(element);
8317 boolean oben_frei = FALSE, unten_frei = FALSE;
8318 boolean links_frei = FALSE, rechts_frei = FALSE;
8319 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8320 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8321 boolean new_wall = FALSE;
8323 if (IS_ANIMATED(graphic))
8324 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8326 if (!MovDelay[ax][ay]) /* start building new wall */
8327 MovDelay[ax][ay] = 6;
8329 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8332 if (MovDelay[ax][ay])
8336 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8338 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8340 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8342 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8345 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8346 element == EL_EXPANDABLE_STEELWALL_ANY)
8350 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8351 Store[ax][ay-1] = element;
8352 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8353 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8354 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8355 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8360 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8361 Store[ax][ay+1] = element;
8362 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8363 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8364 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8365 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8370 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8371 element == EL_EXPANDABLE_STEELWALL_ANY)
8375 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8376 Store[ax-1][ay] = element;
8377 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8378 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8379 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8380 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8386 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8387 Store[ax+1][ay] = element;
8388 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8389 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8390 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8391 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8396 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8398 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8399 unten_massiv = TRUE;
8400 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8401 links_massiv = TRUE;
8402 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8403 rechts_massiv = TRUE;
8405 if (((oben_massiv && unten_massiv) ||
8406 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8407 ((links_massiv && rechts_massiv) ||
8408 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8409 Feld[ax][ay] = EL_WALL;
8412 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8415 void CheckForDragon(int x, int y)
8418 boolean dragon_found = FALSE;
8419 static int xy[4][2] =
8427 for (i = 0; i < NUM_DIRECTIONS; i++)
8429 for (j = 0; j < 4; j++)
8431 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8433 if (IN_LEV_FIELD(xx, yy) &&
8434 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8436 if (Feld[xx][yy] == EL_DRAGON)
8437 dragon_found = TRUE;
8446 for (i = 0; i < NUM_DIRECTIONS; i++)
8448 for (j = 0; j < 3; j++)
8450 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8452 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8454 Feld[xx][yy] = EL_EMPTY;
8455 DrawLevelField(xx, yy);
8464 static void InitBuggyBase(int x, int y)
8466 int element = Feld[x][y];
8467 int activating_delay = FRAMES_PER_SECOND / 4;
8470 (element == EL_SP_BUGGY_BASE ?
8471 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8472 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8474 element == EL_SP_BUGGY_BASE_ACTIVE ?
8475 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8478 static void WarnBuggyBase(int x, int y)
8481 static int xy[4][2] =
8489 for (i = 0; i < NUM_DIRECTIONS; i++)
8491 int xx = x + xy[i][0];
8492 int yy = y + xy[i][1];
8494 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8496 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8503 static void InitTrap(int x, int y)
8505 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8508 static void ActivateTrap(int x, int y)
8510 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8513 static void ChangeActiveTrap(int x, int y)
8515 int graphic = IMG_TRAP_ACTIVE;
8517 /* if new animation frame was drawn, correct crumbled sand border */
8518 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8519 DrawLevelFieldCrumbledSand(x, y);
8522 static int getSpecialActionElement(int element, int number, int base_element)
8524 return (element != EL_EMPTY ? element :
8525 number != -1 ? base_element + number - 1 :
8529 static int getModifiedActionNumber(int value_old, int operator, int operand,
8530 int value_min, int value_max)
8532 int value_new = (operator == CA_MODE_SET ? operand :
8533 operator == CA_MODE_ADD ? value_old + operand :
8534 operator == CA_MODE_SUBTRACT ? value_old - operand :
8535 operator == CA_MODE_MULTIPLY ? value_old * operand :
8536 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8537 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8540 return (value_new < value_min ? value_min :
8541 value_new > value_max ? value_max :
8545 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8547 struct ElementInfo *ei = &element_info[element];
8548 struct ElementChangeInfo *change = &ei->change_page[page];
8549 int target_element = change->target_element;
8550 int action_type = change->action_type;
8551 int action_mode = change->action_mode;
8552 int action_arg = change->action_arg;
8555 if (!change->has_action)
8558 /* ---------- determine action paramater values -------------------------- */
8560 int level_time_value =
8561 (level.time > 0 ? TimeLeft :
8564 int action_arg_element =
8565 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8566 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8567 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8570 int action_arg_direction =
8571 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8572 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8573 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8574 change->actual_trigger_side :
8575 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8576 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8579 int action_arg_number_min =
8580 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8583 int action_arg_number_max =
8584 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8585 action_type == CA_SET_LEVEL_GEMS ? 999 :
8586 action_type == CA_SET_LEVEL_TIME ? 9999 :
8587 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8588 action_type == CA_SET_CE_VALUE ? 9999 :
8589 action_type == CA_SET_CE_SCORE ? 9999 :
8592 int action_arg_number_reset =
8593 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8594 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8595 action_type == CA_SET_LEVEL_TIME ? level.time :
8596 action_type == CA_SET_LEVEL_SCORE ? 0 :
8597 #if USE_NEW_CUSTOM_VALUE
8598 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8600 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8602 action_type == CA_SET_CE_SCORE ? 0 :
8605 int action_arg_number =
8606 (action_arg <= CA_ARG_MAX ? action_arg :
8607 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8608 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8609 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8610 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8611 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8612 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8613 #if USE_NEW_CUSTOM_VALUE
8614 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8616 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8618 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8619 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8620 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8621 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8622 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8623 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8624 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8625 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8626 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8627 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8628 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8631 int action_arg_number_old =
8632 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8633 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8634 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8635 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8636 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8639 int action_arg_number_new =
8640 getModifiedActionNumber(action_arg_number_old,
8641 action_mode, action_arg_number,
8642 action_arg_number_min, action_arg_number_max);
8644 int trigger_player_bits =
8645 (change->actual_trigger_player >= EL_PLAYER_1 &&
8646 change->actual_trigger_player <= EL_PLAYER_4 ?
8647 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8650 int action_arg_player_bits =
8651 (action_arg >= CA_ARG_PLAYER_1 &&
8652 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8653 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8656 /* ---------- execute action -------------------------------------------- */
8658 switch (action_type)
8665 /* ---------- level actions ------------------------------------------- */
8667 case CA_RESTART_LEVEL:
8669 game.restart_level = TRUE;
8674 case CA_SHOW_ENVELOPE:
8676 int element = getSpecialActionElement(action_arg_element,
8677 action_arg_number, EL_ENVELOPE_1);
8679 if (IS_ENVELOPE(element))
8680 local_player->show_envelope = element;
8685 case CA_SET_LEVEL_TIME:
8687 if (level.time > 0) /* only modify limited time value */
8689 TimeLeft = action_arg_number_new;
8691 DrawGameValue_Time(TimeLeft);
8693 if (!TimeLeft && setup.time_limit)
8694 for (i = 0; i < MAX_PLAYERS; i++)
8695 KillPlayer(&stored_player[i]);
8701 case CA_SET_LEVEL_SCORE:
8703 local_player->score = action_arg_number_new;
8705 DrawGameValue_Score(local_player->score);
8710 case CA_SET_LEVEL_GEMS:
8712 local_player->gems_still_needed = action_arg_number_new;
8714 DrawGameValue_Emeralds(local_player->gems_still_needed);
8719 #if !USE_PLAYER_GRAVITY
8720 case CA_SET_LEVEL_GRAVITY:
8722 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8723 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8724 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8730 case CA_SET_LEVEL_WIND:
8732 game.wind_direction = action_arg_direction;
8737 /* ---------- player actions ------------------------------------------ */
8739 case CA_MOVE_PLAYER:
8741 /* automatically move to the next field in specified direction */
8742 for (i = 0; i < MAX_PLAYERS; i++)
8743 if (trigger_player_bits & (1 << i))
8744 stored_player[i].programmed_action = action_arg_direction;
8749 case CA_EXIT_PLAYER:
8751 for (i = 0; i < MAX_PLAYERS; i++)
8752 if (action_arg_player_bits & (1 << i))
8753 PlayerWins(&stored_player[i]);
8758 case CA_KILL_PLAYER:
8760 for (i = 0; i < MAX_PLAYERS; i++)
8761 if (action_arg_player_bits & (1 << i))
8762 KillPlayer(&stored_player[i]);
8767 case CA_SET_PLAYER_KEYS:
8769 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8770 int element = getSpecialActionElement(action_arg_element,
8771 action_arg_number, EL_KEY_1);
8773 if (IS_KEY(element))
8775 for (i = 0; i < MAX_PLAYERS; i++)
8777 if (trigger_player_bits & (1 << i))
8779 stored_player[i].key[KEY_NR(element)] = key_state;
8781 DrawGameDoorValues();
8789 case CA_SET_PLAYER_SPEED:
8791 for (i = 0; i < MAX_PLAYERS; i++)
8793 if (trigger_player_bits & (1 << i))
8795 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8797 if (action_arg == CA_ARG_SPEED_FASTER &&
8798 stored_player[i].cannot_move)
8800 action_arg_number = STEPSIZE_VERY_SLOW;
8802 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8803 action_arg == CA_ARG_SPEED_FASTER)
8805 action_arg_number = 2;
8806 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8809 else if (action_arg == CA_ARG_NUMBER_RESET)
8811 action_arg_number = level.initial_player_stepsize[i];
8815 getModifiedActionNumber(move_stepsize,
8818 action_arg_number_min,
8819 action_arg_number_max);
8821 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8828 case CA_SET_PLAYER_SHIELD:
8830 for (i = 0; i < MAX_PLAYERS; i++)
8832 if (trigger_player_bits & (1 << i))
8834 if (action_arg == CA_ARG_SHIELD_OFF)
8836 stored_player[i].shield_normal_time_left = 0;
8837 stored_player[i].shield_deadly_time_left = 0;
8839 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8841 stored_player[i].shield_normal_time_left = 999999;
8843 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8845 stored_player[i].shield_normal_time_left = 999999;
8846 stored_player[i].shield_deadly_time_left = 999999;
8854 #if USE_PLAYER_GRAVITY
8855 case CA_SET_PLAYER_GRAVITY:
8857 for (i = 0; i < MAX_PLAYERS; i++)
8859 if (trigger_player_bits & (1 << i))
8861 stored_player[i].gravity =
8862 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8863 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8864 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8865 stored_player[i].gravity);
8873 case CA_SET_PLAYER_ARTWORK:
8875 for (i = 0; i < MAX_PLAYERS; i++)
8877 if (trigger_player_bits & (1 << i))
8879 int artwork_element = action_arg_element;
8881 if (action_arg == CA_ARG_ELEMENT_RESET)
8883 (level.use_artwork_element[i] ? level.artwork_element[i] :
8884 stored_player[i].element_nr);
8886 #if USE_GFX_RESET_PLAYER_ARTWORK
8887 if (stored_player[i].artwork_element != artwork_element)
8888 stored_player[i].Frame = 0;
8891 stored_player[i].artwork_element = artwork_element;
8893 SetPlayerWaiting(&stored_player[i], FALSE);
8895 /* set number of special actions for bored and sleeping animation */
8896 stored_player[i].num_special_action_bored =
8897 get_num_special_action(artwork_element,
8898 ACTION_BORING_1, ACTION_BORING_LAST);
8899 stored_player[i].num_special_action_sleeping =
8900 get_num_special_action(artwork_element,
8901 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8908 /* ---------- CE actions ---------------------------------------------- */
8910 case CA_SET_CE_VALUE:
8912 #if USE_NEW_CUSTOM_VALUE
8913 int last_ce_value = CustomValue[x][y];
8915 CustomValue[x][y] = action_arg_number_new;
8917 if (CustomValue[x][y] != last_ce_value)
8919 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8920 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8922 if (CustomValue[x][y] == 0)
8924 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8925 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8933 case CA_SET_CE_SCORE:
8935 #if USE_NEW_CUSTOM_VALUE
8936 int last_ce_score = ei->collect_score;
8938 ei->collect_score = action_arg_number_new;
8940 if (ei->collect_score != last_ce_score)
8942 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8943 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8945 if (ei->collect_score == 0)
8949 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8950 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8953 This is a very special case that seems to be a mixture between
8954 CheckElementChange() and CheckTriggeredElementChange(): while
8955 the first one only affects single elements that are triggered
8956 directly, the second one affects multiple elements in the playfield
8957 that are triggered indirectly by another element. This is a third
8958 case: Changing the CE score always affects multiple identical CEs,
8959 so every affected CE must be checked, not only the single CE for
8960 which the CE score was changed in the first place (as every instance
8961 of that CE shares the same CE score, and therefore also can change)!
8963 SCAN_PLAYFIELD(xx, yy)
8965 if (Feld[xx][yy] == element)
8966 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8967 CE_SCORE_GETS_ZERO);
8976 /* ---------- engine actions ------------------------------------------ */
8978 case CA_SET_ENGINE_SCAN_MODE:
8980 InitPlayfieldScanMode(action_arg);
8990 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8992 int old_element = Feld[x][y];
8993 int new_element = GetElementFromGroupElement(element);
8994 int previous_move_direction = MovDir[x][y];
8995 #if USE_NEW_CUSTOM_VALUE
8996 int last_ce_value = CustomValue[x][y];
8998 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8999 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9000 boolean add_player_onto_element = (new_element_is_player &&
9001 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9002 /* this breaks SnakeBite when a snake is
9003 halfway through a door that closes */
9004 /* NOW FIXED AT LEVEL INIT IN files.c */
9005 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9007 IS_WALKABLE(old_element));
9010 /* check if element under the player changes from accessible to unaccessible
9011 (needed for special case of dropping element which then changes) */
9012 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9013 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9021 if (!add_player_onto_element)
9023 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9024 RemoveMovingField(x, y);
9028 Feld[x][y] = new_element;
9030 #if !USE_GFX_RESET_GFX_ANIMATION
9031 ResetGfxAnimation(x, y);
9032 ResetRandomAnimationValue(x, y);
9035 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9036 MovDir[x][y] = previous_move_direction;
9038 #if USE_NEW_CUSTOM_VALUE
9039 if (element_info[new_element].use_last_ce_value)
9040 CustomValue[x][y] = last_ce_value;
9043 InitField_WithBug1(x, y, FALSE);
9045 new_element = Feld[x][y]; /* element may have changed */
9047 #if USE_GFX_RESET_GFX_ANIMATION
9048 ResetGfxAnimation(x, y);
9049 ResetRandomAnimationValue(x, y);
9052 DrawLevelField(x, y);
9054 if (GFX_CRUMBLED(new_element))
9055 DrawLevelFieldCrumbledSandNeighbours(x, y);
9059 /* check if element under the player changes from accessible to unaccessible
9060 (needed for special case of dropping element which then changes) */
9061 /* (must be checked after creating new element for walkable group elements) */
9062 #if USE_FIX_KILLED_BY_NON_WALKABLE
9063 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9064 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9071 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9072 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9081 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9082 if (new_element_is_player)
9083 RelocatePlayer(x, y, new_element);
9086 ChangeCount[x][y]++; /* count number of changes in the same frame */
9088 TestIfBadThingTouchesPlayer(x, y);
9089 TestIfPlayerTouchesCustomElement(x, y);
9090 TestIfElementTouchesCustomElement(x, y);
9093 static void CreateField(int x, int y, int element)
9095 CreateFieldExt(x, y, element, FALSE);
9098 static void CreateElementFromChange(int x, int y, int element)
9100 element = GET_VALID_RUNTIME_ELEMENT(element);
9102 #if USE_STOP_CHANGED_ELEMENTS
9103 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9105 int old_element = Feld[x][y];
9107 /* prevent changed element from moving in same engine frame
9108 unless both old and new element can either fall or move */
9109 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9110 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9115 CreateFieldExt(x, y, element, TRUE);
9118 static boolean ChangeElement(int x, int y, int element, int page)
9120 struct ElementInfo *ei = &element_info[element];
9121 struct ElementChangeInfo *change = &ei->change_page[page];
9122 int ce_value = CustomValue[x][y];
9123 int ce_score = ei->collect_score;
9125 int old_element = Feld[x][y];
9127 /* always use default change event to prevent running into a loop */
9128 if (ChangeEvent[x][y] == -1)
9129 ChangeEvent[x][y] = CE_DELAY;
9131 if (ChangeEvent[x][y] == CE_DELAY)
9133 /* reset actual trigger element, trigger player and action element */
9134 change->actual_trigger_element = EL_EMPTY;
9135 change->actual_trigger_player = EL_PLAYER_1;
9136 change->actual_trigger_side = CH_SIDE_NONE;
9137 change->actual_trigger_ce_value = 0;
9138 change->actual_trigger_ce_score = 0;
9141 /* do not change elements more than a specified maximum number of changes */
9142 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9145 ChangeCount[x][y]++; /* count number of changes in the same frame */
9147 if (change->explode)
9154 if (change->use_target_content)
9156 boolean complete_replace = TRUE;
9157 boolean can_replace[3][3];
9160 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9163 boolean is_walkable;
9164 boolean is_diggable;
9165 boolean is_collectible;
9166 boolean is_removable;
9167 boolean is_destructible;
9168 int ex = x + xx - 1;
9169 int ey = y + yy - 1;
9170 int content_element = change->target_content.e[xx][yy];
9173 can_replace[xx][yy] = TRUE;
9175 if (ex == x && ey == y) /* do not check changing element itself */
9178 if (content_element == EL_EMPTY_SPACE)
9180 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9185 if (!IN_LEV_FIELD(ex, ey))
9187 can_replace[xx][yy] = FALSE;
9188 complete_replace = FALSE;
9195 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9196 e = MovingOrBlocked2Element(ex, ey);
9198 is_empty = (IS_FREE(ex, ey) ||
9199 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9201 is_walkable = (is_empty || IS_WALKABLE(e));
9202 is_diggable = (is_empty || IS_DIGGABLE(e));
9203 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9204 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9205 is_removable = (is_diggable || is_collectible);
9207 can_replace[xx][yy] =
9208 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9209 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9210 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9211 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9212 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9213 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9214 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9216 if (!can_replace[xx][yy])
9217 complete_replace = FALSE;
9220 if (!change->only_if_complete || complete_replace)
9222 boolean something_has_changed = FALSE;
9224 if (change->only_if_complete && change->use_random_replace &&
9225 RND(100) < change->random_percentage)
9228 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9230 int ex = x + xx - 1;
9231 int ey = y + yy - 1;
9232 int content_element;
9234 if (can_replace[xx][yy] && (!change->use_random_replace ||
9235 RND(100) < change->random_percentage))
9237 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9238 RemoveMovingField(ex, ey);
9240 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9242 content_element = change->target_content.e[xx][yy];
9243 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9244 ce_value, ce_score);
9246 CreateElementFromChange(ex, ey, target_element);
9248 something_has_changed = TRUE;
9250 /* for symmetry reasons, freeze newly created border elements */
9251 if (ex != x || ey != y)
9252 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9256 if (something_has_changed)
9258 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9259 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9265 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9266 ce_value, ce_score);
9268 if (element == EL_DIAGONAL_GROWING ||
9269 element == EL_DIAGONAL_SHRINKING)
9271 target_element = Store[x][y];
9273 Store[x][y] = EL_EMPTY;
9276 CreateElementFromChange(x, y, target_element);
9278 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9279 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9282 /* this uses direct change before indirect change */
9283 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9288 #if USE_NEW_DELAYED_ACTION
9290 static void HandleElementChange(int x, int y, int page)
9292 int element = MovingOrBlocked2Element(x, y);
9293 struct ElementInfo *ei = &element_info[element];
9294 struct ElementChangeInfo *change = &ei->change_page[page];
9297 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9298 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9301 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9302 x, y, element, element_info[element].token_name);
9303 printf("HandleElementChange(): This should never happen!\n");
9308 /* this can happen with classic bombs on walkable, changing elements */
9309 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9312 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9313 ChangeDelay[x][y] = 0;
9319 if (ChangeDelay[x][y] == 0) /* initialize element change */
9321 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9323 if (change->can_change)
9326 /* !!! not clear why graphic animation should be reset at all here !!! */
9327 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9328 #if USE_GFX_RESET_WHEN_NOT_MOVING
9329 /* when a custom element is about to change (for example by change delay),
9330 do not reset graphic animation when the custom element is moving */
9331 if (!IS_MOVING(x, y))
9334 ResetGfxAnimation(x, y);
9335 ResetRandomAnimationValue(x, y);
9339 if (change->pre_change_function)
9340 change->pre_change_function(x, y);
9344 ChangeDelay[x][y]--;
9346 if (ChangeDelay[x][y] != 0) /* continue element change */
9348 if (change->can_change)
9350 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9352 if (IS_ANIMATED(graphic))
9353 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9355 if (change->change_function)
9356 change->change_function(x, y);
9359 else /* finish element change */
9361 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9363 page = ChangePage[x][y];
9364 ChangePage[x][y] = -1;
9366 change = &ei->change_page[page];
9369 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9371 ChangeDelay[x][y] = 1; /* try change after next move step */
9372 ChangePage[x][y] = page; /* remember page to use for change */
9377 if (change->can_change)
9379 if (ChangeElement(x, y, element, page))
9381 if (change->post_change_function)
9382 change->post_change_function(x, y);
9386 if (change->has_action)
9387 ExecuteCustomElementAction(x, y, element, page);
9393 static void HandleElementChange(int x, int y, int page)
9395 int element = MovingOrBlocked2Element(x, y);
9396 struct ElementInfo *ei = &element_info[element];
9397 struct ElementChangeInfo *change = &ei->change_page[page];
9400 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9403 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9404 x, y, element, element_info[element].token_name);
9405 printf("HandleElementChange(): This should never happen!\n");
9410 /* this can happen with classic bombs on walkable, changing elements */
9411 if (!CAN_CHANGE(element))
9414 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9415 ChangeDelay[x][y] = 0;
9421 if (ChangeDelay[x][y] == 0) /* initialize element change */
9423 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9425 ResetGfxAnimation(x, y);
9426 ResetRandomAnimationValue(x, y);
9428 if (change->pre_change_function)
9429 change->pre_change_function(x, y);
9432 ChangeDelay[x][y]--;
9434 if (ChangeDelay[x][y] != 0) /* continue element change */
9436 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9438 if (IS_ANIMATED(graphic))
9439 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9441 if (change->change_function)
9442 change->change_function(x, y);
9444 else /* finish element change */
9446 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9448 page = ChangePage[x][y];
9449 ChangePage[x][y] = -1;
9451 change = &ei->change_page[page];
9454 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9456 ChangeDelay[x][y] = 1; /* try change after next move step */
9457 ChangePage[x][y] = page; /* remember page to use for change */
9462 if (ChangeElement(x, y, element, page))
9464 if (change->post_change_function)
9465 change->post_change_function(x, y);
9472 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9473 int trigger_element,
9479 boolean change_done_any = FALSE;
9480 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9483 if (!(trigger_events[trigger_element][trigger_event]))
9487 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9488 trigger_event, recursion_loop_depth, recursion_loop_detected,
9489 recursion_loop_element, EL_NAME(recursion_loop_element));
9492 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9496 int element = EL_CUSTOM_START + i;
9497 boolean change_done = FALSE;
9500 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9501 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9504 for (p = 0; p < element_info[element].num_change_pages; p++)
9506 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9508 if (change->can_change_or_has_action &&
9509 change->has_event[trigger_event] &&
9510 change->trigger_side & trigger_side &&
9511 change->trigger_player & trigger_player &&
9512 change->trigger_page & trigger_page_bits &&
9513 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9515 change->actual_trigger_element = trigger_element;
9516 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9517 change->actual_trigger_side = trigger_side;
9518 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9519 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9521 if ((change->can_change && !change_done) || change->has_action)
9525 SCAN_PLAYFIELD(x, y)
9527 if (Feld[x][y] == element)
9529 if (change->can_change && !change_done)
9531 ChangeDelay[x][y] = 1;
9532 ChangeEvent[x][y] = trigger_event;
9534 HandleElementChange(x, y, p);
9536 #if USE_NEW_DELAYED_ACTION
9537 else if (change->has_action)
9539 ExecuteCustomElementAction(x, y, element, p);
9540 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9543 if (change->has_action)
9545 ExecuteCustomElementAction(x, y, element, p);
9546 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9552 if (change->can_change)
9555 change_done_any = TRUE;
9562 RECURSION_LOOP_DETECTION_END();
9564 return change_done_any;
9567 static boolean CheckElementChangeExt(int x, int y,
9569 int trigger_element,
9574 boolean change_done = FALSE;
9577 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9578 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9581 if (Feld[x][y] == EL_BLOCKED)
9583 Blocked2Moving(x, y, &x, &y);
9584 element = Feld[x][y];
9588 /* check if element has already changed */
9589 if (Feld[x][y] != element)
9592 /* check if element has already changed or is about to change after moving */
9593 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9594 Feld[x][y] != element) ||
9596 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9597 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9598 ChangePage[x][y] != -1)))
9603 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9604 trigger_event, recursion_loop_depth, recursion_loop_detected,
9605 recursion_loop_element, EL_NAME(recursion_loop_element));
9608 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9610 for (p = 0; p < element_info[element].num_change_pages; p++)
9612 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9614 /* check trigger element for all events where the element that is checked
9615 for changing interacts with a directly adjacent element -- this is
9616 different to element changes that affect other elements to change on the
9617 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9618 boolean check_trigger_element =
9619 (trigger_event == CE_TOUCHING_X ||
9620 trigger_event == CE_HITTING_X ||
9621 trigger_event == CE_HIT_BY_X ||
9623 /* this one was forgotten until 3.2.3 */
9624 trigger_event == CE_DIGGING_X);
9627 if (change->can_change_or_has_action &&
9628 change->has_event[trigger_event] &&
9629 change->trigger_side & trigger_side &&
9630 change->trigger_player & trigger_player &&
9631 (!check_trigger_element ||
9632 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9634 change->actual_trigger_element = trigger_element;
9635 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9636 change->actual_trigger_side = trigger_side;
9637 change->actual_trigger_ce_value = CustomValue[x][y];
9638 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9640 /* special case: trigger element not at (x,y) position for some events */
9641 if (check_trigger_element)
9653 { 0, 0 }, { 0, 0 }, { 0, 0 },
9657 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9658 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9660 change->actual_trigger_ce_value = CustomValue[xx][yy];
9661 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9664 if (change->can_change && !change_done)
9666 ChangeDelay[x][y] = 1;
9667 ChangeEvent[x][y] = trigger_event;
9669 HandleElementChange(x, y, p);
9673 #if USE_NEW_DELAYED_ACTION
9674 else if (change->has_action)
9676 ExecuteCustomElementAction(x, y, element, p);
9677 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9680 if (change->has_action)
9682 ExecuteCustomElementAction(x, y, element, p);
9683 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9689 RECURSION_LOOP_DETECTION_END();
9694 static void PlayPlayerSound(struct PlayerInfo *player)
9696 int jx = player->jx, jy = player->jy;
9697 int sound_element = player->artwork_element;
9698 int last_action = player->last_action_waiting;
9699 int action = player->action_waiting;
9701 if (player->is_waiting)
9703 if (action != last_action)
9704 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9706 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9710 if (action != last_action)
9711 StopSound(element_info[sound_element].sound[last_action]);
9713 if (last_action == ACTION_SLEEPING)
9714 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9718 static void PlayAllPlayersSound()
9722 for (i = 0; i < MAX_PLAYERS; i++)
9723 if (stored_player[i].active)
9724 PlayPlayerSound(&stored_player[i]);
9727 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9729 boolean last_waiting = player->is_waiting;
9730 int move_dir = player->MovDir;
9732 player->dir_waiting = move_dir;
9733 player->last_action_waiting = player->action_waiting;
9737 if (!last_waiting) /* not waiting -> waiting */
9739 player->is_waiting = TRUE;
9741 player->frame_counter_bored =
9743 game.player_boring_delay_fixed +
9744 GetSimpleRandom(game.player_boring_delay_random);
9745 player->frame_counter_sleeping =
9747 game.player_sleeping_delay_fixed +
9748 GetSimpleRandom(game.player_sleeping_delay_random);
9750 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9753 if (game.player_sleeping_delay_fixed +
9754 game.player_sleeping_delay_random > 0 &&
9755 player->anim_delay_counter == 0 &&
9756 player->post_delay_counter == 0 &&
9757 FrameCounter >= player->frame_counter_sleeping)
9758 player->is_sleeping = TRUE;
9759 else if (game.player_boring_delay_fixed +
9760 game.player_boring_delay_random > 0 &&
9761 FrameCounter >= player->frame_counter_bored)
9762 player->is_bored = TRUE;
9764 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9765 player->is_bored ? ACTION_BORING :
9768 if (player->is_sleeping && player->use_murphy)
9770 /* special case for sleeping Murphy when leaning against non-free tile */
9772 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9773 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9774 !IS_MOVING(player->jx - 1, player->jy)))
9776 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9777 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9778 !IS_MOVING(player->jx + 1, player->jy)))
9779 move_dir = MV_RIGHT;
9781 player->is_sleeping = FALSE;
9783 player->dir_waiting = move_dir;
9786 if (player->is_sleeping)
9788 if (player->num_special_action_sleeping > 0)
9790 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9792 int last_special_action = player->special_action_sleeping;
9793 int num_special_action = player->num_special_action_sleeping;
9794 int special_action =
9795 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9796 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9797 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9798 last_special_action + 1 : ACTION_SLEEPING);
9799 int special_graphic =
9800 el_act_dir2img(player->artwork_element, special_action, move_dir);
9802 player->anim_delay_counter =
9803 graphic_info[special_graphic].anim_delay_fixed +
9804 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9805 player->post_delay_counter =
9806 graphic_info[special_graphic].post_delay_fixed +
9807 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9809 player->special_action_sleeping = special_action;
9812 if (player->anim_delay_counter > 0)
9814 player->action_waiting = player->special_action_sleeping;
9815 player->anim_delay_counter--;
9817 else if (player->post_delay_counter > 0)
9819 player->post_delay_counter--;
9823 else if (player->is_bored)
9825 if (player->num_special_action_bored > 0)
9827 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9829 int special_action =
9830 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9831 int special_graphic =
9832 el_act_dir2img(player->artwork_element, special_action, move_dir);
9834 player->anim_delay_counter =
9835 graphic_info[special_graphic].anim_delay_fixed +
9836 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9837 player->post_delay_counter =
9838 graphic_info[special_graphic].post_delay_fixed +
9839 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9841 player->special_action_bored = special_action;
9844 if (player->anim_delay_counter > 0)
9846 player->action_waiting = player->special_action_bored;
9847 player->anim_delay_counter--;
9849 else if (player->post_delay_counter > 0)
9851 player->post_delay_counter--;
9856 else if (last_waiting) /* waiting -> not waiting */
9858 player->is_waiting = FALSE;
9859 player->is_bored = FALSE;
9860 player->is_sleeping = FALSE;
9862 player->frame_counter_bored = -1;
9863 player->frame_counter_sleeping = -1;
9865 player->anim_delay_counter = 0;
9866 player->post_delay_counter = 0;
9868 player->dir_waiting = player->MovDir;
9869 player->action_waiting = ACTION_DEFAULT;
9871 player->special_action_bored = ACTION_DEFAULT;
9872 player->special_action_sleeping = ACTION_DEFAULT;
9876 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9878 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9879 int left = player_action & JOY_LEFT;
9880 int right = player_action & JOY_RIGHT;
9881 int up = player_action & JOY_UP;
9882 int down = player_action & JOY_DOWN;
9883 int button1 = player_action & JOY_BUTTON_1;
9884 int button2 = player_action & JOY_BUTTON_2;
9885 int dx = (left ? -1 : right ? 1 : 0);
9886 int dy = (up ? -1 : down ? 1 : 0);
9888 if (!player->active || tape.pausing)
9894 snapped = SnapField(player, dx, dy);
9898 dropped = DropElement(player);
9900 moved = MovePlayer(player, dx, dy);
9903 if (tape.single_step && tape.recording && !tape.pausing)
9905 if (button1 || (dropped && !moved))
9907 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9908 SnapField(player, 0, 0); /* stop snapping */
9912 SetPlayerWaiting(player, FALSE);
9914 return player_action;
9918 /* no actions for this player (no input at player's configured device) */
9920 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9921 SnapField(player, 0, 0);
9922 CheckGravityMovementWhenNotMoving(player);
9924 if (player->MovPos == 0)
9925 SetPlayerWaiting(player, TRUE);
9927 if (player->MovPos == 0) /* needed for tape.playing */
9928 player->is_moving = FALSE;
9930 player->is_dropping = FALSE;
9931 player->is_dropping_pressed = FALSE;
9932 player->drop_pressed_delay = 0;
9938 static void CheckLevelTime()
9942 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9944 if (level.native_em_level->lev->home == 0) /* all players at home */
9946 PlayerWins(local_player);
9948 AllPlayersGone = TRUE;
9950 level.native_em_level->lev->home = -1;
9953 if (level.native_em_level->ply[0]->alive == 0 &&
9954 level.native_em_level->ply[1]->alive == 0 &&
9955 level.native_em_level->ply[2]->alive == 0 &&
9956 level.native_em_level->ply[3]->alive == 0) /* all dead */
9957 AllPlayersGone = TRUE;
9960 if (TimeFrames >= FRAMES_PER_SECOND)
9965 for (i = 0; i < MAX_PLAYERS; i++)
9967 struct PlayerInfo *player = &stored_player[i];
9969 if (SHIELD_ON(player))
9971 player->shield_normal_time_left--;
9973 if (player->shield_deadly_time_left > 0)
9974 player->shield_deadly_time_left--;
9978 if (!local_player->LevelSolved && !level.use_step_counter)
9986 if (TimeLeft <= 10 && setup.time_limit)
9987 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9989 DrawGameValue_Time(TimeLeft);
9991 if (!TimeLeft && setup.time_limit)
9993 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9994 level.native_em_level->lev->killed_out_of_time = TRUE;
9996 for (i = 0; i < MAX_PLAYERS; i++)
9997 KillPlayer(&stored_player[i]);
10000 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10001 DrawGameValue_Time(TimePlayed);
10003 level.native_em_level->lev->time =
10004 (level.time == 0 ? TimePlayed : TimeLeft);
10007 if (tape.recording || tape.playing)
10008 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10012 void AdvanceFrameAndPlayerCounters(int player_nr)
10016 /* advance frame counters (global frame counter and time frame counter) */
10020 /* advance player counters (counters for move delay, move animation etc.) */
10021 for (i = 0; i < MAX_PLAYERS; i++)
10023 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10024 int move_delay_value = stored_player[i].move_delay_value;
10025 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10027 if (!advance_player_counters) /* not all players may be affected */
10030 #if USE_NEW_PLAYER_ANIM
10031 if (move_frames == 0) /* less than one move per game frame */
10033 int stepsize = TILEX / move_delay_value;
10034 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10035 int count = (stored_player[i].is_moving ?
10036 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10038 if (count % delay == 0)
10043 stored_player[i].Frame += move_frames;
10045 if (stored_player[i].MovPos != 0)
10046 stored_player[i].StepFrame += move_frames;
10048 if (stored_player[i].move_delay > 0)
10049 stored_player[i].move_delay--;
10051 /* due to bugs in previous versions, counter must count up, not down */
10052 if (stored_player[i].push_delay != -1)
10053 stored_player[i].push_delay++;
10055 if (stored_player[i].drop_delay > 0)
10056 stored_player[i].drop_delay--;
10058 if (stored_player[i].is_dropping_pressed)
10059 stored_player[i].drop_pressed_delay++;
10063 void StartGameActions(boolean init_network_game, boolean record_tape,
10066 unsigned long new_random_seed = InitRND(random_seed);
10069 TapeStartRecording(new_random_seed);
10071 #if defined(NETWORK_AVALIABLE)
10072 if (init_network_game)
10074 SendToServer_StartPlaying();
10085 static unsigned long game_frame_delay = 0;
10086 unsigned long game_frame_delay_value;
10087 byte *recorded_player_action;
10088 byte summarized_player_action = 0;
10089 byte tape_action[MAX_PLAYERS];
10092 /* detect endless loops, caused by custom element programming */
10093 if (recursion_loop_detected && recursion_loop_depth == 0)
10095 char *message = getStringCat3("Internal Error ! Element ",
10096 EL_NAME(recursion_loop_element),
10097 " caused endless loop ! Quit the game ?");
10099 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10100 EL_NAME(recursion_loop_element));
10102 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10104 recursion_loop_detected = FALSE; /* if game should be continued */
10111 if (game.restart_level)
10112 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10114 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10116 if (level.native_em_level->lev->home == 0) /* all players at home */
10118 PlayerWins(local_player);
10120 AllPlayersGone = TRUE;
10122 level.native_em_level->lev->home = -1;
10125 if (level.native_em_level->ply[0]->alive == 0 &&
10126 level.native_em_level->ply[1]->alive == 0 &&
10127 level.native_em_level->ply[2]->alive == 0 &&
10128 level.native_em_level->ply[3]->alive == 0) /* all dead */
10129 AllPlayersGone = TRUE;
10132 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10135 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10138 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10141 game_frame_delay_value =
10142 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10144 if (tape.playing && tape.warp_forward && !tape.pausing)
10145 game_frame_delay_value = 0;
10147 /* ---------- main game synchronization point ---------- */
10149 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10151 if (network_playing && !network_player_action_received)
10153 /* try to get network player actions in time */
10155 #if defined(NETWORK_AVALIABLE)
10156 /* last chance to get network player actions without main loop delay */
10157 HandleNetworking();
10160 /* game was quit by network peer */
10161 if (game_status != GAME_MODE_PLAYING)
10164 if (!network_player_action_received)
10165 return; /* failed to get network player actions in time */
10167 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10173 /* at this point we know that we really continue executing the game */
10175 network_player_action_received = FALSE;
10177 /* when playing tape, read previously recorded player input from tape data */
10178 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10181 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10186 if (tape.set_centered_player)
10188 game.centered_player_nr_next = tape.centered_player_nr_next;
10189 game.set_centered_player = TRUE;
10192 for (i = 0; i < MAX_PLAYERS; i++)
10194 summarized_player_action |= stored_player[i].action;
10196 if (!network_playing)
10197 stored_player[i].effective_action = stored_player[i].action;
10200 #if defined(NETWORK_AVALIABLE)
10201 if (network_playing)
10202 SendToServer_MovePlayer(summarized_player_action);
10205 if (!options.network && !setup.team_mode)
10206 local_player->effective_action = summarized_player_action;
10208 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10210 for (i = 0; i < MAX_PLAYERS; i++)
10211 stored_player[i].effective_action =
10212 (i == game.centered_player_nr ? summarized_player_action : 0);
10215 if (recorded_player_action != NULL)
10216 for (i = 0; i < MAX_PLAYERS; i++)
10217 stored_player[i].effective_action = recorded_player_action[i];
10219 for (i = 0; i < MAX_PLAYERS; i++)
10221 tape_action[i] = stored_player[i].effective_action;
10223 /* (this can only happen in the R'n'D game engine) */
10224 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10225 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10228 /* only record actions from input devices, but not programmed actions */
10229 if (tape.recording)
10230 TapeRecordAction(tape_action);
10232 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10234 GameActions_EM_Main();
10242 void GameActions_EM_Main()
10244 byte effective_action[MAX_PLAYERS];
10245 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10248 for (i = 0; i < MAX_PLAYERS; i++)
10249 effective_action[i] = stored_player[i].effective_action;
10251 GameActions_EM(effective_action, warp_mode);
10255 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10258 void GameActions_RND()
10260 int magic_wall_x = 0, magic_wall_y = 0;
10261 int i, x, y, element, graphic;
10263 InitPlayfieldScanModeVars();
10265 #if USE_ONE_MORE_CHANGE_PER_FRAME
10266 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10268 SCAN_PLAYFIELD(x, y)
10270 ChangeCount[x][y] = 0;
10271 ChangeEvent[x][y] = -1;
10276 if (game.set_centered_player)
10278 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10280 /* switching to "all players" only possible if all players fit to screen */
10281 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10283 game.centered_player_nr_next = game.centered_player_nr;
10284 game.set_centered_player = FALSE;
10287 /* do not switch focus to non-existing (or non-active) player */
10288 if (game.centered_player_nr_next >= 0 &&
10289 !stored_player[game.centered_player_nr_next].active)
10291 game.centered_player_nr_next = game.centered_player_nr;
10292 game.set_centered_player = FALSE;
10296 if (game.set_centered_player &&
10297 ScreenMovPos == 0) /* screen currently aligned at tile position */
10301 if (game.centered_player_nr_next == -1)
10303 setScreenCenteredToAllPlayers(&sx, &sy);
10307 sx = stored_player[game.centered_player_nr_next].jx;
10308 sy = stored_player[game.centered_player_nr_next].jy;
10311 game.centered_player_nr = game.centered_player_nr_next;
10312 game.set_centered_player = FALSE;
10314 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10315 DrawGameDoorValues();
10318 for (i = 0; i < MAX_PLAYERS; i++)
10320 int actual_player_action = stored_player[i].effective_action;
10323 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10324 - rnd_equinox_tetrachloride 048
10325 - rnd_equinox_tetrachloride_ii 096
10326 - rnd_emanuel_schmieg 002
10327 - doctor_sloan_ww 001, 020
10329 if (stored_player[i].MovPos == 0)
10330 CheckGravityMovement(&stored_player[i]);
10333 /* overwrite programmed action with tape action */
10334 if (stored_player[i].programmed_action)
10335 actual_player_action = stored_player[i].programmed_action;
10337 PlayerActions(&stored_player[i], actual_player_action);
10339 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10342 ScrollScreen(NULL, SCROLL_GO_ON);
10344 /* for backwards compatibility, the following code emulates a fixed bug that
10345 occured when pushing elements (causing elements that just made their last
10346 pushing step to already (if possible) make their first falling step in the
10347 same game frame, which is bad); this code is also needed to use the famous
10348 "spring push bug" which is used in older levels and might be wanted to be
10349 used also in newer levels, but in this case the buggy pushing code is only
10350 affecting the "spring" element and no other elements */
10352 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10354 for (i = 0; i < MAX_PLAYERS; i++)
10356 struct PlayerInfo *player = &stored_player[i];
10357 int x = player->jx;
10358 int y = player->jy;
10360 if (player->active && player->is_pushing && player->is_moving &&
10362 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10363 Feld[x][y] == EL_SPRING))
10365 ContinueMoving(x, y);
10367 /* continue moving after pushing (this is actually a bug) */
10368 if (!IS_MOVING(x, y))
10369 Stop[x][y] = FALSE;
10375 debug_print_timestamp(0, "start main loop profiling");
10378 SCAN_PLAYFIELD(x, y)
10380 ChangeCount[x][y] = 0;
10381 ChangeEvent[x][y] = -1;
10383 /* this must be handled before main playfield loop */
10384 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10387 if (MovDelay[x][y] <= 0)
10391 #if USE_NEW_SNAP_DELAY
10392 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10395 if (MovDelay[x][y] <= 0)
10398 DrawLevelField(x, y);
10400 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10406 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10408 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10409 printf("GameActions(): This should never happen!\n");
10411 ChangePage[x][y] = -1;
10415 Stop[x][y] = FALSE;
10416 if (WasJustMoving[x][y] > 0)
10417 WasJustMoving[x][y]--;
10418 if (WasJustFalling[x][y] > 0)
10419 WasJustFalling[x][y]--;
10420 if (CheckCollision[x][y] > 0)
10421 CheckCollision[x][y]--;
10422 if (CheckImpact[x][y] > 0)
10423 CheckImpact[x][y]--;
10427 /* reset finished pushing action (not done in ContinueMoving() to allow
10428 continuous pushing animation for elements with zero push delay) */
10429 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10431 ResetGfxAnimation(x, y);
10432 DrawLevelField(x, y);
10436 if (IS_BLOCKED(x, y))
10440 Blocked2Moving(x, y, &oldx, &oldy);
10441 if (!IS_MOVING(oldx, oldy))
10443 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10444 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10445 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10446 printf("GameActions(): This should never happen!\n");
10453 debug_print_timestamp(0, "- time for pre-main loop:");
10456 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10457 SCAN_PLAYFIELD(x, y)
10459 element = Feld[x][y];
10460 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10465 int element2 = element;
10466 int graphic2 = graphic;
10468 int element2 = Feld[x][y];
10469 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10471 int last_gfx_frame = GfxFrame[x][y];
10473 if (graphic_info[graphic2].anim_global_sync)
10474 GfxFrame[x][y] = FrameCounter;
10475 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10476 GfxFrame[x][y] = CustomValue[x][y];
10477 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10478 GfxFrame[x][y] = element_info[element2].collect_score;
10479 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10480 GfxFrame[x][y] = ChangeDelay[x][y];
10482 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10483 DrawLevelGraphicAnimation(x, y, graphic2);
10486 ResetGfxFrame(x, y, TRUE);
10490 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10491 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10492 ResetRandomAnimationValue(x, y);
10496 SetRandomAnimationValue(x, y);
10500 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10503 #endif // -------------------- !!! TEST ONLY !!! --------------------
10506 debug_print_timestamp(0, "- time for TEST loop: -->");
10509 SCAN_PLAYFIELD(x, y)
10511 element = Feld[x][y];
10512 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10514 ResetGfxFrame(x, y, TRUE);
10516 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10517 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10518 ResetRandomAnimationValue(x, y);
10520 SetRandomAnimationValue(x, y);
10522 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10524 if (IS_INACTIVE(element))
10526 if (IS_ANIMATED(graphic))
10527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10532 /* this may take place after moving, so 'element' may have changed */
10533 if (IS_CHANGING(x, y) &&
10534 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10536 int page = element_info[element].event_page_nr[CE_DELAY];
10539 HandleElementChange(x, y, page);
10541 if (CAN_CHANGE(element))
10542 HandleElementChange(x, y, page);
10544 if (HAS_ACTION(element))
10545 ExecuteCustomElementAction(x, y, element, page);
10548 element = Feld[x][y];
10549 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10552 #if 0 // ---------------------------------------------------------------------
10554 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10558 element = Feld[x][y];
10559 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10561 if (IS_ANIMATED(graphic) &&
10562 !IS_MOVING(x, y) &&
10564 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10566 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10567 DrawTwinkleOnField(x, y);
10569 else if (IS_MOVING(x, y))
10570 ContinueMoving(x, y);
10577 case EL_EM_EXIT_OPEN:
10578 case EL_SP_EXIT_OPEN:
10579 case EL_STEEL_EXIT_OPEN:
10580 case EL_EM_STEEL_EXIT_OPEN:
10581 case EL_SP_TERMINAL:
10582 case EL_SP_TERMINAL_ACTIVE:
10583 case EL_EXTRA_TIME:
10584 case EL_SHIELD_NORMAL:
10585 case EL_SHIELD_DEADLY:
10586 if (IS_ANIMATED(graphic))
10587 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10590 case EL_DYNAMITE_ACTIVE:
10591 case EL_EM_DYNAMITE_ACTIVE:
10592 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10593 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10594 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10595 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10596 case EL_SP_DISK_RED_ACTIVE:
10597 CheckDynamite(x, y);
10600 case EL_AMOEBA_GROWING:
10601 AmoebeWaechst(x, y);
10604 case EL_AMOEBA_SHRINKING:
10605 AmoebaDisappearing(x, y);
10608 #if !USE_NEW_AMOEBA_CODE
10609 case EL_AMOEBA_WET:
10610 case EL_AMOEBA_DRY:
10611 case EL_AMOEBA_FULL:
10613 case EL_EMC_DRIPPER:
10614 AmoebeAbleger(x, y);
10618 case EL_GAME_OF_LIFE:
10623 case EL_EXIT_CLOSED:
10627 case EL_EM_EXIT_CLOSED:
10631 case EL_STEEL_EXIT_CLOSED:
10632 CheckExitSteel(x, y);
10635 case EL_EM_STEEL_EXIT_CLOSED:
10636 CheckExitSteelEM(x, y);
10639 case EL_SP_EXIT_CLOSED:
10643 case EL_EXPANDABLE_WALL_GROWING:
10644 case EL_EXPANDABLE_STEELWALL_GROWING:
10645 MauerWaechst(x, y);
10648 case EL_EXPANDABLE_WALL:
10649 case EL_EXPANDABLE_WALL_HORIZONTAL:
10650 case EL_EXPANDABLE_WALL_VERTICAL:
10651 case EL_EXPANDABLE_WALL_ANY:
10652 case EL_BD_EXPANDABLE_WALL:
10653 MauerAbleger(x, y);
10656 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10657 case EL_EXPANDABLE_STEELWALL_VERTICAL:
10658 case EL_EXPANDABLE_STEELWALL_ANY:
10659 MauerAblegerStahl(x, y);
10663 CheckForDragon(x, y);
10669 case EL_ELEMENT_SNAPPING:
10670 case EL_DIAGONAL_SHRINKING:
10671 case EL_DIAGONAL_GROWING:
10674 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10676 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10681 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10682 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10687 #else // ---------------------------------------------------------------------
10689 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10693 element = Feld[x][y];
10694 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10696 if (IS_ANIMATED(graphic) &&
10697 !IS_MOVING(x, y) &&
10699 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10701 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10702 DrawTwinkleOnField(x, y);
10704 else if ((element == EL_ACID ||
10705 element == EL_EXIT_OPEN ||
10706 element == EL_EM_EXIT_OPEN ||
10707 element == EL_SP_EXIT_OPEN ||
10708 element == EL_STEEL_EXIT_OPEN ||
10709 element == EL_EM_STEEL_EXIT_OPEN ||
10710 element == EL_SP_TERMINAL ||
10711 element == EL_SP_TERMINAL_ACTIVE ||
10712 element == EL_EXTRA_TIME ||
10713 element == EL_SHIELD_NORMAL ||
10714 element == EL_SHIELD_DEADLY) &&
10715 IS_ANIMATED(graphic))
10716 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10717 else if (IS_MOVING(x, y))
10718 ContinueMoving(x, y);
10719 else if (IS_ACTIVE_BOMB(element))
10720 CheckDynamite(x, y);
10721 else if (element == EL_AMOEBA_GROWING)
10722 AmoebeWaechst(x, y);
10723 else if (element == EL_AMOEBA_SHRINKING)
10724 AmoebaDisappearing(x, y);
10726 #if !USE_NEW_AMOEBA_CODE
10727 else if (IS_AMOEBALIVE(element))
10728 AmoebeAbleger(x, y);
10731 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10733 else if (element == EL_EXIT_CLOSED)
10735 else if (element == EL_EM_EXIT_CLOSED)
10737 else if (element == EL_STEEL_EXIT_CLOSED)
10738 CheckExitSteel(x, y);
10739 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10740 CheckExitSteelEM(x, y);
10741 else if (element == EL_SP_EXIT_CLOSED)
10743 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10744 element == EL_EXPANDABLE_STEELWALL_GROWING)
10745 MauerWaechst(x, y);
10746 else if (element == EL_EXPANDABLE_WALL ||
10747 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10748 element == EL_EXPANDABLE_WALL_VERTICAL ||
10749 element == EL_EXPANDABLE_WALL_ANY ||
10750 element == EL_BD_EXPANDABLE_WALL)
10751 MauerAbleger(x, y);
10752 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10753 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10754 element == EL_EXPANDABLE_STEELWALL_ANY)
10755 MauerAblegerStahl(x, y);
10756 else if (element == EL_FLAMES)
10757 CheckForDragon(x, y);
10758 else if (element == EL_EXPLOSION)
10759 ; /* drawing of correct explosion animation is handled separately */
10760 else if (element == EL_ELEMENT_SNAPPING ||
10761 element == EL_DIAGONAL_SHRINKING ||
10762 element == EL_DIAGONAL_GROWING)
10764 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10766 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10768 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10769 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10771 #endif // ---------------------------------------------------------------------
10773 if (IS_BELT_ACTIVE(element))
10774 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10776 if (game.magic_wall_active)
10778 int jx = local_player->jx, jy = local_player->jy;
10780 /* play the element sound at the position nearest to the player */
10781 if ((element == EL_MAGIC_WALL_FULL ||
10782 element == EL_MAGIC_WALL_ACTIVE ||
10783 element == EL_MAGIC_WALL_EMPTYING ||
10784 element == EL_BD_MAGIC_WALL_FULL ||
10785 element == EL_BD_MAGIC_WALL_ACTIVE ||
10786 element == EL_BD_MAGIC_WALL_EMPTYING ||
10787 element == EL_DC_MAGIC_WALL_FULL ||
10788 element == EL_DC_MAGIC_WALL_ACTIVE ||
10789 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10790 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10799 debug_print_timestamp(0, "- time for MAIN loop: -->");
10802 #if USE_NEW_AMOEBA_CODE
10803 /* new experimental amoeba growth stuff */
10804 if (!(FrameCounter % 8))
10806 static unsigned long random = 1684108901;
10808 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10810 x = RND(lev_fieldx);
10811 y = RND(lev_fieldy);
10812 element = Feld[x][y];
10814 if (!IS_PLAYER(x,y) &&
10815 (element == EL_EMPTY ||
10816 CAN_GROW_INTO(element) ||
10817 element == EL_QUICKSAND_EMPTY ||
10818 element == EL_QUICKSAND_FAST_EMPTY ||
10819 element == EL_ACID_SPLASH_LEFT ||
10820 element == EL_ACID_SPLASH_RIGHT))
10822 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10823 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10824 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10825 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10826 Feld[x][y] = EL_AMOEBA_DROP;
10829 random = random * 129 + 1;
10835 if (game.explosions_delayed)
10838 game.explosions_delayed = FALSE;
10840 SCAN_PLAYFIELD(x, y)
10842 element = Feld[x][y];
10844 if (ExplodeField[x][y])
10845 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10846 else if (element == EL_EXPLOSION)
10847 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10849 ExplodeField[x][y] = EX_TYPE_NONE;
10852 game.explosions_delayed = TRUE;
10855 if (game.magic_wall_active)
10857 if (!(game.magic_wall_time_left % 4))
10859 int element = Feld[magic_wall_x][magic_wall_y];
10861 if (element == EL_BD_MAGIC_WALL_FULL ||
10862 element == EL_BD_MAGIC_WALL_ACTIVE ||
10863 element == EL_BD_MAGIC_WALL_EMPTYING)
10864 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10865 else if (element == EL_DC_MAGIC_WALL_FULL ||
10866 element == EL_DC_MAGIC_WALL_ACTIVE ||
10867 element == EL_DC_MAGIC_WALL_EMPTYING)
10868 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10870 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10873 if (game.magic_wall_time_left > 0)
10875 game.magic_wall_time_left--;
10876 if (!game.magic_wall_time_left)
10878 SCAN_PLAYFIELD(x, y)
10880 element = Feld[x][y];
10882 if (element == EL_MAGIC_WALL_ACTIVE ||
10883 element == EL_MAGIC_WALL_FULL)
10885 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10886 DrawLevelField(x, y);
10888 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10889 element == EL_BD_MAGIC_WALL_FULL)
10891 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10892 DrawLevelField(x, y);
10894 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10895 element == EL_DC_MAGIC_WALL_FULL)
10897 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10898 DrawLevelField(x, y);
10902 game.magic_wall_active = FALSE;
10907 if (game.light_time_left > 0)
10909 game.light_time_left--;
10911 if (game.light_time_left == 0)
10912 RedrawAllLightSwitchesAndInvisibleElements();
10915 if (game.timegate_time_left > 0)
10917 game.timegate_time_left--;
10919 if (game.timegate_time_left == 0)
10920 CloseAllOpenTimegates();
10923 if (game.lenses_time_left > 0)
10925 game.lenses_time_left--;
10927 if (game.lenses_time_left == 0)
10928 RedrawAllInvisibleElementsForLenses();
10931 if (game.magnify_time_left > 0)
10933 game.magnify_time_left--;
10935 if (game.magnify_time_left == 0)
10936 RedrawAllInvisibleElementsForMagnifier();
10939 for (i = 0; i < MAX_PLAYERS; i++)
10941 struct PlayerInfo *player = &stored_player[i];
10943 if (SHIELD_ON(player))
10945 if (player->shield_deadly_time_left)
10946 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10947 else if (player->shield_normal_time_left)
10948 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10955 PlayAllPlayersSound();
10957 if (options.debug) /* calculate frames per second */
10959 static unsigned long fps_counter = 0;
10960 static int fps_frames = 0;
10961 unsigned long fps_delay_ms = Counter() - fps_counter;
10965 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10967 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10970 fps_counter = Counter();
10973 redraw_mask |= REDRAW_FPS;
10976 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10978 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10980 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10982 local_player->show_envelope = 0;
10986 debug_print_timestamp(0, "stop main loop profiling ");
10987 printf("----------------------------------------------------------\n");
10990 /* use random number generator in every frame to make it less predictable */
10991 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10995 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10997 int min_x = x, min_y = y, max_x = x, max_y = y;
11000 for (i = 0; i < MAX_PLAYERS; i++)
11002 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11004 if (!stored_player[i].active || &stored_player[i] == player)
11007 min_x = MIN(min_x, jx);
11008 min_y = MIN(min_y, jy);
11009 max_x = MAX(max_x, jx);
11010 max_y = MAX(max_y, jy);
11013 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11016 static boolean AllPlayersInVisibleScreen()
11020 for (i = 0; i < MAX_PLAYERS; i++)
11022 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11024 if (!stored_player[i].active)
11027 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11034 void ScrollLevel(int dx, int dy)
11037 static Bitmap *bitmap_db_field2 = NULL;
11038 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11045 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11046 /* only horizontal XOR vertical scroll direction allowed */
11047 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11052 if (bitmap_db_field2 == NULL)
11053 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11055 /* needed when blitting directly to same bitmap -- should not be needed with
11056 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11057 BlitBitmap(drawto_field, bitmap_db_field2,
11058 FX + TILEX * (dx == -1) - softscroll_offset,
11059 FY + TILEY * (dy == -1) - softscroll_offset,
11060 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11061 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11062 FX + TILEX * (dx == 1) - softscroll_offset,
11063 FY + TILEY * (dy == 1) - softscroll_offset);
11064 BlitBitmap(bitmap_db_field2, drawto_field,
11065 FX + TILEX * (dx == 1) - softscroll_offset,
11066 FY + TILEY * (dy == 1) - softscroll_offset,
11067 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11068 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11069 FX + TILEX * (dx == 1) - softscroll_offset,
11070 FY + TILEY * (dy == 1) - softscroll_offset);
11075 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11076 int xsize = (BX2 - BX1 + 1);
11077 int ysize = (BY2 - BY1 + 1);
11078 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11079 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11080 int step = (start < end ? +1 : -1);
11082 for (i = start; i != end; i += step)
11084 BlitBitmap(drawto_field, drawto_field,
11085 FX + TILEX * (dx != 0 ? i + step : 0),
11086 FY + TILEY * (dy != 0 ? i + step : 0),
11087 TILEX * (dx != 0 ? 1 : xsize),
11088 TILEY * (dy != 0 ? 1 : ysize),
11089 FX + TILEX * (dx != 0 ? i : 0),
11090 FY + TILEY * (dy != 0 ? i : 0));
11095 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11097 BlitBitmap(drawto_field, drawto_field,
11098 FX + TILEX * (dx == -1) - softscroll_offset,
11099 FY + TILEY * (dy == -1) - softscroll_offset,
11100 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11101 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11102 FX + TILEX * (dx == 1) - softscroll_offset,
11103 FY + TILEY * (dy == 1) - softscroll_offset);
11109 x = (dx == 1 ? BX1 : BX2);
11110 for (y = BY1; y <= BY2; y++)
11111 DrawScreenField(x, y);
11116 y = (dy == 1 ? BY1 : BY2);
11117 for (x = BX1; x <= BX2; x++)
11118 DrawScreenField(x, y);
11121 redraw_mask |= REDRAW_FIELD;
11124 static boolean canFallDown(struct PlayerInfo *player)
11126 int jx = player->jx, jy = player->jy;
11128 return (IN_LEV_FIELD(jx, jy + 1) &&
11129 (IS_FREE(jx, jy + 1) ||
11130 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11131 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11132 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11135 static boolean canPassField(int x, int y, int move_dir)
11137 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11138 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11139 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11140 int nextx = x + dx;
11141 int nexty = y + dy;
11142 int element = Feld[x][y];
11144 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11145 !CAN_MOVE(element) &&
11146 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11147 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11148 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11151 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11153 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11154 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11155 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11159 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11160 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11161 (IS_DIGGABLE(Feld[newx][newy]) ||
11162 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11163 canPassField(newx, newy, move_dir)));
11166 static void CheckGravityMovement(struct PlayerInfo *player)
11168 #if USE_PLAYER_GRAVITY
11169 if (player->gravity && !player->programmed_action)
11171 if (game.gravity && !player->programmed_action)
11174 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11175 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11176 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11177 int jx = player->jx, jy = player->jy;
11178 boolean player_is_moving_to_valid_field =
11179 (!player_is_snapping &&
11180 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11181 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11182 boolean player_can_fall_down = canFallDown(player);
11184 if (player_can_fall_down &&
11185 !player_is_moving_to_valid_field)
11186 player->programmed_action = MV_DOWN;
11190 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11192 return CheckGravityMovement(player);
11194 #if USE_PLAYER_GRAVITY
11195 if (player->gravity && !player->programmed_action)
11197 if (game.gravity && !player->programmed_action)
11200 int jx = player->jx, jy = player->jy;
11201 boolean field_under_player_is_free =
11202 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11203 boolean player_is_standing_on_valid_field =
11204 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11205 (IS_WALKABLE(Feld[jx][jy]) &&
11206 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11208 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11209 player->programmed_action = MV_DOWN;
11214 MovePlayerOneStep()
11215 -----------------------------------------------------------------------------
11216 dx, dy: direction (non-diagonal) to try to move the player to
11217 real_dx, real_dy: direction as read from input device (can be diagonal)
11220 boolean MovePlayerOneStep(struct PlayerInfo *player,
11221 int dx, int dy, int real_dx, int real_dy)
11223 int jx = player->jx, jy = player->jy;
11224 int new_jx = jx + dx, new_jy = jy + dy;
11225 #if !USE_FIXED_DONT_RUN_INTO
11229 boolean player_can_move = !player->cannot_move;
11231 if (!player->active || (!dx && !dy))
11232 return MP_NO_ACTION;
11234 player->MovDir = (dx < 0 ? MV_LEFT :
11235 dx > 0 ? MV_RIGHT :
11237 dy > 0 ? MV_DOWN : MV_NONE);
11239 if (!IN_LEV_FIELD(new_jx, new_jy))
11240 return MP_NO_ACTION;
11242 if (!player_can_move)
11244 if (player->MovPos == 0)
11246 player->is_moving = FALSE;
11247 player->is_digging = FALSE;
11248 player->is_collecting = FALSE;
11249 player->is_snapping = FALSE;
11250 player->is_pushing = FALSE;
11255 if (!options.network && game.centered_player_nr == -1 &&
11256 !AllPlayersInSight(player, new_jx, new_jy))
11257 return MP_NO_ACTION;
11259 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11260 return MP_NO_ACTION;
11263 #if !USE_FIXED_DONT_RUN_INTO
11264 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11266 /* (moved to DigField()) */
11267 if (player_can_move && DONT_RUN_INTO(element))
11269 if (element == EL_ACID && dx == 0 && dy == 1)
11271 SplashAcid(new_jx, new_jy);
11272 Feld[jx][jy] = EL_PLAYER_1;
11273 InitMovingField(jx, jy, MV_DOWN);
11274 Store[jx][jy] = EL_ACID;
11275 ContinueMoving(jx, jy);
11276 BuryPlayer(player);
11279 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11285 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11286 if (can_move != MP_MOVING)
11289 /* check if DigField() has caused relocation of the player */
11290 if (player->jx != jx || player->jy != jy)
11291 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11293 StorePlayer[jx][jy] = 0;
11294 player->last_jx = jx;
11295 player->last_jy = jy;
11296 player->jx = new_jx;
11297 player->jy = new_jy;
11298 StorePlayer[new_jx][new_jy] = player->element_nr;
11300 if (player->move_delay_value_next != -1)
11302 player->move_delay_value = player->move_delay_value_next;
11303 player->move_delay_value_next = -1;
11307 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11309 player->step_counter++;
11311 PlayerVisit[jx][jy] = FrameCounter;
11313 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11314 player->is_moving = TRUE;
11318 /* should better be called in MovePlayer(), but this breaks some tapes */
11319 ScrollPlayer(player, SCROLL_INIT);
11325 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11327 int jx = player->jx, jy = player->jy;
11328 int old_jx = jx, old_jy = jy;
11329 int moved = MP_NO_ACTION;
11331 if (!player->active)
11336 if (player->MovPos == 0)
11338 player->is_moving = FALSE;
11339 player->is_digging = FALSE;
11340 player->is_collecting = FALSE;
11341 player->is_snapping = FALSE;
11342 player->is_pushing = FALSE;
11348 if (player->move_delay > 0)
11351 player->move_delay = -1; /* set to "uninitialized" value */
11353 /* store if player is automatically moved to next field */
11354 player->is_auto_moving = (player->programmed_action != MV_NONE);
11356 /* remove the last programmed player action */
11357 player->programmed_action = 0;
11359 if (player->MovPos)
11361 /* should only happen if pre-1.2 tape recordings are played */
11362 /* this is only for backward compatibility */
11364 int original_move_delay_value = player->move_delay_value;
11367 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11371 /* scroll remaining steps with finest movement resolution */
11372 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11374 while (player->MovPos)
11376 ScrollPlayer(player, SCROLL_GO_ON);
11377 ScrollScreen(NULL, SCROLL_GO_ON);
11379 AdvanceFrameAndPlayerCounters(player->index_nr);
11385 player->move_delay_value = original_move_delay_value;
11388 player->is_active = FALSE;
11390 if (player->last_move_dir & MV_HORIZONTAL)
11392 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11393 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11397 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11398 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11401 #if USE_FIXED_BORDER_RUNNING_GFX
11402 if (!moved && !player->is_active)
11404 player->is_moving = FALSE;
11405 player->is_digging = FALSE;
11406 player->is_collecting = FALSE;
11407 player->is_snapping = FALSE;
11408 player->is_pushing = FALSE;
11416 if (moved & MP_MOVING && !ScreenMovPos &&
11417 (player->index_nr == game.centered_player_nr ||
11418 game.centered_player_nr == -1))
11420 if (moved & MP_MOVING && !ScreenMovPos &&
11421 (player == local_player || !options.network))
11424 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11425 int offset = (setup.scroll_delay ? 3 : 0);
11427 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11429 /* actual player has left the screen -- scroll in that direction */
11430 if (jx != old_jx) /* player has moved horizontally */
11431 scroll_x += (jx - old_jx);
11432 else /* player has moved vertically */
11433 scroll_y += (jy - old_jy);
11437 if (jx != old_jx) /* player has moved horizontally */
11439 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11440 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11441 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11443 /* don't scroll over playfield boundaries */
11444 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11445 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11447 /* don't scroll more than one field at a time */
11448 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11450 /* don't scroll against the player's moving direction */
11451 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11452 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11453 scroll_x = old_scroll_x;
11455 else /* player has moved vertically */
11457 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11458 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11459 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11461 /* don't scroll over playfield boundaries */
11462 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11463 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11465 /* don't scroll more than one field at a time */
11466 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11468 /* don't scroll against the player's moving direction */
11469 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11470 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11471 scroll_y = old_scroll_y;
11475 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11478 if (!options.network && game.centered_player_nr == -1 &&
11479 !AllPlayersInVisibleScreen())
11481 scroll_x = old_scroll_x;
11482 scroll_y = old_scroll_y;
11486 if (!options.network && !AllPlayersInVisibleScreen())
11488 scroll_x = old_scroll_x;
11489 scroll_y = old_scroll_y;
11494 ScrollScreen(player, SCROLL_INIT);
11495 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11500 player->StepFrame = 0;
11502 if (moved & MP_MOVING)
11504 if (old_jx != jx && old_jy == jy)
11505 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11506 else if (old_jx == jx && old_jy != jy)
11507 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11509 DrawLevelField(jx, jy); /* for "crumbled sand" */
11511 player->last_move_dir = player->MovDir;
11512 player->is_moving = TRUE;
11513 player->is_snapping = FALSE;
11514 player->is_switching = FALSE;
11515 player->is_dropping = FALSE;
11516 player->is_dropping_pressed = FALSE;
11517 player->drop_pressed_delay = 0;
11520 /* should better be called here than above, but this breaks some tapes */
11521 ScrollPlayer(player, SCROLL_INIT);
11526 CheckGravityMovementWhenNotMoving(player);
11528 player->is_moving = FALSE;
11530 /* at this point, the player is allowed to move, but cannot move right now
11531 (e.g. because of something blocking the way) -- ensure that the player
11532 is also allowed to move in the next frame (in old versions before 3.1.1,
11533 the player was forced to wait again for eight frames before next try) */
11535 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11536 player->move_delay = 0; /* allow direct movement in the next frame */
11539 if (player->move_delay == -1) /* not yet initialized by DigField() */
11540 player->move_delay = player->move_delay_value;
11542 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11544 TestIfPlayerTouchesBadThing(jx, jy);
11545 TestIfPlayerTouchesCustomElement(jx, jy);
11548 if (!player->active)
11549 RemovePlayer(player);
11554 void ScrollPlayer(struct PlayerInfo *player, int mode)
11556 int jx = player->jx, jy = player->jy;
11557 int last_jx = player->last_jx, last_jy = player->last_jy;
11558 int move_stepsize = TILEX / player->move_delay_value;
11560 #if USE_NEW_PLAYER_SPEED
11561 if (!player->active)
11564 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11567 if (!player->active || player->MovPos == 0)
11571 if (mode == SCROLL_INIT)
11573 player->actual_frame_counter = FrameCounter;
11574 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11576 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11577 Feld[last_jx][last_jy] == EL_EMPTY)
11579 int last_field_block_delay = 0; /* start with no blocking at all */
11580 int block_delay_adjustment = player->block_delay_adjustment;
11582 /* if player blocks last field, add delay for exactly one move */
11583 if (player->block_last_field)
11585 last_field_block_delay += player->move_delay_value;
11587 /* when blocking enabled, prevent moving up despite gravity */
11588 #if USE_PLAYER_GRAVITY
11589 if (player->gravity && player->MovDir == MV_UP)
11590 block_delay_adjustment = -1;
11592 if (game.gravity && player->MovDir == MV_UP)
11593 block_delay_adjustment = -1;
11597 /* add block delay adjustment (also possible when not blocking) */
11598 last_field_block_delay += block_delay_adjustment;
11600 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11601 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11604 #if USE_NEW_PLAYER_SPEED
11605 if (player->MovPos != 0) /* player has not yet reached destination */
11611 else if (!FrameReached(&player->actual_frame_counter, 1))
11614 #if USE_NEW_PLAYER_SPEED
11615 if (player->MovPos != 0)
11617 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11618 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11620 /* before DrawPlayer() to draw correct player graphic for this case */
11621 if (player->MovPos == 0)
11622 CheckGravityMovement(player);
11625 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11626 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11628 /* before DrawPlayer() to draw correct player graphic for this case */
11629 if (player->MovPos == 0)
11630 CheckGravityMovement(player);
11633 if (player->MovPos == 0) /* player reached destination field */
11635 if (player->move_delay_reset_counter > 0)
11637 player->move_delay_reset_counter--;
11639 if (player->move_delay_reset_counter == 0)
11641 /* continue with normal speed after quickly moving through gate */
11642 HALVE_PLAYER_SPEED(player);
11644 /* be able to make the next move without delay */
11645 player->move_delay = 0;
11649 player->last_jx = jx;
11650 player->last_jy = jy;
11652 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11653 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11654 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11655 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11656 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11657 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11659 DrawPlayer(player); /* needed here only to cleanup last field */
11660 RemovePlayer(player);
11662 if (local_player->friends_still_needed == 0 ||
11663 IS_SP_ELEMENT(Feld[jx][jy]))
11664 PlayerWins(player);
11667 /* this breaks one level: "machine", level 000 */
11669 int move_direction = player->MovDir;
11670 int enter_side = MV_DIR_OPPOSITE(move_direction);
11671 int leave_side = move_direction;
11672 int old_jx = last_jx;
11673 int old_jy = last_jy;
11674 int old_element = Feld[old_jx][old_jy];
11675 int new_element = Feld[jx][jy];
11677 if (IS_CUSTOM_ELEMENT(old_element))
11678 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11680 player->index_bit, leave_side);
11682 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11683 CE_PLAYER_LEAVES_X,
11684 player->index_bit, leave_side);
11686 if (IS_CUSTOM_ELEMENT(new_element))
11687 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11688 player->index_bit, enter_side);
11690 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11691 CE_PLAYER_ENTERS_X,
11692 player->index_bit, enter_side);
11694 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11695 CE_MOVE_OF_X, move_direction);
11698 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11700 TestIfPlayerTouchesBadThing(jx, jy);
11701 TestIfPlayerTouchesCustomElement(jx, jy);
11703 /* needed because pushed element has not yet reached its destination,
11704 so it would trigger a change event at its previous field location */
11705 if (!player->is_pushing)
11706 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11708 if (!player->active)
11709 RemovePlayer(player);
11712 if (!local_player->LevelSolved && level.use_step_counter)
11722 if (TimeLeft <= 10 && setup.time_limit)
11723 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11725 DrawGameValue_Time(TimeLeft);
11727 if (!TimeLeft && setup.time_limit)
11728 for (i = 0; i < MAX_PLAYERS; i++)
11729 KillPlayer(&stored_player[i]);
11731 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11732 DrawGameValue_Time(TimePlayed);
11735 if (tape.single_step && tape.recording && !tape.pausing &&
11736 !player->programmed_action)
11737 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11741 void ScrollScreen(struct PlayerInfo *player, int mode)
11743 static unsigned long screen_frame_counter = 0;
11745 if (mode == SCROLL_INIT)
11747 /* set scrolling step size according to actual player's moving speed */
11748 ScrollStepSize = TILEX / player->move_delay_value;
11750 screen_frame_counter = FrameCounter;
11751 ScreenMovDir = player->MovDir;
11752 ScreenMovPos = player->MovPos;
11753 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11756 else if (!FrameReached(&screen_frame_counter, 1))
11761 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11762 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11763 redraw_mask |= REDRAW_FIELD;
11766 ScreenMovDir = MV_NONE;
11769 void TestIfPlayerTouchesCustomElement(int x, int y)
11771 static int xy[4][2] =
11778 static int trigger_sides[4][2] =
11780 /* center side border side */
11781 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11782 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11783 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11784 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11786 static int touch_dir[4] =
11788 MV_LEFT | MV_RIGHT,
11793 int center_element = Feld[x][y]; /* should always be non-moving! */
11796 for (i = 0; i < NUM_DIRECTIONS; i++)
11798 int xx = x + xy[i][0];
11799 int yy = y + xy[i][1];
11800 int center_side = trigger_sides[i][0];
11801 int border_side = trigger_sides[i][1];
11802 int border_element;
11804 if (!IN_LEV_FIELD(xx, yy))
11807 if (IS_PLAYER(x, y))
11809 struct PlayerInfo *player = PLAYERINFO(x, y);
11811 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11812 border_element = Feld[xx][yy]; /* may be moving! */
11813 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11814 border_element = Feld[xx][yy];
11815 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11816 border_element = MovingOrBlocked2Element(xx, yy);
11818 continue; /* center and border element do not touch */
11820 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11821 player->index_bit, border_side);
11822 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11823 CE_PLAYER_TOUCHES_X,
11824 player->index_bit, border_side);
11826 else if (IS_PLAYER(xx, yy))
11828 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11830 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11832 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11833 continue; /* center and border element do not touch */
11836 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11837 player->index_bit, center_side);
11838 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11839 CE_PLAYER_TOUCHES_X,
11840 player->index_bit, center_side);
11846 #if USE_ELEMENT_TOUCHING_BUGFIX
11848 void TestIfElementTouchesCustomElement(int x, int y)
11850 static int xy[4][2] =
11857 static int trigger_sides[4][2] =
11859 /* center side border side */
11860 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11861 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11862 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11863 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11865 static int touch_dir[4] =
11867 MV_LEFT | MV_RIGHT,
11872 boolean change_center_element = FALSE;
11873 int center_element = Feld[x][y]; /* should always be non-moving! */
11874 int border_element_old[NUM_DIRECTIONS];
11877 for (i = 0; i < NUM_DIRECTIONS; i++)
11879 int xx = x + xy[i][0];
11880 int yy = y + xy[i][1];
11881 int border_element;
11883 border_element_old[i] = -1;
11885 if (!IN_LEV_FIELD(xx, yy))
11888 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11889 border_element = Feld[xx][yy]; /* may be moving! */
11890 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11891 border_element = Feld[xx][yy];
11892 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11893 border_element = MovingOrBlocked2Element(xx, yy);
11895 continue; /* center and border element do not touch */
11897 border_element_old[i] = border_element;
11900 for (i = 0; i < NUM_DIRECTIONS; i++)
11902 int xx = x + xy[i][0];
11903 int yy = y + xy[i][1];
11904 int center_side = trigger_sides[i][0];
11905 int border_element = border_element_old[i];
11907 if (border_element == -1)
11910 /* check for change of border element */
11911 CheckElementChangeBySide(xx, yy, border_element, center_element,
11912 CE_TOUCHING_X, center_side);
11915 for (i = 0; i < NUM_DIRECTIONS; i++)
11917 int border_side = trigger_sides[i][1];
11918 int border_element = border_element_old[i];
11920 if (border_element == -1)
11923 /* check for change of center element (but change it only once) */
11924 if (!change_center_element)
11925 change_center_element =
11926 CheckElementChangeBySide(x, y, center_element, border_element,
11927 CE_TOUCHING_X, border_side);
11933 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11935 static int xy[4][2] =
11942 static int trigger_sides[4][2] =
11944 /* center side border side */
11945 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11946 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11947 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11948 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11950 static int touch_dir[4] =
11952 MV_LEFT | MV_RIGHT,
11957 boolean change_center_element = FALSE;
11958 int center_element = Feld[x][y]; /* should always be non-moving! */
11961 for (i = 0; i < NUM_DIRECTIONS; i++)
11963 int xx = x + xy[i][0];
11964 int yy = y + xy[i][1];
11965 int center_side = trigger_sides[i][0];
11966 int border_side = trigger_sides[i][1];
11967 int border_element;
11969 if (!IN_LEV_FIELD(xx, yy))
11972 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11973 border_element = Feld[xx][yy]; /* may be moving! */
11974 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11975 border_element = Feld[xx][yy];
11976 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11977 border_element = MovingOrBlocked2Element(xx, yy);
11979 continue; /* center and border element do not touch */
11981 /* check for change of center element (but change it only once) */
11982 if (!change_center_element)
11983 change_center_element =
11984 CheckElementChangeBySide(x, y, center_element, border_element,
11985 CE_TOUCHING_X, border_side);
11987 /* check for change of border element */
11988 CheckElementChangeBySide(xx, yy, border_element, center_element,
11989 CE_TOUCHING_X, center_side);
11995 void TestIfElementHitsCustomElement(int x, int y, int direction)
11997 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11998 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11999 int hitx = x + dx, hity = y + dy;
12000 int hitting_element = Feld[x][y];
12001 int touched_element;
12003 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12006 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12007 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12009 if (IN_LEV_FIELD(hitx, hity))
12011 int opposite_direction = MV_DIR_OPPOSITE(direction);
12012 int hitting_side = direction;
12013 int touched_side = opposite_direction;
12014 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12015 MovDir[hitx][hity] != direction ||
12016 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12022 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12023 CE_HITTING_X, touched_side);
12025 CheckElementChangeBySide(hitx, hity, touched_element,
12026 hitting_element, CE_HIT_BY_X, hitting_side);
12028 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12029 CE_HIT_BY_SOMETHING, opposite_direction);
12033 /* "hitting something" is also true when hitting the playfield border */
12034 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12035 CE_HITTING_SOMETHING, direction);
12039 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12041 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12042 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12043 int hitx = x + dx, hity = y + dy;
12044 int hitting_element = Feld[x][y];
12045 int touched_element;
12047 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12048 !IS_FREE(hitx, hity) &&
12049 (!IS_MOVING(hitx, hity) ||
12050 MovDir[hitx][hity] != direction ||
12051 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12054 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12058 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12062 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12063 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12065 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12066 EP_CAN_SMASH_EVERYTHING, direction);
12068 if (IN_LEV_FIELD(hitx, hity))
12070 int opposite_direction = MV_DIR_OPPOSITE(direction);
12071 int hitting_side = direction;
12072 int touched_side = opposite_direction;
12074 int touched_element = MovingOrBlocked2Element(hitx, hity);
12077 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12078 MovDir[hitx][hity] != direction ||
12079 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12088 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12089 CE_SMASHED_BY_SOMETHING, opposite_direction);
12091 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12092 CE_OTHER_IS_SMASHING, touched_side);
12094 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12095 CE_OTHER_GETS_SMASHED, hitting_side);
12101 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12103 int i, kill_x = -1, kill_y = -1;
12105 int bad_element = -1;
12106 static int test_xy[4][2] =
12113 static int test_dir[4] =
12121 for (i = 0; i < NUM_DIRECTIONS; i++)
12123 int test_x, test_y, test_move_dir, test_element;
12125 test_x = good_x + test_xy[i][0];
12126 test_y = good_y + test_xy[i][1];
12128 if (!IN_LEV_FIELD(test_x, test_y))
12132 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12134 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12136 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12137 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12139 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12140 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12144 bad_element = test_element;
12150 if (kill_x != -1 || kill_y != -1)
12152 if (IS_PLAYER(good_x, good_y))
12154 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12156 if (player->shield_deadly_time_left > 0 &&
12157 !IS_INDESTRUCTIBLE(bad_element))
12158 Bang(kill_x, kill_y);
12159 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12160 KillPlayer(player);
12163 Bang(good_x, good_y);
12167 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12169 int i, kill_x = -1, kill_y = -1;
12170 int bad_element = Feld[bad_x][bad_y];
12171 static int test_xy[4][2] =
12178 static int touch_dir[4] =
12180 MV_LEFT | MV_RIGHT,
12185 static int test_dir[4] =
12193 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12196 for (i = 0; i < NUM_DIRECTIONS; i++)
12198 int test_x, test_y, test_move_dir, test_element;
12200 test_x = bad_x + test_xy[i][0];
12201 test_y = bad_y + test_xy[i][1];
12202 if (!IN_LEV_FIELD(test_x, test_y))
12206 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12208 test_element = Feld[test_x][test_y];
12210 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12211 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12213 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12214 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12216 /* good thing is player or penguin that does not move away */
12217 if (IS_PLAYER(test_x, test_y))
12219 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12221 if (bad_element == EL_ROBOT && player->is_moving)
12222 continue; /* robot does not kill player if he is moving */
12224 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12226 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12227 continue; /* center and border element do not touch */
12234 else if (test_element == EL_PENGUIN)
12243 if (kill_x != -1 || kill_y != -1)
12245 if (IS_PLAYER(kill_x, kill_y))
12247 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12249 if (player->shield_deadly_time_left > 0 &&
12250 !IS_INDESTRUCTIBLE(bad_element))
12251 Bang(bad_x, bad_y);
12252 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12253 KillPlayer(player);
12256 Bang(kill_x, kill_y);
12260 void TestIfPlayerTouchesBadThing(int x, int y)
12262 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12265 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12267 TestIfGoodThingHitsBadThing(x, y, move_dir);
12270 void TestIfBadThingTouchesPlayer(int x, int y)
12272 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12275 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12277 TestIfBadThingHitsGoodThing(x, y, move_dir);
12280 void TestIfFriendTouchesBadThing(int x, int y)
12282 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12285 void TestIfBadThingTouchesFriend(int x, int y)
12287 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12290 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12292 int i, kill_x = bad_x, kill_y = bad_y;
12293 static int xy[4][2] =
12301 for (i = 0; i < NUM_DIRECTIONS; i++)
12305 x = bad_x + xy[i][0];
12306 y = bad_y + xy[i][1];
12307 if (!IN_LEV_FIELD(x, y))
12310 element = Feld[x][y];
12311 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12312 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12320 if (kill_x != bad_x || kill_y != bad_y)
12321 Bang(bad_x, bad_y);
12324 void KillPlayer(struct PlayerInfo *player)
12326 int jx = player->jx, jy = player->jy;
12328 if (!player->active)
12331 /* the following code was introduced to prevent an infinite loop when calling
12333 -> CheckTriggeredElementChangeExt()
12334 -> ExecuteCustomElementAction()
12336 -> (infinitely repeating the above sequence of function calls)
12337 which occurs when killing the player while having a CE with the setting
12338 "kill player X when explosion of <player X>"; the solution using a new
12339 field "player->killed" was chosen for backwards compatibility, although
12340 clever use of the fields "player->active" etc. would probably also work */
12342 if (player->killed)
12346 player->killed = TRUE;
12348 /* remove accessible field at the player's position */
12349 Feld[jx][jy] = EL_EMPTY;
12351 /* deactivate shield (else Bang()/Explode() would not work right) */
12352 player->shield_normal_time_left = 0;
12353 player->shield_deadly_time_left = 0;
12356 BuryPlayer(player);
12359 static void KillPlayerUnlessEnemyProtected(int x, int y)
12361 if (!PLAYER_ENEMY_PROTECTED(x, y))
12362 KillPlayer(PLAYERINFO(x, y));
12365 static void KillPlayerUnlessExplosionProtected(int x, int y)
12367 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12368 KillPlayer(PLAYERINFO(x, y));
12371 void BuryPlayer(struct PlayerInfo *player)
12373 int jx = player->jx, jy = player->jy;
12375 if (!player->active)
12378 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12379 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12381 player->GameOver = TRUE;
12382 RemovePlayer(player);
12385 void RemovePlayer(struct PlayerInfo *player)
12387 int jx = player->jx, jy = player->jy;
12388 int i, found = FALSE;
12390 player->present = FALSE;
12391 player->active = FALSE;
12393 if (!ExplodeField[jx][jy])
12394 StorePlayer[jx][jy] = 0;
12396 if (player->is_moving)
12397 DrawLevelField(player->last_jx, player->last_jy);
12399 for (i = 0; i < MAX_PLAYERS; i++)
12400 if (stored_player[i].active)
12404 AllPlayersGone = TRUE;
12410 #if USE_NEW_SNAP_DELAY
12411 static void setFieldForSnapping(int x, int y, int element, int direction)
12413 struct ElementInfo *ei = &element_info[element];
12414 int direction_bit = MV_DIR_TO_BIT(direction);
12415 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12416 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12417 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12419 Feld[x][y] = EL_ELEMENT_SNAPPING;
12420 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12422 ResetGfxAnimation(x, y);
12424 GfxElement[x][y] = element;
12425 GfxAction[x][y] = action;
12426 GfxDir[x][y] = direction;
12427 GfxFrame[x][y] = -1;
12432 =============================================================================
12433 checkDiagonalPushing()
12434 -----------------------------------------------------------------------------
12435 check if diagonal input device direction results in pushing of object
12436 (by checking if the alternative direction is walkable, diggable, ...)
12437 =============================================================================
12440 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12441 int x, int y, int real_dx, int real_dy)
12443 int jx, jy, dx, dy, xx, yy;
12445 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12448 /* diagonal direction: check alternative direction */
12453 xx = jx + (dx == 0 ? real_dx : 0);
12454 yy = jy + (dy == 0 ? real_dy : 0);
12456 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12460 =============================================================================
12462 -----------------------------------------------------------------------------
12463 x, y: field next to player (non-diagonal) to try to dig to
12464 real_dx, real_dy: direction as read from input device (can be diagonal)
12465 =============================================================================
12468 int DigField(struct PlayerInfo *player,
12469 int oldx, int oldy, int x, int y,
12470 int real_dx, int real_dy, int mode)
12472 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12473 boolean player_was_pushing = player->is_pushing;
12474 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12475 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12476 int jx = oldx, jy = oldy;
12477 int dx = x - jx, dy = y - jy;
12478 int nextx = x + dx, nexty = y + dy;
12479 int move_direction = (dx == -1 ? MV_LEFT :
12480 dx == +1 ? MV_RIGHT :
12482 dy == +1 ? MV_DOWN : MV_NONE);
12483 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12484 int dig_side = MV_DIR_OPPOSITE(move_direction);
12485 int old_element = Feld[jx][jy];
12486 #if USE_FIXED_DONT_RUN_INTO
12487 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12493 if (is_player) /* function can also be called by EL_PENGUIN */
12495 if (player->MovPos == 0)
12497 player->is_digging = FALSE;
12498 player->is_collecting = FALSE;
12501 if (player->MovPos == 0) /* last pushing move finished */
12502 player->is_pushing = FALSE;
12504 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12506 player->is_switching = FALSE;
12507 player->push_delay = -1;
12509 return MP_NO_ACTION;
12513 #if !USE_FIXED_DONT_RUN_INTO
12514 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12515 return MP_NO_ACTION;
12518 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12519 old_element = Back[jx][jy];
12521 /* in case of element dropped at player position, check background */
12522 else if (Back[jx][jy] != EL_EMPTY &&
12523 game.engine_version >= VERSION_IDENT(2,2,0,0))
12524 old_element = Back[jx][jy];
12526 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12527 return MP_NO_ACTION; /* field has no opening in this direction */
12529 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12530 return MP_NO_ACTION; /* field has no opening in this direction */
12532 #if USE_FIXED_DONT_RUN_INTO
12533 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12537 Feld[jx][jy] = player->artwork_element;
12538 InitMovingField(jx, jy, MV_DOWN);
12539 Store[jx][jy] = EL_ACID;
12540 ContinueMoving(jx, jy);
12541 BuryPlayer(player);
12543 return MP_DONT_RUN_INTO;
12547 #if USE_FIXED_DONT_RUN_INTO
12548 if (player_can_move && DONT_RUN_INTO(element))
12550 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12552 return MP_DONT_RUN_INTO;
12556 #if USE_FIXED_DONT_RUN_INTO
12557 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12558 return MP_NO_ACTION;
12561 #if !USE_FIXED_DONT_RUN_INTO
12562 element = Feld[x][y];
12565 collect_count = element_info[element].collect_count_initial;
12567 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12568 return MP_NO_ACTION;
12570 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12571 player_can_move = player_can_move_or_snap;
12573 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12574 game.engine_version >= VERSION_IDENT(2,2,0,0))
12576 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12577 player->index_bit, dig_side);
12578 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12579 player->index_bit, dig_side);
12581 if (element == EL_DC_LANDMINE)
12584 if (Feld[x][y] != element) /* field changed by snapping */
12587 return MP_NO_ACTION;
12590 #if USE_PLAYER_GRAVITY
12591 if (player->gravity && is_player && !player->is_auto_moving &&
12592 canFallDown(player) && move_direction != MV_DOWN &&
12593 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12594 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12596 if (game.gravity && is_player && !player->is_auto_moving &&
12597 canFallDown(player) && move_direction != MV_DOWN &&
12598 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12599 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12602 if (player_can_move &&
12603 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12605 int sound_element = SND_ELEMENT(element);
12606 int sound_action = ACTION_WALKING;
12608 if (IS_RND_GATE(element))
12610 if (!player->key[RND_GATE_NR(element)])
12611 return MP_NO_ACTION;
12613 else if (IS_RND_GATE_GRAY(element))
12615 if (!player->key[RND_GATE_GRAY_NR(element)])
12616 return MP_NO_ACTION;
12618 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12620 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12621 return MP_NO_ACTION;
12623 else if (element == EL_EXIT_OPEN ||
12624 element == EL_EM_EXIT_OPEN ||
12625 element == EL_STEEL_EXIT_OPEN ||
12626 element == EL_EM_STEEL_EXIT_OPEN ||
12627 element == EL_SP_EXIT_OPEN ||
12628 element == EL_SP_EXIT_OPENING)
12630 sound_action = ACTION_PASSING; /* player is passing exit */
12632 else if (element == EL_EMPTY)
12634 sound_action = ACTION_MOVING; /* nothing to walk on */
12637 /* play sound from background or player, whatever is available */
12638 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12639 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12641 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12643 else if (player_can_move &&
12644 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12646 if (!ACCESS_FROM(element, opposite_direction))
12647 return MP_NO_ACTION; /* field not accessible from this direction */
12649 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12650 return MP_NO_ACTION;
12652 if (IS_EM_GATE(element))
12654 if (!player->key[EM_GATE_NR(element)])
12655 return MP_NO_ACTION;
12657 else if (IS_EM_GATE_GRAY(element))
12659 if (!player->key[EM_GATE_GRAY_NR(element)])
12660 return MP_NO_ACTION;
12662 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12664 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12665 return MP_NO_ACTION;
12667 else if (IS_EMC_GATE(element))
12669 if (!player->key[EMC_GATE_NR(element)])
12670 return MP_NO_ACTION;
12672 else if (IS_EMC_GATE_GRAY(element))
12674 if (!player->key[EMC_GATE_GRAY_NR(element)])
12675 return MP_NO_ACTION;
12677 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12679 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12680 return MP_NO_ACTION;
12682 else if (element == EL_DC_GATE_WHITE ||
12683 element == EL_DC_GATE_WHITE_GRAY ||
12684 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12686 if (player->num_white_keys == 0)
12687 return MP_NO_ACTION;
12689 player->num_white_keys--;
12691 else if (IS_SP_PORT(element))
12693 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12694 element == EL_SP_GRAVITY_PORT_RIGHT ||
12695 element == EL_SP_GRAVITY_PORT_UP ||
12696 element == EL_SP_GRAVITY_PORT_DOWN)
12697 #if USE_PLAYER_GRAVITY
12698 player->gravity = !player->gravity;
12700 game.gravity = !game.gravity;
12702 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12703 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12704 element == EL_SP_GRAVITY_ON_PORT_UP ||
12705 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12706 #if USE_PLAYER_GRAVITY
12707 player->gravity = TRUE;
12709 game.gravity = TRUE;
12711 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12712 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12713 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12714 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12715 #if USE_PLAYER_GRAVITY
12716 player->gravity = FALSE;
12718 game.gravity = FALSE;
12722 /* automatically move to the next field with double speed */
12723 player->programmed_action = move_direction;
12725 if (player->move_delay_reset_counter == 0)
12727 player->move_delay_reset_counter = 2; /* two double speed steps */
12729 DOUBLE_PLAYER_SPEED(player);
12732 PlayLevelSoundAction(x, y, ACTION_PASSING);
12734 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12738 if (mode != DF_SNAP)
12740 GfxElement[x][y] = GFX_ELEMENT(element);
12741 player->is_digging = TRUE;
12744 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12746 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12747 player->index_bit, dig_side);
12749 if (mode == DF_SNAP)
12751 #if USE_NEW_SNAP_DELAY
12752 if (level.block_snap_field)
12753 setFieldForSnapping(x, y, element, move_direction);
12755 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12757 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12760 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12761 player->index_bit, dig_side);
12764 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12768 if (is_player && mode != DF_SNAP)
12770 GfxElement[x][y] = element;
12771 player->is_collecting = TRUE;
12774 if (element == EL_SPEED_PILL)
12776 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12778 else if (element == EL_EXTRA_TIME && level.time > 0)
12780 TimeLeft += level.extra_time;
12781 DrawGameValue_Time(TimeLeft);
12783 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12785 player->shield_normal_time_left += level.shield_normal_time;
12786 if (element == EL_SHIELD_DEADLY)
12787 player->shield_deadly_time_left += level.shield_deadly_time;
12789 else if (element == EL_DYNAMITE ||
12790 element == EL_EM_DYNAMITE ||
12791 element == EL_SP_DISK_RED)
12793 if (player->inventory_size < MAX_INVENTORY_SIZE)
12794 player->inventory_element[player->inventory_size++] = element;
12796 DrawGameDoorValues();
12798 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12800 player->dynabomb_count++;
12801 player->dynabombs_left++;
12803 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12805 player->dynabomb_size++;
12807 else if (element == EL_DYNABOMB_INCREASE_POWER)
12809 player->dynabomb_xl = TRUE;
12811 else if (IS_KEY(element))
12813 player->key[KEY_NR(element)] = TRUE;
12815 DrawGameDoorValues();
12817 else if (element == EL_DC_KEY_WHITE)
12819 player->num_white_keys++;
12821 /* display white keys? */
12822 /* DrawGameDoorValues(); */
12824 else if (IS_ENVELOPE(element))
12826 player->show_envelope = element;
12828 else if (element == EL_EMC_LENSES)
12830 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12832 RedrawAllInvisibleElementsForLenses();
12834 else if (element == EL_EMC_MAGNIFIER)
12836 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12838 RedrawAllInvisibleElementsForMagnifier();
12840 else if (IS_DROPPABLE(element) ||
12841 IS_THROWABLE(element)) /* can be collected and dropped */
12845 if (collect_count == 0)
12846 player->inventory_infinite_element = element;
12848 for (i = 0; i < collect_count; i++)
12849 if (player->inventory_size < MAX_INVENTORY_SIZE)
12850 player->inventory_element[player->inventory_size++] = element;
12852 DrawGameDoorValues();
12854 else if (collect_count > 0)
12856 local_player->gems_still_needed -= collect_count;
12857 if (local_player->gems_still_needed < 0)
12858 local_player->gems_still_needed = 0;
12860 DrawGameValue_Emeralds(local_player->gems_still_needed);
12863 RaiseScoreElement(element);
12864 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12867 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12868 player->index_bit, dig_side);
12870 if (mode == DF_SNAP)
12872 #if USE_NEW_SNAP_DELAY
12873 if (level.block_snap_field)
12874 setFieldForSnapping(x, y, element, move_direction);
12876 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12878 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12881 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12882 player->index_bit, dig_side);
12885 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12887 if (mode == DF_SNAP && element != EL_BD_ROCK)
12888 return MP_NO_ACTION;
12890 if (CAN_FALL(element) && dy)
12891 return MP_NO_ACTION;
12893 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12894 !(element == EL_SPRING && level.use_spring_bug))
12895 return MP_NO_ACTION;
12897 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12898 ((move_direction & MV_VERTICAL &&
12899 ((element_info[element].move_pattern & MV_LEFT &&
12900 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12901 (element_info[element].move_pattern & MV_RIGHT &&
12902 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12903 (move_direction & MV_HORIZONTAL &&
12904 ((element_info[element].move_pattern & MV_UP &&
12905 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12906 (element_info[element].move_pattern & MV_DOWN &&
12907 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12908 return MP_NO_ACTION;
12910 /* do not push elements already moving away faster than player */
12911 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12912 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12913 return MP_NO_ACTION;
12915 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12917 if (player->push_delay_value == -1 || !player_was_pushing)
12918 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12920 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12922 if (player->push_delay_value == -1)
12923 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12925 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12927 if (!player->is_pushing)
12928 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12931 player->is_pushing = TRUE;
12932 player->is_active = TRUE;
12934 if (!(IN_LEV_FIELD(nextx, nexty) &&
12935 (IS_FREE(nextx, nexty) ||
12936 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12937 IS_SB_ELEMENT(element)))))
12938 return MP_NO_ACTION;
12940 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12941 return MP_NO_ACTION;
12943 if (player->push_delay == -1) /* new pushing; restart delay */
12944 player->push_delay = 0;
12946 if (player->push_delay < player->push_delay_value &&
12947 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12948 element != EL_SPRING && element != EL_BALLOON)
12950 /* make sure that there is no move delay before next try to push */
12951 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12952 player->move_delay = 0;
12954 return MP_NO_ACTION;
12957 if (IS_SB_ELEMENT(element))
12959 if (element == EL_SOKOBAN_FIELD_FULL)
12961 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12962 local_player->sokobanfields_still_needed++;
12965 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12967 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12968 local_player->sokobanfields_still_needed--;
12971 Feld[x][y] = EL_SOKOBAN_OBJECT;
12973 if (Back[x][y] == Back[nextx][nexty])
12974 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12975 else if (Back[x][y] != 0)
12976 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12979 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12982 if (local_player->sokobanfields_still_needed == 0 &&
12983 game.emulation == EMU_SOKOBAN)
12985 PlayerWins(player);
12987 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12991 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12993 InitMovingField(x, y, move_direction);
12994 GfxAction[x][y] = ACTION_PUSHING;
12996 if (mode == DF_SNAP)
12997 ContinueMoving(x, y);
12999 MovPos[x][y] = (dx != 0 ? dx : dy);
13001 Pushed[x][y] = TRUE;
13002 Pushed[nextx][nexty] = TRUE;
13004 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13005 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13007 player->push_delay_value = -1; /* get new value later */
13009 /* check for element change _after_ element has been pushed */
13010 if (game.use_change_when_pushing_bug)
13012 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13013 player->index_bit, dig_side);
13014 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13015 player->index_bit, dig_side);
13018 else if (IS_SWITCHABLE(element))
13020 if (PLAYER_SWITCHING(player, x, y))
13022 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13023 player->index_bit, dig_side);
13028 player->is_switching = TRUE;
13029 player->switch_x = x;
13030 player->switch_y = y;
13032 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13034 if (element == EL_ROBOT_WHEEL)
13036 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13040 DrawLevelField(x, y);
13042 else if (element == EL_SP_TERMINAL)
13046 SCAN_PLAYFIELD(xx, yy)
13048 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13050 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13051 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13054 else if (IS_BELT_SWITCH(element))
13056 ToggleBeltSwitch(x, y);
13058 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13059 element == EL_SWITCHGATE_SWITCH_DOWN ||
13060 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13061 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13063 ToggleSwitchgateSwitch(x, y);
13065 else if (element == EL_LIGHT_SWITCH ||
13066 element == EL_LIGHT_SWITCH_ACTIVE)
13068 ToggleLightSwitch(x, y);
13070 else if (element == EL_TIMEGATE_SWITCH ||
13071 element == EL_DC_TIMEGATE_SWITCH)
13073 ActivateTimegateSwitch(x, y);
13075 else if (element == EL_BALLOON_SWITCH_LEFT ||
13076 element == EL_BALLOON_SWITCH_RIGHT ||
13077 element == EL_BALLOON_SWITCH_UP ||
13078 element == EL_BALLOON_SWITCH_DOWN ||
13079 element == EL_BALLOON_SWITCH_NONE ||
13080 element == EL_BALLOON_SWITCH_ANY)
13082 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13083 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13084 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13085 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13086 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13089 else if (element == EL_LAMP)
13091 Feld[x][y] = EL_LAMP_ACTIVE;
13092 local_player->lights_still_needed--;
13094 ResetGfxAnimation(x, y);
13095 DrawLevelField(x, y);
13097 else if (element == EL_TIME_ORB_FULL)
13099 Feld[x][y] = EL_TIME_ORB_EMPTY;
13101 if (level.time > 0 || level.use_time_orb_bug)
13103 TimeLeft += level.time_orb_time;
13104 DrawGameValue_Time(TimeLeft);
13107 ResetGfxAnimation(x, y);
13108 DrawLevelField(x, y);
13110 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13111 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13115 game.ball_state = !game.ball_state;
13117 SCAN_PLAYFIELD(xx, yy)
13119 int e = Feld[xx][yy];
13121 if (game.ball_state)
13123 if (e == EL_EMC_MAGIC_BALL)
13124 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13125 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13126 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13130 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13131 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13132 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13133 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13138 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13139 player->index_bit, dig_side);
13141 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13142 player->index_bit, dig_side);
13144 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13145 player->index_bit, dig_side);
13151 if (!PLAYER_SWITCHING(player, x, y))
13153 player->is_switching = TRUE;
13154 player->switch_x = x;
13155 player->switch_y = y;
13157 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13158 player->index_bit, dig_side);
13159 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13160 player->index_bit, dig_side);
13162 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13163 player->index_bit, dig_side);
13164 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13165 player->index_bit, dig_side);
13168 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13169 player->index_bit, dig_side);
13170 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13171 player->index_bit, dig_side);
13173 return MP_NO_ACTION;
13176 player->push_delay = -1;
13178 if (is_player) /* function can also be called by EL_PENGUIN */
13180 if (Feld[x][y] != element) /* really digged/collected something */
13182 player->is_collecting = !player->is_digging;
13183 player->is_active = TRUE;
13190 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13192 int jx = player->jx, jy = player->jy;
13193 int x = jx + dx, y = jy + dy;
13194 int snap_direction = (dx == -1 ? MV_LEFT :
13195 dx == +1 ? MV_RIGHT :
13197 dy == +1 ? MV_DOWN : MV_NONE);
13198 boolean can_continue_snapping = (level.continuous_snapping &&
13199 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13201 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13204 if (!player->active || !IN_LEV_FIELD(x, y))
13212 if (player->MovPos == 0)
13213 player->is_pushing = FALSE;
13215 player->is_snapping = FALSE;
13217 if (player->MovPos == 0)
13219 player->is_moving = FALSE;
13220 player->is_digging = FALSE;
13221 player->is_collecting = FALSE;
13227 #if USE_NEW_CONTINUOUS_SNAPPING
13228 /* prevent snapping with already pressed snap key when not allowed */
13229 if (player->is_snapping && !can_continue_snapping)
13232 if (player->is_snapping)
13236 player->MovDir = snap_direction;
13238 if (player->MovPos == 0)
13240 player->is_moving = FALSE;
13241 player->is_digging = FALSE;
13242 player->is_collecting = FALSE;
13245 player->is_dropping = FALSE;
13246 player->is_dropping_pressed = FALSE;
13247 player->drop_pressed_delay = 0;
13249 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13252 player->is_snapping = TRUE;
13253 player->is_active = TRUE;
13255 if (player->MovPos == 0)
13257 player->is_moving = FALSE;
13258 player->is_digging = FALSE;
13259 player->is_collecting = FALSE;
13262 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13263 DrawLevelField(player->last_jx, player->last_jy);
13265 DrawLevelField(x, y);
13270 boolean DropElement(struct PlayerInfo *player)
13272 int old_element, new_element;
13273 int dropx = player->jx, dropy = player->jy;
13274 int drop_direction = player->MovDir;
13275 int drop_side = drop_direction;
13276 int drop_element = (player->inventory_size > 0 ?
13277 player->inventory_element[player->inventory_size - 1] :
13278 player->inventory_infinite_element != EL_UNDEFINED ?
13279 player->inventory_infinite_element :
13280 player->dynabombs_left > 0 ?
13281 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13284 player->is_dropping_pressed = TRUE;
13286 /* do not drop an element on top of another element; when holding drop key
13287 pressed without moving, dropped element must move away before the next
13288 element can be dropped (this is especially important if the next element
13289 is dynamite, which can be placed on background for historical reasons) */
13290 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13293 if (IS_THROWABLE(drop_element))
13295 dropx += GET_DX_FROM_DIR(drop_direction);
13296 dropy += GET_DY_FROM_DIR(drop_direction);
13298 if (!IN_LEV_FIELD(dropx, dropy))
13302 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13303 new_element = drop_element; /* default: no change when dropping */
13305 /* check if player is active, not moving and ready to drop */
13306 if (!player->active || player->MovPos || player->drop_delay > 0)
13309 /* check if player has anything that can be dropped */
13310 if (new_element == EL_UNDEFINED)
13313 /* check if drop key was pressed long enough for EM style dynamite */
13314 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13317 /* check if anything can be dropped at the current position */
13318 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13321 /* collected custom elements can only be dropped on empty fields */
13322 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13325 if (old_element != EL_EMPTY)
13326 Back[dropx][dropy] = old_element; /* store old element on this field */
13328 ResetGfxAnimation(dropx, dropy);
13329 ResetRandomAnimationValue(dropx, dropy);
13331 if (player->inventory_size > 0 ||
13332 player->inventory_infinite_element != EL_UNDEFINED)
13334 if (player->inventory_size > 0)
13336 player->inventory_size--;
13338 DrawGameDoorValues();
13340 if (new_element == EL_DYNAMITE)
13341 new_element = EL_DYNAMITE_ACTIVE;
13342 else if (new_element == EL_EM_DYNAMITE)
13343 new_element = EL_EM_DYNAMITE_ACTIVE;
13344 else if (new_element == EL_SP_DISK_RED)
13345 new_element = EL_SP_DISK_RED_ACTIVE;
13348 Feld[dropx][dropy] = new_element;
13350 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13351 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13352 el2img(Feld[dropx][dropy]), 0);
13354 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13356 /* needed if previous element just changed to "empty" in the last frame */
13357 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13359 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13360 player->index_bit, drop_side);
13361 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13363 player->index_bit, drop_side);
13365 TestIfElementTouchesCustomElement(dropx, dropy);
13367 else /* player is dropping a dyna bomb */
13369 player->dynabombs_left--;
13371 Feld[dropx][dropy] = new_element;
13373 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13374 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13375 el2img(Feld[dropx][dropy]), 0);
13377 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13380 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13381 InitField_WithBug1(dropx, dropy, FALSE);
13383 new_element = Feld[dropx][dropy]; /* element might have changed */
13385 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13386 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13388 int move_direction, nextx, nexty;
13390 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13391 MovDir[dropx][dropy] = drop_direction;
13393 move_direction = MovDir[dropx][dropy];
13394 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13395 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13397 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13399 #if USE_FIX_IMPACT_COLLISION
13400 /* do not cause impact style collision by dropping elements that can fall */
13401 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13403 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13407 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13408 player->is_dropping = TRUE;
13410 player->drop_pressed_delay = 0;
13411 player->is_dropping_pressed = FALSE;
13413 player->drop_x = dropx;
13414 player->drop_y = dropy;
13419 /* ------------------------------------------------------------------------- */
13420 /* game sound playing functions */
13421 /* ------------------------------------------------------------------------- */
13423 static int *loop_sound_frame = NULL;
13424 static int *loop_sound_volume = NULL;
13426 void InitPlayLevelSound()
13428 int num_sounds = getSoundListSize();
13430 checked_free(loop_sound_frame);
13431 checked_free(loop_sound_volume);
13433 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13434 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13437 static void PlayLevelSound(int x, int y, int nr)
13439 int sx = SCREENX(x), sy = SCREENY(y);
13440 int volume, stereo_position;
13441 int max_distance = 8;
13442 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13444 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13445 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13448 if (!IN_LEV_FIELD(x, y) ||
13449 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13450 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13453 volume = SOUND_MAX_VOLUME;
13455 if (!IN_SCR_FIELD(sx, sy))
13457 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13458 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13460 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13463 stereo_position = (SOUND_MAX_LEFT +
13464 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13465 (SCR_FIELDX + 2 * max_distance));
13467 if (IS_LOOP_SOUND(nr))
13469 /* This assures that quieter loop sounds do not overwrite louder ones,
13470 while restarting sound volume comparison with each new game frame. */
13472 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13475 loop_sound_volume[nr] = volume;
13476 loop_sound_frame[nr] = FrameCounter;
13479 PlaySoundExt(nr, volume, stereo_position, type);
13482 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13484 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13485 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13486 y < LEVELY(BY1) ? LEVELY(BY1) :
13487 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13491 static void PlayLevelSoundAction(int x, int y, int action)
13493 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13496 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13498 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13500 if (sound_effect != SND_UNDEFINED)
13501 PlayLevelSound(x, y, sound_effect);
13504 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13507 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13509 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13510 PlayLevelSound(x, y, sound_effect);
13513 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13515 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13517 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13518 PlayLevelSound(x, y, sound_effect);
13521 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13523 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13525 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13526 StopSound(sound_effect);
13529 static void PlayLevelMusic()
13531 if (levelset.music[level_nr] != MUS_UNDEFINED)
13532 PlayMusic(levelset.music[level_nr]); /* from config file */
13534 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13537 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13539 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13540 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13541 int x = xx - 1 - offset;
13542 int y = yy - 1 - offset;
13547 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13551 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13555 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13559 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13563 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13567 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13571 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13574 case SAMPLE_android_clone:
13575 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13578 case SAMPLE_android_move:
13579 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13582 case SAMPLE_spring:
13583 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13587 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13591 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13594 case SAMPLE_eater_eat:
13595 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13599 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13602 case SAMPLE_collect:
13603 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13606 case SAMPLE_diamond:
13607 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13610 case SAMPLE_squash:
13611 /* !!! CHECK THIS !!! */
13613 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13615 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13619 case SAMPLE_wonderfall:
13620 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13624 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13628 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13632 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13636 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13640 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13644 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13647 case SAMPLE_wonder:
13648 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13652 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13655 case SAMPLE_exit_open:
13656 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13659 case SAMPLE_exit_leave:
13660 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13663 case SAMPLE_dynamite:
13664 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13668 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13672 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13676 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13680 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13684 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13688 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13692 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13698 void ChangeTime(int value)
13700 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13704 /* EMC game engine uses value from time counter of RND game engine */
13705 level.native_em_level->lev->time = *time;
13707 DrawGameValue_Time(*time);
13710 void RaiseScore(int value)
13712 /* EMC game engine and RND game engine have separate score counters */
13713 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13714 &level.native_em_level->lev->score : &local_player->score);
13718 DrawGameValue_Score(*score);
13722 void RaiseScore(int value)
13724 local_player->score += value;
13726 DrawGameValue_Score(local_player->score);
13729 void RaiseScoreElement(int element)
13734 case EL_BD_DIAMOND:
13735 case EL_EMERALD_YELLOW:
13736 case EL_EMERALD_RED:
13737 case EL_EMERALD_PURPLE:
13738 case EL_SP_INFOTRON:
13739 RaiseScore(level.score[SC_EMERALD]);
13742 RaiseScore(level.score[SC_DIAMOND]);
13745 RaiseScore(level.score[SC_CRYSTAL]);
13748 RaiseScore(level.score[SC_PEARL]);
13751 case EL_BD_BUTTERFLY:
13752 case EL_SP_ELECTRON:
13753 RaiseScore(level.score[SC_BUG]);
13756 case EL_BD_FIREFLY:
13757 case EL_SP_SNIKSNAK:
13758 RaiseScore(level.score[SC_SPACESHIP]);
13761 case EL_DARK_YAMYAM:
13762 RaiseScore(level.score[SC_YAMYAM]);
13765 RaiseScore(level.score[SC_ROBOT]);
13768 RaiseScore(level.score[SC_PACMAN]);
13771 RaiseScore(level.score[SC_NUT]);
13774 case EL_EM_DYNAMITE:
13775 case EL_SP_DISK_RED:
13776 case EL_DYNABOMB_INCREASE_NUMBER:
13777 case EL_DYNABOMB_INCREASE_SIZE:
13778 case EL_DYNABOMB_INCREASE_POWER:
13779 RaiseScore(level.score[SC_DYNAMITE]);
13781 case EL_SHIELD_NORMAL:
13782 case EL_SHIELD_DEADLY:
13783 RaiseScore(level.score[SC_SHIELD]);
13785 case EL_EXTRA_TIME:
13786 RaiseScore(level.extra_time_score);
13800 case EL_DC_KEY_WHITE:
13801 RaiseScore(level.score[SC_KEY]);
13804 RaiseScore(element_info[element].collect_score);
13809 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13811 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13813 #if defined(NETWORK_AVALIABLE)
13814 if (options.network)
13815 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13821 game_status = GAME_MODE_MAIN;
13827 FadeOut(REDRAW_FIELD);
13829 game_status = GAME_MODE_MAIN;
13831 DrawAndFadeInMainMenu(REDRAW_FIELD);
13835 else /* continue playing the game */
13837 if (tape.playing && tape.deactivate_display)
13838 TapeDeactivateDisplayOff(TRUE);
13840 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13842 if (tape.playing && tape.deactivate_display)
13843 TapeDeactivateDisplayOn();
13847 void RequestQuitGame(boolean ask_if_really_quit)
13849 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13850 boolean skip_request = AllPlayersGone || quick_quit;
13852 RequestQuitGameExt(skip_request, quick_quit,
13853 "Do you really want to quit the game ?");
13857 /* ------------------------------------------------------------------------- */
13858 /* random generator functions */
13859 /* ------------------------------------------------------------------------- */
13861 unsigned int InitEngineRandom_RND(long seed)
13863 game.num_random_calls = 0;
13866 unsigned int rnd_seed = InitEngineRandom(seed);
13868 printf("::: START RND: %d\n", rnd_seed);
13873 return InitEngineRandom(seed);
13879 unsigned int RND(int max)
13883 game.num_random_calls++;
13885 return GetEngineRandom(max);
13892 /* ------------------------------------------------------------------------- */
13893 /* game engine snapshot handling functions */
13894 /* ------------------------------------------------------------------------- */
13896 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13898 struct EngineSnapshotInfo
13900 /* runtime values for custom element collect score */
13901 int collect_score[NUM_CUSTOM_ELEMENTS];
13903 /* runtime values for group element choice position */
13904 int choice_pos[NUM_GROUP_ELEMENTS];
13906 /* runtime values for belt position animations */
13907 int belt_graphic[4 * NUM_BELT_PARTS];
13908 int belt_anim_mode[4 * NUM_BELT_PARTS];
13911 struct EngineSnapshotNodeInfo
13918 static struct EngineSnapshotInfo engine_snapshot_rnd;
13919 static ListNode *engine_snapshot_list = NULL;
13920 static char *snapshot_level_identifier = NULL;
13921 static int snapshot_level_nr = -1;
13923 void FreeEngineSnapshot()
13925 while (engine_snapshot_list != NULL)
13926 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13929 setString(&snapshot_level_identifier, NULL);
13930 snapshot_level_nr = -1;
13933 static void SaveEngineSnapshotValues_RND()
13935 static int belt_base_active_element[4] =
13937 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13938 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13939 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13940 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13944 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13946 int element = EL_CUSTOM_START + i;
13948 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13951 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13953 int element = EL_GROUP_START + i;
13955 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13958 for (i = 0; i < 4; i++)
13960 for (j = 0; j < NUM_BELT_PARTS; j++)
13962 int element = belt_base_active_element[i] + j;
13963 int graphic = el2img(element);
13964 int anim_mode = graphic_info[graphic].anim_mode;
13966 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13967 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13972 static void LoadEngineSnapshotValues_RND()
13974 unsigned long num_random_calls = game.num_random_calls;
13977 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13979 int element = EL_CUSTOM_START + i;
13981 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13984 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13986 int element = EL_GROUP_START + i;
13988 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13991 for (i = 0; i < 4; i++)
13993 for (j = 0; j < NUM_BELT_PARTS; j++)
13995 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13996 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13998 graphic_info[graphic].anim_mode = anim_mode;
14002 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14004 InitRND(tape.random_seed);
14005 for (i = 0; i < num_random_calls; i++)
14009 if (game.num_random_calls != num_random_calls)
14011 Error(ERR_INFO, "number of random calls out of sync");
14012 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14013 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14014 Error(ERR_EXIT, "this should not happen -- please debug");
14018 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14020 struct EngineSnapshotNodeInfo *bi =
14021 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14023 bi->buffer_orig = buffer;
14024 bi->buffer_copy = checked_malloc(size);
14027 memcpy(bi->buffer_copy, buffer, size);
14029 addNodeToList(&engine_snapshot_list, NULL, bi);
14032 void SaveEngineSnapshot()
14034 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14036 if (level_editor_test_game) /* do not save snapshots from editor */
14039 /* copy some special values to a structure better suited for the snapshot */
14041 SaveEngineSnapshotValues_RND();
14042 SaveEngineSnapshotValues_EM();
14044 /* save values stored in special snapshot structure */
14046 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14047 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14049 /* save further RND engine values */
14051 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14052 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14053 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14055 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14056 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14057 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14058 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14060 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14061 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14062 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14063 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14064 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14066 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14067 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14068 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14070 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14072 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14074 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14075 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14077 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14078 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14079 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14080 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14081 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14082 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14083 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14084 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14085 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14086 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14087 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14088 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14089 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14090 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14091 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14092 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14093 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14094 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14096 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14097 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14099 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14100 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14101 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14103 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14104 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14106 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14107 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14108 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14109 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14110 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14112 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14113 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14115 /* save level identification information */
14117 setString(&snapshot_level_identifier, leveldir_current->identifier);
14118 snapshot_level_nr = level_nr;
14121 ListNode *node = engine_snapshot_list;
14124 while (node != NULL)
14126 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14131 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14135 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14137 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14140 void LoadEngineSnapshot()
14142 ListNode *node = engine_snapshot_list;
14144 if (engine_snapshot_list == NULL)
14147 while (node != NULL)
14149 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14154 /* restore special values from snapshot structure */
14156 LoadEngineSnapshotValues_RND();
14157 LoadEngineSnapshotValues_EM();
14160 boolean CheckEngineSnapshot()
14162 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14163 snapshot_level_nr == level_nr);
14167 /* ---------- new game button stuff ---------------------------------------- */
14169 /* graphic position values for game buttons */
14170 #define GAME_BUTTON_XSIZE 30
14171 #define GAME_BUTTON_YSIZE 30
14172 #define GAME_BUTTON_XPOS 5
14173 #define GAME_BUTTON_YPOS 215
14174 #define SOUND_BUTTON_XPOS 5
14175 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14177 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14178 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14179 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14180 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14181 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14182 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14189 } gamebutton_info[NUM_GAME_BUTTONS] =
14192 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14197 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14198 GAME_CTRL_ID_PAUSE,
14202 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14207 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14208 SOUND_CTRL_ID_MUSIC,
14209 "background music on/off"
14212 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14213 SOUND_CTRL_ID_LOOPS,
14214 "sound loops on/off"
14217 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14218 SOUND_CTRL_ID_SIMPLE,
14219 "normal sounds on/off"
14223 void CreateGameButtons()
14227 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14229 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14230 struct GadgetInfo *gi;
14233 unsigned long event_mask;
14234 int gd_xoffset, gd_yoffset;
14235 int gd_x1, gd_x2, gd_y1, gd_y2;
14238 gd_xoffset = gamebutton_info[i].x;
14239 gd_yoffset = gamebutton_info[i].y;
14240 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14241 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14243 if (id == GAME_CTRL_ID_STOP ||
14244 id == GAME_CTRL_ID_PAUSE ||
14245 id == GAME_CTRL_ID_PLAY)
14247 button_type = GD_TYPE_NORMAL_BUTTON;
14249 event_mask = GD_EVENT_RELEASED;
14250 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14251 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14255 button_type = GD_TYPE_CHECK_BUTTON;
14257 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14258 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14259 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14260 event_mask = GD_EVENT_PRESSED;
14261 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
14262 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14265 gi = CreateGadget(GDI_CUSTOM_ID, id,
14266 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14267 GDI_X, DX + gd_xoffset,
14268 GDI_Y, DY + gd_yoffset,
14269 GDI_WIDTH, GAME_BUTTON_XSIZE,
14270 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14271 GDI_TYPE, button_type,
14272 GDI_STATE, GD_BUTTON_UNPRESSED,
14273 GDI_CHECKED, checked,
14274 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14275 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14276 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14277 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14278 GDI_EVENT_MASK, event_mask,
14279 GDI_CALLBACK_ACTION, HandleGameButtons,
14283 Error(ERR_EXIT, "cannot create gadget");
14285 game_gadget[id] = gi;
14289 void FreeGameButtons()
14293 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14294 FreeGadget(game_gadget[i]);
14297 static void MapGameButtons()
14301 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14302 MapGadget(game_gadget[i]);
14305 void UnmapGameButtons()
14309 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14310 UnmapGadget(game_gadget[i]);
14313 static void HandleGameButtons(struct GadgetInfo *gi)
14315 int id = gi->custom_id;
14317 if (game_status != GAME_MODE_PLAYING)
14322 case GAME_CTRL_ID_STOP:
14326 RequestQuitGame(TRUE);
14329 case GAME_CTRL_ID_PAUSE:
14330 if (options.network)
14332 #if defined(NETWORK_AVALIABLE)
14334 SendToServer_ContinuePlaying();
14336 SendToServer_PausePlaying();
14340 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14343 case GAME_CTRL_ID_PLAY:
14346 #if defined(NETWORK_AVALIABLE)
14347 if (options.network)
14348 SendToServer_ContinuePlaying();
14352 tape.pausing = FALSE;
14353 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14358 case SOUND_CTRL_ID_MUSIC:
14359 if (setup.sound_music)
14361 setup.sound_music = FALSE;
14364 else if (audio.music_available)
14366 setup.sound = setup.sound_music = TRUE;
14368 SetAudioMode(setup.sound);
14374 case SOUND_CTRL_ID_LOOPS:
14375 if (setup.sound_loops)
14376 setup.sound_loops = FALSE;
14377 else if (audio.loops_available)
14379 setup.sound = setup.sound_loops = TRUE;
14380 SetAudioMode(setup.sound);
14384 case SOUND_CTRL_ID_SIMPLE:
14385 if (setup.sound_simple)
14386 setup.sound_simple = FALSE;
14387 else if (audio.sound_available)
14389 setup.sound = setup.sound_simple = TRUE;
14390 SetAudioMode(setup.sound);