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;
1079 player->is_dropping = FALSE;
1081 player->is_bored = FALSE;
1082 player->is_sleeping = FALSE;
1084 player->frame_counter_bored = -1;
1085 player->frame_counter_sleeping = -1;
1087 player->anim_delay_counter = 0;
1088 player->post_delay_counter = 0;
1090 player->action_waiting = ACTION_DEFAULT;
1091 player->last_action_waiting = ACTION_DEFAULT;
1092 player->special_action_bored = ACTION_DEFAULT;
1093 player->special_action_sleeping = ACTION_DEFAULT;
1095 player->num_special_action_bored = 0;
1096 player->num_special_action_sleeping = 0;
1098 /* determine number of special actions for bored and sleeping animation */
1099 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1101 boolean found = FALSE;
1103 for (k = 0; k < NUM_DIRECTIONS; k++)
1104 if (el_act_dir2img(player->element_nr, j, k) !=
1105 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1109 player->num_special_action_bored++;
1113 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1115 boolean found = FALSE;
1117 for (k = 0; k < NUM_DIRECTIONS; k++)
1118 if (el_act_dir2img(player->element_nr, j, k) !=
1119 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1123 player->num_special_action_sleeping++;
1128 player->switch_x = -1;
1129 player->switch_y = -1;
1131 player->show_envelope = 0;
1133 player->move_delay = game.initial_move_delay;
1134 player->move_delay_value = game.initial_move_delay_value;
1136 player->push_delay = 0;
1137 player->push_delay_value = game.initial_push_delay_value;
1139 player->drop_delay = 0;
1141 player->last_jx = player->last_jy = 0;
1142 player->jx = player->jy = 0;
1144 player->shield_normal_time_left = 0;
1145 player->shield_deadly_time_left = 0;
1147 player->inventory_size = 0;
1149 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1150 SnapField(player, 0, 0);
1152 player->LevelSolved = FALSE;
1153 player->GameOver = FALSE;
1156 network_player_action_received = FALSE;
1158 #if defined(PLATFORM_UNIX)
1159 /* initial null action */
1160 if (network_playing)
1161 SendToServer_MovePlayer(MV_NO_MOVING);
1169 TimeLeft = level.time;
1171 ScreenMovDir = MV_NO_MOVING;
1175 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1177 AllPlayersGone = FALSE;
1179 game.yamyam_content_nr = 0;
1180 game.magic_wall_active = FALSE;
1181 game.magic_wall_time_left = 0;
1182 game.light_time_left = 0;
1183 game.timegate_time_left = 0;
1184 game.switchgate_pos = 0;
1185 game.balloon_dir = MV_NO_MOVING;
1186 game.gravity = level.initial_gravity;
1187 game.explosions_delayed = TRUE;
1189 game.envelope_active = FALSE;
1191 for (i = 0; i < 4; i++)
1193 game.belt_dir[i] = MV_NO_MOVING;
1194 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1197 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1198 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1200 for (x = 0; x < lev_fieldx; x++)
1202 for (y = 0; y < lev_fieldy; y++)
1204 Feld[x][y] = level.field[x][y];
1205 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1206 ChangeDelay[x][y] = 0;
1207 ChangePage[x][y] = -1;
1208 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1210 WasJustMoving[x][y] = 0;
1211 WasJustFalling[x][y] = 0;
1213 Pushed[x][y] = FALSE;
1215 Changed[x][y] = CE_BITMASK_DEFAULT;
1216 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1218 ExplodePhase[x][y] = 0;
1219 ExplodeField[x][y] = EX_NO_EXPLOSION;
1221 RunnerVisit[x][y] = 0;
1222 PlayerVisit[x][y] = 0;
1225 GfxRandom[x][y] = INIT_GFX_RANDOM();
1226 GfxElement[x][y] = EL_UNDEFINED;
1227 GfxAction[x][y] = ACTION_DEFAULT;
1228 GfxDir[x][y] = MV_NO_MOVING;
1232 for (y = 0; y < lev_fieldy; y++)
1234 for (x = 0; x < lev_fieldx; x++)
1236 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1238 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1240 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1243 InitField(x, y, TRUE);
1249 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1250 emulate_sb ? EMU_SOKOBAN :
1251 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1253 /* correct non-moving belts to start moving left */
1254 for (i = 0; i < 4; i++)
1255 if (game.belt_dir[i] == MV_NO_MOVING)
1256 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1258 /* check if any connected player was not found in playfield */
1259 for (i = 0; i < MAX_PLAYERS; i++)
1261 struct PlayerInfo *player = &stored_player[i];
1263 if (player->connected && !player->present)
1265 for (j = 0; j < MAX_PLAYERS; j++)
1267 struct PlayerInfo *some_player = &stored_player[j];
1268 int jx = some_player->jx, jy = some_player->jy;
1270 /* assign first free player found that is present in the playfield */
1271 if (some_player->present && !some_player->connected)
1273 player->present = TRUE;
1274 player->active = TRUE;
1275 some_player->present = FALSE;
1277 StorePlayer[jx][jy] = player->element_nr;
1278 player->jx = player->last_jx = jx;
1279 player->jy = player->last_jy = jy;
1289 /* when playing a tape, eliminate all players who do not participate */
1291 for (i = 0; i < MAX_PLAYERS; i++)
1293 if (stored_player[i].active && !tape.player_participates[i])
1295 struct PlayerInfo *player = &stored_player[i];
1296 int jx = player->jx, jy = player->jy;
1298 player->active = FALSE;
1299 StorePlayer[jx][jy] = 0;
1300 Feld[jx][jy] = EL_EMPTY;
1304 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1306 /* when in single player mode, eliminate all but the first active player */
1308 for (i = 0; i < MAX_PLAYERS; i++)
1310 if (stored_player[i].active)
1312 for (j = i + 1; j < MAX_PLAYERS; j++)
1314 if (stored_player[j].active)
1316 struct PlayerInfo *player = &stored_player[j];
1317 int jx = player->jx, jy = player->jy;
1319 player->active = FALSE;
1320 StorePlayer[jx][jy] = 0;
1321 Feld[jx][jy] = EL_EMPTY;
1328 /* when recording the game, store which players take part in the game */
1331 for (i = 0; i < MAX_PLAYERS; i++)
1332 if (stored_player[i].active)
1333 tape.player_participates[i] = TRUE;
1338 for (i = 0; i < MAX_PLAYERS; i++)
1340 struct PlayerInfo *player = &stored_player[i];
1342 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1347 if (local_player == player)
1348 printf("Player %d is local player.\n", i+1);
1352 if (BorderElement == EL_EMPTY)
1355 SBX_Right = lev_fieldx - SCR_FIELDX;
1357 SBY_Lower = lev_fieldy - SCR_FIELDY;
1362 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1364 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1367 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1368 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1370 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1371 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1373 /* if local player not found, look for custom element that might create
1374 the player (make some assumptions about the right custom element) */
1375 if (!local_player->present)
1377 int start_x = 0, start_y = 0;
1378 int found_rating = 0;
1379 int found_element = EL_UNDEFINED;
1381 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1383 int element = Feld[x][y];
1388 if (!IS_CUSTOM_ELEMENT(element))
1391 if (CAN_CHANGE(element))
1393 for (i = 0; i < element_info[element].num_change_pages; i++)
1395 content = element_info[element].change_page[i].target_element;
1396 is_player = ELEM_IS_PLAYER(content);
1398 if (is_player && (found_rating < 3 || element < found_element))
1404 found_element = element;
1409 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1411 content = element_info[element].content[xx][yy];
1412 is_player = ELEM_IS_PLAYER(content);
1414 if (is_player && (found_rating < 2 || element < found_element))
1416 start_x = x + xx - 1;
1417 start_y = y + yy - 1;
1420 found_element = element;
1423 if (!CAN_CHANGE(element))
1426 for (i = 0; i < element_info[element].num_change_pages; i++)
1428 content = element_info[element].change_page[i].content[xx][yy];
1429 is_player = ELEM_IS_PLAYER(content);
1431 if (is_player && (found_rating < 1 || element < found_element))
1433 start_x = x + xx - 1;
1434 start_y = y + yy - 1;
1437 found_element = element;
1443 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1444 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1447 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1448 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1454 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1455 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1456 local_player->jx - MIDPOSX);
1458 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1459 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1460 local_player->jy - MIDPOSY);
1462 scroll_x = SBX_Left;
1463 scroll_y = SBY_Upper;
1464 if (local_player->jx >= SBX_Left + MIDPOSX)
1465 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1466 local_player->jx - MIDPOSX :
1468 if (local_player->jy >= SBY_Upper + MIDPOSY)
1469 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1470 local_player->jy - MIDPOSY :
1475 CloseDoor(DOOR_CLOSE_1);
1480 /* after drawing the level, correct some elements */
1481 if (game.timegate_time_left == 0)
1482 CloseAllOpenTimegates();
1484 if (setup.soft_scrolling)
1485 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1487 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1490 /* copy default game door content to main double buffer */
1491 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1492 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1495 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1498 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1499 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1500 BlitBitmap(drawto, drawto,
1501 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1502 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1503 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1504 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1507 DrawGameDoorValues();
1511 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1512 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1513 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1517 /* copy actual game door content to door double buffer for OpenDoor() */
1518 BlitBitmap(drawto, bitmap_db_door,
1519 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1521 OpenDoor(DOOR_OPEN_ALL);
1523 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1525 if (setup.sound_music)
1528 KeyboardAutoRepeatOffUnlessAutoplay();
1532 for (i = 0; i < 4; i++)
1533 printf("Player %d %sactive.\n",
1534 i + 1, (stored_player[i].active ? "" : "not "));
1538 printf("::: starting game [%d]\n", FrameCounter);
1542 void InitMovDir(int x, int y)
1544 int i, element = Feld[x][y];
1545 static int xy[4][2] =
1552 static int direction[3][4] =
1554 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1555 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1556 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1565 Feld[x][y] = EL_BUG;
1566 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1569 case EL_SPACESHIP_RIGHT:
1570 case EL_SPACESHIP_UP:
1571 case EL_SPACESHIP_LEFT:
1572 case EL_SPACESHIP_DOWN:
1573 Feld[x][y] = EL_SPACESHIP;
1574 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1577 case EL_BD_BUTTERFLY_RIGHT:
1578 case EL_BD_BUTTERFLY_UP:
1579 case EL_BD_BUTTERFLY_LEFT:
1580 case EL_BD_BUTTERFLY_DOWN:
1581 Feld[x][y] = EL_BD_BUTTERFLY;
1582 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1585 case EL_BD_FIREFLY_RIGHT:
1586 case EL_BD_FIREFLY_UP:
1587 case EL_BD_FIREFLY_LEFT:
1588 case EL_BD_FIREFLY_DOWN:
1589 Feld[x][y] = EL_BD_FIREFLY;
1590 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1593 case EL_PACMAN_RIGHT:
1595 case EL_PACMAN_LEFT:
1596 case EL_PACMAN_DOWN:
1597 Feld[x][y] = EL_PACMAN;
1598 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1601 case EL_SP_SNIKSNAK:
1602 MovDir[x][y] = MV_UP;
1605 case EL_SP_ELECTRON:
1606 MovDir[x][y] = MV_LEFT;
1613 Feld[x][y] = EL_MOLE;
1614 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1618 if (IS_CUSTOM_ELEMENT(element))
1620 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1621 MovDir[x][y] = element_info[element].move_direction_initial;
1622 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1623 element_info[element].move_pattern == MV_TURNING_LEFT ||
1624 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1625 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1626 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1627 element_info[element].move_pattern == MV_TURNING_RANDOM)
1628 MovDir[x][y] = 1 << RND(4);
1629 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1630 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1631 else if (element_info[element].move_pattern == MV_VERTICAL)
1632 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1633 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1634 MovDir[x][y] = element_info[element].move_pattern;
1635 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1636 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1638 for (i = 0; i < 4; i++)
1640 int x1 = x + xy[i][0];
1641 int y1 = y + xy[i][1];
1643 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1645 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1646 MovDir[x][y] = direction[0][i];
1648 MovDir[x][y] = direction[1][i];
1657 MovDir[x][y] = 1 << RND(4);
1659 if (element != EL_BUG &&
1660 element != EL_SPACESHIP &&
1661 element != EL_BD_BUTTERFLY &&
1662 element != EL_BD_FIREFLY)
1665 for (i = 0; i < 4; i++)
1667 int x1 = x + xy[i][0];
1668 int y1 = y + xy[i][1];
1670 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1672 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1674 MovDir[x][y] = direction[0][i];
1677 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1678 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1680 MovDir[x][y] = direction[1][i];
1689 GfxDir[x][y] = MovDir[x][y];
1692 void InitAmoebaNr(int x, int y)
1695 int group_nr = AmoebeNachbarNr(x, y);
1699 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1701 if (AmoebaCnt[i] == 0)
1709 AmoebaNr[x][y] = group_nr;
1710 AmoebaCnt[group_nr]++;
1711 AmoebaCnt2[group_nr]++;
1717 boolean raise_level = FALSE;
1719 if (local_player->MovPos)
1723 if (tape.auto_play) /* tape might already be stopped here */
1724 tape.auto_play_level_solved = TRUE;
1726 if (tape.playing && tape.auto_play)
1727 tape.auto_play_level_solved = TRUE;
1730 local_player->LevelSolved = FALSE;
1732 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1736 if (!tape.playing && setup.sound_loops)
1737 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1738 SND_CTRL_PLAY_LOOP);
1740 while (TimeLeft > 0)
1742 if (!tape.playing && !setup.sound_loops)
1743 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1744 if (TimeLeft > 0 && !(TimeLeft % 10))
1745 RaiseScore(level.score[SC_TIME_BONUS]);
1746 if (TimeLeft > 100 && !(TimeLeft % 10))
1750 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1757 if (!tape.playing && setup.sound_loops)
1758 StopSound(SND_GAME_LEVELTIME_BONUS);
1760 else if (level.time == 0) /* level without time limit */
1762 if (!tape.playing && setup.sound_loops)
1763 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1764 SND_CTRL_PLAY_LOOP);
1766 while (TimePlayed < 999)
1768 if (!tape.playing && !setup.sound_loops)
1769 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1770 if (TimePlayed < 999 && !(TimePlayed % 10))
1771 RaiseScore(level.score[SC_TIME_BONUS]);
1772 if (TimePlayed < 900 && !(TimePlayed % 10))
1776 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1783 if (!tape.playing && setup.sound_loops)
1784 StopSound(SND_GAME_LEVELTIME_BONUS);
1787 /* close exit door after last player */
1788 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1789 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1791 int element = Feld[ExitX][ExitY];
1793 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1794 EL_SP_EXIT_CLOSING);
1796 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1799 /* Hero disappears */
1800 DrawLevelField(ExitX, ExitY);
1806 CloseDoor(DOOR_CLOSE_1);
1811 SaveTape(tape.level_nr); /* Ask to save tape */
1814 if (level_nr == leveldir_current->handicap_level)
1816 leveldir_current->handicap_level++;
1817 SaveLevelSetup_SeriesInfo();
1820 if (level_editor_test_game)
1821 local_player->score = -1; /* no highscore when playing from editor */
1822 else if (level_nr < leveldir_current->last_level)
1823 raise_level = TRUE; /* advance to next level */
1825 if ((hi_pos = NewHiScore()) >= 0)
1827 game_status = GAME_MODE_SCORES;
1828 DrawHallOfFame(hi_pos);
1837 game_status = GAME_MODE_MAIN;
1854 LoadScore(level_nr);
1856 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1857 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1860 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1862 if (local_player->score > highscore[k].Score)
1864 /* player has made it to the hall of fame */
1866 if (k < MAX_SCORE_ENTRIES - 1)
1868 int m = MAX_SCORE_ENTRIES - 1;
1871 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1872 if (!strcmp(setup.player_name, highscore[l].Name))
1874 if (m == k) /* player's new highscore overwrites his old one */
1878 for (l = m; l > k; l--)
1880 strcpy(highscore[l].Name, highscore[l - 1].Name);
1881 highscore[l].Score = highscore[l - 1].Score;
1888 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1889 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1890 highscore[k].Score = local_player->score;
1896 else if (!strncmp(setup.player_name, highscore[k].Name,
1897 MAX_PLAYER_NAME_LEN))
1898 break; /* player already there with a higher score */
1904 SaveScore(level_nr);
1909 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1911 if (player->GfxAction != action || player->GfxDir != dir)
1914 printf("Player frame reset! (%d => %d, %d => %d)\n",
1915 player->GfxAction, action, player->GfxDir, dir);
1918 player->GfxAction = action;
1919 player->GfxDir = dir;
1921 player->StepFrame = 0;
1925 static void ResetRandomAnimationValue(int x, int y)
1927 GfxRandom[x][y] = INIT_GFX_RANDOM();
1930 static void ResetGfxAnimation(int x, int y)
1933 GfxAction[x][y] = ACTION_DEFAULT;
1934 GfxDir[x][y] = MovDir[x][y];
1937 void InitMovingField(int x, int y, int direction)
1939 int element = Feld[x][y];
1940 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1941 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1945 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1946 ResetGfxAnimation(x, y);
1948 MovDir[newx][newy] = MovDir[x][y] = direction;
1949 GfxDir[x][y] = direction;
1951 if (Feld[newx][newy] == EL_EMPTY)
1952 Feld[newx][newy] = EL_BLOCKED;
1954 if (direction == MV_DOWN && CAN_FALL(element))
1955 GfxAction[x][y] = ACTION_FALLING;
1957 GfxAction[x][y] = ACTION_MOVING;
1959 GfxFrame[newx][newy] = GfxFrame[x][y];
1960 GfxRandom[newx][newy] = GfxRandom[x][y];
1961 GfxAction[newx][newy] = GfxAction[x][y];
1962 GfxDir[newx][newy] = GfxDir[x][y];
1965 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1967 int direction = MovDir[x][y];
1968 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1969 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1975 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1977 int oldx = x, oldy = y;
1978 int direction = MovDir[x][y];
1980 if (direction == MV_LEFT)
1982 else if (direction == MV_RIGHT)
1984 else if (direction == MV_UP)
1986 else if (direction == MV_DOWN)
1989 *comes_from_x = oldx;
1990 *comes_from_y = oldy;
1993 int MovingOrBlocked2Element(int x, int y)
1995 int element = Feld[x][y];
1997 if (element == EL_BLOCKED)
2001 Blocked2Moving(x, y, &oldx, &oldy);
2002 return Feld[oldx][oldy];
2008 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2010 /* like MovingOrBlocked2Element(), but if element is moving
2011 and (x,y) is the field the moving element is just leaving,
2012 return EL_BLOCKED instead of the element value */
2013 int element = Feld[x][y];
2015 if (IS_MOVING(x, y))
2017 if (element == EL_BLOCKED)
2021 Blocked2Moving(x, y, &oldx, &oldy);
2022 return Feld[oldx][oldy];
2031 static void RemoveField(int x, int y)
2033 Feld[x][y] = EL_EMPTY;
2040 ChangeDelay[x][y] = 0;
2041 ChangePage[x][y] = -1;
2042 Pushed[x][y] = FALSE;
2044 GfxElement[x][y] = EL_UNDEFINED;
2045 GfxAction[x][y] = ACTION_DEFAULT;
2046 GfxDir[x][y] = MV_NO_MOVING;
2049 void RemoveMovingField(int x, int y)
2051 int oldx = x, oldy = y, newx = x, newy = y;
2052 int element = Feld[x][y];
2053 int next_element = EL_UNDEFINED;
2055 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2058 if (IS_MOVING(x, y))
2060 Moving2Blocked(x, y, &newx, &newy);
2061 if (Feld[newx][newy] != EL_BLOCKED)
2064 else if (element == EL_BLOCKED)
2066 Blocked2Moving(x, y, &oldx, &oldy);
2067 if (!IS_MOVING(oldx, oldy))
2071 if (element == EL_BLOCKED &&
2072 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2073 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2074 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2075 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2076 next_element = get_next_element(Feld[oldx][oldy]);
2078 RemoveField(oldx, oldy);
2079 RemoveField(newx, newy);
2081 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2083 if (next_element != EL_UNDEFINED)
2084 Feld[oldx][oldy] = next_element;
2086 DrawLevelField(oldx, oldy);
2087 DrawLevelField(newx, newy);
2090 void DrawDynamite(int x, int y)
2092 int sx = SCREENX(x), sy = SCREENY(y);
2093 int graphic = el2img(Feld[x][y]);
2096 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2099 if (IS_WALKABLE_INSIDE(Back[x][y]))
2103 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2104 else if (Store[x][y])
2105 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2107 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2110 if (Back[x][y] || Store[x][y])
2111 DrawGraphicThruMask(sx, sy, graphic, frame);
2113 DrawGraphic(sx, sy, graphic, frame);
2115 if (game.emulation == EMU_SUPAPLEX)
2116 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2117 else if (Store[x][y])
2118 DrawGraphicThruMask(sx, sy, graphic, frame);
2120 DrawGraphic(sx, sy, graphic, frame);
2124 void CheckDynamite(int x, int y)
2126 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2130 if (MovDelay[x][y] != 0)
2133 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2140 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2142 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2143 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2144 StopSound(SND_DYNAMITE_ACTIVE);
2146 StopSound(SND_DYNABOMB_ACTIVE);
2152 void RelocatePlayer(int x, int y, int element)
2154 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2156 if (player->GameOver) /* do not reanimate dead player */
2160 RemoveField(x, y); /* temporarily remove newly placed player */
2161 DrawLevelField(x, y);
2164 if (player->present)
2166 while (player->MovPos)
2168 ScrollPlayer(player, SCROLL_GO_ON);
2169 ScrollScreen(NULL, SCROLL_GO_ON);
2175 Delay(GAME_FRAME_DELAY);
2178 DrawPlayer(player); /* needed here only to cleanup last field */
2179 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2181 player->is_moving = FALSE;
2184 Feld[x][y] = element;
2185 InitPlayerField(x, y, element, TRUE);
2187 if (player == local_player)
2189 int scroll_xx = -999, scroll_yy = -999;
2191 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2194 int fx = FX, fy = FY;
2196 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2197 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2198 local_player->jx - MIDPOSX);
2200 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2201 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2202 local_player->jy - MIDPOSY);
2204 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2205 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2210 fx += dx * TILEX / 2;
2211 fy += dy * TILEY / 2;
2213 ScrollLevel(dx, dy);
2216 /* scroll in two steps of half tile size to make things smoother */
2217 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2219 Delay(GAME_FRAME_DELAY);
2221 /* scroll second step to align at full tile size */
2223 Delay(GAME_FRAME_DELAY);
2228 void Explode(int ex, int ey, int phase, int mode)
2232 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2233 int last_phase = num_phase * delay;
2234 int half_phase = (num_phase / 2) * delay;
2235 int first_phase_after_start = EX_PHASE_START + 1;
2237 if (game.explosions_delayed)
2239 ExplodeField[ex][ey] = mode;
2243 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2245 int center_element = Feld[ex][ey];
2248 /* --- This is only really needed (and now handled) in "Impact()". --- */
2249 /* do not explode moving elements that left the explode field in time */
2250 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2251 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2255 if (mode == EX_NORMAL || mode == EX_CENTER)
2256 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2258 /* remove things displayed in background while burning dynamite */
2259 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2262 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2264 /* put moving element to center field (and let it explode there) */
2265 center_element = MovingOrBlocked2Element(ex, ey);
2266 RemoveMovingField(ex, ey);
2267 Feld[ex][ey] = center_element;
2270 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2272 int xx = x - ex + 1;
2273 int yy = y - ey + 1;
2276 if (!IN_LEV_FIELD(x, y) ||
2277 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2278 (x != ex || y != ey)))
2281 element = Feld[x][y];
2283 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2285 element = MovingOrBlocked2Element(x, y);
2287 if (!IS_EXPLOSION_PROOF(element))
2288 RemoveMovingField(x, y);
2294 if (IS_EXPLOSION_PROOF(element))
2297 /* indestructible elements can only explode in center (but not flames) */
2298 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2299 element == EL_FLAMES)
2304 if ((IS_INDESTRUCTIBLE(element) &&
2305 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2306 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2307 element == EL_FLAMES)
2311 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2313 if (IS_ACTIVE_BOMB(element))
2315 /* re-activate things under the bomb like gate or penguin */
2316 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2323 /* save walkable background elements while explosion on same tile */
2325 if (IS_INDESTRUCTIBLE(element))
2326 Back[x][y] = element;
2328 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2329 Back[x][y] = element;
2332 /* ignite explodable elements reached by other explosion */
2333 if (element == EL_EXPLOSION)
2334 element = Store2[x][y];
2337 if (AmoebaNr[x][y] &&
2338 (element == EL_AMOEBA_FULL ||
2339 element == EL_BD_AMOEBA ||
2340 element == EL_AMOEBA_GROWING))
2342 AmoebaCnt[AmoebaNr[x][y]]--;
2343 AmoebaCnt2[AmoebaNr[x][y]]--;
2349 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2351 switch(StorePlayer[ex][ey])
2354 Store[x][y] = EL_EMERALD_RED;
2357 Store[x][y] = EL_EMERALD;
2360 Store[x][y] = EL_EMERALD_PURPLE;
2364 Store[x][y] = EL_EMERALD_YELLOW;
2368 if (game.emulation == EMU_SUPAPLEX)
2369 Store[x][y] = EL_EMPTY;
2371 else if (center_element == EL_MOLE)
2372 Store[x][y] = EL_EMERALD_RED;
2373 else if (center_element == EL_PENGUIN)
2374 Store[x][y] = EL_EMERALD_PURPLE;
2375 else if (center_element == EL_BUG)
2376 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2377 else if (center_element == EL_BD_BUTTERFLY)
2378 Store[x][y] = EL_BD_DIAMOND;
2379 else if (center_element == EL_SP_ELECTRON)
2380 Store[x][y] = EL_SP_INFOTRON;
2381 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2382 Store[x][y] = level.amoeba_content;
2383 else if (center_element == EL_YAMYAM)
2384 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2385 else if (IS_CUSTOM_ELEMENT(center_element) &&
2386 element_info[center_element].content[xx][yy] != EL_EMPTY)
2387 Store[x][y] = element_info[center_element].content[xx][yy];
2388 else if (element == EL_WALL_EMERALD)
2389 Store[x][y] = EL_EMERALD;
2390 else if (element == EL_WALL_DIAMOND)
2391 Store[x][y] = EL_DIAMOND;
2392 else if (element == EL_WALL_BD_DIAMOND)
2393 Store[x][y] = EL_BD_DIAMOND;
2394 else if (element == EL_WALL_EMERALD_YELLOW)
2395 Store[x][y] = EL_EMERALD_YELLOW;
2396 else if (element == EL_WALL_EMERALD_RED)
2397 Store[x][y] = EL_EMERALD_RED;
2398 else if (element == EL_WALL_EMERALD_PURPLE)
2399 Store[x][y] = EL_EMERALD_PURPLE;
2400 else if (element == EL_WALL_PEARL)
2401 Store[x][y] = EL_PEARL;
2402 else if (element == EL_WALL_CRYSTAL)
2403 Store[x][y] = EL_CRYSTAL;
2404 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2405 Store[x][y] = element_info[element].content[1][1];
2407 Store[x][y] = EL_EMPTY;
2409 if (x != ex || y != ey ||
2410 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2411 Store2[x][y] = element;
2414 if (AmoebaNr[x][y] &&
2415 (element == EL_AMOEBA_FULL ||
2416 element == EL_BD_AMOEBA ||
2417 element == EL_AMOEBA_GROWING))
2419 AmoebaCnt[AmoebaNr[x][y]]--;
2420 AmoebaCnt2[AmoebaNr[x][y]]--;
2426 MovDir[x][y] = MovPos[x][y] = 0;
2427 GfxDir[x][y] = MovDir[x][y];
2432 Feld[x][y] = EL_EXPLOSION;
2434 GfxElement[x][y] = center_element;
2436 GfxElement[x][y] = EL_UNDEFINED;
2439 ExplodePhase[x][y] = 1;
2443 if (center_element == EL_YAMYAM)
2444 game.yamyam_content_nr =
2445 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2456 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2460 /* activate this even in non-DEBUG version until cause for crash in
2461 getGraphicAnimationFrame() (see below) is found and eliminated */
2465 if (GfxElement[x][y] == EL_UNDEFINED)
2468 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2469 printf("Explode(): This should never happen!\n");
2472 GfxElement[x][y] = EL_EMPTY;
2476 if (phase == first_phase_after_start)
2478 int element = Store2[x][y];
2480 if (element == EL_BLACK_ORB)
2482 Feld[x][y] = Store2[x][y];
2487 else if (phase == half_phase)
2489 int element = Store2[x][y];
2491 if (IS_PLAYER(x, y))
2492 KillHeroUnlessProtected(x, y);
2493 else if (CAN_EXPLODE_BY_FIRE(element))
2495 Feld[x][y] = Store2[x][y];
2499 else if (element == EL_AMOEBA_TO_DIAMOND)
2500 AmoebeUmwandeln(x, y);
2503 if (phase == last_phase)
2507 element = Feld[x][y] = Store[x][y];
2508 Store[x][y] = Store2[x][y] = 0;
2509 GfxElement[x][y] = EL_UNDEFINED;
2511 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2512 element = Feld[x][y] = Back[x][y];
2515 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2516 GfxDir[x][y] = MV_NO_MOVING;
2517 ChangeDelay[x][y] = 0;
2518 ChangePage[x][y] = -1;
2520 InitField(x, y, FALSE);
2521 if (CAN_MOVE(element))
2523 DrawLevelField(x, y);
2525 TestIfElementTouchesCustomElement(x, y);
2527 if (GFX_CRUMBLED(element))
2528 DrawLevelFieldCrumbledSandNeighbours(x, y);
2530 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2531 StorePlayer[x][y] = 0;
2533 if (ELEM_IS_PLAYER(element))
2534 RelocatePlayer(x, y, element);
2536 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2539 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2541 int stored = Store[x][y];
2542 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2543 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2546 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2549 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2550 element_info[GfxElement[x][y]].token_name,
2555 DrawLevelFieldCrumbledSand(x, y);
2557 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2559 DrawLevelElement(x, y, Back[x][y]);
2560 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2562 else if (IS_WALKABLE_UNDER(Back[x][y]))
2564 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2565 DrawLevelElementThruMask(x, y, Back[x][y]);
2567 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2568 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2572 void DynaExplode(int ex, int ey)
2575 int dynabomb_element = Feld[ex][ey];
2576 int dynabomb_size = 1;
2577 boolean dynabomb_xl = FALSE;
2578 struct PlayerInfo *player;
2579 static int xy[4][2] =
2587 if (IS_ACTIVE_BOMB(dynabomb_element))
2589 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2590 dynabomb_size = player->dynabomb_size;
2591 dynabomb_xl = player->dynabomb_xl;
2592 player->dynabombs_left++;
2595 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2597 for (i = 0; i < 4; i++)
2599 for (j = 1; j <= dynabomb_size; j++)
2601 int x = ex + j * xy[i % 4][0];
2602 int y = ey + j * xy[i % 4][1];
2605 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2608 element = Feld[x][y];
2610 /* do not restart explosions of fields with active bombs */
2611 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2614 Explode(x, y, EX_PHASE_START, EX_BORDER);
2616 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2617 if (element != EL_EMPTY &&
2618 element != EL_SAND &&
2619 element != EL_EXPLOSION &&
2626 void Bang(int x, int y)
2629 int element = MovingOrBlocked2Element(x, y);
2631 int element = Feld[x][y];
2635 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2637 if (IS_PLAYER(x, y))
2640 struct PlayerInfo *player = PLAYERINFO(x, y);
2642 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2643 player->element_nr);
2648 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2650 if (game.emulation == EMU_SUPAPLEX)
2651 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2653 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2658 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2666 case EL_BD_BUTTERFLY:
2669 case EL_DARK_YAMYAM:
2673 RaiseScoreElement(element);
2674 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2676 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2677 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2678 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2679 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2680 case EL_DYNABOMB_INCREASE_NUMBER:
2681 case EL_DYNABOMB_INCREASE_SIZE:
2682 case EL_DYNABOMB_INCREASE_POWER:
2687 case EL_LAMP_ACTIVE:
2688 if (IS_PLAYER(x, y))
2689 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2691 Explode(x, y, EX_PHASE_START, EX_CENTER);
2694 if (CAN_EXPLODE_DYNA(element))
2696 else if (CAN_EXPLODE_1X1(element))
2697 Explode(x, y, EX_PHASE_START, EX_CENTER);
2699 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2703 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2706 void SplashAcid(int x, int y)
2708 int element = Feld[x][y];
2710 if (element != EL_ACID_SPLASH_LEFT &&
2711 element != EL_ACID_SPLASH_RIGHT)
2713 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2715 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2716 (!IN_LEV_FIELD(x-1, y-1) ||
2717 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2718 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2720 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2721 (!IN_LEV_FIELD(x+1, y-1) ||
2722 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2723 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2727 static void InitBeltMovement()
2729 static int belt_base_element[4] =
2731 EL_CONVEYOR_BELT_1_LEFT,
2732 EL_CONVEYOR_BELT_2_LEFT,
2733 EL_CONVEYOR_BELT_3_LEFT,
2734 EL_CONVEYOR_BELT_4_LEFT
2736 static int belt_base_active_element[4] =
2738 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2739 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2740 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2741 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2746 /* set frame order for belt animation graphic according to belt direction */
2747 for (i = 0; i < 4; i++)
2751 for (j = 0; j < 3; j++)
2753 int element = belt_base_active_element[belt_nr] + j;
2754 int graphic = el2img(element);
2756 if (game.belt_dir[i] == MV_LEFT)
2757 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2759 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2763 for (y = 0; y < lev_fieldy; y++)
2765 for (x = 0; x < lev_fieldx; x++)
2767 int element = Feld[x][y];
2769 for (i = 0; i < 4; i++)
2771 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2773 int e_belt_nr = getBeltNrFromBeltElement(element);
2776 if (e_belt_nr == belt_nr)
2778 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2780 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2788 static void ToggleBeltSwitch(int x, int y)
2790 static int belt_base_element[4] =
2792 EL_CONVEYOR_BELT_1_LEFT,
2793 EL_CONVEYOR_BELT_2_LEFT,
2794 EL_CONVEYOR_BELT_3_LEFT,
2795 EL_CONVEYOR_BELT_4_LEFT
2797 static int belt_base_active_element[4] =
2799 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2800 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2801 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2802 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2804 static int belt_base_switch_element[4] =
2806 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2807 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2808 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2809 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2811 static int belt_move_dir[4] =
2819 int element = Feld[x][y];
2820 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2821 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2822 int belt_dir = belt_move_dir[belt_dir_nr];
2825 if (!IS_BELT_SWITCH(element))
2828 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2829 game.belt_dir[belt_nr] = belt_dir;
2831 if (belt_dir_nr == 3)
2834 /* set frame order for belt animation graphic according to belt direction */
2835 for (i = 0; i < 3; i++)
2837 int element = belt_base_active_element[belt_nr] + i;
2838 int graphic = el2img(element);
2840 if (belt_dir == MV_LEFT)
2841 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2843 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2846 for (yy = 0; yy < lev_fieldy; yy++)
2848 for (xx = 0; xx < lev_fieldx; xx++)
2850 int element = Feld[xx][yy];
2852 if (IS_BELT_SWITCH(element))
2854 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2856 if (e_belt_nr == belt_nr)
2858 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2859 DrawLevelField(xx, yy);
2862 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2864 int e_belt_nr = getBeltNrFromBeltElement(element);
2866 if (e_belt_nr == belt_nr)
2868 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2870 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2871 DrawLevelField(xx, yy);
2874 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2876 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2878 if (e_belt_nr == belt_nr)
2880 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2882 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2883 DrawLevelField(xx, yy);
2890 static void ToggleSwitchgateSwitch(int x, int y)
2894 game.switchgate_pos = !game.switchgate_pos;
2896 for (yy = 0; yy < lev_fieldy; yy++)
2898 for (xx = 0; xx < lev_fieldx; xx++)
2900 int element = Feld[xx][yy];
2902 if (element == EL_SWITCHGATE_SWITCH_UP ||
2903 element == EL_SWITCHGATE_SWITCH_DOWN)
2905 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2906 DrawLevelField(xx, yy);
2908 else if (element == EL_SWITCHGATE_OPEN ||
2909 element == EL_SWITCHGATE_OPENING)
2911 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2913 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2915 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2918 else if (element == EL_SWITCHGATE_CLOSED ||
2919 element == EL_SWITCHGATE_CLOSING)
2921 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2923 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2925 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2932 static int getInvisibleActiveFromInvisibleElement(int element)
2934 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2935 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2936 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2940 static int getInvisibleFromInvisibleActiveElement(int element)
2942 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2943 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2944 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2948 static void RedrawAllLightSwitchesAndInvisibleElements()
2952 for (y = 0; y < lev_fieldy; y++)
2954 for (x = 0; x < lev_fieldx; x++)
2956 int element = Feld[x][y];
2958 if (element == EL_LIGHT_SWITCH &&
2959 game.light_time_left > 0)
2961 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2962 DrawLevelField(x, y);
2964 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2965 game.light_time_left == 0)
2967 Feld[x][y] = EL_LIGHT_SWITCH;
2968 DrawLevelField(x, y);
2970 else if (element == EL_INVISIBLE_STEELWALL ||
2971 element == EL_INVISIBLE_WALL ||
2972 element == EL_INVISIBLE_SAND)
2974 if (game.light_time_left > 0)
2975 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2977 DrawLevelField(x, y);
2979 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2980 element == EL_INVISIBLE_WALL_ACTIVE ||
2981 element == EL_INVISIBLE_SAND_ACTIVE)
2983 if (game.light_time_left == 0)
2984 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2986 DrawLevelField(x, y);
2992 static void ToggleLightSwitch(int x, int y)
2994 int element = Feld[x][y];
2996 game.light_time_left =
2997 (element == EL_LIGHT_SWITCH ?
2998 level.time_light * FRAMES_PER_SECOND : 0);
3000 RedrawAllLightSwitchesAndInvisibleElements();
3003 static void ActivateTimegateSwitch(int x, int y)
3007 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3009 for (yy = 0; yy < lev_fieldy; yy++)
3011 for (xx = 0; xx < lev_fieldx; xx++)
3013 int element = Feld[xx][yy];
3015 if (element == EL_TIMEGATE_CLOSED ||
3016 element == EL_TIMEGATE_CLOSING)
3018 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3019 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3023 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3025 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3026 DrawLevelField(xx, yy);
3033 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3036 inline static int getElementMoveStepsize(int x, int y)
3038 int element = Feld[x][y];
3039 int direction = MovDir[x][y];
3040 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3041 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3042 int horiz_move = (dx != 0);
3043 int sign = (horiz_move ? dx : dy);
3044 int step = sign * element_info[element].move_stepsize;
3046 /* special values for move stepsize for spring and things on conveyor belt */
3049 if (CAN_FALL(element) &&
3050 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3051 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3052 else if (element == EL_SPRING)
3053 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3059 void Impact(int x, int y)
3061 boolean lastline = (y == lev_fieldy-1);
3062 boolean object_hit = FALSE;
3063 boolean impact = (lastline || object_hit);
3064 int element = Feld[x][y];
3065 int smashed = EL_UNDEFINED;
3067 if (!lastline) /* check if element below was hit */
3069 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3072 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3073 MovDir[x][y + 1] != MV_DOWN ||
3074 MovPos[x][y + 1] <= TILEY / 2));
3077 object_hit = !IS_FREE(x, y + 1);
3080 /* do not smash moving elements that left the smashed field in time */
3081 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3082 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3086 smashed = MovingOrBlocked2Element(x, y + 1);
3088 impact = (lastline || object_hit);
3091 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3097 /* only reset graphic animation if graphic really changes after impact */
3099 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3101 ResetGfxAnimation(x, y);
3102 DrawLevelField(x, y);
3105 if (impact && CAN_EXPLODE_IMPACT(element))
3110 else if (impact && element == EL_PEARL)
3112 Feld[x][y] = EL_PEARL_BREAKING;
3113 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3116 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3118 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3123 if (impact && element == EL_AMOEBA_DROP)
3125 if (object_hit && IS_PLAYER(x, y + 1))
3126 KillHeroUnlessProtected(x, y + 1);
3127 else if (object_hit && smashed == EL_PENGUIN)
3131 Feld[x][y] = EL_AMOEBA_GROWING;
3132 Store[x][y] = EL_AMOEBA_WET;
3134 ResetRandomAnimationValue(x, y);
3139 if (object_hit) /* check which object was hit */
3141 if (CAN_PASS_MAGIC_WALL(element) &&
3142 (smashed == EL_MAGIC_WALL ||
3143 smashed == EL_BD_MAGIC_WALL))
3146 int activated_magic_wall =
3147 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3148 EL_BD_MAGIC_WALL_ACTIVE);
3150 /* activate magic wall / mill */
3151 for (yy = 0; yy < lev_fieldy; yy++)
3152 for (xx = 0; xx < lev_fieldx; xx++)
3153 if (Feld[xx][yy] == smashed)
3154 Feld[xx][yy] = activated_magic_wall;
3156 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3157 game.magic_wall_active = TRUE;
3159 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3160 SND_MAGIC_WALL_ACTIVATING :
3161 SND_BD_MAGIC_WALL_ACTIVATING));
3164 if (IS_PLAYER(x, y + 1))
3166 if (CAN_SMASH_PLAYER(element))
3168 KillHeroUnlessProtected(x, y + 1);
3172 else if (smashed == EL_PENGUIN)
3174 if (CAN_SMASH_PLAYER(element))
3180 else if (element == EL_BD_DIAMOND)
3182 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3188 else if ((element == EL_SP_INFOTRON ||
3189 element == EL_SP_ZONK) &&
3190 (smashed == EL_SP_SNIKSNAK ||
3191 smashed == EL_SP_ELECTRON ||
3192 smashed == EL_SP_DISK_ORANGE))
3198 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3204 else if (CAN_SMASH_EVERYTHING(element))
3206 if (IS_CLASSIC_ENEMY(smashed) ||
3207 CAN_EXPLODE_SMASHED(smashed))
3212 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3214 if (smashed == EL_LAMP ||
3215 smashed == EL_LAMP_ACTIVE)
3220 else if (smashed == EL_NUT)
3222 Feld[x][y + 1] = EL_NUT_BREAKING;
3223 PlayLevelSound(x, y, SND_NUT_BREAKING);
3224 RaiseScoreElement(EL_NUT);
3227 else if (smashed == EL_PEARL)
3229 Feld[x][y + 1] = EL_PEARL_BREAKING;
3230 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3233 else if (smashed == EL_DIAMOND)
3235 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3236 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3239 else if (IS_BELT_SWITCH(smashed))
3241 ToggleBeltSwitch(x, y + 1);
3243 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3244 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3246 ToggleSwitchgateSwitch(x, y + 1);
3248 else if (smashed == EL_LIGHT_SWITCH ||
3249 smashed == EL_LIGHT_SWITCH_ACTIVE)
3251 ToggleLightSwitch(x, y + 1);
3255 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3257 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3258 CE_OTHER_IS_SWITCHING);
3259 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3265 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3270 /* play sound of magic wall / mill */
3272 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3273 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3275 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3276 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3277 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3278 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3283 /* play sound of object that hits the ground */
3284 if (lastline || object_hit)
3285 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3288 inline static void TurnRoundExt(int x, int y)
3300 { 0, 0 }, { 0, 0 }, { 0, 0 },
3305 int left, right, back;
3309 { MV_DOWN, MV_UP, MV_RIGHT },
3310 { MV_UP, MV_DOWN, MV_LEFT },
3312 { MV_LEFT, MV_RIGHT, MV_DOWN },
3316 { MV_RIGHT, MV_LEFT, MV_UP }
3319 int element = Feld[x][y];
3320 int move_pattern = element_info[element].move_pattern;
3322 int old_move_dir = MovDir[x][y];
3323 int left_dir = turn[old_move_dir].left;
3324 int right_dir = turn[old_move_dir].right;
3325 int back_dir = turn[old_move_dir].back;
3327 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3328 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3329 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3330 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3332 int left_x = x + left_dx, left_y = y + left_dy;
3333 int right_x = x + right_dx, right_y = y + right_dy;
3334 int move_x = x + move_dx, move_y = y + move_dy;
3338 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3340 TestIfBadThingTouchesOtherBadThing(x, y);
3342 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3343 MovDir[x][y] = right_dir;
3344 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3345 MovDir[x][y] = left_dir;
3347 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3349 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3352 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3353 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3355 TestIfBadThingTouchesOtherBadThing(x, y);
3357 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3358 MovDir[x][y] = left_dir;
3359 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3360 MovDir[x][y] = right_dir;
3362 if ((element == EL_SPACESHIP ||
3363 element == EL_SP_SNIKSNAK ||
3364 element == EL_SP_ELECTRON)
3365 && MovDir[x][y] != old_move_dir)
3367 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3370 else if (element == EL_YAMYAM)
3372 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3373 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3375 if (can_turn_left && can_turn_right)
3376 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3377 else if (can_turn_left)
3378 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3379 else if (can_turn_right)
3380 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3382 MovDir[x][y] = back_dir;
3384 MovDelay[x][y] = 16 + 16 * RND(3);
3386 else if (element == EL_DARK_YAMYAM)
3388 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3389 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3391 if (can_turn_left && can_turn_right)
3392 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3393 else if (can_turn_left)
3394 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3395 else if (can_turn_right)
3396 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3398 MovDir[x][y] = back_dir;
3400 MovDelay[x][y] = 16 + 16 * RND(3);
3402 else if (element == EL_PACMAN)
3404 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3405 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3407 if (can_turn_left && can_turn_right)
3408 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3409 else if (can_turn_left)
3410 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3411 else if (can_turn_right)
3412 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3414 MovDir[x][y] = back_dir;
3416 MovDelay[x][y] = 6 + RND(40);
3418 else if (element == EL_PIG)
3420 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3421 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3422 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3423 boolean should_turn_left, should_turn_right, should_move_on;
3425 int rnd = RND(rnd_value);
3427 should_turn_left = (can_turn_left &&
3429 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3430 y + back_dy + left_dy)));
3431 should_turn_right = (can_turn_right &&
3433 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3434 y + back_dy + right_dy)));
3435 should_move_on = (can_move_on &&
3438 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3439 y + move_dy + left_dy) ||
3440 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3441 y + move_dy + right_dy)));
3443 if (should_turn_left || should_turn_right || should_move_on)
3445 if (should_turn_left && should_turn_right && should_move_on)
3446 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3447 rnd < 2 * rnd_value / 3 ? right_dir :
3449 else if (should_turn_left && should_turn_right)
3450 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3451 else if (should_turn_left && should_move_on)
3452 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3453 else if (should_turn_right && should_move_on)
3454 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3455 else if (should_turn_left)
3456 MovDir[x][y] = left_dir;
3457 else if (should_turn_right)
3458 MovDir[x][y] = right_dir;
3459 else if (should_move_on)
3460 MovDir[x][y] = old_move_dir;
3462 else if (can_move_on && rnd > rnd_value / 8)
3463 MovDir[x][y] = old_move_dir;
3464 else if (can_turn_left && can_turn_right)
3465 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3466 else if (can_turn_left && rnd > rnd_value / 8)
3467 MovDir[x][y] = left_dir;
3468 else if (can_turn_right && rnd > rnd_value/8)
3469 MovDir[x][y] = right_dir;
3471 MovDir[x][y] = back_dir;
3473 xx = x + move_xy[MovDir[x][y]].x;
3474 yy = y + move_xy[MovDir[x][y]].y;
3476 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3477 MovDir[x][y] = old_move_dir;
3481 else if (element == EL_DRAGON)
3483 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3484 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3485 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3487 int rnd = RND(rnd_value);
3490 if (FrameCounter < 1 && x == 0 && y == 29)
3491 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3494 if (can_move_on && rnd > rnd_value / 8)
3495 MovDir[x][y] = old_move_dir;
3496 else if (can_turn_left && can_turn_right)
3497 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3498 else if (can_turn_left && rnd > rnd_value / 8)
3499 MovDir[x][y] = left_dir;
3500 else if (can_turn_right && rnd > rnd_value / 8)
3501 MovDir[x][y] = right_dir;
3503 MovDir[x][y] = back_dir;
3505 xx = x + move_xy[MovDir[x][y]].x;
3506 yy = y + move_xy[MovDir[x][y]].y;
3509 if (FrameCounter < 1 && x == 0 && y == 29)
3510 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3511 xx, yy, Feld[xx][yy],
3516 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3517 MovDir[x][y] = old_move_dir;
3519 if (!IS_FREE(xx, yy))
3520 MovDir[x][y] = old_move_dir;
3524 if (FrameCounter < 1 && x == 0 && y == 29)
3525 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3530 else if (element == EL_MOLE)
3532 boolean can_move_on =
3533 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3534 IS_AMOEBOID(Feld[move_x][move_y]) ||
3535 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3538 boolean can_turn_left =
3539 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3540 IS_AMOEBOID(Feld[left_x][left_y])));
3542 boolean can_turn_right =
3543 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3544 IS_AMOEBOID(Feld[right_x][right_y])));
3546 if (can_turn_left && can_turn_right)
3547 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3548 else if (can_turn_left)
3549 MovDir[x][y] = left_dir;
3551 MovDir[x][y] = right_dir;
3554 if (MovDir[x][y] != old_move_dir)
3557 else if (element == EL_BALLOON)
3559 MovDir[x][y] = game.balloon_dir;
3562 else if (element == EL_SPRING)
3564 if (MovDir[x][y] & MV_HORIZONTAL &&
3565 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3566 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3567 MovDir[x][y] = MV_NO_MOVING;
3571 else if (element == EL_ROBOT ||
3572 element == EL_SATELLITE ||
3573 element == EL_PENGUIN)
3575 int attr_x = -1, attr_y = -1;
3586 for (i = 0; i < MAX_PLAYERS; i++)
3588 struct PlayerInfo *player = &stored_player[i];
3589 int jx = player->jx, jy = player->jy;
3591 if (!player->active)
3595 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3603 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3609 if (element == EL_PENGUIN)
3612 static int xy[4][2] =
3620 for (i = 0; i < 4; i++)
3622 int ex = x + xy[i % 4][0];
3623 int ey = y + xy[i % 4][1];
3625 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3634 MovDir[x][y] = MV_NO_MOVING;
3636 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3637 else if (attr_x > x)
3638 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3640 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3641 else if (attr_y > y)
3642 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3644 if (element == EL_ROBOT)
3648 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3649 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3650 Moving2Blocked(x, y, &newx, &newy);
3652 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3653 MovDelay[x][y] = 8 + 8 * !RND(3);
3655 MovDelay[x][y] = 16;
3657 else if (element == EL_PENGUIN)
3663 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3665 boolean first_horiz = RND(2);
3666 int new_move_dir = MovDir[x][y];
3669 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3670 Moving2Blocked(x, y, &newx, &newy);
3672 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3676 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3677 Moving2Blocked(x, y, &newx, &newy);
3679 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3682 MovDir[x][y] = old_move_dir;
3686 else /* (element == EL_SATELLITE) */
3692 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3694 boolean first_horiz = RND(2);
3695 int new_move_dir = MovDir[x][y];
3698 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3699 Moving2Blocked(x, y, &newx, &newy);
3701 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3705 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3706 Moving2Blocked(x, y, &newx, &newy);
3708 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3711 MovDir[x][y] = old_move_dir;
3716 else if (move_pattern == MV_TURNING_LEFT ||
3717 move_pattern == MV_TURNING_RIGHT ||
3718 move_pattern == MV_TURNING_LEFT_RIGHT ||
3719 move_pattern == MV_TURNING_RIGHT_LEFT ||
3720 move_pattern == MV_TURNING_RANDOM ||
3721 move_pattern == MV_ALL_DIRECTIONS)
3723 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3724 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3726 if (move_pattern == MV_TURNING_LEFT)
3727 MovDir[x][y] = left_dir;
3728 else if (move_pattern == MV_TURNING_RIGHT)
3729 MovDir[x][y] = right_dir;
3730 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3731 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3732 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3733 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3734 else if (move_pattern == MV_TURNING_RANDOM)
3735 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3736 can_turn_right && !can_turn_left ? right_dir :
3737 RND(2) ? left_dir : right_dir);
3738 else if (can_turn_left && can_turn_right)
3739 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3740 else if (can_turn_left)
3741 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3742 else if (can_turn_right)
3743 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3745 MovDir[x][y] = back_dir;
3747 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3749 else if (move_pattern == MV_HORIZONTAL ||
3750 move_pattern == MV_VERTICAL)
3752 if (move_pattern & old_move_dir)
3753 MovDir[x][y] = back_dir;
3754 else if (move_pattern == MV_HORIZONTAL)
3755 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3756 else if (move_pattern == MV_VERTICAL)
3757 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3759 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3761 else if (move_pattern & MV_ANY_DIRECTION)
3763 MovDir[x][y] = move_pattern;
3764 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3766 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3768 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3769 MovDir[x][y] = left_dir;
3770 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3771 MovDir[x][y] = right_dir;
3773 if (MovDir[x][y] != old_move_dir)
3774 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3776 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3778 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3779 MovDir[x][y] = right_dir;
3780 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3781 MovDir[x][y] = left_dir;
3783 if (MovDir[x][y] != old_move_dir)
3784 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3786 else if (move_pattern == MV_TOWARDS_PLAYER ||
3787 move_pattern == MV_AWAY_FROM_PLAYER)
3789 int attr_x = -1, attr_y = -1;
3791 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3802 for (i = 0; i < MAX_PLAYERS; i++)
3804 struct PlayerInfo *player = &stored_player[i];
3805 int jx = player->jx, jy = player->jy;
3807 if (!player->active)
3811 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3819 MovDir[x][y] = MV_NO_MOVING;
3821 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3822 else if (attr_x > x)
3823 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3825 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3826 else if (attr_y > y)
3827 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3829 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3831 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3833 boolean first_horiz = RND(2);
3834 int new_move_dir = MovDir[x][y];
3837 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3838 Moving2Blocked(x, y, &newx, &newy);
3840 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3844 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3845 Moving2Blocked(x, y, &newx, &newy);
3847 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3850 MovDir[x][y] = old_move_dir;
3853 else if (move_pattern == MV_WHEN_PUSHED ||
3854 move_pattern == MV_WHEN_DROPPED)
3856 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3857 MovDir[x][y] = MV_NO_MOVING;
3861 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3862 element == EL_MAZE_RUNNER)
3864 static int test_xy[7][2] =
3874 static int test_dir[7] =
3884 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3885 int move_preference = -1000000; /* start with very low preference */
3886 int new_move_dir = MV_NO_MOVING;
3887 int start_test = RND(4);
3890 for (i = 0; i < 4; i++)
3892 int move_dir = test_dir[start_test + i];
3893 int move_dir_preference;
3895 xx = x + test_xy[start_test + i][0];
3896 yy = y + test_xy[start_test + i][1];
3898 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3899 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3901 new_move_dir = move_dir;
3906 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3909 move_dir_preference = -1 * RunnerVisit[xx][yy];
3910 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3911 move_dir_preference = PlayerVisit[xx][yy];
3913 if (move_dir_preference > move_preference)
3915 /* prefer field that has not been visited for the longest time */
3916 move_preference = move_dir_preference;
3917 new_move_dir = move_dir;
3919 else if (move_dir_preference == move_preference &&
3920 move_dir == old_move_dir)
3922 /* prefer last direction when all directions are preferred equally */
3923 move_preference = move_dir_preference;
3924 new_move_dir = move_dir;
3928 MovDir[x][y] = new_move_dir;
3929 if (old_move_dir != new_move_dir)
3934 static void TurnRound(int x, int y)
3936 int direction = MovDir[x][y];
3939 GfxDir[x][y] = MovDir[x][y];
3945 GfxDir[x][y] = MovDir[x][y];
3948 if (direction != MovDir[x][y])
3953 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3956 GfxAction[x][y] = ACTION_WAITING;
3960 static boolean JustBeingPushed(int x, int y)
3964 for (i = 0; i < MAX_PLAYERS; i++)
3966 struct PlayerInfo *player = &stored_player[i];
3968 if (player->active && player->is_pushing && player->MovPos)
3970 int next_jx = player->jx + (player->jx - player->last_jx);
3971 int next_jy = player->jy + (player->jy - player->last_jy);
3973 if (x == next_jx && y == next_jy)
3981 void StartMoving(int x, int y)
3983 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3984 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3985 int element = Feld[x][y];
3991 if (MovDelay[x][y] == 0)
3992 GfxAction[x][y] = ACTION_DEFAULT;
3994 /* !!! this should be handled more generic (not only for mole) !!! */
3995 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3996 GfxAction[x][y] = ACTION_DEFAULT;
3999 if (CAN_FALL(element) && y < lev_fieldy - 1)
4001 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4002 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4003 if (JustBeingPushed(x, y))
4006 if (element == EL_QUICKSAND_FULL)
4008 if (IS_FREE(x, y + 1))
4010 InitMovingField(x, y, MV_DOWN);
4011 started_moving = TRUE;
4013 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4014 Store[x][y] = EL_ROCK;
4016 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4018 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4021 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4023 if (!MovDelay[x][y])
4024 MovDelay[x][y] = TILEY + 1;
4033 Feld[x][y] = EL_QUICKSAND_EMPTY;
4034 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4035 Store[x][y + 1] = Store[x][y];
4038 PlayLevelSoundAction(x, y, ACTION_FILLING);
4040 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4044 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4045 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4047 InitMovingField(x, y, MV_DOWN);
4048 started_moving = TRUE;
4050 Feld[x][y] = EL_QUICKSAND_FILLING;
4051 Store[x][y] = element;
4053 PlayLevelSoundAction(x, y, ACTION_FILLING);
4055 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4058 else if (element == EL_MAGIC_WALL_FULL)
4060 if (IS_FREE(x, y + 1))
4062 InitMovingField(x, y, MV_DOWN);
4063 started_moving = TRUE;
4065 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4066 Store[x][y] = EL_CHANGED(Store[x][y]);
4068 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4070 if (!MovDelay[x][y])
4071 MovDelay[x][y] = TILEY/4 + 1;
4080 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4081 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4082 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4086 else if (element == EL_BD_MAGIC_WALL_FULL)
4088 if (IS_FREE(x, y + 1))
4090 InitMovingField(x, y, MV_DOWN);
4091 started_moving = TRUE;
4093 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4094 Store[x][y] = EL_CHANGED2(Store[x][y]);
4096 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4098 if (!MovDelay[x][y])
4099 MovDelay[x][y] = TILEY/4 + 1;
4108 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4109 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4110 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4114 else if (CAN_PASS_MAGIC_WALL(element) &&
4115 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4116 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4118 InitMovingField(x, y, MV_DOWN);
4119 started_moving = TRUE;
4122 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4123 EL_BD_MAGIC_WALL_FILLING);
4124 Store[x][y] = element;
4127 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4129 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4134 InitMovingField(x, y, MV_DOWN);
4135 started_moving = TRUE;
4137 Store[x][y] = EL_ACID;
4139 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4140 GfxAction[x][y + 1] = ACTION_ACTIVE;
4144 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4145 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4146 (Feld[x][y + 1] == EL_BLOCKED)) ||
4147 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4148 CAN_SMASH(element) && WasJustFalling[x][y] &&
4149 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4153 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4154 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4155 WasJustMoving[x][y] && !Pushed[x][y + 1])
4157 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4158 WasJustMoving[x][y])
4163 /* this is needed for a special case not covered by calling "Impact()"
4164 from "ContinueMoving()": if an element moves to a tile directly below
4165 another element which was just falling on that tile (which was empty
4166 in the previous frame), the falling element above would just stop
4167 instead of smashing the element below (in previous version, the above
4168 element was just checked for "moving" instead of "falling", resulting
4169 in incorrect smashes caused by horizontal movement of the above
4170 element; also, the case of the player being the element to smash was
4171 simply not covered here... :-/ ) */
4174 WasJustMoving[x][y] = 0;
4175 WasJustFalling[x][y] = 0;
4180 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4182 if (MovDir[x][y] == MV_NO_MOVING)
4184 InitMovingField(x, y, MV_DOWN);
4185 started_moving = TRUE;
4188 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4190 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4191 MovDir[x][y] = MV_DOWN;
4193 InitMovingField(x, y, MV_DOWN);
4194 started_moving = TRUE;
4196 else if (element == EL_AMOEBA_DROP)
4198 Feld[x][y] = EL_AMOEBA_GROWING;
4199 Store[x][y] = EL_AMOEBA_WET;
4201 /* Store[x][y + 1] must be zero, because:
4202 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4205 #if OLD_GAME_BEHAVIOUR
4206 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4208 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4209 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4210 element != EL_DX_SUPABOMB)
4213 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4214 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4215 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4216 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4219 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4220 (IS_FREE(x - 1, y + 1) ||
4221 Feld[x - 1][y + 1] == EL_ACID));
4222 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4223 (IS_FREE(x + 1, y + 1) ||
4224 Feld[x + 1][y + 1] == EL_ACID));
4225 boolean can_fall_any = (can_fall_left || can_fall_right);
4226 boolean can_fall_both = (can_fall_left && can_fall_right);
4228 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4230 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4232 if (slippery_type == SLIPPERY_ONLY_LEFT)
4233 can_fall_right = FALSE;
4234 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4235 can_fall_left = FALSE;
4236 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4237 can_fall_right = FALSE;
4238 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4239 can_fall_left = FALSE;
4241 can_fall_any = (can_fall_left || can_fall_right);
4242 can_fall_both = (can_fall_left && can_fall_right);
4247 if (can_fall_both &&
4248 (game.emulation != EMU_BOULDERDASH &&
4249 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4250 can_fall_left = !(can_fall_right = RND(2));
4252 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4253 started_moving = TRUE;
4256 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4258 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4259 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4260 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4261 int belt_dir = game.belt_dir[belt_nr];
4263 if ((belt_dir == MV_LEFT && left_is_free) ||
4264 (belt_dir == MV_RIGHT && right_is_free))
4266 InitMovingField(x, y, belt_dir);
4267 started_moving = TRUE;
4269 GfxAction[x][y] = ACTION_DEFAULT;
4274 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4275 if (CAN_MOVE(element) && !started_moving)
4277 int move_pattern = element_info[element].move_pattern;
4280 Moving2Blocked(x, y, &newx, &newy);
4283 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4286 if ((element == EL_SATELLITE ||
4287 element == EL_BALLOON ||
4288 element == EL_SPRING)
4289 && JustBeingPushed(x, y))
4294 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4295 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4296 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4299 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4300 element, element_info[element].token_name,
4301 WasJustMoving[x][y],
4302 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4303 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4304 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4305 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4309 WasJustMoving[x][y] = 0;
4312 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4315 if (Feld[x][y] != element) /* element has changed */
4317 element = Feld[x][y];
4318 move_pattern = element_info[element].move_pattern;
4320 if (!CAN_MOVE(element))
4324 if (Feld[x][y] != element) /* element has changed */
4332 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4333 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4335 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4337 Moving2Blocked(x, y, &newx, &newy);
4338 if (Feld[newx][newy] == EL_BLOCKED)
4339 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4345 if (FrameCounter < 1 && x == 0 && y == 29)
4346 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4349 if (!MovDelay[x][y]) /* start new movement phase */
4351 /* all objects that can change their move direction after each step
4352 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4354 if (element != EL_YAMYAM &&
4355 element != EL_DARK_YAMYAM &&
4356 element != EL_PACMAN &&
4357 !(move_pattern & MV_ANY_DIRECTION) &&
4358 move_pattern != MV_TURNING_LEFT &&
4359 move_pattern != MV_TURNING_RIGHT &&
4360 move_pattern != MV_TURNING_LEFT_RIGHT &&
4361 move_pattern != MV_TURNING_RIGHT_LEFT &&
4362 move_pattern != MV_TURNING_RANDOM)
4367 if (FrameCounter < 1 && x == 0 && y == 29)
4368 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4371 if (MovDelay[x][y] && (element == EL_BUG ||
4372 element == EL_SPACESHIP ||
4373 element == EL_SP_SNIKSNAK ||
4374 element == EL_SP_ELECTRON ||
4375 element == EL_MOLE))
4376 DrawLevelField(x, y);
4380 if (MovDelay[x][y]) /* wait some time before next movement */
4385 if (element == EL_YAMYAM)
4388 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4389 DrawLevelElementAnimation(x, y, element);
4393 if (MovDelay[x][y]) /* element still has to wait some time */
4396 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4397 ResetGfxAnimation(x, y);
4401 if (GfxAction[x][y] != ACTION_WAITING)
4402 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4404 GfxAction[x][y] = ACTION_WAITING;
4408 if (element == EL_ROBOT ||
4410 element == EL_PACMAN ||
4412 element == EL_YAMYAM ||
4413 element == EL_DARK_YAMYAM)
4416 DrawLevelElementAnimation(x, y, element);
4418 DrawLevelElementAnimationIfNeeded(x, y, element);
4420 PlayLevelSoundAction(x, y, ACTION_WAITING);
4422 else if (element == EL_SP_ELECTRON)
4423 DrawLevelElementAnimationIfNeeded(x, y, element);
4424 else if (element == EL_DRAGON)
4427 int dir = MovDir[x][y];
4428 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4429 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4430 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4431 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4432 dir == MV_UP ? IMG_FLAMES_1_UP :
4433 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4434 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4437 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4440 GfxAction[x][y] = ACTION_ATTACKING;
4442 if (IS_PLAYER(x, y))
4443 DrawPlayerField(x, y);
4445 DrawLevelField(x, y);
4447 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4449 for (i = 1; i <= 3; i++)
4451 int xx = x + i * dx;
4452 int yy = y + i * dy;
4453 int sx = SCREENX(xx);
4454 int sy = SCREENY(yy);
4455 int flame_graphic = graphic + (i - 1);
4457 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4462 int flamed = MovingOrBlocked2Element(xx, yy);
4464 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4467 RemoveMovingField(xx, yy);
4469 Feld[xx][yy] = EL_FLAMES;
4470 if (IN_SCR_FIELD(sx, sy))
4472 DrawLevelFieldCrumbledSand(xx, yy);
4473 DrawGraphic(sx, sy, flame_graphic, frame);
4478 if (Feld[xx][yy] == EL_FLAMES)
4479 Feld[xx][yy] = EL_EMPTY;
4480 DrawLevelField(xx, yy);
4485 if (MovDelay[x][y]) /* element still has to wait some time */
4487 PlayLevelSoundAction(x, y, ACTION_WAITING);
4493 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4494 for all other elements GfxAction will be set by InitMovingField() */
4495 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4496 GfxAction[x][y] = ACTION_MOVING;
4500 /* now make next step */
4502 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4504 if (DONT_COLLIDE_WITH(element) &&
4505 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4506 !PLAYER_PROTECTED(newx, newy))
4509 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4512 /* player killed by element which is deadly when colliding with */
4514 KillHero(PLAYERINFO(newx, newy));
4519 else if ((element == EL_PENGUIN ||
4520 element == EL_ROBOT ||
4521 element == EL_SATELLITE ||
4522 element == EL_BALLOON ||
4523 IS_CUSTOM_ELEMENT(element)) &&
4524 IN_LEV_FIELD(newx, newy) &&
4525 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4528 Store[x][y] = EL_ACID;
4530 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4532 if (Feld[newx][newy] == EL_EXIT_OPEN)
4536 DrawLevelField(x, y);
4538 Feld[x][y] = EL_EMPTY;
4539 DrawLevelField(x, y);
4542 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4543 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4544 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4546 local_player->friends_still_needed--;
4547 if (!local_player->friends_still_needed &&
4548 !local_player->GameOver && AllPlayersGone)
4549 local_player->LevelSolved = local_player->GameOver = TRUE;
4553 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4555 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4556 DrawLevelField(newx, newy);
4558 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4560 else if (!IS_FREE(newx, newy))
4562 GfxAction[x][y] = ACTION_WAITING;
4564 if (IS_PLAYER(x, y))
4565 DrawPlayerField(x, y);
4567 DrawLevelField(x, y);
4571 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4573 if (IS_FOOD_PIG(Feld[newx][newy]))
4575 if (IS_MOVING(newx, newy))
4576 RemoveMovingField(newx, newy);
4579 Feld[newx][newy] = EL_EMPTY;
4580 DrawLevelField(newx, newy);
4583 PlayLevelSound(x, y, SND_PIG_DIGGING);
4585 else if (!IS_FREE(newx, newy))
4587 if (IS_PLAYER(x, y))
4588 DrawPlayerField(x, y);
4590 DrawLevelField(x, y);
4594 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4595 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4598 if (IS_FREE(newx, newy))
4600 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4603 if (IS_MOVING(newx, newy))
4604 RemoveMovingField(newx, newy);
4607 Feld[newx][newy] = EL_EMPTY;
4608 DrawLevelField(newx, newy);
4611 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4613 else if (!IS_FREE(newx, newy))
4616 if (IS_PLAYER(x, y))
4617 DrawPlayerField(x, y);
4619 DrawLevelField(x, y);
4624 RunnerVisit[x][y] = FrameCounter;
4625 PlayerVisit[x][y] /= 8; /* expire player visit path */
4627 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4629 if (!IS_FREE(newx, newy))
4631 if (IS_PLAYER(x, y))
4632 DrawPlayerField(x, y);
4634 DrawLevelField(x, y);
4640 boolean wanna_flame = !RND(10);
4641 int dx = newx - x, dy = newy - y;
4642 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4643 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4644 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4645 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4646 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4647 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4650 IS_CLASSIC_ENEMY(element1) ||
4651 IS_CLASSIC_ENEMY(element2)) &&
4652 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4653 element1 != EL_FLAMES && element2 != EL_FLAMES)
4656 ResetGfxAnimation(x, y);
4657 GfxAction[x][y] = ACTION_ATTACKING;
4660 if (IS_PLAYER(x, y))
4661 DrawPlayerField(x, y);
4663 DrawLevelField(x, y);
4665 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4667 MovDelay[x][y] = 50;
4669 Feld[newx][newy] = EL_FLAMES;
4670 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4671 Feld[newx1][newy1] = EL_FLAMES;
4672 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4673 Feld[newx2][newy2] = EL_FLAMES;
4679 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4680 Feld[newx][newy] == EL_DIAMOND)
4682 if (IS_MOVING(newx, newy))
4683 RemoveMovingField(newx, newy);
4686 Feld[newx][newy] = EL_EMPTY;
4687 DrawLevelField(newx, newy);
4690 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4692 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4693 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4695 if (AmoebaNr[newx][newy])
4697 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4698 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4699 Feld[newx][newy] == EL_BD_AMOEBA)
4700 AmoebaCnt[AmoebaNr[newx][newy]]--;
4703 if (IS_MOVING(newx, newy))
4704 RemoveMovingField(newx, newy);
4707 Feld[newx][newy] = EL_EMPTY;
4708 DrawLevelField(newx, newy);
4711 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4713 else if ((element == EL_PACMAN || element == EL_MOLE)
4714 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4716 if (AmoebaNr[newx][newy])
4718 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4719 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4720 Feld[newx][newy] == EL_BD_AMOEBA)
4721 AmoebaCnt[AmoebaNr[newx][newy]]--;
4724 if (element == EL_MOLE)
4726 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4727 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4729 ResetGfxAnimation(x, y);
4730 GfxAction[x][y] = ACTION_DIGGING;
4731 DrawLevelField(x, y);
4733 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4734 return; /* wait for shrinking amoeba */
4736 else /* element == EL_PACMAN */
4738 Feld[newx][newy] = EL_EMPTY;
4739 DrawLevelField(newx, newy);
4740 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4743 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4744 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4745 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4747 /* wait for shrinking amoeba to completely disappear */
4750 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4752 /* object was running against a wall */
4757 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4758 DrawLevelElementAnimation(x, y, element);
4760 if (element == EL_BUG ||
4761 element == EL_SPACESHIP ||
4762 element == EL_SP_SNIKSNAK)
4763 DrawLevelField(x, y);
4764 else if (element == EL_MOLE)
4765 DrawLevelField(x, y);
4766 else if (element == EL_BD_BUTTERFLY ||
4767 element == EL_BD_FIREFLY)
4768 DrawLevelElementAnimationIfNeeded(x, y, element);
4769 else if (element == EL_SATELLITE)
4770 DrawLevelElementAnimationIfNeeded(x, y, element);
4771 else if (element == EL_SP_ELECTRON)
4772 DrawLevelElementAnimationIfNeeded(x, y, element);
4775 if (DONT_TOUCH(element))
4776 TestIfBadThingTouchesHero(x, y);
4779 PlayLevelSoundAction(x, y, ACTION_WAITING);
4785 InitMovingField(x, y, MovDir[x][y]);
4787 PlayLevelSoundAction(x, y, ACTION_MOVING);
4791 ContinueMoving(x, y);
4794 void ContinueMoving(int x, int y)
4796 int element = Feld[x][y];
4797 int direction = MovDir[x][y];
4798 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4799 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4800 int newx = x + dx, newy = y + dy;
4802 int nextx = newx + dx, nexty = newy + dy;
4804 boolean pushed = Pushed[x][y];
4806 MovPos[x][y] += getElementMoveStepsize(x, y);
4808 if (pushed) /* special case: moving object pushed by player */
4809 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4811 if (ABS(MovPos[x][y]) < TILEX)
4813 DrawLevelField(x, y);
4815 return; /* element is still moving */
4818 /* element reached destination field */
4820 Feld[x][y] = EL_EMPTY;
4821 Feld[newx][newy] = element;
4822 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4824 if (element == EL_MOLE)
4826 Feld[x][y] = EL_SAND;
4828 DrawLevelFieldCrumbledSandNeighbours(x, y);
4830 else if (element == EL_QUICKSAND_FILLING)
4832 element = Feld[newx][newy] = get_next_element(element);
4833 Store[newx][newy] = Store[x][y];
4835 else if (element == EL_QUICKSAND_EMPTYING)
4837 Feld[x][y] = get_next_element(element);
4838 element = Feld[newx][newy] = Store[x][y];
4840 else if (element == EL_MAGIC_WALL_FILLING)
4842 element = Feld[newx][newy] = get_next_element(element);
4843 if (!game.magic_wall_active)
4844 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4845 Store[newx][newy] = Store[x][y];
4847 else if (element == EL_MAGIC_WALL_EMPTYING)
4849 Feld[x][y] = get_next_element(element);
4850 if (!game.magic_wall_active)
4851 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4852 element = Feld[newx][newy] = Store[x][y];
4854 else if (element == EL_BD_MAGIC_WALL_FILLING)
4856 element = Feld[newx][newy] = get_next_element(element);
4857 if (!game.magic_wall_active)
4858 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4859 Store[newx][newy] = Store[x][y];
4861 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4863 Feld[x][y] = get_next_element(element);
4864 if (!game.magic_wall_active)
4865 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4866 element = Feld[newx][newy] = Store[x][y];
4868 else if (element == EL_AMOEBA_DROPPING)
4870 Feld[x][y] = get_next_element(element);
4871 element = Feld[newx][newy] = Store[x][y];
4873 else if (element == EL_SOKOBAN_OBJECT)
4876 Feld[x][y] = Back[x][y];
4878 if (Back[newx][newy])
4879 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4881 Back[x][y] = Back[newx][newy] = 0;
4883 else if (Store[x][y] == EL_ACID)
4885 element = Feld[newx][newy] = EL_ACID;
4889 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4890 MovDelay[newx][newy] = 0;
4892 /* copy element change control values to new field */
4893 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4894 ChangePage[newx][newy] = ChangePage[x][y];
4895 Changed[newx][newy] = Changed[x][y];
4896 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4898 ChangeDelay[x][y] = 0;
4899 ChangePage[x][y] = -1;
4900 Changed[x][y] = CE_BITMASK_DEFAULT;
4901 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4903 /* copy animation control values to new field */
4904 GfxFrame[newx][newy] = GfxFrame[x][y];
4905 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4906 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4907 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4909 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4911 ResetGfxAnimation(x, y); /* reset animation values for old field */
4914 /* 2.1.1 (does not work correctly for spring) */
4915 if (!CAN_MOVE(element))
4916 MovDir[newx][newy] = 0;
4920 /* (does not work for falling objects that slide horizontally) */
4921 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4922 MovDir[newx][newy] = 0;
4925 if (!CAN_MOVE(element) ||
4926 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4927 MovDir[newx][newy] = 0;
4930 if (!CAN_MOVE(element) ||
4931 (CAN_FALL(element) && direction == MV_DOWN))
4932 GfxDir[x][y] = MovDir[newx][newy] = 0;
4937 DrawLevelField(x, y);
4938 DrawLevelField(newx, newy);
4940 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4942 /* prevent pushed element from moving on in pushed direction */
4943 if (pushed && CAN_MOVE(element) &&
4944 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4945 !(element_info[element].move_pattern & direction))
4946 TurnRound(newx, newy);
4948 if (!pushed) /* special case: moving object pushed by player */
4950 WasJustMoving[newx][newy] = 3;
4952 if (CAN_FALL(element) && direction == MV_DOWN)
4953 WasJustFalling[newx][newy] = 3;
4956 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4958 TestIfBadThingTouchesHero(newx, newy);
4959 TestIfBadThingTouchesFriend(newx, newy);
4961 if (!IS_CUSTOM_ELEMENT(element))
4962 TestIfBadThingTouchesOtherBadThing(newx, newy);
4964 else if (element == EL_PENGUIN)
4965 TestIfFriendTouchesBadThing(newx, newy);
4967 if (CAN_FALL(element) && direction == MV_DOWN &&
4968 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4972 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4976 if (ChangePage[newx][newy] != -1) /* delayed change */
4977 ChangeElement(newx, newy, ChangePage[newx][newy]);
4982 TestIfElementHitsCustomElement(newx, newy, direction);
4986 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4988 int hitting_element = Feld[newx][newy];
4990 /* !!! fix side (direction) orientation here and elsewhere !!! */
4991 CheckElementSideChange(newx, newy, hitting_element,
4992 direction, CE_HITTING_SOMETHING, -1);
4995 if (IN_LEV_FIELD(nextx, nexty))
4997 static int opposite_directions[] =
5004 int move_dir_bit = MV_DIR_BIT(direction);
5005 int opposite_direction = opposite_directions[move_dir_bit];
5006 int hitting_side = direction;
5007 int touched_side = opposite_direction;
5008 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5009 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5010 MovDir[nextx][nexty] != direction ||
5011 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5017 CheckElementSideChange(nextx, nexty, touched_element,
5018 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5020 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5021 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5023 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5025 struct ElementChangeInfo *change =
5026 &element_info[hitting_element].change_page[i];
5028 if (change->can_change &&
5029 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5030 change->sides & touched_side &&
5031 change->trigger_element == touched_element)
5033 CheckElementSideChange(newx, newy, hitting_element,
5034 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5040 if (IS_CUSTOM_ELEMENT(touched_element) &&
5041 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5043 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5045 struct ElementChangeInfo *change =
5046 &element_info[touched_element].change_page[i];
5048 if (change->can_change &&
5049 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5050 change->sides & hitting_side &&
5051 change->trigger_element == hitting_element)
5053 CheckElementSideChange(nextx, nexty, touched_element,
5054 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5065 TestIfPlayerTouchesCustomElement(newx, newy);
5066 TestIfElementTouchesCustomElement(newx, newy);
5069 int AmoebeNachbarNr(int ax, int ay)
5072 int element = Feld[ax][ay];
5074 static int xy[4][2] =
5082 for (i = 0; i < 4; i++)
5084 int x = ax + xy[i][0];
5085 int y = ay + xy[i][1];
5087 if (!IN_LEV_FIELD(x, y))
5090 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5091 group_nr = AmoebaNr[x][y];
5097 void AmoebenVereinigen(int ax, int ay)
5099 int i, x, y, xx, yy;
5100 int new_group_nr = AmoebaNr[ax][ay];
5101 static int xy[4][2] =
5109 if (new_group_nr == 0)
5112 for (i = 0; i < 4; i++)
5117 if (!IN_LEV_FIELD(x, y))
5120 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5121 Feld[x][y] == EL_BD_AMOEBA ||
5122 Feld[x][y] == EL_AMOEBA_DEAD) &&
5123 AmoebaNr[x][y] != new_group_nr)
5125 int old_group_nr = AmoebaNr[x][y];
5127 if (old_group_nr == 0)
5130 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5131 AmoebaCnt[old_group_nr] = 0;
5132 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5133 AmoebaCnt2[old_group_nr] = 0;
5135 for (yy = 0; yy < lev_fieldy; yy++)
5137 for (xx = 0; xx < lev_fieldx; xx++)
5139 if (AmoebaNr[xx][yy] == old_group_nr)
5140 AmoebaNr[xx][yy] = new_group_nr;
5147 void AmoebeUmwandeln(int ax, int ay)
5151 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5153 int group_nr = AmoebaNr[ax][ay];
5158 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5159 printf("AmoebeUmwandeln(): This should never happen!\n");
5164 for (y = 0; y < lev_fieldy; y++)
5166 for (x = 0; x < lev_fieldx; x++)
5168 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5171 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5175 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5176 SND_AMOEBA_TURNING_TO_GEM :
5177 SND_AMOEBA_TURNING_TO_ROCK));
5182 static int xy[4][2] =
5190 for (i = 0; i < 4; i++)
5195 if (!IN_LEV_FIELD(x, y))
5198 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5200 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5201 SND_AMOEBA_TURNING_TO_GEM :
5202 SND_AMOEBA_TURNING_TO_ROCK));
5209 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5212 int group_nr = AmoebaNr[ax][ay];
5213 boolean done = FALSE;
5218 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5219 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5224 for (y = 0; y < lev_fieldy; y++)
5226 for (x = 0; x < lev_fieldx; x++)
5228 if (AmoebaNr[x][y] == group_nr &&
5229 (Feld[x][y] == EL_AMOEBA_DEAD ||
5230 Feld[x][y] == EL_BD_AMOEBA ||
5231 Feld[x][y] == EL_AMOEBA_GROWING))
5234 Feld[x][y] = new_element;
5235 InitField(x, y, FALSE);
5236 DrawLevelField(x, y);
5243 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5244 SND_BD_AMOEBA_TURNING_TO_ROCK :
5245 SND_BD_AMOEBA_TURNING_TO_GEM));
5248 void AmoebeWaechst(int x, int y)
5250 static unsigned long sound_delay = 0;
5251 static unsigned long sound_delay_value = 0;
5253 if (!MovDelay[x][y]) /* start new growing cycle */
5257 if (DelayReached(&sound_delay, sound_delay_value))
5260 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5262 if (Store[x][y] == EL_BD_AMOEBA)
5263 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5265 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5267 sound_delay_value = 30;
5271 if (MovDelay[x][y]) /* wait some time before growing bigger */
5274 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5276 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5277 6 - MovDelay[x][y]);
5279 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5282 if (!MovDelay[x][y])
5284 Feld[x][y] = Store[x][y];
5286 DrawLevelField(x, y);
5291 void AmoebaDisappearing(int x, int y)
5293 static unsigned long sound_delay = 0;
5294 static unsigned long sound_delay_value = 0;
5296 if (!MovDelay[x][y]) /* start new shrinking cycle */
5300 if (DelayReached(&sound_delay, sound_delay_value))
5301 sound_delay_value = 30;
5304 if (MovDelay[x][y]) /* wait some time before shrinking */
5307 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5309 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5310 6 - MovDelay[x][y]);
5312 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5315 if (!MovDelay[x][y])
5317 Feld[x][y] = EL_EMPTY;
5318 DrawLevelField(x, y);
5320 /* don't let mole enter this field in this cycle;
5321 (give priority to objects falling to this field from above) */
5327 void AmoebeAbleger(int ax, int ay)
5330 int element = Feld[ax][ay];
5331 int graphic = el2img(element);
5332 int newax = ax, neway = ay;
5333 static int xy[4][2] =
5341 if (!level.amoeba_speed)
5343 Feld[ax][ay] = EL_AMOEBA_DEAD;
5344 DrawLevelField(ax, ay);
5348 if (IS_ANIMATED(graphic))
5349 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5351 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5352 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5354 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5357 if (MovDelay[ax][ay])
5361 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5364 int x = ax + xy[start][0];
5365 int y = ay + xy[start][1];
5367 if (!IN_LEV_FIELD(x, y))
5370 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5371 if (IS_FREE(x, y) ||
5372 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5378 if (newax == ax && neway == ay)
5381 else /* normal or "filled" (BD style) amoeba */
5384 boolean waiting_for_player = FALSE;
5386 for (i = 0; i < 4; i++)
5388 int j = (start + i) % 4;
5389 int x = ax + xy[j][0];
5390 int y = ay + xy[j][1];
5392 if (!IN_LEV_FIELD(x, y))
5395 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5396 if (IS_FREE(x, y) ||
5397 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5403 else if (IS_PLAYER(x, y))
5404 waiting_for_player = TRUE;
5407 if (newax == ax && neway == ay) /* amoeba cannot grow */
5409 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5411 Feld[ax][ay] = EL_AMOEBA_DEAD;
5412 DrawLevelField(ax, ay);
5413 AmoebaCnt[AmoebaNr[ax][ay]]--;
5415 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5417 if (element == EL_AMOEBA_FULL)
5418 AmoebeUmwandeln(ax, ay);
5419 else if (element == EL_BD_AMOEBA)
5420 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5425 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5427 /* amoeba gets larger by growing in some direction */
5429 int new_group_nr = AmoebaNr[ax][ay];
5432 if (new_group_nr == 0)
5434 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5435 printf("AmoebeAbleger(): This should never happen!\n");
5440 AmoebaNr[newax][neway] = new_group_nr;
5441 AmoebaCnt[new_group_nr]++;
5442 AmoebaCnt2[new_group_nr]++;
5444 /* if amoeba touches other amoeba(s) after growing, unify them */
5445 AmoebenVereinigen(newax, neway);
5447 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5449 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5455 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5456 (neway == lev_fieldy - 1 && newax != ax))
5458 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5459 Store[newax][neway] = element;
5461 else if (neway == ay)
5463 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5465 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5467 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5472 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5473 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5474 Store[ax][ay] = EL_AMOEBA_DROP;
5475 ContinueMoving(ax, ay);
5479 DrawLevelField(newax, neway);
5482 void Life(int ax, int ay)
5485 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5487 int element = Feld[ax][ay];
5488 int graphic = el2img(element);
5489 boolean changed = FALSE;
5491 if (IS_ANIMATED(graphic))
5492 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5497 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5498 MovDelay[ax][ay] = life_time;
5500 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5503 if (MovDelay[ax][ay])
5507 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5509 int xx = ax+x1, yy = ay+y1;
5512 if (!IN_LEV_FIELD(xx, yy))
5515 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5517 int x = xx+x2, y = yy+y2;
5519 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5522 if (((Feld[x][y] == element ||
5523 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5525 (IS_FREE(x, y) && Stop[x][y]))
5529 if (xx == ax && yy == ay) /* field in the middle */
5531 if (nachbarn < life[0] || nachbarn > life[1])
5533 Feld[xx][yy] = EL_EMPTY;
5535 DrawLevelField(xx, yy);
5536 Stop[xx][yy] = TRUE;
5540 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5541 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5542 { /* free border field */
5543 if (nachbarn >= life[2] && nachbarn <= life[3])
5545 Feld[xx][yy] = element;
5546 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5548 DrawLevelField(xx, yy);
5549 Stop[xx][yy] = TRUE;
5556 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5557 SND_GAME_OF_LIFE_GROWING);
5560 static void InitRobotWheel(int x, int y)
5562 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5565 static void RunRobotWheel(int x, int y)
5567 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5570 static void StopRobotWheel(int x, int y)
5572 if (ZX == x && ZY == y)
5576 static void InitTimegateWheel(int x, int y)
5578 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5581 static void RunTimegateWheel(int x, int y)
5583 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5586 void CheckExit(int x, int y)
5588 if (local_player->gems_still_needed > 0 ||
5589 local_player->sokobanfields_still_needed > 0 ||
5590 local_player->lights_still_needed > 0)
5592 int element = Feld[x][y];
5593 int graphic = el2img(element);
5595 if (IS_ANIMATED(graphic))
5596 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5601 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5604 Feld[x][y] = EL_EXIT_OPENING;
5606 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5609 void CheckExitSP(int x, int y)
5611 if (local_player->gems_still_needed > 0)
5613 int element = Feld[x][y];
5614 int graphic = el2img(element);
5616 if (IS_ANIMATED(graphic))
5617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5622 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5625 Feld[x][y] = EL_SP_EXIT_OPENING;
5627 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5630 static void CloseAllOpenTimegates()
5634 for (y = 0; y < lev_fieldy; y++)
5636 for (x = 0; x < lev_fieldx; x++)
5638 int element = Feld[x][y];
5640 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5642 Feld[x][y] = EL_TIMEGATE_CLOSING;
5644 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5646 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5653 void EdelsteinFunkeln(int x, int y)
5655 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5658 if (Feld[x][y] == EL_BD_DIAMOND)
5661 if (MovDelay[x][y] == 0) /* next animation frame */
5662 MovDelay[x][y] = 11 * !SimpleRND(500);
5664 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5668 if (setup.direct_draw && MovDelay[x][y])
5669 SetDrawtoField(DRAW_BUFFERED);
5671 DrawLevelElementAnimation(x, y, Feld[x][y]);
5673 if (MovDelay[x][y] != 0)
5675 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5676 10 - MovDelay[x][y]);
5678 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5680 if (setup.direct_draw)
5684 dest_x = FX + SCREENX(x) * TILEX;
5685 dest_y = FY + SCREENY(y) * TILEY;
5687 BlitBitmap(drawto_field, window,
5688 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5689 SetDrawtoField(DRAW_DIRECT);
5695 void MauerWaechst(int x, int y)
5699 if (!MovDelay[x][y]) /* next animation frame */
5700 MovDelay[x][y] = 3 * delay;
5702 if (MovDelay[x][y]) /* wait some time before next frame */
5706 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5708 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5709 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5711 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5714 if (!MovDelay[x][y])
5716 if (MovDir[x][y] == MV_LEFT)
5718 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5719 DrawLevelField(x - 1, y);
5721 else if (MovDir[x][y] == MV_RIGHT)
5723 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5724 DrawLevelField(x + 1, y);
5726 else if (MovDir[x][y] == MV_UP)
5728 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5729 DrawLevelField(x, y - 1);
5733 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5734 DrawLevelField(x, y + 1);
5737 Feld[x][y] = Store[x][y];
5739 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5740 DrawLevelField(x, y);
5745 void MauerAbleger(int ax, int ay)
5747 int element = Feld[ax][ay];
5748 int graphic = el2img(element);
5749 boolean oben_frei = FALSE, unten_frei = FALSE;
5750 boolean links_frei = FALSE, rechts_frei = FALSE;
5751 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5752 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5753 boolean new_wall = FALSE;
5755 if (IS_ANIMATED(graphic))
5756 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5758 if (!MovDelay[ax][ay]) /* start building new wall */
5759 MovDelay[ax][ay] = 6;
5761 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5764 if (MovDelay[ax][ay])
5768 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5770 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5772 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5774 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5777 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5778 element == EL_EXPANDABLE_WALL_ANY)
5782 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5783 Store[ax][ay-1] = element;
5784 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5785 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5786 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5787 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5792 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5793 Store[ax][ay+1] = element;
5794 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5795 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5796 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5797 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5802 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5803 element == EL_EXPANDABLE_WALL_ANY ||
5804 element == EL_EXPANDABLE_WALL)
5808 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5809 Store[ax-1][ay] = element;
5810 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5811 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5812 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5813 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5819 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5820 Store[ax+1][ay] = element;
5821 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5822 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5823 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5824 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5829 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5830 DrawLevelField(ax, ay);
5832 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5834 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5835 unten_massiv = TRUE;
5836 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5837 links_massiv = TRUE;
5838 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5839 rechts_massiv = TRUE;
5841 if (((oben_massiv && unten_massiv) ||
5842 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5843 element == EL_EXPANDABLE_WALL) &&
5844 ((links_massiv && rechts_massiv) ||
5845 element == EL_EXPANDABLE_WALL_VERTICAL))
5846 Feld[ax][ay] = EL_WALL;
5850 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5852 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5856 void CheckForDragon(int x, int y)
5859 boolean dragon_found = FALSE;
5860 static int xy[4][2] =
5868 for (i = 0; i < 4; i++)
5870 for (j = 0; j < 4; j++)
5872 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5874 if (IN_LEV_FIELD(xx, yy) &&
5875 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5877 if (Feld[xx][yy] == EL_DRAGON)
5878 dragon_found = TRUE;
5887 for (i = 0; i < 4; i++)
5889 for (j = 0; j < 3; j++)
5891 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5893 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5895 Feld[xx][yy] = EL_EMPTY;
5896 DrawLevelField(xx, yy);
5905 static void InitBuggyBase(int x, int y)
5907 int element = Feld[x][y];
5908 int activating_delay = FRAMES_PER_SECOND / 4;
5911 (element == EL_SP_BUGGY_BASE ?
5912 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5913 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5915 element == EL_SP_BUGGY_BASE_ACTIVE ?
5916 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5919 static void WarnBuggyBase(int x, int y)
5922 static int xy[4][2] =
5930 for (i = 0; i < 4; i++)
5932 int xx = x + xy[i][0], yy = y + xy[i][1];
5934 if (IS_PLAYER(xx, yy))
5936 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5943 static void InitTrap(int x, int y)
5945 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5948 static void ActivateTrap(int x, int y)
5950 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5953 static void ChangeActiveTrap(int x, int y)
5955 int graphic = IMG_TRAP_ACTIVE;
5957 /* if new animation frame was drawn, correct crumbled sand border */
5958 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5959 DrawLevelFieldCrumbledSand(x, y);
5962 static void ChangeElementNowExt(int x, int y, int target_element)
5964 /* check if element under player changes from accessible to unaccessible
5965 (needed for special case of dropping element which then changes) */
5966 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5967 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5974 Feld[x][y] = target_element;
5976 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5978 ResetGfxAnimation(x, y);
5979 ResetRandomAnimationValue(x, y);
5981 InitField(x, y, FALSE);
5982 if (CAN_MOVE(Feld[x][y]))
5985 DrawLevelField(x, y);
5987 if (GFX_CRUMBLED(Feld[x][y]))
5988 DrawLevelFieldCrumbledSandNeighbours(x, y);
5990 TestIfBadThingTouchesHero(x, y);
5991 TestIfPlayerTouchesCustomElement(x, y);
5992 TestIfElementTouchesCustomElement(x, y);
5994 if (ELEM_IS_PLAYER(target_element))
5995 RelocatePlayer(x, y, target_element);
5998 static boolean ChangeElementNow(int x, int y, int element, int page)
6000 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6002 /* always use default change event to prevent running into a loop */
6003 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6004 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6006 /* do not change already changed elements with same change event */
6008 if (Changed[x][y] & ChangeEvent[x][y])
6015 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6017 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6019 if (change->explode)
6026 if (change->use_content)
6028 boolean complete_change = TRUE;
6029 boolean can_change[3][3];
6032 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6034 boolean half_destructible;
6035 int ex = x + xx - 1;
6036 int ey = y + yy - 1;
6039 can_change[xx][yy] = TRUE;
6041 if (ex == x && ey == y) /* do not check changing element itself */
6044 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6046 can_change[xx][yy] = FALSE; /* do not change empty borders */
6051 if (!IN_LEV_FIELD(ex, ey))
6053 can_change[xx][yy] = FALSE;
6054 complete_change = FALSE;
6061 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6062 e = MovingOrBlocked2Element(ex, ey);
6064 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6066 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6067 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6068 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6070 can_change[xx][yy] = FALSE;
6071 complete_change = FALSE;
6075 if (!change->only_complete || complete_change)
6077 boolean something_has_changed = FALSE;
6079 if (change->only_complete && change->use_random_change &&
6080 RND(100) < change->random)
6083 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6085 int ex = x + xx - 1;
6086 int ey = y + yy - 1;
6088 if (can_change[xx][yy] && (!change->use_random_change ||
6089 RND(100) < change->random))
6091 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6092 RemoveMovingField(ex, ey);
6094 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6096 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6098 something_has_changed = TRUE;
6100 /* for symmetry reasons, freeze newly created border elements */
6101 if (ex != x || ey != y)
6102 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6106 if (something_has_changed)
6107 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6112 ChangeElementNowExt(x, y, change->target_element);
6114 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6120 static void ChangeElement(int x, int y, int page)
6122 int element = MovingOrBlocked2Element(x, y);
6123 struct ElementInfo *ei = &element_info[element];
6124 struct ElementChangeInfo *change = &ei->change_page[page];
6128 if (!CAN_CHANGE(element))
6131 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6132 x, y, element, element_info[element].token_name);
6133 printf("ChangeElement(): This should never happen!\n");
6139 if (ChangeDelay[x][y] == 0) /* initialize element change */
6141 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6142 RND(change->delay_random * change->delay_frames)) + 1;
6144 ResetGfxAnimation(x, y);
6145 ResetRandomAnimationValue(x, y);
6147 if (change->pre_change_function)
6148 change->pre_change_function(x, y);
6151 ChangeDelay[x][y]--;
6153 if (ChangeDelay[x][y] != 0) /* continue element change */
6155 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6157 if (IS_ANIMATED(graphic))
6158 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6160 if (change->change_function)
6161 change->change_function(x, y);
6163 else /* finish element change */
6165 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6167 page = ChangePage[x][y];
6168 ChangePage[x][y] = -1;
6172 if (IS_MOVING(x, y) && !change->explode)
6174 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6177 ChangeDelay[x][y] = 1; /* try change after next move step */
6178 ChangePage[x][y] = page; /* remember page to use for change */
6183 if (ChangeElementNow(x, y, element, page))
6185 if (change->post_change_function)
6186 change->post_change_function(x, y);
6191 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6192 int trigger_element,
6198 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6201 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6203 int element = EL_CUSTOM_START + i;
6205 boolean change_element = FALSE;
6208 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6211 for (j = 0; j < element_info[element].num_change_pages; j++)
6213 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6215 if (change->can_change &&
6217 change->events & CH_EVENT_BIT(trigger_event) &&
6219 change->sides & trigger_side &&
6220 change->trigger_element == trigger_element)
6223 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6224 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6225 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6228 change_element = TRUE;
6235 if (!change_element)
6238 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6241 if (x == lx && y == ly) /* do not change trigger element itself */
6245 if (Feld[x][y] == element)
6247 ChangeDelay[x][y] = 1;
6248 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6249 ChangeElement(x, y, page);
6257 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6260 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6264 static boolean CheckElementSideChange(int x, int y, int element, int side,
6265 int trigger_event, int page)
6267 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6270 if (Feld[x][y] == EL_BLOCKED)
6272 Blocked2Moving(x, y, &x, &y);
6273 element = Feld[x][y];
6277 page = element_info[element].event_page_nr[trigger_event];
6279 if (!(element_info[element].change_page[page].sides & side))
6282 ChangeDelay[x][y] = 1;
6283 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6284 ChangeElement(x, y, page);
6289 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6291 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6294 static void PlayPlayerSound(struct PlayerInfo *player)
6296 int jx = player->jx, jy = player->jy;
6297 int element = player->element_nr;
6298 int last_action = player->last_action_waiting;
6299 int action = player->action_waiting;
6301 if (player->is_waiting)
6303 if (action != last_action)
6304 PlayLevelSoundElementAction(jx, jy, element, action);
6306 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6310 if (action != last_action)
6311 StopSound(element_info[element].sound[last_action]);
6313 if (last_action == ACTION_SLEEPING)
6314 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6318 static void PlayAllPlayersSound()
6322 for (i = 0; i < MAX_PLAYERS; i++)
6323 if (stored_player[i].active)
6324 PlayPlayerSound(&stored_player[i]);
6327 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6329 boolean last_waiting = player->is_waiting;
6330 int move_dir = player->MovDir;
6332 player->last_action_waiting = player->action_waiting;
6336 if (!last_waiting) /* not waiting -> waiting */
6338 player->is_waiting = TRUE;
6340 player->frame_counter_bored =
6342 game.player_boring_delay_fixed +
6343 SimpleRND(game.player_boring_delay_random);
6344 player->frame_counter_sleeping =
6346 game.player_sleeping_delay_fixed +
6347 SimpleRND(game.player_sleeping_delay_random);
6349 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6352 if (game.player_sleeping_delay_fixed +
6353 game.player_sleeping_delay_random > 0 &&
6354 player->anim_delay_counter == 0 &&
6355 player->post_delay_counter == 0 &&
6356 FrameCounter >= player->frame_counter_sleeping)
6357 player->is_sleeping = TRUE;
6358 else if (game.player_boring_delay_fixed +
6359 game.player_boring_delay_random > 0 &&
6360 FrameCounter >= player->frame_counter_bored)
6361 player->is_bored = TRUE;
6363 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6364 player->is_bored ? ACTION_BORING :
6367 if (player->is_sleeping)
6369 if (player->num_special_action_sleeping > 0)
6371 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6373 int last_special_action = player->special_action_sleeping;
6374 int num_special_action = player->num_special_action_sleeping;
6375 int special_action =
6376 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6377 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6378 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6379 last_special_action + 1 : ACTION_SLEEPING);
6380 int special_graphic =
6381 el_act_dir2img(player->element_nr, special_action, move_dir);
6383 player->anim_delay_counter =
6384 graphic_info[special_graphic].anim_delay_fixed +
6385 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6386 player->post_delay_counter =
6387 graphic_info[special_graphic].post_delay_fixed +
6388 SimpleRND(graphic_info[special_graphic].post_delay_random);
6390 player->special_action_sleeping = special_action;
6393 if (player->anim_delay_counter > 0)
6395 player->action_waiting = player->special_action_sleeping;
6396 player->anim_delay_counter--;
6398 else if (player->post_delay_counter > 0)
6400 player->post_delay_counter--;
6404 else if (player->is_bored)
6406 if (player->num_special_action_bored > 0)
6408 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6410 int special_action =
6411 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6412 int special_graphic =
6413 el_act_dir2img(player->element_nr, special_action, move_dir);
6415 player->anim_delay_counter =
6416 graphic_info[special_graphic].anim_delay_fixed +
6417 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6418 player->post_delay_counter =
6419 graphic_info[special_graphic].post_delay_fixed +
6420 SimpleRND(graphic_info[special_graphic].post_delay_random);
6422 player->special_action_bored = special_action;
6425 if (player->anim_delay_counter > 0)
6427 player->action_waiting = player->special_action_bored;
6428 player->anim_delay_counter--;
6430 else if (player->post_delay_counter > 0)
6432 player->post_delay_counter--;
6437 else if (last_waiting) /* waiting -> not waiting */
6439 player->is_waiting = FALSE;
6440 player->is_bored = FALSE;
6441 player->is_sleeping = FALSE;
6443 player->frame_counter_bored = -1;
6444 player->frame_counter_sleeping = -1;
6446 player->anim_delay_counter = 0;
6447 player->post_delay_counter = 0;
6449 player->action_waiting = ACTION_DEFAULT;
6451 player->special_action_bored = ACTION_DEFAULT;
6452 player->special_action_sleeping = ACTION_DEFAULT;
6457 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6460 static byte stored_player_action[MAX_PLAYERS];
6461 static int num_stored_actions = 0;
6463 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6464 int left = player_action & JOY_LEFT;
6465 int right = player_action & JOY_RIGHT;
6466 int up = player_action & JOY_UP;
6467 int down = player_action & JOY_DOWN;
6468 int button1 = player_action & JOY_BUTTON_1;
6469 int button2 = player_action & JOY_BUTTON_2;
6470 int dx = (left ? -1 : right ? 1 : 0);
6471 int dy = (up ? -1 : down ? 1 : 0);
6474 stored_player_action[player->index_nr] = 0;
6475 num_stored_actions++;
6479 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6482 if (!player->active || tape.pausing)
6488 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6492 snapped = SnapField(player, dx, dy);
6496 dropped = DropElement(player);
6498 moved = MovePlayer(player, dx, dy);
6501 if (tape.single_step && tape.recording && !tape.pausing)
6503 if (button1 || (dropped && !moved))
6505 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6506 SnapField(player, 0, 0); /* stop snapping */
6510 SetPlayerWaiting(player, FALSE);
6513 return player_action;
6515 stored_player_action[player->index_nr] = player_action;
6521 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6524 /* no actions for this player (no input at player's configured device) */
6526 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6527 SnapField(player, 0, 0);
6528 CheckGravityMovement(player);
6530 if (player->MovPos == 0)
6531 SetPlayerWaiting(player, TRUE);
6533 if (player->MovPos == 0) /* needed for tape.playing */
6534 player->is_moving = FALSE;
6536 player->is_dropping = FALSE;
6542 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6544 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6546 TapeRecordAction(stored_player_action);
6547 num_stored_actions = 0;
6554 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6556 static byte stored_player_action[MAX_PLAYERS];
6557 static int num_stored_actions = 0;
6558 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6559 int left = player_action & JOY_LEFT;
6560 int right = player_action & JOY_RIGHT;
6561 int up = player_action & JOY_UP;
6562 int down = player_action & JOY_DOWN;
6563 int button1 = player_action & JOY_BUTTON_1;
6564 int button2 = player_action & JOY_BUTTON_2;
6565 int dx = (left ? -1 : right ? 1 : 0);
6566 int dy = (up ? -1 : down ? 1 : 0);
6568 stored_player_action[player->index_nr] = 0;
6569 num_stored_actions++;
6571 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6573 if (!player->active || tape.pausing)
6578 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6581 snapped = SnapField(player, dx, dy);
6585 dropped = DropElement(player);
6587 moved = MovePlayer(player, dx, dy);
6590 if (tape.single_step && tape.recording && !tape.pausing)
6592 if (button1 || (dropped && !moved))
6594 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6595 SnapField(player, 0, 0); /* stop snapping */
6599 stored_player_action[player->index_nr] = player_action;
6603 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6605 /* no actions for this player (no input at player's configured device) */
6607 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6608 SnapField(player, 0, 0);
6609 CheckGravityMovement(player);
6611 if (player->MovPos == 0)
6612 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6614 if (player->MovPos == 0) /* needed for tape.playing */
6615 player->is_moving = FALSE;
6618 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6620 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6622 TapeRecordAction(stored_player_action);
6623 num_stored_actions = 0;
6630 static unsigned long action_delay = 0;
6631 unsigned long action_delay_value;
6632 int magic_wall_x = 0, magic_wall_y = 0;
6633 int i, x, y, element, graphic;
6634 byte *recorded_player_action;
6635 byte summarized_player_action = 0;
6637 byte tape_action[MAX_PLAYERS];
6640 if (game_status != GAME_MODE_PLAYING)
6643 action_delay_value =
6644 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6646 if (tape.playing && tape.index_search && !tape.pausing)
6647 action_delay_value = 0;
6649 /* ---------- main game synchronization point ---------- */
6651 WaitUntilDelayReached(&action_delay, action_delay_value);
6653 if (network_playing && !network_player_action_received)
6657 printf("DEBUG: try to get network player actions in time\n");
6661 #if defined(PLATFORM_UNIX)
6662 /* last chance to get network player actions without main loop delay */
6666 if (game_status != GAME_MODE_PLAYING)
6669 if (!network_player_action_received)
6673 printf("DEBUG: failed to get network player actions in time\n");
6684 printf("::: getting new tape action [%d]\n", FrameCounter);
6687 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6689 for (i = 0; i < MAX_PLAYERS; i++)
6691 summarized_player_action |= stored_player[i].action;
6693 if (!network_playing)
6694 stored_player[i].effective_action = stored_player[i].action;
6697 #if defined(PLATFORM_UNIX)
6698 if (network_playing)
6699 SendToServer_MovePlayer(summarized_player_action);
6702 if (!options.network && !setup.team_mode)
6703 local_player->effective_action = summarized_player_action;
6705 for (i = 0; i < MAX_PLAYERS; i++)
6707 int actual_player_action = stored_player[i].effective_action;
6709 if (stored_player[i].programmed_action)
6710 actual_player_action = stored_player[i].programmed_action;
6712 if (recorded_player_action)
6713 actual_player_action = recorded_player_action[i];
6715 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6717 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6718 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6720 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6725 TapeRecordAction(tape_action);
6728 network_player_action_received = FALSE;
6730 ScrollScreen(NULL, SCROLL_GO_ON);
6736 for (i = 0; i < MAX_PLAYERS; i++)
6737 stored_player[i].Frame++;
6741 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6743 for (i = 0; i < MAX_PLAYERS; i++)
6745 struct PlayerInfo *player = &stored_player[i];
6749 if (player->active && player->is_pushing && player->is_moving &&
6752 ContinueMoving(x, y);
6754 /* continue moving after pushing (this is actually a bug) */
6755 if (!IS_MOVING(x, y))
6764 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6766 Changed[x][y] = CE_BITMASK_DEFAULT;
6767 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6770 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6772 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6773 printf("GameActions(): This should never happen!\n");
6775 ChangePage[x][y] = -1;
6780 if (WasJustMoving[x][y] > 0)
6781 WasJustMoving[x][y]--;
6782 if (WasJustFalling[x][y] > 0)
6783 WasJustFalling[x][y]--;
6788 /* reset finished pushing action (not done in ContinueMoving() to allow
6789 continous pushing animation for elements with zero push delay) */
6790 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6792 ResetGfxAnimation(x, y);
6793 DrawLevelField(x, y);
6798 if (IS_BLOCKED(x, y))
6802 Blocked2Moving(x, y, &oldx, &oldy);
6803 if (!IS_MOVING(oldx, oldy))
6805 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6806 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6807 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6808 printf("GameActions(): This should never happen!\n");
6814 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6816 element = Feld[x][y];
6818 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6820 graphic = el2img(element);
6826 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6828 element = graphic = 0;
6832 if (graphic_info[graphic].anim_global_sync)
6833 GfxFrame[x][y] = FrameCounter;
6835 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6836 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6837 ResetRandomAnimationValue(x, y);
6839 SetRandomAnimationValue(x, y);
6842 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6845 if (IS_INACTIVE(element))
6847 if (IS_ANIMATED(graphic))
6848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6854 /* this may take place after moving, so 'element' may have changed */
6856 if (IS_CHANGING(x, y))
6858 if (IS_CHANGING(x, y) &&
6859 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6863 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6864 element_info[element].event_page_nr[CE_DELAY]);
6866 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6869 element = Feld[x][y];
6870 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6874 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6879 element = Feld[x][y];
6880 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6882 if (element == EL_MOLE)
6883 printf("::: %d, %d, %d [%d]\n",
6884 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6888 if (element == EL_YAMYAM)
6889 printf("::: %d, %d, %d\n",
6890 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6894 if (IS_ANIMATED(graphic) &&
6898 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6901 if (element == EL_BUG)
6902 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6906 if (element == EL_MOLE)
6907 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6911 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6912 EdelsteinFunkeln(x, y);
6914 else if ((element == EL_ACID ||
6915 element == EL_EXIT_OPEN ||
6916 element == EL_SP_EXIT_OPEN ||
6917 element == EL_SP_TERMINAL ||
6918 element == EL_SP_TERMINAL_ACTIVE ||
6919 element == EL_EXTRA_TIME ||
6920 element == EL_SHIELD_NORMAL ||
6921 element == EL_SHIELD_DEADLY) &&
6922 IS_ANIMATED(graphic))
6923 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6924 else if (IS_MOVING(x, y))
6925 ContinueMoving(x, y);
6926 else if (IS_ACTIVE_BOMB(element))
6927 CheckDynamite(x, y);
6929 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6930 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6932 else if (element == EL_AMOEBA_GROWING)
6933 AmoebeWaechst(x, y);
6934 else if (element == EL_AMOEBA_SHRINKING)
6935 AmoebaDisappearing(x, y);
6937 #if !USE_NEW_AMOEBA_CODE
6938 else if (IS_AMOEBALIVE(element))
6939 AmoebeAbleger(x, y);
6942 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6944 else if (element == EL_EXIT_CLOSED)
6946 else if (element == EL_SP_EXIT_CLOSED)
6948 else if (element == EL_EXPANDABLE_WALL_GROWING)
6950 else if (element == EL_EXPANDABLE_WALL ||
6951 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6952 element == EL_EXPANDABLE_WALL_VERTICAL ||
6953 element == EL_EXPANDABLE_WALL_ANY)
6955 else if (element == EL_FLAMES)
6956 CheckForDragon(x, y);
6958 else if (IS_AUTO_CHANGING(element))
6959 ChangeElement(x, y);
6961 else if (element == EL_EXPLOSION)
6962 ; /* drawing of correct explosion animation is handled separately */
6963 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6964 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6967 /* this may take place after moving, so 'element' may have changed */
6968 if (IS_AUTO_CHANGING(Feld[x][y]))
6969 ChangeElement(x, y);
6972 if (IS_BELT_ACTIVE(element))
6973 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6975 if (game.magic_wall_active)
6977 int jx = local_player->jx, jy = local_player->jy;
6979 /* play the element sound at the position nearest to the player */
6980 if ((element == EL_MAGIC_WALL_FULL ||
6981 element == EL_MAGIC_WALL_ACTIVE ||
6982 element == EL_MAGIC_WALL_EMPTYING ||
6983 element == EL_BD_MAGIC_WALL_FULL ||
6984 element == EL_BD_MAGIC_WALL_ACTIVE ||
6985 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6986 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6994 #if USE_NEW_AMOEBA_CODE
6995 /* new experimental amoeba growth stuff */
6997 if (!(FrameCounter % 8))
7000 static unsigned long random = 1684108901;
7002 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7005 x = (random >> 10) % lev_fieldx;
7006 y = (random >> 20) % lev_fieldy;
7008 x = RND(lev_fieldx);
7009 y = RND(lev_fieldy);
7011 element = Feld[x][y];
7013 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7014 if (!IS_PLAYER(x,y) &&
7015 (element == EL_EMPTY ||
7016 element == EL_SAND ||
7017 element == EL_QUICKSAND_EMPTY ||
7018 element == EL_ACID_SPLASH_LEFT ||
7019 element == EL_ACID_SPLASH_RIGHT))
7021 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7022 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7023 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7024 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7025 Feld[x][y] = EL_AMOEBA_DROP;
7028 random = random * 129 + 1;
7034 if (game.explosions_delayed)
7037 game.explosions_delayed = FALSE;
7039 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7041 element = Feld[x][y];
7043 if (ExplodeField[x][y])
7044 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7045 else if (element == EL_EXPLOSION)
7046 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7048 ExplodeField[x][y] = EX_NO_EXPLOSION;
7051 game.explosions_delayed = TRUE;
7054 if (game.magic_wall_active)
7056 if (!(game.magic_wall_time_left % 4))
7058 int element = Feld[magic_wall_x][magic_wall_y];
7060 if (element == EL_BD_MAGIC_WALL_FULL ||
7061 element == EL_BD_MAGIC_WALL_ACTIVE ||
7062 element == EL_BD_MAGIC_WALL_EMPTYING)
7063 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7065 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7068 if (game.magic_wall_time_left > 0)
7070 game.magic_wall_time_left--;
7071 if (!game.magic_wall_time_left)
7073 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7075 element = Feld[x][y];
7077 if (element == EL_MAGIC_WALL_ACTIVE ||
7078 element == EL_MAGIC_WALL_FULL)
7080 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7081 DrawLevelField(x, y);
7083 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7084 element == EL_BD_MAGIC_WALL_FULL)
7086 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7087 DrawLevelField(x, y);
7091 game.magic_wall_active = FALSE;
7096 if (game.light_time_left > 0)
7098 game.light_time_left--;
7100 if (game.light_time_left == 0)
7101 RedrawAllLightSwitchesAndInvisibleElements();
7104 if (game.timegate_time_left > 0)
7106 game.timegate_time_left--;
7108 if (game.timegate_time_left == 0)
7109 CloseAllOpenTimegates();
7112 for (i = 0; i < MAX_PLAYERS; i++)
7114 struct PlayerInfo *player = &stored_player[i];
7116 if (SHIELD_ON(player))
7118 if (player->shield_deadly_time_left)
7119 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7120 else if (player->shield_normal_time_left)
7121 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7125 if (TimeFrames >= FRAMES_PER_SECOND)
7130 for (i = 0; i < MAX_PLAYERS; i++)
7132 struct PlayerInfo *player = &stored_player[i];
7134 if (SHIELD_ON(player))
7136 player->shield_normal_time_left--;
7138 if (player->shield_deadly_time_left > 0)
7139 player->shield_deadly_time_left--;
7143 if (tape.recording || tape.playing)
7144 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7150 if (TimeLeft <= 10 && setup.time_limit)
7151 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7153 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7155 if (!TimeLeft && setup.time_limit)
7156 for (i = 0; i < MAX_PLAYERS; i++)
7157 KillHero(&stored_player[i]);
7159 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7160 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7164 PlayAllPlayersSound();
7166 if (options.debug) /* calculate frames per second */
7168 static unsigned long fps_counter = 0;
7169 static int fps_frames = 0;
7170 unsigned long fps_delay_ms = Counter() - fps_counter;
7174 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7176 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7179 fps_counter = Counter();
7182 redraw_mask |= REDRAW_FPS;
7186 if (stored_player[0].jx != stored_player[0].last_jx ||
7187 stored_player[0].jy != stored_player[0].last_jy)
7188 printf("::: %d, %d, %d, %d, %d\n",
7189 stored_player[0].MovDir,
7190 stored_player[0].MovPos,
7191 stored_player[0].GfxPos,
7192 stored_player[0].Frame,
7193 stored_player[0].StepFrame);
7200 for (i = 0; i < MAX_PLAYERS; i++)
7203 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7205 stored_player[i].Frame += move_frames;
7207 if (stored_player[i].MovPos != 0)
7208 stored_player[i].StepFrame += move_frames;
7210 if (stored_player[i].drop_delay > 0)
7211 stored_player[i].drop_delay--;
7216 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7218 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7220 local_player->show_envelope = 0;
7225 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7227 int min_x = x, min_y = y, max_x = x, max_y = y;
7230 for (i = 0; i < MAX_PLAYERS; i++)
7232 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7234 if (!stored_player[i].active || &stored_player[i] == player)
7237 min_x = MIN(min_x, jx);
7238 min_y = MIN(min_y, jy);
7239 max_x = MAX(max_x, jx);
7240 max_y = MAX(max_y, jy);
7243 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7246 static boolean AllPlayersInVisibleScreen()
7250 for (i = 0; i < MAX_PLAYERS; i++)
7252 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7254 if (!stored_player[i].active)
7257 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7264 void ScrollLevel(int dx, int dy)
7266 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7269 BlitBitmap(drawto_field, drawto_field,
7270 FX + TILEX * (dx == -1) - softscroll_offset,
7271 FY + TILEY * (dy == -1) - softscroll_offset,
7272 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7273 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7274 FX + TILEX * (dx == 1) - softscroll_offset,
7275 FY + TILEY * (dy == 1) - softscroll_offset);
7279 x = (dx == 1 ? BX1 : BX2);
7280 for (y = BY1; y <= BY2; y++)
7281 DrawScreenField(x, y);
7286 y = (dy == 1 ? BY1 : BY2);
7287 for (x = BX1; x <= BX2; x++)
7288 DrawScreenField(x, y);
7291 redraw_mask |= REDRAW_FIELD;
7294 static void CheckGravityMovement(struct PlayerInfo *player)
7296 if (game.gravity && !player->programmed_action)
7298 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7299 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7301 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7302 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7303 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7304 int jx = player->jx, jy = player->jy;
7305 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7306 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7307 int new_jx = jx + dx, new_jy = jy + dy;
7308 boolean field_under_player_is_free =
7309 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7310 boolean player_is_moving_to_valid_field =
7311 (IN_LEV_FIELD(new_jx, new_jy) &&
7312 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7313 Feld[new_jx][new_jy] == EL_SAND));
7314 /* !!! extend EL_SAND to anything diggable !!! */
7316 if (field_under_player_is_free &&
7317 !player_is_moving_to_valid_field &&
7318 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7319 player->programmed_action = MV_DOWN;
7325 -----------------------------------------------------------------------------
7326 dx, dy: direction (non-diagonal) to try to move the player to
7327 real_dx, real_dy: direction as read from input device (can be diagonal)
7330 boolean MovePlayerOneStep(struct PlayerInfo *player,
7331 int dx, int dy, int real_dx, int real_dy)
7334 static int change_sides[4][2] =
7336 /* enter side leave side */
7337 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7338 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7339 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7340 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7342 int move_direction = (dx == -1 ? MV_LEFT :
7343 dx == +1 ? MV_RIGHT :
7345 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7346 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7347 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7349 int jx = player->jx, jy = player->jy;
7350 int new_jx = jx + dx, new_jy = jy + dy;
7354 if (!player->active || (!dx && !dy))
7355 return MF_NO_ACTION;
7357 player->MovDir = (dx < 0 ? MV_LEFT :
7360 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7362 if (!IN_LEV_FIELD(new_jx, new_jy))
7363 return MF_NO_ACTION;
7365 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7366 return MF_NO_ACTION;
7369 element = MovingOrBlocked2Element(new_jx, new_jy);
7371 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7374 if (DONT_RUN_INTO(element))
7376 if (element == EL_ACID && dx == 0 && dy == 1)
7379 Feld[jx][jy] = EL_PLAYER_1;
7380 InitMovingField(jx, jy, MV_DOWN);
7381 Store[jx][jy] = EL_ACID;
7382 ContinueMoving(jx, jy);
7386 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7391 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7392 if (can_move != MF_MOVING)
7395 /* check if DigField() has caused relocation of the player */
7396 if (player->jx != jx || player->jy != jy)
7397 return MF_NO_ACTION;
7399 StorePlayer[jx][jy] = 0;
7400 player->last_jx = jx;
7401 player->last_jy = jy;
7402 player->jx = new_jx;
7403 player->jy = new_jy;
7404 StorePlayer[new_jx][new_jy] = player->element_nr;
7407 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7409 player->step_counter++;
7411 player->drop_delay = 0;
7413 PlayerVisit[jx][jy] = FrameCounter;
7415 ScrollPlayer(player, SCROLL_INIT);
7418 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7420 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7421 CE_OTHER_GETS_LEFT);
7422 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7423 CE_LEFT_BY_PLAYER, -1);
7426 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7428 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7429 enter_side, CE_OTHER_GETS_ENTERED);
7430 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7431 CE_ENTERED_BY_PLAYER, -1);
7438 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7440 int jx = player->jx, jy = player->jy;
7441 int old_jx = jx, old_jy = jy;
7442 int moved = MF_NO_ACTION;
7445 if (!player->active)
7450 if (player->MovPos == 0)
7452 player->is_moving = FALSE;
7453 player->is_digging = FALSE;
7454 player->is_collecting = FALSE;
7455 player->is_snapping = FALSE;
7456 player->is_pushing = FALSE;
7462 if (!player->active || (!dx && !dy))
7467 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7471 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7472 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7476 /* remove the last programmed player action */
7477 player->programmed_action = 0;
7481 /* should only happen if pre-1.2 tape recordings are played */
7482 /* this is only for backward compatibility */
7484 int original_move_delay_value = player->move_delay_value;
7487 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7491 /* scroll remaining steps with finest movement resolution */
7492 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7494 while (player->MovPos)
7496 ScrollPlayer(player, SCROLL_GO_ON);
7497 ScrollScreen(NULL, SCROLL_GO_ON);
7503 player->move_delay_value = original_move_delay_value;
7506 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7508 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7509 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7513 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7514 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7520 if (moved & MF_MOVING && !ScreenMovPos &&
7521 (player == local_player || !options.network))
7523 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7524 int offset = (setup.scroll_delay ? 3 : 0);
7526 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7528 /* actual player has left the screen -- scroll in that direction */
7529 if (jx != old_jx) /* player has moved horizontally */
7530 scroll_x += (jx - old_jx);
7531 else /* player has moved vertically */
7532 scroll_y += (jy - old_jy);
7536 if (jx != old_jx) /* player has moved horizontally */
7538 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7539 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7540 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7542 /* don't scroll over playfield boundaries */
7543 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7544 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7546 /* don't scroll more than one field at a time */
7547 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7549 /* don't scroll against the player's moving direction */
7550 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7551 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7552 scroll_x = old_scroll_x;
7554 else /* player has moved vertically */
7556 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7557 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7558 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7560 /* don't scroll over playfield boundaries */
7561 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7562 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7564 /* don't scroll more than one field at a time */
7565 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7567 /* don't scroll against the player's moving direction */
7568 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7569 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7570 scroll_y = old_scroll_y;
7574 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7576 if (!options.network && !AllPlayersInVisibleScreen())
7578 scroll_x = old_scroll_x;
7579 scroll_y = old_scroll_y;
7583 ScrollScreen(player, SCROLL_INIT);
7584 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7591 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7593 if (!(moved & MF_MOVING) && !player->is_pushing)
7598 player->StepFrame = 0;
7600 if (moved & MF_MOVING)
7602 if (old_jx != jx && old_jy == jy)
7603 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7604 else if (old_jx == jx && old_jy != jy)
7605 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7607 DrawLevelField(jx, jy); /* for "crumbled sand" */
7609 player->last_move_dir = player->MovDir;
7610 player->is_moving = TRUE;
7612 player->is_snapping = FALSE;
7616 player->is_switching = FALSE;
7619 player->is_dropping = FALSE;
7624 static int change_sides[4][2] =
7626 /* enter side leave side */
7627 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7628 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7629 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7630 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7632 int move_direction = player->MovDir;
7633 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7634 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7637 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7639 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7640 leave_side, CE_OTHER_GETS_LEFT);
7641 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7642 leave_side, CE_LEFT_BY_PLAYER, -1);
7645 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7647 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7648 enter_side, CE_OTHER_GETS_ENTERED);
7649 CheckElementSideChange(jx, jy, Feld[jx][jy],
7650 enter_side, CE_ENTERED_BY_PLAYER, -1);
7661 CheckGravityMovement(player);
7664 player->last_move_dir = MV_NO_MOVING;
7666 player->is_moving = FALSE;
7669 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7671 TestIfHeroTouchesBadThing(jx, jy);
7672 TestIfPlayerTouchesCustomElement(jx, jy);
7675 if (!player->active)
7681 void ScrollPlayer(struct PlayerInfo *player, int mode)
7683 int jx = player->jx, jy = player->jy;
7684 int last_jx = player->last_jx, last_jy = player->last_jy;
7685 int move_stepsize = TILEX / player->move_delay_value;
7687 if (!player->active || !player->MovPos)
7690 if (mode == SCROLL_INIT)
7692 player->actual_frame_counter = FrameCounter;
7693 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7695 if (Feld[last_jx][last_jy] == EL_EMPTY)
7696 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7703 else if (!FrameReached(&player->actual_frame_counter, 1))
7706 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7707 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7709 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7710 Feld[last_jx][last_jy] = EL_EMPTY;
7712 /* before DrawPlayer() to draw correct player graphic for this case */
7713 if (player->MovPos == 0)
7714 CheckGravityMovement(player);
7717 DrawPlayer(player); /* needed here only to cleanup last field */
7720 if (player->MovPos == 0) /* player reached destination field */
7722 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7724 /* continue with normal speed after quickly moving through gate */
7725 HALVE_PLAYER_SPEED(player);
7727 /* be able to make the next move without delay */
7728 player->move_delay = 0;
7731 player->last_jx = jx;
7732 player->last_jy = jy;
7734 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7735 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7736 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7738 DrawPlayer(player); /* needed here only to cleanup last field */
7741 if (local_player->friends_still_needed == 0 ||
7742 IS_SP_ELEMENT(Feld[jx][jy]))
7743 player->LevelSolved = player->GameOver = TRUE;
7746 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7748 TestIfHeroTouchesBadThing(jx, jy);
7749 TestIfPlayerTouchesCustomElement(jx, jy);
7751 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7754 if (!player->active)
7758 if (tape.single_step && tape.recording && !tape.pausing &&
7759 !player->programmed_action)
7760 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7764 void ScrollScreen(struct PlayerInfo *player, int mode)
7766 static unsigned long screen_frame_counter = 0;
7768 if (mode == SCROLL_INIT)
7770 /* set scrolling step size according to actual player's moving speed */
7771 ScrollStepSize = TILEX / player->move_delay_value;
7773 screen_frame_counter = FrameCounter;
7774 ScreenMovDir = player->MovDir;
7775 ScreenMovPos = player->MovPos;
7776 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7779 else if (!FrameReached(&screen_frame_counter, 1))
7784 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7785 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7786 redraw_mask |= REDRAW_FIELD;
7789 ScreenMovDir = MV_NO_MOVING;
7792 void TestIfPlayerTouchesCustomElement(int x, int y)
7794 static int xy[4][2] =
7801 static int change_sides[4][2] =
7803 /* center side border side */
7804 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7805 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7806 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7807 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7809 static int touch_dir[4] =
7816 int center_element = Feld[x][y]; /* should always be non-moving! */
7819 for (i = 0; i < 4; i++)
7821 int xx = x + xy[i][0];
7822 int yy = y + xy[i][1];
7823 int center_side = change_sides[i][0];
7824 int border_side = change_sides[i][1];
7827 if (!IN_LEV_FIELD(xx, yy))
7830 if (IS_PLAYER(x, y))
7832 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7833 border_element = Feld[xx][yy]; /* may be moving! */
7834 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7835 border_element = Feld[xx][yy];
7836 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7837 border_element = MovingOrBlocked2Element(xx, yy);
7839 continue; /* center and border element do not touch */
7841 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7842 CE_OTHER_GETS_TOUCHED);
7843 CheckElementSideChange(xx, yy, border_element, border_side,
7844 CE_TOUCHED_BY_PLAYER, -1);
7846 else if (IS_PLAYER(xx, yy))
7848 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7850 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7852 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7853 continue; /* center and border element do not touch */
7856 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7857 CE_OTHER_GETS_TOUCHED);
7858 CheckElementSideChange(x, y, center_element, center_side,
7859 CE_TOUCHED_BY_PLAYER, -1);
7866 void TestIfElementTouchesCustomElement(int x, int y)
7868 static int xy[4][2] =
7875 static int change_sides[4][2] =
7877 /* center side border side */
7878 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7879 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7880 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7881 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7883 static int touch_dir[4] =
7890 boolean change_center_element = FALSE;
7891 int center_element_change_page = 0;
7892 int center_element = Feld[x][y]; /* should always be non-moving! */
7895 for (i = 0; i < 4; i++)
7897 int xx = x + xy[i][0];
7898 int yy = y + xy[i][1];
7899 int center_side = change_sides[i][0];
7900 int border_side = change_sides[i][1];
7903 if (!IN_LEV_FIELD(xx, yy))
7906 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7907 border_element = Feld[xx][yy]; /* may be moving! */
7908 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7909 border_element = Feld[xx][yy];
7910 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7911 border_element = MovingOrBlocked2Element(xx, yy);
7913 continue; /* center and border element do not touch */
7915 /* check for change of center element (but change it only once) */
7916 if (IS_CUSTOM_ELEMENT(center_element) &&
7917 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7918 !change_center_element)
7920 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7922 struct ElementChangeInfo *change =
7923 &element_info[center_element].change_page[j];
7925 if (change->can_change &&
7926 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7927 change->sides & border_side &&
7928 change->trigger_element == border_element)
7930 change_center_element = TRUE;
7931 center_element_change_page = j;
7938 /* check for change of border element */
7939 if (IS_CUSTOM_ELEMENT(border_element) &&
7940 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7942 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7944 struct ElementChangeInfo *change =
7945 &element_info[border_element].change_page[j];
7947 if (change->can_change &&
7948 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7949 change->sides & center_side &&
7950 change->trigger_element == center_element)
7952 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7953 CE_OTHER_IS_TOUCHING, j);
7960 if (change_center_element)
7961 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7962 CE_OTHER_IS_TOUCHING, center_element_change_page);
7965 void TestIfElementHitsCustomElement(int x, int y, int direction)
7967 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7968 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7969 int hitx = x + dx, hity = y + dy;
7970 int hitting_element = Feld[x][y];
7972 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
7973 !IS_FREE(hitx, hity) &&
7974 (!IS_MOVING(hitx, hity) ||
7975 MovDir[hitx][hity] != direction ||
7976 ABS(MovPos[hitx][hity]) <= TILEY / 2));
7979 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
7983 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
7987 CheckElementSideChange(x, y, hitting_element,
7988 direction, CE_HITTING_SOMETHING, -1);
7990 if (IN_LEV_FIELD(hitx, hity))
7992 static int opposite_directions[] =
7999 int move_dir_bit = MV_DIR_BIT(direction);
8000 int opposite_direction = opposite_directions[move_dir_bit];
8001 int hitting_side = direction;
8002 int touched_side = opposite_direction;
8003 int touched_element = MovingOrBlocked2Element(hitx, hity);
8005 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8006 MovDir[hitx][hity] != direction ||
8007 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8016 CheckElementSideChange(hitx, hity, touched_element,
8017 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8019 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8020 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8022 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8024 struct ElementChangeInfo *change =
8025 &element_info[hitting_element].change_page[i];
8027 if (change->can_change &&
8028 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8029 change->sides & touched_side &&
8030 change->trigger_element == touched_element)
8032 CheckElementSideChange(x, y, hitting_element,
8033 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8039 if (IS_CUSTOM_ELEMENT(touched_element) &&
8040 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8042 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8044 struct ElementChangeInfo *change =
8045 &element_info[touched_element].change_page[i];
8047 if (change->can_change &&
8048 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8049 change->sides & hitting_side &&
8050 change->trigger_element == hitting_element)
8052 CheckElementSideChange(hitx, hity, touched_element,
8053 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8062 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8064 int i, kill_x = -1, kill_y = -1;
8065 static int test_xy[4][2] =
8072 static int test_dir[4] =
8080 for (i = 0; i < 4; i++)
8082 int test_x, test_y, test_move_dir, test_element;
8084 test_x = good_x + test_xy[i][0];
8085 test_y = good_y + test_xy[i][1];
8086 if (!IN_LEV_FIELD(test_x, test_y))
8090 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8093 test_element = Feld[test_x][test_y];
8095 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8098 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8099 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8101 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8102 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8110 if (kill_x != -1 || kill_y != -1)
8112 if (IS_PLAYER(good_x, good_y))
8114 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8116 if (player->shield_deadly_time_left > 0)
8117 Bang(kill_x, kill_y);
8118 else if (!PLAYER_PROTECTED(good_x, good_y))
8122 Bang(good_x, good_y);
8126 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8128 int i, kill_x = -1, kill_y = -1;
8129 int bad_element = Feld[bad_x][bad_y];
8130 static int test_xy[4][2] =
8137 static int touch_dir[4] =
8144 static int test_dir[4] =
8152 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8155 for (i = 0; i < 4; i++)
8157 int test_x, test_y, test_move_dir, test_element;
8159 test_x = bad_x + test_xy[i][0];
8160 test_y = bad_y + test_xy[i][1];
8161 if (!IN_LEV_FIELD(test_x, test_y))
8165 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8167 test_element = Feld[test_x][test_y];
8169 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8170 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8172 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8173 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8175 /* good thing is player or penguin that does not move away */
8176 if (IS_PLAYER(test_x, test_y))
8178 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8180 if (bad_element == EL_ROBOT && player->is_moving)
8181 continue; /* robot does not kill player if he is moving */
8183 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8185 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8186 continue; /* center and border element do not touch */
8193 else if (test_element == EL_PENGUIN)
8202 if (kill_x != -1 || kill_y != -1)
8204 if (IS_PLAYER(kill_x, kill_y))
8206 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8208 if (player->shield_deadly_time_left > 0)
8210 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8214 Bang(kill_x, kill_y);
8218 void TestIfHeroTouchesBadThing(int x, int y)
8220 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8223 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8225 TestIfGoodThingHitsBadThing(x, y, move_dir);
8228 void TestIfBadThingTouchesHero(int x, int y)
8230 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8233 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8235 TestIfBadThingHitsGoodThing(x, y, move_dir);
8238 void TestIfFriendTouchesBadThing(int x, int y)
8240 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8243 void TestIfBadThingTouchesFriend(int x, int y)
8245 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8248 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8250 int i, kill_x = bad_x, kill_y = bad_y;
8251 static int xy[4][2] =
8259 for (i = 0; i < 4; i++)
8263 x = bad_x + xy[i][0];
8264 y = bad_y + xy[i][1];
8265 if (!IN_LEV_FIELD(x, y))
8268 element = Feld[x][y];
8269 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8270 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8278 if (kill_x != bad_x || kill_y != bad_y)
8282 void KillHero(struct PlayerInfo *player)
8284 int jx = player->jx, jy = player->jy;
8286 if (!player->active)
8289 /* remove accessible field at the player's position */
8290 Feld[jx][jy] = EL_EMPTY;
8292 /* deactivate shield (else Bang()/Explode() would not work right) */
8293 player->shield_normal_time_left = 0;
8294 player->shield_deadly_time_left = 0;
8300 static void KillHeroUnlessProtected(int x, int y)
8302 if (!PLAYER_PROTECTED(x, y))
8303 KillHero(PLAYERINFO(x, y));
8306 void BuryHero(struct PlayerInfo *player)
8308 int jx = player->jx, jy = player->jy;
8310 if (!player->active)
8314 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8316 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8318 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8320 player->GameOver = TRUE;
8324 void RemoveHero(struct PlayerInfo *player)
8326 int jx = player->jx, jy = player->jy;
8327 int i, found = FALSE;
8329 player->present = FALSE;
8330 player->active = FALSE;
8332 if (!ExplodeField[jx][jy])
8333 StorePlayer[jx][jy] = 0;
8335 for (i = 0; i < MAX_PLAYERS; i++)
8336 if (stored_player[i].active)
8340 AllPlayersGone = TRUE;
8347 =============================================================================
8348 checkDiagonalPushing()
8349 -----------------------------------------------------------------------------
8350 check if diagonal input device direction results in pushing of object
8351 (by checking if the alternative direction is walkable, diggable, ...)
8352 =============================================================================
8355 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8356 int x, int y, int real_dx, int real_dy)
8358 int jx, jy, dx, dy, xx, yy;
8360 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8363 /* diagonal direction: check alternative direction */
8368 xx = jx + (dx == 0 ? real_dx : 0);
8369 yy = jy + (dy == 0 ? real_dy : 0);
8371 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8375 =============================================================================
8377 -----------------------------------------------------------------------------
8378 x, y: field next to player (non-diagonal) to try to dig to
8379 real_dx, real_dy: direction as read from input device (can be diagonal)
8380 =============================================================================
8383 int DigField(struct PlayerInfo *player,
8384 int x, int y, int real_dx, int real_dy, int mode)
8386 static int change_sides[4] =
8388 CH_SIDE_RIGHT, /* moving left */
8389 CH_SIDE_LEFT, /* moving right */
8390 CH_SIDE_BOTTOM, /* moving up */
8391 CH_SIDE_TOP, /* moving down */
8393 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8394 int jx = player->jx, jy = player->jy;
8395 int dx = x - jx, dy = y - jy;
8396 int nextx = x + dx, nexty = y + dy;
8397 int move_direction = (dx == -1 ? MV_LEFT :
8398 dx == +1 ? MV_RIGHT :
8400 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8401 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8404 if (player->MovPos == 0)
8406 player->is_digging = FALSE;
8407 player->is_collecting = FALSE;
8410 if (player->MovPos == 0) /* last pushing move finished */
8411 player->is_pushing = FALSE;
8413 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8415 player->is_switching = FALSE;
8416 player->push_delay = 0;
8418 return MF_NO_ACTION;
8421 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8422 return MF_NO_ACTION;
8425 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8427 if (IS_TUBE(Feld[jx][jy]) ||
8428 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8432 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8433 int tube_leave_directions[][2] =
8435 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8436 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8437 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8438 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8439 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8440 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8441 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8442 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8443 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8444 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8445 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8446 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8449 while (tube_leave_directions[i][0] != tube_element)
8452 if (tube_leave_directions[i][0] == -1) /* should not happen */
8456 if (!(tube_leave_directions[i][1] & move_direction))
8457 return MF_NO_ACTION; /* tube has no opening in this direction */
8460 element = Feld[x][y];
8462 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8463 game.engine_version >= VERSION_IDENT(2,2,0,0))
8464 return MF_NO_ACTION;
8468 case EL_SP_PORT_LEFT:
8469 case EL_SP_PORT_RIGHT:
8471 case EL_SP_PORT_DOWN:
8472 case EL_SP_PORT_HORIZONTAL:
8473 case EL_SP_PORT_VERTICAL:
8474 case EL_SP_PORT_ANY:
8475 case EL_SP_GRAVITY_PORT_LEFT:
8476 case EL_SP_GRAVITY_PORT_RIGHT:
8477 case EL_SP_GRAVITY_PORT_UP:
8478 case EL_SP_GRAVITY_PORT_DOWN:
8480 element != EL_SP_PORT_LEFT &&
8481 element != EL_SP_GRAVITY_PORT_LEFT &&
8482 element != EL_SP_PORT_HORIZONTAL &&
8483 element != EL_SP_PORT_ANY) ||
8485 element != EL_SP_PORT_RIGHT &&
8486 element != EL_SP_GRAVITY_PORT_RIGHT &&
8487 element != EL_SP_PORT_HORIZONTAL &&
8488 element != EL_SP_PORT_ANY) ||
8490 element != EL_SP_PORT_UP &&
8491 element != EL_SP_GRAVITY_PORT_UP &&
8492 element != EL_SP_PORT_VERTICAL &&
8493 element != EL_SP_PORT_ANY) ||
8495 element != EL_SP_PORT_DOWN &&
8496 element != EL_SP_GRAVITY_PORT_DOWN &&
8497 element != EL_SP_PORT_VERTICAL &&
8498 element != EL_SP_PORT_ANY) ||
8499 !IN_LEV_FIELD(nextx, nexty) ||
8500 !IS_FREE(nextx, nexty))
8501 return MF_NO_ACTION;
8503 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8504 element == EL_SP_GRAVITY_PORT_RIGHT ||
8505 element == EL_SP_GRAVITY_PORT_UP ||
8506 element == EL_SP_GRAVITY_PORT_DOWN)
8507 game.gravity = !game.gravity;
8509 /* automatically move to the next field with double speed */
8510 player->programmed_action = move_direction;
8511 DOUBLE_PLAYER_SPEED(player);
8513 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8517 case EL_TUBE_VERTICAL:
8518 case EL_TUBE_HORIZONTAL:
8519 case EL_TUBE_VERTICAL_LEFT:
8520 case EL_TUBE_VERTICAL_RIGHT:
8521 case EL_TUBE_HORIZONTAL_UP:
8522 case EL_TUBE_HORIZONTAL_DOWN:
8523 case EL_TUBE_LEFT_UP:
8524 case EL_TUBE_LEFT_DOWN:
8525 case EL_TUBE_RIGHT_UP:
8526 case EL_TUBE_RIGHT_DOWN:
8529 int tube_enter_directions[][2] =
8531 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8532 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8533 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8534 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8535 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8536 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8537 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8538 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8539 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8540 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8541 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8542 { -1, MV_NO_MOVING }
8545 while (tube_enter_directions[i][0] != element)
8548 if (tube_enter_directions[i][0] == -1) /* should not happen */
8552 if (!(tube_enter_directions[i][1] & move_direction))
8553 return MF_NO_ACTION; /* tube has no opening in this direction */
8555 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8561 if (IS_WALKABLE(element))
8563 int sound_action = ACTION_WALKING;
8565 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8567 if (!player->key[element - EL_GATE_1])
8568 return MF_NO_ACTION;
8570 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8572 if (!player->key[element - EL_GATE_1_GRAY])
8573 return MF_NO_ACTION;
8575 else if (element == EL_EXIT_OPEN ||
8576 element == EL_SP_EXIT_OPEN ||
8577 element == EL_SP_EXIT_OPENING)
8579 sound_action = ACTION_PASSING; /* player is passing exit */
8581 else if (element == EL_EMPTY)
8583 sound_action = ACTION_MOVING; /* nothing to walk on */
8586 /* play sound from background or player, whatever is available */
8587 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8588 PlayLevelSoundElementAction(x, y, element, sound_action);
8590 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8594 else if (IS_PASSABLE(element))
8596 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8597 return MF_NO_ACTION;
8600 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8601 return MF_NO_ACTION;
8604 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8606 if (!player->key[element - EL_EM_GATE_1])
8607 return MF_NO_ACTION;
8609 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8611 if (!player->key[element - EL_EM_GATE_1_GRAY])
8612 return MF_NO_ACTION;
8615 /* automatically move to the next field with double speed */
8616 player->programmed_action = move_direction;
8617 DOUBLE_PLAYER_SPEED(player);
8619 PlayLevelSoundAction(x, y, ACTION_PASSING);
8623 else if (IS_DIGGABLE(element))
8627 if (mode != DF_SNAP)
8630 GfxElement[x][y] = GFX_ELEMENT(element);
8633 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8635 player->is_digging = TRUE;
8638 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8640 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8643 if (mode == DF_SNAP)
8644 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8649 else if (IS_COLLECTIBLE(element))
8653 if (mode != DF_SNAP)
8655 GfxElement[x][y] = element;
8656 player->is_collecting = TRUE;
8659 if (element == EL_SPEED_PILL)
8660 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8661 else if (element == EL_EXTRA_TIME && level.time > 0)
8664 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8666 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8668 player->shield_normal_time_left += 10;
8669 if (element == EL_SHIELD_DEADLY)
8670 player->shield_deadly_time_left += 10;
8672 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8674 if (player->inventory_size < MAX_INVENTORY_SIZE)
8675 player->inventory_element[player->inventory_size++] = element;
8677 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8678 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8680 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8682 player->dynabomb_count++;
8683 player->dynabombs_left++;
8685 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8687 player->dynabomb_size++;
8689 else if (element == EL_DYNABOMB_INCREASE_POWER)
8691 player->dynabomb_xl = TRUE;
8693 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8694 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8696 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8697 element - EL_KEY_1 : element - EL_EM_KEY_1);
8699 player->key[key_nr] = TRUE;
8701 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8702 el2edimg(EL_KEY_1 + key_nr));
8703 redraw_mask |= REDRAW_DOOR_1;
8705 else if (IS_ENVELOPE(element))
8708 player->show_envelope = element;
8710 ShowEnvelope(element - EL_ENVELOPE_1);
8713 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8717 for (i = 0; i < element_info[element].collect_count; i++)
8718 if (player->inventory_size < MAX_INVENTORY_SIZE)
8719 player->inventory_element[player->inventory_size++] = element;
8721 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8722 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8724 else if (element_info[element].collect_count > 0)
8726 local_player->gems_still_needed -=
8727 element_info[element].collect_count;
8728 if (local_player->gems_still_needed < 0)
8729 local_player->gems_still_needed = 0;
8731 DrawText(DX_EMERALDS, DY_EMERALDS,
8732 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8735 RaiseScoreElement(element);
8736 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8738 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8741 if (mode == DF_SNAP)
8742 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8747 else if (IS_PUSHABLE(element))
8749 if (mode == DF_SNAP && element != EL_BD_ROCK)
8750 return MF_NO_ACTION;
8752 if (CAN_FALL(element) && dy)
8753 return MF_NO_ACTION;
8755 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8756 !(element == EL_SPRING && use_spring_bug))
8757 return MF_NO_ACTION;
8760 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8761 ((move_direction & MV_VERTICAL &&
8762 ((element_info[element].move_pattern & MV_LEFT &&
8763 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8764 (element_info[element].move_pattern & MV_RIGHT &&
8765 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8766 (move_direction & MV_HORIZONTAL &&
8767 ((element_info[element].move_pattern & MV_UP &&
8768 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8769 (element_info[element].move_pattern & MV_DOWN &&
8770 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8771 return MF_NO_ACTION;
8775 /* do not push elements already moving away faster than player */
8776 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8777 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8778 return MF_NO_ACTION;
8780 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8781 return MF_NO_ACTION;
8785 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8787 if (player->push_delay_value == -1)
8788 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8790 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8792 if (!player->is_pushing)
8793 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8797 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8798 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8799 !player_is_pushing))
8800 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8803 if (!player->is_pushing &&
8804 game.engine_version >= VERSION_IDENT(2,2,0,7))
8805 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8809 printf("::: push delay: %ld [%d, %d] [%d]\n",
8810 player->push_delay_value, FrameCounter, game.engine_version,
8811 player->is_pushing);
8814 player->is_pushing = TRUE;
8816 if (!(IN_LEV_FIELD(nextx, nexty) &&
8817 (IS_FREE(nextx, nexty) ||
8818 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8819 IS_SB_ELEMENT(element)))))
8820 return MF_NO_ACTION;
8822 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8823 return MF_NO_ACTION;
8825 if (player->push_delay == 0) /* new pushing; restart delay */
8826 player->push_delay = FrameCounter;
8828 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8829 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8830 element != EL_SPRING && element != EL_BALLOON)
8832 /* make sure that there is no move delay before next try to push */
8833 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8834 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8836 return MF_NO_ACTION;
8840 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8843 if (IS_SB_ELEMENT(element))
8845 if (element == EL_SOKOBAN_FIELD_FULL)
8847 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8848 local_player->sokobanfields_still_needed++;
8851 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8853 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8854 local_player->sokobanfields_still_needed--;
8857 Feld[x][y] = EL_SOKOBAN_OBJECT;
8859 if (Back[x][y] == Back[nextx][nexty])
8860 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8861 else if (Back[x][y] != 0)
8862 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8865 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8868 if (local_player->sokobanfields_still_needed == 0 &&
8869 game.emulation == EMU_SOKOBAN)
8871 player->LevelSolved = player->GameOver = TRUE;
8872 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8876 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8878 InitMovingField(x, y, move_direction);
8879 GfxAction[x][y] = ACTION_PUSHING;
8881 if (mode == DF_SNAP)
8882 ContinueMoving(x, y);
8884 MovPos[x][y] = (dx != 0 ? dx : dy);
8886 Pushed[x][y] = TRUE;
8887 Pushed[nextx][nexty] = TRUE;
8889 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8890 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8892 player->push_delay_value = -1; /* get new value later */
8894 CheckTriggeredElementSideChange(x, y, element, dig_side,
8895 CE_OTHER_GETS_PUSHED);
8896 CheckElementSideChange(x, y, element, dig_side,
8897 CE_PUSHED_BY_PLAYER, -1);
8901 else if (IS_SWITCHABLE(element))
8903 if (PLAYER_SWITCHING(player, x, y))
8906 player->is_switching = TRUE;
8907 player->switch_x = x;
8908 player->switch_y = y;
8910 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8912 if (element == EL_ROBOT_WHEEL)
8914 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8918 DrawLevelField(x, y);
8920 else if (element == EL_SP_TERMINAL)
8924 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8926 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8928 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8929 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8932 else if (IS_BELT_SWITCH(element))
8934 ToggleBeltSwitch(x, y);
8936 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8937 element == EL_SWITCHGATE_SWITCH_DOWN)
8939 ToggleSwitchgateSwitch(x, y);
8941 else if (element == EL_LIGHT_SWITCH ||
8942 element == EL_LIGHT_SWITCH_ACTIVE)
8944 ToggleLightSwitch(x, y);
8947 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8948 SND_LIGHT_SWITCH_ACTIVATING :
8949 SND_LIGHT_SWITCH_DEACTIVATING);
8952 else if (element == EL_TIMEGATE_SWITCH)
8954 ActivateTimegateSwitch(x, y);
8956 else if (element == EL_BALLOON_SWITCH_LEFT ||
8957 element == EL_BALLOON_SWITCH_RIGHT ||
8958 element == EL_BALLOON_SWITCH_UP ||
8959 element == EL_BALLOON_SWITCH_DOWN ||
8960 element == EL_BALLOON_SWITCH_ANY)
8962 if (element == EL_BALLOON_SWITCH_ANY)
8963 game.balloon_dir = move_direction;
8965 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8966 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8967 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8968 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8971 else if (element == EL_LAMP)
8973 Feld[x][y] = EL_LAMP_ACTIVE;
8974 local_player->lights_still_needed--;
8976 DrawLevelField(x, y);
8978 else if (element == EL_TIME_ORB_FULL)
8980 Feld[x][y] = EL_TIME_ORB_EMPTY;
8982 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8984 DrawLevelField(x, y);
8987 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8995 if (!PLAYER_SWITCHING(player, x, y))
8997 player->is_switching = TRUE;
8998 player->switch_x = x;
8999 player->switch_y = y;
9001 CheckTriggeredElementSideChange(x, y, element, dig_side,
9002 CE_OTHER_IS_SWITCHING);
9003 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9006 CheckTriggeredElementSideChange(x, y, element, dig_side,
9007 CE_OTHER_GETS_PRESSED);
9008 CheckElementSideChange(x, y, element, dig_side,
9009 CE_PRESSED_BY_PLAYER, -1);
9012 return MF_NO_ACTION;
9015 player->push_delay = 0;
9017 if (Feld[x][y] != element) /* really digged/collected something */
9018 player->is_collecting = !player->is_digging;
9023 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9025 int jx = player->jx, jy = player->jy;
9026 int x = jx + dx, y = jy + dy;
9027 int snap_direction = (dx == -1 ? MV_LEFT :
9028 dx == +1 ? MV_RIGHT :
9030 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9032 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9035 if (!player->active || !IN_LEV_FIELD(x, y))
9043 if (player->MovPos == 0)
9044 player->is_pushing = FALSE;
9046 player->is_snapping = FALSE;
9048 if (player->MovPos == 0)
9050 player->is_moving = FALSE;
9051 player->is_digging = FALSE;
9052 player->is_collecting = FALSE;
9058 if (player->is_snapping)
9061 player->MovDir = snap_direction;
9063 player->is_moving = FALSE;
9064 player->is_digging = FALSE;
9065 player->is_collecting = FALSE;
9067 player->is_dropping = FALSE;
9069 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9072 player->is_snapping = TRUE;
9074 player->is_moving = FALSE;
9075 player->is_digging = FALSE;
9076 player->is_collecting = FALSE;
9078 DrawLevelField(x, y);
9084 boolean DropElement(struct PlayerInfo *player)
9086 int jx = player->jx, jy = player->jy;
9087 int old_element = Feld[jx][jy];
9090 /* check if player is active, not moving and ready to drop */
9091 if (!player->active || player->MovPos || player->drop_delay > 0)
9094 /* check if player has anything that can be dropped */
9095 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9098 /* check if anything can be dropped at the current position */
9099 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9102 /* collected custom elements can only be dropped on empty fields */
9103 if (player->inventory_size > 0 &&
9104 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9105 && old_element != EL_EMPTY)
9108 if (old_element != EL_EMPTY)
9109 Back[jx][jy] = old_element; /* store old element on this field */
9111 ResetGfxAnimation(jx, jy);
9112 ResetRandomAnimationValue(jx, jy);
9114 if (player->inventory_size > 0)
9116 player->inventory_size--;
9117 new_element = player->inventory_element[player->inventory_size];
9119 if (new_element == EL_DYNAMITE)
9120 new_element = EL_DYNAMITE_ACTIVE;
9121 else if (new_element == EL_SP_DISK_RED)
9122 new_element = EL_SP_DISK_RED_ACTIVE;
9124 Feld[jx][jy] = new_element;
9126 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9127 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9129 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9130 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9132 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9134 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9135 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9137 TestIfElementTouchesCustomElement(jx, jy);
9139 else /* player is dropping a dyna bomb */
9141 player->dynabombs_left--;
9142 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9144 Feld[jx][jy] = new_element;
9146 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9147 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9149 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9156 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9158 InitField(jx, jy, FALSE);
9159 if (CAN_MOVE(Feld[jx][jy]))
9163 new_element = Feld[jx][jy];
9165 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9166 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9168 int move_stepsize = element_info[new_element].move_stepsize;
9169 int direction, dx, dy, nextx, nexty;
9171 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9172 MovDir[jx][jy] = player->MovDir;
9174 direction = MovDir[jx][jy];
9175 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9176 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9180 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9183 WasJustMoving[jx][jy] = 3;
9185 InitMovingField(jx, jy, direction);
9186 ContinueMoving(jx, jy);
9191 Changed[jx][jy] = 0; /* allow another change */
9194 TestIfElementHitsCustomElement(jx, jy, direction);
9196 CheckElementSideChange(jx, jy, new_element,
9197 direction, CE_HITTING_SOMETHING, -1);
9201 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9205 player->drop_delay = 8 + 8 + 8;
9210 player->is_dropping = TRUE;
9216 /* ------------------------------------------------------------------------- */
9217 /* game sound playing functions */
9218 /* ------------------------------------------------------------------------- */
9220 static int *loop_sound_frame = NULL;
9221 static int *loop_sound_volume = NULL;
9223 void InitPlayLevelSound()
9225 int num_sounds = getSoundListSize();
9227 checked_free(loop_sound_frame);
9228 checked_free(loop_sound_volume);
9230 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9231 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9234 static void PlayLevelSound(int x, int y, int nr)
9236 int sx = SCREENX(x), sy = SCREENY(y);
9237 int volume, stereo_position;
9238 int max_distance = 8;
9239 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9241 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9242 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9245 if (!IN_LEV_FIELD(x, y) ||
9246 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9247 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9250 volume = SOUND_MAX_VOLUME;
9252 if (!IN_SCR_FIELD(sx, sy))
9254 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9255 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9257 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9260 stereo_position = (SOUND_MAX_LEFT +
9261 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9262 (SCR_FIELDX + 2 * max_distance));
9264 if (IS_LOOP_SOUND(nr))
9266 /* This assures that quieter loop sounds do not overwrite louder ones,
9267 while restarting sound volume comparison with each new game frame. */
9269 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9272 loop_sound_volume[nr] = volume;
9273 loop_sound_frame[nr] = FrameCounter;
9276 PlaySoundExt(nr, volume, stereo_position, type);
9279 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9281 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9282 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9283 y < LEVELY(BY1) ? LEVELY(BY1) :
9284 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9288 static void PlayLevelSoundAction(int x, int y, int action)
9290 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9293 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9295 int sound_effect = element_info[element].sound[action];
9297 if (sound_effect != SND_UNDEFINED)
9298 PlayLevelSound(x, y, sound_effect);
9301 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9304 int sound_effect = element_info[element].sound[action];
9306 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9307 PlayLevelSound(x, y, sound_effect);
9310 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9312 int sound_effect = element_info[Feld[x][y]].sound[action];
9314 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9315 PlayLevelSound(x, y, sound_effect);
9318 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9320 int sound_effect = element_info[Feld[x][y]].sound[action];
9322 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9323 StopSound(sound_effect);
9326 static void PlayLevelMusic()
9328 if (levelset.music[level_nr] != MUS_UNDEFINED)
9329 PlayMusic(levelset.music[level_nr]); /* from config file */
9331 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9334 void RaiseScore(int value)
9336 local_player->score += value;
9337 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9340 void RaiseScoreElement(int element)
9346 case EL_EMERALD_YELLOW:
9347 case EL_EMERALD_RED:
9348 case EL_EMERALD_PURPLE:
9349 case EL_SP_INFOTRON:
9350 RaiseScore(level.score[SC_EMERALD]);
9353 RaiseScore(level.score[SC_DIAMOND]);
9356 RaiseScore(level.score[SC_CRYSTAL]);
9359 RaiseScore(level.score[SC_PEARL]);
9362 case EL_BD_BUTTERFLY:
9363 case EL_SP_ELECTRON:
9364 RaiseScore(level.score[SC_BUG]);
9368 case EL_SP_SNIKSNAK:
9369 RaiseScore(level.score[SC_SPACESHIP]);
9372 case EL_DARK_YAMYAM:
9373 RaiseScore(level.score[SC_YAMYAM]);
9376 RaiseScore(level.score[SC_ROBOT]);
9379 RaiseScore(level.score[SC_PACMAN]);
9382 RaiseScore(level.score[SC_NUT]);
9385 case EL_SP_DISK_RED:
9386 case EL_DYNABOMB_INCREASE_NUMBER:
9387 case EL_DYNABOMB_INCREASE_SIZE:
9388 case EL_DYNABOMB_INCREASE_POWER:
9389 RaiseScore(level.score[SC_DYNAMITE]);
9391 case EL_SHIELD_NORMAL:
9392 case EL_SHIELD_DEADLY:
9393 RaiseScore(level.score[SC_SHIELD]);
9396 RaiseScore(level.score[SC_TIME_BONUS]);
9402 RaiseScore(level.score[SC_KEY]);
9405 RaiseScore(element_info[element].collect_score);
9410 void RequestQuitGame(boolean ask_if_really_quit)
9412 if (AllPlayersGone ||
9413 !ask_if_really_quit ||
9414 level_editor_test_game ||
9415 Request("Do you really want to quit the game ?",
9416 REQ_ASK | REQ_STAY_CLOSED))
9418 #if defined(PLATFORM_UNIX)
9419 if (options.network)
9420 SendToServer_StopPlaying();
9424 game_status = GAME_MODE_MAIN;
9430 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9435 /* ---------- new game button stuff ---------------------------------------- */
9437 /* graphic position values for game buttons */
9438 #define GAME_BUTTON_XSIZE 30
9439 #define GAME_BUTTON_YSIZE 30
9440 #define GAME_BUTTON_XPOS 5
9441 #define GAME_BUTTON_YPOS 215
9442 #define SOUND_BUTTON_XPOS 5
9443 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9445 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9446 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9447 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9448 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9449 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9450 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9457 } gamebutton_info[NUM_GAME_BUTTONS] =
9460 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9465 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9470 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9475 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9476 SOUND_CTRL_ID_MUSIC,
9477 "background music on/off"
9480 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9481 SOUND_CTRL_ID_LOOPS,
9482 "sound loops on/off"
9485 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9486 SOUND_CTRL_ID_SIMPLE,
9487 "normal sounds on/off"
9491 void CreateGameButtons()
9495 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9497 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9498 struct GadgetInfo *gi;
9501 unsigned long event_mask;
9502 int gd_xoffset, gd_yoffset;
9503 int gd_x1, gd_x2, gd_y1, gd_y2;
9506 gd_xoffset = gamebutton_info[i].x;
9507 gd_yoffset = gamebutton_info[i].y;
9508 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9509 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9511 if (id == GAME_CTRL_ID_STOP ||
9512 id == GAME_CTRL_ID_PAUSE ||
9513 id == GAME_CTRL_ID_PLAY)
9515 button_type = GD_TYPE_NORMAL_BUTTON;
9517 event_mask = GD_EVENT_RELEASED;
9518 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9519 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9523 button_type = GD_TYPE_CHECK_BUTTON;
9525 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9526 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9527 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9528 event_mask = GD_EVENT_PRESSED;
9529 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9530 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9533 gi = CreateGadget(GDI_CUSTOM_ID, id,
9534 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9535 GDI_X, DX + gd_xoffset,
9536 GDI_Y, DY + gd_yoffset,
9537 GDI_WIDTH, GAME_BUTTON_XSIZE,
9538 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9539 GDI_TYPE, button_type,
9540 GDI_STATE, GD_BUTTON_UNPRESSED,
9541 GDI_CHECKED, checked,
9542 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9543 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9544 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9545 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9546 GDI_EVENT_MASK, event_mask,
9547 GDI_CALLBACK_ACTION, HandleGameButtons,
9551 Error(ERR_EXIT, "cannot create gadget");
9553 game_gadget[id] = gi;
9557 void FreeGameButtons()
9561 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9562 FreeGadget(game_gadget[i]);
9565 static void MapGameButtons()
9569 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9570 MapGadget(game_gadget[i]);
9573 void UnmapGameButtons()
9577 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9578 UnmapGadget(game_gadget[i]);
9581 static void HandleGameButtons(struct GadgetInfo *gi)
9583 int id = gi->custom_id;
9585 if (game_status != GAME_MODE_PLAYING)
9590 case GAME_CTRL_ID_STOP:
9591 RequestQuitGame(TRUE);
9594 case GAME_CTRL_ID_PAUSE:
9595 if (options.network)
9597 #if defined(PLATFORM_UNIX)
9599 SendToServer_ContinuePlaying();
9601 SendToServer_PausePlaying();
9605 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9608 case GAME_CTRL_ID_PLAY:
9611 #if defined(PLATFORM_UNIX)
9612 if (options.network)
9613 SendToServer_ContinuePlaying();
9617 tape.pausing = FALSE;
9618 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9623 case SOUND_CTRL_ID_MUSIC:
9624 if (setup.sound_music)
9626 setup.sound_music = FALSE;
9629 else if (audio.music_available)
9631 setup.sound = setup.sound_music = TRUE;
9633 SetAudioMode(setup.sound);
9639 case SOUND_CTRL_ID_LOOPS:
9640 if (setup.sound_loops)
9641 setup.sound_loops = FALSE;
9642 else if (audio.loops_available)
9644 setup.sound = setup.sound_loops = TRUE;
9645 SetAudioMode(setup.sound);
9649 case SOUND_CTRL_ID_SIMPLE:
9650 if (setup.sound_simple)
9651 setup.sound_simple = FALSE;
9652 else if (audio.sound_available)
9654 setup.sound = setup.sound_simple = TRUE;
9655 SetAudioMode(setup.sound);