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->move_delay_reset_counter = 0;
1138 player->push_delay = 0;
1139 player->push_delay_value = game.initial_push_delay_value;
1141 player->drop_delay = 0;
1143 player->last_jx = player->last_jy = 0;
1144 player->jx = player->jy = 0;
1146 player->shield_normal_time_left = 0;
1147 player->shield_deadly_time_left = 0;
1149 player->inventory_size = 0;
1151 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1152 SnapField(player, 0, 0);
1154 player->LevelSolved = FALSE;
1155 player->GameOver = FALSE;
1158 network_player_action_received = FALSE;
1160 #if defined(PLATFORM_UNIX)
1161 /* initial null action */
1162 if (network_playing)
1163 SendToServer_MovePlayer(MV_NO_MOVING);
1171 TimeLeft = level.time;
1173 ScreenMovDir = MV_NO_MOVING;
1177 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1179 AllPlayersGone = FALSE;
1181 game.yamyam_content_nr = 0;
1182 game.magic_wall_active = FALSE;
1183 game.magic_wall_time_left = 0;
1184 game.light_time_left = 0;
1185 game.timegate_time_left = 0;
1186 game.switchgate_pos = 0;
1187 game.balloon_dir = MV_NO_MOVING;
1188 game.gravity = level.initial_gravity;
1189 game.explosions_delayed = TRUE;
1191 game.envelope_active = FALSE;
1193 for (i = 0; i < 4; i++)
1195 game.belt_dir[i] = MV_NO_MOVING;
1196 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1199 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1200 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1202 for (x = 0; x < lev_fieldx; x++)
1204 for (y = 0; y < lev_fieldy; y++)
1206 Feld[x][y] = level.field[x][y];
1207 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1208 ChangeDelay[x][y] = 0;
1209 ChangePage[x][y] = -1;
1210 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1212 WasJustMoving[x][y] = 0;
1213 WasJustFalling[x][y] = 0;
1215 Pushed[x][y] = FALSE;
1217 Changed[x][y] = CE_BITMASK_DEFAULT;
1218 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1220 ExplodePhase[x][y] = 0;
1221 ExplodeField[x][y] = EX_NO_EXPLOSION;
1223 RunnerVisit[x][y] = 0;
1224 PlayerVisit[x][y] = 0;
1227 GfxRandom[x][y] = INIT_GFX_RANDOM();
1228 GfxElement[x][y] = EL_UNDEFINED;
1229 GfxAction[x][y] = ACTION_DEFAULT;
1230 GfxDir[x][y] = MV_NO_MOVING;
1234 for (y = 0; y < lev_fieldy; y++)
1236 for (x = 0; x < lev_fieldx; x++)
1238 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1240 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1242 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1245 InitField(x, y, TRUE);
1251 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1252 emulate_sb ? EMU_SOKOBAN :
1253 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1255 /* correct non-moving belts to start moving left */
1256 for (i = 0; i < 4; i++)
1257 if (game.belt_dir[i] == MV_NO_MOVING)
1258 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1260 /* check if any connected player was not found in playfield */
1261 for (i = 0; i < MAX_PLAYERS; i++)
1263 struct PlayerInfo *player = &stored_player[i];
1265 if (player->connected && !player->present)
1267 for (j = 0; j < MAX_PLAYERS; j++)
1269 struct PlayerInfo *some_player = &stored_player[j];
1270 int jx = some_player->jx, jy = some_player->jy;
1272 /* assign first free player found that is present in the playfield */
1273 if (some_player->present && !some_player->connected)
1275 player->present = TRUE;
1276 player->active = TRUE;
1277 some_player->present = FALSE;
1279 StorePlayer[jx][jy] = player->element_nr;
1280 player->jx = player->last_jx = jx;
1281 player->jy = player->last_jy = jy;
1291 /* when playing a tape, eliminate all players who do not participate */
1293 for (i = 0; i < MAX_PLAYERS; i++)
1295 if (stored_player[i].active && !tape.player_participates[i])
1297 struct PlayerInfo *player = &stored_player[i];
1298 int jx = player->jx, jy = player->jy;
1300 player->active = FALSE;
1301 StorePlayer[jx][jy] = 0;
1302 Feld[jx][jy] = EL_EMPTY;
1306 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1308 /* when in single player mode, eliminate all but the first active player */
1310 for (i = 0; i < MAX_PLAYERS; i++)
1312 if (stored_player[i].active)
1314 for (j = i + 1; j < MAX_PLAYERS; j++)
1316 if (stored_player[j].active)
1318 struct PlayerInfo *player = &stored_player[j];
1319 int jx = player->jx, jy = player->jy;
1321 player->active = FALSE;
1322 StorePlayer[jx][jy] = 0;
1323 Feld[jx][jy] = EL_EMPTY;
1330 /* when recording the game, store which players take part in the game */
1333 for (i = 0; i < MAX_PLAYERS; i++)
1334 if (stored_player[i].active)
1335 tape.player_participates[i] = TRUE;
1340 for (i = 0; i < MAX_PLAYERS; i++)
1342 struct PlayerInfo *player = &stored_player[i];
1344 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1349 if (local_player == player)
1350 printf("Player %d is local player.\n", i+1);
1354 if (BorderElement == EL_EMPTY)
1357 SBX_Right = lev_fieldx - SCR_FIELDX;
1359 SBY_Lower = lev_fieldy - SCR_FIELDY;
1364 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1366 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1369 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1370 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1372 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1373 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1375 /* if local player not found, look for custom element that might create
1376 the player (make some assumptions about the right custom element) */
1377 if (!local_player->present)
1379 int start_x = 0, start_y = 0;
1380 int found_rating = 0;
1381 int found_element = EL_UNDEFINED;
1383 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1385 int element = Feld[x][y];
1390 if (!IS_CUSTOM_ELEMENT(element))
1393 if (CAN_CHANGE(element))
1395 for (i = 0; i < element_info[element].num_change_pages; i++)
1397 content = element_info[element].change_page[i].target_element;
1398 is_player = ELEM_IS_PLAYER(content);
1400 if (is_player && (found_rating < 3 || element < found_element))
1406 found_element = element;
1411 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1413 content = element_info[element].content[xx][yy];
1414 is_player = ELEM_IS_PLAYER(content);
1416 if (is_player && (found_rating < 2 || element < found_element))
1418 start_x = x + xx - 1;
1419 start_y = y + yy - 1;
1422 found_element = element;
1425 if (!CAN_CHANGE(element))
1428 for (i = 0; i < element_info[element].num_change_pages; i++)
1430 content = element_info[element].change_page[i].content[xx][yy];
1431 is_player = ELEM_IS_PLAYER(content);
1433 if (is_player && (found_rating < 1 || element < found_element))
1435 start_x = x + xx - 1;
1436 start_y = y + yy - 1;
1439 found_element = element;
1445 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1446 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1449 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1450 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1456 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1457 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1458 local_player->jx - MIDPOSX);
1460 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1461 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1462 local_player->jy - MIDPOSY);
1464 scroll_x = SBX_Left;
1465 scroll_y = SBY_Upper;
1466 if (local_player->jx >= SBX_Left + MIDPOSX)
1467 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1468 local_player->jx - MIDPOSX :
1470 if (local_player->jy >= SBY_Upper + MIDPOSY)
1471 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1472 local_player->jy - MIDPOSY :
1477 CloseDoor(DOOR_CLOSE_1);
1482 /* after drawing the level, correct some elements */
1483 if (game.timegate_time_left == 0)
1484 CloseAllOpenTimegates();
1486 if (setup.soft_scrolling)
1487 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1489 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1492 /* copy default game door content to main double buffer */
1493 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1494 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1497 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1500 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1501 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1502 BlitBitmap(drawto, drawto,
1503 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1504 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1505 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1506 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1509 DrawGameDoorValues();
1513 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1514 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1515 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1519 /* copy actual game door content to door double buffer for OpenDoor() */
1520 BlitBitmap(drawto, bitmap_db_door,
1521 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1523 OpenDoor(DOOR_OPEN_ALL);
1525 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1527 if (setup.sound_music)
1530 KeyboardAutoRepeatOffUnlessAutoplay();
1534 for (i = 0; i < 4; i++)
1535 printf("Player %d %sactive.\n",
1536 i + 1, (stored_player[i].active ? "" : "not "));
1540 printf("::: starting game [%d]\n", FrameCounter);
1544 void InitMovDir(int x, int y)
1546 int i, element = Feld[x][y];
1547 static int xy[4][2] =
1554 static int direction[3][4] =
1556 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1557 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1558 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1567 Feld[x][y] = EL_BUG;
1568 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1571 case EL_SPACESHIP_RIGHT:
1572 case EL_SPACESHIP_UP:
1573 case EL_SPACESHIP_LEFT:
1574 case EL_SPACESHIP_DOWN:
1575 Feld[x][y] = EL_SPACESHIP;
1576 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1579 case EL_BD_BUTTERFLY_RIGHT:
1580 case EL_BD_BUTTERFLY_UP:
1581 case EL_BD_BUTTERFLY_LEFT:
1582 case EL_BD_BUTTERFLY_DOWN:
1583 Feld[x][y] = EL_BD_BUTTERFLY;
1584 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1587 case EL_BD_FIREFLY_RIGHT:
1588 case EL_BD_FIREFLY_UP:
1589 case EL_BD_FIREFLY_LEFT:
1590 case EL_BD_FIREFLY_DOWN:
1591 Feld[x][y] = EL_BD_FIREFLY;
1592 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1595 case EL_PACMAN_RIGHT:
1597 case EL_PACMAN_LEFT:
1598 case EL_PACMAN_DOWN:
1599 Feld[x][y] = EL_PACMAN;
1600 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1603 case EL_SP_SNIKSNAK:
1604 MovDir[x][y] = MV_UP;
1607 case EL_SP_ELECTRON:
1608 MovDir[x][y] = MV_LEFT;
1615 Feld[x][y] = EL_MOLE;
1616 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1620 if (IS_CUSTOM_ELEMENT(element))
1622 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1623 MovDir[x][y] = element_info[element].move_direction_initial;
1624 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1625 element_info[element].move_pattern == MV_TURNING_LEFT ||
1626 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1627 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1628 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1629 element_info[element].move_pattern == MV_TURNING_RANDOM)
1630 MovDir[x][y] = 1 << RND(4);
1631 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1632 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1633 else if (element_info[element].move_pattern == MV_VERTICAL)
1634 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1635 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1636 MovDir[x][y] = element_info[element].move_pattern;
1637 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1638 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1640 for (i = 0; i < 4; i++)
1642 int x1 = x + xy[i][0];
1643 int y1 = y + xy[i][1];
1645 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1647 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1648 MovDir[x][y] = direction[0][i];
1650 MovDir[x][y] = direction[1][i];
1659 MovDir[x][y] = 1 << RND(4);
1661 if (element != EL_BUG &&
1662 element != EL_SPACESHIP &&
1663 element != EL_BD_BUTTERFLY &&
1664 element != EL_BD_FIREFLY)
1667 for (i = 0; i < 4; i++)
1669 int x1 = x + xy[i][0];
1670 int y1 = y + xy[i][1];
1672 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1674 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1676 MovDir[x][y] = direction[0][i];
1679 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1680 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1682 MovDir[x][y] = direction[1][i];
1691 GfxDir[x][y] = MovDir[x][y];
1694 void InitAmoebaNr(int x, int y)
1697 int group_nr = AmoebeNachbarNr(x, y);
1701 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1703 if (AmoebaCnt[i] == 0)
1711 AmoebaNr[x][y] = group_nr;
1712 AmoebaCnt[group_nr]++;
1713 AmoebaCnt2[group_nr]++;
1719 boolean raise_level = FALSE;
1721 if (local_player->MovPos)
1725 if (tape.auto_play) /* tape might already be stopped here */
1726 tape.auto_play_level_solved = TRUE;
1728 if (tape.playing && tape.auto_play)
1729 tape.auto_play_level_solved = TRUE;
1732 local_player->LevelSolved = FALSE;
1734 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1738 if (!tape.playing && setup.sound_loops)
1739 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1740 SND_CTRL_PLAY_LOOP);
1742 while (TimeLeft > 0)
1744 if (!tape.playing && !setup.sound_loops)
1745 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1746 if (TimeLeft > 0 && !(TimeLeft % 10))
1747 RaiseScore(level.score[SC_TIME_BONUS]);
1748 if (TimeLeft > 100 && !(TimeLeft % 10))
1752 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1759 if (!tape.playing && setup.sound_loops)
1760 StopSound(SND_GAME_LEVELTIME_BONUS);
1762 else if (level.time == 0) /* level without time limit */
1764 if (!tape.playing && setup.sound_loops)
1765 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1766 SND_CTRL_PLAY_LOOP);
1768 while (TimePlayed < 999)
1770 if (!tape.playing && !setup.sound_loops)
1771 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1772 if (TimePlayed < 999 && !(TimePlayed % 10))
1773 RaiseScore(level.score[SC_TIME_BONUS]);
1774 if (TimePlayed < 900 && !(TimePlayed % 10))
1778 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1785 if (!tape.playing && setup.sound_loops)
1786 StopSound(SND_GAME_LEVELTIME_BONUS);
1789 /* close exit door after last player */
1790 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1791 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1793 int element = Feld[ExitX][ExitY];
1795 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1796 EL_SP_EXIT_CLOSING);
1798 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1801 /* Hero disappears */
1802 DrawLevelField(ExitX, ExitY);
1808 CloseDoor(DOOR_CLOSE_1);
1813 SaveTape(tape.level_nr); /* Ask to save tape */
1816 if (level_nr == leveldir_current->handicap_level)
1818 leveldir_current->handicap_level++;
1819 SaveLevelSetup_SeriesInfo();
1822 if (level_editor_test_game)
1823 local_player->score = -1; /* no highscore when playing from editor */
1824 else if (level_nr < leveldir_current->last_level)
1825 raise_level = TRUE; /* advance to next level */
1827 if ((hi_pos = NewHiScore()) >= 0)
1829 game_status = GAME_MODE_SCORES;
1830 DrawHallOfFame(hi_pos);
1839 game_status = GAME_MODE_MAIN;
1856 LoadScore(level_nr);
1858 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1859 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1862 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1864 if (local_player->score > highscore[k].Score)
1866 /* player has made it to the hall of fame */
1868 if (k < MAX_SCORE_ENTRIES - 1)
1870 int m = MAX_SCORE_ENTRIES - 1;
1873 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1874 if (!strcmp(setup.player_name, highscore[l].Name))
1876 if (m == k) /* player's new highscore overwrites his old one */
1880 for (l = m; l > k; l--)
1882 strcpy(highscore[l].Name, highscore[l - 1].Name);
1883 highscore[l].Score = highscore[l - 1].Score;
1890 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1891 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1892 highscore[k].Score = local_player->score;
1898 else if (!strncmp(setup.player_name, highscore[k].Name,
1899 MAX_PLAYER_NAME_LEN))
1900 break; /* player already there with a higher score */
1906 SaveScore(level_nr);
1911 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1913 if (player->GfxAction != action || player->GfxDir != dir)
1916 printf("Player frame reset! (%d => %d, %d => %d)\n",
1917 player->GfxAction, action, player->GfxDir, dir);
1920 player->GfxAction = action;
1921 player->GfxDir = dir;
1923 player->StepFrame = 0;
1927 static void ResetRandomAnimationValue(int x, int y)
1929 GfxRandom[x][y] = INIT_GFX_RANDOM();
1932 static void ResetGfxAnimation(int x, int y)
1935 GfxAction[x][y] = ACTION_DEFAULT;
1936 GfxDir[x][y] = MovDir[x][y];
1939 void InitMovingField(int x, int y, int direction)
1941 int element = Feld[x][y];
1942 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1943 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1947 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1948 ResetGfxAnimation(x, y);
1950 MovDir[newx][newy] = MovDir[x][y] = direction;
1951 GfxDir[x][y] = direction;
1953 if (Feld[newx][newy] == EL_EMPTY)
1954 Feld[newx][newy] = EL_BLOCKED;
1956 if (direction == MV_DOWN && CAN_FALL(element))
1957 GfxAction[x][y] = ACTION_FALLING;
1959 GfxAction[x][y] = ACTION_MOVING;
1961 GfxFrame[newx][newy] = GfxFrame[x][y];
1962 GfxRandom[newx][newy] = GfxRandom[x][y];
1963 GfxAction[newx][newy] = GfxAction[x][y];
1964 GfxDir[newx][newy] = GfxDir[x][y];
1967 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1969 int direction = MovDir[x][y];
1970 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1971 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1977 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1979 int oldx = x, oldy = y;
1980 int direction = MovDir[x][y];
1982 if (direction == MV_LEFT)
1984 else if (direction == MV_RIGHT)
1986 else if (direction == MV_UP)
1988 else if (direction == MV_DOWN)
1991 *comes_from_x = oldx;
1992 *comes_from_y = oldy;
1995 int MovingOrBlocked2Element(int x, int y)
1997 int element = Feld[x][y];
1999 if (element == EL_BLOCKED)
2003 Blocked2Moving(x, y, &oldx, &oldy);
2004 return Feld[oldx][oldy];
2010 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2012 /* like MovingOrBlocked2Element(), but if element is moving
2013 and (x,y) is the field the moving element is just leaving,
2014 return EL_BLOCKED instead of the element value */
2015 int element = Feld[x][y];
2017 if (IS_MOVING(x, y))
2019 if (element == EL_BLOCKED)
2023 Blocked2Moving(x, y, &oldx, &oldy);
2024 return Feld[oldx][oldy];
2033 static void RemoveField(int x, int y)
2035 Feld[x][y] = EL_EMPTY;
2042 ChangeDelay[x][y] = 0;
2043 ChangePage[x][y] = -1;
2044 Pushed[x][y] = FALSE;
2046 GfxElement[x][y] = EL_UNDEFINED;
2047 GfxAction[x][y] = ACTION_DEFAULT;
2048 GfxDir[x][y] = MV_NO_MOVING;
2051 void RemoveMovingField(int x, int y)
2053 int oldx = x, oldy = y, newx = x, newy = y;
2054 int element = Feld[x][y];
2055 int next_element = EL_UNDEFINED;
2057 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2060 if (IS_MOVING(x, y))
2062 Moving2Blocked(x, y, &newx, &newy);
2063 if (Feld[newx][newy] != EL_BLOCKED)
2066 else if (element == EL_BLOCKED)
2068 Blocked2Moving(x, y, &oldx, &oldy);
2069 if (!IS_MOVING(oldx, oldy))
2073 if (element == EL_BLOCKED &&
2074 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2075 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2076 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2077 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2078 next_element = get_next_element(Feld[oldx][oldy]);
2080 RemoveField(oldx, oldy);
2081 RemoveField(newx, newy);
2083 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2085 if (next_element != EL_UNDEFINED)
2086 Feld[oldx][oldy] = next_element;
2088 DrawLevelField(oldx, oldy);
2089 DrawLevelField(newx, newy);
2092 void DrawDynamite(int x, int y)
2094 int sx = SCREENX(x), sy = SCREENY(y);
2095 int graphic = el2img(Feld[x][y]);
2098 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2101 if (IS_WALKABLE_INSIDE(Back[x][y]))
2105 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2106 else if (Store[x][y])
2107 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2109 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2112 if (Back[x][y] || Store[x][y])
2113 DrawGraphicThruMask(sx, sy, graphic, frame);
2115 DrawGraphic(sx, sy, graphic, frame);
2117 if (game.emulation == EMU_SUPAPLEX)
2118 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2119 else if (Store[x][y])
2120 DrawGraphicThruMask(sx, sy, graphic, frame);
2122 DrawGraphic(sx, sy, graphic, frame);
2126 void CheckDynamite(int x, int y)
2128 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2132 if (MovDelay[x][y] != 0)
2135 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2142 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2144 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2145 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2146 StopSound(SND_DYNAMITE_ACTIVE);
2148 StopSound(SND_DYNABOMB_ACTIVE);
2154 void RelocatePlayer(int x, int y, int element)
2156 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2158 if (player->GameOver) /* do not reanimate dead player */
2162 RemoveField(x, y); /* temporarily remove newly placed player */
2163 DrawLevelField(x, y);
2166 if (player->present)
2168 while (player->MovPos)
2170 ScrollPlayer(player, SCROLL_GO_ON);
2171 ScrollScreen(NULL, SCROLL_GO_ON);
2177 Delay(GAME_FRAME_DELAY);
2180 DrawPlayer(player); /* needed here only to cleanup last field */
2181 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2183 player->is_moving = FALSE;
2186 Feld[x][y] = element;
2187 InitPlayerField(x, y, element, TRUE);
2189 if (player == local_player)
2191 int scroll_xx = -999, scroll_yy = -999;
2193 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2196 int fx = FX, fy = FY;
2198 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2199 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2200 local_player->jx - MIDPOSX);
2202 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2203 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2204 local_player->jy - MIDPOSY);
2206 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2207 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2212 fx += dx * TILEX / 2;
2213 fy += dy * TILEY / 2;
2215 ScrollLevel(dx, dy);
2218 /* scroll in two steps of half tile size to make things smoother */
2219 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2221 Delay(GAME_FRAME_DELAY);
2223 /* scroll second step to align at full tile size */
2225 Delay(GAME_FRAME_DELAY);
2230 void Explode(int ex, int ey, int phase, int mode)
2234 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2235 int last_phase = num_phase * delay;
2236 int half_phase = (num_phase / 2) * delay;
2237 int first_phase_after_start = EX_PHASE_START + 1;
2239 if (game.explosions_delayed)
2241 ExplodeField[ex][ey] = mode;
2245 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2247 int center_element = Feld[ex][ey];
2250 /* --- This is only really needed (and now handled) in "Impact()". --- */
2251 /* do not explode moving elements that left the explode field in time */
2252 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2253 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2257 if (mode == EX_NORMAL || mode == EX_CENTER)
2258 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2260 /* remove things displayed in background while burning dynamite */
2261 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2264 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2266 /* put moving element to center field (and let it explode there) */
2267 center_element = MovingOrBlocked2Element(ex, ey);
2268 RemoveMovingField(ex, ey);
2269 Feld[ex][ey] = center_element;
2272 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2274 int xx = x - ex + 1;
2275 int yy = y - ey + 1;
2278 if (!IN_LEV_FIELD(x, y) ||
2279 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2280 (x != ex || y != ey)))
2283 element = Feld[x][y];
2285 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2287 element = MovingOrBlocked2Element(x, y);
2289 if (!IS_EXPLOSION_PROOF(element))
2290 RemoveMovingField(x, y);
2296 if (IS_EXPLOSION_PROOF(element))
2299 /* indestructible elements can only explode in center (but not flames) */
2300 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2301 element == EL_FLAMES)
2306 if ((IS_INDESTRUCTIBLE(element) &&
2307 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2308 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2309 element == EL_FLAMES)
2313 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2315 if (IS_ACTIVE_BOMB(element))
2317 /* re-activate things under the bomb like gate or penguin */
2318 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2325 /* save walkable background elements while explosion on same tile */
2327 if (IS_INDESTRUCTIBLE(element))
2328 Back[x][y] = element;
2330 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2331 Back[x][y] = element;
2334 /* ignite explodable elements reached by other explosion */
2335 if (element == EL_EXPLOSION)
2336 element = Store2[x][y];
2339 if (AmoebaNr[x][y] &&
2340 (element == EL_AMOEBA_FULL ||
2341 element == EL_BD_AMOEBA ||
2342 element == EL_AMOEBA_GROWING))
2344 AmoebaCnt[AmoebaNr[x][y]]--;
2345 AmoebaCnt2[AmoebaNr[x][y]]--;
2351 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2353 switch(StorePlayer[ex][ey])
2356 Store[x][y] = EL_EMERALD_RED;
2359 Store[x][y] = EL_EMERALD;
2362 Store[x][y] = EL_EMERALD_PURPLE;
2366 Store[x][y] = EL_EMERALD_YELLOW;
2370 if (game.emulation == EMU_SUPAPLEX)
2371 Store[x][y] = EL_EMPTY;
2373 else if (center_element == EL_MOLE)
2374 Store[x][y] = EL_EMERALD_RED;
2375 else if (center_element == EL_PENGUIN)
2376 Store[x][y] = EL_EMERALD_PURPLE;
2377 else if (center_element == EL_BUG)
2378 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2379 else if (center_element == EL_BD_BUTTERFLY)
2380 Store[x][y] = EL_BD_DIAMOND;
2381 else if (center_element == EL_SP_ELECTRON)
2382 Store[x][y] = EL_SP_INFOTRON;
2383 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2384 Store[x][y] = level.amoeba_content;
2385 else if (center_element == EL_YAMYAM)
2386 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2387 else if (IS_CUSTOM_ELEMENT(center_element) &&
2388 element_info[center_element].content[xx][yy] != EL_EMPTY)
2389 Store[x][y] = element_info[center_element].content[xx][yy];
2390 else if (element == EL_WALL_EMERALD)
2391 Store[x][y] = EL_EMERALD;
2392 else if (element == EL_WALL_DIAMOND)
2393 Store[x][y] = EL_DIAMOND;
2394 else if (element == EL_WALL_BD_DIAMOND)
2395 Store[x][y] = EL_BD_DIAMOND;
2396 else if (element == EL_WALL_EMERALD_YELLOW)
2397 Store[x][y] = EL_EMERALD_YELLOW;
2398 else if (element == EL_WALL_EMERALD_RED)
2399 Store[x][y] = EL_EMERALD_RED;
2400 else if (element == EL_WALL_EMERALD_PURPLE)
2401 Store[x][y] = EL_EMERALD_PURPLE;
2402 else if (element == EL_WALL_PEARL)
2403 Store[x][y] = EL_PEARL;
2404 else if (element == EL_WALL_CRYSTAL)
2405 Store[x][y] = EL_CRYSTAL;
2406 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2407 Store[x][y] = element_info[element].content[1][1];
2409 Store[x][y] = EL_EMPTY;
2411 if (x != ex || y != ey ||
2412 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2413 Store2[x][y] = element;
2416 if (AmoebaNr[x][y] &&
2417 (element == EL_AMOEBA_FULL ||
2418 element == EL_BD_AMOEBA ||
2419 element == EL_AMOEBA_GROWING))
2421 AmoebaCnt[AmoebaNr[x][y]]--;
2422 AmoebaCnt2[AmoebaNr[x][y]]--;
2428 MovDir[x][y] = MovPos[x][y] = 0;
2429 GfxDir[x][y] = MovDir[x][y];
2434 Feld[x][y] = EL_EXPLOSION;
2436 GfxElement[x][y] = center_element;
2438 GfxElement[x][y] = EL_UNDEFINED;
2441 ExplodePhase[x][y] = 1;
2445 if (center_element == EL_YAMYAM)
2446 game.yamyam_content_nr =
2447 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2458 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2462 /* activate this even in non-DEBUG version until cause for crash in
2463 getGraphicAnimationFrame() (see below) is found and eliminated */
2467 if (GfxElement[x][y] == EL_UNDEFINED)
2470 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2471 printf("Explode(): This should never happen!\n");
2474 GfxElement[x][y] = EL_EMPTY;
2478 if (phase == first_phase_after_start)
2480 int element = Store2[x][y];
2482 if (element == EL_BLACK_ORB)
2484 Feld[x][y] = Store2[x][y];
2489 else if (phase == half_phase)
2491 int element = Store2[x][y];
2493 if (IS_PLAYER(x, y))
2494 KillHeroUnlessProtected(x, y);
2495 else if (CAN_EXPLODE_BY_FIRE(element))
2497 Feld[x][y] = Store2[x][y];
2501 else if (element == EL_AMOEBA_TO_DIAMOND)
2502 AmoebeUmwandeln(x, y);
2505 if (phase == last_phase)
2509 element = Feld[x][y] = Store[x][y];
2510 Store[x][y] = Store2[x][y] = 0;
2511 GfxElement[x][y] = EL_UNDEFINED;
2513 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2514 element = Feld[x][y] = Back[x][y];
2517 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2518 GfxDir[x][y] = MV_NO_MOVING;
2519 ChangeDelay[x][y] = 0;
2520 ChangePage[x][y] = -1;
2522 InitField(x, y, FALSE);
2523 if (CAN_MOVE(element))
2525 DrawLevelField(x, y);
2527 TestIfElementTouchesCustomElement(x, y);
2529 if (GFX_CRUMBLED(element))
2530 DrawLevelFieldCrumbledSandNeighbours(x, y);
2532 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2533 StorePlayer[x][y] = 0;
2535 if (ELEM_IS_PLAYER(element))
2536 RelocatePlayer(x, y, element);
2538 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2541 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2543 int stored = Store[x][y];
2544 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2545 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2548 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2551 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2552 element_info[GfxElement[x][y]].token_name,
2557 DrawLevelFieldCrumbledSand(x, y);
2559 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2561 DrawLevelElement(x, y, Back[x][y]);
2562 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2564 else if (IS_WALKABLE_UNDER(Back[x][y]))
2566 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2567 DrawLevelElementThruMask(x, y, Back[x][y]);
2569 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2570 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2574 void DynaExplode(int ex, int ey)
2577 int dynabomb_element = Feld[ex][ey];
2578 int dynabomb_size = 1;
2579 boolean dynabomb_xl = FALSE;
2580 struct PlayerInfo *player;
2581 static int xy[4][2] =
2589 if (IS_ACTIVE_BOMB(dynabomb_element))
2591 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2592 dynabomb_size = player->dynabomb_size;
2593 dynabomb_xl = player->dynabomb_xl;
2594 player->dynabombs_left++;
2597 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2599 for (i = 0; i < 4; i++)
2601 for (j = 1; j <= dynabomb_size; j++)
2603 int x = ex + j * xy[i % 4][0];
2604 int y = ey + j * xy[i % 4][1];
2607 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2610 element = Feld[x][y];
2612 /* do not restart explosions of fields with active bombs */
2613 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2616 Explode(x, y, EX_PHASE_START, EX_BORDER);
2618 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2619 if (element != EL_EMPTY &&
2620 element != EL_SAND &&
2621 element != EL_EXPLOSION &&
2628 void Bang(int x, int y)
2631 int element = MovingOrBlocked2Element(x, y);
2633 int element = Feld[x][y];
2637 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2639 if (IS_PLAYER(x, y))
2642 struct PlayerInfo *player = PLAYERINFO(x, y);
2644 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2645 player->element_nr);
2650 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2652 if (game.emulation == EMU_SUPAPLEX)
2653 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2655 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2660 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2668 case EL_BD_BUTTERFLY:
2671 case EL_DARK_YAMYAM:
2675 RaiseScoreElement(element);
2676 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2678 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2679 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2680 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2681 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2682 case EL_DYNABOMB_INCREASE_NUMBER:
2683 case EL_DYNABOMB_INCREASE_SIZE:
2684 case EL_DYNABOMB_INCREASE_POWER:
2689 case EL_LAMP_ACTIVE:
2690 if (IS_PLAYER(x, y))
2691 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2693 Explode(x, y, EX_PHASE_START, EX_CENTER);
2696 if (CAN_EXPLODE_DYNA(element))
2698 else if (CAN_EXPLODE_1X1(element))
2699 Explode(x, y, EX_PHASE_START, EX_CENTER);
2701 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2705 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2708 void SplashAcid(int x, int y)
2710 int element = Feld[x][y];
2712 if (element != EL_ACID_SPLASH_LEFT &&
2713 element != EL_ACID_SPLASH_RIGHT)
2715 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2717 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2718 (!IN_LEV_FIELD(x-1, y-1) ||
2719 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2720 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2722 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2723 (!IN_LEV_FIELD(x+1, y-1) ||
2724 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2725 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2729 static void InitBeltMovement()
2731 static int belt_base_element[4] =
2733 EL_CONVEYOR_BELT_1_LEFT,
2734 EL_CONVEYOR_BELT_2_LEFT,
2735 EL_CONVEYOR_BELT_3_LEFT,
2736 EL_CONVEYOR_BELT_4_LEFT
2738 static int belt_base_active_element[4] =
2740 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2741 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2742 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2743 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2748 /* set frame order for belt animation graphic according to belt direction */
2749 for (i = 0; i < 4; i++)
2753 for (j = 0; j < 3; j++)
2755 int element = belt_base_active_element[belt_nr] + j;
2756 int graphic = el2img(element);
2758 if (game.belt_dir[i] == MV_LEFT)
2759 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2761 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2765 for (y = 0; y < lev_fieldy; y++)
2767 for (x = 0; x < lev_fieldx; x++)
2769 int element = Feld[x][y];
2771 for (i = 0; i < 4; i++)
2773 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2775 int e_belt_nr = getBeltNrFromBeltElement(element);
2778 if (e_belt_nr == belt_nr)
2780 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2782 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2790 static void ToggleBeltSwitch(int x, int y)
2792 static int belt_base_element[4] =
2794 EL_CONVEYOR_BELT_1_LEFT,
2795 EL_CONVEYOR_BELT_2_LEFT,
2796 EL_CONVEYOR_BELT_3_LEFT,
2797 EL_CONVEYOR_BELT_4_LEFT
2799 static int belt_base_active_element[4] =
2801 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2802 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2803 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2804 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2806 static int belt_base_switch_element[4] =
2808 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2809 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2810 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2811 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2813 static int belt_move_dir[4] =
2821 int element = Feld[x][y];
2822 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2823 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2824 int belt_dir = belt_move_dir[belt_dir_nr];
2827 if (!IS_BELT_SWITCH(element))
2830 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2831 game.belt_dir[belt_nr] = belt_dir;
2833 if (belt_dir_nr == 3)
2836 /* set frame order for belt animation graphic according to belt direction */
2837 for (i = 0; i < 3; i++)
2839 int element = belt_base_active_element[belt_nr] + i;
2840 int graphic = el2img(element);
2842 if (belt_dir == MV_LEFT)
2843 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2845 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2848 for (yy = 0; yy < lev_fieldy; yy++)
2850 for (xx = 0; xx < lev_fieldx; xx++)
2852 int element = Feld[xx][yy];
2854 if (IS_BELT_SWITCH(element))
2856 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2858 if (e_belt_nr == belt_nr)
2860 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2861 DrawLevelField(xx, yy);
2864 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2866 int e_belt_nr = getBeltNrFromBeltElement(element);
2868 if (e_belt_nr == belt_nr)
2870 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2872 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2873 DrawLevelField(xx, yy);
2876 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2878 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2880 if (e_belt_nr == belt_nr)
2882 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2884 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2885 DrawLevelField(xx, yy);
2892 static void ToggleSwitchgateSwitch(int x, int y)
2896 game.switchgate_pos = !game.switchgate_pos;
2898 for (yy = 0; yy < lev_fieldy; yy++)
2900 for (xx = 0; xx < lev_fieldx; xx++)
2902 int element = Feld[xx][yy];
2904 if (element == EL_SWITCHGATE_SWITCH_UP ||
2905 element == EL_SWITCHGATE_SWITCH_DOWN)
2907 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2908 DrawLevelField(xx, yy);
2910 else if (element == EL_SWITCHGATE_OPEN ||
2911 element == EL_SWITCHGATE_OPENING)
2913 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2915 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2917 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2920 else if (element == EL_SWITCHGATE_CLOSED ||
2921 element == EL_SWITCHGATE_CLOSING)
2923 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2925 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2927 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2934 static int getInvisibleActiveFromInvisibleElement(int element)
2936 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2937 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2938 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2942 static int getInvisibleFromInvisibleActiveElement(int element)
2944 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2945 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2946 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2950 static void RedrawAllLightSwitchesAndInvisibleElements()
2954 for (y = 0; y < lev_fieldy; y++)
2956 for (x = 0; x < lev_fieldx; x++)
2958 int element = Feld[x][y];
2960 if (element == EL_LIGHT_SWITCH &&
2961 game.light_time_left > 0)
2963 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2964 DrawLevelField(x, y);
2966 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2967 game.light_time_left == 0)
2969 Feld[x][y] = EL_LIGHT_SWITCH;
2970 DrawLevelField(x, y);
2972 else if (element == EL_INVISIBLE_STEELWALL ||
2973 element == EL_INVISIBLE_WALL ||
2974 element == EL_INVISIBLE_SAND)
2976 if (game.light_time_left > 0)
2977 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2979 DrawLevelField(x, y);
2981 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2982 element == EL_INVISIBLE_WALL_ACTIVE ||
2983 element == EL_INVISIBLE_SAND_ACTIVE)
2985 if (game.light_time_left == 0)
2986 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2988 DrawLevelField(x, y);
2994 static void ToggleLightSwitch(int x, int y)
2996 int element = Feld[x][y];
2998 game.light_time_left =
2999 (element == EL_LIGHT_SWITCH ?
3000 level.time_light * FRAMES_PER_SECOND : 0);
3002 RedrawAllLightSwitchesAndInvisibleElements();
3005 static void ActivateTimegateSwitch(int x, int y)
3009 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3011 for (yy = 0; yy < lev_fieldy; yy++)
3013 for (xx = 0; xx < lev_fieldx; xx++)
3015 int element = Feld[xx][yy];
3017 if (element == EL_TIMEGATE_CLOSED ||
3018 element == EL_TIMEGATE_CLOSING)
3020 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3021 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3025 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3027 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3028 DrawLevelField(xx, yy);
3035 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3038 inline static int getElementMoveStepsize(int x, int y)
3040 int element = Feld[x][y];
3041 int direction = MovDir[x][y];
3042 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3043 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3044 int horiz_move = (dx != 0);
3045 int sign = (horiz_move ? dx : dy);
3046 int step = sign * element_info[element].move_stepsize;
3048 /* special values for move stepsize for spring and things on conveyor belt */
3051 if (CAN_FALL(element) &&
3052 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3053 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3054 else if (element == EL_SPRING)
3055 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3061 void Impact(int x, int y)
3063 boolean lastline = (y == lev_fieldy-1);
3064 boolean object_hit = FALSE;
3065 boolean impact = (lastline || object_hit);
3066 int element = Feld[x][y];
3067 int smashed = EL_UNDEFINED;
3069 if (!lastline) /* check if element below was hit */
3071 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3074 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3075 MovDir[x][y + 1] != MV_DOWN ||
3076 MovPos[x][y + 1] <= TILEY / 2));
3079 object_hit = !IS_FREE(x, y + 1);
3082 /* do not smash moving elements that left the smashed field in time */
3083 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3084 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3088 smashed = MovingOrBlocked2Element(x, y + 1);
3090 impact = (lastline || object_hit);
3093 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3099 /* only reset graphic animation if graphic really changes after impact */
3101 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3103 ResetGfxAnimation(x, y);
3104 DrawLevelField(x, y);
3107 if (impact && CAN_EXPLODE_IMPACT(element))
3112 else if (impact && element == EL_PEARL)
3114 Feld[x][y] = EL_PEARL_BREAKING;
3115 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3118 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3120 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3125 if (impact && element == EL_AMOEBA_DROP)
3127 if (object_hit && IS_PLAYER(x, y + 1))
3128 KillHeroUnlessProtected(x, y + 1);
3129 else if (object_hit && smashed == EL_PENGUIN)
3133 Feld[x][y] = EL_AMOEBA_GROWING;
3134 Store[x][y] = EL_AMOEBA_WET;
3136 ResetRandomAnimationValue(x, y);
3141 if (object_hit) /* check which object was hit */
3143 if (CAN_PASS_MAGIC_WALL(element) &&
3144 (smashed == EL_MAGIC_WALL ||
3145 smashed == EL_BD_MAGIC_WALL))
3148 int activated_magic_wall =
3149 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3150 EL_BD_MAGIC_WALL_ACTIVE);
3152 /* activate magic wall / mill */
3153 for (yy = 0; yy < lev_fieldy; yy++)
3154 for (xx = 0; xx < lev_fieldx; xx++)
3155 if (Feld[xx][yy] == smashed)
3156 Feld[xx][yy] = activated_magic_wall;
3158 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3159 game.magic_wall_active = TRUE;
3161 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3162 SND_MAGIC_WALL_ACTIVATING :
3163 SND_BD_MAGIC_WALL_ACTIVATING));
3166 if (IS_PLAYER(x, y + 1))
3168 if (CAN_SMASH_PLAYER(element))
3170 KillHeroUnlessProtected(x, y + 1);
3174 else if (smashed == EL_PENGUIN)
3176 if (CAN_SMASH_PLAYER(element))
3182 else if (element == EL_BD_DIAMOND)
3184 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3190 else if ((element == EL_SP_INFOTRON ||
3191 element == EL_SP_ZONK) &&
3192 (smashed == EL_SP_SNIKSNAK ||
3193 smashed == EL_SP_ELECTRON ||
3194 smashed == EL_SP_DISK_ORANGE))
3200 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3206 else if (CAN_SMASH_EVERYTHING(element))
3208 if (IS_CLASSIC_ENEMY(smashed) ||
3209 CAN_EXPLODE_SMASHED(smashed))
3214 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3216 if (smashed == EL_LAMP ||
3217 smashed == EL_LAMP_ACTIVE)
3222 else if (smashed == EL_NUT)
3224 Feld[x][y + 1] = EL_NUT_BREAKING;
3225 PlayLevelSound(x, y, SND_NUT_BREAKING);
3226 RaiseScoreElement(EL_NUT);
3229 else if (smashed == EL_PEARL)
3231 Feld[x][y + 1] = EL_PEARL_BREAKING;
3232 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3235 else if (smashed == EL_DIAMOND)
3237 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3238 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3241 else if (IS_BELT_SWITCH(smashed))
3243 ToggleBeltSwitch(x, y + 1);
3245 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3246 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3248 ToggleSwitchgateSwitch(x, y + 1);
3250 else if (smashed == EL_LIGHT_SWITCH ||
3251 smashed == EL_LIGHT_SWITCH_ACTIVE)
3253 ToggleLightSwitch(x, y + 1);
3257 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3259 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3260 CE_OTHER_IS_SWITCHING);
3261 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3267 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3272 /* play sound of magic wall / mill */
3274 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3275 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3277 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3278 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3279 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3280 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3285 /* play sound of object that hits the ground */
3286 if (lastline || object_hit)
3287 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3290 inline static void TurnRoundExt(int x, int y)
3302 { 0, 0 }, { 0, 0 }, { 0, 0 },
3307 int left, right, back;
3311 { MV_DOWN, MV_UP, MV_RIGHT },
3312 { MV_UP, MV_DOWN, MV_LEFT },
3314 { MV_LEFT, MV_RIGHT, MV_DOWN },
3318 { MV_RIGHT, MV_LEFT, MV_UP }
3321 int element = Feld[x][y];
3322 int move_pattern = element_info[element].move_pattern;
3324 int old_move_dir = MovDir[x][y];
3325 int left_dir = turn[old_move_dir].left;
3326 int right_dir = turn[old_move_dir].right;
3327 int back_dir = turn[old_move_dir].back;
3329 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3330 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3331 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3332 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3334 int left_x = x + left_dx, left_y = y + left_dy;
3335 int right_x = x + right_dx, right_y = y + right_dy;
3336 int move_x = x + move_dx, move_y = y + move_dy;
3340 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3342 TestIfBadThingTouchesOtherBadThing(x, y);
3344 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3345 MovDir[x][y] = right_dir;
3346 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3347 MovDir[x][y] = left_dir;
3349 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3351 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3354 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3355 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3357 TestIfBadThingTouchesOtherBadThing(x, y);
3359 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3360 MovDir[x][y] = left_dir;
3361 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3362 MovDir[x][y] = right_dir;
3364 if ((element == EL_SPACESHIP ||
3365 element == EL_SP_SNIKSNAK ||
3366 element == EL_SP_ELECTRON)
3367 && MovDir[x][y] != old_move_dir)
3369 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3372 else if (element == EL_YAMYAM)
3374 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3375 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3377 if (can_turn_left && can_turn_right)
3378 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3379 else if (can_turn_left)
3380 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3381 else if (can_turn_right)
3382 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3384 MovDir[x][y] = back_dir;
3386 MovDelay[x][y] = 16 + 16 * RND(3);
3388 else if (element == EL_DARK_YAMYAM)
3390 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3391 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3393 if (can_turn_left && can_turn_right)
3394 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3395 else if (can_turn_left)
3396 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3397 else if (can_turn_right)
3398 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3400 MovDir[x][y] = back_dir;
3402 MovDelay[x][y] = 16 + 16 * RND(3);
3404 else if (element == EL_PACMAN)
3406 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3407 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3409 if (can_turn_left && can_turn_right)
3410 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3411 else if (can_turn_left)
3412 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3413 else if (can_turn_right)
3414 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3416 MovDir[x][y] = back_dir;
3418 MovDelay[x][y] = 6 + RND(40);
3420 else if (element == EL_PIG)
3422 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3423 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3424 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3425 boolean should_turn_left, should_turn_right, should_move_on;
3427 int rnd = RND(rnd_value);
3429 should_turn_left = (can_turn_left &&
3431 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3432 y + back_dy + left_dy)));
3433 should_turn_right = (can_turn_right &&
3435 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3436 y + back_dy + right_dy)));
3437 should_move_on = (can_move_on &&
3440 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3441 y + move_dy + left_dy) ||
3442 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3443 y + move_dy + right_dy)));
3445 if (should_turn_left || should_turn_right || should_move_on)
3447 if (should_turn_left && should_turn_right && should_move_on)
3448 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3449 rnd < 2 * rnd_value / 3 ? right_dir :
3451 else if (should_turn_left && should_turn_right)
3452 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3453 else if (should_turn_left && should_move_on)
3454 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3455 else if (should_turn_right && should_move_on)
3456 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3457 else if (should_turn_left)
3458 MovDir[x][y] = left_dir;
3459 else if (should_turn_right)
3460 MovDir[x][y] = right_dir;
3461 else if (should_move_on)
3462 MovDir[x][y] = old_move_dir;
3464 else if (can_move_on && rnd > rnd_value / 8)
3465 MovDir[x][y] = old_move_dir;
3466 else if (can_turn_left && can_turn_right)
3467 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3468 else if (can_turn_left && rnd > rnd_value / 8)
3469 MovDir[x][y] = left_dir;
3470 else if (can_turn_right && rnd > rnd_value/8)
3471 MovDir[x][y] = right_dir;
3473 MovDir[x][y] = back_dir;
3475 xx = x + move_xy[MovDir[x][y]].x;
3476 yy = y + move_xy[MovDir[x][y]].y;
3478 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3479 MovDir[x][y] = old_move_dir;
3483 else if (element == EL_DRAGON)
3485 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3486 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3487 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3489 int rnd = RND(rnd_value);
3492 if (FrameCounter < 1 && x == 0 && y == 29)
3493 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3496 if (can_move_on && rnd > rnd_value / 8)
3497 MovDir[x][y] = old_move_dir;
3498 else if (can_turn_left && can_turn_right)
3499 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3500 else if (can_turn_left && rnd > rnd_value / 8)
3501 MovDir[x][y] = left_dir;
3502 else if (can_turn_right && rnd > rnd_value / 8)
3503 MovDir[x][y] = right_dir;
3505 MovDir[x][y] = back_dir;
3507 xx = x + move_xy[MovDir[x][y]].x;
3508 yy = y + move_xy[MovDir[x][y]].y;
3511 if (FrameCounter < 1 && x == 0 && y == 29)
3512 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3513 xx, yy, Feld[xx][yy],
3518 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3519 MovDir[x][y] = old_move_dir;
3521 if (!IS_FREE(xx, yy))
3522 MovDir[x][y] = old_move_dir;
3526 if (FrameCounter < 1 && x == 0 && y == 29)
3527 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3532 else if (element == EL_MOLE)
3534 boolean can_move_on =
3535 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3536 IS_AMOEBOID(Feld[move_x][move_y]) ||
3537 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3540 boolean can_turn_left =
3541 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3542 IS_AMOEBOID(Feld[left_x][left_y])));
3544 boolean can_turn_right =
3545 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3546 IS_AMOEBOID(Feld[right_x][right_y])));
3548 if (can_turn_left && can_turn_right)
3549 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3550 else if (can_turn_left)
3551 MovDir[x][y] = left_dir;
3553 MovDir[x][y] = right_dir;
3556 if (MovDir[x][y] != old_move_dir)
3559 else if (element == EL_BALLOON)
3561 MovDir[x][y] = game.balloon_dir;
3564 else if (element == EL_SPRING)
3566 if (MovDir[x][y] & MV_HORIZONTAL &&
3567 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3568 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3569 MovDir[x][y] = MV_NO_MOVING;
3573 else if (element == EL_ROBOT ||
3574 element == EL_SATELLITE ||
3575 element == EL_PENGUIN)
3577 int attr_x = -1, attr_y = -1;
3588 for (i = 0; i < MAX_PLAYERS; i++)
3590 struct PlayerInfo *player = &stored_player[i];
3591 int jx = player->jx, jy = player->jy;
3593 if (!player->active)
3597 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3605 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3611 if (element == EL_PENGUIN)
3614 static int xy[4][2] =
3622 for (i = 0; i < 4; i++)
3624 int ex = x + xy[i % 4][0];
3625 int ey = y + xy[i % 4][1];
3627 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3636 MovDir[x][y] = MV_NO_MOVING;
3638 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3639 else if (attr_x > x)
3640 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3642 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3643 else if (attr_y > y)
3644 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3646 if (element == EL_ROBOT)
3650 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3651 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3652 Moving2Blocked(x, y, &newx, &newy);
3654 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3655 MovDelay[x][y] = 8 + 8 * !RND(3);
3657 MovDelay[x][y] = 16;
3659 else if (element == EL_PENGUIN)
3665 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3667 boolean first_horiz = RND(2);
3668 int new_move_dir = MovDir[x][y];
3671 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3672 Moving2Blocked(x, y, &newx, &newy);
3674 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3678 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3679 Moving2Blocked(x, y, &newx, &newy);
3681 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3684 MovDir[x][y] = old_move_dir;
3688 else /* (element == EL_SATELLITE) */
3694 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3696 boolean first_horiz = RND(2);
3697 int new_move_dir = MovDir[x][y];
3700 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3701 Moving2Blocked(x, y, &newx, &newy);
3703 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3707 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3708 Moving2Blocked(x, y, &newx, &newy);
3710 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3713 MovDir[x][y] = old_move_dir;
3718 else if (move_pattern == MV_TURNING_LEFT ||
3719 move_pattern == MV_TURNING_RIGHT ||
3720 move_pattern == MV_TURNING_LEFT_RIGHT ||
3721 move_pattern == MV_TURNING_RIGHT_LEFT ||
3722 move_pattern == MV_TURNING_RANDOM ||
3723 move_pattern == MV_ALL_DIRECTIONS)
3725 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3726 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3728 if (move_pattern == MV_TURNING_LEFT)
3729 MovDir[x][y] = left_dir;
3730 else if (move_pattern == MV_TURNING_RIGHT)
3731 MovDir[x][y] = right_dir;
3732 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3733 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3734 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3735 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3736 else if (move_pattern == MV_TURNING_RANDOM)
3737 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3738 can_turn_right && !can_turn_left ? right_dir :
3739 RND(2) ? left_dir : right_dir);
3740 else if (can_turn_left && can_turn_right)
3741 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3742 else if (can_turn_left)
3743 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3744 else if (can_turn_right)
3745 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3747 MovDir[x][y] = back_dir;
3749 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3751 else if (move_pattern == MV_HORIZONTAL ||
3752 move_pattern == MV_VERTICAL)
3754 if (move_pattern & old_move_dir)
3755 MovDir[x][y] = back_dir;
3756 else if (move_pattern == MV_HORIZONTAL)
3757 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3758 else if (move_pattern == MV_VERTICAL)
3759 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3761 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3763 else if (move_pattern & MV_ANY_DIRECTION)
3765 MovDir[x][y] = move_pattern;
3766 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3768 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3770 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3771 MovDir[x][y] = left_dir;
3772 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3773 MovDir[x][y] = right_dir;
3775 if (MovDir[x][y] != old_move_dir)
3776 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3778 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3780 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3781 MovDir[x][y] = right_dir;
3782 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3783 MovDir[x][y] = left_dir;
3785 if (MovDir[x][y] != old_move_dir)
3786 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3788 else if (move_pattern == MV_TOWARDS_PLAYER ||
3789 move_pattern == MV_AWAY_FROM_PLAYER)
3791 int attr_x = -1, attr_y = -1;
3793 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3804 for (i = 0; i < MAX_PLAYERS; i++)
3806 struct PlayerInfo *player = &stored_player[i];
3807 int jx = player->jx, jy = player->jy;
3809 if (!player->active)
3813 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3821 MovDir[x][y] = MV_NO_MOVING;
3823 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3824 else if (attr_x > x)
3825 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3827 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3828 else if (attr_y > y)
3829 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3831 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3833 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3835 boolean first_horiz = RND(2);
3836 int new_move_dir = MovDir[x][y];
3839 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3840 Moving2Blocked(x, y, &newx, &newy);
3842 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3846 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3847 Moving2Blocked(x, y, &newx, &newy);
3849 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3852 MovDir[x][y] = old_move_dir;
3855 else if (move_pattern == MV_WHEN_PUSHED ||
3856 move_pattern == MV_WHEN_DROPPED)
3858 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3859 MovDir[x][y] = MV_NO_MOVING;
3863 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3864 element == EL_MAZE_RUNNER)
3866 static int test_xy[7][2] =
3876 static int test_dir[7] =
3886 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3887 int move_preference = -1000000; /* start with very low preference */
3888 int new_move_dir = MV_NO_MOVING;
3889 int start_test = RND(4);
3892 for (i = 0; i < 4; i++)
3894 int move_dir = test_dir[start_test + i];
3895 int move_dir_preference;
3897 xx = x + test_xy[start_test + i][0];
3898 yy = y + test_xy[start_test + i][1];
3900 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3901 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3903 new_move_dir = move_dir;
3908 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3911 move_dir_preference = -1 * RunnerVisit[xx][yy];
3912 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3913 move_dir_preference = PlayerVisit[xx][yy];
3915 if (move_dir_preference > move_preference)
3917 /* prefer field that has not been visited for the longest time */
3918 move_preference = move_dir_preference;
3919 new_move_dir = move_dir;
3921 else if (move_dir_preference == move_preference &&
3922 move_dir == old_move_dir)
3924 /* prefer last direction when all directions are preferred equally */
3925 move_preference = move_dir_preference;
3926 new_move_dir = move_dir;
3930 MovDir[x][y] = new_move_dir;
3931 if (old_move_dir != new_move_dir)
3936 static void TurnRound(int x, int y)
3938 int direction = MovDir[x][y];
3941 GfxDir[x][y] = MovDir[x][y];
3947 GfxDir[x][y] = MovDir[x][y];
3950 if (direction != MovDir[x][y])
3955 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3958 GfxAction[x][y] = ACTION_WAITING;
3962 static boolean JustBeingPushed(int x, int y)
3966 for (i = 0; i < MAX_PLAYERS; i++)
3968 struct PlayerInfo *player = &stored_player[i];
3970 if (player->active && player->is_pushing && player->MovPos)
3972 int next_jx = player->jx + (player->jx - player->last_jx);
3973 int next_jy = player->jy + (player->jy - player->last_jy);
3975 if (x == next_jx && y == next_jy)
3983 void StartMoving(int x, int y)
3985 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3986 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3987 int element = Feld[x][y];
3993 if (MovDelay[x][y] == 0)
3994 GfxAction[x][y] = ACTION_DEFAULT;
3996 /* !!! this should be handled more generic (not only for mole) !!! */
3997 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3998 GfxAction[x][y] = ACTION_DEFAULT;
4001 if (CAN_FALL(element) && y < lev_fieldy - 1)
4003 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4004 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4005 if (JustBeingPushed(x, y))
4008 if (element == EL_QUICKSAND_FULL)
4010 if (IS_FREE(x, y + 1))
4012 InitMovingField(x, y, MV_DOWN);
4013 started_moving = TRUE;
4015 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4016 Store[x][y] = EL_ROCK;
4018 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4020 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4023 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4025 if (!MovDelay[x][y])
4026 MovDelay[x][y] = TILEY + 1;
4035 Feld[x][y] = EL_QUICKSAND_EMPTY;
4036 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4037 Store[x][y + 1] = Store[x][y];
4040 PlayLevelSoundAction(x, y, ACTION_FILLING);
4042 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4046 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4047 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4049 InitMovingField(x, y, MV_DOWN);
4050 started_moving = TRUE;
4052 Feld[x][y] = EL_QUICKSAND_FILLING;
4053 Store[x][y] = element;
4055 PlayLevelSoundAction(x, y, ACTION_FILLING);
4057 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4060 else if (element == EL_MAGIC_WALL_FULL)
4062 if (IS_FREE(x, y + 1))
4064 InitMovingField(x, y, MV_DOWN);
4065 started_moving = TRUE;
4067 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4068 Store[x][y] = EL_CHANGED(Store[x][y]);
4070 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4072 if (!MovDelay[x][y])
4073 MovDelay[x][y] = TILEY/4 + 1;
4082 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4083 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4084 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4088 else if (element == EL_BD_MAGIC_WALL_FULL)
4090 if (IS_FREE(x, y + 1))
4092 InitMovingField(x, y, MV_DOWN);
4093 started_moving = TRUE;
4095 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4096 Store[x][y] = EL_CHANGED2(Store[x][y]);
4098 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4100 if (!MovDelay[x][y])
4101 MovDelay[x][y] = TILEY/4 + 1;
4110 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4111 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4112 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4116 else if (CAN_PASS_MAGIC_WALL(element) &&
4117 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4118 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4120 InitMovingField(x, y, MV_DOWN);
4121 started_moving = TRUE;
4124 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4125 EL_BD_MAGIC_WALL_FILLING);
4126 Store[x][y] = element;
4129 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4131 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4136 InitMovingField(x, y, MV_DOWN);
4137 started_moving = TRUE;
4139 Store[x][y] = EL_ACID;
4141 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4142 GfxAction[x][y + 1] = ACTION_ACTIVE;
4146 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4147 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4148 (Feld[x][y + 1] == EL_BLOCKED)) ||
4149 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4150 CAN_SMASH(element) && WasJustFalling[x][y] &&
4151 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4155 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4156 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4157 WasJustMoving[x][y] && !Pushed[x][y + 1])
4159 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4160 WasJustMoving[x][y])
4165 /* this is needed for a special case not covered by calling "Impact()"
4166 from "ContinueMoving()": if an element moves to a tile directly below
4167 another element which was just falling on that tile (which was empty
4168 in the previous frame), the falling element above would just stop
4169 instead of smashing the element below (in previous version, the above
4170 element was just checked for "moving" instead of "falling", resulting
4171 in incorrect smashes caused by horizontal movement of the above
4172 element; also, the case of the player being the element to smash was
4173 simply not covered here... :-/ ) */
4176 WasJustMoving[x][y] = 0;
4177 WasJustFalling[x][y] = 0;
4182 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4184 if (MovDir[x][y] == MV_NO_MOVING)
4186 InitMovingField(x, y, MV_DOWN);
4187 started_moving = TRUE;
4190 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4192 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4193 MovDir[x][y] = MV_DOWN;
4195 InitMovingField(x, y, MV_DOWN);
4196 started_moving = TRUE;
4198 else if (element == EL_AMOEBA_DROP)
4200 Feld[x][y] = EL_AMOEBA_GROWING;
4201 Store[x][y] = EL_AMOEBA_WET;
4203 /* Store[x][y + 1] must be zero, because:
4204 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4207 #if OLD_GAME_BEHAVIOUR
4208 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4210 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4211 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4212 element != EL_DX_SUPABOMB)
4215 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4216 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4217 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4218 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4221 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4222 (IS_FREE(x - 1, y + 1) ||
4223 Feld[x - 1][y + 1] == EL_ACID));
4224 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4225 (IS_FREE(x + 1, y + 1) ||
4226 Feld[x + 1][y + 1] == EL_ACID));
4227 boolean can_fall_any = (can_fall_left || can_fall_right);
4228 boolean can_fall_both = (can_fall_left && can_fall_right);
4230 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4232 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4234 if (slippery_type == SLIPPERY_ONLY_LEFT)
4235 can_fall_right = FALSE;
4236 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4237 can_fall_left = FALSE;
4238 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4239 can_fall_right = FALSE;
4240 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4241 can_fall_left = FALSE;
4243 can_fall_any = (can_fall_left || can_fall_right);
4244 can_fall_both = (can_fall_left && can_fall_right);
4249 if (can_fall_both &&
4250 (game.emulation != EMU_BOULDERDASH &&
4251 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4252 can_fall_left = !(can_fall_right = RND(2));
4254 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4255 started_moving = TRUE;
4258 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4260 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4261 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4262 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4263 int belt_dir = game.belt_dir[belt_nr];
4265 if ((belt_dir == MV_LEFT && left_is_free) ||
4266 (belt_dir == MV_RIGHT && right_is_free))
4268 InitMovingField(x, y, belt_dir);
4269 started_moving = TRUE;
4271 GfxAction[x][y] = ACTION_DEFAULT;
4276 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4277 if (CAN_MOVE(element) && !started_moving)
4279 int move_pattern = element_info[element].move_pattern;
4282 Moving2Blocked(x, y, &newx, &newy);
4285 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4288 if ((element == EL_SATELLITE ||
4289 element == EL_BALLOON ||
4290 element == EL_SPRING)
4291 && JustBeingPushed(x, y))
4296 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4297 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4298 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4301 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4302 element, element_info[element].token_name,
4303 WasJustMoving[x][y],
4304 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4305 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4306 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4307 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4311 WasJustMoving[x][y] = 0;
4314 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4317 if (Feld[x][y] != element) /* element has changed */
4319 element = Feld[x][y];
4320 move_pattern = element_info[element].move_pattern;
4322 if (!CAN_MOVE(element))
4326 if (Feld[x][y] != element) /* element has changed */
4334 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4335 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4337 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4339 Moving2Blocked(x, y, &newx, &newy);
4340 if (Feld[newx][newy] == EL_BLOCKED)
4341 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4347 if (FrameCounter < 1 && x == 0 && y == 29)
4348 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4351 if (!MovDelay[x][y]) /* start new movement phase */
4353 /* all objects that can change their move direction after each step
4354 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4356 if (element != EL_YAMYAM &&
4357 element != EL_DARK_YAMYAM &&
4358 element != EL_PACMAN &&
4359 !(move_pattern & MV_ANY_DIRECTION) &&
4360 move_pattern != MV_TURNING_LEFT &&
4361 move_pattern != MV_TURNING_RIGHT &&
4362 move_pattern != MV_TURNING_LEFT_RIGHT &&
4363 move_pattern != MV_TURNING_RIGHT_LEFT &&
4364 move_pattern != MV_TURNING_RANDOM)
4369 if (FrameCounter < 1 && x == 0 && y == 29)
4370 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4373 if (MovDelay[x][y] && (element == EL_BUG ||
4374 element == EL_SPACESHIP ||
4375 element == EL_SP_SNIKSNAK ||
4376 element == EL_SP_ELECTRON ||
4377 element == EL_MOLE))
4378 DrawLevelField(x, y);
4382 if (MovDelay[x][y]) /* wait some time before next movement */
4387 if (element == EL_YAMYAM)
4390 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4391 DrawLevelElementAnimation(x, y, element);
4395 if (MovDelay[x][y]) /* element still has to wait some time */
4398 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4399 ResetGfxAnimation(x, y);
4403 if (GfxAction[x][y] != ACTION_WAITING)
4404 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4406 GfxAction[x][y] = ACTION_WAITING;
4410 if (element == EL_ROBOT ||
4412 element == EL_PACMAN ||
4414 element == EL_YAMYAM ||
4415 element == EL_DARK_YAMYAM)
4418 DrawLevelElementAnimation(x, y, element);
4420 DrawLevelElementAnimationIfNeeded(x, y, element);
4422 PlayLevelSoundAction(x, y, ACTION_WAITING);
4424 else if (element == EL_SP_ELECTRON)
4425 DrawLevelElementAnimationIfNeeded(x, y, element);
4426 else if (element == EL_DRAGON)
4429 int dir = MovDir[x][y];
4430 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4431 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4432 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4433 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4434 dir == MV_UP ? IMG_FLAMES_1_UP :
4435 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4436 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4439 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4442 GfxAction[x][y] = ACTION_ATTACKING;
4444 if (IS_PLAYER(x, y))
4445 DrawPlayerField(x, y);
4447 DrawLevelField(x, y);
4449 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4451 for (i = 1; i <= 3; i++)
4453 int xx = x + i * dx;
4454 int yy = y + i * dy;
4455 int sx = SCREENX(xx);
4456 int sy = SCREENY(yy);
4457 int flame_graphic = graphic + (i - 1);
4459 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4464 int flamed = MovingOrBlocked2Element(xx, yy);
4466 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4469 RemoveMovingField(xx, yy);
4471 Feld[xx][yy] = EL_FLAMES;
4472 if (IN_SCR_FIELD(sx, sy))
4474 DrawLevelFieldCrumbledSand(xx, yy);
4475 DrawGraphic(sx, sy, flame_graphic, frame);
4480 if (Feld[xx][yy] == EL_FLAMES)
4481 Feld[xx][yy] = EL_EMPTY;
4482 DrawLevelField(xx, yy);
4487 if (MovDelay[x][y]) /* element still has to wait some time */
4489 PlayLevelSoundAction(x, y, ACTION_WAITING);
4495 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4496 for all other elements GfxAction will be set by InitMovingField() */
4497 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4498 GfxAction[x][y] = ACTION_MOVING;
4502 /* now make next step */
4504 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4506 if (DONT_COLLIDE_WITH(element) &&
4507 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4508 !PLAYER_PROTECTED(newx, newy))
4511 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4514 /* player killed by element which is deadly when colliding with */
4516 KillHero(PLAYERINFO(newx, newy));
4521 else if ((element == EL_PENGUIN ||
4522 element == EL_ROBOT ||
4523 element == EL_SATELLITE ||
4524 element == EL_BALLOON ||
4525 IS_CUSTOM_ELEMENT(element)) &&
4526 IN_LEV_FIELD(newx, newy) &&
4527 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4530 Store[x][y] = EL_ACID;
4532 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4534 if (Feld[newx][newy] == EL_EXIT_OPEN)
4538 DrawLevelField(x, y);
4540 Feld[x][y] = EL_EMPTY;
4541 DrawLevelField(x, y);
4544 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4545 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4546 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4548 local_player->friends_still_needed--;
4549 if (!local_player->friends_still_needed &&
4550 !local_player->GameOver && AllPlayersGone)
4551 local_player->LevelSolved = local_player->GameOver = TRUE;
4555 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4557 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4558 DrawLevelField(newx, newy);
4560 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4562 else if (!IS_FREE(newx, newy))
4564 GfxAction[x][y] = ACTION_WAITING;
4566 if (IS_PLAYER(x, y))
4567 DrawPlayerField(x, y);
4569 DrawLevelField(x, y);
4573 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4575 if (IS_FOOD_PIG(Feld[newx][newy]))
4577 if (IS_MOVING(newx, newy))
4578 RemoveMovingField(newx, newy);
4581 Feld[newx][newy] = EL_EMPTY;
4582 DrawLevelField(newx, newy);
4585 PlayLevelSound(x, y, SND_PIG_DIGGING);
4587 else if (!IS_FREE(newx, newy))
4589 if (IS_PLAYER(x, y))
4590 DrawPlayerField(x, y);
4592 DrawLevelField(x, y);
4596 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4597 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4600 if (IS_FREE(newx, newy))
4602 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4605 if (IS_MOVING(newx, newy))
4606 RemoveMovingField(newx, newy);
4609 Feld[newx][newy] = EL_EMPTY;
4610 DrawLevelField(newx, newy);
4613 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4615 else if (!IS_FREE(newx, newy))
4618 if (IS_PLAYER(x, y))
4619 DrawPlayerField(x, y);
4621 DrawLevelField(x, y);
4626 RunnerVisit[x][y] = FrameCounter;
4627 PlayerVisit[x][y] /= 8; /* expire player visit path */
4629 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4631 if (!IS_FREE(newx, newy))
4633 if (IS_PLAYER(x, y))
4634 DrawPlayerField(x, y);
4636 DrawLevelField(x, y);
4642 boolean wanna_flame = !RND(10);
4643 int dx = newx - x, dy = newy - y;
4644 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4645 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4646 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4647 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4648 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4649 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4652 IS_CLASSIC_ENEMY(element1) ||
4653 IS_CLASSIC_ENEMY(element2)) &&
4654 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4655 element1 != EL_FLAMES && element2 != EL_FLAMES)
4658 ResetGfxAnimation(x, y);
4659 GfxAction[x][y] = ACTION_ATTACKING;
4662 if (IS_PLAYER(x, y))
4663 DrawPlayerField(x, y);
4665 DrawLevelField(x, y);
4667 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4669 MovDelay[x][y] = 50;
4671 Feld[newx][newy] = EL_FLAMES;
4672 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4673 Feld[newx1][newy1] = EL_FLAMES;
4674 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4675 Feld[newx2][newy2] = EL_FLAMES;
4681 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4682 Feld[newx][newy] == EL_DIAMOND)
4684 if (IS_MOVING(newx, newy))
4685 RemoveMovingField(newx, newy);
4688 Feld[newx][newy] = EL_EMPTY;
4689 DrawLevelField(newx, newy);
4692 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4694 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4695 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4697 if (AmoebaNr[newx][newy])
4699 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4700 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4701 Feld[newx][newy] == EL_BD_AMOEBA)
4702 AmoebaCnt[AmoebaNr[newx][newy]]--;
4705 if (IS_MOVING(newx, newy))
4706 RemoveMovingField(newx, newy);
4709 Feld[newx][newy] = EL_EMPTY;
4710 DrawLevelField(newx, newy);
4713 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4715 else if ((element == EL_PACMAN || element == EL_MOLE)
4716 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4718 if (AmoebaNr[newx][newy])
4720 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4721 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4722 Feld[newx][newy] == EL_BD_AMOEBA)
4723 AmoebaCnt[AmoebaNr[newx][newy]]--;
4726 if (element == EL_MOLE)
4728 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4729 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4731 ResetGfxAnimation(x, y);
4732 GfxAction[x][y] = ACTION_DIGGING;
4733 DrawLevelField(x, y);
4735 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4736 return; /* wait for shrinking amoeba */
4738 else /* element == EL_PACMAN */
4740 Feld[newx][newy] = EL_EMPTY;
4741 DrawLevelField(newx, newy);
4742 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4745 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4746 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4747 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4749 /* wait for shrinking amoeba to completely disappear */
4752 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4754 /* object was running against a wall */
4759 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4760 DrawLevelElementAnimation(x, y, element);
4762 if (element == EL_BUG ||
4763 element == EL_SPACESHIP ||
4764 element == EL_SP_SNIKSNAK)
4765 DrawLevelField(x, y);
4766 else if (element == EL_MOLE)
4767 DrawLevelField(x, y);
4768 else if (element == EL_BD_BUTTERFLY ||
4769 element == EL_BD_FIREFLY)
4770 DrawLevelElementAnimationIfNeeded(x, y, element);
4771 else if (element == EL_SATELLITE)
4772 DrawLevelElementAnimationIfNeeded(x, y, element);
4773 else if (element == EL_SP_ELECTRON)
4774 DrawLevelElementAnimationIfNeeded(x, y, element);
4777 if (DONT_TOUCH(element))
4778 TestIfBadThingTouchesHero(x, y);
4781 PlayLevelSoundAction(x, y, ACTION_WAITING);
4787 InitMovingField(x, y, MovDir[x][y]);
4789 PlayLevelSoundAction(x, y, ACTION_MOVING);
4793 ContinueMoving(x, y);
4796 void ContinueMoving(int x, int y)
4798 int element = Feld[x][y];
4799 int direction = MovDir[x][y];
4800 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4801 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4802 int newx = x + dx, newy = y + dy;
4804 int nextx = newx + dx, nexty = newy + dy;
4806 boolean pushed = Pushed[x][y];
4808 MovPos[x][y] += getElementMoveStepsize(x, y);
4810 if (pushed) /* special case: moving object pushed by player */
4811 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4813 if (ABS(MovPos[x][y]) < TILEX)
4815 DrawLevelField(x, y);
4817 return; /* element is still moving */
4820 /* element reached destination field */
4822 Feld[x][y] = EL_EMPTY;
4823 Feld[newx][newy] = element;
4824 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4826 if (element == EL_MOLE)
4828 Feld[x][y] = EL_SAND;
4830 DrawLevelFieldCrumbledSandNeighbours(x, y);
4832 else if (element == EL_QUICKSAND_FILLING)
4834 element = Feld[newx][newy] = get_next_element(element);
4835 Store[newx][newy] = Store[x][y];
4837 else if (element == EL_QUICKSAND_EMPTYING)
4839 Feld[x][y] = get_next_element(element);
4840 element = Feld[newx][newy] = Store[x][y];
4842 else if (element == EL_MAGIC_WALL_FILLING)
4844 element = Feld[newx][newy] = get_next_element(element);
4845 if (!game.magic_wall_active)
4846 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4847 Store[newx][newy] = Store[x][y];
4849 else if (element == EL_MAGIC_WALL_EMPTYING)
4851 Feld[x][y] = get_next_element(element);
4852 if (!game.magic_wall_active)
4853 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4854 element = Feld[newx][newy] = Store[x][y];
4856 else if (element == EL_BD_MAGIC_WALL_FILLING)
4858 element = Feld[newx][newy] = get_next_element(element);
4859 if (!game.magic_wall_active)
4860 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4861 Store[newx][newy] = Store[x][y];
4863 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4865 Feld[x][y] = get_next_element(element);
4866 if (!game.magic_wall_active)
4867 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4868 element = Feld[newx][newy] = Store[x][y];
4870 else if (element == EL_AMOEBA_DROPPING)
4872 Feld[x][y] = get_next_element(element);
4873 element = Feld[newx][newy] = Store[x][y];
4875 else if (element == EL_SOKOBAN_OBJECT)
4878 Feld[x][y] = Back[x][y];
4880 if (Back[newx][newy])
4881 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4883 Back[x][y] = Back[newx][newy] = 0;
4885 else if (Store[x][y] == EL_ACID)
4887 element = Feld[newx][newy] = EL_ACID;
4891 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4892 MovDelay[newx][newy] = 0;
4894 /* copy element change control values to new field */
4895 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4896 ChangePage[newx][newy] = ChangePage[x][y];
4897 Changed[newx][newy] = Changed[x][y];
4898 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4900 ChangeDelay[x][y] = 0;
4901 ChangePage[x][y] = -1;
4902 Changed[x][y] = CE_BITMASK_DEFAULT;
4903 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4905 /* copy animation control values to new field */
4906 GfxFrame[newx][newy] = GfxFrame[x][y];
4907 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4908 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4909 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4911 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4913 ResetGfxAnimation(x, y); /* reset animation values for old field */
4916 /* 2.1.1 (does not work correctly for spring) */
4917 if (!CAN_MOVE(element))
4918 MovDir[newx][newy] = 0;
4922 /* (does not work for falling objects that slide horizontally) */
4923 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4924 MovDir[newx][newy] = 0;
4927 if (!CAN_MOVE(element) ||
4928 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4929 MovDir[newx][newy] = 0;
4932 if (!CAN_MOVE(element) ||
4933 (CAN_FALL(element) && direction == MV_DOWN))
4934 GfxDir[x][y] = MovDir[newx][newy] = 0;
4939 DrawLevelField(x, y);
4940 DrawLevelField(newx, newy);
4942 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4944 /* prevent pushed element from moving on in pushed direction */
4945 if (pushed && CAN_MOVE(element) &&
4946 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4947 !(element_info[element].move_pattern & direction))
4948 TurnRound(newx, newy);
4950 if (!pushed) /* special case: moving object pushed by player */
4952 WasJustMoving[newx][newy] = 3;
4954 if (CAN_FALL(element) && direction == MV_DOWN)
4955 WasJustFalling[newx][newy] = 3;
4958 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4960 TestIfBadThingTouchesHero(newx, newy);
4961 TestIfBadThingTouchesFriend(newx, newy);
4963 if (!IS_CUSTOM_ELEMENT(element))
4964 TestIfBadThingTouchesOtherBadThing(newx, newy);
4966 else if (element == EL_PENGUIN)
4967 TestIfFriendTouchesBadThing(newx, newy);
4969 if (CAN_FALL(element) && direction == MV_DOWN &&
4970 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4974 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4978 if (ChangePage[newx][newy] != -1) /* delayed change */
4979 ChangeElement(newx, newy, ChangePage[newx][newy]);
4984 TestIfElementHitsCustomElement(newx, newy, direction);
4988 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4990 int hitting_element = Feld[newx][newy];
4992 /* !!! fix side (direction) orientation here and elsewhere !!! */
4993 CheckElementSideChange(newx, newy, hitting_element,
4994 direction, CE_HITTING_SOMETHING, -1);
4997 if (IN_LEV_FIELD(nextx, nexty))
4999 static int opposite_directions[] =
5006 int move_dir_bit = MV_DIR_BIT(direction);
5007 int opposite_direction = opposite_directions[move_dir_bit];
5008 int hitting_side = direction;
5009 int touched_side = opposite_direction;
5010 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5011 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5012 MovDir[nextx][nexty] != direction ||
5013 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5019 CheckElementSideChange(nextx, nexty, touched_element,
5020 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5022 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5023 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5025 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5027 struct ElementChangeInfo *change =
5028 &element_info[hitting_element].change_page[i];
5030 if (change->can_change &&
5031 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5032 change->sides & touched_side &&
5033 change->trigger_element == touched_element)
5035 CheckElementSideChange(newx, newy, hitting_element,
5036 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5042 if (IS_CUSTOM_ELEMENT(touched_element) &&
5043 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5045 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5047 struct ElementChangeInfo *change =
5048 &element_info[touched_element].change_page[i];
5050 if (change->can_change &&
5051 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5052 change->sides & hitting_side &&
5053 change->trigger_element == hitting_element)
5055 CheckElementSideChange(nextx, nexty, touched_element,
5056 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5067 TestIfPlayerTouchesCustomElement(newx, newy);
5068 TestIfElementTouchesCustomElement(newx, newy);
5071 int AmoebeNachbarNr(int ax, int ay)
5074 int element = Feld[ax][ay];
5076 static int xy[4][2] =
5084 for (i = 0; i < 4; i++)
5086 int x = ax + xy[i][0];
5087 int y = ay + xy[i][1];
5089 if (!IN_LEV_FIELD(x, y))
5092 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5093 group_nr = AmoebaNr[x][y];
5099 void AmoebenVereinigen(int ax, int ay)
5101 int i, x, y, xx, yy;
5102 int new_group_nr = AmoebaNr[ax][ay];
5103 static int xy[4][2] =
5111 if (new_group_nr == 0)
5114 for (i = 0; i < 4; i++)
5119 if (!IN_LEV_FIELD(x, y))
5122 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5123 Feld[x][y] == EL_BD_AMOEBA ||
5124 Feld[x][y] == EL_AMOEBA_DEAD) &&
5125 AmoebaNr[x][y] != new_group_nr)
5127 int old_group_nr = AmoebaNr[x][y];
5129 if (old_group_nr == 0)
5132 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5133 AmoebaCnt[old_group_nr] = 0;
5134 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5135 AmoebaCnt2[old_group_nr] = 0;
5137 for (yy = 0; yy < lev_fieldy; yy++)
5139 for (xx = 0; xx < lev_fieldx; xx++)
5141 if (AmoebaNr[xx][yy] == old_group_nr)
5142 AmoebaNr[xx][yy] = new_group_nr;
5149 void AmoebeUmwandeln(int ax, int ay)
5153 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5155 int group_nr = AmoebaNr[ax][ay];
5160 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5161 printf("AmoebeUmwandeln(): This should never happen!\n");
5166 for (y = 0; y < lev_fieldy; y++)
5168 for (x = 0; x < lev_fieldx; x++)
5170 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5173 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5177 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5178 SND_AMOEBA_TURNING_TO_GEM :
5179 SND_AMOEBA_TURNING_TO_ROCK));
5184 static int xy[4][2] =
5192 for (i = 0; i < 4; i++)
5197 if (!IN_LEV_FIELD(x, y))
5200 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5202 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5203 SND_AMOEBA_TURNING_TO_GEM :
5204 SND_AMOEBA_TURNING_TO_ROCK));
5211 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5214 int group_nr = AmoebaNr[ax][ay];
5215 boolean done = FALSE;
5220 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5221 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5226 for (y = 0; y < lev_fieldy; y++)
5228 for (x = 0; x < lev_fieldx; x++)
5230 if (AmoebaNr[x][y] == group_nr &&
5231 (Feld[x][y] == EL_AMOEBA_DEAD ||
5232 Feld[x][y] == EL_BD_AMOEBA ||
5233 Feld[x][y] == EL_AMOEBA_GROWING))
5236 Feld[x][y] = new_element;
5237 InitField(x, y, FALSE);
5238 DrawLevelField(x, y);
5245 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5246 SND_BD_AMOEBA_TURNING_TO_ROCK :
5247 SND_BD_AMOEBA_TURNING_TO_GEM));
5250 void AmoebeWaechst(int x, int y)
5252 static unsigned long sound_delay = 0;
5253 static unsigned long sound_delay_value = 0;
5255 if (!MovDelay[x][y]) /* start new growing cycle */
5259 if (DelayReached(&sound_delay, sound_delay_value))
5262 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5264 if (Store[x][y] == EL_BD_AMOEBA)
5265 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5267 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5269 sound_delay_value = 30;
5273 if (MovDelay[x][y]) /* wait some time before growing bigger */
5276 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5278 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5279 6 - MovDelay[x][y]);
5281 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5284 if (!MovDelay[x][y])
5286 Feld[x][y] = Store[x][y];
5288 DrawLevelField(x, y);
5293 void AmoebaDisappearing(int x, int y)
5295 static unsigned long sound_delay = 0;
5296 static unsigned long sound_delay_value = 0;
5298 if (!MovDelay[x][y]) /* start new shrinking cycle */
5302 if (DelayReached(&sound_delay, sound_delay_value))
5303 sound_delay_value = 30;
5306 if (MovDelay[x][y]) /* wait some time before shrinking */
5309 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5311 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5312 6 - MovDelay[x][y]);
5314 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5317 if (!MovDelay[x][y])
5319 Feld[x][y] = EL_EMPTY;
5320 DrawLevelField(x, y);
5322 /* don't let mole enter this field in this cycle;
5323 (give priority to objects falling to this field from above) */
5329 void AmoebeAbleger(int ax, int ay)
5332 int element = Feld[ax][ay];
5333 int graphic = el2img(element);
5334 int newax = ax, neway = ay;
5335 static int xy[4][2] =
5343 if (!level.amoeba_speed)
5345 Feld[ax][ay] = EL_AMOEBA_DEAD;
5346 DrawLevelField(ax, ay);
5350 if (IS_ANIMATED(graphic))
5351 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5353 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5354 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5356 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5359 if (MovDelay[ax][ay])
5363 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5366 int x = ax + xy[start][0];
5367 int y = ay + xy[start][1];
5369 if (!IN_LEV_FIELD(x, y))
5372 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5373 if (IS_FREE(x, y) ||
5374 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5380 if (newax == ax && neway == ay)
5383 else /* normal or "filled" (BD style) amoeba */
5386 boolean waiting_for_player = FALSE;
5388 for (i = 0; i < 4; i++)
5390 int j = (start + i) % 4;
5391 int x = ax + xy[j][0];
5392 int y = ay + xy[j][1];
5394 if (!IN_LEV_FIELD(x, y))
5397 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5398 if (IS_FREE(x, y) ||
5399 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5405 else if (IS_PLAYER(x, y))
5406 waiting_for_player = TRUE;
5409 if (newax == ax && neway == ay) /* amoeba cannot grow */
5411 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5413 Feld[ax][ay] = EL_AMOEBA_DEAD;
5414 DrawLevelField(ax, ay);
5415 AmoebaCnt[AmoebaNr[ax][ay]]--;
5417 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5419 if (element == EL_AMOEBA_FULL)
5420 AmoebeUmwandeln(ax, ay);
5421 else if (element == EL_BD_AMOEBA)
5422 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5427 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5429 /* amoeba gets larger by growing in some direction */
5431 int new_group_nr = AmoebaNr[ax][ay];
5434 if (new_group_nr == 0)
5436 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5437 printf("AmoebeAbleger(): This should never happen!\n");
5442 AmoebaNr[newax][neway] = new_group_nr;
5443 AmoebaCnt[new_group_nr]++;
5444 AmoebaCnt2[new_group_nr]++;
5446 /* if amoeba touches other amoeba(s) after growing, unify them */
5447 AmoebenVereinigen(newax, neway);
5449 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5451 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5457 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5458 (neway == lev_fieldy - 1 && newax != ax))
5460 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5461 Store[newax][neway] = element;
5463 else if (neway == ay)
5465 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5467 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5469 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5474 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5475 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5476 Store[ax][ay] = EL_AMOEBA_DROP;
5477 ContinueMoving(ax, ay);
5481 DrawLevelField(newax, neway);
5484 void Life(int ax, int ay)
5487 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5489 int element = Feld[ax][ay];
5490 int graphic = el2img(element);
5491 boolean changed = FALSE;
5493 if (IS_ANIMATED(graphic))
5494 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5499 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5500 MovDelay[ax][ay] = life_time;
5502 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5505 if (MovDelay[ax][ay])
5509 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5511 int xx = ax+x1, yy = ay+y1;
5514 if (!IN_LEV_FIELD(xx, yy))
5517 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5519 int x = xx+x2, y = yy+y2;
5521 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5524 if (((Feld[x][y] == element ||
5525 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5527 (IS_FREE(x, y) && Stop[x][y]))
5531 if (xx == ax && yy == ay) /* field in the middle */
5533 if (nachbarn < life[0] || nachbarn > life[1])
5535 Feld[xx][yy] = EL_EMPTY;
5537 DrawLevelField(xx, yy);
5538 Stop[xx][yy] = TRUE;
5542 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5543 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5544 { /* free border field */
5545 if (nachbarn >= life[2] && nachbarn <= life[3])
5547 Feld[xx][yy] = element;
5548 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5550 DrawLevelField(xx, yy);
5551 Stop[xx][yy] = TRUE;
5558 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5559 SND_GAME_OF_LIFE_GROWING);
5562 static void InitRobotWheel(int x, int y)
5564 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5567 static void RunRobotWheel(int x, int y)
5569 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5572 static void StopRobotWheel(int x, int y)
5574 if (ZX == x && ZY == y)
5578 static void InitTimegateWheel(int x, int y)
5580 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5583 static void RunTimegateWheel(int x, int y)
5585 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5588 void CheckExit(int x, int y)
5590 if (local_player->gems_still_needed > 0 ||
5591 local_player->sokobanfields_still_needed > 0 ||
5592 local_player->lights_still_needed > 0)
5594 int element = Feld[x][y];
5595 int graphic = el2img(element);
5597 if (IS_ANIMATED(graphic))
5598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5603 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5606 Feld[x][y] = EL_EXIT_OPENING;
5608 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5611 void CheckExitSP(int x, int y)
5613 if (local_player->gems_still_needed > 0)
5615 int element = Feld[x][y];
5616 int graphic = el2img(element);
5618 if (IS_ANIMATED(graphic))
5619 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5624 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5627 Feld[x][y] = EL_SP_EXIT_OPENING;
5629 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5632 static void CloseAllOpenTimegates()
5636 for (y = 0; y < lev_fieldy; y++)
5638 for (x = 0; x < lev_fieldx; x++)
5640 int element = Feld[x][y];
5642 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5644 Feld[x][y] = EL_TIMEGATE_CLOSING;
5646 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5648 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5655 void EdelsteinFunkeln(int x, int y)
5657 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5660 if (Feld[x][y] == EL_BD_DIAMOND)
5663 if (MovDelay[x][y] == 0) /* next animation frame */
5664 MovDelay[x][y] = 11 * !SimpleRND(500);
5666 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5670 if (setup.direct_draw && MovDelay[x][y])
5671 SetDrawtoField(DRAW_BUFFERED);
5673 DrawLevelElementAnimation(x, y, Feld[x][y]);
5675 if (MovDelay[x][y] != 0)
5677 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5678 10 - MovDelay[x][y]);
5680 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5682 if (setup.direct_draw)
5686 dest_x = FX + SCREENX(x) * TILEX;
5687 dest_y = FY + SCREENY(y) * TILEY;
5689 BlitBitmap(drawto_field, window,
5690 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5691 SetDrawtoField(DRAW_DIRECT);
5697 void MauerWaechst(int x, int y)
5701 if (!MovDelay[x][y]) /* next animation frame */
5702 MovDelay[x][y] = 3 * delay;
5704 if (MovDelay[x][y]) /* wait some time before next frame */
5708 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5710 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5711 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5713 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5716 if (!MovDelay[x][y])
5718 if (MovDir[x][y] == MV_LEFT)
5720 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5721 DrawLevelField(x - 1, y);
5723 else if (MovDir[x][y] == MV_RIGHT)
5725 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5726 DrawLevelField(x + 1, y);
5728 else if (MovDir[x][y] == MV_UP)
5730 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5731 DrawLevelField(x, y - 1);
5735 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5736 DrawLevelField(x, y + 1);
5739 Feld[x][y] = Store[x][y];
5741 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5742 DrawLevelField(x, y);
5747 void MauerAbleger(int ax, int ay)
5749 int element = Feld[ax][ay];
5750 int graphic = el2img(element);
5751 boolean oben_frei = FALSE, unten_frei = FALSE;
5752 boolean links_frei = FALSE, rechts_frei = FALSE;
5753 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5754 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5755 boolean new_wall = FALSE;
5757 if (IS_ANIMATED(graphic))
5758 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5760 if (!MovDelay[ax][ay]) /* start building new wall */
5761 MovDelay[ax][ay] = 6;
5763 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5766 if (MovDelay[ax][ay])
5770 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5772 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5774 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5776 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5779 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5780 element == EL_EXPANDABLE_WALL_ANY)
5784 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5785 Store[ax][ay-1] = element;
5786 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5787 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5788 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5789 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5794 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5795 Store[ax][ay+1] = element;
5796 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5797 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5798 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5799 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5804 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5805 element == EL_EXPANDABLE_WALL_ANY ||
5806 element == EL_EXPANDABLE_WALL)
5810 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5811 Store[ax-1][ay] = element;
5812 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5813 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5814 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5815 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5821 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5822 Store[ax+1][ay] = element;
5823 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5824 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5825 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5826 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5831 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5832 DrawLevelField(ax, ay);
5834 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5836 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5837 unten_massiv = TRUE;
5838 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5839 links_massiv = TRUE;
5840 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5841 rechts_massiv = TRUE;
5843 if (((oben_massiv && unten_massiv) ||
5844 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5845 element == EL_EXPANDABLE_WALL) &&
5846 ((links_massiv && rechts_massiv) ||
5847 element == EL_EXPANDABLE_WALL_VERTICAL))
5848 Feld[ax][ay] = EL_WALL;
5852 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5854 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5858 void CheckForDragon(int x, int y)
5861 boolean dragon_found = FALSE;
5862 static int xy[4][2] =
5870 for (i = 0; i < 4; i++)
5872 for (j = 0; j < 4; j++)
5874 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5876 if (IN_LEV_FIELD(xx, yy) &&
5877 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5879 if (Feld[xx][yy] == EL_DRAGON)
5880 dragon_found = TRUE;
5889 for (i = 0; i < 4; i++)
5891 for (j = 0; j < 3; j++)
5893 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5895 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5897 Feld[xx][yy] = EL_EMPTY;
5898 DrawLevelField(xx, yy);
5907 static void InitBuggyBase(int x, int y)
5909 int element = Feld[x][y];
5910 int activating_delay = FRAMES_PER_SECOND / 4;
5913 (element == EL_SP_BUGGY_BASE ?
5914 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5915 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5917 element == EL_SP_BUGGY_BASE_ACTIVE ?
5918 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5921 static void WarnBuggyBase(int x, int y)
5924 static int xy[4][2] =
5932 for (i = 0; i < 4; i++)
5934 int xx = x + xy[i][0], yy = y + xy[i][1];
5936 if (IS_PLAYER(xx, yy))
5938 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5945 static void InitTrap(int x, int y)
5947 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5950 static void ActivateTrap(int x, int y)
5952 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5955 static void ChangeActiveTrap(int x, int y)
5957 int graphic = IMG_TRAP_ACTIVE;
5959 /* if new animation frame was drawn, correct crumbled sand border */
5960 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5961 DrawLevelFieldCrumbledSand(x, y);
5964 static void ChangeElementNowExt(int x, int y, int target_element)
5966 /* check if element under player changes from accessible to unaccessible
5967 (needed for special case of dropping element which then changes) */
5968 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5969 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5976 Feld[x][y] = target_element;
5978 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5980 ResetGfxAnimation(x, y);
5981 ResetRandomAnimationValue(x, y);
5983 InitField(x, y, FALSE);
5984 if (CAN_MOVE(Feld[x][y]))
5987 DrawLevelField(x, y);
5989 if (GFX_CRUMBLED(Feld[x][y]))
5990 DrawLevelFieldCrumbledSandNeighbours(x, y);
5992 TestIfBadThingTouchesHero(x, y);
5993 TestIfPlayerTouchesCustomElement(x, y);
5994 TestIfElementTouchesCustomElement(x, y);
5996 if (ELEM_IS_PLAYER(target_element))
5997 RelocatePlayer(x, y, target_element);
6000 static boolean ChangeElementNow(int x, int y, int element, int page)
6002 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6004 /* always use default change event to prevent running into a loop */
6005 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6006 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6008 /* do not change already changed elements with same change event */
6010 if (Changed[x][y] & ChangeEvent[x][y])
6017 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6019 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6021 if (change->explode)
6028 if (change->use_content)
6030 boolean complete_change = TRUE;
6031 boolean can_change[3][3];
6034 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6036 boolean half_destructible;
6037 int ex = x + xx - 1;
6038 int ey = y + yy - 1;
6041 can_change[xx][yy] = TRUE;
6043 if (ex == x && ey == y) /* do not check changing element itself */
6046 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6048 can_change[xx][yy] = FALSE; /* do not change empty borders */
6053 if (!IN_LEV_FIELD(ex, ey))
6055 can_change[xx][yy] = FALSE;
6056 complete_change = FALSE;
6063 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6064 e = MovingOrBlocked2Element(ex, ey);
6066 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6068 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6069 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6070 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6072 can_change[xx][yy] = FALSE;
6073 complete_change = FALSE;
6077 if (!change->only_complete || complete_change)
6079 boolean something_has_changed = FALSE;
6081 if (change->only_complete && change->use_random_change &&
6082 RND(100) < change->random)
6085 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6087 int ex = x + xx - 1;
6088 int ey = y + yy - 1;
6090 if (can_change[xx][yy] && (!change->use_random_change ||
6091 RND(100) < change->random))
6093 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6094 RemoveMovingField(ex, ey);
6096 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6098 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6100 something_has_changed = TRUE;
6102 /* for symmetry reasons, freeze newly created border elements */
6103 if (ex != x || ey != y)
6104 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6108 if (something_has_changed)
6109 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6114 ChangeElementNowExt(x, y, change->target_element);
6116 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6122 static void ChangeElement(int x, int y, int page)
6124 int element = MovingOrBlocked2Element(x, y);
6125 struct ElementInfo *ei = &element_info[element];
6126 struct ElementChangeInfo *change = &ei->change_page[page];
6130 if (!CAN_CHANGE(element))
6133 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6134 x, y, element, element_info[element].token_name);
6135 printf("ChangeElement(): This should never happen!\n");
6141 if (ChangeDelay[x][y] == 0) /* initialize element change */
6143 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6144 RND(change->delay_random * change->delay_frames)) + 1;
6146 ResetGfxAnimation(x, y);
6147 ResetRandomAnimationValue(x, y);
6149 if (change->pre_change_function)
6150 change->pre_change_function(x, y);
6153 ChangeDelay[x][y]--;
6155 if (ChangeDelay[x][y] != 0) /* continue element change */
6157 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6159 if (IS_ANIMATED(graphic))
6160 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6162 if (change->change_function)
6163 change->change_function(x, y);
6165 else /* finish element change */
6167 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6169 page = ChangePage[x][y];
6170 ChangePage[x][y] = -1;
6174 if (IS_MOVING(x, y) && !change->explode)
6176 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6179 ChangeDelay[x][y] = 1; /* try change after next move step */
6180 ChangePage[x][y] = page; /* remember page to use for change */
6185 if (ChangeElementNow(x, y, element, page))
6187 if (change->post_change_function)
6188 change->post_change_function(x, y);
6193 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6194 int trigger_element,
6200 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6203 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6205 int element = EL_CUSTOM_START + i;
6207 boolean change_element = FALSE;
6210 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6213 for (j = 0; j < element_info[element].num_change_pages; j++)
6215 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6217 if (change->can_change &&
6219 change->events & CH_EVENT_BIT(trigger_event) &&
6221 change->sides & trigger_side &&
6222 change->trigger_element == trigger_element)
6225 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6226 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6227 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6230 change_element = TRUE;
6237 if (!change_element)
6240 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6243 if (x == lx && y == ly) /* do not change trigger element itself */
6247 if (Feld[x][y] == element)
6249 ChangeDelay[x][y] = 1;
6250 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6251 ChangeElement(x, y, page);
6259 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6262 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6266 static boolean CheckElementSideChange(int x, int y, int element, int side,
6267 int trigger_event, int page)
6269 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6272 if (Feld[x][y] == EL_BLOCKED)
6274 Blocked2Moving(x, y, &x, &y);
6275 element = Feld[x][y];
6279 page = element_info[element].event_page_nr[trigger_event];
6281 if (!(element_info[element].change_page[page].sides & side))
6284 ChangeDelay[x][y] = 1;
6285 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6286 ChangeElement(x, y, page);
6291 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6293 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6296 static void PlayPlayerSound(struct PlayerInfo *player)
6298 int jx = player->jx, jy = player->jy;
6299 int element = player->element_nr;
6300 int last_action = player->last_action_waiting;
6301 int action = player->action_waiting;
6303 if (player->is_waiting)
6305 if (action != last_action)
6306 PlayLevelSoundElementAction(jx, jy, element, action);
6308 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6312 if (action != last_action)
6313 StopSound(element_info[element].sound[last_action]);
6315 if (last_action == ACTION_SLEEPING)
6316 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6320 static void PlayAllPlayersSound()
6324 for (i = 0; i < MAX_PLAYERS; i++)
6325 if (stored_player[i].active)
6326 PlayPlayerSound(&stored_player[i]);
6329 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6331 boolean last_waiting = player->is_waiting;
6332 int move_dir = player->MovDir;
6334 player->last_action_waiting = player->action_waiting;
6338 if (!last_waiting) /* not waiting -> waiting */
6340 player->is_waiting = TRUE;
6342 player->frame_counter_bored =
6344 game.player_boring_delay_fixed +
6345 SimpleRND(game.player_boring_delay_random);
6346 player->frame_counter_sleeping =
6348 game.player_sleeping_delay_fixed +
6349 SimpleRND(game.player_sleeping_delay_random);
6351 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6354 if (game.player_sleeping_delay_fixed +
6355 game.player_sleeping_delay_random > 0 &&
6356 player->anim_delay_counter == 0 &&
6357 player->post_delay_counter == 0 &&
6358 FrameCounter >= player->frame_counter_sleeping)
6359 player->is_sleeping = TRUE;
6360 else if (game.player_boring_delay_fixed +
6361 game.player_boring_delay_random > 0 &&
6362 FrameCounter >= player->frame_counter_bored)
6363 player->is_bored = TRUE;
6365 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6366 player->is_bored ? ACTION_BORING :
6369 if (player->is_sleeping)
6371 if (player->num_special_action_sleeping > 0)
6373 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6375 int last_special_action = player->special_action_sleeping;
6376 int num_special_action = player->num_special_action_sleeping;
6377 int special_action =
6378 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6379 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6380 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6381 last_special_action + 1 : ACTION_SLEEPING);
6382 int special_graphic =
6383 el_act_dir2img(player->element_nr, special_action, move_dir);
6385 player->anim_delay_counter =
6386 graphic_info[special_graphic].anim_delay_fixed +
6387 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6388 player->post_delay_counter =
6389 graphic_info[special_graphic].post_delay_fixed +
6390 SimpleRND(graphic_info[special_graphic].post_delay_random);
6392 player->special_action_sleeping = special_action;
6395 if (player->anim_delay_counter > 0)
6397 player->action_waiting = player->special_action_sleeping;
6398 player->anim_delay_counter--;
6400 else if (player->post_delay_counter > 0)
6402 player->post_delay_counter--;
6406 else if (player->is_bored)
6408 if (player->num_special_action_bored > 0)
6410 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6412 int special_action =
6413 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6414 int special_graphic =
6415 el_act_dir2img(player->element_nr, special_action, move_dir);
6417 player->anim_delay_counter =
6418 graphic_info[special_graphic].anim_delay_fixed +
6419 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6420 player->post_delay_counter =
6421 graphic_info[special_graphic].post_delay_fixed +
6422 SimpleRND(graphic_info[special_graphic].post_delay_random);
6424 player->special_action_bored = special_action;
6427 if (player->anim_delay_counter > 0)
6429 player->action_waiting = player->special_action_bored;
6430 player->anim_delay_counter--;
6432 else if (player->post_delay_counter > 0)
6434 player->post_delay_counter--;
6439 else if (last_waiting) /* waiting -> not waiting */
6441 player->is_waiting = FALSE;
6442 player->is_bored = FALSE;
6443 player->is_sleeping = FALSE;
6445 player->frame_counter_bored = -1;
6446 player->frame_counter_sleeping = -1;
6448 player->anim_delay_counter = 0;
6449 player->post_delay_counter = 0;
6451 player->action_waiting = ACTION_DEFAULT;
6453 player->special_action_bored = ACTION_DEFAULT;
6454 player->special_action_sleeping = ACTION_DEFAULT;
6459 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6462 static byte stored_player_action[MAX_PLAYERS];
6463 static int num_stored_actions = 0;
6465 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6466 int left = player_action & JOY_LEFT;
6467 int right = player_action & JOY_RIGHT;
6468 int up = player_action & JOY_UP;
6469 int down = player_action & JOY_DOWN;
6470 int button1 = player_action & JOY_BUTTON_1;
6471 int button2 = player_action & JOY_BUTTON_2;
6472 int dx = (left ? -1 : right ? 1 : 0);
6473 int dy = (up ? -1 : down ? 1 : 0);
6476 stored_player_action[player->index_nr] = 0;
6477 num_stored_actions++;
6481 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6484 if (!player->active || tape.pausing)
6490 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6494 snapped = SnapField(player, dx, dy);
6498 dropped = DropElement(player);
6500 moved = MovePlayer(player, dx, dy);
6503 if (tape.single_step && tape.recording && !tape.pausing)
6505 if (button1 || (dropped && !moved))
6507 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6508 SnapField(player, 0, 0); /* stop snapping */
6512 SetPlayerWaiting(player, FALSE);
6515 return player_action;
6517 stored_player_action[player->index_nr] = player_action;
6523 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6526 /* no actions for this player (no input at player's configured device) */
6528 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6529 SnapField(player, 0, 0);
6530 CheckGravityMovement(player);
6532 if (player->MovPos == 0)
6533 SetPlayerWaiting(player, TRUE);
6535 if (player->MovPos == 0) /* needed for tape.playing */
6536 player->is_moving = FALSE;
6538 player->is_dropping = FALSE;
6544 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6546 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6548 TapeRecordAction(stored_player_action);
6549 num_stored_actions = 0;
6556 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6558 static byte stored_player_action[MAX_PLAYERS];
6559 static int num_stored_actions = 0;
6560 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6561 int left = player_action & JOY_LEFT;
6562 int right = player_action & JOY_RIGHT;
6563 int up = player_action & JOY_UP;
6564 int down = player_action & JOY_DOWN;
6565 int button1 = player_action & JOY_BUTTON_1;
6566 int button2 = player_action & JOY_BUTTON_2;
6567 int dx = (left ? -1 : right ? 1 : 0);
6568 int dy = (up ? -1 : down ? 1 : 0);
6570 stored_player_action[player->index_nr] = 0;
6571 num_stored_actions++;
6573 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6575 if (!player->active || tape.pausing)
6580 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6583 snapped = SnapField(player, dx, dy);
6587 dropped = DropElement(player);
6589 moved = MovePlayer(player, dx, dy);
6592 if (tape.single_step && tape.recording && !tape.pausing)
6594 if (button1 || (dropped && !moved))
6596 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6597 SnapField(player, 0, 0); /* stop snapping */
6601 stored_player_action[player->index_nr] = player_action;
6605 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6607 /* no actions for this player (no input at player's configured device) */
6609 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6610 SnapField(player, 0, 0);
6611 CheckGravityMovement(player);
6613 if (player->MovPos == 0)
6614 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6616 if (player->MovPos == 0) /* needed for tape.playing */
6617 player->is_moving = FALSE;
6620 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6622 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6624 TapeRecordAction(stored_player_action);
6625 num_stored_actions = 0;
6632 static unsigned long action_delay = 0;
6633 unsigned long action_delay_value;
6634 int magic_wall_x = 0, magic_wall_y = 0;
6635 int i, x, y, element, graphic;
6636 byte *recorded_player_action;
6637 byte summarized_player_action = 0;
6639 byte tape_action[MAX_PLAYERS];
6642 if (game_status != GAME_MODE_PLAYING)
6645 action_delay_value =
6646 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6648 if (tape.playing && tape.index_search && !tape.pausing)
6649 action_delay_value = 0;
6651 /* ---------- main game synchronization point ---------- */
6653 WaitUntilDelayReached(&action_delay, action_delay_value);
6655 if (network_playing && !network_player_action_received)
6659 printf("DEBUG: try to get network player actions in time\n");
6663 #if defined(PLATFORM_UNIX)
6664 /* last chance to get network player actions without main loop delay */
6668 if (game_status != GAME_MODE_PLAYING)
6671 if (!network_player_action_received)
6675 printf("DEBUG: failed to get network player actions in time\n");
6686 printf("::: getting new tape action [%d]\n", FrameCounter);
6689 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6691 for (i = 0; i < MAX_PLAYERS; i++)
6693 summarized_player_action |= stored_player[i].action;
6695 if (!network_playing)
6696 stored_player[i].effective_action = stored_player[i].action;
6699 #if defined(PLATFORM_UNIX)
6700 if (network_playing)
6701 SendToServer_MovePlayer(summarized_player_action);
6704 if (!options.network && !setup.team_mode)
6705 local_player->effective_action = summarized_player_action;
6707 for (i = 0; i < MAX_PLAYERS; i++)
6709 int actual_player_action = stored_player[i].effective_action;
6711 if (stored_player[i].programmed_action)
6712 actual_player_action = stored_player[i].programmed_action;
6714 if (recorded_player_action)
6715 actual_player_action = recorded_player_action[i];
6717 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6719 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6720 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6722 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6727 TapeRecordAction(tape_action);
6730 network_player_action_received = FALSE;
6732 ScrollScreen(NULL, SCROLL_GO_ON);
6738 for (i = 0; i < MAX_PLAYERS; i++)
6739 stored_player[i].Frame++;
6743 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6745 for (i = 0; i < MAX_PLAYERS; i++)
6747 struct PlayerInfo *player = &stored_player[i];
6751 if (player->active && player->is_pushing && player->is_moving &&
6754 ContinueMoving(x, y);
6756 /* continue moving after pushing (this is actually a bug) */
6757 if (!IS_MOVING(x, y))
6766 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6768 Changed[x][y] = CE_BITMASK_DEFAULT;
6769 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6772 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6774 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6775 printf("GameActions(): This should never happen!\n");
6777 ChangePage[x][y] = -1;
6782 if (WasJustMoving[x][y] > 0)
6783 WasJustMoving[x][y]--;
6784 if (WasJustFalling[x][y] > 0)
6785 WasJustFalling[x][y]--;
6790 /* reset finished pushing action (not done in ContinueMoving() to allow
6791 continous pushing animation for elements with zero push delay) */
6792 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6794 ResetGfxAnimation(x, y);
6795 DrawLevelField(x, y);
6800 if (IS_BLOCKED(x, y))
6804 Blocked2Moving(x, y, &oldx, &oldy);
6805 if (!IS_MOVING(oldx, oldy))
6807 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6808 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6809 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6810 printf("GameActions(): This should never happen!\n");
6816 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6818 element = Feld[x][y];
6820 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6822 graphic = el2img(element);
6828 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6830 element = graphic = 0;
6834 if (graphic_info[graphic].anim_global_sync)
6835 GfxFrame[x][y] = FrameCounter;
6837 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6838 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6839 ResetRandomAnimationValue(x, y);
6841 SetRandomAnimationValue(x, y);
6844 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6847 if (IS_INACTIVE(element))
6849 if (IS_ANIMATED(graphic))
6850 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6856 /* this may take place after moving, so 'element' may have changed */
6858 if (IS_CHANGING(x, y))
6860 if (IS_CHANGING(x, y) &&
6861 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6865 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6866 element_info[element].event_page_nr[CE_DELAY]);
6868 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6871 element = Feld[x][y];
6872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6876 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6881 element = Feld[x][y];
6882 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6884 if (element == EL_MOLE)
6885 printf("::: %d, %d, %d [%d]\n",
6886 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6890 if (element == EL_YAMYAM)
6891 printf("::: %d, %d, %d\n",
6892 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6896 if (IS_ANIMATED(graphic) &&
6900 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6903 if (element == EL_BUG)
6904 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6908 if (element == EL_MOLE)
6909 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6913 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6914 EdelsteinFunkeln(x, y);
6916 else if ((element == EL_ACID ||
6917 element == EL_EXIT_OPEN ||
6918 element == EL_SP_EXIT_OPEN ||
6919 element == EL_SP_TERMINAL ||
6920 element == EL_SP_TERMINAL_ACTIVE ||
6921 element == EL_EXTRA_TIME ||
6922 element == EL_SHIELD_NORMAL ||
6923 element == EL_SHIELD_DEADLY) &&
6924 IS_ANIMATED(graphic))
6925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6926 else if (IS_MOVING(x, y))
6927 ContinueMoving(x, y);
6928 else if (IS_ACTIVE_BOMB(element))
6929 CheckDynamite(x, y);
6931 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6932 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6934 else if (element == EL_AMOEBA_GROWING)
6935 AmoebeWaechst(x, y);
6936 else if (element == EL_AMOEBA_SHRINKING)
6937 AmoebaDisappearing(x, y);
6939 #if !USE_NEW_AMOEBA_CODE
6940 else if (IS_AMOEBALIVE(element))
6941 AmoebeAbleger(x, y);
6944 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6946 else if (element == EL_EXIT_CLOSED)
6948 else if (element == EL_SP_EXIT_CLOSED)
6950 else if (element == EL_EXPANDABLE_WALL_GROWING)
6952 else if (element == EL_EXPANDABLE_WALL ||
6953 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6954 element == EL_EXPANDABLE_WALL_VERTICAL ||
6955 element == EL_EXPANDABLE_WALL_ANY)
6957 else if (element == EL_FLAMES)
6958 CheckForDragon(x, y);
6960 else if (IS_AUTO_CHANGING(element))
6961 ChangeElement(x, y);
6963 else if (element == EL_EXPLOSION)
6964 ; /* drawing of correct explosion animation is handled separately */
6965 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6966 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6969 /* this may take place after moving, so 'element' may have changed */
6970 if (IS_AUTO_CHANGING(Feld[x][y]))
6971 ChangeElement(x, y);
6974 if (IS_BELT_ACTIVE(element))
6975 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6977 if (game.magic_wall_active)
6979 int jx = local_player->jx, jy = local_player->jy;
6981 /* play the element sound at the position nearest to the player */
6982 if ((element == EL_MAGIC_WALL_FULL ||
6983 element == EL_MAGIC_WALL_ACTIVE ||
6984 element == EL_MAGIC_WALL_EMPTYING ||
6985 element == EL_BD_MAGIC_WALL_FULL ||
6986 element == EL_BD_MAGIC_WALL_ACTIVE ||
6987 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6988 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6996 #if USE_NEW_AMOEBA_CODE
6997 /* new experimental amoeba growth stuff */
6999 if (!(FrameCounter % 8))
7002 static unsigned long random = 1684108901;
7004 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7007 x = (random >> 10) % lev_fieldx;
7008 y = (random >> 20) % lev_fieldy;
7010 x = RND(lev_fieldx);
7011 y = RND(lev_fieldy);
7013 element = Feld[x][y];
7015 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7016 if (!IS_PLAYER(x,y) &&
7017 (element == EL_EMPTY ||
7018 element == EL_SAND ||
7019 element == EL_QUICKSAND_EMPTY ||
7020 element == EL_ACID_SPLASH_LEFT ||
7021 element == EL_ACID_SPLASH_RIGHT))
7023 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7024 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7025 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7026 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7027 Feld[x][y] = EL_AMOEBA_DROP;
7030 random = random * 129 + 1;
7036 if (game.explosions_delayed)
7039 game.explosions_delayed = FALSE;
7041 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7043 element = Feld[x][y];
7045 if (ExplodeField[x][y])
7046 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7047 else if (element == EL_EXPLOSION)
7048 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7050 ExplodeField[x][y] = EX_NO_EXPLOSION;
7053 game.explosions_delayed = TRUE;
7056 if (game.magic_wall_active)
7058 if (!(game.magic_wall_time_left % 4))
7060 int element = Feld[magic_wall_x][magic_wall_y];
7062 if (element == EL_BD_MAGIC_WALL_FULL ||
7063 element == EL_BD_MAGIC_WALL_ACTIVE ||
7064 element == EL_BD_MAGIC_WALL_EMPTYING)
7065 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7067 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7070 if (game.magic_wall_time_left > 0)
7072 game.magic_wall_time_left--;
7073 if (!game.magic_wall_time_left)
7075 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7077 element = Feld[x][y];
7079 if (element == EL_MAGIC_WALL_ACTIVE ||
7080 element == EL_MAGIC_WALL_FULL)
7082 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7083 DrawLevelField(x, y);
7085 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7086 element == EL_BD_MAGIC_WALL_FULL)
7088 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7089 DrawLevelField(x, y);
7093 game.magic_wall_active = FALSE;
7098 if (game.light_time_left > 0)
7100 game.light_time_left--;
7102 if (game.light_time_left == 0)
7103 RedrawAllLightSwitchesAndInvisibleElements();
7106 if (game.timegate_time_left > 0)
7108 game.timegate_time_left--;
7110 if (game.timegate_time_left == 0)
7111 CloseAllOpenTimegates();
7114 for (i = 0; i < MAX_PLAYERS; i++)
7116 struct PlayerInfo *player = &stored_player[i];
7118 if (SHIELD_ON(player))
7120 if (player->shield_deadly_time_left)
7121 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7122 else if (player->shield_normal_time_left)
7123 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7127 if (TimeFrames >= FRAMES_PER_SECOND)
7132 for (i = 0; i < MAX_PLAYERS; i++)
7134 struct PlayerInfo *player = &stored_player[i];
7136 if (SHIELD_ON(player))
7138 player->shield_normal_time_left--;
7140 if (player->shield_deadly_time_left > 0)
7141 player->shield_deadly_time_left--;
7145 if (tape.recording || tape.playing)
7146 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7152 if (TimeLeft <= 10 && setup.time_limit)
7153 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7155 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7157 if (!TimeLeft && setup.time_limit)
7158 for (i = 0; i < MAX_PLAYERS; i++)
7159 KillHero(&stored_player[i]);
7161 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7162 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7166 PlayAllPlayersSound();
7168 if (options.debug) /* calculate frames per second */
7170 static unsigned long fps_counter = 0;
7171 static int fps_frames = 0;
7172 unsigned long fps_delay_ms = Counter() - fps_counter;
7176 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7178 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7181 fps_counter = Counter();
7184 redraw_mask |= REDRAW_FPS;
7188 if (stored_player[0].jx != stored_player[0].last_jx ||
7189 stored_player[0].jy != stored_player[0].last_jy)
7190 printf("::: %d, %d, %d, %d, %d\n",
7191 stored_player[0].MovDir,
7192 stored_player[0].MovPos,
7193 stored_player[0].GfxPos,
7194 stored_player[0].Frame,
7195 stored_player[0].StepFrame);
7202 for (i = 0; i < MAX_PLAYERS; i++)
7205 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7207 stored_player[i].Frame += move_frames;
7209 if (stored_player[i].MovPos != 0)
7210 stored_player[i].StepFrame += move_frames;
7212 if (stored_player[i].drop_delay > 0)
7213 stored_player[i].drop_delay--;
7218 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7220 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7222 local_player->show_envelope = 0;
7227 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7229 int min_x = x, min_y = y, max_x = x, max_y = y;
7232 for (i = 0; i < MAX_PLAYERS; i++)
7234 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7236 if (!stored_player[i].active || &stored_player[i] == player)
7239 min_x = MIN(min_x, jx);
7240 min_y = MIN(min_y, jy);
7241 max_x = MAX(max_x, jx);
7242 max_y = MAX(max_y, jy);
7245 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7248 static boolean AllPlayersInVisibleScreen()
7252 for (i = 0; i < MAX_PLAYERS; i++)
7254 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7256 if (!stored_player[i].active)
7259 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7266 void ScrollLevel(int dx, int dy)
7268 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7271 BlitBitmap(drawto_field, drawto_field,
7272 FX + TILEX * (dx == -1) - softscroll_offset,
7273 FY + TILEY * (dy == -1) - softscroll_offset,
7274 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7275 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7276 FX + TILEX * (dx == 1) - softscroll_offset,
7277 FY + TILEY * (dy == 1) - softscroll_offset);
7281 x = (dx == 1 ? BX1 : BX2);
7282 for (y = BY1; y <= BY2; y++)
7283 DrawScreenField(x, y);
7288 y = (dy == 1 ? BY1 : BY2);
7289 for (x = BX1; x <= BX2; x++)
7290 DrawScreenField(x, y);
7293 redraw_mask |= REDRAW_FIELD;
7296 static void CheckGravityMovement(struct PlayerInfo *player)
7298 if (game.gravity && !player->programmed_action)
7300 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7301 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7303 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7304 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7305 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7306 int jx = player->jx, jy = player->jy;
7307 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7308 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7309 int new_jx = jx + dx, new_jy = jy + dy;
7310 boolean field_under_player_is_free =
7311 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7312 boolean player_is_moving_to_valid_field =
7313 (IN_LEV_FIELD(new_jx, new_jy) &&
7314 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7315 Feld[new_jx][new_jy] == EL_SAND));
7316 /* !!! extend EL_SAND to anything diggable !!! */
7318 if (field_under_player_is_free &&
7319 !player_is_moving_to_valid_field &&
7320 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7321 player->programmed_action = MV_DOWN;
7327 -----------------------------------------------------------------------------
7328 dx, dy: direction (non-diagonal) to try to move the player to
7329 real_dx, real_dy: direction as read from input device (can be diagonal)
7332 boolean MovePlayerOneStep(struct PlayerInfo *player,
7333 int dx, int dy, int real_dx, int real_dy)
7336 static int change_sides[4][2] =
7338 /* enter side leave side */
7339 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7340 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7341 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7342 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7344 int move_direction = (dx == -1 ? MV_LEFT :
7345 dx == +1 ? MV_RIGHT :
7347 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7348 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7349 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7351 int jx = player->jx, jy = player->jy;
7352 int new_jx = jx + dx, new_jy = jy + dy;
7356 if (!player->active || (!dx && !dy))
7357 return MF_NO_ACTION;
7359 player->MovDir = (dx < 0 ? MV_LEFT :
7362 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7364 if (!IN_LEV_FIELD(new_jx, new_jy))
7365 return MF_NO_ACTION;
7367 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7368 return MF_NO_ACTION;
7371 element = MovingOrBlocked2Element(new_jx, new_jy);
7373 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7376 if (DONT_RUN_INTO(element))
7378 if (element == EL_ACID && dx == 0 && dy == 1)
7381 Feld[jx][jy] = EL_PLAYER_1;
7382 InitMovingField(jx, jy, MV_DOWN);
7383 Store[jx][jy] = EL_ACID;
7384 ContinueMoving(jx, jy);
7388 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7393 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7394 if (can_move != MF_MOVING)
7397 /* check if DigField() has caused relocation of the player */
7398 if (player->jx != jx || player->jy != jy)
7399 return MF_NO_ACTION;
7401 StorePlayer[jx][jy] = 0;
7402 player->last_jx = jx;
7403 player->last_jy = jy;
7404 player->jx = new_jx;
7405 player->jy = new_jy;
7406 StorePlayer[new_jx][new_jy] = player->element_nr;
7409 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7411 player->step_counter++;
7413 player->drop_delay = 0;
7415 PlayerVisit[jx][jy] = FrameCounter;
7417 ScrollPlayer(player, SCROLL_INIT);
7420 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7422 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7423 CE_OTHER_GETS_LEFT);
7424 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7425 CE_LEFT_BY_PLAYER, -1);
7428 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7430 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7431 enter_side, CE_OTHER_GETS_ENTERED);
7432 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7433 CE_ENTERED_BY_PLAYER, -1);
7440 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7442 int jx = player->jx, jy = player->jy;
7443 int old_jx = jx, old_jy = jy;
7444 int moved = MF_NO_ACTION;
7447 if (!player->active)
7452 if (player->MovPos == 0)
7454 player->is_moving = FALSE;
7455 player->is_digging = FALSE;
7456 player->is_collecting = FALSE;
7457 player->is_snapping = FALSE;
7458 player->is_pushing = FALSE;
7464 if (!player->active || (!dx && !dy))
7469 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7473 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7474 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7478 /* remove the last programmed player action */
7479 player->programmed_action = 0;
7483 /* should only happen if pre-1.2 tape recordings are played */
7484 /* this is only for backward compatibility */
7486 int original_move_delay_value = player->move_delay_value;
7489 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7493 /* scroll remaining steps with finest movement resolution */
7494 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7496 while (player->MovPos)
7498 ScrollPlayer(player, SCROLL_GO_ON);
7499 ScrollScreen(NULL, SCROLL_GO_ON);
7505 player->move_delay_value = original_move_delay_value;
7508 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7510 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7511 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7515 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7516 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7522 if (moved & MF_MOVING && !ScreenMovPos &&
7523 (player == local_player || !options.network))
7525 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7526 int offset = (setup.scroll_delay ? 3 : 0);
7528 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7530 /* actual player has left the screen -- scroll in that direction */
7531 if (jx != old_jx) /* player has moved horizontally */
7532 scroll_x += (jx - old_jx);
7533 else /* player has moved vertically */
7534 scroll_y += (jy - old_jy);
7538 if (jx != old_jx) /* player has moved horizontally */
7540 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7541 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7542 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7544 /* don't scroll over playfield boundaries */
7545 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7546 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7548 /* don't scroll more than one field at a time */
7549 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7551 /* don't scroll against the player's moving direction */
7552 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7553 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7554 scroll_x = old_scroll_x;
7556 else /* player has moved vertically */
7558 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7559 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7560 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7562 /* don't scroll over playfield boundaries */
7563 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7564 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7566 /* don't scroll more than one field at a time */
7567 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7569 /* don't scroll against the player's moving direction */
7570 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7571 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7572 scroll_y = old_scroll_y;
7576 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7578 if (!options.network && !AllPlayersInVisibleScreen())
7580 scroll_x = old_scroll_x;
7581 scroll_y = old_scroll_y;
7585 ScrollScreen(player, SCROLL_INIT);
7586 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7593 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7595 if (!(moved & MF_MOVING) && !player->is_pushing)
7600 player->StepFrame = 0;
7602 if (moved & MF_MOVING)
7604 if (old_jx != jx && old_jy == jy)
7605 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7606 else if (old_jx == jx && old_jy != jy)
7607 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7609 DrawLevelField(jx, jy); /* for "crumbled sand" */
7611 player->last_move_dir = player->MovDir;
7612 player->is_moving = TRUE;
7614 player->is_snapping = FALSE;
7618 player->is_switching = FALSE;
7621 player->is_dropping = FALSE;
7626 static int change_sides[4][2] =
7628 /* enter side leave side */
7629 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7630 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7631 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7632 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7634 int move_direction = player->MovDir;
7635 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7636 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7639 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7641 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7642 leave_side, CE_OTHER_GETS_LEFT);
7643 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7644 leave_side, CE_LEFT_BY_PLAYER, -1);
7647 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7649 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7650 enter_side, CE_OTHER_GETS_ENTERED);
7651 CheckElementSideChange(jx, jy, Feld[jx][jy],
7652 enter_side, CE_ENTERED_BY_PLAYER, -1);
7663 CheckGravityMovement(player);
7666 player->last_move_dir = MV_NO_MOVING;
7668 player->is_moving = FALSE;
7671 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7673 TestIfHeroTouchesBadThing(jx, jy);
7674 TestIfPlayerTouchesCustomElement(jx, jy);
7677 if (!player->active)
7683 void ScrollPlayer(struct PlayerInfo *player, int mode)
7685 int jx = player->jx, jy = player->jy;
7686 int last_jx = player->last_jx, last_jy = player->last_jy;
7687 int move_stepsize = TILEX / player->move_delay_value;
7689 if (!player->active || !player->MovPos)
7692 if (mode == SCROLL_INIT)
7694 player->actual_frame_counter = FrameCounter;
7695 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7697 if (Feld[last_jx][last_jy] == EL_EMPTY)
7698 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7705 else if (!FrameReached(&player->actual_frame_counter, 1))
7708 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7709 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7711 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7712 Feld[last_jx][last_jy] = EL_EMPTY;
7714 /* before DrawPlayer() to draw correct player graphic for this case */
7715 if (player->MovPos == 0)
7716 CheckGravityMovement(player);
7719 DrawPlayer(player); /* needed here only to cleanup last field */
7722 if (player->MovPos == 0) /* player reached destination field */
7725 if (player->move_delay_reset_counter > 0)
7727 player->move_delay_reset_counter--;
7729 if (player->move_delay_reset_counter == 0)
7731 /* continue with normal speed after quickly moving through gate */
7732 HALVE_PLAYER_SPEED(player);
7734 /* be able to make the next move without delay */
7735 player->move_delay = 0;
7739 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7741 /* continue with normal speed after quickly moving through gate */
7742 HALVE_PLAYER_SPEED(player);
7744 /* be able to make the next move without delay */
7745 player->move_delay = 0;
7749 player->last_jx = jx;
7750 player->last_jy = jy;
7752 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7753 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7754 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7756 DrawPlayer(player); /* needed here only to cleanup last field */
7759 if (local_player->friends_still_needed == 0 ||
7760 IS_SP_ELEMENT(Feld[jx][jy]))
7761 player->LevelSolved = player->GameOver = TRUE;
7764 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7766 TestIfHeroTouchesBadThing(jx, jy);
7767 TestIfPlayerTouchesCustomElement(jx, jy);
7769 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7772 if (!player->active)
7776 if (tape.single_step && tape.recording && !tape.pausing &&
7777 !player->programmed_action)
7778 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7782 void ScrollScreen(struct PlayerInfo *player, int mode)
7784 static unsigned long screen_frame_counter = 0;
7786 if (mode == SCROLL_INIT)
7788 /* set scrolling step size according to actual player's moving speed */
7789 ScrollStepSize = TILEX / player->move_delay_value;
7791 screen_frame_counter = FrameCounter;
7792 ScreenMovDir = player->MovDir;
7793 ScreenMovPos = player->MovPos;
7794 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7797 else if (!FrameReached(&screen_frame_counter, 1))
7802 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7803 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7804 redraw_mask |= REDRAW_FIELD;
7807 ScreenMovDir = MV_NO_MOVING;
7810 void TestIfPlayerTouchesCustomElement(int x, int y)
7812 static int xy[4][2] =
7819 static int change_sides[4][2] =
7821 /* center side border side */
7822 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7823 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7824 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7825 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7827 static int touch_dir[4] =
7834 int center_element = Feld[x][y]; /* should always be non-moving! */
7837 for (i = 0; i < 4; i++)
7839 int xx = x + xy[i][0];
7840 int yy = y + xy[i][1];
7841 int center_side = change_sides[i][0];
7842 int border_side = change_sides[i][1];
7845 if (!IN_LEV_FIELD(xx, yy))
7848 if (IS_PLAYER(x, y))
7850 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7851 border_element = Feld[xx][yy]; /* may be moving! */
7852 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7853 border_element = Feld[xx][yy];
7854 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7855 border_element = MovingOrBlocked2Element(xx, yy);
7857 continue; /* center and border element do not touch */
7859 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7860 CE_OTHER_GETS_TOUCHED);
7861 CheckElementSideChange(xx, yy, border_element, border_side,
7862 CE_TOUCHED_BY_PLAYER, -1);
7864 else if (IS_PLAYER(xx, yy))
7866 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7868 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7870 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7871 continue; /* center and border element do not touch */
7874 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7875 CE_OTHER_GETS_TOUCHED);
7876 CheckElementSideChange(x, y, center_element, center_side,
7877 CE_TOUCHED_BY_PLAYER, -1);
7884 void TestIfElementTouchesCustomElement(int x, int y)
7886 static int xy[4][2] =
7893 static int change_sides[4][2] =
7895 /* center side border side */
7896 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7897 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7898 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7899 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7901 static int touch_dir[4] =
7908 boolean change_center_element = FALSE;
7909 int center_element_change_page = 0;
7910 int center_element = Feld[x][y]; /* should always be non-moving! */
7913 for (i = 0; i < 4; i++)
7915 int xx = x + xy[i][0];
7916 int yy = y + xy[i][1];
7917 int center_side = change_sides[i][0];
7918 int border_side = change_sides[i][1];
7921 if (!IN_LEV_FIELD(xx, yy))
7924 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7925 border_element = Feld[xx][yy]; /* may be moving! */
7926 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7927 border_element = Feld[xx][yy];
7928 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7929 border_element = MovingOrBlocked2Element(xx, yy);
7931 continue; /* center and border element do not touch */
7933 /* check for change of center element (but change it only once) */
7934 if (IS_CUSTOM_ELEMENT(center_element) &&
7935 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7936 !change_center_element)
7938 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7940 struct ElementChangeInfo *change =
7941 &element_info[center_element].change_page[j];
7943 if (change->can_change &&
7944 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7945 change->sides & border_side &&
7946 change->trigger_element == border_element)
7948 change_center_element = TRUE;
7949 center_element_change_page = j;
7956 /* check for change of border element */
7957 if (IS_CUSTOM_ELEMENT(border_element) &&
7958 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7960 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7962 struct ElementChangeInfo *change =
7963 &element_info[border_element].change_page[j];
7965 if (change->can_change &&
7966 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7967 change->sides & center_side &&
7968 change->trigger_element == center_element)
7970 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7971 CE_OTHER_IS_TOUCHING, j);
7978 if (change_center_element)
7979 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7980 CE_OTHER_IS_TOUCHING, center_element_change_page);
7983 void TestIfElementHitsCustomElement(int x, int y, int direction)
7985 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7986 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7987 int hitx = x + dx, hity = y + dy;
7988 int hitting_element = Feld[x][y];
7990 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
7991 !IS_FREE(hitx, hity) &&
7992 (!IS_MOVING(hitx, hity) ||
7993 MovDir[hitx][hity] != direction ||
7994 ABS(MovPos[hitx][hity]) <= TILEY / 2));
7997 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8001 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8005 CheckElementSideChange(x, y, hitting_element,
8006 direction, CE_HITTING_SOMETHING, -1);
8008 if (IN_LEV_FIELD(hitx, hity))
8010 static int opposite_directions[] =
8017 int move_dir_bit = MV_DIR_BIT(direction);
8018 int opposite_direction = opposite_directions[move_dir_bit];
8019 int hitting_side = direction;
8020 int touched_side = opposite_direction;
8021 int touched_element = MovingOrBlocked2Element(hitx, hity);
8023 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8024 MovDir[hitx][hity] != direction ||
8025 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8034 CheckElementSideChange(hitx, hity, touched_element,
8035 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8037 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8038 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8040 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8042 struct ElementChangeInfo *change =
8043 &element_info[hitting_element].change_page[i];
8045 if (change->can_change &&
8046 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8047 change->sides & touched_side &&
8048 change->trigger_element == touched_element)
8050 CheckElementSideChange(x, y, hitting_element,
8051 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8057 if (IS_CUSTOM_ELEMENT(touched_element) &&
8058 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8060 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8062 struct ElementChangeInfo *change =
8063 &element_info[touched_element].change_page[i];
8065 if (change->can_change &&
8066 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8067 change->sides & hitting_side &&
8068 change->trigger_element == hitting_element)
8070 CheckElementSideChange(hitx, hity, touched_element,
8071 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8080 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8082 int i, kill_x = -1, kill_y = -1;
8083 static int test_xy[4][2] =
8090 static int test_dir[4] =
8098 for (i = 0; i < 4; i++)
8100 int test_x, test_y, test_move_dir, test_element;
8102 test_x = good_x + test_xy[i][0];
8103 test_y = good_y + test_xy[i][1];
8104 if (!IN_LEV_FIELD(test_x, test_y))
8108 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8111 test_element = Feld[test_x][test_y];
8113 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8116 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8117 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8119 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8120 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8128 if (kill_x != -1 || kill_y != -1)
8130 if (IS_PLAYER(good_x, good_y))
8132 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8134 if (player->shield_deadly_time_left > 0)
8135 Bang(kill_x, kill_y);
8136 else if (!PLAYER_PROTECTED(good_x, good_y))
8140 Bang(good_x, good_y);
8144 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8146 int i, kill_x = -1, kill_y = -1;
8147 int bad_element = Feld[bad_x][bad_y];
8148 static int test_xy[4][2] =
8155 static int touch_dir[4] =
8162 static int test_dir[4] =
8170 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8173 for (i = 0; i < 4; i++)
8175 int test_x, test_y, test_move_dir, test_element;
8177 test_x = bad_x + test_xy[i][0];
8178 test_y = bad_y + test_xy[i][1];
8179 if (!IN_LEV_FIELD(test_x, test_y))
8183 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8185 test_element = Feld[test_x][test_y];
8187 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8188 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8190 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8191 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8193 /* good thing is player or penguin that does not move away */
8194 if (IS_PLAYER(test_x, test_y))
8196 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8198 if (bad_element == EL_ROBOT && player->is_moving)
8199 continue; /* robot does not kill player if he is moving */
8201 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8203 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8204 continue; /* center and border element do not touch */
8211 else if (test_element == EL_PENGUIN)
8220 if (kill_x != -1 || kill_y != -1)
8222 if (IS_PLAYER(kill_x, kill_y))
8224 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8226 if (player->shield_deadly_time_left > 0)
8228 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8232 Bang(kill_x, kill_y);
8236 void TestIfHeroTouchesBadThing(int x, int y)
8238 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8241 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8243 TestIfGoodThingHitsBadThing(x, y, move_dir);
8246 void TestIfBadThingTouchesHero(int x, int y)
8248 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8251 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8253 TestIfBadThingHitsGoodThing(x, y, move_dir);
8256 void TestIfFriendTouchesBadThing(int x, int y)
8258 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8261 void TestIfBadThingTouchesFriend(int x, int y)
8263 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8266 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8268 int i, kill_x = bad_x, kill_y = bad_y;
8269 static int xy[4][2] =
8277 for (i = 0; i < 4; i++)
8281 x = bad_x + xy[i][0];
8282 y = bad_y + xy[i][1];
8283 if (!IN_LEV_FIELD(x, y))
8286 element = Feld[x][y];
8287 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8288 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8296 if (kill_x != bad_x || kill_y != bad_y)
8300 void KillHero(struct PlayerInfo *player)
8302 int jx = player->jx, jy = player->jy;
8304 if (!player->active)
8307 /* remove accessible field at the player's position */
8308 Feld[jx][jy] = EL_EMPTY;
8310 /* deactivate shield (else Bang()/Explode() would not work right) */
8311 player->shield_normal_time_left = 0;
8312 player->shield_deadly_time_left = 0;
8318 static void KillHeroUnlessProtected(int x, int y)
8320 if (!PLAYER_PROTECTED(x, y))
8321 KillHero(PLAYERINFO(x, y));
8324 void BuryHero(struct PlayerInfo *player)
8326 int jx = player->jx, jy = player->jy;
8328 if (!player->active)
8332 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8334 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8336 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8338 player->GameOver = TRUE;
8342 void RemoveHero(struct PlayerInfo *player)
8344 int jx = player->jx, jy = player->jy;
8345 int i, found = FALSE;
8347 player->present = FALSE;
8348 player->active = FALSE;
8350 if (!ExplodeField[jx][jy])
8351 StorePlayer[jx][jy] = 0;
8353 for (i = 0; i < MAX_PLAYERS; i++)
8354 if (stored_player[i].active)
8358 AllPlayersGone = TRUE;
8365 =============================================================================
8366 checkDiagonalPushing()
8367 -----------------------------------------------------------------------------
8368 check if diagonal input device direction results in pushing of object
8369 (by checking if the alternative direction is walkable, diggable, ...)
8370 =============================================================================
8373 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8374 int x, int y, int real_dx, int real_dy)
8376 int jx, jy, dx, dy, xx, yy;
8378 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8381 /* diagonal direction: check alternative direction */
8386 xx = jx + (dx == 0 ? real_dx : 0);
8387 yy = jy + (dy == 0 ? real_dy : 0);
8389 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8393 =============================================================================
8395 -----------------------------------------------------------------------------
8396 x, y: field next to player (non-diagonal) to try to dig to
8397 real_dx, real_dy: direction as read from input device (can be diagonal)
8398 =============================================================================
8401 int DigField(struct PlayerInfo *player,
8402 int x, int y, int real_dx, int real_dy, int mode)
8404 static int change_sides[4] =
8406 CH_SIDE_RIGHT, /* moving left */
8407 CH_SIDE_LEFT, /* moving right */
8408 CH_SIDE_BOTTOM, /* moving up */
8409 CH_SIDE_TOP, /* moving down */
8411 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8412 int jx = player->jx, jy = player->jy;
8413 int dx = x - jx, dy = y - jy;
8414 int nextx = x + dx, nexty = y + dy;
8415 int move_direction = (dx == -1 ? MV_LEFT :
8416 dx == +1 ? MV_RIGHT :
8418 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8419 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8422 if (player->MovPos == 0)
8424 player->is_digging = FALSE;
8425 player->is_collecting = FALSE;
8428 if (player->MovPos == 0) /* last pushing move finished */
8429 player->is_pushing = FALSE;
8431 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8433 player->is_switching = FALSE;
8434 player->push_delay = 0;
8436 return MF_NO_ACTION;
8439 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8440 return MF_NO_ACTION;
8443 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8445 if (IS_TUBE(Feld[jx][jy]) ||
8446 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8450 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8451 int tube_leave_directions[][2] =
8453 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8454 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8455 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8456 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8457 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8458 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8459 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8460 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8461 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8462 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8463 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8464 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8467 while (tube_leave_directions[i][0] != tube_element)
8470 if (tube_leave_directions[i][0] == -1) /* should not happen */
8474 if (!(tube_leave_directions[i][1] & move_direction))
8475 return MF_NO_ACTION; /* tube has no opening in this direction */
8478 element = Feld[x][y];
8480 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8481 game.engine_version >= VERSION_IDENT(2,2,0,0))
8482 return MF_NO_ACTION;
8486 case EL_SP_PORT_LEFT:
8487 case EL_SP_PORT_RIGHT:
8489 case EL_SP_PORT_DOWN:
8490 case EL_SP_PORT_HORIZONTAL:
8491 case EL_SP_PORT_VERTICAL:
8492 case EL_SP_PORT_ANY:
8493 case EL_SP_GRAVITY_PORT_LEFT:
8494 case EL_SP_GRAVITY_PORT_RIGHT:
8495 case EL_SP_GRAVITY_PORT_UP:
8496 case EL_SP_GRAVITY_PORT_DOWN:
8498 element != EL_SP_PORT_LEFT &&
8499 element != EL_SP_GRAVITY_PORT_LEFT &&
8500 element != EL_SP_PORT_HORIZONTAL &&
8501 element != EL_SP_PORT_ANY) ||
8503 element != EL_SP_PORT_RIGHT &&
8504 element != EL_SP_GRAVITY_PORT_RIGHT &&
8505 element != EL_SP_PORT_HORIZONTAL &&
8506 element != EL_SP_PORT_ANY) ||
8508 element != EL_SP_PORT_UP &&
8509 element != EL_SP_GRAVITY_PORT_UP &&
8510 element != EL_SP_PORT_VERTICAL &&
8511 element != EL_SP_PORT_ANY) ||
8513 element != EL_SP_PORT_DOWN &&
8514 element != EL_SP_GRAVITY_PORT_DOWN &&
8515 element != EL_SP_PORT_VERTICAL &&
8516 element != EL_SP_PORT_ANY) ||
8517 !IN_LEV_FIELD(nextx, nexty) ||
8518 !IS_FREE(nextx, nexty))
8519 return MF_NO_ACTION;
8521 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8522 element == EL_SP_GRAVITY_PORT_RIGHT ||
8523 element == EL_SP_GRAVITY_PORT_UP ||
8524 element == EL_SP_GRAVITY_PORT_DOWN)
8525 game.gravity = !game.gravity;
8527 /* automatically move to the next field with double speed */
8528 player->programmed_action = move_direction;
8530 if (player->move_delay_reset_counter == 0)
8532 player->move_delay_reset_counter = 2; /* two double speed steps */
8534 DOUBLE_PLAYER_SPEED(player);
8537 DOUBLE_PLAYER_SPEED(player);
8539 player->move_delay_reset_counter = 2;
8542 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8546 case EL_TUBE_VERTICAL:
8547 case EL_TUBE_HORIZONTAL:
8548 case EL_TUBE_VERTICAL_LEFT:
8549 case EL_TUBE_VERTICAL_RIGHT:
8550 case EL_TUBE_HORIZONTAL_UP:
8551 case EL_TUBE_HORIZONTAL_DOWN:
8552 case EL_TUBE_LEFT_UP:
8553 case EL_TUBE_LEFT_DOWN:
8554 case EL_TUBE_RIGHT_UP:
8555 case EL_TUBE_RIGHT_DOWN:
8558 int tube_enter_directions[][2] =
8560 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8561 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8562 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8563 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8564 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8565 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8566 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8567 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8568 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8569 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8570 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8571 { -1, MV_NO_MOVING }
8574 while (tube_enter_directions[i][0] != element)
8577 if (tube_enter_directions[i][0] == -1) /* should not happen */
8581 if (!(tube_enter_directions[i][1] & move_direction))
8582 return MF_NO_ACTION; /* tube has no opening in this direction */
8584 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8590 if (IS_WALKABLE(element))
8592 int sound_action = ACTION_WALKING;
8594 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8596 if (!player->key[element - EL_GATE_1])
8597 return MF_NO_ACTION;
8599 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8601 if (!player->key[element - EL_GATE_1_GRAY])
8602 return MF_NO_ACTION;
8604 else if (element == EL_EXIT_OPEN ||
8605 element == EL_SP_EXIT_OPEN ||
8606 element == EL_SP_EXIT_OPENING)
8608 sound_action = ACTION_PASSING; /* player is passing exit */
8610 else if (element == EL_EMPTY)
8612 sound_action = ACTION_MOVING; /* nothing to walk on */
8615 /* play sound from background or player, whatever is available */
8616 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8617 PlayLevelSoundElementAction(x, y, element, sound_action);
8619 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8623 else if (IS_PASSABLE(element))
8625 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8626 return MF_NO_ACTION;
8629 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8630 return MF_NO_ACTION;
8633 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8635 if (!player->key[element - EL_EM_GATE_1])
8636 return MF_NO_ACTION;
8638 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8640 if (!player->key[element - EL_EM_GATE_1_GRAY])
8641 return MF_NO_ACTION;
8644 /* automatically move to the next field with double speed */
8645 player->programmed_action = move_direction;
8647 if (player->move_delay_reset_counter == 0)
8649 player->move_delay_reset_counter = 2; /* two double speed steps */
8651 DOUBLE_PLAYER_SPEED(player);
8654 DOUBLE_PLAYER_SPEED(player);
8656 player->move_delay_reset_counter = 2;
8659 PlayLevelSoundAction(x, y, ACTION_PASSING);
8663 else if (IS_DIGGABLE(element))
8667 if (mode != DF_SNAP)
8670 GfxElement[x][y] = GFX_ELEMENT(element);
8673 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8675 player->is_digging = TRUE;
8678 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8680 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8683 if (mode == DF_SNAP)
8684 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8689 else if (IS_COLLECTIBLE(element))
8693 if (mode != DF_SNAP)
8695 GfxElement[x][y] = element;
8696 player->is_collecting = TRUE;
8699 if (element == EL_SPEED_PILL)
8700 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8701 else if (element == EL_EXTRA_TIME && level.time > 0)
8704 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8706 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8708 player->shield_normal_time_left += 10;
8709 if (element == EL_SHIELD_DEADLY)
8710 player->shield_deadly_time_left += 10;
8712 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8714 if (player->inventory_size < MAX_INVENTORY_SIZE)
8715 player->inventory_element[player->inventory_size++] = element;
8717 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8718 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8720 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8722 player->dynabomb_count++;
8723 player->dynabombs_left++;
8725 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8727 player->dynabomb_size++;
8729 else if (element == EL_DYNABOMB_INCREASE_POWER)
8731 player->dynabomb_xl = TRUE;
8733 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8734 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8736 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8737 element - EL_KEY_1 : element - EL_EM_KEY_1);
8739 player->key[key_nr] = TRUE;
8741 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8742 el2edimg(EL_KEY_1 + key_nr));
8743 redraw_mask |= REDRAW_DOOR_1;
8745 else if (IS_ENVELOPE(element))
8748 player->show_envelope = element;
8750 ShowEnvelope(element - EL_ENVELOPE_1);
8753 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8757 for (i = 0; i < element_info[element].collect_count; i++)
8758 if (player->inventory_size < MAX_INVENTORY_SIZE)
8759 player->inventory_element[player->inventory_size++] = element;
8761 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8762 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8764 else if (element_info[element].collect_count > 0)
8766 local_player->gems_still_needed -=
8767 element_info[element].collect_count;
8768 if (local_player->gems_still_needed < 0)
8769 local_player->gems_still_needed = 0;
8771 DrawText(DX_EMERALDS, DY_EMERALDS,
8772 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8775 RaiseScoreElement(element);
8776 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8778 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8781 if (mode == DF_SNAP)
8782 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8787 else if (IS_PUSHABLE(element))
8789 if (mode == DF_SNAP && element != EL_BD_ROCK)
8790 return MF_NO_ACTION;
8792 if (CAN_FALL(element) && dy)
8793 return MF_NO_ACTION;
8795 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8796 !(element == EL_SPRING && use_spring_bug))
8797 return MF_NO_ACTION;
8800 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8801 ((move_direction & MV_VERTICAL &&
8802 ((element_info[element].move_pattern & MV_LEFT &&
8803 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8804 (element_info[element].move_pattern & MV_RIGHT &&
8805 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8806 (move_direction & MV_HORIZONTAL &&
8807 ((element_info[element].move_pattern & MV_UP &&
8808 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8809 (element_info[element].move_pattern & MV_DOWN &&
8810 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8811 return MF_NO_ACTION;
8815 /* do not push elements already moving away faster than player */
8816 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8817 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8818 return MF_NO_ACTION;
8820 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8821 return MF_NO_ACTION;
8825 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8827 if (player->push_delay_value == -1)
8828 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8830 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8832 if (!player->is_pushing)
8833 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8837 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8838 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8839 !player_is_pushing))
8840 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8843 if (!player->is_pushing &&
8844 game.engine_version >= VERSION_IDENT(2,2,0,7))
8845 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8849 printf("::: push delay: %ld [%d, %d] [%d]\n",
8850 player->push_delay_value, FrameCounter, game.engine_version,
8851 player->is_pushing);
8854 player->is_pushing = TRUE;
8856 if (!(IN_LEV_FIELD(nextx, nexty) &&
8857 (IS_FREE(nextx, nexty) ||
8858 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8859 IS_SB_ELEMENT(element)))))
8860 return MF_NO_ACTION;
8862 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8863 return MF_NO_ACTION;
8865 if (player->push_delay == 0) /* new pushing; restart delay */
8866 player->push_delay = FrameCounter;
8868 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8869 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8870 element != EL_SPRING && element != EL_BALLOON)
8872 /* make sure that there is no move delay before next try to push */
8873 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8874 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8876 return MF_NO_ACTION;
8880 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8883 if (IS_SB_ELEMENT(element))
8885 if (element == EL_SOKOBAN_FIELD_FULL)
8887 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8888 local_player->sokobanfields_still_needed++;
8891 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8893 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8894 local_player->sokobanfields_still_needed--;
8897 Feld[x][y] = EL_SOKOBAN_OBJECT;
8899 if (Back[x][y] == Back[nextx][nexty])
8900 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8901 else if (Back[x][y] != 0)
8902 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8905 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8908 if (local_player->sokobanfields_still_needed == 0 &&
8909 game.emulation == EMU_SOKOBAN)
8911 player->LevelSolved = player->GameOver = TRUE;
8912 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8916 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8918 InitMovingField(x, y, move_direction);
8919 GfxAction[x][y] = ACTION_PUSHING;
8921 if (mode == DF_SNAP)
8922 ContinueMoving(x, y);
8924 MovPos[x][y] = (dx != 0 ? dx : dy);
8926 Pushed[x][y] = TRUE;
8927 Pushed[nextx][nexty] = TRUE;
8929 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8930 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8932 player->push_delay_value = -1; /* get new value later */
8934 CheckTriggeredElementSideChange(x, y, element, dig_side,
8935 CE_OTHER_GETS_PUSHED);
8936 CheckElementSideChange(x, y, element, dig_side,
8937 CE_PUSHED_BY_PLAYER, -1);
8941 else if (IS_SWITCHABLE(element))
8943 if (PLAYER_SWITCHING(player, x, y))
8946 player->is_switching = TRUE;
8947 player->switch_x = x;
8948 player->switch_y = y;
8950 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8952 if (element == EL_ROBOT_WHEEL)
8954 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8958 DrawLevelField(x, y);
8960 else if (element == EL_SP_TERMINAL)
8964 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8966 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8968 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8969 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8972 else if (IS_BELT_SWITCH(element))
8974 ToggleBeltSwitch(x, y);
8976 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8977 element == EL_SWITCHGATE_SWITCH_DOWN)
8979 ToggleSwitchgateSwitch(x, y);
8981 else if (element == EL_LIGHT_SWITCH ||
8982 element == EL_LIGHT_SWITCH_ACTIVE)
8984 ToggleLightSwitch(x, y);
8987 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8988 SND_LIGHT_SWITCH_ACTIVATING :
8989 SND_LIGHT_SWITCH_DEACTIVATING);
8992 else if (element == EL_TIMEGATE_SWITCH)
8994 ActivateTimegateSwitch(x, y);
8996 else if (element == EL_BALLOON_SWITCH_LEFT ||
8997 element == EL_BALLOON_SWITCH_RIGHT ||
8998 element == EL_BALLOON_SWITCH_UP ||
8999 element == EL_BALLOON_SWITCH_DOWN ||
9000 element == EL_BALLOON_SWITCH_ANY)
9002 if (element == EL_BALLOON_SWITCH_ANY)
9003 game.balloon_dir = move_direction;
9005 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9006 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9007 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9008 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9011 else if (element == EL_LAMP)
9013 Feld[x][y] = EL_LAMP_ACTIVE;
9014 local_player->lights_still_needed--;
9016 DrawLevelField(x, y);
9018 else if (element == EL_TIME_ORB_FULL)
9020 Feld[x][y] = EL_TIME_ORB_EMPTY;
9022 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9024 DrawLevelField(x, y);
9027 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9035 if (!PLAYER_SWITCHING(player, x, y))
9037 player->is_switching = TRUE;
9038 player->switch_x = x;
9039 player->switch_y = y;
9041 CheckTriggeredElementSideChange(x, y, element, dig_side,
9042 CE_OTHER_IS_SWITCHING);
9043 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9046 CheckTriggeredElementSideChange(x, y, element, dig_side,
9047 CE_OTHER_GETS_PRESSED);
9048 CheckElementSideChange(x, y, element, dig_side,
9049 CE_PRESSED_BY_PLAYER, -1);
9052 return MF_NO_ACTION;
9055 player->push_delay = 0;
9057 if (Feld[x][y] != element) /* really digged/collected something */
9058 player->is_collecting = !player->is_digging;
9063 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9065 int jx = player->jx, jy = player->jy;
9066 int x = jx + dx, y = jy + dy;
9067 int snap_direction = (dx == -1 ? MV_LEFT :
9068 dx == +1 ? MV_RIGHT :
9070 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9072 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9075 if (!player->active || !IN_LEV_FIELD(x, y))
9083 if (player->MovPos == 0)
9084 player->is_pushing = FALSE;
9086 player->is_snapping = FALSE;
9088 if (player->MovPos == 0)
9090 player->is_moving = FALSE;
9091 player->is_digging = FALSE;
9092 player->is_collecting = FALSE;
9098 if (player->is_snapping)
9101 player->MovDir = snap_direction;
9103 player->is_moving = FALSE;
9104 player->is_digging = FALSE;
9105 player->is_collecting = FALSE;
9107 player->is_dropping = FALSE;
9109 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9112 player->is_snapping = TRUE;
9114 player->is_moving = FALSE;
9115 player->is_digging = FALSE;
9116 player->is_collecting = FALSE;
9118 DrawLevelField(x, y);
9124 boolean DropElement(struct PlayerInfo *player)
9126 int jx = player->jx, jy = player->jy;
9127 int old_element = Feld[jx][jy];
9130 /* check if player is active, not moving and ready to drop */
9131 if (!player->active || player->MovPos || player->drop_delay > 0)
9134 /* check if player has anything that can be dropped */
9135 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9138 /* check if anything can be dropped at the current position */
9139 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9142 /* collected custom elements can only be dropped on empty fields */
9143 if (player->inventory_size > 0 &&
9144 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9145 && old_element != EL_EMPTY)
9148 if (old_element != EL_EMPTY)
9149 Back[jx][jy] = old_element; /* store old element on this field */
9151 ResetGfxAnimation(jx, jy);
9152 ResetRandomAnimationValue(jx, jy);
9154 if (player->inventory_size > 0)
9156 player->inventory_size--;
9157 new_element = player->inventory_element[player->inventory_size];
9159 if (new_element == EL_DYNAMITE)
9160 new_element = EL_DYNAMITE_ACTIVE;
9161 else if (new_element == EL_SP_DISK_RED)
9162 new_element = EL_SP_DISK_RED_ACTIVE;
9164 Feld[jx][jy] = new_element;
9166 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9167 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9169 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9170 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9172 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9174 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9175 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9177 TestIfElementTouchesCustomElement(jx, jy);
9179 else /* player is dropping a dyna bomb */
9181 player->dynabombs_left--;
9182 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9184 Feld[jx][jy] = new_element;
9186 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9187 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9189 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9196 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9198 InitField(jx, jy, FALSE);
9199 if (CAN_MOVE(Feld[jx][jy]))
9203 new_element = Feld[jx][jy];
9205 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9206 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9208 int move_stepsize = element_info[new_element].move_stepsize;
9209 int direction, dx, dy, nextx, nexty;
9211 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9212 MovDir[jx][jy] = player->MovDir;
9214 direction = MovDir[jx][jy];
9215 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9216 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9220 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9223 WasJustMoving[jx][jy] = 3;
9225 InitMovingField(jx, jy, direction);
9226 ContinueMoving(jx, jy);
9231 Changed[jx][jy] = 0; /* allow another change */
9234 TestIfElementHitsCustomElement(jx, jy, direction);
9236 CheckElementSideChange(jx, jy, new_element,
9237 direction, CE_HITTING_SOMETHING, -1);
9241 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9245 player->drop_delay = 8 + 8 + 8;
9250 player->is_dropping = TRUE;
9256 /* ------------------------------------------------------------------------- */
9257 /* game sound playing functions */
9258 /* ------------------------------------------------------------------------- */
9260 static int *loop_sound_frame = NULL;
9261 static int *loop_sound_volume = NULL;
9263 void InitPlayLevelSound()
9265 int num_sounds = getSoundListSize();
9267 checked_free(loop_sound_frame);
9268 checked_free(loop_sound_volume);
9270 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9271 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9274 static void PlayLevelSound(int x, int y, int nr)
9276 int sx = SCREENX(x), sy = SCREENY(y);
9277 int volume, stereo_position;
9278 int max_distance = 8;
9279 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9281 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9282 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9285 if (!IN_LEV_FIELD(x, y) ||
9286 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9287 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9290 volume = SOUND_MAX_VOLUME;
9292 if (!IN_SCR_FIELD(sx, sy))
9294 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9295 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9297 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9300 stereo_position = (SOUND_MAX_LEFT +
9301 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9302 (SCR_FIELDX + 2 * max_distance));
9304 if (IS_LOOP_SOUND(nr))
9306 /* This assures that quieter loop sounds do not overwrite louder ones,
9307 while restarting sound volume comparison with each new game frame. */
9309 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9312 loop_sound_volume[nr] = volume;
9313 loop_sound_frame[nr] = FrameCounter;
9316 PlaySoundExt(nr, volume, stereo_position, type);
9319 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9321 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9322 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9323 y < LEVELY(BY1) ? LEVELY(BY1) :
9324 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9328 static void PlayLevelSoundAction(int x, int y, int action)
9330 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9333 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9335 int sound_effect = element_info[element].sound[action];
9337 if (sound_effect != SND_UNDEFINED)
9338 PlayLevelSound(x, y, sound_effect);
9341 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9344 int sound_effect = element_info[element].sound[action];
9346 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9347 PlayLevelSound(x, y, sound_effect);
9350 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9352 int sound_effect = element_info[Feld[x][y]].sound[action];
9354 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9355 PlayLevelSound(x, y, sound_effect);
9358 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9360 int sound_effect = element_info[Feld[x][y]].sound[action];
9362 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9363 StopSound(sound_effect);
9366 static void PlayLevelMusic()
9368 if (levelset.music[level_nr] != MUS_UNDEFINED)
9369 PlayMusic(levelset.music[level_nr]); /* from config file */
9371 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9374 void RaiseScore(int value)
9376 local_player->score += value;
9377 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9380 void RaiseScoreElement(int element)
9386 case EL_EMERALD_YELLOW:
9387 case EL_EMERALD_RED:
9388 case EL_EMERALD_PURPLE:
9389 case EL_SP_INFOTRON:
9390 RaiseScore(level.score[SC_EMERALD]);
9393 RaiseScore(level.score[SC_DIAMOND]);
9396 RaiseScore(level.score[SC_CRYSTAL]);
9399 RaiseScore(level.score[SC_PEARL]);
9402 case EL_BD_BUTTERFLY:
9403 case EL_SP_ELECTRON:
9404 RaiseScore(level.score[SC_BUG]);
9408 case EL_SP_SNIKSNAK:
9409 RaiseScore(level.score[SC_SPACESHIP]);
9412 case EL_DARK_YAMYAM:
9413 RaiseScore(level.score[SC_YAMYAM]);
9416 RaiseScore(level.score[SC_ROBOT]);
9419 RaiseScore(level.score[SC_PACMAN]);
9422 RaiseScore(level.score[SC_NUT]);
9425 case EL_SP_DISK_RED:
9426 case EL_DYNABOMB_INCREASE_NUMBER:
9427 case EL_DYNABOMB_INCREASE_SIZE:
9428 case EL_DYNABOMB_INCREASE_POWER:
9429 RaiseScore(level.score[SC_DYNAMITE]);
9431 case EL_SHIELD_NORMAL:
9432 case EL_SHIELD_DEADLY:
9433 RaiseScore(level.score[SC_SHIELD]);
9436 RaiseScore(level.score[SC_TIME_BONUS]);
9442 RaiseScore(level.score[SC_KEY]);
9445 RaiseScore(element_info[element].collect_score);
9450 void RequestQuitGame(boolean ask_if_really_quit)
9452 if (AllPlayersGone ||
9453 !ask_if_really_quit ||
9454 level_editor_test_game ||
9455 Request("Do you really want to quit the game ?",
9456 REQ_ASK | REQ_STAY_CLOSED))
9458 #if defined(PLATFORM_UNIX)
9459 if (options.network)
9460 SendToServer_StopPlaying();
9464 game_status = GAME_MODE_MAIN;
9470 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9475 /* ---------- new game button stuff ---------------------------------------- */
9477 /* graphic position values for game buttons */
9478 #define GAME_BUTTON_XSIZE 30
9479 #define GAME_BUTTON_YSIZE 30
9480 #define GAME_BUTTON_XPOS 5
9481 #define GAME_BUTTON_YPOS 215
9482 #define SOUND_BUTTON_XPOS 5
9483 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9485 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9486 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9487 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9488 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9489 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9490 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9497 } gamebutton_info[NUM_GAME_BUTTONS] =
9500 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9505 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9510 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9515 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9516 SOUND_CTRL_ID_MUSIC,
9517 "background music on/off"
9520 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9521 SOUND_CTRL_ID_LOOPS,
9522 "sound loops on/off"
9525 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9526 SOUND_CTRL_ID_SIMPLE,
9527 "normal sounds on/off"
9531 void CreateGameButtons()
9535 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9537 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9538 struct GadgetInfo *gi;
9541 unsigned long event_mask;
9542 int gd_xoffset, gd_yoffset;
9543 int gd_x1, gd_x2, gd_y1, gd_y2;
9546 gd_xoffset = gamebutton_info[i].x;
9547 gd_yoffset = gamebutton_info[i].y;
9548 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9549 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9551 if (id == GAME_CTRL_ID_STOP ||
9552 id == GAME_CTRL_ID_PAUSE ||
9553 id == GAME_CTRL_ID_PLAY)
9555 button_type = GD_TYPE_NORMAL_BUTTON;
9557 event_mask = GD_EVENT_RELEASED;
9558 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9559 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9563 button_type = GD_TYPE_CHECK_BUTTON;
9565 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9566 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9567 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9568 event_mask = GD_EVENT_PRESSED;
9569 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9570 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9573 gi = CreateGadget(GDI_CUSTOM_ID, id,
9574 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9575 GDI_X, DX + gd_xoffset,
9576 GDI_Y, DY + gd_yoffset,
9577 GDI_WIDTH, GAME_BUTTON_XSIZE,
9578 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9579 GDI_TYPE, button_type,
9580 GDI_STATE, GD_BUTTON_UNPRESSED,
9581 GDI_CHECKED, checked,
9582 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9583 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9584 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9585 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9586 GDI_EVENT_MASK, event_mask,
9587 GDI_CALLBACK_ACTION, HandleGameButtons,
9591 Error(ERR_EXIT, "cannot create gadget");
9593 game_gadget[id] = gi;
9597 void FreeGameButtons()
9601 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9602 FreeGadget(game_gadget[i]);
9605 static void MapGameButtons()
9609 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9610 MapGadget(game_gadget[i]);
9613 void UnmapGameButtons()
9617 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9618 UnmapGadget(game_gadget[i]);
9621 static void HandleGameButtons(struct GadgetInfo *gi)
9623 int id = gi->custom_id;
9625 if (game_status != GAME_MODE_PLAYING)
9630 case GAME_CTRL_ID_STOP:
9631 RequestQuitGame(TRUE);
9634 case GAME_CTRL_ID_PAUSE:
9635 if (options.network)
9637 #if defined(PLATFORM_UNIX)
9639 SendToServer_ContinuePlaying();
9641 SendToServer_PausePlaying();
9645 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9648 case GAME_CTRL_ID_PLAY:
9651 #if defined(PLATFORM_UNIX)
9652 if (options.network)
9653 SendToServer_ContinuePlaying();
9657 tape.pausing = FALSE;
9658 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9663 case SOUND_CTRL_ID_MUSIC:
9664 if (setup.sound_music)
9666 setup.sound_music = FALSE;
9669 else if (audio.music_available)
9671 setup.sound = setup.sound_music = TRUE;
9673 SetAudioMode(setup.sound);
9679 case SOUND_CTRL_ID_LOOPS:
9680 if (setup.sound_loops)
9681 setup.sound_loops = FALSE;
9682 else if (audio.loops_available)
9684 setup.sound = setup.sound_loops = TRUE;
9685 SetAudioMode(setup.sound);
9689 case SOUND_CTRL_ID_SIMPLE:
9690 if (setup.sound_simple)
9691 setup.sound_simple = FALSE;
9692 else if (audio.sound_available)
9694 setup.sound = setup.sound_simple = TRUE;
9695 SetAudioMode(setup.sound);