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)
6488 printf("::: [%d %d %d %d] [%d %d]\n",
6489 left, right, up, down, button1, button2);
6495 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6499 snapped = SnapField(player, dx, dy);
6503 dropped = DropElement(player);
6505 moved = MovePlayer(player, dx, dy);
6508 if (tape.single_step && tape.recording && !tape.pausing)
6510 if (button1 || (dropped && !moved))
6512 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6513 SnapField(player, 0, 0); /* stop snapping */
6517 SetPlayerWaiting(player, FALSE);
6520 return player_action;
6522 stored_player_action[player->index_nr] = player_action;
6528 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6531 /* no actions for this player (no input at player's configured device) */
6533 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6534 SnapField(player, 0, 0);
6535 CheckGravityMovement(player);
6537 if (player->MovPos == 0)
6538 SetPlayerWaiting(player, TRUE);
6540 if (player->MovPos == 0) /* needed for tape.playing */
6541 player->is_moving = FALSE;
6543 player->is_dropping = FALSE;
6549 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6551 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6553 TapeRecordAction(stored_player_action);
6554 num_stored_actions = 0;
6561 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6563 static byte stored_player_action[MAX_PLAYERS];
6564 static int num_stored_actions = 0;
6565 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6566 int left = player_action & JOY_LEFT;
6567 int right = player_action & JOY_RIGHT;
6568 int up = player_action & JOY_UP;
6569 int down = player_action & JOY_DOWN;
6570 int button1 = player_action & JOY_BUTTON_1;
6571 int button2 = player_action & JOY_BUTTON_2;
6572 int dx = (left ? -1 : right ? 1 : 0);
6573 int dy = (up ? -1 : down ? 1 : 0);
6575 stored_player_action[player->index_nr] = 0;
6576 num_stored_actions++;
6578 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6580 if (!player->active || tape.pausing)
6585 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6588 snapped = SnapField(player, dx, dy);
6592 dropped = DropElement(player);
6594 moved = MovePlayer(player, dx, dy);
6597 if (tape.single_step && tape.recording && !tape.pausing)
6599 if (button1 || (dropped && !moved))
6601 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6602 SnapField(player, 0, 0); /* stop snapping */
6606 stored_player_action[player->index_nr] = player_action;
6610 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6612 /* no actions for this player (no input at player's configured device) */
6614 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6615 SnapField(player, 0, 0);
6616 CheckGravityMovement(player);
6618 if (player->MovPos == 0)
6619 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6621 if (player->MovPos == 0) /* needed for tape.playing */
6622 player->is_moving = FALSE;
6625 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6627 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6629 TapeRecordAction(stored_player_action);
6630 num_stored_actions = 0;
6637 static unsigned long action_delay = 0;
6638 unsigned long action_delay_value;
6639 int magic_wall_x = 0, magic_wall_y = 0;
6640 int i, x, y, element, graphic;
6641 byte *recorded_player_action;
6642 byte summarized_player_action = 0;
6644 byte tape_action[MAX_PLAYERS];
6647 if (game_status != GAME_MODE_PLAYING)
6650 action_delay_value =
6651 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6653 if (tape.playing && tape.index_search && !tape.pausing)
6654 action_delay_value = 0;
6656 /* ---------- main game synchronization point ---------- */
6658 WaitUntilDelayReached(&action_delay, action_delay_value);
6660 if (network_playing && !network_player_action_received)
6664 printf("DEBUG: try to get network player actions in time\n");
6668 #if defined(PLATFORM_UNIX)
6669 /* last chance to get network player actions without main loop delay */
6673 if (game_status != GAME_MODE_PLAYING)
6676 if (!network_player_action_received)
6680 printf("DEBUG: failed to get network player actions in time\n");
6691 printf("::: getting new tape action [%d]\n", FrameCounter);
6694 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6696 for (i = 0; i < MAX_PLAYERS; i++)
6698 summarized_player_action |= stored_player[i].action;
6700 if (!network_playing)
6701 stored_player[i].effective_action = stored_player[i].action;
6704 #if defined(PLATFORM_UNIX)
6705 if (network_playing)
6706 SendToServer_MovePlayer(summarized_player_action);
6709 if (!options.network && !setup.team_mode)
6710 local_player->effective_action = summarized_player_action;
6712 for (i = 0; i < MAX_PLAYERS; i++)
6714 int actual_player_action = stored_player[i].effective_action;
6716 if (stored_player[i].programmed_action)
6717 actual_player_action = stored_player[i].programmed_action;
6719 if (recorded_player_action)
6720 actual_player_action = recorded_player_action[i];
6722 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6724 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6725 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6727 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6732 TapeRecordAction(tape_action);
6735 network_player_action_received = FALSE;
6737 ScrollScreen(NULL, SCROLL_GO_ON);
6743 for (i = 0; i < MAX_PLAYERS; i++)
6744 stored_player[i].Frame++;
6748 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6750 for (i = 0; i < MAX_PLAYERS; i++)
6752 struct PlayerInfo *player = &stored_player[i];
6756 if (player->active && player->is_pushing && player->is_moving &&
6759 ContinueMoving(x, y);
6761 /* continue moving after pushing (this is actually a bug) */
6762 if (!IS_MOVING(x, y))
6771 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6773 Changed[x][y] = CE_BITMASK_DEFAULT;
6774 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6777 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6779 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6780 printf("GameActions(): This should never happen!\n");
6782 ChangePage[x][y] = -1;
6787 if (WasJustMoving[x][y] > 0)
6788 WasJustMoving[x][y]--;
6789 if (WasJustFalling[x][y] > 0)
6790 WasJustFalling[x][y]--;
6795 /* reset finished pushing action (not done in ContinueMoving() to allow
6796 continous pushing animation for elements with zero push delay) */
6797 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6799 ResetGfxAnimation(x, y);
6800 DrawLevelField(x, y);
6805 if (IS_BLOCKED(x, y))
6809 Blocked2Moving(x, y, &oldx, &oldy);
6810 if (!IS_MOVING(oldx, oldy))
6812 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6813 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6814 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6815 printf("GameActions(): This should never happen!\n");
6821 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6823 element = Feld[x][y];
6825 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6827 graphic = el2img(element);
6833 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6835 element = graphic = 0;
6839 if (graphic_info[graphic].anim_global_sync)
6840 GfxFrame[x][y] = FrameCounter;
6842 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6843 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6844 ResetRandomAnimationValue(x, y);
6846 SetRandomAnimationValue(x, y);
6849 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6852 if (IS_INACTIVE(element))
6854 if (IS_ANIMATED(graphic))
6855 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6861 /* this may take place after moving, so 'element' may have changed */
6863 if (IS_CHANGING(x, y))
6865 if (IS_CHANGING(x, y) &&
6866 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6870 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6871 element_info[element].event_page_nr[CE_DELAY]);
6873 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6876 element = Feld[x][y];
6877 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6881 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6886 element = Feld[x][y];
6887 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6889 if (element == EL_MOLE)
6890 printf("::: %d, %d, %d [%d]\n",
6891 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6895 if (element == EL_YAMYAM)
6896 printf("::: %d, %d, %d\n",
6897 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6901 if (IS_ANIMATED(graphic) &&
6905 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6908 if (element == EL_BUG)
6909 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6913 if (element == EL_MOLE)
6914 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6918 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6919 EdelsteinFunkeln(x, y);
6921 else if ((element == EL_ACID ||
6922 element == EL_EXIT_OPEN ||
6923 element == EL_SP_EXIT_OPEN ||
6924 element == EL_SP_TERMINAL ||
6925 element == EL_SP_TERMINAL_ACTIVE ||
6926 element == EL_EXTRA_TIME ||
6927 element == EL_SHIELD_NORMAL ||
6928 element == EL_SHIELD_DEADLY) &&
6929 IS_ANIMATED(graphic))
6930 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6931 else if (IS_MOVING(x, y))
6932 ContinueMoving(x, y);
6933 else if (IS_ACTIVE_BOMB(element))
6934 CheckDynamite(x, y);
6936 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6937 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6939 else if (element == EL_AMOEBA_GROWING)
6940 AmoebeWaechst(x, y);
6941 else if (element == EL_AMOEBA_SHRINKING)
6942 AmoebaDisappearing(x, y);
6944 #if !USE_NEW_AMOEBA_CODE
6945 else if (IS_AMOEBALIVE(element))
6946 AmoebeAbleger(x, y);
6949 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6951 else if (element == EL_EXIT_CLOSED)
6953 else if (element == EL_SP_EXIT_CLOSED)
6955 else if (element == EL_EXPANDABLE_WALL_GROWING)
6957 else if (element == EL_EXPANDABLE_WALL ||
6958 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6959 element == EL_EXPANDABLE_WALL_VERTICAL ||
6960 element == EL_EXPANDABLE_WALL_ANY)
6962 else if (element == EL_FLAMES)
6963 CheckForDragon(x, y);
6965 else if (IS_AUTO_CHANGING(element))
6966 ChangeElement(x, y);
6968 else if (element == EL_EXPLOSION)
6969 ; /* drawing of correct explosion animation is handled separately */
6970 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6971 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6974 /* this may take place after moving, so 'element' may have changed */
6975 if (IS_AUTO_CHANGING(Feld[x][y]))
6976 ChangeElement(x, y);
6979 if (IS_BELT_ACTIVE(element))
6980 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6982 if (game.magic_wall_active)
6984 int jx = local_player->jx, jy = local_player->jy;
6986 /* play the element sound at the position nearest to the player */
6987 if ((element == EL_MAGIC_WALL_FULL ||
6988 element == EL_MAGIC_WALL_ACTIVE ||
6989 element == EL_MAGIC_WALL_EMPTYING ||
6990 element == EL_BD_MAGIC_WALL_FULL ||
6991 element == EL_BD_MAGIC_WALL_ACTIVE ||
6992 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6993 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7001 #if USE_NEW_AMOEBA_CODE
7002 /* new experimental amoeba growth stuff */
7004 if (!(FrameCounter % 8))
7007 static unsigned long random = 1684108901;
7009 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7012 x = (random >> 10) % lev_fieldx;
7013 y = (random >> 20) % lev_fieldy;
7015 x = RND(lev_fieldx);
7016 y = RND(lev_fieldy);
7018 element = Feld[x][y];
7020 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7021 if (!IS_PLAYER(x,y) &&
7022 (element == EL_EMPTY ||
7023 element == EL_SAND ||
7024 element == EL_QUICKSAND_EMPTY ||
7025 element == EL_ACID_SPLASH_LEFT ||
7026 element == EL_ACID_SPLASH_RIGHT))
7028 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7029 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7030 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7031 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7032 Feld[x][y] = EL_AMOEBA_DROP;
7035 random = random * 129 + 1;
7041 if (game.explosions_delayed)
7044 game.explosions_delayed = FALSE;
7046 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7048 element = Feld[x][y];
7050 if (ExplodeField[x][y])
7051 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7052 else if (element == EL_EXPLOSION)
7053 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7055 ExplodeField[x][y] = EX_NO_EXPLOSION;
7058 game.explosions_delayed = TRUE;
7061 if (game.magic_wall_active)
7063 if (!(game.magic_wall_time_left % 4))
7065 int element = Feld[magic_wall_x][magic_wall_y];
7067 if (element == EL_BD_MAGIC_WALL_FULL ||
7068 element == EL_BD_MAGIC_WALL_ACTIVE ||
7069 element == EL_BD_MAGIC_WALL_EMPTYING)
7070 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7072 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7075 if (game.magic_wall_time_left > 0)
7077 game.magic_wall_time_left--;
7078 if (!game.magic_wall_time_left)
7080 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7082 element = Feld[x][y];
7084 if (element == EL_MAGIC_WALL_ACTIVE ||
7085 element == EL_MAGIC_WALL_FULL)
7087 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7088 DrawLevelField(x, y);
7090 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7091 element == EL_BD_MAGIC_WALL_FULL)
7093 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7094 DrawLevelField(x, y);
7098 game.magic_wall_active = FALSE;
7103 if (game.light_time_left > 0)
7105 game.light_time_left--;
7107 if (game.light_time_left == 0)
7108 RedrawAllLightSwitchesAndInvisibleElements();
7111 if (game.timegate_time_left > 0)
7113 game.timegate_time_left--;
7115 if (game.timegate_time_left == 0)
7116 CloseAllOpenTimegates();
7119 for (i = 0; i < MAX_PLAYERS; i++)
7121 struct PlayerInfo *player = &stored_player[i];
7123 if (SHIELD_ON(player))
7125 if (player->shield_deadly_time_left)
7126 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7127 else if (player->shield_normal_time_left)
7128 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7132 if (TimeFrames >= FRAMES_PER_SECOND)
7137 for (i = 0; i < MAX_PLAYERS; i++)
7139 struct PlayerInfo *player = &stored_player[i];
7141 if (SHIELD_ON(player))
7143 player->shield_normal_time_left--;
7145 if (player->shield_deadly_time_left > 0)
7146 player->shield_deadly_time_left--;
7150 if (tape.recording || tape.playing)
7151 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7157 if (TimeLeft <= 10 && setup.time_limit)
7158 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7160 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7162 if (!TimeLeft && setup.time_limit)
7163 for (i = 0; i < MAX_PLAYERS; i++)
7164 KillHero(&stored_player[i]);
7166 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7167 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7171 PlayAllPlayersSound();
7173 if (options.debug) /* calculate frames per second */
7175 static unsigned long fps_counter = 0;
7176 static int fps_frames = 0;
7177 unsigned long fps_delay_ms = Counter() - fps_counter;
7181 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7183 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7186 fps_counter = Counter();
7189 redraw_mask |= REDRAW_FPS;
7193 if (stored_player[0].jx != stored_player[0].last_jx ||
7194 stored_player[0].jy != stored_player[0].last_jy)
7195 printf("::: %d, %d, %d, %d, %d\n",
7196 stored_player[0].MovDir,
7197 stored_player[0].MovPos,
7198 stored_player[0].GfxPos,
7199 stored_player[0].Frame,
7200 stored_player[0].StepFrame);
7207 for (i = 0; i < MAX_PLAYERS; i++)
7210 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7212 stored_player[i].Frame += move_frames;
7214 if (stored_player[i].MovPos != 0)
7215 stored_player[i].StepFrame += move_frames;
7217 if (stored_player[i].drop_delay > 0)
7218 stored_player[i].drop_delay--;
7223 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7225 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7227 local_player->show_envelope = 0;
7232 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7234 int min_x = x, min_y = y, max_x = x, max_y = y;
7237 for (i = 0; i < MAX_PLAYERS; i++)
7239 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7241 if (!stored_player[i].active || &stored_player[i] == player)
7244 min_x = MIN(min_x, jx);
7245 min_y = MIN(min_y, jy);
7246 max_x = MAX(max_x, jx);
7247 max_y = MAX(max_y, jy);
7250 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7253 static boolean AllPlayersInVisibleScreen()
7257 for (i = 0; i < MAX_PLAYERS; i++)
7259 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7261 if (!stored_player[i].active)
7264 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7271 void ScrollLevel(int dx, int dy)
7273 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7276 BlitBitmap(drawto_field, drawto_field,
7277 FX + TILEX * (dx == -1) - softscroll_offset,
7278 FY + TILEY * (dy == -1) - softscroll_offset,
7279 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7280 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7281 FX + TILEX * (dx == 1) - softscroll_offset,
7282 FY + TILEY * (dy == 1) - softscroll_offset);
7286 x = (dx == 1 ? BX1 : BX2);
7287 for (y = BY1; y <= BY2; y++)
7288 DrawScreenField(x, y);
7293 y = (dy == 1 ? BY1 : BY2);
7294 for (x = BX1; x <= BX2; x++)
7295 DrawScreenField(x, y);
7298 redraw_mask |= REDRAW_FIELD;
7301 static void CheckGravityMovement(struct PlayerInfo *player)
7303 if (game.gravity && !player->programmed_action)
7305 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7306 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7308 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7309 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7310 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7311 int jx = player->jx, jy = player->jy;
7312 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7313 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7314 int new_jx = jx + dx, new_jy = jy + dy;
7315 boolean field_under_player_is_free =
7316 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7317 boolean player_is_moving_to_valid_field =
7318 (IN_LEV_FIELD(new_jx, new_jy) &&
7319 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7320 Feld[new_jx][new_jy] == EL_SAND));
7321 /* !!! extend EL_SAND to anything diggable !!! */
7323 if (field_under_player_is_free &&
7324 !player_is_moving_to_valid_field &&
7325 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7326 player->programmed_action = MV_DOWN;
7332 -----------------------------------------------------------------------------
7333 dx, dy: direction (non-diagonal) to try to move the player to
7334 real_dx, real_dy: direction as read from input device (can be diagonal)
7337 boolean MovePlayerOneStep(struct PlayerInfo *player,
7338 int dx, int dy, int real_dx, int real_dy)
7341 static int change_sides[4][2] =
7343 /* enter side leave side */
7344 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7345 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7346 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7347 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7349 int move_direction = (dx == -1 ? MV_LEFT :
7350 dx == +1 ? MV_RIGHT :
7352 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7353 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7354 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7356 int jx = player->jx, jy = player->jy;
7357 int new_jx = jx + dx, new_jy = jy + dy;
7361 if (!player->active || (!dx && !dy))
7362 return MF_NO_ACTION;
7364 player->MovDir = (dx < 0 ? MV_LEFT :
7367 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7369 if (!IN_LEV_FIELD(new_jx, new_jy))
7370 return MF_NO_ACTION;
7372 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7373 return MF_NO_ACTION;
7376 element = MovingOrBlocked2Element(new_jx, new_jy);
7378 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7381 if (DONT_RUN_INTO(element))
7383 if (element == EL_ACID && dx == 0 && dy == 1)
7386 Feld[jx][jy] = EL_PLAYER_1;
7387 InitMovingField(jx, jy, MV_DOWN);
7388 Store[jx][jy] = EL_ACID;
7389 ContinueMoving(jx, jy);
7393 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7398 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7399 if (can_move != MF_MOVING)
7402 /* check if DigField() has caused relocation of the player */
7403 if (player->jx != jx || player->jy != jy)
7404 return MF_NO_ACTION;
7406 StorePlayer[jx][jy] = 0;
7407 player->last_jx = jx;
7408 player->last_jy = jy;
7409 player->jx = new_jx;
7410 player->jy = new_jy;
7411 StorePlayer[new_jx][new_jy] = player->element_nr;
7414 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7416 player->step_counter++;
7418 player->drop_delay = 0;
7420 PlayerVisit[jx][jy] = FrameCounter;
7422 ScrollPlayer(player, SCROLL_INIT);
7425 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7427 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7428 CE_OTHER_GETS_LEFT);
7429 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7430 CE_LEFT_BY_PLAYER, -1);
7433 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7435 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7436 enter_side, CE_OTHER_GETS_ENTERED);
7437 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7438 CE_ENTERED_BY_PLAYER, -1);
7445 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7447 int jx = player->jx, jy = player->jy;
7448 int old_jx = jx, old_jy = jy;
7449 int moved = MF_NO_ACTION;
7452 if (!player->active)
7457 if (player->MovPos == 0)
7459 player->is_moving = FALSE;
7460 player->is_digging = FALSE;
7461 player->is_collecting = FALSE;
7462 player->is_snapping = FALSE;
7463 player->is_pushing = FALSE;
7469 if (!player->active || (!dx && !dy))
7474 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7478 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7479 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7483 /* remove the last programmed player action */
7484 player->programmed_action = 0;
7488 /* should only happen if pre-1.2 tape recordings are played */
7489 /* this is only for backward compatibility */
7491 int original_move_delay_value = player->move_delay_value;
7494 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7498 /* scroll remaining steps with finest movement resolution */
7499 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7501 while (player->MovPos)
7503 ScrollPlayer(player, SCROLL_GO_ON);
7504 ScrollScreen(NULL, SCROLL_GO_ON);
7510 player->move_delay_value = original_move_delay_value;
7513 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7515 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7516 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7520 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7521 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7527 if (moved & MF_MOVING && !ScreenMovPos &&
7528 (player == local_player || !options.network))
7530 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7531 int offset = (setup.scroll_delay ? 3 : 0);
7533 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7535 /* actual player has left the screen -- scroll in that direction */
7536 if (jx != old_jx) /* player has moved horizontally */
7537 scroll_x += (jx - old_jx);
7538 else /* player has moved vertically */
7539 scroll_y += (jy - old_jy);
7543 if (jx != old_jx) /* player has moved horizontally */
7545 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7546 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7547 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7549 /* don't scroll over playfield boundaries */
7550 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7551 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7553 /* don't scroll more than one field at a time */
7554 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7556 /* don't scroll against the player's moving direction */
7557 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7558 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7559 scroll_x = old_scroll_x;
7561 else /* player has moved vertically */
7563 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7564 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7565 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7567 /* don't scroll over playfield boundaries */
7568 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7569 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7571 /* don't scroll more than one field at a time */
7572 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7574 /* don't scroll against the player's moving direction */
7575 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7576 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7577 scroll_y = old_scroll_y;
7581 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7583 if (!options.network && !AllPlayersInVisibleScreen())
7585 scroll_x = old_scroll_x;
7586 scroll_y = old_scroll_y;
7590 ScrollScreen(player, SCROLL_INIT);
7591 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7598 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7600 if (!(moved & MF_MOVING) && !player->is_pushing)
7605 player->StepFrame = 0;
7607 if (moved & MF_MOVING)
7609 if (old_jx != jx && old_jy == jy)
7610 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7611 else if (old_jx == jx && old_jy != jy)
7612 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7614 DrawLevelField(jx, jy); /* for "crumbled sand" */
7616 player->last_move_dir = player->MovDir;
7617 player->is_moving = TRUE;
7619 player->is_snapping = FALSE;
7623 player->is_switching = FALSE;
7626 player->is_dropping = FALSE;
7631 static int change_sides[4][2] =
7633 /* enter side leave side */
7634 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7635 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7636 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7637 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7639 int move_direction = player->MovDir;
7640 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7641 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7644 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7646 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7647 leave_side, CE_OTHER_GETS_LEFT);
7648 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7649 leave_side, CE_LEFT_BY_PLAYER, -1);
7652 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7654 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7655 enter_side, CE_OTHER_GETS_ENTERED);
7656 CheckElementSideChange(jx, jy, Feld[jx][jy],
7657 enter_side, CE_ENTERED_BY_PLAYER, -1);
7668 CheckGravityMovement(player);
7671 player->last_move_dir = MV_NO_MOVING;
7673 player->is_moving = FALSE;
7676 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7678 TestIfHeroTouchesBadThing(jx, jy);
7679 TestIfPlayerTouchesCustomElement(jx, jy);
7682 if (!player->active)
7688 void ScrollPlayer(struct PlayerInfo *player, int mode)
7690 int jx = player->jx, jy = player->jy;
7691 int last_jx = player->last_jx, last_jy = player->last_jy;
7692 int move_stepsize = TILEX / player->move_delay_value;
7694 if (!player->active || !player->MovPos)
7697 if (mode == SCROLL_INIT)
7699 player->actual_frame_counter = FrameCounter;
7700 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7702 if (Feld[last_jx][last_jy] == EL_EMPTY)
7703 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7710 else if (!FrameReached(&player->actual_frame_counter, 1))
7713 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7714 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7716 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7717 Feld[last_jx][last_jy] = EL_EMPTY;
7719 /* before DrawPlayer() to draw correct player graphic for this case */
7720 if (player->MovPos == 0)
7721 CheckGravityMovement(player);
7724 DrawPlayer(player); /* needed here only to cleanup last field */
7727 if (player->MovPos == 0) /* player reached destination field */
7730 if (player->move_delay_reset_counter > 0)
7732 player->move_delay_reset_counter--;
7734 if (player->move_delay_reset_counter == 0)
7736 /* continue with normal speed after quickly moving through gate */
7737 HALVE_PLAYER_SPEED(player);
7739 /* be able to make the next move without delay */
7740 player->move_delay = 0;
7744 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7746 /* continue with normal speed after quickly moving through gate */
7747 HALVE_PLAYER_SPEED(player);
7749 /* be able to make the next move without delay */
7750 player->move_delay = 0;
7754 player->last_jx = jx;
7755 player->last_jy = jy;
7757 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7758 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7759 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7761 DrawPlayer(player); /* needed here only to cleanup last field */
7764 if (local_player->friends_still_needed == 0 ||
7765 IS_SP_ELEMENT(Feld[jx][jy]))
7766 player->LevelSolved = player->GameOver = TRUE;
7769 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7771 TestIfHeroTouchesBadThing(jx, jy);
7772 TestIfPlayerTouchesCustomElement(jx, jy);
7774 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7777 if (!player->active)
7781 if (tape.single_step && tape.recording && !tape.pausing &&
7782 !player->programmed_action)
7783 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7787 void ScrollScreen(struct PlayerInfo *player, int mode)
7789 static unsigned long screen_frame_counter = 0;
7791 if (mode == SCROLL_INIT)
7793 /* set scrolling step size according to actual player's moving speed */
7794 ScrollStepSize = TILEX / player->move_delay_value;
7796 screen_frame_counter = FrameCounter;
7797 ScreenMovDir = player->MovDir;
7798 ScreenMovPos = player->MovPos;
7799 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7802 else if (!FrameReached(&screen_frame_counter, 1))
7807 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7808 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7809 redraw_mask |= REDRAW_FIELD;
7812 ScreenMovDir = MV_NO_MOVING;
7815 void TestIfPlayerTouchesCustomElement(int x, int y)
7817 static int xy[4][2] =
7824 static int change_sides[4][2] =
7826 /* center side border side */
7827 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7828 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7829 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7830 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7832 static int touch_dir[4] =
7839 int center_element = Feld[x][y]; /* should always be non-moving! */
7842 for (i = 0; i < 4; i++)
7844 int xx = x + xy[i][0];
7845 int yy = y + xy[i][1];
7846 int center_side = change_sides[i][0];
7847 int border_side = change_sides[i][1];
7850 if (!IN_LEV_FIELD(xx, yy))
7853 if (IS_PLAYER(x, y))
7855 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7856 border_element = Feld[xx][yy]; /* may be moving! */
7857 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7858 border_element = Feld[xx][yy];
7859 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7860 border_element = MovingOrBlocked2Element(xx, yy);
7862 continue; /* center and border element do not touch */
7864 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7865 CE_OTHER_GETS_TOUCHED);
7866 CheckElementSideChange(xx, yy, border_element, border_side,
7867 CE_TOUCHED_BY_PLAYER, -1);
7869 else if (IS_PLAYER(xx, yy))
7871 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7873 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7875 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7876 continue; /* center and border element do not touch */
7879 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7880 CE_OTHER_GETS_TOUCHED);
7881 CheckElementSideChange(x, y, center_element, center_side,
7882 CE_TOUCHED_BY_PLAYER, -1);
7889 void TestIfElementTouchesCustomElement(int x, int y)
7891 static int xy[4][2] =
7898 static int change_sides[4][2] =
7900 /* center side border side */
7901 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7902 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7903 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7904 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7906 static int touch_dir[4] =
7913 boolean change_center_element = FALSE;
7914 int center_element_change_page = 0;
7915 int center_element = Feld[x][y]; /* should always be non-moving! */
7918 for (i = 0; i < 4; i++)
7920 int xx = x + xy[i][0];
7921 int yy = y + xy[i][1];
7922 int center_side = change_sides[i][0];
7923 int border_side = change_sides[i][1];
7926 if (!IN_LEV_FIELD(xx, yy))
7929 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7930 border_element = Feld[xx][yy]; /* may be moving! */
7931 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7932 border_element = Feld[xx][yy];
7933 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7934 border_element = MovingOrBlocked2Element(xx, yy);
7936 continue; /* center and border element do not touch */
7938 /* check for change of center element (but change it only once) */
7939 if (IS_CUSTOM_ELEMENT(center_element) &&
7940 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7941 !change_center_element)
7943 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7945 struct ElementChangeInfo *change =
7946 &element_info[center_element].change_page[j];
7948 if (change->can_change &&
7949 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7950 change->sides & border_side &&
7951 change->trigger_element == border_element)
7953 change_center_element = TRUE;
7954 center_element_change_page = j;
7961 /* check for change of border element */
7962 if (IS_CUSTOM_ELEMENT(border_element) &&
7963 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7965 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7967 struct ElementChangeInfo *change =
7968 &element_info[border_element].change_page[j];
7970 if (change->can_change &&
7971 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7972 change->sides & center_side &&
7973 change->trigger_element == center_element)
7975 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7976 CE_OTHER_IS_TOUCHING, j);
7983 if (change_center_element)
7984 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7985 CE_OTHER_IS_TOUCHING, center_element_change_page);
7988 void TestIfElementHitsCustomElement(int x, int y, int direction)
7990 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7991 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7992 int hitx = x + dx, hity = y + dy;
7993 int hitting_element = Feld[x][y];
7995 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
7996 !IS_FREE(hitx, hity) &&
7997 (!IS_MOVING(hitx, hity) ||
7998 MovDir[hitx][hity] != direction ||
7999 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8002 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8006 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8010 CheckElementSideChange(x, y, hitting_element,
8011 direction, CE_HITTING_SOMETHING, -1);
8013 if (IN_LEV_FIELD(hitx, hity))
8015 static int opposite_directions[] =
8022 int move_dir_bit = MV_DIR_BIT(direction);
8023 int opposite_direction = opposite_directions[move_dir_bit];
8024 int hitting_side = direction;
8025 int touched_side = opposite_direction;
8026 int touched_element = MovingOrBlocked2Element(hitx, hity);
8028 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8029 MovDir[hitx][hity] != direction ||
8030 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8039 CheckElementSideChange(hitx, hity, touched_element,
8040 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8042 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8043 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8045 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8047 struct ElementChangeInfo *change =
8048 &element_info[hitting_element].change_page[i];
8050 if (change->can_change &&
8051 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8052 change->sides & touched_side &&
8053 change->trigger_element == touched_element)
8055 CheckElementSideChange(x, y, hitting_element,
8056 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8062 if (IS_CUSTOM_ELEMENT(touched_element) &&
8063 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8065 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8067 struct ElementChangeInfo *change =
8068 &element_info[touched_element].change_page[i];
8070 if (change->can_change &&
8071 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8072 change->sides & hitting_side &&
8073 change->trigger_element == hitting_element)
8075 CheckElementSideChange(hitx, hity, touched_element,
8076 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8085 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8087 int i, kill_x = -1, kill_y = -1;
8088 static int test_xy[4][2] =
8095 static int test_dir[4] =
8103 for (i = 0; i < 4; i++)
8105 int test_x, test_y, test_move_dir, test_element;
8107 test_x = good_x + test_xy[i][0];
8108 test_y = good_y + test_xy[i][1];
8109 if (!IN_LEV_FIELD(test_x, test_y))
8113 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8116 test_element = Feld[test_x][test_y];
8118 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8121 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8122 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8124 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8125 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8133 if (kill_x != -1 || kill_y != -1)
8135 if (IS_PLAYER(good_x, good_y))
8137 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8139 if (player->shield_deadly_time_left > 0)
8140 Bang(kill_x, kill_y);
8141 else if (!PLAYER_PROTECTED(good_x, good_y))
8145 Bang(good_x, good_y);
8149 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8151 int i, kill_x = -1, kill_y = -1;
8152 int bad_element = Feld[bad_x][bad_y];
8153 static int test_xy[4][2] =
8160 static int touch_dir[4] =
8167 static int test_dir[4] =
8175 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8178 for (i = 0; i < 4; i++)
8180 int test_x, test_y, test_move_dir, test_element;
8182 test_x = bad_x + test_xy[i][0];
8183 test_y = bad_y + test_xy[i][1];
8184 if (!IN_LEV_FIELD(test_x, test_y))
8188 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8190 test_element = Feld[test_x][test_y];
8192 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8193 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8195 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8196 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8198 /* good thing is player or penguin that does not move away */
8199 if (IS_PLAYER(test_x, test_y))
8201 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8203 if (bad_element == EL_ROBOT && player->is_moving)
8204 continue; /* robot does not kill player if he is moving */
8206 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8208 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8209 continue; /* center and border element do not touch */
8216 else if (test_element == EL_PENGUIN)
8225 if (kill_x != -1 || kill_y != -1)
8227 if (IS_PLAYER(kill_x, kill_y))
8229 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8231 if (player->shield_deadly_time_left > 0)
8233 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8237 Bang(kill_x, kill_y);
8241 void TestIfHeroTouchesBadThing(int x, int y)
8243 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8246 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8248 TestIfGoodThingHitsBadThing(x, y, move_dir);
8251 void TestIfBadThingTouchesHero(int x, int y)
8253 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8256 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8258 TestIfBadThingHitsGoodThing(x, y, move_dir);
8261 void TestIfFriendTouchesBadThing(int x, int y)
8263 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8266 void TestIfBadThingTouchesFriend(int x, int y)
8268 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8271 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8273 int i, kill_x = bad_x, kill_y = bad_y;
8274 static int xy[4][2] =
8282 for (i = 0; i < 4; i++)
8286 x = bad_x + xy[i][0];
8287 y = bad_y + xy[i][1];
8288 if (!IN_LEV_FIELD(x, y))
8291 element = Feld[x][y];
8292 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8293 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8301 if (kill_x != bad_x || kill_y != bad_y)
8305 void KillHero(struct PlayerInfo *player)
8307 int jx = player->jx, jy = player->jy;
8309 if (!player->active)
8312 /* remove accessible field at the player's position */
8313 Feld[jx][jy] = EL_EMPTY;
8315 /* deactivate shield (else Bang()/Explode() would not work right) */
8316 player->shield_normal_time_left = 0;
8317 player->shield_deadly_time_left = 0;
8323 static void KillHeroUnlessProtected(int x, int y)
8325 if (!PLAYER_PROTECTED(x, y))
8326 KillHero(PLAYERINFO(x, y));
8329 void BuryHero(struct PlayerInfo *player)
8331 int jx = player->jx, jy = player->jy;
8333 if (!player->active)
8337 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8339 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8341 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8343 player->GameOver = TRUE;
8347 void RemoveHero(struct PlayerInfo *player)
8349 int jx = player->jx, jy = player->jy;
8350 int i, found = FALSE;
8352 player->present = FALSE;
8353 player->active = FALSE;
8355 if (!ExplodeField[jx][jy])
8356 StorePlayer[jx][jy] = 0;
8358 for (i = 0; i < MAX_PLAYERS; i++)
8359 if (stored_player[i].active)
8363 AllPlayersGone = TRUE;
8370 =============================================================================
8371 checkDiagonalPushing()
8372 -----------------------------------------------------------------------------
8373 check if diagonal input device direction results in pushing of object
8374 (by checking if the alternative direction is walkable, diggable, ...)
8375 =============================================================================
8378 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8379 int x, int y, int real_dx, int real_dy)
8381 int jx, jy, dx, dy, xx, yy;
8383 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8386 /* diagonal direction: check alternative direction */
8391 xx = jx + (dx == 0 ? real_dx : 0);
8392 yy = jy + (dy == 0 ? real_dy : 0);
8394 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8398 =============================================================================
8400 -----------------------------------------------------------------------------
8401 x, y: field next to player (non-diagonal) to try to dig to
8402 real_dx, real_dy: direction as read from input device (can be diagonal)
8403 =============================================================================
8406 int DigField(struct PlayerInfo *player,
8407 int x, int y, int real_dx, int real_dy, int mode)
8409 static int change_sides[4] =
8411 CH_SIDE_RIGHT, /* moving left */
8412 CH_SIDE_LEFT, /* moving right */
8413 CH_SIDE_BOTTOM, /* moving up */
8414 CH_SIDE_TOP, /* moving down */
8416 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8417 int jx = player->jx, jy = player->jy;
8418 int dx = x - jx, dy = y - jy;
8419 int nextx = x + dx, nexty = y + dy;
8420 int move_direction = (dx == -1 ? MV_LEFT :
8421 dx == +1 ? MV_RIGHT :
8423 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8424 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8427 if (player->MovPos == 0)
8429 player->is_digging = FALSE;
8430 player->is_collecting = FALSE;
8433 if (player->MovPos == 0) /* last pushing move finished */
8434 player->is_pushing = FALSE;
8436 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8438 player->is_switching = FALSE;
8439 player->push_delay = 0;
8441 return MF_NO_ACTION;
8444 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8445 return MF_NO_ACTION;
8448 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8450 if (IS_TUBE(Feld[jx][jy]) ||
8451 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8455 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8456 int tube_leave_directions[][2] =
8458 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8459 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8460 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8461 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8462 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8463 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8464 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8465 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8466 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8467 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8468 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8469 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8472 while (tube_leave_directions[i][0] != tube_element)
8475 if (tube_leave_directions[i][0] == -1) /* should not happen */
8479 if (!(tube_leave_directions[i][1] & move_direction))
8480 return MF_NO_ACTION; /* tube has no opening in this direction */
8483 element = Feld[x][y];
8485 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8486 game.engine_version >= VERSION_IDENT(2,2,0,0))
8487 return MF_NO_ACTION;
8491 case EL_SP_PORT_LEFT:
8492 case EL_SP_PORT_RIGHT:
8494 case EL_SP_PORT_DOWN:
8495 case EL_SP_PORT_HORIZONTAL:
8496 case EL_SP_PORT_VERTICAL:
8497 case EL_SP_PORT_ANY:
8498 case EL_SP_GRAVITY_PORT_LEFT:
8499 case EL_SP_GRAVITY_PORT_RIGHT:
8500 case EL_SP_GRAVITY_PORT_UP:
8501 case EL_SP_GRAVITY_PORT_DOWN:
8503 element != EL_SP_PORT_LEFT &&
8504 element != EL_SP_GRAVITY_PORT_LEFT &&
8505 element != EL_SP_PORT_HORIZONTAL &&
8506 element != EL_SP_PORT_ANY) ||
8508 element != EL_SP_PORT_RIGHT &&
8509 element != EL_SP_GRAVITY_PORT_RIGHT &&
8510 element != EL_SP_PORT_HORIZONTAL &&
8511 element != EL_SP_PORT_ANY) ||
8513 element != EL_SP_PORT_UP &&
8514 element != EL_SP_GRAVITY_PORT_UP &&
8515 element != EL_SP_PORT_VERTICAL &&
8516 element != EL_SP_PORT_ANY) ||
8518 element != EL_SP_PORT_DOWN &&
8519 element != EL_SP_GRAVITY_PORT_DOWN &&
8520 element != EL_SP_PORT_VERTICAL &&
8521 element != EL_SP_PORT_ANY) ||
8522 !IN_LEV_FIELD(nextx, nexty) ||
8523 !IS_FREE(nextx, nexty))
8524 return MF_NO_ACTION;
8526 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8527 element == EL_SP_GRAVITY_PORT_RIGHT ||
8528 element == EL_SP_GRAVITY_PORT_UP ||
8529 element == EL_SP_GRAVITY_PORT_DOWN)
8530 game.gravity = !game.gravity;
8532 /* automatically move to the next field with double speed */
8533 player->programmed_action = move_direction;
8535 if (player->move_delay_reset_counter == 0)
8537 player->move_delay_reset_counter = 2; /* two double speed steps */
8539 DOUBLE_PLAYER_SPEED(player);
8542 player->move_delay_reset_counter = 2;
8544 DOUBLE_PLAYER_SPEED(player);
8547 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8551 case EL_TUBE_VERTICAL:
8552 case EL_TUBE_HORIZONTAL:
8553 case EL_TUBE_VERTICAL_LEFT:
8554 case EL_TUBE_VERTICAL_RIGHT:
8555 case EL_TUBE_HORIZONTAL_UP:
8556 case EL_TUBE_HORIZONTAL_DOWN:
8557 case EL_TUBE_LEFT_UP:
8558 case EL_TUBE_LEFT_DOWN:
8559 case EL_TUBE_RIGHT_UP:
8560 case EL_TUBE_RIGHT_DOWN:
8563 int tube_enter_directions[][2] =
8565 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8566 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8567 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8568 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8569 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8570 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8571 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8572 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8573 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8574 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8575 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8576 { -1, MV_NO_MOVING }
8579 while (tube_enter_directions[i][0] != element)
8582 if (tube_enter_directions[i][0] == -1) /* should not happen */
8586 if (!(tube_enter_directions[i][1] & move_direction))
8587 return MF_NO_ACTION; /* tube has no opening in this direction */
8589 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8595 if (IS_WALKABLE(element))
8597 int sound_action = ACTION_WALKING;
8599 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8601 if (!player->key[element - EL_GATE_1])
8602 return MF_NO_ACTION;
8604 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8606 if (!player->key[element - EL_GATE_1_GRAY])
8607 return MF_NO_ACTION;
8609 else if (element == EL_EXIT_OPEN ||
8610 element == EL_SP_EXIT_OPEN ||
8611 element == EL_SP_EXIT_OPENING)
8613 sound_action = ACTION_PASSING; /* player is passing exit */
8615 else if (element == EL_EMPTY)
8617 sound_action = ACTION_MOVING; /* nothing to walk on */
8620 /* play sound from background or player, whatever is available */
8621 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8622 PlayLevelSoundElementAction(x, y, element, sound_action);
8624 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8628 else if (IS_PASSABLE(element))
8630 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8631 return MF_NO_ACTION;
8634 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8635 return MF_NO_ACTION;
8638 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8640 if (!player->key[element - EL_EM_GATE_1])
8641 return MF_NO_ACTION;
8643 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8645 if (!player->key[element - EL_EM_GATE_1_GRAY])
8646 return MF_NO_ACTION;
8649 /* automatically move to the next field with double speed */
8650 player->programmed_action = move_direction;
8652 if (player->move_delay_reset_counter == 0)
8654 player->move_delay_reset_counter = 2; /* two double speed steps */
8656 DOUBLE_PLAYER_SPEED(player);
8659 player->move_delay_reset_counter = 2;
8661 DOUBLE_PLAYER_SPEED(player);
8664 PlayLevelSoundAction(x, y, ACTION_PASSING);
8668 else if (IS_DIGGABLE(element))
8672 if (mode != DF_SNAP)
8675 GfxElement[x][y] = GFX_ELEMENT(element);
8678 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8680 player->is_digging = TRUE;
8683 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8685 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8688 if (mode == DF_SNAP)
8689 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8694 else if (IS_COLLECTIBLE(element))
8698 if (mode != DF_SNAP)
8700 GfxElement[x][y] = element;
8701 player->is_collecting = TRUE;
8704 if (element == EL_SPEED_PILL)
8705 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8706 else if (element == EL_EXTRA_TIME && level.time > 0)
8709 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8711 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8713 player->shield_normal_time_left += 10;
8714 if (element == EL_SHIELD_DEADLY)
8715 player->shield_deadly_time_left += 10;
8717 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8719 if (player->inventory_size < MAX_INVENTORY_SIZE)
8720 player->inventory_element[player->inventory_size++] = element;
8722 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8723 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8725 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8727 player->dynabomb_count++;
8728 player->dynabombs_left++;
8730 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8732 player->dynabomb_size++;
8734 else if (element == EL_DYNABOMB_INCREASE_POWER)
8736 player->dynabomb_xl = TRUE;
8738 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8739 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8741 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8742 element - EL_KEY_1 : element - EL_EM_KEY_1);
8744 player->key[key_nr] = TRUE;
8746 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8747 el2edimg(EL_KEY_1 + key_nr));
8748 redraw_mask |= REDRAW_DOOR_1;
8750 else if (IS_ENVELOPE(element))
8753 player->show_envelope = element;
8755 ShowEnvelope(element - EL_ENVELOPE_1);
8758 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8762 for (i = 0; i < element_info[element].collect_count; i++)
8763 if (player->inventory_size < MAX_INVENTORY_SIZE)
8764 player->inventory_element[player->inventory_size++] = element;
8766 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8767 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8769 else if (element_info[element].collect_count > 0)
8771 local_player->gems_still_needed -=
8772 element_info[element].collect_count;
8773 if (local_player->gems_still_needed < 0)
8774 local_player->gems_still_needed = 0;
8776 DrawText(DX_EMERALDS, DY_EMERALDS,
8777 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8780 RaiseScoreElement(element);
8781 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8783 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8786 if (mode == DF_SNAP)
8787 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8792 else if (IS_PUSHABLE(element))
8794 if (mode == DF_SNAP && element != EL_BD_ROCK)
8795 return MF_NO_ACTION;
8797 if (CAN_FALL(element) && dy)
8798 return MF_NO_ACTION;
8800 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8801 !(element == EL_SPRING && use_spring_bug))
8802 return MF_NO_ACTION;
8805 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8806 ((move_direction & MV_VERTICAL &&
8807 ((element_info[element].move_pattern & MV_LEFT &&
8808 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8809 (element_info[element].move_pattern & MV_RIGHT &&
8810 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8811 (move_direction & MV_HORIZONTAL &&
8812 ((element_info[element].move_pattern & MV_UP &&
8813 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8814 (element_info[element].move_pattern & MV_DOWN &&
8815 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8816 return MF_NO_ACTION;
8820 /* do not push elements already moving away faster than player */
8821 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8822 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8823 return MF_NO_ACTION;
8825 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8826 return MF_NO_ACTION;
8830 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8832 if (player->push_delay_value == -1)
8833 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8835 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8837 if (!player->is_pushing)
8838 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8842 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8843 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8844 !player_is_pushing))
8845 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8848 if (!player->is_pushing &&
8849 game.engine_version >= VERSION_IDENT(2,2,0,7))
8850 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8854 printf("::: push delay: %ld [%d, %d] [%d]\n",
8855 player->push_delay_value, FrameCounter, game.engine_version,
8856 player->is_pushing);
8859 player->is_pushing = TRUE;
8861 if (!(IN_LEV_FIELD(nextx, nexty) &&
8862 (IS_FREE(nextx, nexty) ||
8863 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8864 IS_SB_ELEMENT(element)))))
8865 return MF_NO_ACTION;
8867 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8868 return MF_NO_ACTION;
8870 if (player->push_delay == 0) /* new pushing; restart delay */
8871 player->push_delay = FrameCounter;
8873 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8874 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8875 element != EL_SPRING && element != EL_BALLOON)
8877 /* make sure that there is no move delay before next try to push */
8878 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8879 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8881 return MF_NO_ACTION;
8885 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8888 if (IS_SB_ELEMENT(element))
8890 if (element == EL_SOKOBAN_FIELD_FULL)
8892 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8893 local_player->sokobanfields_still_needed++;
8896 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8898 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8899 local_player->sokobanfields_still_needed--;
8902 Feld[x][y] = EL_SOKOBAN_OBJECT;
8904 if (Back[x][y] == Back[nextx][nexty])
8905 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8906 else if (Back[x][y] != 0)
8907 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8910 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8913 if (local_player->sokobanfields_still_needed == 0 &&
8914 game.emulation == EMU_SOKOBAN)
8916 player->LevelSolved = player->GameOver = TRUE;
8917 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8921 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8923 InitMovingField(x, y, move_direction);
8924 GfxAction[x][y] = ACTION_PUSHING;
8926 if (mode == DF_SNAP)
8927 ContinueMoving(x, y);
8929 MovPos[x][y] = (dx != 0 ? dx : dy);
8931 Pushed[x][y] = TRUE;
8932 Pushed[nextx][nexty] = TRUE;
8934 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8935 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8937 player->push_delay_value = -1; /* get new value later */
8939 CheckTriggeredElementSideChange(x, y, element, dig_side,
8940 CE_OTHER_GETS_PUSHED);
8941 CheckElementSideChange(x, y, element, dig_side,
8942 CE_PUSHED_BY_PLAYER, -1);
8946 else if (IS_SWITCHABLE(element))
8948 if (PLAYER_SWITCHING(player, x, y))
8951 player->is_switching = TRUE;
8952 player->switch_x = x;
8953 player->switch_y = y;
8955 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8957 if (element == EL_ROBOT_WHEEL)
8959 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8963 DrawLevelField(x, y);
8965 else if (element == EL_SP_TERMINAL)
8969 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8971 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8973 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8974 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8977 else if (IS_BELT_SWITCH(element))
8979 ToggleBeltSwitch(x, y);
8981 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8982 element == EL_SWITCHGATE_SWITCH_DOWN)
8984 ToggleSwitchgateSwitch(x, y);
8986 else if (element == EL_LIGHT_SWITCH ||
8987 element == EL_LIGHT_SWITCH_ACTIVE)
8989 ToggleLightSwitch(x, y);
8992 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8993 SND_LIGHT_SWITCH_ACTIVATING :
8994 SND_LIGHT_SWITCH_DEACTIVATING);
8997 else if (element == EL_TIMEGATE_SWITCH)
8999 ActivateTimegateSwitch(x, y);
9001 else if (element == EL_BALLOON_SWITCH_LEFT ||
9002 element == EL_BALLOON_SWITCH_RIGHT ||
9003 element == EL_BALLOON_SWITCH_UP ||
9004 element == EL_BALLOON_SWITCH_DOWN ||
9005 element == EL_BALLOON_SWITCH_ANY)
9007 if (element == EL_BALLOON_SWITCH_ANY)
9008 game.balloon_dir = move_direction;
9010 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9011 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9012 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9013 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9016 else if (element == EL_LAMP)
9018 Feld[x][y] = EL_LAMP_ACTIVE;
9019 local_player->lights_still_needed--;
9021 DrawLevelField(x, y);
9023 else if (element == EL_TIME_ORB_FULL)
9025 Feld[x][y] = EL_TIME_ORB_EMPTY;
9027 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9029 DrawLevelField(x, y);
9032 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9040 if (!PLAYER_SWITCHING(player, x, y))
9042 player->is_switching = TRUE;
9043 player->switch_x = x;
9044 player->switch_y = y;
9046 CheckTriggeredElementSideChange(x, y, element, dig_side,
9047 CE_OTHER_IS_SWITCHING);
9048 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9051 CheckTriggeredElementSideChange(x, y, element, dig_side,
9052 CE_OTHER_GETS_PRESSED);
9053 CheckElementSideChange(x, y, element, dig_side,
9054 CE_PRESSED_BY_PLAYER, -1);
9057 return MF_NO_ACTION;
9060 player->push_delay = 0;
9062 if (Feld[x][y] != element) /* really digged/collected something */
9063 player->is_collecting = !player->is_digging;
9068 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9070 int jx = player->jx, jy = player->jy;
9071 int x = jx + dx, y = jy + dy;
9072 int snap_direction = (dx == -1 ? MV_LEFT :
9073 dx == +1 ? MV_RIGHT :
9075 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9077 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9080 if (!player->active || !IN_LEV_FIELD(x, y))
9088 if (player->MovPos == 0)
9089 player->is_pushing = FALSE;
9091 player->is_snapping = FALSE;
9093 if (player->MovPos == 0)
9095 player->is_moving = FALSE;
9096 player->is_digging = FALSE;
9097 player->is_collecting = FALSE;
9103 if (player->is_snapping)
9106 player->MovDir = snap_direction;
9109 if (player->MovPos == 0)
9112 player->is_moving = FALSE;
9113 player->is_digging = FALSE;
9114 player->is_collecting = FALSE;
9117 player->is_dropping = FALSE;
9119 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9122 player->is_snapping = TRUE;
9125 if (player->MovPos == 0)
9128 player->is_moving = FALSE;
9129 player->is_digging = FALSE;
9130 player->is_collecting = FALSE;
9133 DrawLevelField(x, y);
9139 boolean DropElement(struct PlayerInfo *player)
9141 int jx = player->jx, jy = player->jy;
9142 int old_element = Feld[jx][jy];
9145 /* check if player is active, not moving and ready to drop */
9146 if (!player->active || player->MovPos || player->drop_delay > 0)
9149 /* check if player has anything that can be dropped */
9150 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9153 /* check if anything can be dropped at the current position */
9154 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9157 /* collected custom elements can only be dropped on empty fields */
9158 if (player->inventory_size > 0 &&
9159 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9160 && old_element != EL_EMPTY)
9163 if (old_element != EL_EMPTY)
9164 Back[jx][jy] = old_element; /* store old element on this field */
9166 ResetGfxAnimation(jx, jy);
9167 ResetRandomAnimationValue(jx, jy);
9169 if (player->inventory_size > 0)
9171 player->inventory_size--;
9172 new_element = player->inventory_element[player->inventory_size];
9174 if (new_element == EL_DYNAMITE)
9175 new_element = EL_DYNAMITE_ACTIVE;
9176 else if (new_element == EL_SP_DISK_RED)
9177 new_element = EL_SP_DISK_RED_ACTIVE;
9179 Feld[jx][jy] = new_element;
9181 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9182 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9184 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9185 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9187 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9189 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9190 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9192 TestIfElementTouchesCustomElement(jx, jy);
9194 else /* player is dropping a dyna bomb */
9196 player->dynabombs_left--;
9197 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9199 Feld[jx][jy] = new_element;
9201 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9202 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9204 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9211 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9213 InitField(jx, jy, FALSE);
9214 if (CAN_MOVE(Feld[jx][jy]))
9218 new_element = Feld[jx][jy];
9220 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9221 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9223 int move_stepsize = element_info[new_element].move_stepsize;
9224 int direction, dx, dy, nextx, nexty;
9226 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9227 MovDir[jx][jy] = player->MovDir;
9229 direction = MovDir[jx][jy];
9230 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9231 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9235 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9238 WasJustMoving[jx][jy] = 3;
9240 InitMovingField(jx, jy, direction);
9241 ContinueMoving(jx, jy);
9246 Changed[jx][jy] = 0; /* allow another change */
9249 TestIfElementHitsCustomElement(jx, jy, direction);
9251 CheckElementSideChange(jx, jy, new_element,
9252 direction, CE_HITTING_SOMETHING, -1);
9256 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9260 player->drop_delay = 8 + 8 + 8;
9265 player->is_dropping = TRUE;
9271 /* ------------------------------------------------------------------------- */
9272 /* game sound playing functions */
9273 /* ------------------------------------------------------------------------- */
9275 static int *loop_sound_frame = NULL;
9276 static int *loop_sound_volume = NULL;
9278 void InitPlayLevelSound()
9280 int num_sounds = getSoundListSize();
9282 checked_free(loop_sound_frame);
9283 checked_free(loop_sound_volume);
9285 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9286 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9289 static void PlayLevelSound(int x, int y, int nr)
9291 int sx = SCREENX(x), sy = SCREENY(y);
9292 int volume, stereo_position;
9293 int max_distance = 8;
9294 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9296 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9297 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9300 if (!IN_LEV_FIELD(x, y) ||
9301 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9302 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9305 volume = SOUND_MAX_VOLUME;
9307 if (!IN_SCR_FIELD(sx, sy))
9309 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9310 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9312 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9315 stereo_position = (SOUND_MAX_LEFT +
9316 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9317 (SCR_FIELDX + 2 * max_distance));
9319 if (IS_LOOP_SOUND(nr))
9321 /* This assures that quieter loop sounds do not overwrite louder ones,
9322 while restarting sound volume comparison with each new game frame. */
9324 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9327 loop_sound_volume[nr] = volume;
9328 loop_sound_frame[nr] = FrameCounter;
9331 PlaySoundExt(nr, volume, stereo_position, type);
9334 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9336 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9337 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9338 y < LEVELY(BY1) ? LEVELY(BY1) :
9339 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9343 static void PlayLevelSoundAction(int x, int y, int action)
9345 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9348 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9350 int sound_effect = element_info[element].sound[action];
9352 if (sound_effect != SND_UNDEFINED)
9353 PlayLevelSound(x, y, sound_effect);
9356 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9359 int sound_effect = element_info[element].sound[action];
9361 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9362 PlayLevelSound(x, y, sound_effect);
9365 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9367 int sound_effect = element_info[Feld[x][y]].sound[action];
9369 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9370 PlayLevelSound(x, y, sound_effect);
9373 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9375 int sound_effect = element_info[Feld[x][y]].sound[action];
9377 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9378 StopSound(sound_effect);
9381 static void PlayLevelMusic()
9383 if (levelset.music[level_nr] != MUS_UNDEFINED)
9384 PlayMusic(levelset.music[level_nr]); /* from config file */
9386 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9389 void RaiseScore(int value)
9391 local_player->score += value;
9392 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9395 void RaiseScoreElement(int element)
9401 case EL_EMERALD_YELLOW:
9402 case EL_EMERALD_RED:
9403 case EL_EMERALD_PURPLE:
9404 case EL_SP_INFOTRON:
9405 RaiseScore(level.score[SC_EMERALD]);
9408 RaiseScore(level.score[SC_DIAMOND]);
9411 RaiseScore(level.score[SC_CRYSTAL]);
9414 RaiseScore(level.score[SC_PEARL]);
9417 case EL_BD_BUTTERFLY:
9418 case EL_SP_ELECTRON:
9419 RaiseScore(level.score[SC_BUG]);
9423 case EL_SP_SNIKSNAK:
9424 RaiseScore(level.score[SC_SPACESHIP]);
9427 case EL_DARK_YAMYAM:
9428 RaiseScore(level.score[SC_YAMYAM]);
9431 RaiseScore(level.score[SC_ROBOT]);
9434 RaiseScore(level.score[SC_PACMAN]);
9437 RaiseScore(level.score[SC_NUT]);
9440 case EL_SP_DISK_RED:
9441 case EL_DYNABOMB_INCREASE_NUMBER:
9442 case EL_DYNABOMB_INCREASE_SIZE:
9443 case EL_DYNABOMB_INCREASE_POWER:
9444 RaiseScore(level.score[SC_DYNAMITE]);
9446 case EL_SHIELD_NORMAL:
9447 case EL_SHIELD_DEADLY:
9448 RaiseScore(level.score[SC_SHIELD]);
9451 RaiseScore(level.score[SC_TIME_BONUS]);
9457 RaiseScore(level.score[SC_KEY]);
9460 RaiseScore(element_info[element].collect_score);
9465 void RequestQuitGame(boolean ask_if_really_quit)
9467 if (AllPlayersGone ||
9468 !ask_if_really_quit ||
9469 level_editor_test_game ||
9470 Request("Do you really want to quit the game ?",
9471 REQ_ASK | REQ_STAY_CLOSED))
9473 #if defined(PLATFORM_UNIX)
9474 if (options.network)
9475 SendToServer_StopPlaying();
9479 game_status = GAME_MODE_MAIN;
9485 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9490 /* ---------- new game button stuff ---------------------------------------- */
9492 /* graphic position values for game buttons */
9493 #define GAME_BUTTON_XSIZE 30
9494 #define GAME_BUTTON_YSIZE 30
9495 #define GAME_BUTTON_XPOS 5
9496 #define GAME_BUTTON_YPOS 215
9497 #define SOUND_BUTTON_XPOS 5
9498 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9500 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9501 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9502 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9503 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9504 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9505 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9512 } gamebutton_info[NUM_GAME_BUTTONS] =
9515 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9520 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9525 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9530 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9531 SOUND_CTRL_ID_MUSIC,
9532 "background music on/off"
9535 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9536 SOUND_CTRL_ID_LOOPS,
9537 "sound loops on/off"
9540 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9541 SOUND_CTRL_ID_SIMPLE,
9542 "normal sounds on/off"
9546 void CreateGameButtons()
9550 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9552 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9553 struct GadgetInfo *gi;
9556 unsigned long event_mask;
9557 int gd_xoffset, gd_yoffset;
9558 int gd_x1, gd_x2, gd_y1, gd_y2;
9561 gd_xoffset = gamebutton_info[i].x;
9562 gd_yoffset = gamebutton_info[i].y;
9563 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9564 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9566 if (id == GAME_CTRL_ID_STOP ||
9567 id == GAME_CTRL_ID_PAUSE ||
9568 id == GAME_CTRL_ID_PLAY)
9570 button_type = GD_TYPE_NORMAL_BUTTON;
9572 event_mask = GD_EVENT_RELEASED;
9573 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9574 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9578 button_type = GD_TYPE_CHECK_BUTTON;
9580 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9581 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9582 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9583 event_mask = GD_EVENT_PRESSED;
9584 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9585 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9588 gi = CreateGadget(GDI_CUSTOM_ID, id,
9589 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9590 GDI_X, DX + gd_xoffset,
9591 GDI_Y, DY + gd_yoffset,
9592 GDI_WIDTH, GAME_BUTTON_XSIZE,
9593 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9594 GDI_TYPE, button_type,
9595 GDI_STATE, GD_BUTTON_UNPRESSED,
9596 GDI_CHECKED, checked,
9597 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9598 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9599 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9600 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9601 GDI_EVENT_MASK, event_mask,
9602 GDI_CALLBACK_ACTION, HandleGameButtons,
9606 Error(ERR_EXIT, "cannot create gadget");
9608 game_gadget[id] = gi;
9612 void FreeGameButtons()
9616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9617 FreeGadget(game_gadget[i]);
9620 static void MapGameButtons()
9624 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9625 MapGadget(game_gadget[i]);
9628 void UnmapGameButtons()
9632 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9633 UnmapGadget(game_gadget[i]);
9636 static void HandleGameButtons(struct GadgetInfo *gi)
9638 int id = gi->custom_id;
9640 if (game_status != GAME_MODE_PLAYING)
9645 case GAME_CTRL_ID_STOP:
9646 RequestQuitGame(TRUE);
9649 case GAME_CTRL_ID_PAUSE:
9650 if (options.network)
9652 #if defined(PLATFORM_UNIX)
9654 SendToServer_ContinuePlaying();
9656 SendToServer_PausePlaying();
9660 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9663 case GAME_CTRL_ID_PLAY:
9666 #if defined(PLATFORM_UNIX)
9667 if (options.network)
9668 SendToServer_ContinuePlaying();
9672 tape.pausing = FALSE;
9673 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9678 case SOUND_CTRL_ID_MUSIC:
9679 if (setup.sound_music)
9681 setup.sound_music = FALSE;
9684 else if (audio.music_available)
9686 setup.sound = setup.sound_music = TRUE;
9688 SetAudioMode(setup.sound);
9694 case SOUND_CTRL_ID_LOOPS:
9695 if (setup.sound_loops)
9696 setup.sound_loops = FALSE;
9697 else if (audio.loops_available)
9699 setup.sound = setup.sound_loops = TRUE;
9700 SetAudioMode(setup.sound);
9704 case SOUND_CTRL_ID_SIMPLE:
9705 if (setup.sound_simple)
9706 setup.sound_simple = FALSE;
9707 else if (audio.sound_available)
9709 setup.sound = setup.sound_simple = TRUE;
9710 SetAudioMode(setup.sound);