1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
148 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
149 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
151 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
153 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
156 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
159 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
160 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
162 /* game button identifiers */
163 #define GAME_CTRL_ID_STOP 0
164 #define GAME_CTRL_ID_PAUSE 1
165 #define GAME_CTRL_ID_PLAY 2
166 #define SOUND_CTRL_ID_MUSIC 3
167 #define SOUND_CTRL_ID_LOOPS 4
168 #define SOUND_CTRL_ID_SIMPLE 5
170 #define NUM_GAME_BUTTONS 6
173 /* forward declaration for internal use */
175 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
176 static boolean MovePlayer(struct PlayerInfo *, int, int);
177 static void ScrollPlayer(struct PlayerInfo *, int);
178 static void ScrollScreen(struct PlayerInfo *, int);
180 static void InitBeltMovement(void);
181 static void CloseAllOpenTimegates(void);
182 static void CheckGravityMovement(struct PlayerInfo *);
183 static void KillHeroUnlessProtected(int, int);
185 static void TestIfPlayerTouchesCustomElement(int, int);
186 static void TestIfElementTouchesCustomElement(int, int);
187 static void TestIfElementHitsCustomElement(int, int, int);
189 static void ChangeElement(int, int, int);
190 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
191 static boolean CheckTriggeredElementChange(int, int, int, int);
192 static boolean CheckElementSideChange(int, int, int, int, int, int);
193 static boolean CheckElementChange(int, int, int, int);
195 static void PlayLevelSound(int, int, int);
196 static void PlayLevelSoundNearest(int, int, int);
197 static void PlayLevelSoundAction(int, int, int);
198 static void PlayLevelSoundElementAction(int, int, int, int);
199 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
200 static void PlayLevelSoundActionIfLoop(int, int, int);
201 static void StopLevelSoundActionIfLoop(int, int, int);
202 static void PlayLevelMusic();
204 static void MapGameButtons();
205 static void HandleGameButtons(struct GadgetInfo *);
207 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
210 /* ------------------------------------------------------------------------- */
211 /* definition of elements that automatically change to other elements after */
212 /* a specified time, eventually calling a function when changing */
213 /* ------------------------------------------------------------------------- */
215 /* forward declaration for changer functions */
216 static void InitBuggyBase(int x, int y);
217 static void WarnBuggyBase(int x, int y);
219 static void InitTrap(int x, int y);
220 static void ActivateTrap(int x, int y);
221 static void ChangeActiveTrap(int x, int y);
223 static void InitRobotWheel(int x, int y);
224 static void RunRobotWheel(int x, int y);
225 static void StopRobotWheel(int x, int y);
227 static void InitTimegateWheel(int x, int y);
228 static void RunTimegateWheel(int x, int y);
230 struct ChangingElementInfo
235 void (*pre_change_function)(int x, int y);
236 void (*change_function)(int x, int y);
237 void (*post_change_function)(int x, int y);
240 static struct ChangingElementInfo change_delay_list[] =
291 EL_SWITCHGATE_OPENING,
299 EL_SWITCHGATE_CLOSING,
300 EL_SWITCHGATE_CLOSED,
332 EL_ACID_SPLASH_RIGHT,
341 EL_SP_BUGGY_BASE_ACTIVATING,
348 EL_SP_BUGGY_BASE_ACTIVATING,
349 EL_SP_BUGGY_BASE_ACTIVE,
356 EL_SP_BUGGY_BASE_ACTIVE,
380 EL_ROBOT_WHEEL_ACTIVE,
388 EL_TIMEGATE_SWITCH_ACTIVE,
409 int push_delay_fixed, push_delay_random;
414 { EL_BALLOON, 0, 0 },
416 { EL_SOKOBAN_OBJECT, 2, 0 },
417 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
418 { EL_SATELLITE, 2, 0 },
419 { EL_SP_DISK_YELLOW, 2, 0 },
421 { EL_UNDEFINED, 0, 0 },
429 move_stepsize_list[] =
431 { EL_AMOEBA_DROP, 2 },
432 { EL_AMOEBA_DROPPING, 2 },
433 { EL_QUICKSAND_FILLING, 1 },
434 { EL_QUICKSAND_EMPTYING, 1 },
435 { EL_MAGIC_WALL_FILLING, 2 },
436 { EL_BD_MAGIC_WALL_FILLING, 2 },
437 { EL_MAGIC_WALL_EMPTYING, 2 },
438 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
448 collect_count_list[] =
451 { EL_BD_DIAMOND, 1 },
452 { EL_EMERALD_YELLOW, 1 },
453 { EL_EMERALD_RED, 1 },
454 { EL_EMERALD_PURPLE, 1 },
456 { EL_SP_INFOTRON, 1 },
463 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
465 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
466 CH_EVENT_BIT(CE_DELAY))
467 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
468 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
469 IS_JUST_CHANGING(x, y))
471 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
474 void GetPlayerConfig()
476 if (!audio.sound_available)
477 setup.sound_simple = FALSE;
479 if (!audio.loops_available)
480 setup.sound_loops = FALSE;
482 if (!audio.music_available)
483 setup.sound_music = FALSE;
485 if (!video.fullscreen_available)
486 setup.fullscreen = FALSE;
488 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
490 SetAudioMode(setup.sound);
494 static int getBeltNrFromBeltElement(int element)
496 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
497 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
498 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
501 static int getBeltNrFromBeltActiveElement(int element)
503 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
504 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
505 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
508 static int getBeltNrFromBeltSwitchElement(int element)
510 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
511 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
512 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
515 static int getBeltDirNrFromBeltSwitchElement(int element)
517 static int belt_base_element[4] =
519 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
520 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
521 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
522 EL_CONVEYOR_BELT_4_SWITCH_LEFT
525 int belt_nr = getBeltNrFromBeltSwitchElement(element);
526 int belt_dir_nr = element - belt_base_element[belt_nr];
528 return (belt_dir_nr % 3);
531 static int getBeltDirFromBeltSwitchElement(int element)
533 static int belt_move_dir[3] =
540 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
542 return belt_move_dir[belt_dir_nr];
545 static void InitPlayerField(int x, int y, int element, boolean init_game)
547 if (element == EL_SP_MURPHY)
551 if (stored_player[0].present)
553 Feld[x][y] = EL_SP_MURPHY_CLONE;
559 stored_player[0].use_murphy_graphic = TRUE;
562 Feld[x][y] = EL_PLAYER_1;
568 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
569 int jx = player->jx, jy = player->jy;
571 player->present = TRUE;
573 if (!options.network || player->connected)
575 player->active = TRUE;
577 /* remove potentially duplicate players */
578 if (StorePlayer[jx][jy] == Feld[x][y])
579 StorePlayer[jx][jy] = 0;
581 StorePlayer[x][y] = Feld[x][y];
585 printf("Player %d activated.\n", player->element_nr);
586 printf("[Local player is %d and currently %s.]\n",
587 local_player->element_nr,
588 local_player->active ? "active" : "not active");
592 Feld[x][y] = EL_EMPTY;
593 player->jx = player->last_jx = x;
594 player->jy = player->last_jy = y;
598 static void InitField(int x, int y, boolean init_game)
600 int element = Feld[x][y];
609 InitPlayerField(x, y, element, init_game);
613 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
614 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
615 else if (x > 0 && Feld[x-1][y] == EL_ACID)
616 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
617 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
618 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
619 else if (y > 0 && Feld[x][y-1] == EL_ACID)
620 Feld[x][y] = EL_ACID_POOL_BOTTOM;
621 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
622 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
630 case EL_SPACESHIP_RIGHT:
631 case EL_SPACESHIP_UP:
632 case EL_SPACESHIP_LEFT:
633 case EL_SPACESHIP_DOWN:
635 case EL_BD_BUTTERFLY_RIGHT:
636 case EL_BD_BUTTERFLY_UP:
637 case EL_BD_BUTTERFLY_LEFT:
638 case EL_BD_BUTTERFLY_DOWN:
639 case EL_BD_BUTTERFLY:
640 case EL_BD_FIREFLY_RIGHT:
641 case EL_BD_FIREFLY_UP:
642 case EL_BD_FIREFLY_LEFT:
643 case EL_BD_FIREFLY_DOWN:
645 case EL_PACMAN_RIGHT:
669 if (y == lev_fieldy - 1)
671 Feld[x][y] = EL_AMOEBA_GROWING;
672 Store[x][y] = EL_AMOEBA_WET;
676 case EL_DYNAMITE_ACTIVE:
677 case EL_SP_DISK_RED_ACTIVE:
678 case EL_DYNABOMB_PLAYER_1_ACTIVE:
679 case EL_DYNABOMB_PLAYER_2_ACTIVE:
680 case EL_DYNABOMB_PLAYER_3_ACTIVE:
681 case EL_DYNABOMB_PLAYER_4_ACTIVE:
686 local_player->lights_still_needed++;
689 case EL_SOKOBAN_FIELD_EMPTY:
690 local_player->sokobanfields_still_needed++;
694 local_player->friends_still_needed++;
699 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
704 Feld[x][y] = EL_EMPTY;
709 case EL_EM_KEY_1_FILE:
710 Feld[x][y] = EL_EM_KEY_1;
712 case EL_EM_KEY_2_FILE:
713 Feld[x][y] = EL_EM_KEY_2;
715 case EL_EM_KEY_3_FILE:
716 Feld[x][y] = EL_EM_KEY_3;
718 case EL_EM_KEY_4_FILE:
719 Feld[x][y] = EL_EM_KEY_4;
723 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
724 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
725 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
726 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
727 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
728 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
729 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
730 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
731 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
732 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
733 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
734 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
737 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
738 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
739 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
741 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
743 game.belt_dir[belt_nr] = belt_dir;
744 game.belt_dir_nr[belt_nr] = belt_dir_nr;
746 else /* more than one switch -- set it like the first switch */
748 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
753 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
755 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
758 case EL_LIGHT_SWITCH_ACTIVE:
760 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
764 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
770 void DrawGameDoorValues()
774 for (i = 0; i < MAX_PLAYERS; i++)
775 for (j = 0; j < 4; j++)
776 if (stored_player[i].key[j])
777 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
778 el2edimg(EL_KEY_1 + j));
780 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
781 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
782 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
783 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
784 DrawText(DX + XX_SCORE, DY + YY_SCORE,
785 int2str(local_player->score, 5), FONT_TEXT_2);
786 DrawText(DX + XX_TIME, DY + YY_TIME,
787 int2str(TimeLeft, 3), FONT_TEXT_2);
792 =============================================================================
794 -----------------------------------------------------------------------------
795 initialize game engine due to level / tape version number
796 =============================================================================
799 static void InitGameEngine()
803 /* set game engine from tape file when re-playing, else from level file */
804 game.engine_version = (tape.playing ? tape.engine_version :
807 /* dynamically adjust element properties according to game engine version */
808 InitElementPropertiesEngine(game.engine_version);
811 printf("level %d: level version == %06d\n", level_nr, level.game_version);
812 printf(" tape version == %06d [%s] [file: %06d]\n",
813 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
815 printf(" => game.engine_version == %06d\n", game.engine_version);
818 /* ---------- initialize player's initial move delay --------------------- */
820 /* dynamically adjust player properties according to game engine version */
821 game.initial_move_delay =
822 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
823 INITIAL_MOVE_DELAY_OFF);
825 /* dynamically adjust player properties according to level information */
826 game.initial_move_delay_value =
827 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
829 /* ---------- initialize player's initial push delay --------------------- */
831 /* dynamically adjust player properties according to game engine version */
832 game.initial_push_delay_value =
833 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
835 /* ---------- initialize changing elements ------------------------------- */
837 /* initialize changing elements information */
838 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
840 struct ElementInfo *ei = &element_info[i];
842 /* this pointer might have been changed in the level editor */
843 ei->change = &ei->change_page[0];
845 if (!IS_CUSTOM_ELEMENT(i))
847 ei->change->target_element = EL_EMPTY_SPACE;
848 ei->change->delay_fixed = 0;
849 ei->change->delay_random = 0;
850 ei->change->delay_frames = 1;
853 ei->change_events = CE_BITMASK_DEFAULT;
854 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
856 ei->event_page_nr[j] = 0;
857 ei->event_page[j] = &ei->change_page[0];
861 /* add changing elements from pre-defined list */
862 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
864 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
865 struct ElementInfo *ei = &element_info[ch_delay->element];
867 ei->change->target_element = ch_delay->target_element;
868 ei->change->delay_fixed = ch_delay->change_delay;
870 ei->change->pre_change_function = ch_delay->pre_change_function;
871 ei->change->change_function = ch_delay->change_function;
872 ei->change->post_change_function = ch_delay->post_change_function;
874 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
878 /* add change events from custom element configuration */
879 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
881 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
883 for (j = 0; j < ei->num_change_pages; j++)
885 if (!ei->change_page[j].can_change)
888 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
890 /* only add event page for the first page found with this event */
891 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
892 !(ei->change_events & CH_EVENT_BIT(k)))
894 ei->change_events |= CH_EVENT_BIT(k);
895 ei->event_page_nr[k] = j;
896 ei->event_page[k] = &ei->change_page[j];
904 /* add change events from custom element configuration */
905 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
907 int element = EL_CUSTOM_START + i;
909 /* only add custom elements that change after fixed/random frame delay */
910 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
911 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
915 /* ---------- initialize trigger events ---------------------------------- */
917 /* initialize trigger events information */
918 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
919 trigger_events[i] = EP_BITMASK_DEFAULT;
922 /* add trigger events from element change event properties */
923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
925 struct ElementInfo *ei = &element_info[i];
927 for (j = 0; j < ei->num_change_pages; j++)
929 if (!ei->change_page[j].can_change)
932 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
934 int trigger_element = ei->change_page[j].trigger_element;
936 trigger_events[trigger_element] |= ei->change_page[j].events;
941 /* add trigger events from element change event properties */
942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
943 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
944 trigger_events[element_info[i].change->trigger_element] |=
945 element_info[i].change->events;
948 /* ---------- initialize push delay -------------------------------------- */
950 /* initialize push delay values to default */
951 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
953 if (!IS_CUSTOM_ELEMENT(i))
955 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
956 element_info[i].push_delay_random = game.default_push_delay_random;
960 /* set push delay value for certain elements from pre-defined list */
961 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
963 int e = push_delay_list[i].element;
965 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
966 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
969 /* ---------- initialize move stepsize ----------------------------------- */
971 /* initialize move stepsize values to default */
972 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
973 if (!IS_CUSTOM_ELEMENT(i))
974 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
976 /* set move stepsize value for certain elements from pre-defined list */
977 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
979 int e = move_stepsize_list[i].element;
981 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
984 /* ---------- initialize gem count --------------------------------------- */
986 /* initialize gem count values for each element */
987 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
988 if (!IS_CUSTOM_ELEMENT(i))
989 element_info[i].collect_count = 0;
991 /* add gem count values for all elements from pre-defined list */
992 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
993 element_info[collect_count_list[i].element].collect_count =
994 collect_count_list[i].count;
999 =============================================================================
1001 -----------------------------------------------------------------------------
1002 initialize and start new game
1003 =============================================================================
1008 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1009 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1010 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1017 #if USE_NEW_AMOEBA_CODE
1018 printf("Using new amoeba code.\n");
1020 printf("Using old amoeba code.\n");
1025 /* don't play tapes over network */
1026 network_playing = (options.network && !tape.playing);
1028 for (i = 0; i < MAX_PLAYERS; i++)
1030 struct PlayerInfo *player = &stored_player[i];
1032 player->index_nr = i;
1033 player->element_nr = EL_PLAYER_1 + i;
1035 player->present = FALSE;
1036 player->active = FALSE;
1039 player->effective_action = 0;
1040 player->programmed_action = 0;
1043 player->gems_still_needed = level.gems_needed;
1044 player->sokobanfields_still_needed = 0;
1045 player->lights_still_needed = 0;
1046 player->friends_still_needed = 0;
1048 for (j = 0; j < 4; j++)
1049 player->key[j] = FALSE;
1051 player->dynabomb_count = 0;
1052 player->dynabomb_size = 1;
1053 player->dynabombs_left = 0;
1054 player->dynabomb_xl = FALSE;
1056 player->MovDir = MV_NO_MOVING;
1059 player->GfxDir = MV_NO_MOVING;
1060 player->GfxAction = ACTION_DEFAULT;
1062 player->StepFrame = 0;
1064 player->use_murphy_graphic = FALSE;
1066 player->actual_frame_counter = 0;
1068 player->step_counter = 0;
1070 player->last_move_dir = MV_NO_MOVING;
1072 player->is_waiting = FALSE;
1073 player->is_moving = FALSE;
1074 player->is_digging = FALSE;
1075 player->is_snapping = FALSE;
1076 player->is_collecting = FALSE;
1077 player->is_pushing = FALSE;
1078 player->is_switching = FALSE;
1080 player->is_bored = FALSE;
1081 player->is_sleeping = FALSE;
1083 player->frame_counter_bored = -1;
1084 player->frame_counter_sleeping = -1;
1086 player->anim_delay_counter = 0;
1087 player->post_delay_counter = 0;
1089 player->action_waiting = ACTION_DEFAULT;
1090 player->last_action_waiting = ACTION_DEFAULT;
1091 player->special_action_bored = ACTION_DEFAULT;
1092 player->special_action_sleeping = ACTION_DEFAULT;
1094 player->num_special_action_bored = 0;
1095 player->num_special_action_sleeping = 0;
1097 /* determine number of special actions for bored and sleeping animation */
1098 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1100 boolean found = FALSE;
1102 for (k = 0; k < NUM_DIRECTIONS; k++)
1103 if (el_act_dir2img(player->element_nr, j, k) !=
1104 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1108 player->num_special_action_bored++;
1112 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1114 boolean found = FALSE;
1116 for (k = 0; k < NUM_DIRECTIONS; k++)
1117 if (el_act_dir2img(player->element_nr, j, k) !=
1118 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1122 player->num_special_action_sleeping++;
1127 player->switch_x = -1;
1128 player->switch_y = -1;
1130 player->show_envelope = 0;
1132 player->move_delay = game.initial_move_delay;
1133 player->move_delay_value = game.initial_move_delay_value;
1135 player->push_delay = 0;
1136 player->push_delay_value = game.initial_push_delay_value;
1138 player->drop_delay = 0;
1140 player->last_jx = player->last_jy = 0;
1141 player->jx = player->jy = 0;
1143 player->shield_normal_time_left = 0;
1144 player->shield_deadly_time_left = 0;
1146 player->inventory_size = 0;
1148 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1149 SnapField(player, 0, 0);
1151 player->LevelSolved = FALSE;
1152 player->GameOver = FALSE;
1155 network_player_action_received = FALSE;
1157 #if defined(PLATFORM_UNIX)
1158 /* initial null action */
1159 if (network_playing)
1160 SendToServer_MovePlayer(MV_NO_MOVING);
1168 TimeLeft = level.time;
1170 ScreenMovDir = MV_NO_MOVING;
1174 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1176 AllPlayersGone = FALSE;
1178 game.yamyam_content_nr = 0;
1179 game.magic_wall_active = FALSE;
1180 game.magic_wall_time_left = 0;
1181 game.light_time_left = 0;
1182 game.timegate_time_left = 0;
1183 game.switchgate_pos = 0;
1184 game.balloon_dir = MV_NO_MOVING;
1185 game.gravity = level.initial_gravity;
1186 game.explosions_delayed = TRUE;
1188 game.envelope_active = FALSE;
1190 for (i = 0; i < 4; i++)
1192 game.belt_dir[i] = MV_NO_MOVING;
1193 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1196 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1197 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1199 for (x = 0; x < lev_fieldx; x++)
1201 for (y = 0; y < lev_fieldy; y++)
1203 Feld[x][y] = level.field[x][y];
1204 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1205 ChangeDelay[x][y] = 0;
1206 ChangePage[x][y] = -1;
1207 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1209 WasJustMoving[x][y] = 0;
1210 WasJustFalling[x][y] = 0;
1212 Pushed[x][y] = FALSE;
1214 Changed[x][y] = CE_BITMASK_DEFAULT;
1215 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1217 ExplodePhase[x][y] = 0;
1218 ExplodeField[x][y] = EX_NO_EXPLOSION;
1220 RunnerVisit[x][y] = 0;
1221 PlayerVisit[x][y] = 0;
1224 GfxRandom[x][y] = INIT_GFX_RANDOM();
1225 GfxElement[x][y] = EL_UNDEFINED;
1226 GfxAction[x][y] = ACTION_DEFAULT;
1227 GfxDir[x][y] = MV_NO_MOVING;
1231 for (y = 0; y < lev_fieldy; y++)
1233 for (x = 0; x < lev_fieldx; x++)
1235 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1237 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1239 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1242 InitField(x, y, TRUE);
1248 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1249 emulate_sb ? EMU_SOKOBAN :
1250 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1252 /* correct non-moving belts to start moving left */
1253 for (i = 0; i < 4; i++)
1254 if (game.belt_dir[i] == MV_NO_MOVING)
1255 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1257 /* check if any connected player was not found in playfield */
1258 for (i = 0; i < MAX_PLAYERS; i++)
1260 struct PlayerInfo *player = &stored_player[i];
1262 if (player->connected && !player->present)
1264 for (j = 0; j < MAX_PLAYERS; j++)
1266 struct PlayerInfo *some_player = &stored_player[j];
1267 int jx = some_player->jx, jy = some_player->jy;
1269 /* assign first free player found that is present in the playfield */
1270 if (some_player->present && !some_player->connected)
1272 player->present = TRUE;
1273 player->active = TRUE;
1274 some_player->present = FALSE;
1276 StorePlayer[jx][jy] = player->element_nr;
1277 player->jx = player->last_jx = jx;
1278 player->jy = player->last_jy = jy;
1288 /* when playing a tape, eliminate all players who do not participate */
1290 for (i = 0; i < MAX_PLAYERS; i++)
1292 if (stored_player[i].active && !tape.player_participates[i])
1294 struct PlayerInfo *player = &stored_player[i];
1295 int jx = player->jx, jy = player->jy;
1297 player->active = FALSE;
1298 StorePlayer[jx][jy] = 0;
1299 Feld[jx][jy] = EL_EMPTY;
1303 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1305 /* when in single player mode, eliminate all but the first active player */
1307 for (i = 0; i < MAX_PLAYERS; i++)
1309 if (stored_player[i].active)
1311 for (j = i + 1; j < MAX_PLAYERS; j++)
1313 if (stored_player[j].active)
1315 struct PlayerInfo *player = &stored_player[j];
1316 int jx = player->jx, jy = player->jy;
1318 player->active = FALSE;
1319 StorePlayer[jx][jy] = 0;
1320 Feld[jx][jy] = EL_EMPTY;
1327 /* when recording the game, store which players take part in the game */
1330 for (i = 0; i < MAX_PLAYERS; i++)
1331 if (stored_player[i].active)
1332 tape.player_participates[i] = TRUE;
1337 for (i = 0; i < MAX_PLAYERS; i++)
1339 struct PlayerInfo *player = &stored_player[i];
1341 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1346 if (local_player == player)
1347 printf("Player %d is local player.\n", i+1);
1351 if (BorderElement == EL_EMPTY)
1354 SBX_Right = lev_fieldx - SCR_FIELDX;
1356 SBY_Lower = lev_fieldy - SCR_FIELDY;
1361 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1363 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1366 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1367 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1369 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1370 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1372 /* if local player not found, look for custom element that might create
1373 the player (make some assumptions about the right custom element) */
1374 if (!local_player->present)
1376 int start_x = 0, start_y = 0;
1377 int found_rating = 0;
1378 int found_element = EL_UNDEFINED;
1380 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1382 int element = Feld[x][y];
1387 if (!IS_CUSTOM_ELEMENT(element))
1390 if (CAN_CHANGE(element))
1392 for (i = 0; i < element_info[element].num_change_pages; i++)
1394 content = element_info[element].change_page[i].target_element;
1395 is_player = ELEM_IS_PLAYER(content);
1397 if (is_player && (found_rating < 3 || element < found_element))
1403 found_element = element;
1408 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1410 content = element_info[element].content[xx][yy];
1411 is_player = ELEM_IS_PLAYER(content);
1413 if (is_player && (found_rating < 2 || element < found_element))
1415 start_x = x + xx - 1;
1416 start_y = y + yy - 1;
1419 found_element = element;
1422 if (!CAN_CHANGE(element))
1425 for (i = 0; i < element_info[element].num_change_pages; i++)
1427 content = element_info[element].change_page[i].content[xx][yy];
1428 is_player = ELEM_IS_PLAYER(content);
1430 if (is_player && (found_rating < 1 || element < found_element))
1432 start_x = x + xx - 1;
1433 start_y = y + yy - 1;
1436 found_element = element;
1442 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1443 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1446 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1447 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1453 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1454 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1455 local_player->jx - MIDPOSX);
1457 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1458 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1459 local_player->jy - MIDPOSY);
1461 scroll_x = SBX_Left;
1462 scroll_y = SBY_Upper;
1463 if (local_player->jx >= SBX_Left + MIDPOSX)
1464 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1465 local_player->jx - MIDPOSX :
1467 if (local_player->jy >= SBY_Upper + MIDPOSY)
1468 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1469 local_player->jy - MIDPOSY :
1474 CloseDoor(DOOR_CLOSE_1);
1479 /* after drawing the level, correct some elements */
1480 if (game.timegate_time_left == 0)
1481 CloseAllOpenTimegates();
1483 if (setup.soft_scrolling)
1484 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1486 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1489 /* copy default game door content to main double buffer */
1490 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1491 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1494 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1497 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1498 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1499 BlitBitmap(drawto, drawto,
1500 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1501 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1502 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1503 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1506 DrawGameDoorValues();
1510 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1511 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1512 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1516 /* copy actual game door content to door double buffer for OpenDoor() */
1517 BlitBitmap(drawto, bitmap_db_door,
1518 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1520 OpenDoor(DOOR_OPEN_ALL);
1522 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1524 if (setup.sound_music)
1527 KeyboardAutoRepeatOffUnlessAutoplay();
1531 for (i = 0; i < 4; i++)
1532 printf("Player %d %sactive.\n",
1533 i + 1, (stored_player[i].active ? "" : "not "));
1537 printf("::: starting game [%d]\n", FrameCounter);
1541 void InitMovDir(int x, int y)
1543 int i, element = Feld[x][y];
1544 static int xy[4][2] =
1551 static int direction[3][4] =
1553 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1554 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1555 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1564 Feld[x][y] = EL_BUG;
1565 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1568 case EL_SPACESHIP_RIGHT:
1569 case EL_SPACESHIP_UP:
1570 case EL_SPACESHIP_LEFT:
1571 case EL_SPACESHIP_DOWN:
1572 Feld[x][y] = EL_SPACESHIP;
1573 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1576 case EL_BD_BUTTERFLY_RIGHT:
1577 case EL_BD_BUTTERFLY_UP:
1578 case EL_BD_BUTTERFLY_LEFT:
1579 case EL_BD_BUTTERFLY_DOWN:
1580 Feld[x][y] = EL_BD_BUTTERFLY;
1581 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1584 case EL_BD_FIREFLY_RIGHT:
1585 case EL_BD_FIREFLY_UP:
1586 case EL_BD_FIREFLY_LEFT:
1587 case EL_BD_FIREFLY_DOWN:
1588 Feld[x][y] = EL_BD_FIREFLY;
1589 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1592 case EL_PACMAN_RIGHT:
1594 case EL_PACMAN_LEFT:
1595 case EL_PACMAN_DOWN:
1596 Feld[x][y] = EL_PACMAN;
1597 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1600 case EL_SP_SNIKSNAK:
1601 MovDir[x][y] = MV_UP;
1604 case EL_SP_ELECTRON:
1605 MovDir[x][y] = MV_LEFT;
1612 Feld[x][y] = EL_MOLE;
1613 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1617 if (IS_CUSTOM_ELEMENT(element))
1619 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1620 MovDir[x][y] = element_info[element].move_direction_initial;
1621 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1622 element_info[element].move_pattern == MV_TURNING_LEFT ||
1623 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1624 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1625 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1626 element_info[element].move_pattern == MV_TURNING_RANDOM)
1627 MovDir[x][y] = 1 << RND(4);
1628 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1629 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1630 else if (element_info[element].move_pattern == MV_VERTICAL)
1631 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1632 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1633 MovDir[x][y] = element_info[element].move_pattern;
1634 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1635 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1637 for (i = 0; i < 4; i++)
1639 int x1 = x + xy[i][0];
1640 int y1 = y + xy[i][1];
1642 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1644 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1645 MovDir[x][y] = direction[0][i];
1647 MovDir[x][y] = direction[1][i];
1656 MovDir[x][y] = 1 << RND(4);
1658 if (element != EL_BUG &&
1659 element != EL_SPACESHIP &&
1660 element != EL_BD_BUTTERFLY &&
1661 element != EL_BD_FIREFLY)
1664 for (i = 0; i < 4; i++)
1666 int x1 = x + xy[i][0];
1667 int y1 = y + xy[i][1];
1669 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1671 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1673 MovDir[x][y] = direction[0][i];
1676 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1677 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1679 MovDir[x][y] = direction[1][i];
1688 GfxDir[x][y] = MovDir[x][y];
1691 void InitAmoebaNr(int x, int y)
1694 int group_nr = AmoebeNachbarNr(x, y);
1698 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1700 if (AmoebaCnt[i] == 0)
1708 AmoebaNr[x][y] = group_nr;
1709 AmoebaCnt[group_nr]++;
1710 AmoebaCnt2[group_nr]++;
1716 boolean raise_level = FALSE;
1718 if (local_player->MovPos)
1722 if (tape.auto_play) /* tape might already be stopped here */
1723 tape.auto_play_level_solved = TRUE;
1725 if (tape.playing && tape.auto_play)
1726 tape.auto_play_level_solved = TRUE;
1729 local_player->LevelSolved = FALSE;
1731 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1735 if (!tape.playing && setup.sound_loops)
1736 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1737 SND_CTRL_PLAY_LOOP);
1739 while (TimeLeft > 0)
1741 if (!tape.playing && !setup.sound_loops)
1742 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1743 if (TimeLeft > 0 && !(TimeLeft % 10))
1744 RaiseScore(level.score[SC_TIME_BONUS]);
1745 if (TimeLeft > 100 && !(TimeLeft % 10))
1749 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1756 if (!tape.playing && setup.sound_loops)
1757 StopSound(SND_GAME_LEVELTIME_BONUS);
1759 else if (level.time == 0) /* level without time limit */
1761 if (!tape.playing && setup.sound_loops)
1762 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1763 SND_CTRL_PLAY_LOOP);
1765 while (TimePlayed < 999)
1767 if (!tape.playing && !setup.sound_loops)
1768 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1769 if (TimePlayed < 999 && !(TimePlayed % 10))
1770 RaiseScore(level.score[SC_TIME_BONUS]);
1771 if (TimePlayed < 900 && !(TimePlayed % 10))
1775 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1782 if (!tape.playing && setup.sound_loops)
1783 StopSound(SND_GAME_LEVELTIME_BONUS);
1786 /* close exit door after last player */
1787 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1788 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1790 int element = Feld[ExitX][ExitY];
1792 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1793 EL_SP_EXIT_CLOSING);
1795 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1798 /* Hero disappears */
1799 DrawLevelField(ExitX, ExitY);
1805 CloseDoor(DOOR_CLOSE_1);
1810 SaveTape(tape.level_nr); /* Ask to save tape */
1813 if (level_nr == leveldir_current->handicap_level)
1815 leveldir_current->handicap_level++;
1816 SaveLevelSetup_SeriesInfo();
1819 if (level_editor_test_game)
1820 local_player->score = -1; /* no highscore when playing from editor */
1821 else if (level_nr < leveldir_current->last_level)
1822 raise_level = TRUE; /* advance to next level */
1824 if ((hi_pos = NewHiScore()) >= 0)
1826 game_status = GAME_MODE_SCORES;
1827 DrawHallOfFame(hi_pos);
1836 game_status = GAME_MODE_MAIN;
1853 LoadScore(level_nr);
1855 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1856 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1859 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1861 if (local_player->score > highscore[k].Score)
1863 /* player has made it to the hall of fame */
1865 if (k < MAX_SCORE_ENTRIES - 1)
1867 int m = MAX_SCORE_ENTRIES - 1;
1870 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1871 if (!strcmp(setup.player_name, highscore[l].Name))
1873 if (m == k) /* player's new highscore overwrites his old one */
1877 for (l = m; l > k; l--)
1879 strcpy(highscore[l].Name, highscore[l - 1].Name);
1880 highscore[l].Score = highscore[l - 1].Score;
1887 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1888 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1889 highscore[k].Score = local_player->score;
1895 else if (!strncmp(setup.player_name, highscore[k].Name,
1896 MAX_PLAYER_NAME_LEN))
1897 break; /* player already there with a higher score */
1903 SaveScore(level_nr);
1908 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1910 if (player->GfxAction != action || player->GfxDir != dir)
1913 printf("Player frame reset! (%d => %d, %d => %d)\n",
1914 player->GfxAction, action, player->GfxDir, dir);
1917 player->GfxAction = action;
1918 player->GfxDir = dir;
1920 player->StepFrame = 0;
1924 static void ResetRandomAnimationValue(int x, int y)
1926 GfxRandom[x][y] = INIT_GFX_RANDOM();
1929 static void ResetGfxAnimation(int x, int y)
1932 GfxAction[x][y] = ACTION_DEFAULT;
1933 GfxDir[x][y] = MovDir[x][y];
1936 void InitMovingField(int x, int y, int direction)
1938 int element = Feld[x][y];
1939 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1940 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1944 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1945 ResetGfxAnimation(x, y);
1947 MovDir[newx][newy] = MovDir[x][y] = direction;
1948 GfxDir[x][y] = direction;
1950 if (Feld[newx][newy] == EL_EMPTY)
1951 Feld[newx][newy] = EL_BLOCKED;
1953 if (direction == MV_DOWN && CAN_FALL(element))
1954 GfxAction[x][y] = ACTION_FALLING;
1956 GfxAction[x][y] = ACTION_MOVING;
1958 GfxFrame[newx][newy] = GfxFrame[x][y];
1959 GfxRandom[newx][newy] = GfxRandom[x][y];
1960 GfxAction[newx][newy] = GfxAction[x][y];
1961 GfxDir[newx][newy] = GfxDir[x][y];
1964 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1966 int direction = MovDir[x][y];
1967 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1968 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1974 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1976 int oldx = x, oldy = y;
1977 int direction = MovDir[x][y];
1979 if (direction == MV_LEFT)
1981 else if (direction == MV_RIGHT)
1983 else if (direction == MV_UP)
1985 else if (direction == MV_DOWN)
1988 *comes_from_x = oldx;
1989 *comes_from_y = oldy;
1992 int MovingOrBlocked2Element(int x, int y)
1994 int element = Feld[x][y];
1996 if (element == EL_BLOCKED)
2000 Blocked2Moving(x, y, &oldx, &oldy);
2001 return Feld[oldx][oldy];
2007 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2009 /* like MovingOrBlocked2Element(), but if element is moving
2010 and (x,y) is the field the moving element is just leaving,
2011 return EL_BLOCKED instead of the element value */
2012 int element = Feld[x][y];
2014 if (IS_MOVING(x, y))
2016 if (element == EL_BLOCKED)
2020 Blocked2Moving(x, y, &oldx, &oldy);
2021 return Feld[oldx][oldy];
2030 static void RemoveField(int x, int y)
2032 Feld[x][y] = EL_EMPTY;
2039 ChangeDelay[x][y] = 0;
2040 ChangePage[x][y] = -1;
2041 Pushed[x][y] = FALSE;
2043 GfxElement[x][y] = EL_UNDEFINED;
2044 GfxAction[x][y] = ACTION_DEFAULT;
2045 GfxDir[x][y] = MV_NO_MOVING;
2048 void RemoveMovingField(int x, int y)
2050 int oldx = x, oldy = y, newx = x, newy = y;
2051 int element = Feld[x][y];
2052 int next_element = EL_UNDEFINED;
2054 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2057 if (IS_MOVING(x, y))
2059 Moving2Blocked(x, y, &newx, &newy);
2060 if (Feld[newx][newy] != EL_BLOCKED)
2063 else if (element == EL_BLOCKED)
2065 Blocked2Moving(x, y, &oldx, &oldy);
2066 if (!IS_MOVING(oldx, oldy))
2070 if (element == EL_BLOCKED &&
2071 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2072 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2073 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2074 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2075 next_element = get_next_element(Feld[oldx][oldy]);
2077 RemoveField(oldx, oldy);
2078 RemoveField(newx, newy);
2080 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2082 if (next_element != EL_UNDEFINED)
2083 Feld[oldx][oldy] = next_element;
2085 DrawLevelField(oldx, oldy);
2086 DrawLevelField(newx, newy);
2089 void DrawDynamite(int x, int y)
2091 int sx = SCREENX(x), sy = SCREENY(y);
2092 int graphic = el2img(Feld[x][y]);
2095 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2098 if (IS_WALKABLE_INSIDE(Back[x][y]))
2102 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2103 else if (Store[x][y])
2104 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2106 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2109 if (Back[x][y] || Store[x][y])
2110 DrawGraphicThruMask(sx, sy, graphic, frame);
2112 DrawGraphic(sx, sy, graphic, frame);
2114 if (game.emulation == EMU_SUPAPLEX)
2115 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2116 else if (Store[x][y])
2117 DrawGraphicThruMask(sx, sy, graphic, frame);
2119 DrawGraphic(sx, sy, graphic, frame);
2123 void CheckDynamite(int x, int y)
2125 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2129 if (MovDelay[x][y] != 0)
2132 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2139 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2141 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2142 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2143 StopSound(SND_DYNAMITE_ACTIVE);
2145 StopSound(SND_DYNABOMB_ACTIVE);
2151 void RelocatePlayer(int x, int y, int element)
2153 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2155 if (player->GameOver) /* do not reanimate dead player */
2159 RemoveField(x, y); /* temporarily remove newly placed player */
2160 DrawLevelField(x, y);
2163 if (player->present)
2165 while (player->MovPos)
2167 ScrollPlayer(player, SCROLL_GO_ON);
2168 ScrollScreen(NULL, SCROLL_GO_ON);
2174 Delay(GAME_FRAME_DELAY);
2177 DrawPlayer(player); /* needed here only to cleanup last field */
2178 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2180 player->is_moving = FALSE;
2183 Feld[x][y] = element;
2184 InitPlayerField(x, y, element, TRUE);
2186 if (player == local_player)
2188 int scroll_xx = -999, scroll_yy = -999;
2190 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2193 int fx = FX, fy = FY;
2195 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2196 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2197 local_player->jx - MIDPOSX);
2199 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2200 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2201 local_player->jy - MIDPOSY);
2203 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2204 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2209 fx += dx * TILEX / 2;
2210 fy += dy * TILEY / 2;
2212 ScrollLevel(dx, dy);
2215 /* scroll in two steps of half tile size to make things smoother */
2216 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2218 Delay(GAME_FRAME_DELAY);
2220 /* scroll second step to align at full tile size */
2222 Delay(GAME_FRAME_DELAY);
2227 void Explode(int ex, int ey, int phase, int mode)
2231 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2232 int last_phase = num_phase * delay;
2233 int half_phase = (num_phase / 2) * delay;
2234 int first_phase_after_start = EX_PHASE_START + 1;
2236 if (game.explosions_delayed)
2238 ExplodeField[ex][ey] = mode;
2242 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2244 int center_element = Feld[ex][ey];
2247 /* --- This is only really needed (and now handled) in "Impact()". --- */
2248 /* do not explode moving elements that left the explode field in time */
2249 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2250 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2254 if (mode == EX_NORMAL || mode == EX_CENTER)
2255 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2257 /* remove things displayed in background while burning dynamite */
2258 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2261 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2263 /* put moving element to center field (and let it explode there) */
2264 center_element = MovingOrBlocked2Element(ex, ey);
2265 RemoveMovingField(ex, ey);
2266 Feld[ex][ey] = center_element;
2269 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2271 int xx = x - ex + 1;
2272 int yy = y - ey + 1;
2275 if (!IN_LEV_FIELD(x, y) ||
2276 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2277 (x != ex || y != ey)))
2280 element = Feld[x][y];
2282 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2284 element = MovingOrBlocked2Element(x, y);
2286 if (!IS_EXPLOSION_PROOF(element))
2287 RemoveMovingField(x, y);
2293 if (IS_EXPLOSION_PROOF(element))
2296 /* indestructible elements can only explode in center (but not flames) */
2297 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2298 element == EL_FLAMES)
2303 if ((IS_INDESTRUCTIBLE(element) &&
2304 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2305 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2306 element == EL_FLAMES)
2310 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2312 if (IS_ACTIVE_BOMB(element))
2314 /* re-activate things under the bomb like gate or penguin */
2315 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2322 /* save walkable background elements while explosion on same tile */
2324 if (IS_INDESTRUCTIBLE(element))
2325 Back[x][y] = element;
2327 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2328 Back[x][y] = element;
2331 /* ignite explodable elements reached by other explosion */
2332 if (element == EL_EXPLOSION)
2333 element = Store2[x][y];
2336 if (AmoebaNr[x][y] &&
2337 (element == EL_AMOEBA_FULL ||
2338 element == EL_BD_AMOEBA ||
2339 element == EL_AMOEBA_GROWING))
2341 AmoebaCnt[AmoebaNr[x][y]]--;
2342 AmoebaCnt2[AmoebaNr[x][y]]--;
2348 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2350 switch(StorePlayer[ex][ey])
2353 Store[x][y] = EL_EMERALD_RED;
2356 Store[x][y] = EL_EMERALD;
2359 Store[x][y] = EL_EMERALD_PURPLE;
2363 Store[x][y] = EL_EMERALD_YELLOW;
2367 if (game.emulation == EMU_SUPAPLEX)
2368 Store[x][y] = EL_EMPTY;
2370 else if (center_element == EL_MOLE)
2371 Store[x][y] = EL_EMERALD_RED;
2372 else if (center_element == EL_PENGUIN)
2373 Store[x][y] = EL_EMERALD_PURPLE;
2374 else if (center_element == EL_BUG)
2375 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2376 else if (center_element == EL_BD_BUTTERFLY)
2377 Store[x][y] = EL_BD_DIAMOND;
2378 else if (center_element == EL_SP_ELECTRON)
2379 Store[x][y] = EL_SP_INFOTRON;
2380 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2381 Store[x][y] = level.amoeba_content;
2382 else if (center_element == EL_YAMYAM)
2383 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2384 else if (IS_CUSTOM_ELEMENT(center_element) &&
2385 element_info[center_element].content[xx][yy] != EL_EMPTY)
2386 Store[x][y] = element_info[center_element].content[xx][yy];
2387 else if (element == EL_WALL_EMERALD)
2388 Store[x][y] = EL_EMERALD;
2389 else if (element == EL_WALL_DIAMOND)
2390 Store[x][y] = EL_DIAMOND;
2391 else if (element == EL_WALL_BD_DIAMOND)
2392 Store[x][y] = EL_BD_DIAMOND;
2393 else if (element == EL_WALL_EMERALD_YELLOW)
2394 Store[x][y] = EL_EMERALD_YELLOW;
2395 else if (element == EL_WALL_EMERALD_RED)
2396 Store[x][y] = EL_EMERALD_RED;
2397 else if (element == EL_WALL_EMERALD_PURPLE)
2398 Store[x][y] = EL_EMERALD_PURPLE;
2399 else if (element == EL_WALL_PEARL)
2400 Store[x][y] = EL_PEARL;
2401 else if (element == EL_WALL_CRYSTAL)
2402 Store[x][y] = EL_CRYSTAL;
2403 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2404 Store[x][y] = element_info[element].content[1][1];
2406 Store[x][y] = EL_EMPTY;
2408 if (x != ex || y != ey ||
2409 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2410 Store2[x][y] = element;
2413 if (AmoebaNr[x][y] &&
2414 (element == EL_AMOEBA_FULL ||
2415 element == EL_BD_AMOEBA ||
2416 element == EL_AMOEBA_GROWING))
2418 AmoebaCnt[AmoebaNr[x][y]]--;
2419 AmoebaCnt2[AmoebaNr[x][y]]--;
2425 MovDir[x][y] = MovPos[x][y] = 0;
2426 GfxDir[x][y] = MovDir[x][y];
2431 Feld[x][y] = EL_EXPLOSION;
2433 GfxElement[x][y] = center_element;
2435 GfxElement[x][y] = EL_UNDEFINED;
2438 ExplodePhase[x][y] = 1;
2442 if (center_element == EL_YAMYAM)
2443 game.yamyam_content_nr =
2444 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2455 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2459 /* activate this even in non-DEBUG version until cause for crash in
2460 getGraphicAnimationFrame() (see below) is found and eliminated */
2464 if (GfxElement[x][y] == EL_UNDEFINED)
2467 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2468 printf("Explode(): This should never happen!\n");
2471 GfxElement[x][y] = EL_EMPTY;
2475 if (phase == first_phase_after_start)
2477 int element = Store2[x][y];
2479 if (element == EL_BLACK_ORB)
2481 Feld[x][y] = Store2[x][y];
2486 else if (phase == half_phase)
2488 int element = Store2[x][y];
2490 if (IS_PLAYER(x, y))
2491 KillHeroUnlessProtected(x, y);
2492 else if (CAN_EXPLODE_BY_FIRE(element))
2494 Feld[x][y] = Store2[x][y];
2498 else if (element == EL_AMOEBA_TO_DIAMOND)
2499 AmoebeUmwandeln(x, y);
2502 if (phase == last_phase)
2506 element = Feld[x][y] = Store[x][y];
2507 Store[x][y] = Store2[x][y] = 0;
2508 GfxElement[x][y] = EL_UNDEFINED;
2510 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2511 element = Feld[x][y] = Back[x][y];
2514 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2515 GfxDir[x][y] = MV_NO_MOVING;
2516 ChangeDelay[x][y] = 0;
2517 ChangePage[x][y] = -1;
2519 InitField(x, y, FALSE);
2520 if (CAN_MOVE(element))
2522 DrawLevelField(x, y);
2524 TestIfElementTouchesCustomElement(x, y);
2526 if (GFX_CRUMBLED(element))
2527 DrawLevelFieldCrumbledSandNeighbours(x, y);
2529 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2530 StorePlayer[x][y] = 0;
2532 if (ELEM_IS_PLAYER(element))
2533 RelocatePlayer(x, y, element);
2535 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2538 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2540 int stored = Store[x][y];
2541 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2542 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2545 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2548 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2549 element_info[GfxElement[x][y]].token_name,
2554 DrawLevelFieldCrumbledSand(x, y);
2556 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2558 DrawLevelElement(x, y, Back[x][y]);
2559 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2561 else if (IS_WALKABLE_UNDER(Back[x][y]))
2563 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2564 DrawLevelElementThruMask(x, y, Back[x][y]);
2566 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2567 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2571 void DynaExplode(int ex, int ey)
2574 int dynabomb_element = Feld[ex][ey];
2575 int dynabomb_size = 1;
2576 boolean dynabomb_xl = FALSE;
2577 struct PlayerInfo *player;
2578 static int xy[4][2] =
2586 if (IS_ACTIVE_BOMB(dynabomb_element))
2588 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2589 dynabomb_size = player->dynabomb_size;
2590 dynabomb_xl = player->dynabomb_xl;
2591 player->dynabombs_left++;
2594 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2596 for (i = 0; i < 4; i++)
2598 for (j = 1; j <= dynabomb_size; j++)
2600 int x = ex + j * xy[i % 4][0];
2601 int y = ey + j * xy[i % 4][1];
2604 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2607 element = Feld[x][y];
2609 /* do not restart explosions of fields with active bombs */
2610 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2613 Explode(x, y, EX_PHASE_START, EX_BORDER);
2615 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2616 if (element != EL_EMPTY &&
2617 element != EL_SAND &&
2618 element != EL_EXPLOSION &&
2625 void Bang(int x, int y)
2628 int element = MovingOrBlocked2Element(x, y);
2630 int element = Feld[x][y];
2634 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2636 if (IS_PLAYER(x, y))
2639 struct PlayerInfo *player = PLAYERINFO(x, y);
2641 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2642 player->element_nr);
2647 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2649 if (game.emulation == EMU_SUPAPLEX)
2650 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2652 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2657 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2665 case EL_BD_BUTTERFLY:
2668 case EL_DARK_YAMYAM:
2672 RaiseScoreElement(element);
2673 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2675 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2676 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2677 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2678 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2679 case EL_DYNABOMB_INCREASE_NUMBER:
2680 case EL_DYNABOMB_INCREASE_SIZE:
2681 case EL_DYNABOMB_INCREASE_POWER:
2686 case EL_LAMP_ACTIVE:
2687 if (IS_PLAYER(x, y))
2688 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2690 Explode(x, y, EX_PHASE_START, EX_CENTER);
2693 if (CAN_EXPLODE_DYNA(element))
2695 else if (CAN_EXPLODE_1X1(element))
2696 Explode(x, y, EX_PHASE_START, EX_CENTER);
2698 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2702 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2705 void SplashAcid(int x, int y)
2707 int element = Feld[x][y];
2709 if (element != EL_ACID_SPLASH_LEFT &&
2710 element != EL_ACID_SPLASH_RIGHT)
2712 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2714 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2715 (!IN_LEV_FIELD(x-1, y-1) ||
2716 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2717 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2719 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2720 (!IN_LEV_FIELD(x+1, y-1) ||
2721 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2722 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2726 static void InitBeltMovement()
2728 static int belt_base_element[4] =
2730 EL_CONVEYOR_BELT_1_LEFT,
2731 EL_CONVEYOR_BELT_2_LEFT,
2732 EL_CONVEYOR_BELT_3_LEFT,
2733 EL_CONVEYOR_BELT_4_LEFT
2735 static int belt_base_active_element[4] =
2737 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2738 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2739 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2740 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2745 /* set frame order for belt animation graphic according to belt direction */
2746 for (i = 0; i < 4; i++)
2750 for (j = 0; j < 3; j++)
2752 int element = belt_base_active_element[belt_nr] + j;
2753 int graphic = el2img(element);
2755 if (game.belt_dir[i] == MV_LEFT)
2756 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2758 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2762 for (y = 0; y < lev_fieldy; y++)
2764 for (x = 0; x < lev_fieldx; x++)
2766 int element = Feld[x][y];
2768 for (i = 0; i < 4; i++)
2770 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2772 int e_belt_nr = getBeltNrFromBeltElement(element);
2775 if (e_belt_nr == belt_nr)
2777 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2779 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2787 static void ToggleBeltSwitch(int x, int y)
2789 static int belt_base_element[4] =
2791 EL_CONVEYOR_BELT_1_LEFT,
2792 EL_CONVEYOR_BELT_2_LEFT,
2793 EL_CONVEYOR_BELT_3_LEFT,
2794 EL_CONVEYOR_BELT_4_LEFT
2796 static int belt_base_active_element[4] =
2798 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2799 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2800 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2801 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2803 static int belt_base_switch_element[4] =
2805 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2806 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2807 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2808 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2810 static int belt_move_dir[4] =
2818 int element = Feld[x][y];
2819 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2820 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2821 int belt_dir = belt_move_dir[belt_dir_nr];
2824 if (!IS_BELT_SWITCH(element))
2827 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2828 game.belt_dir[belt_nr] = belt_dir;
2830 if (belt_dir_nr == 3)
2833 /* set frame order for belt animation graphic according to belt direction */
2834 for (i = 0; i < 3; i++)
2836 int element = belt_base_active_element[belt_nr] + i;
2837 int graphic = el2img(element);
2839 if (belt_dir == MV_LEFT)
2840 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2842 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2845 for (yy = 0; yy < lev_fieldy; yy++)
2847 for (xx = 0; xx < lev_fieldx; xx++)
2849 int element = Feld[xx][yy];
2851 if (IS_BELT_SWITCH(element))
2853 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2855 if (e_belt_nr == belt_nr)
2857 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2858 DrawLevelField(xx, yy);
2861 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2863 int e_belt_nr = getBeltNrFromBeltElement(element);
2865 if (e_belt_nr == belt_nr)
2867 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2869 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2870 DrawLevelField(xx, yy);
2873 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2875 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2877 if (e_belt_nr == belt_nr)
2879 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2881 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2882 DrawLevelField(xx, yy);
2889 static void ToggleSwitchgateSwitch(int x, int y)
2893 game.switchgate_pos = !game.switchgate_pos;
2895 for (yy = 0; yy < lev_fieldy; yy++)
2897 for (xx = 0; xx < lev_fieldx; xx++)
2899 int element = Feld[xx][yy];
2901 if (element == EL_SWITCHGATE_SWITCH_UP ||
2902 element == EL_SWITCHGATE_SWITCH_DOWN)
2904 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2905 DrawLevelField(xx, yy);
2907 else if (element == EL_SWITCHGATE_OPEN ||
2908 element == EL_SWITCHGATE_OPENING)
2910 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2912 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2914 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2917 else if (element == EL_SWITCHGATE_CLOSED ||
2918 element == EL_SWITCHGATE_CLOSING)
2920 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2922 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2924 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2931 static int getInvisibleActiveFromInvisibleElement(int element)
2933 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2934 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2935 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2939 static int getInvisibleFromInvisibleActiveElement(int element)
2941 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2942 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2943 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2947 static void RedrawAllLightSwitchesAndInvisibleElements()
2951 for (y = 0; y < lev_fieldy; y++)
2953 for (x = 0; x < lev_fieldx; x++)
2955 int element = Feld[x][y];
2957 if (element == EL_LIGHT_SWITCH &&
2958 game.light_time_left > 0)
2960 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2961 DrawLevelField(x, y);
2963 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2964 game.light_time_left == 0)
2966 Feld[x][y] = EL_LIGHT_SWITCH;
2967 DrawLevelField(x, y);
2969 else if (element == EL_INVISIBLE_STEELWALL ||
2970 element == EL_INVISIBLE_WALL ||
2971 element == EL_INVISIBLE_SAND)
2973 if (game.light_time_left > 0)
2974 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2976 DrawLevelField(x, y);
2978 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2979 element == EL_INVISIBLE_WALL_ACTIVE ||
2980 element == EL_INVISIBLE_SAND_ACTIVE)
2982 if (game.light_time_left == 0)
2983 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2985 DrawLevelField(x, y);
2991 static void ToggleLightSwitch(int x, int y)
2993 int element = Feld[x][y];
2995 game.light_time_left =
2996 (element == EL_LIGHT_SWITCH ?
2997 level.time_light * FRAMES_PER_SECOND : 0);
2999 RedrawAllLightSwitchesAndInvisibleElements();
3002 static void ActivateTimegateSwitch(int x, int y)
3006 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3008 for (yy = 0; yy < lev_fieldy; yy++)
3010 for (xx = 0; xx < lev_fieldx; xx++)
3012 int element = Feld[xx][yy];
3014 if (element == EL_TIMEGATE_CLOSED ||
3015 element == EL_TIMEGATE_CLOSING)
3017 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3018 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3022 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3024 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3025 DrawLevelField(xx, yy);
3032 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3035 inline static int getElementMoveStepsize(int x, int y)
3037 int element = Feld[x][y];
3038 int direction = MovDir[x][y];
3039 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3040 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3041 int horiz_move = (dx != 0);
3042 int sign = (horiz_move ? dx : dy);
3043 int step = sign * element_info[element].move_stepsize;
3045 /* special values for move stepsize for spring and things on conveyor belt */
3048 if (CAN_FALL(element) &&
3049 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3050 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3051 else if (element == EL_SPRING)
3052 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3058 void Impact(int x, int y)
3060 boolean lastline = (y == lev_fieldy-1);
3061 boolean object_hit = FALSE;
3062 boolean impact = (lastline || object_hit);
3063 int element = Feld[x][y];
3064 int smashed = EL_UNDEFINED;
3066 if (!lastline) /* check if element below was hit */
3068 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3071 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3072 MovDir[x][y + 1] != MV_DOWN ||
3073 MovPos[x][y + 1] <= TILEY / 2));
3075 /* do not smash moving elements that left the smashed field in time */
3076 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3077 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3081 smashed = MovingOrBlocked2Element(x, y + 1);
3083 impact = (lastline || object_hit);
3086 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3092 /* only reset graphic animation if graphic really changes after impact */
3094 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3096 ResetGfxAnimation(x, y);
3097 DrawLevelField(x, y);
3100 if (impact && CAN_EXPLODE_IMPACT(element))
3105 else if (impact && element == EL_PEARL)
3107 Feld[x][y] = EL_PEARL_BREAKING;
3108 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3111 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3113 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3118 if (impact && element == EL_AMOEBA_DROP)
3120 if (object_hit && IS_PLAYER(x, y + 1))
3121 KillHeroUnlessProtected(x, y + 1);
3122 else if (object_hit && smashed == EL_PENGUIN)
3126 Feld[x][y] = EL_AMOEBA_GROWING;
3127 Store[x][y] = EL_AMOEBA_WET;
3129 ResetRandomAnimationValue(x, y);
3134 if (object_hit) /* check which object was hit */
3136 if (CAN_PASS_MAGIC_WALL(element) &&
3137 (smashed == EL_MAGIC_WALL ||
3138 smashed == EL_BD_MAGIC_WALL))
3141 int activated_magic_wall =
3142 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3143 EL_BD_MAGIC_WALL_ACTIVE);
3145 /* activate magic wall / mill */
3146 for (yy = 0; yy < lev_fieldy; yy++)
3147 for (xx = 0; xx < lev_fieldx; xx++)
3148 if (Feld[xx][yy] == smashed)
3149 Feld[xx][yy] = activated_magic_wall;
3151 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3152 game.magic_wall_active = TRUE;
3154 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3155 SND_MAGIC_WALL_ACTIVATING :
3156 SND_BD_MAGIC_WALL_ACTIVATING));
3159 if (IS_PLAYER(x, y + 1))
3161 if (CAN_SMASH_PLAYER(element))
3163 KillHeroUnlessProtected(x, y + 1);
3167 else if (smashed == EL_PENGUIN)
3169 if (CAN_SMASH_PLAYER(element))
3175 else if (element == EL_BD_DIAMOND)
3177 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3183 else if ((element == EL_SP_INFOTRON ||
3184 element == EL_SP_ZONK) &&
3185 (smashed == EL_SP_SNIKSNAK ||
3186 smashed == EL_SP_ELECTRON ||
3187 smashed == EL_SP_DISK_ORANGE))
3193 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3199 else if (CAN_SMASH_EVERYTHING(element))
3201 if (IS_CLASSIC_ENEMY(smashed) ||
3202 CAN_EXPLODE_SMASHED(smashed))
3207 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3209 if (smashed == EL_LAMP ||
3210 smashed == EL_LAMP_ACTIVE)
3215 else if (smashed == EL_NUT)
3217 Feld[x][y + 1] = EL_NUT_BREAKING;
3218 PlayLevelSound(x, y, SND_NUT_BREAKING);
3219 RaiseScoreElement(EL_NUT);
3222 else if (smashed == EL_PEARL)
3224 Feld[x][y + 1] = EL_PEARL_BREAKING;
3225 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3228 else if (smashed == EL_DIAMOND)
3230 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3231 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3234 else if (IS_BELT_SWITCH(smashed))
3236 ToggleBeltSwitch(x, y + 1);
3238 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3239 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3241 ToggleSwitchgateSwitch(x, y + 1);
3243 else if (smashed == EL_LIGHT_SWITCH ||
3244 smashed == EL_LIGHT_SWITCH_ACTIVE)
3246 ToggleLightSwitch(x, y + 1);
3250 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3252 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3253 CE_OTHER_IS_SWITCHING);
3254 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3260 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3265 /* play sound of magic wall / mill */
3267 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3268 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3270 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3271 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3272 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3273 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3278 /* play sound of object that hits the ground */
3279 if (lastline || object_hit)
3280 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3283 inline static void TurnRoundExt(int x, int y)
3295 { 0, 0 }, { 0, 0 }, { 0, 0 },
3300 int left, right, back;
3304 { MV_DOWN, MV_UP, MV_RIGHT },
3305 { MV_UP, MV_DOWN, MV_LEFT },
3307 { MV_LEFT, MV_RIGHT, MV_DOWN },
3311 { MV_RIGHT, MV_LEFT, MV_UP }
3314 int element = Feld[x][y];
3315 int move_pattern = element_info[element].move_pattern;
3317 int old_move_dir = MovDir[x][y];
3318 int left_dir = turn[old_move_dir].left;
3319 int right_dir = turn[old_move_dir].right;
3320 int back_dir = turn[old_move_dir].back;
3322 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3323 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3324 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3325 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3327 int left_x = x + left_dx, left_y = y + left_dy;
3328 int right_x = x + right_dx, right_y = y + right_dy;
3329 int move_x = x + move_dx, move_y = y + move_dy;
3333 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3335 TestIfBadThingTouchesOtherBadThing(x, y);
3337 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3338 MovDir[x][y] = right_dir;
3339 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3340 MovDir[x][y] = left_dir;
3342 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3344 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3347 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3348 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3350 TestIfBadThingTouchesOtherBadThing(x, y);
3352 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3353 MovDir[x][y] = left_dir;
3354 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3355 MovDir[x][y] = right_dir;
3357 if ((element == EL_SPACESHIP ||
3358 element == EL_SP_SNIKSNAK ||
3359 element == EL_SP_ELECTRON)
3360 && MovDir[x][y] != old_move_dir)
3362 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3365 else if (element == EL_YAMYAM)
3367 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3368 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3370 if (can_turn_left && can_turn_right)
3371 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3372 else if (can_turn_left)
3373 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3374 else if (can_turn_right)
3375 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3377 MovDir[x][y] = back_dir;
3379 MovDelay[x][y] = 16 + 16 * RND(3);
3381 else if (element == EL_DARK_YAMYAM)
3383 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3384 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3386 if (can_turn_left && can_turn_right)
3387 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3388 else if (can_turn_left)
3389 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3390 else if (can_turn_right)
3391 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3393 MovDir[x][y] = back_dir;
3395 MovDelay[x][y] = 16 + 16 * RND(3);
3397 else if (element == EL_PACMAN)
3399 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3400 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3402 if (can_turn_left && can_turn_right)
3403 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3404 else if (can_turn_left)
3405 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3406 else if (can_turn_right)
3407 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3409 MovDir[x][y] = back_dir;
3411 MovDelay[x][y] = 6 + RND(40);
3413 else if (element == EL_PIG)
3415 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3416 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3417 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3418 boolean should_turn_left, should_turn_right, should_move_on;
3420 int rnd = RND(rnd_value);
3422 should_turn_left = (can_turn_left &&
3424 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3425 y + back_dy + left_dy)));
3426 should_turn_right = (can_turn_right &&
3428 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3429 y + back_dy + right_dy)));
3430 should_move_on = (can_move_on &&
3433 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3434 y + move_dy + left_dy) ||
3435 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3436 y + move_dy + right_dy)));
3438 if (should_turn_left || should_turn_right || should_move_on)
3440 if (should_turn_left && should_turn_right && should_move_on)
3441 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3442 rnd < 2 * rnd_value / 3 ? right_dir :
3444 else if (should_turn_left && should_turn_right)
3445 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3446 else if (should_turn_left && should_move_on)
3447 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3448 else if (should_turn_right && should_move_on)
3449 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3450 else if (should_turn_left)
3451 MovDir[x][y] = left_dir;
3452 else if (should_turn_right)
3453 MovDir[x][y] = right_dir;
3454 else if (should_move_on)
3455 MovDir[x][y] = old_move_dir;
3457 else if (can_move_on && rnd > rnd_value / 8)
3458 MovDir[x][y] = old_move_dir;
3459 else if (can_turn_left && can_turn_right)
3460 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3461 else if (can_turn_left && rnd > rnd_value / 8)
3462 MovDir[x][y] = left_dir;
3463 else if (can_turn_right && rnd > rnd_value/8)
3464 MovDir[x][y] = right_dir;
3466 MovDir[x][y] = back_dir;
3468 xx = x + move_xy[MovDir[x][y]].x;
3469 yy = y + move_xy[MovDir[x][y]].y;
3471 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3472 MovDir[x][y] = old_move_dir;
3476 else if (element == EL_DRAGON)
3478 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3479 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3480 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3482 int rnd = RND(rnd_value);
3485 if (FrameCounter < 1 && x == 0 && y == 29)
3486 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3489 if (can_move_on && rnd > rnd_value / 8)
3490 MovDir[x][y] = old_move_dir;
3491 else if (can_turn_left && can_turn_right)
3492 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3493 else if (can_turn_left && rnd > rnd_value / 8)
3494 MovDir[x][y] = left_dir;
3495 else if (can_turn_right && rnd > rnd_value / 8)
3496 MovDir[x][y] = right_dir;
3498 MovDir[x][y] = back_dir;
3500 xx = x + move_xy[MovDir[x][y]].x;
3501 yy = y + move_xy[MovDir[x][y]].y;
3504 if (FrameCounter < 1 && x == 0 && y == 29)
3505 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3506 xx, yy, Feld[xx][yy],
3511 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3512 MovDir[x][y] = old_move_dir;
3514 if (!IS_FREE(xx, yy))
3515 MovDir[x][y] = old_move_dir;
3519 if (FrameCounter < 1 && x == 0 && y == 29)
3520 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3525 else if (element == EL_MOLE)
3527 boolean can_move_on =
3528 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3529 IS_AMOEBOID(Feld[move_x][move_y]) ||
3530 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3533 boolean can_turn_left =
3534 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3535 IS_AMOEBOID(Feld[left_x][left_y])));
3537 boolean can_turn_right =
3538 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3539 IS_AMOEBOID(Feld[right_x][right_y])));
3541 if (can_turn_left && can_turn_right)
3542 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3543 else if (can_turn_left)
3544 MovDir[x][y] = left_dir;
3546 MovDir[x][y] = right_dir;
3549 if (MovDir[x][y] != old_move_dir)
3552 else if (element == EL_BALLOON)
3554 MovDir[x][y] = game.balloon_dir;
3557 else if (element == EL_SPRING)
3559 if (MovDir[x][y] & MV_HORIZONTAL &&
3560 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3561 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3562 MovDir[x][y] = MV_NO_MOVING;
3566 else if (element == EL_ROBOT ||
3567 element == EL_SATELLITE ||
3568 element == EL_PENGUIN)
3570 int attr_x = -1, attr_y = -1;
3581 for (i = 0; i < MAX_PLAYERS; i++)
3583 struct PlayerInfo *player = &stored_player[i];
3584 int jx = player->jx, jy = player->jy;
3586 if (!player->active)
3590 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3598 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3604 if (element == EL_PENGUIN)
3607 static int xy[4][2] =
3615 for (i = 0; i < 4; i++)
3617 int ex = x + xy[i % 4][0];
3618 int ey = y + xy[i % 4][1];
3620 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3629 MovDir[x][y] = MV_NO_MOVING;
3631 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3632 else if (attr_x > x)
3633 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3635 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3636 else if (attr_y > y)
3637 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3639 if (element == EL_ROBOT)
3643 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3644 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3645 Moving2Blocked(x, y, &newx, &newy);
3647 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3648 MovDelay[x][y] = 8 + 8 * !RND(3);
3650 MovDelay[x][y] = 16;
3652 else if (element == EL_PENGUIN)
3658 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3660 boolean first_horiz = RND(2);
3661 int new_move_dir = MovDir[x][y];
3664 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3665 Moving2Blocked(x, y, &newx, &newy);
3667 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3671 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3672 Moving2Blocked(x, y, &newx, &newy);
3674 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3677 MovDir[x][y] = old_move_dir;
3681 else /* (element == EL_SATELLITE) */
3687 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3689 boolean first_horiz = RND(2);
3690 int new_move_dir = MovDir[x][y];
3693 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3694 Moving2Blocked(x, y, &newx, &newy);
3696 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3700 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3701 Moving2Blocked(x, y, &newx, &newy);
3703 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3706 MovDir[x][y] = old_move_dir;
3711 else if (move_pattern == MV_TURNING_LEFT ||
3712 move_pattern == MV_TURNING_RIGHT ||
3713 move_pattern == MV_TURNING_LEFT_RIGHT ||
3714 move_pattern == MV_TURNING_RIGHT_LEFT ||
3715 move_pattern == MV_TURNING_RANDOM ||
3716 move_pattern == MV_ALL_DIRECTIONS)
3718 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3719 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3721 if (move_pattern == MV_TURNING_LEFT)
3722 MovDir[x][y] = left_dir;
3723 else if (move_pattern == MV_TURNING_RIGHT)
3724 MovDir[x][y] = right_dir;
3725 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3726 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3727 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3728 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3729 else if (move_pattern == MV_TURNING_RANDOM)
3730 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3731 can_turn_right && !can_turn_left ? right_dir :
3732 RND(2) ? left_dir : right_dir);
3733 else if (can_turn_left && can_turn_right)
3734 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3735 else if (can_turn_left)
3736 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3737 else if (can_turn_right)
3738 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3740 MovDir[x][y] = back_dir;
3742 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3744 else if (move_pattern == MV_HORIZONTAL ||
3745 move_pattern == MV_VERTICAL)
3747 if (move_pattern & old_move_dir)
3748 MovDir[x][y] = back_dir;
3749 else if (move_pattern == MV_HORIZONTAL)
3750 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3751 else if (move_pattern == MV_VERTICAL)
3752 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3754 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3756 else if (move_pattern & MV_ANY_DIRECTION)
3758 MovDir[x][y] = move_pattern;
3759 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3761 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3763 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3764 MovDir[x][y] = left_dir;
3765 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3766 MovDir[x][y] = right_dir;
3768 if (MovDir[x][y] != old_move_dir)
3769 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3771 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3773 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3774 MovDir[x][y] = right_dir;
3775 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3776 MovDir[x][y] = left_dir;
3778 if (MovDir[x][y] != old_move_dir)
3779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3781 else if (move_pattern == MV_TOWARDS_PLAYER ||
3782 move_pattern == MV_AWAY_FROM_PLAYER)
3784 int attr_x = -1, attr_y = -1;
3786 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 struct PlayerInfo *player = &stored_player[i];
3800 int jx = player->jx, jy = player->jy;
3802 if (!player->active)
3806 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3814 MovDir[x][y] = MV_NO_MOVING;
3816 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3817 else if (attr_x > x)
3818 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3820 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3821 else if (attr_y > y)
3822 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3824 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3826 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3828 boolean first_horiz = RND(2);
3829 int new_move_dir = MovDir[x][y];
3832 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3833 Moving2Blocked(x, y, &newx, &newy);
3835 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3839 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3840 Moving2Blocked(x, y, &newx, &newy);
3842 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3845 MovDir[x][y] = old_move_dir;
3848 else if (move_pattern == MV_WHEN_PUSHED)
3850 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3851 MovDir[x][y] = MV_NO_MOVING;
3855 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3856 element == EL_MAZE_RUNNER)
3858 static int test_xy[7][2] =
3868 static int test_dir[7] =
3878 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3879 int move_preference = -1000000; /* start with very low preference */
3880 int new_move_dir = MV_NO_MOVING;
3881 int start_test = RND(4);
3884 for (i = 0; i < 4; i++)
3886 int move_dir = test_dir[start_test + i];
3887 int move_dir_preference;
3889 xx = x + test_xy[start_test + i][0];
3890 yy = y + test_xy[start_test + i][1];
3892 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3893 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3895 new_move_dir = move_dir;
3900 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3903 move_dir_preference = -1 * RunnerVisit[xx][yy];
3904 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3905 move_dir_preference = PlayerVisit[xx][yy];
3907 if (move_dir_preference > move_preference)
3909 /* prefer field that has not been visited for the longest time */
3910 move_preference = move_dir_preference;
3911 new_move_dir = move_dir;
3913 else if (move_dir_preference == move_preference &&
3914 move_dir == old_move_dir)
3916 /* prefer last direction when all directions are preferred equally */
3917 move_preference = move_dir_preference;
3918 new_move_dir = move_dir;
3922 MovDir[x][y] = new_move_dir;
3923 if (old_move_dir != new_move_dir)
3928 static void TurnRound(int x, int y)
3930 int direction = MovDir[x][y];
3933 GfxDir[x][y] = MovDir[x][y];
3939 GfxDir[x][y] = MovDir[x][y];
3942 if (direction != MovDir[x][y])
3947 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3950 GfxAction[x][y] = ACTION_WAITING;
3954 static boolean JustBeingPushed(int x, int y)
3958 for (i = 0; i < MAX_PLAYERS; i++)
3960 struct PlayerInfo *player = &stored_player[i];
3962 if (player->active && player->is_pushing && player->MovPos)
3964 int next_jx = player->jx + (player->jx - player->last_jx);
3965 int next_jy = player->jy + (player->jy - player->last_jy);
3967 if (x == next_jx && y == next_jy)
3975 void StartMoving(int x, int y)
3977 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3978 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3979 int element = Feld[x][y];
3985 if (MovDelay[x][y] == 0)
3986 GfxAction[x][y] = ACTION_DEFAULT;
3988 /* !!! this should be handled more generic (not only for mole) !!! */
3989 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3990 GfxAction[x][y] = ACTION_DEFAULT;
3993 if (CAN_FALL(element) && y < lev_fieldy - 1)
3995 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3996 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3997 if (JustBeingPushed(x, y))
4000 if (element == EL_QUICKSAND_FULL)
4002 if (IS_FREE(x, y + 1))
4004 InitMovingField(x, y, MV_DOWN);
4005 started_moving = TRUE;
4007 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4008 Store[x][y] = EL_ROCK;
4010 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4012 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4015 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4017 if (!MovDelay[x][y])
4018 MovDelay[x][y] = TILEY + 1;
4027 Feld[x][y] = EL_QUICKSAND_EMPTY;
4028 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4029 Store[x][y + 1] = Store[x][y];
4032 PlayLevelSoundAction(x, y, ACTION_FILLING);
4034 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4038 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4039 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4041 InitMovingField(x, y, MV_DOWN);
4042 started_moving = TRUE;
4044 Feld[x][y] = EL_QUICKSAND_FILLING;
4045 Store[x][y] = element;
4047 PlayLevelSoundAction(x, y, ACTION_FILLING);
4049 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4052 else if (element == EL_MAGIC_WALL_FULL)
4054 if (IS_FREE(x, y + 1))
4056 InitMovingField(x, y, MV_DOWN);
4057 started_moving = TRUE;
4059 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4060 Store[x][y] = EL_CHANGED(Store[x][y]);
4062 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4064 if (!MovDelay[x][y])
4065 MovDelay[x][y] = TILEY/4 + 1;
4074 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4075 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4076 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4080 else if (element == EL_BD_MAGIC_WALL_FULL)
4082 if (IS_FREE(x, y + 1))
4084 InitMovingField(x, y, MV_DOWN);
4085 started_moving = TRUE;
4087 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4088 Store[x][y] = EL_CHANGED2(Store[x][y]);
4090 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4092 if (!MovDelay[x][y])
4093 MovDelay[x][y] = TILEY/4 + 1;
4102 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4103 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4104 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4108 else if (CAN_PASS_MAGIC_WALL(element) &&
4109 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4110 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4112 InitMovingField(x, y, MV_DOWN);
4113 started_moving = TRUE;
4116 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4117 EL_BD_MAGIC_WALL_FILLING);
4118 Store[x][y] = element;
4121 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4123 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4128 InitMovingField(x, y, MV_DOWN);
4129 started_moving = TRUE;
4131 Store[x][y] = EL_ACID;
4133 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4134 GfxAction[x][y + 1] = ACTION_ACTIVE;
4138 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4139 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4140 (Feld[x][y + 1] == EL_BLOCKED)) ||
4141 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4142 CAN_SMASH(element) && WasJustFalling[x][y] &&
4143 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4147 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4148 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4149 WasJustMoving[x][y] && !Pushed[x][y + 1])
4151 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4152 WasJustMoving[x][y])
4157 /* this is needed for a special case not covered by calling "Impact()"
4158 from "ContinueMoving()": if an element moves to a tile directly below
4159 another element which was just falling on that tile (which was empty
4160 in the previous frame), the falling element above would just stop
4161 instead of smashing the element below (in previous version, the above
4162 element was just checked for "moving" instead of "falling", resulting
4163 in incorrect smashes caused by horizontal movement of the above
4164 element; also, the case of the player being the element to smash was
4165 simply not covered here... :-/ ) */
4169 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4171 if (MovDir[x][y] == MV_NO_MOVING)
4173 InitMovingField(x, y, MV_DOWN);
4174 started_moving = TRUE;
4177 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4179 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4180 MovDir[x][y] = MV_DOWN;
4182 InitMovingField(x, y, MV_DOWN);
4183 started_moving = TRUE;
4185 else if (element == EL_AMOEBA_DROP)
4187 Feld[x][y] = EL_AMOEBA_GROWING;
4188 Store[x][y] = EL_AMOEBA_WET;
4190 /* Store[x][y + 1] must be zero, because:
4191 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4194 #if OLD_GAME_BEHAVIOUR
4195 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4197 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4198 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4199 element != EL_DX_SUPABOMB)
4202 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4203 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4204 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4205 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4208 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4209 (IS_FREE(x - 1, y + 1) ||
4210 Feld[x - 1][y + 1] == EL_ACID));
4211 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4212 (IS_FREE(x + 1, y + 1) ||
4213 Feld[x + 1][y + 1] == EL_ACID));
4214 boolean can_fall_any = (can_fall_left || can_fall_right);
4215 boolean can_fall_both = (can_fall_left && can_fall_right);
4217 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4219 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4221 if (slippery_type == SLIPPERY_ONLY_LEFT)
4222 can_fall_right = FALSE;
4223 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4224 can_fall_left = FALSE;
4225 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4226 can_fall_right = FALSE;
4227 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4228 can_fall_left = FALSE;
4230 can_fall_any = (can_fall_left || can_fall_right);
4231 can_fall_both = (can_fall_left && can_fall_right);
4236 if (can_fall_both &&
4237 (game.emulation != EMU_BOULDERDASH &&
4238 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4239 can_fall_left = !(can_fall_right = RND(2));
4241 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4242 started_moving = TRUE;
4245 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4247 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4248 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4249 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4250 int belt_dir = game.belt_dir[belt_nr];
4252 if ((belt_dir == MV_LEFT && left_is_free) ||
4253 (belt_dir == MV_RIGHT && right_is_free))
4255 InitMovingField(x, y, belt_dir);
4256 started_moving = TRUE;
4258 GfxAction[x][y] = ACTION_DEFAULT;
4263 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4264 if (CAN_MOVE(element) && !started_moving)
4266 int move_pattern = element_info[element].move_pattern;
4269 Moving2Blocked(x, y, &newx, &newy);
4272 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4275 if ((element == EL_SATELLITE ||
4276 element == EL_BALLOON ||
4277 element == EL_SPRING)
4278 && JustBeingPushed(x, y))
4283 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4284 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4285 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4288 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4289 element, element_info[element].token_name,
4290 WasJustMoving[x][y],
4291 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4292 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4293 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4294 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4297 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4299 if (Feld[x][y] != element) /* element has changed */
4306 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4307 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4309 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4311 Moving2Blocked(x, y, &newx, &newy);
4312 if (Feld[newx][newy] == EL_BLOCKED)
4313 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4319 if (FrameCounter < 1 && x == 0 && y == 29)
4320 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4323 if (!MovDelay[x][y]) /* start new movement phase */
4325 /* all objects that can change their move direction after each step
4326 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4328 if (element != EL_YAMYAM &&
4329 element != EL_DARK_YAMYAM &&
4330 element != EL_PACMAN &&
4331 !(move_pattern & MV_ANY_DIRECTION) &&
4332 move_pattern != MV_TURNING_LEFT &&
4333 move_pattern != MV_TURNING_RIGHT &&
4334 move_pattern != MV_TURNING_LEFT_RIGHT &&
4335 move_pattern != MV_TURNING_RIGHT_LEFT &&
4336 move_pattern != MV_TURNING_RANDOM)
4341 if (FrameCounter < 1 && x == 0 && y == 29)
4342 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4345 if (MovDelay[x][y] && (element == EL_BUG ||
4346 element == EL_SPACESHIP ||
4347 element == EL_SP_SNIKSNAK ||
4348 element == EL_SP_ELECTRON ||
4349 element == EL_MOLE))
4350 DrawLevelField(x, y);
4354 if (MovDelay[x][y]) /* wait some time before next movement */
4359 if (element == EL_YAMYAM)
4362 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4363 DrawLevelElementAnimation(x, y, element);
4367 if (MovDelay[x][y]) /* element still has to wait some time */
4370 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4371 ResetGfxAnimation(x, y);
4375 if (GfxAction[x][y] != ACTION_WAITING)
4376 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4378 GfxAction[x][y] = ACTION_WAITING;
4382 if (element == EL_ROBOT ||
4384 element == EL_PACMAN ||
4386 element == EL_YAMYAM ||
4387 element == EL_DARK_YAMYAM)
4390 DrawLevelElementAnimation(x, y, element);
4392 DrawLevelElementAnimationIfNeeded(x, y, element);
4394 PlayLevelSoundAction(x, y, ACTION_WAITING);
4396 else if (element == EL_SP_ELECTRON)
4397 DrawLevelElementAnimationIfNeeded(x, y, element);
4398 else if (element == EL_DRAGON)
4401 int dir = MovDir[x][y];
4402 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4403 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4404 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4405 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4406 dir == MV_UP ? IMG_FLAMES_1_UP :
4407 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4408 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4411 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4414 GfxAction[x][y] = ACTION_ATTACKING;
4416 if (IS_PLAYER(x, y))
4417 DrawPlayerField(x, y);
4419 DrawLevelField(x, y);
4421 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4423 for (i = 1; i <= 3; i++)
4425 int xx = x + i * dx;
4426 int yy = y + i * dy;
4427 int sx = SCREENX(xx);
4428 int sy = SCREENY(yy);
4429 int flame_graphic = graphic + (i - 1);
4431 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4436 int flamed = MovingOrBlocked2Element(xx, yy);
4438 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4441 RemoveMovingField(xx, yy);
4443 Feld[xx][yy] = EL_FLAMES;
4444 if (IN_SCR_FIELD(sx, sy))
4446 DrawLevelFieldCrumbledSand(xx, yy);
4447 DrawGraphic(sx, sy, flame_graphic, frame);
4452 if (Feld[xx][yy] == EL_FLAMES)
4453 Feld[xx][yy] = EL_EMPTY;
4454 DrawLevelField(xx, yy);
4459 if (MovDelay[x][y]) /* element still has to wait some time */
4461 PlayLevelSoundAction(x, y, ACTION_WAITING);
4467 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4468 for all other elements GfxAction will be set by InitMovingField() */
4469 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4470 GfxAction[x][y] = ACTION_MOVING;
4474 /* now make next step */
4476 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4478 if (DONT_COLLIDE_WITH(element) &&
4479 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4480 !PLAYER_PROTECTED(newx, newy))
4483 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4486 /* player killed by element which is deadly when colliding with */
4488 KillHero(PLAYERINFO(newx, newy));
4493 else if ((element == EL_PENGUIN ||
4494 element == EL_ROBOT ||
4495 element == EL_SATELLITE ||
4496 element == EL_BALLOON ||
4497 IS_CUSTOM_ELEMENT(element)) &&
4498 IN_LEV_FIELD(newx, newy) &&
4499 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4502 Store[x][y] = EL_ACID;
4504 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4506 if (Feld[newx][newy] == EL_EXIT_OPEN)
4510 DrawLevelField(x, y);
4512 Feld[x][y] = EL_EMPTY;
4513 DrawLevelField(x, y);
4516 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4517 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4518 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4520 local_player->friends_still_needed--;
4521 if (!local_player->friends_still_needed &&
4522 !local_player->GameOver && AllPlayersGone)
4523 local_player->LevelSolved = local_player->GameOver = TRUE;
4527 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4529 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4530 DrawLevelField(newx, newy);
4532 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4534 else if (!IS_FREE(newx, newy))
4536 GfxAction[x][y] = ACTION_WAITING;
4538 if (IS_PLAYER(x, y))
4539 DrawPlayerField(x, y);
4541 DrawLevelField(x, y);
4545 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4547 if (IS_FOOD_PIG(Feld[newx][newy]))
4549 if (IS_MOVING(newx, newy))
4550 RemoveMovingField(newx, newy);
4553 Feld[newx][newy] = EL_EMPTY;
4554 DrawLevelField(newx, newy);
4557 PlayLevelSound(x, y, SND_PIG_DIGGING);
4559 else if (!IS_FREE(newx, newy))
4561 if (IS_PLAYER(x, y))
4562 DrawPlayerField(x, y);
4564 DrawLevelField(x, y);
4568 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4569 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4572 if (IS_FREE(newx, newy))
4574 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4577 if (IS_MOVING(newx, newy))
4578 RemoveMovingField(newx, newy);
4581 Feld[newx][newy] = EL_EMPTY;
4582 DrawLevelField(newx, newy);
4585 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4587 else if (!IS_FREE(newx, newy))
4590 if (IS_PLAYER(x, y))
4591 DrawPlayerField(x, y);
4593 DrawLevelField(x, y);
4598 RunnerVisit[x][y] = FrameCounter;
4599 PlayerVisit[x][y] /= 8; /* expire player visit path */
4601 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4603 if (!IS_FREE(newx, newy))
4605 if (IS_PLAYER(x, y))
4606 DrawPlayerField(x, y);
4608 DrawLevelField(x, y);
4614 boolean wanna_flame = !RND(10);
4615 int dx = newx - x, dy = newy - y;
4616 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4617 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4618 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4619 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4620 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4621 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4624 IS_CLASSIC_ENEMY(element1) ||
4625 IS_CLASSIC_ENEMY(element2)) &&
4626 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4627 element1 != EL_FLAMES && element2 != EL_FLAMES)
4630 ResetGfxAnimation(x, y);
4631 GfxAction[x][y] = ACTION_ATTACKING;
4634 if (IS_PLAYER(x, y))
4635 DrawPlayerField(x, y);
4637 DrawLevelField(x, y);
4639 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4641 MovDelay[x][y] = 50;
4643 Feld[newx][newy] = EL_FLAMES;
4644 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4645 Feld[newx1][newy1] = EL_FLAMES;
4646 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4647 Feld[newx2][newy2] = EL_FLAMES;
4653 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4654 Feld[newx][newy] == EL_DIAMOND)
4656 if (IS_MOVING(newx, newy))
4657 RemoveMovingField(newx, newy);
4660 Feld[newx][newy] = EL_EMPTY;
4661 DrawLevelField(newx, newy);
4664 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4666 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4667 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4669 if (AmoebaNr[newx][newy])
4671 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4672 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4673 Feld[newx][newy] == EL_BD_AMOEBA)
4674 AmoebaCnt[AmoebaNr[newx][newy]]--;
4677 if (IS_MOVING(newx, newy))
4678 RemoveMovingField(newx, newy);
4681 Feld[newx][newy] = EL_EMPTY;
4682 DrawLevelField(newx, newy);
4685 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4687 else if ((element == EL_PACMAN || element == EL_MOLE)
4688 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4690 if (AmoebaNr[newx][newy])
4692 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4693 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4694 Feld[newx][newy] == EL_BD_AMOEBA)
4695 AmoebaCnt[AmoebaNr[newx][newy]]--;
4698 if (element == EL_MOLE)
4700 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4701 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4703 ResetGfxAnimation(x, y);
4704 GfxAction[x][y] = ACTION_DIGGING;
4705 DrawLevelField(x, y);
4707 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4708 return; /* wait for shrinking amoeba */
4710 else /* element == EL_PACMAN */
4712 Feld[newx][newy] = EL_EMPTY;
4713 DrawLevelField(newx, newy);
4714 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4717 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4718 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4719 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4721 /* wait for shrinking amoeba to completely disappear */
4724 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4726 /* object was running against a wall */
4731 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4732 DrawLevelElementAnimation(x, y, element);
4734 if (element == EL_BUG ||
4735 element == EL_SPACESHIP ||
4736 element == EL_SP_SNIKSNAK)
4737 DrawLevelField(x, y);
4738 else if (element == EL_MOLE)
4739 DrawLevelField(x, y);
4740 else if (element == EL_BD_BUTTERFLY ||
4741 element == EL_BD_FIREFLY)
4742 DrawLevelElementAnimationIfNeeded(x, y, element);
4743 else if (element == EL_SATELLITE)
4744 DrawLevelElementAnimationIfNeeded(x, y, element);
4745 else if (element == EL_SP_ELECTRON)
4746 DrawLevelElementAnimationIfNeeded(x, y, element);
4749 if (DONT_TOUCH(element))
4750 TestIfBadThingTouchesHero(x, y);
4753 PlayLevelSoundAction(x, y, ACTION_WAITING);
4759 InitMovingField(x, y, MovDir[x][y]);
4761 PlayLevelSoundAction(x, y, ACTION_MOVING);
4765 ContinueMoving(x, y);
4768 void ContinueMoving(int x, int y)
4770 int element = Feld[x][y];
4771 int direction = MovDir[x][y];
4772 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4773 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4774 int newx = x + dx, newy = y + dy;
4776 int nextx = newx + dx, nexty = newy + dy;
4778 boolean pushed = Pushed[x][y];
4780 MovPos[x][y] += getElementMoveStepsize(x, y);
4782 if (pushed) /* special case: moving object pushed by player */
4783 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4785 if (ABS(MovPos[x][y]) < TILEX)
4787 DrawLevelField(x, y);
4789 return; /* element is still moving */
4792 /* element reached destination field */
4794 Feld[x][y] = EL_EMPTY;
4795 Feld[newx][newy] = element;
4796 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4798 if (element == EL_MOLE)
4800 Feld[x][y] = EL_SAND;
4802 DrawLevelFieldCrumbledSandNeighbours(x, y);
4804 else if (element == EL_QUICKSAND_FILLING)
4806 element = Feld[newx][newy] = get_next_element(element);
4807 Store[newx][newy] = Store[x][y];
4809 else if (element == EL_QUICKSAND_EMPTYING)
4811 Feld[x][y] = get_next_element(element);
4812 element = Feld[newx][newy] = Store[x][y];
4814 else if (element == EL_MAGIC_WALL_FILLING)
4816 element = Feld[newx][newy] = get_next_element(element);
4817 if (!game.magic_wall_active)
4818 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4819 Store[newx][newy] = Store[x][y];
4821 else if (element == EL_MAGIC_WALL_EMPTYING)
4823 Feld[x][y] = get_next_element(element);
4824 if (!game.magic_wall_active)
4825 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4826 element = Feld[newx][newy] = Store[x][y];
4828 else if (element == EL_BD_MAGIC_WALL_FILLING)
4830 element = Feld[newx][newy] = get_next_element(element);
4831 if (!game.magic_wall_active)
4832 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4833 Store[newx][newy] = Store[x][y];
4835 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4837 Feld[x][y] = get_next_element(element);
4838 if (!game.magic_wall_active)
4839 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4840 element = Feld[newx][newy] = Store[x][y];
4842 else if (element == EL_AMOEBA_DROPPING)
4844 Feld[x][y] = get_next_element(element);
4845 element = Feld[newx][newy] = Store[x][y];
4847 else if (element == EL_SOKOBAN_OBJECT)
4850 Feld[x][y] = Back[x][y];
4852 if (Back[newx][newy])
4853 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4855 Back[x][y] = Back[newx][newy] = 0;
4857 else if (Store[x][y] == EL_ACID)
4859 element = Feld[newx][newy] = EL_ACID;
4863 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4864 MovDelay[newx][newy] = 0;
4866 /* copy element change control values to new field */
4867 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4868 ChangePage[newx][newy] = ChangePage[x][y];
4869 Changed[newx][newy] = Changed[x][y];
4870 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4872 ChangeDelay[x][y] = 0;
4873 ChangePage[x][y] = -1;
4874 Changed[x][y] = CE_BITMASK_DEFAULT;
4875 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4877 /* copy animation control values to new field */
4878 GfxFrame[newx][newy] = GfxFrame[x][y];
4879 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4880 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4881 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4883 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4885 ResetGfxAnimation(x, y); /* reset animation values for old field */
4888 /* 2.1.1 (does not work correctly for spring) */
4889 if (!CAN_MOVE(element))
4890 MovDir[newx][newy] = 0;
4894 /* (does not work for falling objects that slide horizontally) */
4895 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4896 MovDir[newx][newy] = 0;
4899 if (!CAN_MOVE(element) ||
4900 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4901 MovDir[newx][newy] = 0;
4904 if (!CAN_MOVE(element) ||
4905 (CAN_FALL(element) && direction == MV_DOWN))
4906 GfxDir[x][y] = MovDir[newx][newy] = 0;
4911 DrawLevelField(x, y);
4912 DrawLevelField(newx, newy);
4914 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4916 /* prevent pushed element from moving on in pushed direction */
4917 if (pushed && CAN_MOVE(element) &&
4918 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4919 !(element_info[element].move_pattern & direction))
4920 TurnRound(newx, newy);
4922 if (!pushed) /* special case: moving object pushed by player */
4924 WasJustMoving[newx][newy] = 3;
4926 if (CAN_FALL(element) && direction == MV_DOWN)
4927 WasJustFalling[newx][newy] = 3;
4930 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4932 TestIfBadThingTouchesHero(newx, newy);
4933 TestIfBadThingTouchesFriend(newx, newy);
4935 if (!IS_CUSTOM_ELEMENT(element))
4936 TestIfBadThingTouchesOtherBadThing(newx, newy);
4938 else if (element == EL_PENGUIN)
4939 TestIfFriendTouchesBadThing(newx, newy);
4941 if (CAN_FALL(element) && direction == MV_DOWN &&
4942 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4946 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4950 if (ChangePage[newx][newy] != -1) /* delayed change */
4951 ChangeElement(newx, newy, ChangePage[newx][newy]);
4956 TestIfElementHitsCustomElement(newx, newy, direction);
4960 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4962 int hitting_element = Feld[newx][newy];
4964 /* !!! fix side (direction) orientation here and elsewhere !!! */
4965 CheckElementSideChange(newx, newy, hitting_element,
4966 direction, CE_HITTING_SOMETHING, -1);
4969 if (IN_LEV_FIELD(nextx, nexty))
4971 static int opposite_directions[] =
4978 int move_dir_bit = MV_DIR_BIT(direction);
4979 int opposite_direction = opposite_directions[move_dir_bit];
4980 int hitting_side = direction;
4981 int touched_side = opposite_direction;
4982 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4983 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4984 MovDir[nextx][nexty] != direction ||
4985 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4991 CheckElementSideChange(nextx, nexty, touched_element,
4992 opposite_direction, CE_HIT_BY_SOMETHING, -1);
4994 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4995 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
4997 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4999 struct ElementChangeInfo *change =
5000 &element_info[hitting_element].change_page[i];
5002 if (change->can_change &&
5003 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5004 change->sides & touched_side &&
5005 change->trigger_element == touched_element)
5007 CheckElementSideChange(newx, newy, hitting_element,
5008 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5014 if (IS_CUSTOM_ELEMENT(touched_element) &&
5015 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5017 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5019 struct ElementChangeInfo *change =
5020 &element_info[touched_element].change_page[i];
5022 if (change->can_change &&
5023 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5024 change->sides & hitting_side &&
5025 change->trigger_element == hitting_element)
5027 CheckElementSideChange(nextx, nexty, touched_element,
5028 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5039 TestIfPlayerTouchesCustomElement(newx, newy);
5040 TestIfElementTouchesCustomElement(newx, newy);
5043 int AmoebeNachbarNr(int ax, int ay)
5046 int element = Feld[ax][ay];
5048 static int xy[4][2] =
5056 for (i = 0; i < 4; i++)
5058 int x = ax + xy[i][0];
5059 int y = ay + xy[i][1];
5061 if (!IN_LEV_FIELD(x, y))
5064 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5065 group_nr = AmoebaNr[x][y];
5071 void AmoebenVereinigen(int ax, int ay)
5073 int i, x, y, xx, yy;
5074 int new_group_nr = AmoebaNr[ax][ay];
5075 static int xy[4][2] =
5083 if (new_group_nr == 0)
5086 for (i = 0; i < 4; i++)
5091 if (!IN_LEV_FIELD(x, y))
5094 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5095 Feld[x][y] == EL_BD_AMOEBA ||
5096 Feld[x][y] == EL_AMOEBA_DEAD) &&
5097 AmoebaNr[x][y] != new_group_nr)
5099 int old_group_nr = AmoebaNr[x][y];
5101 if (old_group_nr == 0)
5104 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5105 AmoebaCnt[old_group_nr] = 0;
5106 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5107 AmoebaCnt2[old_group_nr] = 0;
5109 for (yy = 0; yy < lev_fieldy; yy++)
5111 for (xx = 0; xx < lev_fieldx; xx++)
5113 if (AmoebaNr[xx][yy] == old_group_nr)
5114 AmoebaNr[xx][yy] = new_group_nr;
5121 void AmoebeUmwandeln(int ax, int ay)
5125 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5127 int group_nr = AmoebaNr[ax][ay];
5132 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5133 printf("AmoebeUmwandeln(): This should never happen!\n");
5138 for (y = 0; y < lev_fieldy; y++)
5140 for (x = 0; x < lev_fieldx; x++)
5142 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5145 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5149 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5150 SND_AMOEBA_TURNING_TO_GEM :
5151 SND_AMOEBA_TURNING_TO_ROCK));
5156 static int xy[4][2] =
5164 for (i = 0; i < 4; i++)
5169 if (!IN_LEV_FIELD(x, y))
5172 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5174 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5175 SND_AMOEBA_TURNING_TO_GEM :
5176 SND_AMOEBA_TURNING_TO_ROCK));
5183 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5186 int group_nr = AmoebaNr[ax][ay];
5187 boolean done = FALSE;
5192 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5193 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5198 for (y = 0; y < lev_fieldy; y++)
5200 for (x = 0; x < lev_fieldx; x++)
5202 if (AmoebaNr[x][y] == group_nr &&
5203 (Feld[x][y] == EL_AMOEBA_DEAD ||
5204 Feld[x][y] == EL_BD_AMOEBA ||
5205 Feld[x][y] == EL_AMOEBA_GROWING))
5208 Feld[x][y] = new_element;
5209 InitField(x, y, FALSE);
5210 DrawLevelField(x, y);
5217 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5218 SND_BD_AMOEBA_TURNING_TO_ROCK :
5219 SND_BD_AMOEBA_TURNING_TO_GEM));
5222 void AmoebeWaechst(int x, int y)
5224 static unsigned long sound_delay = 0;
5225 static unsigned long sound_delay_value = 0;
5227 if (!MovDelay[x][y]) /* start new growing cycle */
5231 if (DelayReached(&sound_delay, sound_delay_value))
5234 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5236 if (Store[x][y] == EL_BD_AMOEBA)
5237 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5239 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5241 sound_delay_value = 30;
5245 if (MovDelay[x][y]) /* wait some time before growing bigger */
5248 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5250 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5251 6 - MovDelay[x][y]);
5253 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5256 if (!MovDelay[x][y])
5258 Feld[x][y] = Store[x][y];
5260 DrawLevelField(x, y);
5265 void AmoebaDisappearing(int x, int y)
5267 static unsigned long sound_delay = 0;
5268 static unsigned long sound_delay_value = 0;
5270 if (!MovDelay[x][y]) /* start new shrinking cycle */
5274 if (DelayReached(&sound_delay, sound_delay_value))
5275 sound_delay_value = 30;
5278 if (MovDelay[x][y]) /* wait some time before shrinking */
5281 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5283 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5284 6 - MovDelay[x][y]);
5286 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5289 if (!MovDelay[x][y])
5291 Feld[x][y] = EL_EMPTY;
5292 DrawLevelField(x, y);
5294 /* don't let mole enter this field in this cycle;
5295 (give priority to objects falling to this field from above) */
5301 void AmoebeAbleger(int ax, int ay)
5304 int element = Feld[ax][ay];
5305 int graphic = el2img(element);
5306 int newax = ax, neway = ay;
5307 static int xy[4][2] =
5315 if (!level.amoeba_speed)
5317 Feld[ax][ay] = EL_AMOEBA_DEAD;
5318 DrawLevelField(ax, ay);
5322 if (IS_ANIMATED(graphic))
5323 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5325 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5326 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5328 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5331 if (MovDelay[ax][ay])
5335 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5338 int x = ax + xy[start][0];
5339 int y = ay + xy[start][1];
5341 if (!IN_LEV_FIELD(x, y))
5344 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5345 if (IS_FREE(x, y) ||
5346 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5352 if (newax == ax && neway == ay)
5355 else /* normal or "filled" (BD style) amoeba */
5358 boolean waiting_for_player = FALSE;
5360 for (i = 0; i < 4; i++)
5362 int j = (start + i) % 4;
5363 int x = ax + xy[j][0];
5364 int y = ay + xy[j][1];
5366 if (!IN_LEV_FIELD(x, y))
5369 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5370 if (IS_FREE(x, y) ||
5371 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5377 else if (IS_PLAYER(x, y))
5378 waiting_for_player = TRUE;
5381 if (newax == ax && neway == ay) /* amoeba cannot grow */
5383 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5385 Feld[ax][ay] = EL_AMOEBA_DEAD;
5386 DrawLevelField(ax, ay);
5387 AmoebaCnt[AmoebaNr[ax][ay]]--;
5389 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5391 if (element == EL_AMOEBA_FULL)
5392 AmoebeUmwandeln(ax, ay);
5393 else if (element == EL_BD_AMOEBA)
5394 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5399 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5401 /* amoeba gets larger by growing in some direction */
5403 int new_group_nr = AmoebaNr[ax][ay];
5406 if (new_group_nr == 0)
5408 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5409 printf("AmoebeAbleger(): This should never happen!\n");
5414 AmoebaNr[newax][neway] = new_group_nr;
5415 AmoebaCnt[new_group_nr]++;
5416 AmoebaCnt2[new_group_nr]++;
5418 /* if amoeba touches other amoeba(s) after growing, unify them */
5419 AmoebenVereinigen(newax, neway);
5421 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5423 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5429 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5430 (neway == lev_fieldy - 1 && newax != ax))
5432 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5433 Store[newax][neway] = element;
5435 else if (neway == ay)
5437 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5439 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5441 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5446 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5447 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5448 Store[ax][ay] = EL_AMOEBA_DROP;
5449 ContinueMoving(ax, ay);
5453 DrawLevelField(newax, neway);
5456 void Life(int ax, int ay)
5459 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5461 int element = Feld[ax][ay];
5462 int graphic = el2img(element);
5463 boolean changed = FALSE;
5465 if (IS_ANIMATED(graphic))
5466 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5471 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5472 MovDelay[ax][ay] = life_time;
5474 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5477 if (MovDelay[ax][ay])
5481 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5483 int xx = ax+x1, yy = ay+y1;
5486 if (!IN_LEV_FIELD(xx, yy))
5489 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5491 int x = xx+x2, y = yy+y2;
5493 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5496 if (((Feld[x][y] == element ||
5497 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5499 (IS_FREE(x, y) && Stop[x][y]))
5503 if (xx == ax && yy == ay) /* field in the middle */
5505 if (nachbarn < life[0] || nachbarn > life[1])
5507 Feld[xx][yy] = EL_EMPTY;
5509 DrawLevelField(xx, yy);
5510 Stop[xx][yy] = TRUE;
5514 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5515 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5516 { /* free border field */
5517 if (nachbarn >= life[2] && nachbarn <= life[3])
5519 Feld[xx][yy] = element;
5520 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5522 DrawLevelField(xx, yy);
5523 Stop[xx][yy] = TRUE;
5530 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5531 SND_GAME_OF_LIFE_GROWING);
5534 static void InitRobotWheel(int x, int y)
5536 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5539 static void RunRobotWheel(int x, int y)
5541 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5544 static void StopRobotWheel(int x, int y)
5546 if (ZX == x && ZY == y)
5550 static void InitTimegateWheel(int x, int y)
5552 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5555 static void RunTimegateWheel(int x, int y)
5557 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5560 void CheckExit(int x, int y)
5562 if (local_player->gems_still_needed > 0 ||
5563 local_player->sokobanfields_still_needed > 0 ||
5564 local_player->lights_still_needed > 0)
5566 int element = Feld[x][y];
5567 int graphic = el2img(element);
5569 if (IS_ANIMATED(graphic))
5570 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5575 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5578 Feld[x][y] = EL_EXIT_OPENING;
5580 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5583 void CheckExitSP(int x, int y)
5585 if (local_player->gems_still_needed > 0)
5587 int element = Feld[x][y];
5588 int graphic = el2img(element);
5590 if (IS_ANIMATED(graphic))
5591 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5596 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5599 Feld[x][y] = EL_SP_EXIT_OPENING;
5601 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5604 static void CloseAllOpenTimegates()
5608 for (y = 0; y < lev_fieldy; y++)
5610 for (x = 0; x < lev_fieldx; x++)
5612 int element = Feld[x][y];
5614 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5616 Feld[x][y] = EL_TIMEGATE_CLOSING;
5618 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5620 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5627 void EdelsteinFunkeln(int x, int y)
5629 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5632 if (Feld[x][y] == EL_BD_DIAMOND)
5635 if (MovDelay[x][y] == 0) /* next animation frame */
5636 MovDelay[x][y] = 11 * !SimpleRND(500);
5638 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5642 if (setup.direct_draw && MovDelay[x][y])
5643 SetDrawtoField(DRAW_BUFFERED);
5645 DrawLevelElementAnimation(x, y, Feld[x][y]);
5647 if (MovDelay[x][y] != 0)
5649 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5650 10 - MovDelay[x][y]);
5652 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5654 if (setup.direct_draw)
5658 dest_x = FX + SCREENX(x) * TILEX;
5659 dest_y = FY + SCREENY(y) * TILEY;
5661 BlitBitmap(drawto_field, window,
5662 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5663 SetDrawtoField(DRAW_DIRECT);
5669 void MauerWaechst(int x, int y)
5673 if (!MovDelay[x][y]) /* next animation frame */
5674 MovDelay[x][y] = 3 * delay;
5676 if (MovDelay[x][y]) /* wait some time before next frame */
5680 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5682 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5683 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5685 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5688 if (!MovDelay[x][y])
5690 if (MovDir[x][y] == MV_LEFT)
5692 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5693 DrawLevelField(x - 1, y);
5695 else if (MovDir[x][y] == MV_RIGHT)
5697 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5698 DrawLevelField(x + 1, y);
5700 else if (MovDir[x][y] == MV_UP)
5702 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5703 DrawLevelField(x, y - 1);
5707 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5708 DrawLevelField(x, y + 1);
5711 Feld[x][y] = Store[x][y];
5713 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5714 DrawLevelField(x, y);
5719 void MauerAbleger(int ax, int ay)
5721 int element = Feld[ax][ay];
5722 int graphic = el2img(element);
5723 boolean oben_frei = FALSE, unten_frei = FALSE;
5724 boolean links_frei = FALSE, rechts_frei = FALSE;
5725 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5726 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5727 boolean new_wall = FALSE;
5729 if (IS_ANIMATED(graphic))
5730 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5732 if (!MovDelay[ax][ay]) /* start building new wall */
5733 MovDelay[ax][ay] = 6;
5735 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5738 if (MovDelay[ax][ay])
5742 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5744 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5746 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5748 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5751 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5752 element == EL_EXPANDABLE_WALL_ANY)
5756 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5757 Store[ax][ay-1] = element;
5758 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5759 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5760 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5761 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5766 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5767 Store[ax][ay+1] = element;
5768 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5769 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5770 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5771 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5776 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5777 element == EL_EXPANDABLE_WALL_ANY ||
5778 element == EL_EXPANDABLE_WALL)
5782 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5783 Store[ax-1][ay] = element;
5784 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5785 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5786 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5787 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5793 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5794 Store[ax+1][ay] = element;
5795 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5796 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5797 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5798 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5803 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5804 DrawLevelField(ax, ay);
5806 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5808 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5809 unten_massiv = TRUE;
5810 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5811 links_massiv = TRUE;
5812 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5813 rechts_massiv = TRUE;
5815 if (((oben_massiv && unten_massiv) ||
5816 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5817 element == EL_EXPANDABLE_WALL) &&
5818 ((links_massiv && rechts_massiv) ||
5819 element == EL_EXPANDABLE_WALL_VERTICAL))
5820 Feld[ax][ay] = EL_WALL;
5824 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5826 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5830 void CheckForDragon(int x, int y)
5833 boolean dragon_found = FALSE;
5834 static int xy[4][2] =
5842 for (i = 0; i < 4; i++)
5844 for (j = 0; j < 4; j++)
5846 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5848 if (IN_LEV_FIELD(xx, yy) &&
5849 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5851 if (Feld[xx][yy] == EL_DRAGON)
5852 dragon_found = TRUE;
5861 for (i = 0; i < 4; i++)
5863 for (j = 0; j < 3; j++)
5865 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5867 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5869 Feld[xx][yy] = EL_EMPTY;
5870 DrawLevelField(xx, yy);
5879 static void InitBuggyBase(int x, int y)
5881 int element = Feld[x][y];
5882 int activating_delay = FRAMES_PER_SECOND / 4;
5885 (element == EL_SP_BUGGY_BASE ?
5886 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5887 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5889 element == EL_SP_BUGGY_BASE_ACTIVE ?
5890 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5893 static void WarnBuggyBase(int x, int y)
5896 static int xy[4][2] =
5904 for (i = 0; i < 4; i++)
5906 int xx = x + xy[i][0], yy = y + xy[i][1];
5908 if (IS_PLAYER(xx, yy))
5910 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5917 static void InitTrap(int x, int y)
5919 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5922 static void ActivateTrap(int x, int y)
5924 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5927 static void ChangeActiveTrap(int x, int y)
5929 int graphic = IMG_TRAP_ACTIVE;
5931 /* if new animation frame was drawn, correct crumbled sand border */
5932 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5933 DrawLevelFieldCrumbledSand(x, y);
5936 static void ChangeElementNowExt(int x, int y, int target_element)
5938 /* check if element under player changes from accessible to unaccessible
5939 (needed for special case of dropping element which then changes) */
5940 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5941 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5948 Feld[x][y] = target_element;
5950 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5952 ResetGfxAnimation(x, y);
5953 ResetRandomAnimationValue(x, y);
5955 InitField(x, y, FALSE);
5956 if (CAN_MOVE(Feld[x][y]))
5959 DrawLevelField(x, y);
5961 if (GFX_CRUMBLED(Feld[x][y]))
5962 DrawLevelFieldCrumbledSandNeighbours(x, y);
5964 TestIfBadThingTouchesHero(x, y);
5965 TestIfPlayerTouchesCustomElement(x, y);
5966 TestIfElementTouchesCustomElement(x, y);
5968 if (ELEM_IS_PLAYER(target_element))
5969 RelocatePlayer(x, y, target_element);
5972 static boolean ChangeElementNow(int x, int y, int element, int page)
5974 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5976 /* always use default change event to prevent running into a loop */
5977 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5978 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5980 /* do not change already changed elements with same change event */
5982 if (Changed[x][y] & ChangeEvent[x][y])
5989 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5991 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5993 if (change->explode)
6000 if (change->use_content)
6002 boolean complete_change = TRUE;
6003 boolean can_change[3][3];
6006 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6008 boolean half_destructible;
6009 int ex = x + xx - 1;
6010 int ey = y + yy - 1;
6013 can_change[xx][yy] = TRUE;
6015 if (ex == x && ey == y) /* do not check changing element itself */
6018 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6020 can_change[xx][yy] = FALSE; /* do not change empty borders */
6025 if (!IN_LEV_FIELD(ex, ey))
6027 can_change[xx][yy] = FALSE;
6028 complete_change = FALSE;
6035 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6036 e = MovingOrBlocked2Element(ex, ey);
6038 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6040 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6041 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6042 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6044 can_change[xx][yy] = FALSE;
6045 complete_change = FALSE;
6049 if (!change->only_complete || complete_change)
6051 boolean something_has_changed = FALSE;
6053 if (change->only_complete && change->use_random_change &&
6054 RND(100) < change->random)
6057 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6059 int ex = x + xx - 1;
6060 int ey = y + yy - 1;
6062 if (can_change[xx][yy] && (!change->use_random_change ||
6063 RND(100) < change->random))
6065 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6066 RemoveMovingField(ex, ey);
6068 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6070 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6072 something_has_changed = TRUE;
6074 /* for symmetry reasons, freeze newly created border elements */
6075 if (ex != x || ey != y)
6076 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6080 if (something_has_changed)
6081 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6086 ChangeElementNowExt(x, y, change->target_element);
6088 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6094 static void ChangeElement(int x, int y, int page)
6096 int element = MovingOrBlocked2Element(x, y);
6097 struct ElementInfo *ei = &element_info[element];
6098 struct ElementChangeInfo *change = &ei->change_page[page];
6102 if (!CAN_CHANGE(element))
6105 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6106 x, y, element, element_info[element].token_name);
6107 printf("ChangeElement(): This should never happen!\n");
6113 if (ChangeDelay[x][y] == 0) /* initialize element change */
6115 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6116 RND(change->delay_random * change->delay_frames)) + 1;
6118 ResetGfxAnimation(x, y);
6119 ResetRandomAnimationValue(x, y);
6121 if (change->pre_change_function)
6122 change->pre_change_function(x, y);
6125 ChangeDelay[x][y]--;
6127 if (ChangeDelay[x][y] != 0) /* continue element change */
6129 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6131 if (IS_ANIMATED(graphic))
6132 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6134 if (change->change_function)
6135 change->change_function(x, y);
6137 else /* finish element change */
6139 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6141 page = ChangePage[x][y];
6142 ChangePage[x][y] = -1;
6145 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6147 ChangeDelay[x][y] = 1; /* try change after next move step */
6148 ChangePage[x][y] = page; /* remember page to use for change */
6153 if (ChangeElementNow(x, y, element, page))
6155 if (change->post_change_function)
6156 change->post_change_function(x, y);
6161 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6162 int trigger_element,
6168 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6171 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6173 int element = EL_CUSTOM_START + i;
6175 boolean change_element = FALSE;
6178 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6181 for (j = 0; j < element_info[element].num_change_pages; j++)
6183 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6185 if (change->can_change &&
6187 change->events & CH_EVENT_BIT(trigger_event) &&
6189 change->sides & trigger_side &&
6190 change->trigger_element == trigger_element)
6193 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6194 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6195 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6198 change_element = TRUE;
6205 if (!change_element)
6208 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6211 if (x == lx && y == ly) /* do not change trigger element itself */
6215 if (Feld[x][y] == element)
6217 ChangeDelay[x][y] = 1;
6218 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6219 ChangeElement(x, y, page);
6227 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6230 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6234 static boolean CheckElementSideChange(int x, int y, int element, int side,
6235 int trigger_event, int page)
6237 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6240 if (Feld[x][y] == EL_BLOCKED)
6242 Blocked2Moving(x, y, &x, &y);
6243 element = Feld[x][y];
6247 page = element_info[element].event_page_nr[trigger_event];
6249 if (!(element_info[element].change_page[page].sides & side))
6252 ChangeDelay[x][y] = 1;
6253 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6254 ChangeElement(x, y, page);
6259 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6261 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6264 static void PlayPlayerSound(struct PlayerInfo *player)
6266 int jx = player->jx, jy = player->jy;
6267 int element = player->element_nr;
6268 int last_action = player->last_action_waiting;
6269 int action = player->action_waiting;
6271 if (player->is_waiting)
6273 if (action != last_action)
6274 PlayLevelSoundElementAction(jx, jy, element, action);
6276 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6280 if (action != last_action)
6281 StopSound(element_info[element].sound[last_action]);
6283 if (last_action == ACTION_SLEEPING)
6284 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6288 static void PlayAllPlayersSound()
6292 for (i = 0; i < MAX_PLAYERS; i++)
6293 if (stored_player[i].active)
6294 PlayPlayerSound(&stored_player[i]);
6297 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6299 boolean last_waiting = player->is_waiting;
6300 int move_dir = player->MovDir;
6302 player->last_action_waiting = player->action_waiting;
6306 if (!last_waiting) /* not waiting -> waiting */
6308 player->is_waiting = TRUE;
6310 player->frame_counter_bored =
6312 game.player_boring_delay_fixed +
6313 SimpleRND(game.player_boring_delay_random);
6314 player->frame_counter_sleeping =
6316 game.player_sleeping_delay_fixed +
6317 SimpleRND(game.player_sleeping_delay_random);
6319 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6322 if (game.player_sleeping_delay_fixed +
6323 game.player_sleeping_delay_random > 0 &&
6324 player->anim_delay_counter == 0 &&
6325 player->post_delay_counter == 0 &&
6326 FrameCounter >= player->frame_counter_sleeping)
6327 player->is_sleeping = TRUE;
6328 else if (game.player_boring_delay_fixed +
6329 game.player_boring_delay_random > 0 &&
6330 FrameCounter >= player->frame_counter_bored)
6331 player->is_bored = TRUE;
6333 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6334 player->is_bored ? ACTION_BORING :
6337 if (player->is_sleeping)
6339 if (player->num_special_action_sleeping > 0)
6341 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6343 int last_special_action = player->special_action_sleeping;
6344 int num_special_action = player->num_special_action_sleeping;
6345 int special_action =
6346 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6347 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6348 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6349 last_special_action + 1 : ACTION_SLEEPING);
6350 int special_graphic =
6351 el_act_dir2img(player->element_nr, special_action, move_dir);
6353 player->anim_delay_counter =
6354 graphic_info[special_graphic].anim_delay_fixed +
6355 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6356 player->post_delay_counter =
6357 graphic_info[special_graphic].post_delay_fixed +
6358 SimpleRND(graphic_info[special_graphic].post_delay_random);
6360 player->special_action_sleeping = special_action;
6363 if (player->anim_delay_counter > 0)
6365 player->action_waiting = player->special_action_sleeping;
6366 player->anim_delay_counter--;
6368 else if (player->post_delay_counter > 0)
6370 player->post_delay_counter--;
6374 else if (player->is_bored)
6376 if (player->num_special_action_bored > 0)
6378 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6380 int special_action =
6381 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6382 int special_graphic =
6383 el_act_dir2img(player->element_nr, special_action, move_dir);
6385 player->anim_delay_counter =
6386 graphic_info[special_graphic].anim_delay_fixed +
6387 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6388 player->post_delay_counter =
6389 graphic_info[special_graphic].post_delay_fixed +
6390 SimpleRND(graphic_info[special_graphic].post_delay_random);
6392 player->special_action_bored = special_action;
6395 if (player->anim_delay_counter > 0)
6397 player->action_waiting = player->special_action_bored;
6398 player->anim_delay_counter--;
6400 else if (player->post_delay_counter > 0)
6402 player->post_delay_counter--;
6407 else if (last_waiting) /* waiting -> not waiting */
6409 player->is_waiting = FALSE;
6410 player->is_bored = FALSE;
6411 player->is_sleeping = FALSE;
6413 player->frame_counter_bored = -1;
6414 player->frame_counter_sleeping = -1;
6416 player->anim_delay_counter = 0;
6417 player->post_delay_counter = 0;
6419 player->action_waiting = ACTION_DEFAULT;
6421 player->special_action_bored = ACTION_DEFAULT;
6422 player->special_action_sleeping = ACTION_DEFAULT;
6427 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6430 static byte stored_player_action[MAX_PLAYERS];
6431 static int num_stored_actions = 0;
6433 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6434 int left = player_action & JOY_LEFT;
6435 int right = player_action & JOY_RIGHT;
6436 int up = player_action & JOY_UP;
6437 int down = player_action & JOY_DOWN;
6438 int button1 = player_action & JOY_BUTTON_1;
6439 int button2 = player_action & JOY_BUTTON_2;
6440 int dx = (left ? -1 : right ? 1 : 0);
6441 int dy = (up ? -1 : down ? 1 : 0);
6444 stored_player_action[player->index_nr] = 0;
6445 num_stored_actions++;
6449 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6452 if (!player->active || tape.pausing)
6458 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6462 snapped = SnapField(player, dx, dy);
6466 dropped = DropElement(player);
6468 moved = MovePlayer(player, dx, dy);
6471 if (tape.single_step && tape.recording && !tape.pausing)
6473 if (button1 || (dropped && !moved))
6475 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6476 SnapField(player, 0, 0); /* stop snapping */
6480 SetPlayerWaiting(player, FALSE);
6483 return player_action;
6485 stored_player_action[player->index_nr] = player_action;
6491 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6494 /* no actions for this player (no input at player's configured device) */
6496 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6497 SnapField(player, 0, 0);
6498 CheckGravityMovement(player);
6500 if (player->MovPos == 0)
6501 SetPlayerWaiting(player, TRUE);
6503 if (player->MovPos == 0) /* needed for tape.playing */
6504 player->is_moving = FALSE;
6510 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6512 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6514 TapeRecordAction(stored_player_action);
6515 num_stored_actions = 0;
6522 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6524 static byte stored_player_action[MAX_PLAYERS];
6525 static int num_stored_actions = 0;
6526 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6527 int left = player_action & JOY_LEFT;
6528 int right = player_action & JOY_RIGHT;
6529 int up = player_action & JOY_UP;
6530 int down = player_action & JOY_DOWN;
6531 int button1 = player_action & JOY_BUTTON_1;
6532 int button2 = player_action & JOY_BUTTON_2;
6533 int dx = (left ? -1 : right ? 1 : 0);
6534 int dy = (up ? -1 : down ? 1 : 0);
6536 stored_player_action[player->index_nr] = 0;
6537 num_stored_actions++;
6539 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6541 if (!player->active || tape.pausing)
6546 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6549 snapped = SnapField(player, dx, dy);
6553 dropped = DropElement(player);
6555 moved = MovePlayer(player, dx, dy);
6558 if (tape.single_step && tape.recording && !tape.pausing)
6560 if (button1 || (dropped && !moved))
6562 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6563 SnapField(player, 0, 0); /* stop snapping */
6567 stored_player_action[player->index_nr] = player_action;
6571 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6573 /* no actions for this player (no input at player's configured device) */
6575 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6576 SnapField(player, 0, 0);
6577 CheckGravityMovement(player);
6579 if (player->MovPos == 0)
6580 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6582 if (player->MovPos == 0) /* needed for tape.playing */
6583 player->is_moving = FALSE;
6586 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6588 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6590 TapeRecordAction(stored_player_action);
6591 num_stored_actions = 0;
6598 static unsigned long action_delay = 0;
6599 unsigned long action_delay_value;
6600 int magic_wall_x = 0, magic_wall_y = 0;
6601 int i, x, y, element, graphic;
6602 byte *recorded_player_action;
6603 byte summarized_player_action = 0;
6605 byte tape_action[MAX_PLAYERS];
6608 if (game_status != GAME_MODE_PLAYING)
6611 action_delay_value =
6612 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6614 if (tape.playing && tape.index_search && !tape.pausing)
6615 action_delay_value = 0;
6617 /* ---------- main game synchronization point ---------- */
6619 WaitUntilDelayReached(&action_delay, action_delay_value);
6621 if (network_playing && !network_player_action_received)
6625 printf("DEBUG: try to get network player actions in time\n");
6629 #if defined(PLATFORM_UNIX)
6630 /* last chance to get network player actions without main loop delay */
6634 if (game_status != GAME_MODE_PLAYING)
6637 if (!network_player_action_received)
6641 printf("DEBUG: failed to get network player actions in time\n");
6652 printf("::: getting new tape action [%d]\n", FrameCounter);
6655 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6657 for (i = 0; i < MAX_PLAYERS; i++)
6659 summarized_player_action |= stored_player[i].action;
6661 if (!network_playing)
6662 stored_player[i].effective_action = stored_player[i].action;
6665 #if defined(PLATFORM_UNIX)
6666 if (network_playing)
6667 SendToServer_MovePlayer(summarized_player_action);
6670 if (!options.network && !setup.team_mode)
6671 local_player->effective_action = summarized_player_action;
6673 for (i = 0; i < MAX_PLAYERS; i++)
6675 int actual_player_action = stored_player[i].effective_action;
6677 if (stored_player[i].programmed_action)
6678 actual_player_action = stored_player[i].programmed_action;
6680 if (recorded_player_action)
6681 actual_player_action = recorded_player_action[i];
6683 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6685 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6686 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6688 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6693 TapeRecordAction(tape_action);
6696 network_player_action_received = FALSE;
6698 ScrollScreen(NULL, SCROLL_GO_ON);
6704 for (i = 0; i < MAX_PLAYERS; i++)
6705 stored_player[i].Frame++;
6709 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6711 for (i = 0; i < MAX_PLAYERS; i++)
6713 struct PlayerInfo *player = &stored_player[i];
6717 if (player->active && player->is_pushing && player->is_moving &&
6720 ContinueMoving(x, y);
6722 /* continue moving after pushing (this is actually a bug) */
6723 if (!IS_MOVING(x, y))
6732 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6734 Changed[x][y] = CE_BITMASK_DEFAULT;
6735 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6738 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6740 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6741 printf("GameActions(): This should never happen!\n");
6743 ChangePage[x][y] = -1;
6748 if (WasJustMoving[x][y] > 0)
6749 WasJustMoving[x][y]--;
6750 if (WasJustFalling[x][y] > 0)
6751 WasJustFalling[x][y]--;
6756 /* reset finished pushing action (not done in ContinueMoving() to allow
6757 continous pushing animation for elements with zero push delay) */
6758 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6760 ResetGfxAnimation(x, y);
6761 DrawLevelField(x, y);
6766 if (IS_BLOCKED(x, y))
6770 Blocked2Moving(x, y, &oldx, &oldy);
6771 if (!IS_MOVING(oldx, oldy))
6773 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6774 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6775 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6776 printf("GameActions(): This should never happen!\n");
6782 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6784 element = Feld[x][y];
6786 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6788 graphic = el2img(element);
6794 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6796 element = graphic = 0;
6800 if (graphic_info[graphic].anim_global_sync)
6801 GfxFrame[x][y] = FrameCounter;
6803 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6804 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6805 ResetRandomAnimationValue(x, y);
6807 SetRandomAnimationValue(x, y);
6810 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6813 if (IS_INACTIVE(element))
6815 if (IS_ANIMATED(graphic))
6816 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6822 /* this may take place after moving, so 'element' may have changed */
6824 if (IS_CHANGING(x, y))
6826 if (IS_CHANGING(x, y) &&
6827 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6831 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6832 element_info[element].event_page_nr[CE_DELAY]);
6834 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6837 element = Feld[x][y];
6838 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6842 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6847 element = Feld[x][y];
6848 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6850 if (element == EL_MOLE)
6851 printf("::: %d, %d, %d [%d]\n",
6852 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6856 if (element == EL_YAMYAM)
6857 printf("::: %d, %d, %d\n",
6858 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6862 if (IS_ANIMATED(graphic) &&
6866 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6869 if (element == EL_BUG)
6870 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6874 if (element == EL_MOLE)
6875 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6879 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6880 EdelsteinFunkeln(x, y);
6882 else if ((element == EL_ACID ||
6883 element == EL_EXIT_OPEN ||
6884 element == EL_SP_EXIT_OPEN ||
6885 element == EL_SP_TERMINAL ||
6886 element == EL_SP_TERMINAL_ACTIVE ||
6887 element == EL_EXTRA_TIME ||
6888 element == EL_SHIELD_NORMAL ||
6889 element == EL_SHIELD_DEADLY) &&
6890 IS_ANIMATED(graphic))
6891 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6892 else if (IS_MOVING(x, y))
6893 ContinueMoving(x, y);
6894 else if (IS_ACTIVE_BOMB(element))
6895 CheckDynamite(x, y);
6897 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6898 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6900 else if (element == EL_AMOEBA_GROWING)
6901 AmoebeWaechst(x, y);
6902 else if (element == EL_AMOEBA_SHRINKING)
6903 AmoebaDisappearing(x, y);
6905 #if !USE_NEW_AMOEBA_CODE
6906 else if (IS_AMOEBALIVE(element))
6907 AmoebeAbleger(x, y);
6910 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6912 else if (element == EL_EXIT_CLOSED)
6914 else if (element == EL_SP_EXIT_CLOSED)
6916 else if (element == EL_EXPANDABLE_WALL_GROWING)
6918 else if (element == EL_EXPANDABLE_WALL ||
6919 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6920 element == EL_EXPANDABLE_WALL_VERTICAL ||
6921 element == EL_EXPANDABLE_WALL_ANY)
6923 else if (element == EL_FLAMES)
6924 CheckForDragon(x, y);
6926 else if (IS_AUTO_CHANGING(element))
6927 ChangeElement(x, y);
6929 else if (element == EL_EXPLOSION)
6930 ; /* drawing of correct explosion animation is handled separately */
6931 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6932 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6935 /* this may take place after moving, so 'element' may have changed */
6936 if (IS_AUTO_CHANGING(Feld[x][y]))
6937 ChangeElement(x, y);
6940 if (IS_BELT_ACTIVE(element))
6941 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6943 if (game.magic_wall_active)
6945 int jx = local_player->jx, jy = local_player->jy;
6947 /* play the element sound at the position nearest to the player */
6948 if ((element == EL_MAGIC_WALL_FULL ||
6949 element == EL_MAGIC_WALL_ACTIVE ||
6950 element == EL_MAGIC_WALL_EMPTYING ||
6951 element == EL_BD_MAGIC_WALL_FULL ||
6952 element == EL_BD_MAGIC_WALL_ACTIVE ||
6953 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6954 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6962 #if USE_NEW_AMOEBA_CODE
6963 /* new experimental amoeba growth stuff */
6965 if (!(FrameCounter % 8))
6968 static unsigned long random = 1684108901;
6970 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6973 x = (random >> 10) % lev_fieldx;
6974 y = (random >> 20) % lev_fieldy;
6976 x = RND(lev_fieldx);
6977 y = RND(lev_fieldy);
6979 element = Feld[x][y];
6981 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6982 if (!IS_PLAYER(x,y) &&
6983 (element == EL_EMPTY ||
6984 element == EL_SAND ||
6985 element == EL_QUICKSAND_EMPTY ||
6986 element == EL_ACID_SPLASH_LEFT ||
6987 element == EL_ACID_SPLASH_RIGHT))
6989 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6990 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6991 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6992 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6993 Feld[x][y] = EL_AMOEBA_DROP;
6996 random = random * 129 + 1;
7002 if (game.explosions_delayed)
7005 game.explosions_delayed = FALSE;
7007 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7009 element = Feld[x][y];
7011 if (ExplodeField[x][y])
7012 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7013 else if (element == EL_EXPLOSION)
7014 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7016 ExplodeField[x][y] = EX_NO_EXPLOSION;
7019 game.explosions_delayed = TRUE;
7022 if (game.magic_wall_active)
7024 if (!(game.magic_wall_time_left % 4))
7026 int element = Feld[magic_wall_x][magic_wall_y];
7028 if (element == EL_BD_MAGIC_WALL_FULL ||
7029 element == EL_BD_MAGIC_WALL_ACTIVE ||
7030 element == EL_BD_MAGIC_WALL_EMPTYING)
7031 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7033 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7036 if (game.magic_wall_time_left > 0)
7038 game.magic_wall_time_left--;
7039 if (!game.magic_wall_time_left)
7041 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7043 element = Feld[x][y];
7045 if (element == EL_MAGIC_WALL_ACTIVE ||
7046 element == EL_MAGIC_WALL_FULL)
7048 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7049 DrawLevelField(x, y);
7051 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7052 element == EL_BD_MAGIC_WALL_FULL)
7054 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7055 DrawLevelField(x, y);
7059 game.magic_wall_active = FALSE;
7064 if (game.light_time_left > 0)
7066 game.light_time_left--;
7068 if (game.light_time_left == 0)
7069 RedrawAllLightSwitchesAndInvisibleElements();
7072 if (game.timegate_time_left > 0)
7074 game.timegate_time_left--;
7076 if (game.timegate_time_left == 0)
7077 CloseAllOpenTimegates();
7080 for (i = 0; i < MAX_PLAYERS; i++)
7082 struct PlayerInfo *player = &stored_player[i];
7084 if (SHIELD_ON(player))
7086 if (player->shield_deadly_time_left)
7087 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7088 else if (player->shield_normal_time_left)
7089 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7093 if (TimeFrames >= FRAMES_PER_SECOND)
7098 for (i = 0; i < MAX_PLAYERS; i++)
7100 struct PlayerInfo *player = &stored_player[i];
7102 if (SHIELD_ON(player))
7104 player->shield_normal_time_left--;
7106 if (player->shield_deadly_time_left > 0)
7107 player->shield_deadly_time_left--;
7111 if (tape.recording || tape.playing)
7112 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7118 if (TimeLeft <= 10 && setup.time_limit)
7119 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7121 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7123 if (!TimeLeft && setup.time_limit)
7124 for (i = 0; i < MAX_PLAYERS; i++)
7125 KillHero(&stored_player[i]);
7127 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7128 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7132 PlayAllPlayersSound();
7134 if (options.debug) /* calculate frames per second */
7136 static unsigned long fps_counter = 0;
7137 static int fps_frames = 0;
7138 unsigned long fps_delay_ms = Counter() - fps_counter;
7142 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7144 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7147 fps_counter = Counter();
7150 redraw_mask |= REDRAW_FPS;
7154 if (stored_player[0].jx != stored_player[0].last_jx ||
7155 stored_player[0].jy != stored_player[0].last_jy)
7156 printf("::: %d, %d, %d, %d, %d\n",
7157 stored_player[0].MovDir,
7158 stored_player[0].MovPos,
7159 stored_player[0].GfxPos,
7160 stored_player[0].Frame,
7161 stored_player[0].StepFrame);
7168 for (i = 0; i < MAX_PLAYERS; i++)
7171 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7173 stored_player[i].Frame += move_frames;
7175 if (stored_player[i].MovPos != 0)
7176 stored_player[i].StepFrame += move_frames;
7178 if (stored_player[i].drop_delay > 0)
7179 stored_player[i].drop_delay--;
7184 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7186 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7188 local_player->show_envelope = 0;
7193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7195 int min_x = x, min_y = y, max_x = x, max_y = y;
7198 for (i = 0; i < MAX_PLAYERS; i++)
7200 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7202 if (!stored_player[i].active || &stored_player[i] == player)
7205 min_x = MIN(min_x, jx);
7206 min_y = MIN(min_y, jy);
7207 max_x = MAX(max_x, jx);
7208 max_y = MAX(max_y, jy);
7211 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7214 static boolean AllPlayersInVisibleScreen()
7218 for (i = 0; i < MAX_PLAYERS; i++)
7220 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7222 if (!stored_player[i].active)
7225 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7232 void ScrollLevel(int dx, int dy)
7234 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7237 BlitBitmap(drawto_field, drawto_field,
7238 FX + TILEX * (dx == -1) - softscroll_offset,
7239 FY + TILEY * (dy == -1) - softscroll_offset,
7240 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7241 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7242 FX + TILEX * (dx == 1) - softscroll_offset,
7243 FY + TILEY * (dy == 1) - softscroll_offset);
7247 x = (dx == 1 ? BX1 : BX2);
7248 for (y = BY1; y <= BY2; y++)
7249 DrawScreenField(x, y);
7254 y = (dy == 1 ? BY1 : BY2);
7255 for (x = BX1; x <= BX2; x++)
7256 DrawScreenField(x, y);
7259 redraw_mask |= REDRAW_FIELD;
7262 static void CheckGravityMovement(struct PlayerInfo *player)
7264 if (game.gravity && !player->programmed_action)
7266 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7267 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7269 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7270 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7271 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7272 int jx = player->jx, jy = player->jy;
7273 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7274 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7275 int new_jx = jx + dx, new_jy = jy + dy;
7276 boolean field_under_player_is_free =
7277 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7278 boolean player_is_moving_to_valid_field =
7279 (IN_LEV_FIELD(new_jx, new_jy) &&
7280 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7281 Feld[new_jx][new_jy] == EL_SAND));
7282 /* !!! extend EL_SAND to anything diggable !!! */
7284 if (field_under_player_is_free &&
7285 !player_is_moving_to_valid_field &&
7286 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7287 player->programmed_action = MV_DOWN;
7293 -----------------------------------------------------------------------------
7294 dx, dy: direction (non-diagonal) to try to move the player to
7295 real_dx, real_dy: direction as read from input device (can be diagonal)
7298 boolean MovePlayerOneStep(struct PlayerInfo *player,
7299 int dx, int dy, int real_dx, int real_dy)
7302 static int change_sides[4][2] =
7304 /* enter side leave side */
7305 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7306 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7307 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7308 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7310 int move_direction = (dx == -1 ? MV_LEFT :
7311 dx == +1 ? MV_RIGHT :
7313 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7314 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7315 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7317 int jx = player->jx, jy = player->jy;
7318 int new_jx = jx + dx, new_jy = jy + dy;
7322 if (!player->active || (!dx && !dy))
7323 return MF_NO_ACTION;
7325 player->MovDir = (dx < 0 ? MV_LEFT :
7328 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7330 if (!IN_LEV_FIELD(new_jx, new_jy))
7331 return MF_NO_ACTION;
7333 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7334 return MF_NO_ACTION;
7337 element = MovingOrBlocked2Element(new_jx, new_jy);
7339 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7342 if (DONT_RUN_INTO(element))
7344 if (element == EL_ACID && dx == 0 && dy == 1)
7347 Feld[jx][jy] = EL_PLAYER_1;
7348 InitMovingField(jx, jy, MV_DOWN);
7349 Store[jx][jy] = EL_ACID;
7350 ContinueMoving(jx, jy);
7354 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7359 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7360 if (can_move != MF_MOVING)
7363 /* check if DigField() has caused relocation of the player */
7364 if (player->jx != jx || player->jy != jy)
7365 return MF_NO_ACTION;
7367 StorePlayer[jx][jy] = 0;
7368 player->last_jx = jx;
7369 player->last_jy = jy;
7370 player->jx = new_jx;
7371 player->jy = new_jy;
7372 StorePlayer[new_jx][new_jy] = player->element_nr;
7375 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7377 player->step_counter++;
7379 player->drop_delay = 0;
7381 PlayerVisit[jx][jy] = FrameCounter;
7383 ScrollPlayer(player, SCROLL_INIT);
7386 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7388 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7389 CE_OTHER_GETS_LEFT);
7390 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7391 CE_LEFT_BY_PLAYER, -1);
7394 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7396 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7397 enter_side, CE_OTHER_GETS_ENTERED);
7398 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7399 CE_ENTERED_BY_PLAYER, -1);
7406 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7408 int jx = player->jx, jy = player->jy;
7409 int old_jx = jx, old_jy = jy;
7410 int moved = MF_NO_ACTION;
7412 if (!player->active || (!dx && !dy))
7416 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7420 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7421 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7425 /* remove the last programmed player action */
7426 player->programmed_action = 0;
7430 /* should only happen if pre-1.2 tape recordings are played */
7431 /* this is only for backward compatibility */
7433 int original_move_delay_value = player->move_delay_value;
7436 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7440 /* scroll remaining steps with finest movement resolution */
7441 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7443 while (player->MovPos)
7445 ScrollPlayer(player, SCROLL_GO_ON);
7446 ScrollScreen(NULL, SCROLL_GO_ON);
7452 player->move_delay_value = original_move_delay_value;
7455 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7457 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7458 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7462 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7463 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7469 if (moved & MF_MOVING && !ScreenMovPos &&
7470 (player == local_player || !options.network))
7472 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7473 int offset = (setup.scroll_delay ? 3 : 0);
7475 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7477 /* actual player has left the screen -- scroll in that direction */
7478 if (jx != old_jx) /* player has moved horizontally */
7479 scroll_x += (jx - old_jx);
7480 else /* player has moved vertically */
7481 scroll_y += (jy - old_jy);
7485 if (jx != old_jx) /* player has moved horizontally */
7487 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7488 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7489 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7491 /* don't scroll over playfield boundaries */
7492 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7493 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7495 /* don't scroll more than one field at a time */
7496 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7498 /* don't scroll against the player's moving direction */
7499 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7500 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7501 scroll_x = old_scroll_x;
7503 else /* player has moved vertically */
7505 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7506 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7507 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7509 /* don't scroll over playfield boundaries */
7510 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7511 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7513 /* don't scroll more than one field at a time */
7514 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7516 /* don't scroll against the player's moving direction */
7517 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7518 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7519 scroll_y = old_scroll_y;
7523 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7525 if (!options.network && !AllPlayersInVisibleScreen())
7527 scroll_x = old_scroll_x;
7528 scroll_y = old_scroll_y;
7532 ScrollScreen(player, SCROLL_INIT);
7533 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7540 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7542 if (!(moved & MF_MOVING) && !player->is_pushing)
7547 player->StepFrame = 0;
7549 if (moved & MF_MOVING)
7551 if (old_jx != jx && old_jy == jy)
7552 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7553 else if (old_jx == jx && old_jy != jy)
7554 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7556 DrawLevelField(jx, jy); /* for "crumbled sand" */
7558 player->last_move_dir = player->MovDir;
7559 player->is_moving = TRUE;
7561 player->is_snapping = FALSE;
7565 player->is_switching = FALSE;
7571 static int change_sides[4][2] =
7573 /* enter side leave side */
7574 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7575 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7576 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7577 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7579 int move_direction = player->MovDir;
7580 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7581 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7584 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7586 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7587 leave_side, CE_OTHER_GETS_LEFT);
7588 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7589 leave_side, CE_LEFT_BY_PLAYER, -1);
7592 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7594 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7595 enter_side, CE_OTHER_GETS_ENTERED);
7596 CheckElementSideChange(jx, jy, Feld[jx][jy],
7597 enter_side, CE_ENTERED_BY_PLAYER, -1);
7608 CheckGravityMovement(player);
7611 player->last_move_dir = MV_NO_MOVING;
7613 player->is_moving = FALSE;
7616 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7618 TestIfHeroTouchesBadThing(jx, jy);
7619 TestIfPlayerTouchesCustomElement(jx, jy);
7622 if (!player->active)
7628 void ScrollPlayer(struct PlayerInfo *player, int mode)
7630 int jx = player->jx, jy = player->jy;
7631 int last_jx = player->last_jx, last_jy = player->last_jy;
7632 int move_stepsize = TILEX / player->move_delay_value;
7634 if (!player->active || !player->MovPos)
7637 if (mode == SCROLL_INIT)
7639 player->actual_frame_counter = FrameCounter;
7640 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7642 if (Feld[last_jx][last_jy] == EL_EMPTY)
7643 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7650 else if (!FrameReached(&player->actual_frame_counter, 1))
7653 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7654 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7656 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7657 Feld[last_jx][last_jy] = EL_EMPTY;
7659 /* before DrawPlayer() to draw correct player graphic for this case */
7660 if (player->MovPos == 0)
7661 CheckGravityMovement(player);
7664 DrawPlayer(player); /* needed here only to cleanup last field */
7667 if (player->MovPos == 0) /* player reached destination field */
7669 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7671 /* continue with normal speed after quickly moving through gate */
7672 HALVE_PLAYER_SPEED(player);
7674 /* be able to make the next move without delay */
7675 player->move_delay = 0;
7678 player->last_jx = jx;
7679 player->last_jy = jy;
7681 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7682 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7683 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7685 DrawPlayer(player); /* needed here only to cleanup last field */
7688 if (local_player->friends_still_needed == 0 ||
7689 IS_SP_ELEMENT(Feld[jx][jy]))
7690 player->LevelSolved = player->GameOver = TRUE;
7693 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7695 TestIfHeroTouchesBadThing(jx, jy);
7696 TestIfPlayerTouchesCustomElement(jx, jy);
7698 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7701 if (!player->active)
7705 if (tape.single_step && tape.recording && !tape.pausing &&
7706 !player->programmed_action)
7707 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7711 void ScrollScreen(struct PlayerInfo *player, int mode)
7713 static unsigned long screen_frame_counter = 0;
7715 if (mode == SCROLL_INIT)
7717 /* set scrolling step size according to actual player's moving speed */
7718 ScrollStepSize = TILEX / player->move_delay_value;
7720 screen_frame_counter = FrameCounter;
7721 ScreenMovDir = player->MovDir;
7722 ScreenMovPos = player->MovPos;
7723 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7726 else if (!FrameReached(&screen_frame_counter, 1))
7731 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7732 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7733 redraw_mask |= REDRAW_FIELD;
7736 ScreenMovDir = MV_NO_MOVING;
7739 void TestIfPlayerTouchesCustomElement(int x, int y)
7741 static int xy[4][2] =
7748 static int change_sides[4][2] =
7750 /* center side border side */
7751 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7752 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7753 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7754 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7756 static int touch_dir[4] =
7763 int center_element = Feld[x][y]; /* should always be non-moving! */
7766 for (i = 0; i < 4; i++)
7768 int xx = x + xy[i][0];
7769 int yy = y + xy[i][1];
7770 int center_side = change_sides[i][0];
7771 int border_side = change_sides[i][1];
7774 if (!IN_LEV_FIELD(xx, yy))
7777 if (IS_PLAYER(x, y))
7779 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7780 border_element = Feld[xx][yy]; /* may be moving! */
7781 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7782 border_element = Feld[xx][yy];
7783 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7784 border_element = MovingOrBlocked2Element(xx, yy);
7786 continue; /* center and border element do not touch */
7788 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7789 CE_OTHER_GETS_TOUCHED);
7790 CheckElementSideChange(xx, yy, border_element, border_side,
7791 CE_TOUCHED_BY_PLAYER, -1);
7793 else if (IS_PLAYER(xx, yy))
7795 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7797 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7799 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7800 continue; /* center and border element do not touch */
7803 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7804 CE_OTHER_GETS_TOUCHED);
7805 CheckElementSideChange(x, y, center_element, center_side,
7806 CE_TOUCHED_BY_PLAYER, -1);
7813 void TestIfElementTouchesCustomElement(int x, int y)
7815 static int xy[4][2] =
7822 static int change_sides[4][2] =
7824 /* center side border side */
7825 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7826 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7827 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7828 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7830 static int touch_dir[4] =
7837 boolean change_center_element = FALSE;
7838 int center_element_change_page = 0;
7839 int center_element = Feld[x][y]; /* should always be non-moving! */
7842 for (i = 0; i < 4; i++)
7844 int xx = x + xy[i][0];
7845 int yy = y + xy[i][1];
7846 int center_side = change_sides[i][0];
7847 int border_side = change_sides[i][1];
7850 if (!IN_LEV_FIELD(xx, yy))
7853 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7854 border_element = Feld[xx][yy]; /* may be moving! */
7855 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7856 border_element = Feld[xx][yy];
7857 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7858 border_element = MovingOrBlocked2Element(xx, yy);
7860 continue; /* center and border element do not touch */
7862 /* check for change of center element (but change it only once) */
7863 if (IS_CUSTOM_ELEMENT(center_element) &&
7864 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7865 !change_center_element)
7867 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7869 struct ElementChangeInfo *change =
7870 &element_info[center_element].change_page[j];
7872 if (change->can_change &&
7873 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7874 change->sides & border_side &&
7875 change->trigger_element == border_element)
7877 change_center_element = TRUE;
7878 center_element_change_page = j;
7885 /* check for change of border element */
7886 if (IS_CUSTOM_ELEMENT(border_element) &&
7887 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7889 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7891 struct ElementChangeInfo *change =
7892 &element_info[border_element].change_page[j];
7894 if (change->can_change &&
7895 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7896 change->sides & center_side &&
7897 change->trigger_element == center_element)
7899 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7900 CE_OTHER_IS_TOUCHING, j);
7907 if (change_center_element)
7908 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7909 CE_OTHER_IS_TOUCHING, center_element_change_page);
7912 void TestIfElementHitsCustomElement(int x, int y, int direction)
7914 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7915 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7916 int hitx = x + dx, hity = y + dy;
7917 int hitting_element = Feld[x][y];
7919 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
7922 CheckElementSideChange(x, y, hitting_element,
7923 direction, CE_HITTING_SOMETHING, -1);
7925 if (IN_LEV_FIELD(hitx, hity))
7927 static int opposite_directions[] =
7934 int move_dir_bit = MV_DIR_BIT(direction);
7935 int opposite_direction = opposite_directions[move_dir_bit];
7936 int hitting_side = direction;
7937 int touched_side = opposite_direction;
7938 int touched_element = MovingOrBlocked2Element(hitx, hity);
7939 boolean object_hit = (!IS_MOVING(hitx, hity) ||
7940 MovDir[hitx][hity] != direction ||
7941 ABS(MovPos[hitx][hity]) <= TILEY / 2);
7949 CheckElementSideChange(hitx, hity, touched_element,
7950 opposite_direction, CE_HIT_BY_SOMETHING, -1);
7952 if (IS_CUSTOM_ELEMENT(hitting_element) &&
7953 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
7955 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
7957 struct ElementChangeInfo *change =
7958 &element_info[hitting_element].change_page[i];
7960 if (change->can_change &&
7961 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
7962 change->sides & touched_side &&
7963 change->trigger_element == touched_element)
7965 CheckElementSideChange(x, y, hitting_element,
7966 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
7972 if (IS_CUSTOM_ELEMENT(touched_element) &&
7973 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
7975 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
7977 struct ElementChangeInfo *change =
7978 &element_info[touched_element].change_page[i];
7980 if (change->can_change &&
7981 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
7982 change->sides & hitting_side &&
7983 change->trigger_element == hitting_element)
7985 CheckElementSideChange(hitx, hity, touched_element,
7986 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
7995 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7997 int i, kill_x = -1, kill_y = -1;
7998 static int test_xy[4][2] =
8005 static int test_dir[4] =
8013 for (i = 0; i < 4; i++)
8015 int test_x, test_y, test_move_dir, test_element;
8017 test_x = good_x + test_xy[i][0];
8018 test_y = good_y + test_xy[i][1];
8019 if (!IN_LEV_FIELD(test_x, test_y))
8023 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8026 test_element = Feld[test_x][test_y];
8028 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8031 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8032 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8034 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8035 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8043 if (kill_x != -1 || kill_y != -1)
8045 if (IS_PLAYER(good_x, good_y))
8047 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8049 if (player->shield_deadly_time_left > 0)
8050 Bang(kill_x, kill_y);
8051 else if (!PLAYER_PROTECTED(good_x, good_y))
8055 Bang(good_x, good_y);
8059 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8061 int i, kill_x = -1, kill_y = -1;
8062 int bad_element = Feld[bad_x][bad_y];
8063 static int test_xy[4][2] =
8070 static int touch_dir[4] =
8077 static int test_dir[4] =
8085 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8088 for (i = 0; i < 4; i++)
8090 int test_x, test_y, test_move_dir, test_element;
8092 test_x = bad_x + test_xy[i][0];
8093 test_y = bad_y + test_xy[i][1];
8094 if (!IN_LEV_FIELD(test_x, test_y))
8098 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8100 test_element = Feld[test_x][test_y];
8102 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8103 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8105 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8106 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8108 /* good thing is player or penguin that does not move away */
8109 if (IS_PLAYER(test_x, test_y))
8111 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8113 if (bad_element == EL_ROBOT && player->is_moving)
8114 continue; /* robot does not kill player if he is moving */
8116 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8118 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8119 continue; /* center and border element do not touch */
8126 else if (test_element == EL_PENGUIN)
8135 if (kill_x != -1 || kill_y != -1)
8137 if (IS_PLAYER(kill_x, kill_y))
8139 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8141 if (player->shield_deadly_time_left > 0)
8143 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8147 Bang(kill_x, kill_y);
8151 void TestIfHeroTouchesBadThing(int x, int y)
8153 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8156 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8158 TestIfGoodThingHitsBadThing(x, y, move_dir);
8161 void TestIfBadThingTouchesHero(int x, int y)
8163 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8166 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8168 TestIfBadThingHitsGoodThing(x, y, move_dir);
8171 void TestIfFriendTouchesBadThing(int x, int y)
8173 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8176 void TestIfBadThingTouchesFriend(int x, int y)
8178 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8181 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8183 int i, kill_x = bad_x, kill_y = bad_y;
8184 static int xy[4][2] =
8192 for (i = 0; i < 4; i++)
8196 x = bad_x + xy[i][0];
8197 y = bad_y + xy[i][1];
8198 if (!IN_LEV_FIELD(x, y))
8201 element = Feld[x][y];
8202 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8203 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8211 if (kill_x != bad_x || kill_y != bad_y)
8215 void KillHero(struct PlayerInfo *player)
8217 int jx = player->jx, jy = player->jy;
8219 if (!player->active)
8222 /* remove accessible field at the player's position */
8223 Feld[jx][jy] = EL_EMPTY;
8225 /* deactivate shield (else Bang()/Explode() would not work right) */
8226 player->shield_normal_time_left = 0;
8227 player->shield_deadly_time_left = 0;
8233 static void KillHeroUnlessProtected(int x, int y)
8235 if (!PLAYER_PROTECTED(x, y))
8236 KillHero(PLAYERINFO(x, y));
8239 void BuryHero(struct PlayerInfo *player)
8241 int jx = player->jx, jy = player->jy;
8243 if (!player->active)
8247 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8249 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8251 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8253 player->GameOver = TRUE;
8257 void RemoveHero(struct PlayerInfo *player)
8259 int jx = player->jx, jy = player->jy;
8260 int i, found = FALSE;
8262 player->present = FALSE;
8263 player->active = FALSE;
8265 if (!ExplodeField[jx][jy])
8266 StorePlayer[jx][jy] = 0;
8268 for (i = 0; i < MAX_PLAYERS; i++)
8269 if (stored_player[i].active)
8273 AllPlayersGone = TRUE;
8280 =============================================================================
8281 checkDiagonalPushing()
8282 -----------------------------------------------------------------------------
8283 check if diagonal input device direction results in pushing of object
8284 (by checking if the alternative direction is walkable, diggable, ...)
8285 =============================================================================
8288 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8289 int x, int y, int real_dx, int real_dy)
8291 int jx, jy, dx, dy, xx, yy;
8293 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8296 /* diagonal direction: check alternative direction */
8301 xx = jx + (dx == 0 ? real_dx : 0);
8302 yy = jy + (dy == 0 ? real_dy : 0);
8304 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8308 =============================================================================
8310 -----------------------------------------------------------------------------
8311 x, y: field next to player (non-diagonal) to try to dig to
8312 real_dx, real_dy: direction as read from input device (can be diagonal)
8313 =============================================================================
8316 int DigField(struct PlayerInfo *player,
8317 int x, int y, int real_dx, int real_dy, int mode)
8319 static int change_sides[4] =
8321 CH_SIDE_RIGHT, /* moving left */
8322 CH_SIDE_LEFT, /* moving right */
8323 CH_SIDE_BOTTOM, /* moving up */
8324 CH_SIDE_TOP, /* moving down */
8326 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8327 int jx = player->jx, jy = player->jy;
8328 int dx = x - jx, dy = y - jy;
8329 int nextx = x + dx, nexty = y + dy;
8330 int move_direction = (dx == -1 ? MV_LEFT :
8331 dx == +1 ? MV_RIGHT :
8333 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8334 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8337 if (player->MovPos == 0)
8339 player->is_digging = FALSE;
8340 player->is_collecting = FALSE;
8343 if (player->MovPos == 0) /* last pushing move finished */
8344 player->is_pushing = FALSE;
8346 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8348 player->is_switching = FALSE;
8349 player->push_delay = 0;
8351 return MF_NO_ACTION;
8354 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8355 return MF_NO_ACTION;
8358 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8360 if (IS_TUBE(Feld[jx][jy]) ||
8361 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8365 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8366 int tube_leave_directions[][2] =
8368 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8369 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8370 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8371 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8372 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8373 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8374 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8375 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8376 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8377 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8378 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8379 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8382 while (tube_leave_directions[i][0] != tube_element)
8385 if (tube_leave_directions[i][0] == -1) /* should not happen */
8389 if (!(tube_leave_directions[i][1] & move_direction))
8390 return MF_NO_ACTION; /* tube has no opening in this direction */
8393 element = Feld[x][y];
8395 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8396 game.engine_version >= VERSION_IDENT(2,2,0,0))
8397 return MF_NO_ACTION;
8401 case EL_SP_PORT_LEFT:
8402 case EL_SP_PORT_RIGHT:
8404 case EL_SP_PORT_DOWN:
8405 case EL_SP_PORT_HORIZONTAL:
8406 case EL_SP_PORT_VERTICAL:
8407 case EL_SP_PORT_ANY:
8408 case EL_SP_GRAVITY_PORT_LEFT:
8409 case EL_SP_GRAVITY_PORT_RIGHT:
8410 case EL_SP_GRAVITY_PORT_UP:
8411 case EL_SP_GRAVITY_PORT_DOWN:
8413 element != EL_SP_PORT_LEFT &&
8414 element != EL_SP_GRAVITY_PORT_LEFT &&
8415 element != EL_SP_PORT_HORIZONTAL &&
8416 element != EL_SP_PORT_ANY) ||
8418 element != EL_SP_PORT_RIGHT &&
8419 element != EL_SP_GRAVITY_PORT_RIGHT &&
8420 element != EL_SP_PORT_HORIZONTAL &&
8421 element != EL_SP_PORT_ANY) ||
8423 element != EL_SP_PORT_UP &&
8424 element != EL_SP_GRAVITY_PORT_UP &&
8425 element != EL_SP_PORT_VERTICAL &&
8426 element != EL_SP_PORT_ANY) ||
8428 element != EL_SP_PORT_DOWN &&
8429 element != EL_SP_GRAVITY_PORT_DOWN &&
8430 element != EL_SP_PORT_VERTICAL &&
8431 element != EL_SP_PORT_ANY) ||
8432 !IN_LEV_FIELD(nextx, nexty) ||
8433 !IS_FREE(nextx, nexty))
8434 return MF_NO_ACTION;
8436 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8437 element == EL_SP_GRAVITY_PORT_RIGHT ||
8438 element == EL_SP_GRAVITY_PORT_UP ||
8439 element == EL_SP_GRAVITY_PORT_DOWN)
8440 game.gravity = !game.gravity;
8442 /* automatically move to the next field with double speed */
8443 player->programmed_action = move_direction;
8444 DOUBLE_PLAYER_SPEED(player);
8446 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8450 case EL_TUBE_VERTICAL:
8451 case EL_TUBE_HORIZONTAL:
8452 case EL_TUBE_VERTICAL_LEFT:
8453 case EL_TUBE_VERTICAL_RIGHT:
8454 case EL_TUBE_HORIZONTAL_UP:
8455 case EL_TUBE_HORIZONTAL_DOWN:
8456 case EL_TUBE_LEFT_UP:
8457 case EL_TUBE_LEFT_DOWN:
8458 case EL_TUBE_RIGHT_UP:
8459 case EL_TUBE_RIGHT_DOWN:
8462 int tube_enter_directions[][2] =
8464 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8465 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8466 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8467 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8468 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8469 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8470 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8471 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8472 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8473 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8474 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8475 { -1, MV_NO_MOVING }
8478 while (tube_enter_directions[i][0] != element)
8481 if (tube_enter_directions[i][0] == -1) /* should not happen */
8485 if (!(tube_enter_directions[i][1] & move_direction))
8486 return MF_NO_ACTION; /* tube has no opening in this direction */
8488 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8494 if (IS_WALKABLE(element))
8496 int sound_action = ACTION_WALKING;
8498 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8500 if (!player->key[element - EL_GATE_1])
8501 return MF_NO_ACTION;
8503 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8505 if (!player->key[element - EL_GATE_1_GRAY])
8506 return MF_NO_ACTION;
8508 else if (element == EL_EXIT_OPEN ||
8509 element == EL_SP_EXIT_OPEN ||
8510 element == EL_SP_EXIT_OPENING)
8512 sound_action = ACTION_PASSING; /* player is passing exit */
8514 else if (element == EL_EMPTY)
8516 sound_action = ACTION_MOVING; /* nothing to walk on */
8519 /* play sound from background or player, whatever is available */
8520 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8521 PlayLevelSoundElementAction(x, y, element, sound_action);
8523 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8527 else if (IS_PASSABLE(element))
8529 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8530 return MF_NO_ACTION;
8533 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8534 return MF_NO_ACTION;
8537 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8539 if (!player->key[element - EL_EM_GATE_1])
8540 return MF_NO_ACTION;
8542 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8544 if (!player->key[element - EL_EM_GATE_1_GRAY])
8545 return MF_NO_ACTION;
8548 /* automatically move to the next field with double speed */
8549 player->programmed_action = move_direction;
8550 DOUBLE_PLAYER_SPEED(player);
8552 PlayLevelSoundAction(x, y, ACTION_PASSING);
8556 else if (IS_DIGGABLE(element))
8560 if (mode != DF_SNAP)
8563 GfxElement[x][y] = GFX_ELEMENT(element);
8566 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8568 player->is_digging = TRUE;
8571 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8573 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8576 if (mode == DF_SNAP)
8577 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8582 else if (IS_COLLECTIBLE(element))
8586 if (mode != DF_SNAP)
8588 GfxElement[x][y] = element;
8589 player->is_collecting = TRUE;
8592 if (element == EL_SPEED_PILL)
8593 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8594 else if (element == EL_EXTRA_TIME && level.time > 0)
8597 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8599 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8601 player->shield_normal_time_left += 10;
8602 if (element == EL_SHIELD_DEADLY)
8603 player->shield_deadly_time_left += 10;
8605 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8607 if (player->inventory_size < MAX_INVENTORY_SIZE)
8608 player->inventory_element[player->inventory_size++] = element;
8610 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8611 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8613 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8615 player->dynabomb_count++;
8616 player->dynabombs_left++;
8618 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8620 player->dynabomb_size++;
8622 else if (element == EL_DYNABOMB_INCREASE_POWER)
8624 player->dynabomb_xl = TRUE;
8626 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8627 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8629 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8630 element - EL_KEY_1 : element - EL_EM_KEY_1);
8632 player->key[key_nr] = TRUE;
8634 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8635 el2edimg(EL_KEY_1 + key_nr));
8636 redraw_mask |= REDRAW_DOOR_1;
8638 else if (IS_ENVELOPE(element))
8641 player->show_envelope = element;
8643 ShowEnvelope(element - EL_ENVELOPE_1);
8646 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8650 for (i = 0; i < element_info[element].collect_count; i++)
8651 if (player->inventory_size < MAX_INVENTORY_SIZE)
8652 player->inventory_element[player->inventory_size++] = element;
8654 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8655 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8657 else if (element_info[element].collect_count > 0)
8659 local_player->gems_still_needed -=
8660 element_info[element].collect_count;
8661 if (local_player->gems_still_needed < 0)
8662 local_player->gems_still_needed = 0;
8664 DrawText(DX_EMERALDS, DY_EMERALDS,
8665 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8668 RaiseScoreElement(element);
8669 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8671 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8674 if (mode == DF_SNAP)
8675 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8680 else if (IS_PUSHABLE(element))
8682 if (mode == DF_SNAP && element != EL_BD_ROCK)
8683 return MF_NO_ACTION;
8685 if (CAN_FALL(element) && dy)
8686 return MF_NO_ACTION;
8688 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8689 !(element == EL_SPRING && use_spring_bug))
8690 return MF_NO_ACTION;
8693 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8694 ((move_direction & MV_VERTICAL &&
8695 ((element_info[element].move_pattern & MV_LEFT &&
8696 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8697 (element_info[element].move_pattern & MV_RIGHT &&
8698 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8699 (move_direction & MV_HORIZONTAL &&
8700 ((element_info[element].move_pattern & MV_UP &&
8701 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8702 (element_info[element].move_pattern & MV_DOWN &&
8703 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8704 return MF_NO_ACTION;
8708 /* do not push elements already moving away faster than player */
8709 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8710 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8711 return MF_NO_ACTION;
8713 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8714 return MF_NO_ACTION;
8718 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8720 if (player->push_delay_value == -1)
8721 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8723 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8725 if (!player->is_pushing)
8726 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8730 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8731 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8732 !player_is_pushing))
8733 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8736 if (!player->is_pushing &&
8737 game.engine_version >= VERSION_IDENT(2,2,0,7))
8738 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8742 printf("::: push delay: %ld [%d, %d] [%d]\n",
8743 player->push_delay_value, FrameCounter, game.engine_version,
8744 player->is_pushing);
8747 player->is_pushing = TRUE;
8749 if (!(IN_LEV_FIELD(nextx, nexty) &&
8750 (IS_FREE(nextx, nexty) ||
8751 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8752 IS_SB_ELEMENT(element)))))
8753 return MF_NO_ACTION;
8755 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8756 return MF_NO_ACTION;
8758 if (player->push_delay == 0) /* new pushing; restart delay */
8759 player->push_delay = FrameCounter;
8761 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8762 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8763 element != EL_SPRING && element != EL_BALLOON)
8765 /* make sure that there is no move delay before next try to push */
8766 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8767 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8769 return MF_NO_ACTION;
8773 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8776 if (IS_SB_ELEMENT(element))
8778 if (element == EL_SOKOBAN_FIELD_FULL)
8780 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8781 local_player->sokobanfields_still_needed++;
8784 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8786 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8787 local_player->sokobanfields_still_needed--;
8790 Feld[x][y] = EL_SOKOBAN_OBJECT;
8792 if (Back[x][y] == Back[nextx][nexty])
8793 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8794 else if (Back[x][y] != 0)
8795 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8798 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8801 if (local_player->sokobanfields_still_needed == 0 &&
8802 game.emulation == EMU_SOKOBAN)
8804 player->LevelSolved = player->GameOver = TRUE;
8805 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8809 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8811 InitMovingField(x, y, move_direction);
8812 GfxAction[x][y] = ACTION_PUSHING;
8814 if (mode == DF_SNAP)
8815 ContinueMoving(x, y);
8817 MovPos[x][y] = (dx != 0 ? dx : dy);
8819 Pushed[x][y] = TRUE;
8820 Pushed[nextx][nexty] = TRUE;
8822 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8823 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8825 player->push_delay_value = -1; /* get new value later */
8827 CheckTriggeredElementSideChange(x, y, element, dig_side,
8828 CE_OTHER_GETS_PUSHED);
8829 CheckElementSideChange(x, y, element, dig_side,
8830 CE_PUSHED_BY_PLAYER, -1);
8834 else if (IS_SWITCHABLE(element))
8836 if (PLAYER_SWITCHING(player, x, y))
8839 player->is_switching = TRUE;
8840 player->switch_x = x;
8841 player->switch_y = y;
8843 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8845 if (element == EL_ROBOT_WHEEL)
8847 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8851 DrawLevelField(x, y);
8853 else if (element == EL_SP_TERMINAL)
8857 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8859 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8861 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8862 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8865 else if (IS_BELT_SWITCH(element))
8867 ToggleBeltSwitch(x, y);
8869 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8870 element == EL_SWITCHGATE_SWITCH_DOWN)
8872 ToggleSwitchgateSwitch(x, y);
8874 else if (element == EL_LIGHT_SWITCH ||
8875 element == EL_LIGHT_SWITCH_ACTIVE)
8877 ToggleLightSwitch(x, y);
8880 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8881 SND_LIGHT_SWITCH_ACTIVATING :
8882 SND_LIGHT_SWITCH_DEACTIVATING);
8885 else if (element == EL_TIMEGATE_SWITCH)
8887 ActivateTimegateSwitch(x, y);
8889 else if (element == EL_BALLOON_SWITCH_LEFT ||
8890 element == EL_BALLOON_SWITCH_RIGHT ||
8891 element == EL_BALLOON_SWITCH_UP ||
8892 element == EL_BALLOON_SWITCH_DOWN ||
8893 element == EL_BALLOON_SWITCH_ANY)
8895 if (element == EL_BALLOON_SWITCH_ANY)
8896 game.balloon_dir = move_direction;
8898 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8899 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8900 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8901 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8904 else if (element == EL_LAMP)
8906 Feld[x][y] = EL_LAMP_ACTIVE;
8907 local_player->lights_still_needed--;
8909 DrawLevelField(x, y);
8911 else if (element == EL_TIME_ORB_FULL)
8913 Feld[x][y] = EL_TIME_ORB_EMPTY;
8915 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8917 DrawLevelField(x, y);
8920 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8928 if (!PLAYER_SWITCHING(player, x, y))
8930 player->is_switching = TRUE;
8931 player->switch_x = x;
8932 player->switch_y = y;
8934 CheckTriggeredElementSideChange(x, y, element, dig_side,
8935 CE_OTHER_IS_SWITCHING);
8936 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8939 CheckTriggeredElementSideChange(x, y, element, dig_side,
8940 CE_OTHER_GETS_PRESSED);
8941 CheckElementSideChange(x, y, element, dig_side,
8942 CE_PRESSED_BY_PLAYER, -1);
8945 return MF_NO_ACTION;
8948 player->push_delay = 0;
8950 if (Feld[x][y] != element) /* really digged/collected something */
8951 player->is_collecting = !player->is_digging;
8956 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8958 int jx = player->jx, jy = player->jy;
8959 int x = jx + dx, y = jy + dy;
8960 int snap_direction = (dx == -1 ? MV_LEFT :
8961 dx == +1 ? MV_RIGHT :
8963 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8965 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8968 if (!player->active || !IN_LEV_FIELD(x, y))
8976 if (player->MovPos == 0)
8977 player->is_pushing = FALSE;
8979 player->is_snapping = FALSE;
8981 if (player->MovPos == 0)
8983 player->is_moving = FALSE;
8984 player->is_digging = FALSE;
8985 player->is_collecting = FALSE;
8991 if (player->is_snapping)
8994 player->MovDir = snap_direction;
8996 player->is_moving = FALSE;
8997 player->is_digging = FALSE;
8998 player->is_collecting = FALSE;
9000 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9003 player->is_snapping = TRUE;
9005 player->is_moving = FALSE;
9006 player->is_digging = FALSE;
9007 player->is_collecting = FALSE;
9009 DrawLevelField(x, y);
9015 boolean DropElement(struct PlayerInfo *player)
9017 int jx = player->jx, jy = player->jy;
9018 int old_element = Feld[jx][jy];
9021 /* check if player is active, not moving and ready to drop */
9022 if (!player->active || player->MovPos || player->drop_delay > 0)
9025 /* check if player has anything that can be dropped */
9026 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9029 /* check if anything can be dropped at the current position */
9030 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9033 /* collected custom elements can only be dropped on empty fields */
9034 if (player->inventory_size > 0 &&
9035 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9036 && old_element != EL_EMPTY)
9039 if (old_element != EL_EMPTY)
9040 Back[jx][jy] = old_element; /* store old element on this field */
9042 ResetGfxAnimation(jx, jy);
9043 ResetRandomAnimationValue(jx, jy);
9045 if (player->inventory_size > 0)
9047 player->inventory_size--;
9048 new_element = player->inventory_element[player->inventory_size];
9050 if (new_element == EL_DYNAMITE)
9051 new_element = EL_DYNAMITE_ACTIVE;
9052 else if (new_element == EL_SP_DISK_RED)
9053 new_element = EL_SP_DISK_RED_ACTIVE;
9055 Feld[jx][jy] = new_element;
9057 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9058 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9060 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9061 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9063 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9065 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9066 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9068 TestIfElementTouchesCustomElement(jx, jy);
9070 else /* player is dropping a dyna bomb */
9072 player->dynabombs_left--;
9073 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9075 Feld[jx][jy] = new_element;
9077 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9078 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9080 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9087 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9089 InitField(jx, jy, FALSE);
9090 if (CAN_MOVE(Feld[jx][jy]))
9094 new_element = Feld[jx][jy];
9096 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9097 element_info[new_element].move_pattern == MV_PROJECTILE)
9099 int move_stepsize = element_info[new_element].move_stepsize;
9100 int direction, dx, dy, nextx, nexty;
9102 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9103 MovDir[jx][jy] = player->MovDir;
9105 direction = MovDir[jx][jy];
9106 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9107 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9111 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9114 WasJustMoving[jx][jy] = 3;
9116 InitMovingField(jx, jy, direction);
9117 ContinueMoving(jx, jy);
9122 Changed[jx][jy] = 0; /* allow another change */
9125 TestIfElementHitsCustomElement(jx, jy, direction);
9127 CheckElementSideChange(jx, jy, new_element,
9128 direction, CE_HITTING_SOMETHING, -1);
9132 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9136 player->drop_delay = 8 + 8 + 8;
9146 /* ------------------------------------------------------------------------- */
9147 /* game sound playing functions */
9148 /* ------------------------------------------------------------------------- */
9150 static int *loop_sound_frame = NULL;
9151 static int *loop_sound_volume = NULL;
9153 void InitPlayLevelSound()
9155 int num_sounds = getSoundListSize();
9157 checked_free(loop_sound_frame);
9158 checked_free(loop_sound_volume);
9160 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9161 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9164 static void PlayLevelSound(int x, int y, int nr)
9166 int sx = SCREENX(x), sy = SCREENY(y);
9167 int volume, stereo_position;
9168 int max_distance = 8;
9169 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9171 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9172 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9175 if (!IN_LEV_FIELD(x, y) ||
9176 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9177 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9180 volume = SOUND_MAX_VOLUME;
9182 if (!IN_SCR_FIELD(sx, sy))
9184 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9185 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9187 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9190 stereo_position = (SOUND_MAX_LEFT +
9191 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9192 (SCR_FIELDX + 2 * max_distance));
9194 if (IS_LOOP_SOUND(nr))
9196 /* This assures that quieter loop sounds do not overwrite louder ones,
9197 while restarting sound volume comparison with each new game frame. */
9199 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9202 loop_sound_volume[nr] = volume;
9203 loop_sound_frame[nr] = FrameCounter;
9206 PlaySoundExt(nr, volume, stereo_position, type);
9209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9211 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9212 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9213 y < LEVELY(BY1) ? LEVELY(BY1) :
9214 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9218 static void PlayLevelSoundAction(int x, int y, int action)
9220 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9225 int sound_effect = element_info[element].sound[action];
9227 if (sound_effect != SND_UNDEFINED)
9228 PlayLevelSound(x, y, sound_effect);
9231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9234 int sound_effect = element_info[element].sound[action];
9236 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9237 PlayLevelSound(x, y, sound_effect);
9240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9242 int sound_effect = element_info[Feld[x][y]].sound[action];
9244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9245 PlayLevelSound(x, y, sound_effect);
9248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9250 int sound_effect = element_info[Feld[x][y]].sound[action];
9252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9253 StopSound(sound_effect);
9256 static void PlayLevelMusic()
9258 if (levelset.music[level_nr] != MUS_UNDEFINED)
9259 PlayMusic(levelset.music[level_nr]); /* from config file */
9261 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9264 void RaiseScore(int value)
9266 local_player->score += value;
9267 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9270 void RaiseScoreElement(int element)
9276 case EL_EMERALD_YELLOW:
9277 case EL_EMERALD_RED:
9278 case EL_EMERALD_PURPLE:
9279 case EL_SP_INFOTRON:
9280 RaiseScore(level.score[SC_EMERALD]);
9283 RaiseScore(level.score[SC_DIAMOND]);
9286 RaiseScore(level.score[SC_CRYSTAL]);
9289 RaiseScore(level.score[SC_PEARL]);
9292 case EL_BD_BUTTERFLY:
9293 case EL_SP_ELECTRON:
9294 RaiseScore(level.score[SC_BUG]);
9298 case EL_SP_SNIKSNAK:
9299 RaiseScore(level.score[SC_SPACESHIP]);
9302 case EL_DARK_YAMYAM:
9303 RaiseScore(level.score[SC_YAMYAM]);
9306 RaiseScore(level.score[SC_ROBOT]);
9309 RaiseScore(level.score[SC_PACMAN]);
9312 RaiseScore(level.score[SC_NUT]);
9315 case EL_SP_DISK_RED:
9316 case EL_DYNABOMB_INCREASE_NUMBER:
9317 case EL_DYNABOMB_INCREASE_SIZE:
9318 case EL_DYNABOMB_INCREASE_POWER:
9319 RaiseScore(level.score[SC_DYNAMITE]);
9321 case EL_SHIELD_NORMAL:
9322 case EL_SHIELD_DEADLY:
9323 RaiseScore(level.score[SC_SHIELD]);
9326 RaiseScore(level.score[SC_TIME_BONUS]);
9332 RaiseScore(level.score[SC_KEY]);
9335 RaiseScore(element_info[element].collect_score);
9340 void RequestQuitGame(boolean ask_if_really_quit)
9342 if (AllPlayersGone ||
9343 !ask_if_really_quit ||
9344 level_editor_test_game ||
9345 Request("Do you really want to quit the game ?",
9346 REQ_ASK | REQ_STAY_CLOSED))
9348 #if defined(PLATFORM_UNIX)
9349 if (options.network)
9350 SendToServer_StopPlaying();
9354 game_status = GAME_MODE_MAIN;
9360 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9365 /* ---------- new game button stuff ---------------------------------------- */
9367 /* graphic position values for game buttons */
9368 #define GAME_BUTTON_XSIZE 30
9369 #define GAME_BUTTON_YSIZE 30
9370 #define GAME_BUTTON_XPOS 5
9371 #define GAME_BUTTON_YPOS 215
9372 #define SOUND_BUTTON_XPOS 5
9373 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9375 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9376 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9377 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9378 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9379 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9380 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9387 } gamebutton_info[NUM_GAME_BUTTONS] =
9390 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9395 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9400 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9405 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9406 SOUND_CTRL_ID_MUSIC,
9407 "background music on/off"
9410 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9411 SOUND_CTRL_ID_LOOPS,
9412 "sound loops on/off"
9415 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9416 SOUND_CTRL_ID_SIMPLE,
9417 "normal sounds on/off"
9421 void CreateGameButtons()
9425 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9427 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9428 struct GadgetInfo *gi;
9431 unsigned long event_mask;
9432 int gd_xoffset, gd_yoffset;
9433 int gd_x1, gd_x2, gd_y1, gd_y2;
9436 gd_xoffset = gamebutton_info[i].x;
9437 gd_yoffset = gamebutton_info[i].y;
9438 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9439 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9441 if (id == GAME_CTRL_ID_STOP ||
9442 id == GAME_CTRL_ID_PAUSE ||
9443 id == GAME_CTRL_ID_PLAY)
9445 button_type = GD_TYPE_NORMAL_BUTTON;
9447 event_mask = GD_EVENT_RELEASED;
9448 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9449 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9453 button_type = GD_TYPE_CHECK_BUTTON;
9455 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9456 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9457 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9458 event_mask = GD_EVENT_PRESSED;
9459 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9460 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9463 gi = CreateGadget(GDI_CUSTOM_ID, id,
9464 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9465 GDI_X, DX + gd_xoffset,
9466 GDI_Y, DY + gd_yoffset,
9467 GDI_WIDTH, GAME_BUTTON_XSIZE,
9468 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9469 GDI_TYPE, button_type,
9470 GDI_STATE, GD_BUTTON_UNPRESSED,
9471 GDI_CHECKED, checked,
9472 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9473 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9474 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9475 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9476 GDI_EVENT_MASK, event_mask,
9477 GDI_CALLBACK_ACTION, HandleGameButtons,
9481 Error(ERR_EXIT, "cannot create gadget");
9483 game_gadget[id] = gi;
9487 void FreeGameButtons()
9491 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9492 FreeGadget(game_gadget[i]);
9495 static void MapGameButtons()
9499 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9500 MapGadget(game_gadget[i]);
9503 void UnmapGameButtons()
9507 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9508 UnmapGadget(game_gadget[i]);
9511 static void HandleGameButtons(struct GadgetInfo *gi)
9513 int id = gi->custom_id;
9515 if (game_status != GAME_MODE_PLAYING)
9520 case GAME_CTRL_ID_STOP:
9521 RequestQuitGame(TRUE);
9524 case GAME_CTRL_ID_PAUSE:
9525 if (options.network)
9527 #if defined(PLATFORM_UNIX)
9529 SendToServer_ContinuePlaying();
9531 SendToServer_PausePlaying();
9535 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9538 case GAME_CTRL_ID_PLAY:
9541 #if defined(PLATFORM_UNIX)
9542 if (options.network)
9543 SendToServer_ContinuePlaying();
9547 tape.pausing = FALSE;
9548 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9553 case SOUND_CTRL_ID_MUSIC:
9554 if (setup.sound_music)
9556 setup.sound_music = FALSE;
9559 else if (audio.music_available)
9561 setup.sound = setup.sound_music = TRUE;
9563 SetAudioMode(setup.sound);
9569 case SOUND_CTRL_ID_LOOPS:
9570 if (setup.sound_loops)
9571 setup.sound_loops = FALSE;
9572 else if (audio.loops_available)
9574 setup.sound = setup.sound_loops = TRUE;
9575 SetAudioMode(setup.sound);
9579 case SOUND_CTRL_ID_SIMPLE:
9580 if (setup.sound_simple)
9581 setup.sound_simple = FALSE;
9582 else if (audio.sound_available)
9584 setup.sound = setup.sound_simple = TRUE;
9585 SetAudioMode(setup.sound);