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))
147 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
148 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
149 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
151 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
154 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
155 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
157 /* game button identifiers */
158 #define GAME_CTRL_ID_STOP 0
159 #define GAME_CTRL_ID_PAUSE 1
160 #define GAME_CTRL_ID_PLAY 2
161 #define SOUND_CTRL_ID_MUSIC 3
162 #define SOUND_CTRL_ID_LOOPS 4
163 #define SOUND_CTRL_ID_SIMPLE 5
165 #define NUM_GAME_BUTTONS 6
168 /* forward declaration for internal use */
170 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
171 static boolean MovePlayer(struct PlayerInfo *, int, int);
172 static void ScrollPlayer(struct PlayerInfo *, int);
173 static void ScrollScreen(struct PlayerInfo *, int);
175 static void InitBeltMovement(void);
176 static void CloseAllOpenTimegates(void);
177 static void CheckGravityMovement(struct PlayerInfo *);
178 static void KillHeroUnlessProtected(int, int);
180 static void TestIfPlayerTouchesCustomElement(int, int);
181 static void TestIfElementTouchesCustomElement(int, int);
183 static void ChangeElement(int, int, int);
184 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
185 static boolean CheckTriggeredElementChange(int, int, int, int);
186 static boolean CheckElementSideChange(int, int, int, int, int, int);
187 static boolean CheckElementChange(int, int, int, int);
189 static void PlayLevelSound(int, int, int);
190 static void PlayLevelSoundNearest(int, int, int);
191 static void PlayLevelSoundAction(int, int, int);
192 static void PlayLevelSoundElementAction(int, int, int, int);
193 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
194 static void PlayLevelSoundActionIfLoop(int, int, int);
195 static void StopLevelSoundActionIfLoop(int, int, int);
196 static void PlayLevelMusic();
198 static void MapGameButtons();
199 static void HandleGameButtons(struct GadgetInfo *);
201 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
204 /* ------------------------------------------------------------------------- */
205 /* definition of elements that automatically change to other elements after */
206 /* a specified time, eventually calling a function when changing */
207 /* ------------------------------------------------------------------------- */
209 /* forward declaration for changer functions */
210 static void InitBuggyBase(int x, int y);
211 static void WarnBuggyBase(int x, int y);
213 static void InitTrap(int x, int y);
214 static void ActivateTrap(int x, int y);
215 static void ChangeActiveTrap(int x, int y);
217 static void InitRobotWheel(int x, int y);
218 static void RunRobotWheel(int x, int y);
219 static void StopRobotWheel(int x, int y);
221 static void InitTimegateWheel(int x, int y);
222 static void RunTimegateWheel(int x, int y);
224 struct ChangingElementInfo
229 void (*pre_change_function)(int x, int y);
230 void (*change_function)(int x, int y);
231 void (*post_change_function)(int x, int y);
234 static struct ChangingElementInfo change_delay_list[] =
285 EL_SWITCHGATE_OPENING,
293 EL_SWITCHGATE_CLOSING,
294 EL_SWITCHGATE_CLOSED,
326 EL_ACID_SPLASH_RIGHT,
335 EL_SP_BUGGY_BASE_ACTIVATING,
342 EL_SP_BUGGY_BASE_ACTIVATING,
343 EL_SP_BUGGY_BASE_ACTIVE,
350 EL_SP_BUGGY_BASE_ACTIVE,
374 EL_ROBOT_WHEEL_ACTIVE,
382 EL_TIMEGATE_SWITCH_ACTIVE,
403 int push_delay_fixed, push_delay_random;
408 { EL_BALLOON, 0, 0 },
410 { EL_SOKOBAN_OBJECT, 2, 0 },
411 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
412 { EL_SATELLITE, 2, 0 },
413 { EL_SP_DISK_YELLOW, 2, 0 },
415 { EL_UNDEFINED, 0, 0 },
423 move_stepsize_list[] =
425 { EL_AMOEBA_DROP, 2 },
426 { EL_AMOEBA_DROPPING, 2 },
427 { EL_QUICKSAND_FILLING, 1 },
428 { EL_QUICKSAND_EMPTYING, 1 },
429 { EL_MAGIC_WALL_FILLING, 2 },
430 { EL_BD_MAGIC_WALL_FILLING, 2 },
431 { EL_MAGIC_WALL_EMPTYING, 2 },
432 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
442 collect_count_list[] =
445 { EL_BD_DIAMOND, 1 },
446 { EL_EMERALD_YELLOW, 1 },
447 { EL_EMERALD_RED, 1 },
448 { EL_EMERALD_PURPLE, 1 },
450 { EL_SP_INFOTRON, 1 },
457 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
459 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
460 CH_EVENT_BIT(CE_DELAY))
461 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
462 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
463 IS_JUST_CHANGING(x, y))
465 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
468 void GetPlayerConfig()
470 if (!audio.sound_available)
471 setup.sound_simple = FALSE;
473 if (!audio.loops_available)
474 setup.sound_loops = FALSE;
476 if (!audio.music_available)
477 setup.sound_music = FALSE;
479 if (!video.fullscreen_available)
480 setup.fullscreen = FALSE;
482 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
484 SetAudioMode(setup.sound);
488 static int getBeltNrFromBeltElement(int element)
490 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
491 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
492 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
495 static int getBeltNrFromBeltActiveElement(int element)
497 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
498 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
499 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
502 static int getBeltNrFromBeltSwitchElement(int element)
504 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
505 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
506 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
509 static int getBeltDirNrFromBeltSwitchElement(int element)
511 static int belt_base_element[4] =
513 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
514 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
515 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
516 EL_CONVEYOR_BELT_4_SWITCH_LEFT
519 int belt_nr = getBeltNrFromBeltSwitchElement(element);
520 int belt_dir_nr = element - belt_base_element[belt_nr];
522 return (belt_dir_nr % 3);
525 static int getBeltDirFromBeltSwitchElement(int element)
527 static int belt_move_dir[3] =
534 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
536 return belt_move_dir[belt_dir_nr];
539 static void InitPlayerField(int x, int y, int element, boolean init_game)
541 if (element == EL_SP_MURPHY)
545 if (stored_player[0].present)
547 Feld[x][y] = EL_SP_MURPHY_CLONE;
553 stored_player[0].use_murphy_graphic = TRUE;
556 Feld[x][y] = EL_PLAYER_1;
562 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
563 int jx = player->jx, jy = player->jy;
565 player->present = TRUE;
567 if (!options.network || player->connected)
569 player->active = TRUE;
571 /* remove potentially duplicate players */
572 if (StorePlayer[jx][jy] == Feld[x][y])
573 StorePlayer[jx][jy] = 0;
575 StorePlayer[x][y] = Feld[x][y];
579 printf("Player %d activated.\n", player->element_nr);
580 printf("[Local player is %d and currently %s.]\n",
581 local_player->element_nr,
582 local_player->active ? "active" : "not active");
586 Feld[x][y] = EL_EMPTY;
587 player->jx = player->last_jx = x;
588 player->jy = player->last_jy = y;
592 static void InitField(int x, int y, boolean init_game)
594 int element = Feld[x][y];
603 InitPlayerField(x, y, element, init_game);
607 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
608 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
609 else if (x > 0 && Feld[x-1][y] == EL_ACID)
610 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
611 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
612 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
613 else if (y > 0 && Feld[x][y-1] == EL_ACID)
614 Feld[x][y] = EL_ACID_POOL_BOTTOM;
615 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
616 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
624 case EL_SPACESHIP_RIGHT:
625 case EL_SPACESHIP_UP:
626 case EL_SPACESHIP_LEFT:
627 case EL_SPACESHIP_DOWN:
629 case EL_BD_BUTTERFLY_RIGHT:
630 case EL_BD_BUTTERFLY_UP:
631 case EL_BD_BUTTERFLY_LEFT:
632 case EL_BD_BUTTERFLY_DOWN:
633 case EL_BD_BUTTERFLY:
634 case EL_BD_FIREFLY_RIGHT:
635 case EL_BD_FIREFLY_UP:
636 case EL_BD_FIREFLY_LEFT:
637 case EL_BD_FIREFLY_DOWN:
639 case EL_PACMAN_RIGHT:
663 if (y == lev_fieldy - 1)
665 Feld[x][y] = EL_AMOEBA_GROWING;
666 Store[x][y] = EL_AMOEBA_WET;
670 case EL_DYNAMITE_ACTIVE:
675 local_player->lights_still_needed++;
678 case EL_SOKOBAN_FIELD_EMPTY:
679 local_player->sokobanfields_still_needed++;
683 local_player->friends_still_needed++;
688 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
693 Feld[x][y] = EL_EMPTY;
698 case EL_EM_KEY_1_FILE:
699 Feld[x][y] = EL_EM_KEY_1;
701 case EL_EM_KEY_2_FILE:
702 Feld[x][y] = EL_EM_KEY_2;
704 case EL_EM_KEY_3_FILE:
705 Feld[x][y] = EL_EM_KEY_3;
707 case EL_EM_KEY_4_FILE:
708 Feld[x][y] = EL_EM_KEY_4;
712 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
713 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
714 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
715 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
716 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
717 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
718 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
719 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
720 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
721 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
722 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
723 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
726 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
727 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
728 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
730 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
732 game.belt_dir[belt_nr] = belt_dir;
733 game.belt_dir_nr[belt_nr] = belt_dir_nr;
735 else /* more than one switch -- set it like the first switch */
737 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
742 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
744 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
747 case EL_LIGHT_SWITCH_ACTIVE:
749 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
753 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
759 void DrawGameDoorValues()
763 for (i = 0; i < MAX_PLAYERS; i++)
764 for (j = 0; j < 4; j++)
765 if (stored_player[i].key[j])
766 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
767 el2edimg(EL_KEY_1 + j));
769 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
770 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
771 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
772 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
773 DrawText(DX + XX_SCORE, DY + YY_SCORE,
774 int2str(local_player->score, 5), FONT_TEXT_2);
775 DrawText(DX + XX_TIME, DY + YY_TIME,
776 int2str(TimeLeft, 3), FONT_TEXT_2);
781 =============================================================================
783 -----------------------------------------------------------------------------
784 initialize game engine due to level / tape version number
785 =============================================================================
788 static void InitGameEngine()
792 /* set game engine from tape file when re-playing, else from level file */
793 game.engine_version = (tape.playing ? tape.engine_version :
796 /* dynamically adjust element properties according to game engine version */
797 InitElementPropertiesEngine(game.engine_version);
800 printf("level %d: level version == %06d\n", level_nr, level.game_version);
801 printf(" tape version == %06d [%s] [file: %06d]\n",
802 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
804 printf(" => game.engine_version == %06d\n", game.engine_version);
807 /* ---------- initialize player's initial move delay --------------------- */
809 /* dynamically adjust player properties according to game engine version */
810 game.initial_move_delay =
811 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
812 INITIAL_MOVE_DELAY_OFF);
814 /* dynamically adjust player properties according to level information */
815 game.initial_move_delay_value =
816 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
818 /* ---------- initialize player's initial push delay --------------------- */
820 /* dynamically adjust player properties according to game engine version */
821 game.initial_push_delay_value =
822 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
824 /* ---------- initialize changing elements ------------------------------- */
826 /* initialize changing elements information */
827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
829 struct ElementInfo *ei = &element_info[i];
831 /* this pointer might have been changed in the level editor */
832 ei->change = &ei->change_page[0];
834 if (!IS_CUSTOM_ELEMENT(i))
836 ei->change->target_element = EL_EMPTY_SPACE;
837 ei->change->delay_fixed = 0;
838 ei->change->delay_random = 0;
839 ei->change->delay_frames = 1;
842 ei->change_events = CE_BITMASK_DEFAULT;
843 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
845 ei->event_page_nr[j] = 0;
846 ei->event_page[j] = &ei->change_page[0];
850 /* add changing elements from pre-defined list */
851 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
853 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
854 struct ElementInfo *ei = &element_info[ch_delay->element];
856 ei->change->target_element = ch_delay->target_element;
857 ei->change->delay_fixed = ch_delay->change_delay;
859 ei->change->pre_change_function = ch_delay->pre_change_function;
860 ei->change->change_function = ch_delay->change_function;
861 ei->change->post_change_function = ch_delay->post_change_function;
863 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
867 /* add change events from custom element configuration */
868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
870 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
872 for (j = 0; j < ei->num_change_pages; j++)
874 if (!ei->change_page[j].can_change)
877 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
879 /* only add event page for the first page found with this event */
880 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
881 !(ei->change_events & CH_EVENT_BIT(k)))
883 ei->change_events |= CH_EVENT_BIT(k);
884 ei->event_page_nr[k] = j;
885 ei->event_page[k] = &ei->change_page[j];
893 /* add change events from custom element configuration */
894 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
896 int element = EL_CUSTOM_START + i;
898 /* only add custom elements that change after fixed/random frame delay */
899 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
900 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
904 /* ---------- initialize trigger events ---------------------------------- */
906 /* initialize trigger events information */
907 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
908 trigger_events[i] = EP_BITMASK_DEFAULT;
911 /* add trigger events from element change event properties */
912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
914 struct ElementInfo *ei = &element_info[i];
916 for (j = 0; j < ei->num_change_pages; j++)
918 if (!ei->change_page[j].can_change)
921 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
923 int trigger_element = ei->change_page[j].trigger_element;
925 trigger_events[trigger_element] |= ei->change_page[j].events;
930 /* add trigger events from element change event properties */
931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
932 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
933 trigger_events[element_info[i].change->trigger_element] |=
934 element_info[i].change->events;
937 /* ---------- initialize push delay -------------------------------------- */
939 /* initialize push delay values to default */
940 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
942 if (!IS_CUSTOM_ELEMENT(i))
944 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
945 element_info[i].push_delay_random = game.default_push_delay_random;
949 /* set push delay value for certain elements from pre-defined list */
950 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
952 int e = push_delay_list[i].element;
954 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
955 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
958 /* ---------- initialize move stepsize ----------------------------------- */
960 /* initialize move stepsize values to default */
961 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
962 if (!IS_CUSTOM_ELEMENT(i))
963 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
965 /* set move stepsize value for certain elements from pre-defined list */
966 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
968 int e = move_stepsize_list[i].element;
970 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
973 /* ---------- initialize gem count --------------------------------------- */
975 /* initialize gem count values for each element */
976 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
977 if (!IS_CUSTOM_ELEMENT(i))
978 element_info[i].collect_count = 0;
980 /* add gem count values for all elements from pre-defined list */
981 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
982 element_info[collect_count_list[i].element].collect_count =
983 collect_count_list[i].count;
988 =============================================================================
990 -----------------------------------------------------------------------------
991 initialize and start new game
992 =============================================================================
997 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
998 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
999 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1006 #if USE_NEW_AMOEBA_CODE
1007 printf("Using new amoeba code.\n");
1009 printf("Using old amoeba code.\n");
1014 /* don't play tapes over network */
1015 network_playing = (options.network && !tape.playing);
1017 for (i = 0; i < MAX_PLAYERS; i++)
1019 struct PlayerInfo *player = &stored_player[i];
1021 player->index_nr = i;
1022 player->element_nr = EL_PLAYER_1 + i;
1024 player->present = FALSE;
1025 player->active = FALSE;
1028 player->effective_action = 0;
1029 player->programmed_action = 0;
1032 player->gems_still_needed = level.gems_needed;
1033 player->sokobanfields_still_needed = 0;
1034 player->lights_still_needed = 0;
1035 player->friends_still_needed = 0;
1037 for (j = 0; j < 4; j++)
1038 player->key[j] = FALSE;
1040 player->dynabomb_count = 0;
1041 player->dynabomb_size = 1;
1042 player->dynabombs_left = 0;
1043 player->dynabomb_xl = FALSE;
1045 player->MovDir = MV_NO_MOVING;
1048 player->GfxDir = MV_NO_MOVING;
1049 player->GfxAction = ACTION_DEFAULT;
1051 player->StepFrame = 0;
1053 player->use_murphy_graphic = FALSE;
1055 player->actual_frame_counter = 0;
1057 player->step_counter = 0;
1059 player->last_move_dir = MV_NO_MOVING;
1061 player->is_waiting = FALSE;
1062 player->is_moving = FALSE;
1063 player->is_digging = FALSE;
1064 player->is_snapping = FALSE;
1065 player->is_collecting = FALSE;
1066 player->is_pushing = FALSE;
1067 player->is_switching = FALSE;
1069 player->is_bored = FALSE;
1070 player->is_sleeping = FALSE;
1072 player->frame_counter_bored = -1;
1073 player->frame_counter_sleeping = -1;
1075 player->anim_delay_counter = 0;
1076 player->post_delay_counter = 0;
1078 player->action_waiting = ACTION_DEFAULT;
1079 player->special_action_bored = ACTION_DEFAULT;
1080 player->special_action_sleeping = ACTION_DEFAULT;
1082 player->num_special_action_bored = 0;
1083 player->num_special_action_sleeping = 0;
1085 /* determine number of special actions for bored and sleeping animation */
1086 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1088 boolean found = FALSE;
1090 for (k = 0; k < NUM_DIRECTIONS; k++)
1091 if (el_act_dir2img(player->element_nr, j, k) !=
1092 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1096 player->num_special_action_bored++;
1100 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1102 boolean found = FALSE;
1104 for (k = 0; k < NUM_DIRECTIONS; k++)
1105 if (el_act_dir2img(player->element_nr, j, k) !=
1106 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1110 player->num_special_action_sleeping++;
1115 player->switch_x = -1;
1116 player->switch_y = -1;
1118 player->show_envelope = 0;
1120 player->move_delay = game.initial_move_delay;
1121 player->move_delay_value = game.initial_move_delay_value;
1123 player->push_delay = 0;
1124 player->push_delay_value = game.initial_push_delay_value;
1126 player->last_jx = player->last_jy = 0;
1127 player->jx = player->jy = 0;
1129 player->shield_normal_time_left = 0;
1130 player->shield_deadly_time_left = 0;
1132 player->inventory_size = 0;
1134 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1135 SnapField(player, 0, 0);
1137 player->LevelSolved = FALSE;
1138 player->GameOver = FALSE;
1141 network_player_action_received = FALSE;
1143 #if defined(PLATFORM_UNIX)
1144 /* initial null action */
1145 if (network_playing)
1146 SendToServer_MovePlayer(MV_NO_MOVING);
1154 TimeLeft = level.time;
1156 ScreenMovDir = MV_NO_MOVING;
1160 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1162 AllPlayersGone = FALSE;
1164 game.yamyam_content_nr = 0;
1165 game.magic_wall_active = FALSE;
1166 game.magic_wall_time_left = 0;
1167 game.light_time_left = 0;
1168 game.timegate_time_left = 0;
1169 game.switchgate_pos = 0;
1170 game.balloon_dir = MV_NO_MOVING;
1171 game.gravity = level.initial_gravity;
1172 game.explosions_delayed = TRUE;
1174 game.envelope_active = FALSE;
1176 for (i = 0; i < 4; i++)
1178 game.belt_dir[i] = MV_NO_MOVING;
1179 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1182 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1183 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1185 for (x = 0; x < lev_fieldx; x++)
1187 for (y = 0; y < lev_fieldy; y++)
1189 Feld[x][y] = level.field[x][y];
1190 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1191 ChangeDelay[x][y] = 0;
1192 ChangePage[x][y] = -1;
1193 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1195 WasJustMoving[x][y] = 0;
1196 WasJustFalling[x][y] = 0;
1198 Pushed[x][y] = FALSE;
1200 Changed[x][y] = CE_BITMASK_DEFAULT;
1201 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1203 ExplodePhase[x][y] = 0;
1204 ExplodeField[x][y] = EX_NO_EXPLOSION;
1206 RunnerVisit[x][y] = 0;
1207 PlayerVisit[x][y] = 0;
1210 GfxRandom[x][y] = INIT_GFX_RANDOM();
1211 GfxElement[x][y] = EL_UNDEFINED;
1212 GfxAction[x][y] = ACTION_DEFAULT;
1213 GfxDir[x][y] = MV_NO_MOVING;
1217 for (y = 0; y < lev_fieldy; y++)
1219 for (x = 0; x < lev_fieldx; x++)
1221 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1223 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1225 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1228 InitField(x, y, TRUE);
1234 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1235 emulate_sb ? EMU_SOKOBAN :
1236 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1238 /* correct non-moving belts to start moving left */
1239 for (i = 0; i < 4; i++)
1240 if (game.belt_dir[i] == MV_NO_MOVING)
1241 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1243 /* check if any connected player was not found in playfield */
1244 for (i = 0; i < MAX_PLAYERS; i++)
1246 struct PlayerInfo *player = &stored_player[i];
1248 if (player->connected && !player->present)
1250 for (j = 0; j < MAX_PLAYERS; j++)
1252 struct PlayerInfo *some_player = &stored_player[j];
1253 int jx = some_player->jx, jy = some_player->jy;
1255 /* assign first free player found that is present in the playfield */
1256 if (some_player->present && !some_player->connected)
1258 player->present = TRUE;
1259 player->active = TRUE;
1260 some_player->present = FALSE;
1262 StorePlayer[jx][jy] = player->element_nr;
1263 player->jx = player->last_jx = jx;
1264 player->jy = player->last_jy = jy;
1274 /* when playing a tape, eliminate all players who do not participate */
1276 for (i = 0; i < MAX_PLAYERS; i++)
1278 if (stored_player[i].active && !tape.player_participates[i])
1280 struct PlayerInfo *player = &stored_player[i];
1281 int jx = player->jx, jy = player->jy;
1283 player->active = FALSE;
1284 StorePlayer[jx][jy] = 0;
1285 Feld[jx][jy] = EL_EMPTY;
1289 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1291 /* when in single player mode, eliminate all but the first active player */
1293 for (i = 0; i < MAX_PLAYERS; i++)
1295 if (stored_player[i].active)
1297 for (j = i + 1; j < MAX_PLAYERS; j++)
1299 if (stored_player[j].active)
1301 struct PlayerInfo *player = &stored_player[j];
1302 int jx = player->jx, jy = player->jy;
1304 player->active = FALSE;
1305 StorePlayer[jx][jy] = 0;
1306 Feld[jx][jy] = EL_EMPTY;
1313 /* when recording the game, store which players take part in the game */
1316 for (i = 0; i < MAX_PLAYERS; i++)
1317 if (stored_player[i].active)
1318 tape.player_participates[i] = TRUE;
1323 for (i = 0; i < MAX_PLAYERS; i++)
1325 struct PlayerInfo *player = &stored_player[i];
1327 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1332 if (local_player == player)
1333 printf("Player %d is local player.\n", i+1);
1337 if (BorderElement == EL_EMPTY)
1340 SBX_Right = lev_fieldx - SCR_FIELDX;
1342 SBY_Lower = lev_fieldy - SCR_FIELDY;
1347 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1349 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1352 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1353 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1355 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1356 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1358 /* if local player not found, look for custom element that might create
1359 the player (make some assumptions about the right custom element) */
1360 if (!local_player->present)
1362 int start_x = 0, start_y = 0;
1363 int found_rating = 0;
1364 int found_element = EL_UNDEFINED;
1366 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1368 int element = Feld[x][y];
1373 if (!IS_CUSTOM_ELEMENT(element))
1376 if (CAN_CHANGE(element))
1378 for (i = 0; i < element_info[element].num_change_pages; i++)
1380 content = element_info[element].change_page[i].target_element;
1381 is_player = ELEM_IS_PLAYER(content);
1383 if (is_player && (found_rating < 3 || element < found_element))
1389 found_element = element;
1394 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1396 content = element_info[element].content[xx][yy];
1397 is_player = ELEM_IS_PLAYER(content);
1399 if (is_player && (found_rating < 2 || element < found_element))
1401 start_x = x + xx - 1;
1402 start_y = y + yy - 1;
1405 found_element = element;
1408 if (!CAN_CHANGE(element))
1411 for (i = 0; i < element_info[element].num_change_pages; i++)
1413 content = element_info[element].change_page[i].content[xx][yy];
1414 is_player = ELEM_IS_PLAYER(content);
1416 if (is_player && (found_rating < 1 || element < found_element))
1418 start_x = x + xx - 1;
1419 start_y = y + yy - 1;
1422 found_element = element;
1428 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1429 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1432 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1433 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1439 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1440 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1441 local_player->jx - MIDPOSX);
1443 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1444 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1445 local_player->jy - MIDPOSY);
1447 scroll_x = SBX_Left;
1448 scroll_y = SBY_Upper;
1449 if (local_player->jx >= SBX_Left + MIDPOSX)
1450 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1451 local_player->jx - MIDPOSX :
1453 if (local_player->jy >= SBY_Upper + MIDPOSY)
1454 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1455 local_player->jy - MIDPOSY :
1460 CloseDoor(DOOR_CLOSE_1);
1465 /* after drawing the level, correct some elements */
1466 if (game.timegate_time_left == 0)
1467 CloseAllOpenTimegates();
1469 if (setup.soft_scrolling)
1470 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1472 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1475 /* copy default game door content to main double buffer */
1476 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1477 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1480 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1483 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1484 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1485 BlitBitmap(drawto, drawto,
1486 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1487 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1488 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1489 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1492 DrawGameDoorValues();
1496 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1497 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1498 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1502 /* copy actual game door content to door double buffer for OpenDoor() */
1503 BlitBitmap(drawto, bitmap_db_door,
1504 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1506 OpenDoor(DOOR_OPEN_ALL);
1508 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1510 if (setup.sound_music)
1513 KeyboardAutoRepeatOffUnlessAutoplay();
1517 for (i = 0; i < 4; i++)
1518 printf("Player %d %sactive.\n",
1519 i + 1, (stored_player[i].active ? "" : "not "));
1523 printf("::: starting game [%d]\n", FrameCounter);
1527 void InitMovDir(int x, int y)
1529 int i, element = Feld[x][y];
1530 static int xy[4][2] =
1537 static int direction[3][4] =
1539 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1540 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1541 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1550 Feld[x][y] = EL_BUG;
1551 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1554 case EL_SPACESHIP_RIGHT:
1555 case EL_SPACESHIP_UP:
1556 case EL_SPACESHIP_LEFT:
1557 case EL_SPACESHIP_DOWN:
1558 Feld[x][y] = EL_SPACESHIP;
1559 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1562 case EL_BD_BUTTERFLY_RIGHT:
1563 case EL_BD_BUTTERFLY_UP:
1564 case EL_BD_BUTTERFLY_LEFT:
1565 case EL_BD_BUTTERFLY_DOWN:
1566 Feld[x][y] = EL_BD_BUTTERFLY;
1567 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1570 case EL_BD_FIREFLY_RIGHT:
1571 case EL_BD_FIREFLY_UP:
1572 case EL_BD_FIREFLY_LEFT:
1573 case EL_BD_FIREFLY_DOWN:
1574 Feld[x][y] = EL_BD_FIREFLY;
1575 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1578 case EL_PACMAN_RIGHT:
1580 case EL_PACMAN_LEFT:
1581 case EL_PACMAN_DOWN:
1582 Feld[x][y] = EL_PACMAN;
1583 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1586 case EL_SP_SNIKSNAK:
1587 MovDir[x][y] = MV_UP;
1590 case EL_SP_ELECTRON:
1591 MovDir[x][y] = MV_LEFT;
1598 Feld[x][y] = EL_MOLE;
1599 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1603 if (IS_CUSTOM_ELEMENT(element))
1605 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1606 MovDir[x][y] = element_info[element].move_direction_initial;
1607 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1608 element_info[element].move_pattern == MV_TURNING_LEFT ||
1609 element_info[element].move_pattern == MV_TURNING_RIGHT)
1610 MovDir[x][y] = 1 << RND(4);
1611 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1612 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1613 else if (element_info[element].move_pattern == MV_VERTICAL)
1614 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1615 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1616 MovDir[x][y] = element_info[element].move_pattern;
1617 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1618 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1620 for (i = 0; i < 4; i++)
1622 int x1 = x + xy[i][0];
1623 int y1 = y + xy[i][1];
1625 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1627 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1628 MovDir[x][y] = direction[0][i];
1630 MovDir[x][y] = direction[1][i];
1639 MovDir[x][y] = 1 << RND(4);
1641 if (element != EL_BUG &&
1642 element != EL_SPACESHIP &&
1643 element != EL_BD_BUTTERFLY &&
1644 element != EL_BD_FIREFLY)
1647 for (i = 0; i < 4; i++)
1649 int x1 = x + xy[i][0];
1650 int y1 = y + xy[i][1];
1652 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1654 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1656 MovDir[x][y] = direction[0][i];
1659 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1660 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1662 MovDir[x][y] = direction[1][i];
1671 GfxDir[x][y] = MovDir[x][y];
1674 void InitAmoebaNr(int x, int y)
1677 int group_nr = AmoebeNachbarNr(x, y);
1681 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1683 if (AmoebaCnt[i] == 0)
1691 AmoebaNr[x][y] = group_nr;
1692 AmoebaCnt[group_nr]++;
1693 AmoebaCnt2[group_nr]++;
1699 boolean raise_level = FALSE;
1701 if (local_player->MovPos)
1705 if (tape.auto_play) /* tape might already be stopped here */
1706 tape.auto_play_level_solved = TRUE;
1708 if (tape.playing && tape.auto_play)
1709 tape.auto_play_level_solved = TRUE;
1712 local_player->LevelSolved = FALSE;
1714 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1718 if (!tape.playing && setup.sound_loops)
1719 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1720 SND_CTRL_PLAY_LOOP);
1722 while (TimeLeft > 0)
1724 if (!tape.playing && !setup.sound_loops)
1725 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1726 if (TimeLeft > 0 && !(TimeLeft % 10))
1727 RaiseScore(level.score[SC_TIME_BONUS]);
1728 if (TimeLeft > 100 && !(TimeLeft % 10))
1732 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1739 if (!tape.playing && setup.sound_loops)
1740 StopSound(SND_GAME_LEVELTIME_BONUS);
1742 else if (level.time == 0) /* level without time limit */
1744 if (!tape.playing && setup.sound_loops)
1745 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1746 SND_CTRL_PLAY_LOOP);
1748 while (TimePlayed < 999)
1750 if (!tape.playing && !setup.sound_loops)
1751 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1752 if (TimePlayed < 999 && !(TimePlayed % 10))
1753 RaiseScore(level.score[SC_TIME_BONUS]);
1754 if (TimePlayed < 900 && !(TimePlayed % 10))
1758 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1765 if (!tape.playing && setup.sound_loops)
1766 StopSound(SND_GAME_LEVELTIME_BONUS);
1769 /* close exit door after last player */
1770 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1771 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1773 int element = Feld[ExitX][ExitY];
1775 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1776 EL_SP_EXIT_CLOSING);
1778 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1781 /* Hero disappears */
1782 DrawLevelField(ExitX, ExitY);
1788 CloseDoor(DOOR_CLOSE_1);
1793 SaveTape(tape.level_nr); /* Ask to save tape */
1796 if (level_nr == leveldir_current->handicap_level)
1798 leveldir_current->handicap_level++;
1799 SaveLevelSetup_SeriesInfo();
1802 if (level_editor_test_game)
1803 local_player->score = -1; /* no highscore when playing from editor */
1804 else if (level_nr < leveldir_current->last_level)
1805 raise_level = TRUE; /* advance to next level */
1807 if ((hi_pos = NewHiScore()) >= 0)
1809 game_status = GAME_MODE_SCORES;
1810 DrawHallOfFame(hi_pos);
1819 game_status = GAME_MODE_MAIN;
1836 LoadScore(level_nr);
1838 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1839 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1842 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1844 if (local_player->score > highscore[k].Score)
1846 /* player has made it to the hall of fame */
1848 if (k < MAX_SCORE_ENTRIES - 1)
1850 int m = MAX_SCORE_ENTRIES - 1;
1853 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1854 if (!strcmp(setup.player_name, highscore[l].Name))
1856 if (m == k) /* player's new highscore overwrites his old one */
1860 for (l = m; l > k; l--)
1862 strcpy(highscore[l].Name, highscore[l - 1].Name);
1863 highscore[l].Score = highscore[l - 1].Score;
1870 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1871 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1872 highscore[k].Score = local_player->score;
1878 else if (!strncmp(setup.player_name, highscore[k].Name,
1879 MAX_PLAYER_NAME_LEN))
1880 break; /* player already there with a higher score */
1886 SaveScore(level_nr);
1891 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1893 if (player->GfxAction != action || player->GfxDir != dir)
1896 printf("Player frame reset! (%d => %d, %d => %d)\n",
1897 player->GfxAction, action, player->GfxDir, dir);
1900 player->GfxAction = action;
1901 player->GfxDir = dir;
1903 player->StepFrame = 0;
1907 static void ResetRandomAnimationValue(int x, int y)
1909 GfxRandom[x][y] = INIT_GFX_RANDOM();
1912 static void ResetGfxAnimation(int x, int y)
1915 GfxAction[x][y] = ACTION_DEFAULT;
1916 GfxDir[x][y] = MovDir[x][y];
1919 void InitMovingField(int x, int y, int direction)
1921 int element = Feld[x][y];
1922 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1923 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1927 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1928 ResetGfxAnimation(x, y);
1930 MovDir[newx][newy] = MovDir[x][y] = direction;
1931 GfxDir[x][y] = direction;
1933 if (Feld[newx][newy] == EL_EMPTY)
1934 Feld[newx][newy] = EL_BLOCKED;
1936 if (direction == MV_DOWN && CAN_FALL(element))
1937 GfxAction[x][y] = ACTION_FALLING;
1939 GfxAction[x][y] = ACTION_MOVING;
1941 GfxFrame[newx][newy] = GfxFrame[x][y];
1942 GfxRandom[newx][newy] = GfxRandom[x][y];
1943 GfxAction[newx][newy] = GfxAction[x][y];
1944 GfxDir[newx][newy] = GfxDir[x][y];
1947 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1949 int direction = MovDir[x][y];
1950 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1951 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1957 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1959 int oldx = x, oldy = y;
1960 int direction = MovDir[x][y];
1962 if (direction == MV_LEFT)
1964 else if (direction == MV_RIGHT)
1966 else if (direction == MV_UP)
1968 else if (direction == MV_DOWN)
1971 *comes_from_x = oldx;
1972 *comes_from_y = oldy;
1975 int MovingOrBlocked2Element(int x, int y)
1977 int element = Feld[x][y];
1979 if (element == EL_BLOCKED)
1983 Blocked2Moving(x, y, &oldx, &oldy);
1984 return Feld[oldx][oldy];
1990 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1992 /* like MovingOrBlocked2Element(), but if element is moving
1993 and (x,y) is the field the moving element is just leaving,
1994 return EL_BLOCKED instead of the element value */
1995 int element = Feld[x][y];
1997 if (IS_MOVING(x, y))
1999 if (element == EL_BLOCKED)
2003 Blocked2Moving(x, y, &oldx, &oldy);
2004 return Feld[oldx][oldy];
2013 static void RemoveField(int x, int y)
2015 Feld[x][y] = EL_EMPTY;
2022 ChangeDelay[x][y] = 0;
2023 ChangePage[x][y] = -1;
2024 Pushed[x][y] = FALSE;
2026 GfxElement[x][y] = EL_UNDEFINED;
2027 GfxAction[x][y] = ACTION_DEFAULT;
2028 GfxDir[x][y] = MV_NO_MOVING;
2031 void RemoveMovingField(int x, int y)
2033 int oldx = x, oldy = y, newx = x, newy = y;
2034 int element = Feld[x][y];
2035 int next_element = EL_UNDEFINED;
2037 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2040 if (IS_MOVING(x, y))
2042 Moving2Blocked(x, y, &newx, &newy);
2043 if (Feld[newx][newy] != EL_BLOCKED)
2046 else if (element == EL_BLOCKED)
2048 Blocked2Moving(x, y, &oldx, &oldy);
2049 if (!IS_MOVING(oldx, oldy))
2053 if (element == EL_BLOCKED &&
2054 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2055 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2056 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2057 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2058 next_element = get_next_element(Feld[oldx][oldy]);
2060 RemoveField(oldx, oldy);
2061 RemoveField(newx, newy);
2063 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2065 if (next_element != EL_UNDEFINED)
2066 Feld[oldx][oldy] = next_element;
2068 DrawLevelField(oldx, oldy);
2069 DrawLevelField(newx, newy);
2072 void DrawDynamite(int x, int y)
2074 int sx = SCREENX(x), sy = SCREENY(y);
2075 int graphic = el2img(Feld[x][y]);
2078 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2081 if (IS_WALKABLE_INSIDE(Back[x][y]))
2085 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2086 else if (Store[x][y])
2087 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2089 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2092 if (Back[x][y] || Store[x][y])
2093 DrawGraphicThruMask(sx, sy, graphic, frame);
2095 DrawGraphic(sx, sy, graphic, frame);
2097 if (game.emulation == EMU_SUPAPLEX)
2098 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2099 else if (Store[x][y])
2100 DrawGraphicThruMask(sx, sy, graphic, frame);
2102 DrawGraphic(sx, sy, graphic, frame);
2106 void CheckDynamite(int x, int y)
2108 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2112 if (MovDelay[x][y] != 0)
2115 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2122 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2124 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2125 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2126 StopSound(SND_DYNAMITE_ACTIVE);
2128 StopSound(SND_DYNABOMB_ACTIVE);
2134 void RelocatePlayer(int x, int y, int element)
2136 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2139 RemoveField(x, y); /* temporarily remove newly placed player */
2140 DrawLevelField(x, y);
2143 if (player->present)
2145 while (player->MovPos)
2147 ScrollPlayer(player, SCROLL_GO_ON);
2148 ScrollScreen(NULL, SCROLL_GO_ON);
2154 Delay(GAME_FRAME_DELAY);
2157 DrawPlayer(player); /* needed here only to cleanup last field */
2158 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2160 player->is_moving = FALSE;
2163 Feld[x][y] = element;
2164 InitPlayerField(x, y, element, TRUE);
2166 if (player == local_player)
2168 int scroll_xx = -999, scroll_yy = -999;
2170 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2173 int fx = FX, fy = FY;
2175 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2176 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2177 local_player->jx - MIDPOSX);
2179 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2180 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2181 local_player->jy - MIDPOSY);
2183 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2184 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2189 fx += dx * TILEX / 2;
2190 fy += dy * TILEY / 2;
2192 ScrollLevel(dx, dy);
2195 /* scroll in two steps of half tile size to make things smoother */
2196 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2198 Delay(GAME_FRAME_DELAY);
2200 /* scroll second step to align at full tile size */
2202 Delay(GAME_FRAME_DELAY);
2207 void Explode(int ex, int ey, int phase, int mode)
2211 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2212 int last_phase = num_phase * delay;
2213 int half_phase = (num_phase / 2) * delay;
2214 int first_phase_after_start = EX_PHASE_START + 1;
2216 if (game.explosions_delayed)
2218 ExplodeField[ex][ey] = mode;
2222 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2224 int center_element = Feld[ex][ey];
2227 /* --- This is only really needed (and now handled) in "Impact()". --- */
2228 /* do not explode moving elements that left the explode field in time */
2229 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2230 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2234 if (mode == EX_NORMAL || mode == EX_CENTER)
2235 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2237 /* remove things displayed in background while burning dynamite */
2238 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2241 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2243 /* put moving element to center field (and let it explode there) */
2244 center_element = MovingOrBlocked2Element(ex, ey);
2245 RemoveMovingField(ex, ey);
2246 Feld[ex][ey] = center_element;
2249 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2251 int xx = x - ex + 1;
2252 int yy = y - ey + 1;
2255 if (!IN_LEV_FIELD(x, y) ||
2256 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2257 (x != ex || y != ey)))
2260 element = Feld[x][y];
2262 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2264 element = MovingOrBlocked2Element(x, y);
2266 if (!IS_EXPLOSION_PROOF(element))
2267 RemoveMovingField(x, y);
2273 if (IS_EXPLOSION_PROOF(element))
2276 /* indestructible elements can only explode in center (but not flames) */
2277 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2278 element == EL_FLAMES)
2283 if ((IS_INDESTRUCTIBLE(element) &&
2284 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2285 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2286 element == EL_FLAMES)
2290 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2292 if (IS_ACTIVE_BOMB(element))
2294 /* re-activate things under the bomb like gate or penguin */
2295 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2302 /* save walkable background elements while explosion on same tile */
2304 if (IS_INDESTRUCTIBLE(element))
2305 Back[x][y] = element;
2307 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2308 Back[x][y] = element;
2311 /* ignite explodable elements reached by other explosion */
2312 if (element == EL_EXPLOSION)
2313 element = Store2[x][y];
2316 if (AmoebaNr[x][y] &&
2317 (element == EL_AMOEBA_FULL ||
2318 element == EL_BD_AMOEBA ||
2319 element == EL_AMOEBA_GROWING))
2321 AmoebaCnt[AmoebaNr[x][y]]--;
2322 AmoebaCnt2[AmoebaNr[x][y]]--;
2328 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2330 switch(StorePlayer[ex][ey])
2333 Store[x][y] = EL_EMERALD_RED;
2336 Store[x][y] = EL_EMERALD;
2339 Store[x][y] = EL_EMERALD_PURPLE;
2343 Store[x][y] = EL_EMERALD_YELLOW;
2347 if (game.emulation == EMU_SUPAPLEX)
2348 Store[x][y] = EL_EMPTY;
2350 else if (center_element == EL_MOLE)
2351 Store[x][y] = EL_EMERALD_RED;
2352 else if (center_element == EL_PENGUIN)
2353 Store[x][y] = EL_EMERALD_PURPLE;
2354 else if (center_element == EL_BUG)
2355 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2356 else if (center_element == EL_BD_BUTTERFLY)
2357 Store[x][y] = EL_BD_DIAMOND;
2358 else if (center_element == EL_SP_ELECTRON)
2359 Store[x][y] = EL_SP_INFOTRON;
2360 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2361 Store[x][y] = level.amoeba_content;
2362 else if (center_element == EL_YAMYAM)
2363 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2364 else if (IS_CUSTOM_ELEMENT(center_element) &&
2365 element_info[center_element].content[xx][yy] != EL_EMPTY)
2366 Store[x][y] = element_info[center_element].content[xx][yy];
2367 else if (element == EL_WALL_EMERALD)
2368 Store[x][y] = EL_EMERALD;
2369 else if (element == EL_WALL_DIAMOND)
2370 Store[x][y] = EL_DIAMOND;
2371 else if (element == EL_WALL_BD_DIAMOND)
2372 Store[x][y] = EL_BD_DIAMOND;
2373 else if (element == EL_WALL_EMERALD_YELLOW)
2374 Store[x][y] = EL_EMERALD_YELLOW;
2375 else if (element == EL_WALL_EMERALD_RED)
2376 Store[x][y] = EL_EMERALD_RED;
2377 else if (element == EL_WALL_EMERALD_PURPLE)
2378 Store[x][y] = EL_EMERALD_PURPLE;
2379 else if (element == EL_WALL_PEARL)
2380 Store[x][y] = EL_PEARL;
2381 else if (element == EL_WALL_CRYSTAL)
2382 Store[x][y] = EL_CRYSTAL;
2383 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2384 Store[x][y] = element_info[element].content[1][1];
2386 Store[x][y] = EL_EMPTY;
2388 if (x != ex || y != ey ||
2389 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2390 Store2[x][y] = element;
2393 if (AmoebaNr[x][y] &&
2394 (element == EL_AMOEBA_FULL ||
2395 element == EL_BD_AMOEBA ||
2396 element == EL_AMOEBA_GROWING))
2398 AmoebaCnt[AmoebaNr[x][y]]--;
2399 AmoebaCnt2[AmoebaNr[x][y]]--;
2405 MovDir[x][y] = MovPos[x][y] = 0;
2406 GfxDir[x][y] = MovDir[x][y];
2411 Feld[x][y] = EL_EXPLOSION;
2413 GfxElement[x][y] = center_element;
2415 GfxElement[x][y] = EL_UNDEFINED;
2418 ExplodePhase[x][y] = 1;
2422 if (center_element == EL_YAMYAM)
2423 game.yamyam_content_nr =
2424 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2435 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2439 /* activate this even in non-DEBUG version until cause for crash in
2440 getGraphicAnimationFrame() (see below) is found and eliminated */
2444 if (GfxElement[x][y] == EL_UNDEFINED)
2447 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2448 printf("Explode(): This should never happen!\n");
2451 GfxElement[x][y] = EL_EMPTY;
2455 if (phase == first_phase_after_start)
2457 int element = Store2[x][y];
2459 if (element == EL_BLACK_ORB)
2461 Feld[x][y] = Store2[x][y];
2466 else if (phase == half_phase)
2468 int element = Store2[x][y];
2470 if (IS_PLAYER(x, y))
2471 KillHeroUnlessProtected(x, y);
2472 else if (CAN_EXPLODE_BY_FIRE(element))
2474 Feld[x][y] = Store2[x][y];
2478 else if (element == EL_AMOEBA_TO_DIAMOND)
2479 AmoebeUmwandeln(x, y);
2482 if (phase == last_phase)
2486 element = Feld[x][y] = Store[x][y];
2487 Store[x][y] = Store2[x][y] = 0;
2488 GfxElement[x][y] = EL_UNDEFINED;
2490 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2491 element = Feld[x][y] = Back[x][y];
2494 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2495 GfxDir[x][y] = MV_NO_MOVING;
2496 ChangeDelay[x][y] = 0;
2497 ChangePage[x][y] = -1;
2499 InitField(x, y, FALSE);
2500 if (CAN_MOVE(element))
2502 DrawLevelField(x, y);
2504 TestIfElementTouchesCustomElement(x, y);
2506 if (GFX_CRUMBLED(element))
2507 DrawLevelFieldCrumbledSandNeighbours(x, y);
2509 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2510 StorePlayer[x][y] = 0;
2512 if (ELEM_IS_PLAYER(element))
2513 RelocatePlayer(x, y, element);
2515 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2518 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2520 int stored = Store[x][y];
2521 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2522 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2525 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2528 DrawLevelFieldCrumbledSand(x, y);
2530 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2532 DrawLevelElement(x, y, Back[x][y]);
2533 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2535 else if (IS_WALKABLE_UNDER(Back[x][y]))
2537 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2538 DrawLevelElementThruMask(x, y, Back[x][y]);
2540 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2541 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2545 void DynaExplode(int ex, int ey)
2548 int dynabomb_size = 1;
2549 boolean dynabomb_xl = FALSE;
2550 struct PlayerInfo *player;
2551 static int xy[4][2] =
2559 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2561 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2562 dynabomb_size = player->dynabomb_size;
2563 dynabomb_xl = player->dynabomb_xl;
2564 player->dynabombs_left++;
2567 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2569 for (i = 0; i < 4; i++)
2571 for (j = 1; j <= dynabomb_size; j++)
2573 int x = ex + j * xy[i % 4][0];
2574 int y = ey + j * xy[i % 4][1];
2577 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2580 element = Feld[x][y];
2582 /* do not restart explosions of fields with active bombs */
2583 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2586 Explode(x, y, EX_PHASE_START, EX_BORDER);
2588 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2589 if (element != EL_EMPTY &&
2590 element != EL_SAND &&
2591 element != EL_EXPLOSION &&
2598 void Bang(int x, int y)
2601 int element = MovingOrBlocked2Element(x, y);
2603 int element = Feld[x][y];
2607 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2609 if (IS_PLAYER(x, y))
2612 struct PlayerInfo *player = PLAYERINFO(x, y);
2614 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2615 player->element_nr);
2620 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2622 if (game.emulation == EMU_SUPAPLEX)
2623 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2625 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2630 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2638 case EL_BD_BUTTERFLY:
2641 case EL_DARK_YAMYAM:
2645 RaiseScoreElement(element);
2646 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2648 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2649 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2650 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2651 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2652 case EL_DYNABOMB_INCREASE_NUMBER:
2653 case EL_DYNABOMB_INCREASE_SIZE:
2654 case EL_DYNABOMB_INCREASE_POWER:
2659 case EL_LAMP_ACTIVE:
2660 if (IS_PLAYER(x, y))
2661 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2663 Explode(x, y, EX_PHASE_START, EX_CENTER);
2666 if (CAN_EXPLODE_1X1(element))
2667 Explode(x, y, EX_PHASE_START, EX_CENTER);
2669 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2673 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2676 void SplashAcid(int x, int y)
2678 int element = Feld[x][y];
2680 if (element != EL_ACID_SPLASH_LEFT &&
2681 element != EL_ACID_SPLASH_RIGHT)
2683 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2685 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2686 (!IN_LEV_FIELD(x-1, y-1) ||
2687 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2688 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2690 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2691 (!IN_LEV_FIELD(x+1, y-1) ||
2692 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2693 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2697 static void InitBeltMovement()
2699 static int belt_base_element[4] =
2701 EL_CONVEYOR_BELT_1_LEFT,
2702 EL_CONVEYOR_BELT_2_LEFT,
2703 EL_CONVEYOR_BELT_3_LEFT,
2704 EL_CONVEYOR_BELT_4_LEFT
2706 static int belt_base_active_element[4] =
2708 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2709 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2711 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2716 /* set frame order for belt animation graphic according to belt direction */
2717 for (i = 0; i < 4; i++)
2721 for (j = 0; j < 3; j++)
2723 int element = belt_base_active_element[belt_nr] + j;
2724 int graphic = el2img(element);
2726 if (game.belt_dir[i] == MV_LEFT)
2727 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2729 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2733 for (y = 0; y < lev_fieldy; y++)
2735 for (x = 0; x < lev_fieldx; x++)
2737 int element = Feld[x][y];
2739 for (i = 0; i < 4; i++)
2741 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2743 int e_belt_nr = getBeltNrFromBeltElement(element);
2746 if (e_belt_nr == belt_nr)
2748 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2750 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2758 static void ToggleBeltSwitch(int x, int y)
2760 static int belt_base_element[4] =
2762 EL_CONVEYOR_BELT_1_LEFT,
2763 EL_CONVEYOR_BELT_2_LEFT,
2764 EL_CONVEYOR_BELT_3_LEFT,
2765 EL_CONVEYOR_BELT_4_LEFT
2767 static int belt_base_active_element[4] =
2769 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2770 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2771 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2772 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2774 static int belt_base_switch_element[4] =
2776 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2777 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2778 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2779 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2781 static int belt_move_dir[4] =
2789 int element = Feld[x][y];
2790 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2791 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2792 int belt_dir = belt_move_dir[belt_dir_nr];
2795 if (!IS_BELT_SWITCH(element))
2798 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2799 game.belt_dir[belt_nr] = belt_dir;
2801 if (belt_dir_nr == 3)
2804 /* set frame order for belt animation graphic according to belt direction */
2805 for (i = 0; i < 3; i++)
2807 int element = belt_base_active_element[belt_nr] + i;
2808 int graphic = el2img(element);
2810 if (belt_dir == MV_LEFT)
2811 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2813 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2816 for (yy = 0; yy < lev_fieldy; yy++)
2818 for (xx = 0; xx < lev_fieldx; xx++)
2820 int element = Feld[xx][yy];
2822 if (IS_BELT_SWITCH(element))
2824 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2826 if (e_belt_nr == belt_nr)
2828 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2829 DrawLevelField(xx, yy);
2832 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2834 int e_belt_nr = getBeltNrFromBeltElement(element);
2836 if (e_belt_nr == belt_nr)
2838 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2840 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2841 DrawLevelField(xx, yy);
2844 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2846 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2848 if (e_belt_nr == belt_nr)
2850 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2852 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2853 DrawLevelField(xx, yy);
2860 static void ToggleSwitchgateSwitch(int x, int y)
2864 game.switchgate_pos = !game.switchgate_pos;
2866 for (yy = 0; yy < lev_fieldy; yy++)
2868 for (xx = 0; xx < lev_fieldx; xx++)
2870 int element = Feld[xx][yy];
2872 if (element == EL_SWITCHGATE_SWITCH_UP ||
2873 element == EL_SWITCHGATE_SWITCH_DOWN)
2875 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2876 DrawLevelField(xx, yy);
2878 else if (element == EL_SWITCHGATE_OPEN ||
2879 element == EL_SWITCHGATE_OPENING)
2881 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2883 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2885 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2888 else if (element == EL_SWITCHGATE_CLOSED ||
2889 element == EL_SWITCHGATE_CLOSING)
2891 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2893 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2895 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2902 static int getInvisibleActiveFromInvisibleElement(int element)
2904 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2905 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2906 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2910 static int getInvisibleFromInvisibleActiveElement(int element)
2912 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2913 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2914 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2918 static void RedrawAllLightSwitchesAndInvisibleElements()
2922 for (y = 0; y < lev_fieldy; y++)
2924 for (x = 0; x < lev_fieldx; x++)
2926 int element = Feld[x][y];
2928 if (element == EL_LIGHT_SWITCH &&
2929 game.light_time_left > 0)
2931 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2932 DrawLevelField(x, y);
2934 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2935 game.light_time_left == 0)
2937 Feld[x][y] = EL_LIGHT_SWITCH;
2938 DrawLevelField(x, y);
2940 else if (element == EL_INVISIBLE_STEELWALL ||
2941 element == EL_INVISIBLE_WALL ||
2942 element == EL_INVISIBLE_SAND)
2944 if (game.light_time_left > 0)
2945 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2947 DrawLevelField(x, y);
2949 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2950 element == EL_INVISIBLE_WALL_ACTIVE ||
2951 element == EL_INVISIBLE_SAND_ACTIVE)
2953 if (game.light_time_left == 0)
2954 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2956 DrawLevelField(x, y);
2962 static void ToggleLightSwitch(int x, int y)
2964 int element = Feld[x][y];
2966 game.light_time_left =
2967 (element == EL_LIGHT_SWITCH ?
2968 level.time_light * FRAMES_PER_SECOND : 0);
2970 RedrawAllLightSwitchesAndInvisibleElements();
2973 static void ActivateTimegateSwitch(int x, int y)
2977 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2979 for (yy = 0; yy < lev_fieldy; yy++)
2981 for (xx = 0; xx < lev_fieldx; xx++)
2983 int element = Feld[xx][yy];
2985 if (element == EL_TIMEGATE_CLOSED ||
2986 element == EL_TIMEGATE_CLOSING)
2988 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2989 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2993 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2995 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2996 DrawLevelField(xx, yy);
3003 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3006 inline static int getElementMoveStepsize(int x, int y)
3008 int element = Feld[x][y];
3009 int direction = MovDir[x][y];
3010 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3011 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3012 int horiz_move = (dx != 0);
3013 int sign = (horiz_move ? dx : dy);
3014 int step = sign * element_info[element].move_stepsize;
3016 /* special values for move stepsize for spring and things on conveyor belt */
3019 if (CAN_FALL(element) &&
3020 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3021 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3022 else if (element == EL_SPRING)
3023 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3029 void Impact(int x, int y)
3031 boolean lastline = (y == lev_fieldy-1);
3032 boolean object_hit = FALSE;
3033 boolean impact = (lastline || object_hit);
3034 int element = Feld[x][y];
3035 int smashed = EL_UNDEFINED;
3037 if (!lastline) /* check if element below was hit */
3039 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3042 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3043 MovDir[x][y + 1] != MV_DOWN ||
3044 MovPos[x][y + 1] <= TILEY / 2));
3046 /* do not smash moving elements that left the smashed field in time */
3047 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3048 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3052 smashed = MovingOrBlocked2Element(x, y + 1);
3054 impact = (lastline || object_hit);
3057 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3063 /* only reset graphic animation if graphic really changes after impact */
3065 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3067 ResetGfxAnimation(x, y);
3068 DrawLevelField(x, y);
3071 if (impact && CAN_EXPLODE_IMPACT(element))
3076 else if (impact && element == EL_PEARL)
3078 Feld[x][y] = EL_PEARL_BREAKING;
3079 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3082 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3084 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3089 if (impact && element == EL_AMOEBA_DROP)
3091 if (object_hit && IS_PLAYER(x, y + 1))
3092 KillHeroUnlessProtected(x, y + 1);
3093 else if (object_hit && smashed == EL_PENGUIN)
3097 Feld[x][y] = EL_AMOEBA_GROWING;
3098 Store[x][y] = EL_AMOEBA_WET;
3100 ResetRandomAnimationValue(x, y);
3105 if (object_hit) /* check which object was hit */
3107 if (CAN_PASS_MAGIC_WALL(element) &&
3108 (smashed == EL_MAGIC_WALL ||
3109 smashed == EL_BD_MAGIC_WALL))
3112 int activated_magic_wall =
3113 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3114 EL_BD_MAGIC_WALL_ACTIVE);
3116 /* activate magic wall / mill */
3117 for (yy = 0; yy < lev_fieldy; yy++)
3118 for (xx = 0; xx < lev_fieldx; xx++)
3119 if (Feld[xx][yy] == smashed)
3120 Feld[xx][yy] = activated_magic_wall;
3122 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3123 game.magic_wall_active = TRUE;
3125 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3126 SND_MAGIC_WALL_ACTIVATING :
3127 SND_BD_MAGIC_WALL_ACTIVATING));
3130 if (IS_PLAYER(x, y + 1))
3132 if (CAN_SMASH_PLAYER(element))
3134 KillHeroUnlessProtected(x, y + 1);
3138 else if (smashed == EL_PENGUIN)
3140 if (CAN_SMASH_PLAYER(element))
3146 else if (element == EL_BD_DIAMOND)
3148 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3154 else if ((element == EL_SP_INFOTRON ||
3155 element == EL_SP_ZONK) &&
3156 (smashed == EL_SP_SNIKSNAK ||
3157 smashed == EL_SP_ELECTRON ||
3158 smashed == EL_SP_DISK_ORANGE))
3164 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3170 else if (CAN_SMASH_EVERYTHING(element))
3172 if (IS_CLASSIC_ENEMY(smashed) ||
3173 CAN_EXPLODE_SMASHED(smashed))
3178 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3180 if (smashed == EL_LAMP ||
3181 smashed == EL_LAMP_ACTIVE)
3186 else if (smashed == EL_NUT)
3188 Feld[x][y + 1] = EL_NUT_BREAKING;
3189 PlayLevelSound(x, y, SND_NUT_BREAKING);
3190 RaiseScoreElement(EL_NUT);
3193 else if (smashed == EL_PEARL)
3195 Feld[x][y + 1] = EL_PEARL_BREAKING;
3196 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3199 else if (smashed == EL_DIAMOND)
3201 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3202 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3205 else if (IS_BELT_SWITCH(smashed))
3207 ToggleBeltSwitch(x, y + 1);
3209 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3210 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3212 ToggleSwitchgateSwitch(x, y + 1);
3214 else if (smashed == EL_LIGHT_SWITCH ||
3215 smashed == EL_LIGHT_SWITCH_ACTIVE)
3217 ToggleLightSwitch(x, y + 1);
3221 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3223 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3224 CE_OTHER_IS_SWITCHING);
3225 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3231 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3236 /* play sound of magic wall / mill */
3238 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3239 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3241 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3242 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3243 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3244 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3249 /* play sound of object that hits the ground */
3250 if (lastline || object_hit)
3251 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3254 inline static void TurnRoundExt(int x, int y)
3266 { 0, 0 }, { 0, 0 }, { 0, 0 },
3271 int left, right, back;
3275 { MV_DOWN, MV_UP, MV_RIGHT },
3276 { MV_UP, MV_DOWN, MV_LEFT },
3278 { MV_LEFT, MV_RIGHT, MV_DOWN },
3282 { MV_RIGHT, MV_LEFT, MV_UP }
3285 int element = Feld[x][y];
3286 int move_pattern = element_info[element].move_pattern;
3288 int old_move_dir = MovDir[x][y];
3289 int left_dir = turn[old_move_dir].left;
3290 int right_dir = turn[old_move_dir].right;
3291 int back_dir = turn[old_move_dir].back;
3293 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3294 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3295 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3296 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3298 int left_x = x + left_dx, left_y = y + left_dy;
3299 int right_x = x + right_dx, right_y = y + right_dy;
3300 int move_x = x + move_dx, move_y = y + move_dy;
3304 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3306 TestIfBadThingTouchesOtherBadThing(x, y);
3308 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3309 MovDir[x][y] = right_dir;
3310 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3311 MovDir[x][y] = left_dir;
3313 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3315 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3318 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3319 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3321 TestIfBadThingTouchesOtherBadThing(x, y);
3323 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3324 MovDir[x][y] = left_dir;
3325 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3326 MovDir[x][y] = right_dir;
3328 if ((element == EL_SPACESHIP ||
3329 element == EL_SP_SNIKSNAK ||
3330 element == EL_SP_ELECTRON)
3331 && MovDir[x][y] != old_move_dir)
3333 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3336 else if (element == EL_YAMYAM)
3338 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3339 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3341 if (can_turn_left && can_turn_right)
3342 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3343 else if (can_turn_left)
3344 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3345 else if (can_turn_right)
3346 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3348 MovDir[x][y] = back_dir;
3350 MovDelay[x][y] = 16 + 16 * RND(3);
3352 else if (element == EL_DARK_YAMYAM)
3354 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3355 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3357 if (can_turn_left && can_turn_right)
3358 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3359 else if (can_turn_left)
3360 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3361 else if (can_turn_right)
3362 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3364 MovDir[x][y] = back_dir;
3366 MovDelay[x][y] = 16 + 16 * RND(3);
3368 else if (element == EL_PACMAN)
3370 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3371 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3373 if (can_turn_left && can_turn_right)
3374 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3375 else if (can_turn_left)
3376 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3377 else if (can_turn_right)
3378 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3380 MovDir[x][y] = back_dir;
3382 MovDelay[x][y] = 6 + RND(40);
3384 else if (element == EL_PIG)
3386 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3387 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3388 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3389 boolean should_turn_left, should_turn_right, should_move_on;
3391 int rnd = RND(rnd_value);
3393 should_turn_left = (can_turn_left &&
3395 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3396 y + back_dy + left_dy)));
3397 should_turn_right = (can_turn_right &&
3399 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3400 y + back_dy + right_dy)));
3401 should_move_on = (can_move_on &&
3404 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3405 y + move_dy + left_dy) ||
3406 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3407 y + move_dy + right_dy)));
3409 if (should_turn_left || should_turn_right || should_move_on)
3411 if (should_turn_left && should_turn_right && should_move_on)
3412 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3413 rnd < 2 * rnd_value / 3 ? right_dir :
3415 else if (should_turn_left && should_turn_right)
3416 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3417 else if (should_turn_left && should_move_on)
3418 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3419 else if (should_turn_right && should_move_on)
3420 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3421 else if (should_turn_left)
3422 MovDir[x][y] = left_dir;
3423 else if (should_turn_right)
3424 MovDir[x][y] = right_dir;
3425 else if (should_move_on)
3426 MovDir[x][y] = old_move_dir;
3428 else if (can_move_on && rnd > rnd_value / 8)
3429 MovDir[x][y] = old_move_dir;
3430 else if (can_turn_left && can_turn_right)
3431 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3432 else if (can_turn_left && rnd > rnd_value / 8)
3433 MovDir[x][y] = left_dir;
3434 else if (can_turn_right && rnd > rnd_value/8)
3435 MovDir[x][y] = right_dir;
3437 MovDir[x][y] = back_dir;
3439 xx = x + move_xy[MovDir[x][y]].x;
3440 yy = y + move_xy[MovDir[x][y]].y;
3442 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3443 MovDir[x][y] = old_move_dir;
3447 else if (element == EL_DRAGON)
3449 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3450 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3451 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3453 int rnd = RND(rnd_value);
3456 if (FrameCounter < 1 && x == 0 && y == 29)
3457 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3460 if (can_move_on && rnd > rnd_value / 8)
3461 MovDir[x][y] = old_move_dir;
3462 else if (can_turn_left && can_turn_right)
3463 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3464 else if (can_turn_left && rnd > rnd_value / 8)
3465 MovDir[x][y] = left_dir;
3466 else if (can_turn_right && rnd > rnd_value / 8)
3467 MovDir[x][y] = right_dir;
3469 MovDir[x][y] = back_dir;
3471 xx = x + move_xy[MovDir[x][y]].x;
3472 yy = y + move_xy[MovDir[x][y]].y;
3475 if (FrameCounter < 1 && x == 0 && y == 29)
3476 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3477 xx, yy, Feld[xx][yy],
3482 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3483 MovDir[x][y] = old_move_dir;
3485 if (!IS_FREE(xx, yy))
3486 MovDir[x][y] = old_move_dir;
3490 if (FrameCounter < 1 && x == 0 && y == 29)
3491 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3496 else if (element == EL_MOLE)
3498 boolean can_move_on =
3499 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3500 IS_AMOEBOID(Feld[move_x][move_y]) ||
3501 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3504 boolean can_turn_left =
3505 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3506 IS_AMOEBOID(Feld[left_x][left_y])));
3508 boolean can_turn_right =
3509 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3510 IS_AMOEBOID(Feld[right_x][right_y])));
3512 if (can_turn_left && can_turn_right)
3513 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3514 else if (can_turn_left)
3515 MovDir[x][y] = left_dir;
3517 MovDir[x][y] = right_dir;
3520 if (MovDir[x][y] != old_move_dir)
3523 else if (element == EL_BALLOON)
3525 MovDir[x][y] = game.balloon_dir;
3528 else if (element == EL_SPRING)
3530 if (MovDir[x][y] & MV_HORIZONTAL &&
3531 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3532 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3533 MovDir[x][y] = MV_NO_MOVING;
3537 else if (element == EL_ROBOT ||
3538 element == EL_SATELLITE ||
3539 element == EL_PENGUIN)
3541 int attr_x = -1, attr_y = -1;
3552 for (i = 0; i < MAX_PLAYERS; i++)
3554 struct PlayerInfo *player = &stored_player[i];
3555 int jx = player->jx, jy = player->jy;
3557 if (!player->active)
3561 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3569 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3575 if (element == EL_PENGUIN)
3578 static int xy[4][2] =
3586 for (i = 0; i < 4; i++)
3588 int ex = x + xy[i % 4][0];
3589 int ey = y + xy[i % 4][1];
3591 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3600 MovDir[x][y] = MV_NO_MOVING;
3602 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3603 else if (attr_x > x)
3604 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3606 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3607 else if (attr_y > y)
3608 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3610 if (element == EL_ROBOT)
3614 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3615 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3616 Moving2Blocked(x, y, &newx, &newy);
3618 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3619 MovDelay[x][y] = 8 + 8 * !RND(3);
3621 MovDelay[x][y] = 16;
3623 else if (element == EL_PENGUIN)
3629 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3631 boolean first_horiz = RND(2);
3632 int new_move_dir = MovDir[x][y];
3635 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3636 Moving2Blocked(x, y, &newx, &newy);
3638 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3642 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3643 Moving2Blocked(x, y, &newx, &newy);
3645 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3648 MovDir[x][y] = old_move_dir;
3652 else /* (element == EL_SATELLITE) */
3658 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3660 boolean first_horiz = RND(2);
3661 int new_move_dir = MovDir[x][y];
3664 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3665 Moving2Blocked(x, y, &newx, &newy);
3667 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3671 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3672 Moving2Blocked(x, y, &newx, &newy);
3674 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3677 MovDir[x][y] = old_move_dir;
3682 else if (move_pattern == MV_ALL_DIRECTIONS ||
3683 move_pattern == MV_TURNING_LEFT ||
3684 move_pattern == MV_TURNING_RIGHT)
3686 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3687 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3689 if (move_pattern == MV_TURNING_LEFT)
3690 MovDir[x][y] = left_dir;
3691 else if (move_pattern == MV_TURNING_RIGHT)
3692 MovDir[x][y] = right_dir;
3693 else if (can_turn_left && can_turn_right)
3694 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3695 else if (can_turn_left)
3696 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3697 else if (can_turn_right)
3698 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3700 MovDir[x][y] = back_dir;
3702 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3704 else if (move_pattern == MV_HORIZONTAL ||
3705 move_pattern == MV_VERTICAL)
3707 if (move_pattern & old_move_dir)
3708 MovDir[x][y] = back_dir;
3709 else if (move_pattern == MV_HORIZONTAL)
3710 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3711 else if (move_pattern == MV_VERTICAL)
3712 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3714 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3716 else if (move_pattern & MV_ANY_DIRECTION)
3718 MovDir[x][y] = move_pattern;
3719 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3721 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3723 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3724 MovDir[x][y] = left_dir;
3725 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3726 MovDir[x][y] = right_dir;
3728 if (MovDir[x][y] != old_move_dir)
3729 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3731 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3733 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3734 MovDir[x][y] = right_dir;
3735 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3736 MovDir[x][y] = left_dir;
3738 if (MovDir[x][y] != old_move_dir)
3739 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3741 else if (move_pattern == MV_TOWARDS_PLAYER ||
3742 move_pattern == MV_AWAY_FROM_PLAYER)
3744 int attr_x = -1, attr_y = -1;
3746 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3757 for (i = 0; i < MAX_PLAYERS; i++)
3759 struct PlayerInfo *player = &stored_player[i];
3760 int jx = player->jx, jy = player->jy;
3762 if (!player->active)
3766 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3774 MovDir[x][y] = MV_NO_MOVING;
3776 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3777 else if (attr_x > x)
3778 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3780 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3781 else if (attr_y > y)
3782 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3784 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3786 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3788 boolean first_horiz = RND(2);
3789 int new_move_dir = MovDir[x][y];
3792 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3793 Moving2Blocked(x, y, &newx, &newy);
3795 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3799 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3800 Moving2Blocked(x, y, &newx, &newy);
3802 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3805 MovDir[x][y] = old_move_dir;
3808 else if (move_pattern == MV_WHEN_PUSHED)
3810 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3811 MovDir[x][y] = MV_NO_MOVING;
3815 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3816 element == EL_MAZE_RUNNER)
3818 static int test_xy[7][2] =
3828 static int test_dir[7] =
3838 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3839 int move_preference = -1000000; /* start with very low preference */
3840 int new_move_dir = MV_NO_MOVING;
3841 int start_test = RND(4);
3844 for (i = 0; i < 4; i++)
3846 int move_dir = test_dir[start_test + i];
3847 int move_dir_preference;
3849 xx = x + test_xy[start_test + i][0];
3850 yy = y + test_xy[start_test + i][1];
3852 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3853 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3855 new_move_dir = move_dir;
3860 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3863 move_dir_preference = -1 * RunnerVisit[xx][yy];
3864 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3865 move_dir_preference = PlayerVisit[xx][yy];
3867 if (move_dir_preference > move_preference)
3869 /* prefer field that has not been visited for the longest time */
3870 move_preference = move_dir_preference;
3871 new_move_dir = move_dir;
3873 else if (move_dir_preference == move_preference &&
3874 move_dir == old_move_dir)
3876 /* prefer last direction when all directions are preferred equally */
3877 move_preference = move_dir_preference;
3878 new_move_dir = move_dir;
3882 MovDir[x][y] = new_move_dir;
3883 if (old_move_dir != new_move_dir)
3888 static void TurnRound(int x, int y)
3890 int direction = MovDir[x][y];
3893 GfxDir[x][y] = MovDir[x][y];
3899 GfxDir[x][y] = MovDir[x][y];
3902 if (direction != MovDir[x][y])
3907 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3910 GfxAction[x][y] = ACTION_WAITING;
3914 static boolean JustBeingPushed(int x, int y)
3918 for (i = 0; i < MAX_PLAYERS; i++)
3920 struct PlayerInfo *player = &stored_player[i];
3922 if (player->active && player->is_pushing && player->MovPos)
3924 int next_jx = player->jx + (player->jx - player->last_jx);
3925 int next_jy = player->jy + (player->jy - player->last_jy);
3927 if (x == next_jx && y == next_jy)
3935 void StartMoving(int x, int y)
3937 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3938 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3939 int element = Feld[x][y];
3945 if (MovDelay[x][y] == 0)
3946 GfxAction[x][y] = ACTION_DEFAULT;
3948 /* !!! this should be handled more generic (not only for mole) !!! */
3949 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3950 GfxAction[x][y] = ACTION_DEFAULT;
3953 if (CAN_FALL(element) && y < lev_fieldy - 1)
3955 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3956 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3957 if (JustBeingPushed(x, y))
3960 if (element == EL_QUICKSAND_FULL)
3962 if (IS_FREE(x, y + 1))
3964 InitMovingField(x, y, MV_DOWN);
3965 started_moving = TRUE;
3967 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3968 Store[x][y] = EL_ROCK;
3970 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3972 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3975 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3977 if (!MovDelay[x][y])
3978 MovDelay[x][y] = TILEY + 1;
3987 Feld[x][y] = EL_QUICKSAND_EMPTY;
3988 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3989 Store[x][y + 1] = Store[x][y];
3992 PlayLevelSoundAction(x, y, ACTION_FILLING);
3994 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3998 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3999 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4001 InitMovingField(x, y, MV_DOWN);
4002 started_moving = TRUE;
4004 Feld[x][y] = EL_QUICKSAND_FILLING;
4005 Store[x][y] = element;
4007 PlayLevelSoundAction(x, y, ACTION_FILLING);
4009 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4012 else if (element == EL_MAGIC_WALL_FULL)
4014 if (IS_FREE(x, y + 1))
4016 InitMovingField(x, y, MV_DOWN);
4017 started_moving = TRUE;
4019 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4020 Store[x][y] = EL_CHANGED(Store[x][y]);
4022 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4024 if (!MovDelay[x][y])
4025 MovDelay[x][y] = TILEY/4 + 1;
4034 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4035 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4036 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4040 else if (element == EL_BD_MAGIC_WALL_FULL)
4042 if (IS_FREE(x, y + 1))
4044 InitMovingField(x, y, MV_DOWN);
4045 started_moving = TRUE;
4047 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4048 Store[x][y] = EL_CHANGED2(Store[x][y]);
4050 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4052 if (!MovDelay[x][y])
4053 MovDelay[x][y] = TILEY/4 + 1;
4062 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4063 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4064 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4068 else if (CAN_PASS_MAGIC_WALL(element) &&
4069 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4070 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4072 InitMovingField(x, y, MV_DOWN);
4073 started_moving = TRUE;
4076 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4077 EL_BD_MAGIC_WALL_FILLING);
4078 Store[x][y] = element;
4081 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4083 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4088 InitMovingField(x, y, MV_DOWN);
4089 started_moving = TRUE;
4091 Store[x][y] = EL_ACID;
4093 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4094 GfxAction[x][y + 1] = ACTION_ACTIVE;
4098 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4099 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4100 (Feld[x][y + 1] == EL_BLOCKED)) ||
4101 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4102 CAN_SMASH(element) && WasJustFalling[x][y] &&
4103 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4107 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4108 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4109 WasJustMoving[x][y] && !Pushed[x][y + 1])
4111 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4112 WasJustMoving[x][y])
4117 /* this is needed for a special case not covered by calling "Impact()"
4118 from "ContinueMoving()": if an element moves to a tile directly below
4119 another element which was just falling on that tile (which was empty
4120 in the previous frame), the falling element above would just stop
4121 instead of smashing the element below (in previous version, the above
4122 element was just checked for "moving" instead of "falling", resulting
4123 in incorrect smashes caused by horizontal movement of the above
4124 element; also, the case of the player being the element to smash was
4125 simply not covered here... :-/ ) */
4129 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4131 if (MovDir[x][y] == MV_NO_MOVING)
4133 InitMovingField(x, y, MV_DOWN);
4134 started_moving = TRUE;
4137 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4139 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4140 MovDir[x][y] = MV_DOWN;
4142 InitMovingField(x, y, MV_DOWN);
4143 started_moving = TRUE;
4145 else if (element == EL_AMOEBA_DROP)
4147 Feld[x][y] = EL_AMOEBA_GROWING;
4148 Store[x][y] = EL_AMOEBA_WET;
4150 /* Store[x][y + 1] must be zero, because:
4151 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4154 #if OLD_GAME_BEHAVIOUR
4155 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4157 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4158 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4159 element != EL_DX_SUPABOMB)
4162 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4163 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4164 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4165 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4168 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4169 (IS_FREE(x - 1, y + 1) ||
4170 Feld[x - 1][y + 1] == EL_ACID));
4171 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4172 (IS_FREE(x + 1, y + 1) ||
4173 Feld[x + 1][y + 1] == EL_ACID));
4174 boolean can_fall_any = (can_fall_left || can_fall_right);
4175 boolean can_fall_both = (can_fall_left && can_fall_right);
4177 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4179 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4181 if (slippery_type == SLIPPERY_ONLY_LEFT)
4182 can_fall_right = FALSE;
4183 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4184 can_fall_left = FALSE;
4185 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4186 can_fall_right = FALSE;
4187 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4188 can_fall_left = FALSE;
4190 can_fall_any = (can_fall_left || can_fall_right);
4191 can_fall_both = (can_fall_left && can_fall_right);
4196 if (can_fall_both &&
4197 (game.emulation != EMU_BOULDERDASH &&
4198 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4199 can_fall_left = !(can_fall_right = RND(2));
4201 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4202 started_moving = TRUE;
4205 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4207 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4208 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4209 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4210 int belt_dir = game.belt_dir[belt_nr];
4212 if ((belt_dir == MV_LEFT && left_is_free) ||
4213 (belt_dir == MV_RIGHT && right_is_free))
4215 InitMovingField(x, y, belt_dir);
4216 started_moving = TRUE;
4218 GfxAction[x][y] = ACTION_DEFAULT;
4223 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4224 if (CAN_MOVE(element) && !started_moving)
4226 int move_pattern = element_info[element].move_pattern;
4230 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4233 if ((element == EL_SATELLITE ||
4234 element == EL_BALLOON ||
4235 element == EL_SPRING)
4236 && JustBeingPushed(x, y))
4242 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4243 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4245 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4247 Moving2Blocked(x, y, &newx, &newy);
4248 if (Feld[newx][newy] == EL_BLOCKED)
4249 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4255 if (FrameCounter < 1 && x == 0 && y == 29)
4256 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4259 if (!MovDelay[x][y]) /* start new movement phase */
4261 /* all objects that can change their move direction after each step
4262 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4264 if (element != EL_YAMYAM &&
4265 element != EL_DARK_YAMYAM &&
4266 element != EL_PACMAN &&
4267 !(move_pattern & MV_ANY_DIRECTION) &&
4268 move_pattern != MV_TURNING_LEFT &&
4269 move_pattern != MV_TURNING_RIGHT)
4274 if (FrameCounter < 1 && x == 0 && y == 29)
4275 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4278 if (MovDelay[x][y] && (element == EL_BUG ||
4279 element == EL_SPACESHIP ||
4280 element == EL_SP_SNIKSNAK ||
4281 element == EL_SP_ELECTRON ||
4282 element == EL_MOLE))
4283 DrawLevelField(x, y);
4287 if (MovDelay[x][y]) /* wait some time before next movement */
4292 if (element == EL_YAMYAM)
4295 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4296 DrawLevelElementAnimation(x, y, element);
4300 if (MovDelay[x][y]) /* element still has to wait some time */
4303 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4304 ResetGfxAnimation(x, y);
4308 if (GfxAction[x][y] != ACTION_WAITING)
4309 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4311 GfxAction[x][y] = ACTION_WAITING;
4315 if (element == EL_ROBOT ||
4317 element == EL_PACMAN ||
4319 element == EL_YAMYAM ||
4320 element == EL_DARK_YAMYAM)
4323 DrawLevelElementAnimation(x, y, element);
4325 DrawLevelElementAnimationIfNeeded(x, y, element);
4327 PlayLevelSoundAction(x, y, ACTION_WAITING);
4329 else if (element == EL_SP_ELECTRON)
4330 DrawLevelElementAnimationIfNeeded(x, y, element);
4331 else if (element == EL_DRAGON)
4334 int dir = MovDir[x][y];
4335 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4336 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4337 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4338 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4339 dir == MV_UP ? IMG_FLAMES_1_UP :
4340 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4341 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4344 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4347 GfxAction[x][y] = ACTION_ATTACKING;
4349 if (IS_PLAYER(x, y))
4350 DrawPlayerField(x, y);
4352 DrawLevelField(x, y);
4354 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4356 for (i = 1; i <= 3; i++)
4358 int xx = x + i * dx;
4359 int yy = y + i * dy;
4360 int sx = SCREENX(xx);
4361 int sy = SCREENY(yy);
4362 int flame_graphic = graphic + (i - 1);
4364 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4369 int flamed = MovingOrBlocked2Element(xx, yy);
4371 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4374 RemoveMovingField(xx, yy);
4376 Feld[xx][yy] = EL_FLAMES;
4377 if (IN_SCR_FIELD(sx, sy))
4379 DrawLevelFieldCrumbledSand(xx, yy);
4380 DrawGraphic(sx, sy, flame_graphic, frame);
4385 if (Feld[xx][yy] == EL_FLAMES)
4386 Feld[xx][yy] = EL_EMPTY;
4387 DrawLevelField(xx, yy);
4392 if (MovDelay[x][y]) /* element still has to wait some time */
4394 PlayLevelSoundAction(x, y, ACTION_WAITING);
4400 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4401 for all other elements GfxAction will be set by InitMovingField() */
4402 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4403 GfxAction[x][y] = ACTION_MOVING;
4407 /* now make next step */
4409 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4411 if (DONT_COLLIDE_WITH(element) &&
4412 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4413 !PLAYER_PROTECTED(newx, newy))
4416 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4419 /* player killed by element which is deadly when colliding with */
4421 KillHero(PLAYERINFO(newx, newy));
4426 else if ((element == EL_PENGUIN ||
4427 element == EL_ROBOT ||
4428 element == EL_SATELLITE ||
4429 element == EL_BALLOON ||
4430 IS_CUSTOM_ELEMENT(element)) &&
4431 IN_LEV_FIELD(newx, newy) &&
4432 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4435 Store[x][y] = EL_ACID;
4437 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4439 if (Feld[newx][newy] == EL_EXIT_OPEN)
4441 Feld[x][y] = EL_EMPTY;
4442 DrawLevelField(x, y);
4444 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4445 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4446 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4448 local_player->friends_still_needed--;
4449 if (!local_player->friends_still_needed &&
4450 !local_player->GameOver && AllPlayersGone)
4451 local_player->LevelSolved = local_player->GameOver = TRUE;
4455 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4457 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4458 DrawLevelField(newx, newy);
4460 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4462 else if (!IS_FREE(newx, newy))
4464 GfxAction[x][y] = ACTION_WAITING;
4466 if (IS_PLAYER(x, y))
4467 DrawPlayerField(x, y);
4469 DrawLevelField(x, y);
4473 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4475 if (IS_FOOD_PIG(Feld[newx][newy]))
4477 if (IS_MOVING(newx, newy))
4478 RemoveMovingField(newx, newy);
4481 Feld[newx][newy] = EL_EMPTY;
4482 DrawLevelField(newx, newy);
4485 PlayLevelSound(x, y, SND_PIG_DIGGING);
4487 else if (!IS_FREE(newx, newy))
4489 if (IS_PLAYER(x, y))
4490 DrawPlayerField(x, y);
4492 DrawLevelField(x, y);
4496 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4497 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4499 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4501 if (IS_MOVING(newx, newy))
4502 RemoveMovingField(newx, newy);
4505 Feld[newx][newy] = EL_EMPTY;
4506 DrawLevelField(newx, newy);
4509 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4511 else if (!IS_FREE(newx, newy))
4514 if (IS_PLAYER(x, y))
4515 DrawPlayerField(x, y);
4517 DrawLevelField(x, y);
4522 RunnerVisit[x][y] = FrameCounter;
4523 PlayerVisit[x][y] /= 8; /* expire player visit path */
4525 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4527 if (!IS_FREE(newx, newy))
4529 if (IS_PLAYER(x, y))
4530 DrawPlayerField(x, y);
4532 DrawLevelField(x, y);
4538 boolean wanna_flame = !RND(10);
4539 int dx = newx - x, dy = newy - y;
4540 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4541 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4542 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4543 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4544 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4545 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4548 IS_CLASSIC_ENEMY(element1) ||
4549 IS_CLASSIC_ENEMY(element2)) &&
4550 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4551 element1 != EL_FLAMES && element2 != EL_FLAMES)
4554 ResetGfxAnimation(x, y);
4555 GfxAction[x][y] = ACTION_ATTACKING;
4558 if (IS_PLAYER(x, y))
4559 DrawPlayerField(x, y);
4561 DrawLevelField(x, y);
4563 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4565 MovDelay[x][y] = 50;
4567 Feld[newx][newy] = EL_FLAMES;
4568 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4569 Feld[newx1][newy1] = EL_FLAMES;
4570 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4571 Feld[newx2][newy2] = EL_FLAMES;
4577 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4578 Feld[newx][newy] == EL_DIAMOND)
4580 if (IS_MOVING(newx, newy))
4581 RemoveMovingField(newx, newy);
4584 Feld[newx][newy] = EL_EMPTY;
4585 DrawLevelField(newx, newy);
4588 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4590 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4591 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4593 if (AmoebaNr[newx][newy])
4595 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4596 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4597 Feld[newx][newy] == EL_BD_AMOEBA)
4598 AmoebaCnt[AmoebaNr[newx][newy]]--;
4601 if (IS_MOVING(newx, newy))
4602 RemoveMovingField(newx, newy);
4605 Feld[newx][newy] = EL_EMPTY;
4606 DrawLevelField(newx, newy);
4609 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4611 else if ((element == EL_PACMAN || element == EL_MOLE)
4612 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4614 if (AmoebaNr[newx][newy])
4616 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4617 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4618 Feld[newx][newy] == EL_BD_AMOEBA)
4619 AmoebaCnt[AmoebaNr[newx][newy]]--;
4622 if (element == EL_MOLE)
4624 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4625 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4627 ResetGfxAnimation(x, y);
4628 GfxAction[x][y] = ACTION_DIGGING;
4629 DrawLevelField(x, y);
4631 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4632 return; /* wait for shrinking amoeba */
4634 else /* element == EL_PACMAN */
4636 Feld[newx][newy] = EL_EMPTY;
4637 DrawLevelField(newx, newy);
4638 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4641 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4642 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4643 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4645 /* wait for shrinking amoeba to completely disappear */
4648 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4650 /* object was running against a wall */
4655 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4656 DrawLevelElementAnimation(x, y, element);
4658 if (element == EL_BUG ||
4659 element == EL_SPACESHIP ||
4660 element == EL_SP_SNIKSNAK)
4661 DrawLevelField(x, y);
4662 else if (element == EL_MOLE)
4663 DrawLevelField(x, y);
4664 else if (element == EL_BD_BUTTERFLY ||
4665 element == EL_BD_FIREFLY)
4666 DrawLevelElementAnimationIfNeeded(x, y, element);
4667 else if (element == EL_SATELLITE)
4668 DrawLevelElementAnimationIfNeeded(x, y, element);
4669 else if (element == EL_SP_ELECTRON)
4670 DrawLevelElementAnimationIfNeeded(x, y, element);
4673 if (DONT_TOUCH(element))
4674 TestIfBadThingTouchesHero(x, y);
4677 PlayLevelSoundAction(x, y, ACTION_WAITING);
4683 InitMovingField(x, y, MovDir[x][y]);
4685 PlayLevelSoundAction(x, y, ACTION_MOVING);
4689 ContinueMoving(x, y);
4692 void ContinueMoving(int x, int y)
4694 int element = Feld[x][y];
4695 int direction = MovDir[x][y];
4696 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4697 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4698 int newx = x + dx, newy = y + dy;
4699 int nextx = newx + dx, nexty = newy + dy;
4700 boolean pushed = Pushed[x][y];
4702 MovPos[x][y] += getElementMoveStepsize(x, y);
4704 if (pushed) /* special case: moving object pushed by player */
4705 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4707 if (ABS(MovPos[x][y]) < TILEX)
4709 DrawLevelField(x, y);
4711 return; /* element is still moving */
4714 /* element reached destination field */
4716 Feld[x][y] = EL_EMPTY;
4717 Feld[newx][newy] = element;
4718 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4720 if (element == EL_MOLE)
4722 Feld[x][y] = EL_SAND;
4724 DrawLevelFieldCrumbledSandNeighbours(x, y);
4726 else if (element == EL_QUICKSAND_FILLING)
4728 element = Feld[newx][newy] = get_next_element(element);
4729 Store[newx][newy] = Store[x][y];
4731 else if (element == EL_QUICKSAND_EMPTYING)
4733 Feld[x][y] = get_next_element(element);
4734 element = Feld[newx][newy] = Store[x][y];
4736 else if (element == EL_MAGIC_WALL_FILLING)
4738 element = Feld[newx][newy] = get_next_element(element);
4739 if (!game.magic_wall_active)
4740 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4741 Store[newx][newy] = Store[x][y];
4743 else if (element == EL_MAGIC_WALL_EMPTYING)
4745 Feld[x][y] = get_next_element(element);
4746 if (!game.magic_wall_active)
4747 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4748 element = Feld[newx][newy] = Store[x][y];
4750 else if (element == EL_BD_MAGIC_WALL_FILLING)
4752 element = Feld[newx][newy] = get_next_element(element);
4753 if (!game.magic_wall_active)
4754 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4755 Store[newx][newy] = Store[x][y];
4757 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4759 Feld[x][y] = get_next_element(element);
4760 if (!game.magic_wall_active)
4761 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4762 element = Feld[newx][newy] = Store[x][y];
4764 else if (element == EL_AMOEBA_DROPPING)
4766 Feld[x][y] = get_next_element(element);
4767 element = Feld[newx][newy] = Store[x][y];
4769 else if (element == EL_SOKOBAN_OBJECT)
4772 Feld[x][y] = Back[x][y];
4774 if (Back[newx][newy])
4775 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4777 Back[x][y] = Back[newx][newy] = 0;
4779 else if (Store[x][y] == EL_ACID)
4781 element = Feld[newx][newy] = EL_ACID;
4785 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4786 MovDelay[newx][newy] = 0;
4788 /* copy element change control values to new field */
4789 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4790 ChangePage[newx][newy] = ChangePage[x][y];
4791 Changed[newx][newy] = Changed[x][y];
4792 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4794 ChangeDelay[x][y] = 0;
4795 ChangePage[x][y] = -1;
4796 Changed[x][y] = CE_BITMASK_DEFAULT;
4797 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4799 /* copy animation control values to new field */
4800 GfxFrame[newx][newy] = GfxFrame[x][y];
4801 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4802 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4803 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4805 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4807 ResetGfxAnimation(x, y); /* reset animation values for old field */
4810 /* 2.1.1 (does not work correctly for spring) */
4811 if (!CAN_MOVE(element))
4812 MovDir[newx][newy] = 0;
4816 /* (does not work for falling objects that slide horizontally) */
4817 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4818 MovDir[newx][newy] = 0;
4821 if (!CAN_MOVE(element) ||
4822 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4823 MovDir[newx][newy] = 0;
4826 if (!CAN_MOVE(element) ||
4827 (CAN_FALL(element) && direction == MV_DOWN))
4828 GfxDir[x][y] = MovDir[newx][newy] = 0;
4833 DrawLevelField(x, y);
4834 DrawLevelField(newx, newy);
4836 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4838 /* prevent pushed element from moving on in pushed direction */
4839 if (pushed && CAN_MOVE(element) &&
4840 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4841 !(element_info[element].move_pattern & direction))
4842 TurnRound(newx, newy);
4844 if (!pushed) /* special case: moving object pushed by player */
4846 WasJustMoving[newx][newy] = 3;
4848 if (CAN_FALL(element) && direction == MV_DOWN)
4849 WasJustFalling[newx][newy] = 3;
4852 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4854 TestIfBadThingTouchesHero(newx, newy);
4855 TestIfBadThingTouchesFriend(newx, newy);
4857 if (!IS_CUSTOM_ELEMENT(element))
4858 TestIfBadThingTouchesOtherBadThing(newx, newy);
4860 else if (element == EL_PENGUIN)
4861 TestIfFriendTouchesBadThing(newx, newy);
4863 if (CAN_FALL(element) && direction == MV_DOWN &&
4864 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4868 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4872 if (ChangePage[newx][newy] != -1) /* delayed change */
4873 ChangeElement(newx, newy, ChangePage[newx][newy]);
4876 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4878 /* !!! fix side (direction) orientation here and elsewhere !!! */
4879 CheckElementSideChange(newx, newy, Feld[newx][newy],
4880 direction, CE_COLLISION_ACTIVE, -1);
4883 if (IN_LEV_FIELD(nextx, nexty))
4885 static int opposite_directions[] =
4892 int move_dir_bit = MV_DIR_BIT(direction);
4893 int opposite_direction = opposite_directions[move_dir_bit];
4894 int hitting_side = direction;
4895 int touched_side = opposite_direction;
4896 int hitting_element = Feld[newx][newy];
4897 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4898 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4899 MovDir[nextx][nexty] != direction ||
4900 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4906 CheckElementSideChange(nextx, nexty, Feld[nextx][nexty],
4907 opposite_direction, CE_COLLISION_PASSIVE, -1);
4909 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4910 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4912 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4914 struct ElementChangeInfo *change =
4915 &element_info[hitting_element].change_page[i];
4917 if (change->can_change &&
4918 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4919 change->sides & touched_side &&
4920 change->trigger_element == touched_element)
4922 CheckElementSideChange(newx, newy, hitting_element,
4923 CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4929 if (IS_CUSTOM_ELEMENT(touched_element) &&
4930 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4932 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4934 struct ElementChangeInfo *change =
4935 &element_info[touched_element].change_page[i];
4937 if (change->can_change &&
4938 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4939 change->sides & hitting_side &&
4940 change->trigger_element == hitting_element)
4942 CheckElementSideChange(nextx, nexty, touched_element,
4943 CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4953 TestIfPlayerTouchesCustomElement(newx, newy);
4954 TestIfElementTouchesCustomElement(newx, newy);
4957 int AmoebeNachbarNr(int ax, int ay)
4960 int element = Feld[ax][ay];
4962 static int xy[4][2] =
4970 for (i = 0; i < 4; i++)
4972 int x = ax + xy[i][0];
4973 int y = ay + xy[i][1];
4975 if (!IN_LEV_FIELD(x, y))
4978 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4979 group_nr = AmoebaNr[x][y];
4985 void AmoebenVereinigen(int ax, int ay)
4987 int i, x, y, xx, yy;
4988 int new_group_nr = AmoebaNr[ax][ay];
4989 static int xy[4][2] =
4997 if (new_group_nr == 0)
5000 for (i = 0; i < 4; i++)
5005 if (!IN_LEV_FIELD(x, y))
5008 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5009 Feld[x][y] == EL_BD_AMOEBA ||
5010 Feld[x][y] == EL_AMOEBA_DEAD) &&
5011 AmoebaNr[x][y] != new_group_nr)
5013 int old_group_nr = AmoebaNr[x][y];
5015 if (old_group_nr == 0)
5018 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5019 AmoebaCnt[old_group_nr] = 0;
5020 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5021 AmoebaCnt2[old_group_nr] = 0;
5023 for (yy = 0; yy < lev_fieldy; yy++)
5025 for (xx = 0; xx < lev_fieldx; xx++)
5027 if (AmoebaNr[xx][yy] == old_group_nr)
5028 AmoebaNr[xx][yy] = new_group_nr;
5035 void AmoebeUmwandeln(int ax, int ay)
5039 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5041 int group_nr = AmoebaNr[ax][ay];
5046 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5047 printf("AmoebeUmwandeln(): This should never happen!\n");
5052 for (y = 0; y < lev_fieldy; y++)
5054 for (x = 0; x < lev_fieldx; x++)
5056 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5059 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5063 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5064 SND_AMOEBA_TURNING_TO_GEM :
5065 SND_AMOEBA_TURNING_TO_ROCK));
5070 static int xy[4][2] =
5078 for (i = 0; i < 4; i++)
5083 if (!IN_LEV_FIELD(x, y))
5086 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5088 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5089 SND_AMOEBA_TURNING_TO_GEM :
5090 SND_AMOEBA_TURNING_TO_ROCK));
5097 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5100 int group_nr = AmoebaNr[ax][ay];
5101 boolean done = FALSE;
5106 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5107 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5112 for (y = 0; y < lev_fieldy; y++)
5114 for (x = 0; x < lev_fieldx; x++)
5116 if (AmoebaNr[x][y] == group_nr &&
5117 (Feld[x][y] == EL_AMOEBA_DEAD ||
5118 Feld[x][y] == EL_BD_AMOEBA ||
5119 Feld[x][y] == EL_AMOEBA_GROWING))
5122 Feld[x][y] = new_element;
5123 InitField(x, y, FALSE);
5124 DrawLevelField(x, y);
5131 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5132 SND_BD_AMOEBA_TURNING_TO_ROCK :
5133 SND_BD_AMOEBA_TURNING_TO_GEM));
5136 void AmoebeWaechst(int x, int y)
5138 static unsigned long sound_delay = 0;
5139 static unsigned long sound_delay_value = 0;
5141 if (!MovDelay[x][y]) /* start new growing cycle */
5145 if (DelayReached(&sound_delay, sound_delay_value))
5148 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5150 if (Store[x][y] == EL_BD_AMOEBA)
5151 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5153 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5155 sound_delay_value = 30;
5159 if (MovDelay[x][y]) /* wait some time before growing bigger */
5162 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5164 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5165 6 - MovDelay[x][y]);
5167 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5170 if (!MovDelay[x][y])
5172 Feld[x][y] = Store[x][y];
5174 DrawLevelField(x, y);
5179 void AmoebaDisappearing(int x, int y)
5181 static unsigned long sound_delay = 0;
5182 static unsigned long sound_delay_value = 0;
5184 if (!MovDelay[x][y]) /* start new shrinking cycle */
5188 if (DelayReached(&sound_delay, sound_delay_value))
5189 sound_delay_value = 30;
5192 if (MovDelay[x][y]) /* wait some time before shrinking */
5195 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5197 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5198 6 - MovDelay[x][y]);
5200 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5203 if (!MovDelay[x][y])
5205 Feld[x][y] = EL_EMPTY;
5206 DrawLevelField(x, y);
5208 /* don't let mole enter this field in this cycle;
5209 (give priority to objects falling to this field from above) */
5215 void AmoebeAbleger(int ax, int ay)
5218 int element = Feld[ax][ay];
5219 int graphic = el2img(element);
5220 int newax = ax, neway = ay;
5221 static int xy[4][2] =
5229 if (!level.amoeba_speed)
5231 Feld[ax][ay] = EL_AMOEBA_DEAD;
5232 DrawLevelField(ax, ay);
5236 if (IS_ANIMATED(graphic))
5237 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5239 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5240 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5242 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5245 if (MovDelay[ax][ay])
5249 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5252 int x = ax + xy[start][0];
5253 int y = ay + xy[start][1];
5255 if (!IN_LEV_FIELD(x, y))
5258 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5259 if (IS_FREE(x, y) ||
5260 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5266 if (newax == ax && neway == ay)
5269 else /* normal or "filled" (BD style) amoeba */
5272 boolean waiting_for_player = FALSE;
5274 for (i = 0; i < 4; i++)
5276 int j = (start + i) % 4;
5277 int x = ax + xy[j][0];
5278 int y = ay + xy[j][1];
5280 if (!IN_LEV_FIELD(x, y))
5283 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5284 if (IS_FREE(x, y) ||
5285 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5291 else if (IS_PLAYER(x, y))
5292 waiting_for_player = TRUE;
5295 if (newax == ax && neway == ay) /* amoeba cannot grow */
5297 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5299 Feld[ax][ay] = EL_AMOEBA_DEAD;
5300 DrawLevelField(ax, ay);
5301 AmoebaCnt[AmoebaNr[ax][ay]]--;
5303 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5305 if (element == EL_AMOEBA_FULL)
5306 AmoebeUmwandeln(ax, ay);
5307 else if (element == EL_BD_AMOEBA)
5308 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5313 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5315 /* amoeba gets larger by growing in some direction */
5317 int new_group_nr = AmoebaNr[ax][ay];
5320 if (new_group_nr == 0)
5322 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5323 printf("AmoebeAbleger(): This should never happen!\n");
5328 AmoebaNr[newax][neway] = new_group_nr;
5329 AmoebaCnt[new_group_nr]++;
5330 AmoebaCnt2[new_group_nr]++;
5332 /* if amoeba touches other amoeba(s) after growing, unify them */
5333 AmoebenVereinigen(newax, neway);
5335 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5337 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5343 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5344 (neway == lev_fieldy - 1 && newax != ax))
5346 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5347 Store[newax][neway] = element;
5349 else if (neway == ay)
5351 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5353 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5355 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5360 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5361 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5362 Store[ax][ay] = EL_AMOEBA_DROP;
5363 ContinueMoving(ax, ay);
5367 DrawLevelField(newax, neway);
5370 void Life(int ax, int ay)
5373 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5375 int element = Feld[ax][ay];
5376 int graphic = el2img(element);
5377 boolean changed = FALSE;
5379 if (IS_ANIMATED(graphic))
5380 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5385 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5386 MovDelay[ax][ay] = life_time;
5388 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5391 if (MovDelay[ax][ay])
5395 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5397 int xx = ax+x1, yy = ay+y1;
5400 if (!IN_LEV_FIELD(xx, yy))
5403 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5405 int x = xx+x2, y = yy+y2;
5407 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5410 if (((Feld[x][y] == element ||
5411 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5413 (IS_FREE(x, y) && Stop[x][y]))
5417 if (xx == ax && yy == ay) /* field in the middle */
5419 if (nachbarn < life[0] || nachbarn > life[1])
5421 Feld[xx][yy] = EL_EMPTY;
5423 DrawLevelField(xx, yy);
5424 Stop[xx][yy] = TRUE;
5428 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5429 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5430 { /* free border field */
5431 if (nachbarn >= life[2] && nachbarn <= life[3])
5433 Feld[xx][yy] = element;
5434 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5436 DrawLevelField(xx, yy);
5437 Stop[xx][yy] = TRUE;
5444 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5445 SND_GAME_OF_LIFE_GROWING);
5448 static void InitRobotWheel(int x, int y)
5450 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5453 static void RunRobotWheel(int x, int y)
5455 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5458 static void StopRobotWheel(int x, int y)
5460 if (ZX == x && ZY == y)
5464 static void InitTimegateWheel(int x, int y)
5466 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5469 static void RunTimegateWheel(int x, int y)
5471 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5474 void CheckExit(int x, int y)
5476 if (local_player->gems_still_needed > 0 ||
5477 local_player->sokobanfields_still_needed > 0 ||
5478 local_player->lights_still_needed > 0)
5480 int element = Feld[x][y];
5481 int graphic = el2img(element);
5483 if (IS_ANIMATED(graphic))
5484 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5489 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5492 Feld[x][y] = EL_EXIT_OPENING;
5494 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5497 void CheckExitSP(int x, int y)
5499 if (local_player->gems_still_needed > 0)
5501 int element = Feld[x][y];
5502 int graphic = el2img(element);
5504 if (IS_ANIMATED(graphic))
5505 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5510 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5513 Feld[x][y] = EL_SP_EXIT_OPENING;
5515 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5518 static void CloseAllOpenTimegates()
5522 for (y = 0; y < lev_fieldy; y++)
5524 for (x = 0; x < lev_fieldx; x++)
5526 int element = Feld[x][y];
5528 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5530 Feld[x][y] = EL_TIMEGATE_CLOSING;
5532 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5534 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5541 void EdelsteinFunkeln(int x, int y)
5543 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5546 if (Feld[x][y] == EL_BD_DIAMOND)
5549 if (MovDelay[x][y] == 0) /* next animation frame */
5550 MovDelay[x][y] = 11 * !SimpleRND(500);
5552 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5556 if (setup.direct_draw && MovDelay[x][y])
5557 SetDrawtoField(DRAW_BUFFERED);
5559 DrawLevelElementAnimation(x, y, Feld[x][y]);
5561 if (MovDelay[x][y] != 0)
5563 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5564 10 - MovDelay[x][y]);
5566 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5568 if (setup.direct_draw)
5572 dest_x = FX + SCREENX(x) * TILEX;
5573 dest_y = FY + SCREENY(y) * TILEY;
5575 BlitBitmap(drawto_field, window,
5576 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5577 SetDrawtoField(DRAW_DIRECT);
5583 void MauerWaechst(int x, int y)
5587 if (!MovDelay[x][y]) /* next animation frame */
5588 MovDelay[x][y] = 3 * delay;
5590 if (MovDelay[x][y]) /* wait some time before next frame */
5594 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5596 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5597 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5599 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5602 if (!MovDelay[x][y])
5604 if (MovDir[x][y] == MV_LEFT)
5606 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5607 DrawLevelField(x - 1, y);
5609 else if (MovDir[x][y] == MV_RIGHT)
5611 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5612 DrawLevelField(x + 1, y);
5614 else if (MovDir[x][y] == MV_UP)
5616 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5617 DrawLevelField(x, y - 1);
5621 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5622 DrawLevelField(x, y + 1);
5625 Feld[x][y] = Store[x][y];
5627 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5628 DrawLevelField(x, y);
5633 void MauerAbleger(int ax, int ay)
5635 int element = Feld[ax][ay];
5636 int graphic = el2img(element);
5637 boolean oben_frei = FALSE, unten_frei = FALSE;
5638 boolean links_frei = FALSE, rechts_frei = FALSE;
5639 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5640 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5641 boolean new_wall = FALSE;
5643 if (IS_ANIMATED(graphic))
5644 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5646 if (!MovDelay[ax][ay]) /* start building new wall */
5647 MovDelay[ax][ay] = 6;
5649 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5652 if (MovDelay[ax][ay])
5656 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5658 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5660 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5662 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5665 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5666 element == EL_EXPANDABLE_WALL_ANY)
5670 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5671 Store[ax][ay-1] = element;
5672 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5673 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5674 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5675 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5680 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5681 Store[ax][ay+1] = element;
5682 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5683 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5684 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5685 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5690 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5691 element == EL_EXPANDABLE_WALL_ANY ||
5692 element == EL_EXPANDABLE_WALL)
5696 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5697 Store[ax-1][ay] = element;
5698 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5699 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5700 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5701 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5707 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5708 Store[ax+1][ay] = element;
5709 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5710 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5711 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5712 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5717 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5718 DrawLevelField(ax, ay);
5720 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5722 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5723 unten_massiv = TRUE;
5724 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5725 links_massiv = TRUE;
5726 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5727 rechts_massiv = TRUE;
5729 if (((oben_massiv && unten_massiv) ||
5730 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5731 element == EL_EXPANDABLE_WALL) &&
5732 ((links_massiv && rechts_massiv) ||
5733 element == EL_EXPANDABLE_WALL_VERTICAL))
5734 Feld[ax][ay] = EL_WALL;
5738 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5740 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5744 void CheckForDragon(int x, int y)
5747 boolean dragon_found = FALSE;
5748 static int xy[4][2] =
5756 for (i = 0; i < 4; i++)
5758 for (j = 0; j < 4; j++)
5760 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5762 if (IN_LEV_FIELD(xx, yy) &&
5763 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5765 if (Feld[xx][yy] == EL_DRAGON)
5766 dragon_found = TRUE;
5775 for (i = 0; i < 4; i++)
5777 for (j = 0; j < 3; j++)
5779 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5781 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5783 Feld[xx][yy] = EL_EMPTY;
5784 DrawLevelField(xx, yy);
5793 static void InitBuggyBase(int x, int y)
5795 int element = Feld[x][y];
5796 int activating_delay = FRAMES_PER_SECOND / 4;
5799 (element == EL_SP_BUGGY_BASE ?
5800 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5801 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5803 element == EL_SP_BUGGY_BASE_ACTIVE ?
5804 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5807 static void WarnBuggyBase(int x, int y)
5810 static int xy[4][2] =
5818 for (i = 0; i < 4; i++)
5820 int xx = x + xy[i][0], yy = y + xy[i][1];
5822 if (IS_PLAYER(xx, yy))
5824 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5831 static void InitTrap(int x, int y)
5833 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5836 static void ActivateTrap(int x, int y)
5838 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5841 static void ChangeActiveTrap(int x, int y)
5843 int graphic = IMG_TRAP_ACTIVE;
5845 /* if new animation frame was drawn, correct crumbled sand border */
5846 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5847 DrawLevelFieldCrumbledSand(x, y);
5850 static void ChangeElementNowExt(int x, int y, int target_element)
5852 /* check if element under player changes from accessible to unaccessible
5853 (needed for special case of dropping element which then changes) */
5854 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5855 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5862 Feld[x][y] = target_element;
5864 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5866 ResetGfxAnimation(x, y);
5867 ResetRandomAnimationValue(x, y);
5869 InitField(x, y, FALSE);
5870 if (CAN_MOVE(Feld[x][y]))
5873 DrawLevelField(x, y);
5875 if (GFX_CRUMBLED(Feld[x][y]))
5876 DrawLevelFieldCrumbledSandNeighbours(x, y);
5878 TestIfBadThingTouchesHero(x, y);
5879 TestIfPlayerTouchesCustomElement(x, y);
5880 TestIfElementTouchesCustomElement(x, y);
5882 if (ELEM_IS_PLAYER(target_element))
5883 RelocatePlayer(x, y, target_element);
5886 static boolean ChangeElementNow(int x, int y, int element, int page)
5888 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5890 /* always use default change event to prevent running into a loop */
5891 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5892 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5894 /* do not change already changed elements with same change event */
5896 if (Changed[x][y] & ChangeEvent[x][y])
5903 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5905 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5907 if (change->explode)
5914 if (change->use_content)
5916 boolean complete_change = TRUE;
5917 boolean can_change[3][3];
5920 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5922 boolean half_destructible;
5923 int ex = x + xx - 1;
5924 int ey = y + yy - 1;
5927 can_change[xx][yy] = TRUE;
5929 if (ex == x && ey == y) /* do not check changing element itself */
5932 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5934 can_change[xx][yy] = FALSE; /* do not change empty borders */
5939 if (!IN_LEV_FIELD(ex, ey))
5941 can_change[xx][yy] = FALSE;
5942 complete_change = FALSE;
5949 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5950 e = MovingOrBlocked2Element(ex, ey);
5952 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5954 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5955 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5956 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5958 can_change[xx][yy] = FALSE;
5959 complete_change = FALSE;
5963 if (!change->only_complete || complete_change)
5965 boolean something_has_changed = FALSE;
5967 if (change->only_complete && change->use_random_change &&
5968 RND(100) < change->random)
5971 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5973 int ex = x + xx - 1;
5974 int ey = y + yy - 1;
5976 if (can_change[xx][yy] && (!change->use_random_change ||
5977 RND(100) < change->random))
5979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5980 RemoveMovingField(ex, ey);
5982 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5984 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5986 something_has_changed = TRUE;
5988 /* for symmetry reasons, freeze newly created border elements */
5989 if (ex != x || ey != y)
5990 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5994 if (something_has_changed)
5995 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6000 ChangeElementNowExt(x, y, change->target_element);
6002 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6008 static void ChangeElement(int x, int y, int page)
6010 int element = MovingOrBlocked2Element(x, y);
6011 struct ElementInfo *ei = &element_info[element];
6012 struct ElementChangeInfo *change = &ei->change_page[page];
6016 if (!CAN_CHANGE(element))
6019 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6020 x, y, element, element_info[element].token_name);
6021 printf("ChangeElement(): This should never happen!\n");
6027 if (ChangeDelay[x][y] == 0) /* initialize element change */
6029 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6030 RND(change->delay_random * change->delay_frames)) + 1;
6032 ResetGfxAnimation(x, y);
6033 ResetRandomAnimationValue(x, y);
6035 if (change->pre_change_function)
6036 change->pre_change_function(x, y);
6039 ChangeDelay[x][y]--;
6041 if (ChangeDelay[x][y] != 0) /* continue element change */
6043 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6045 if (IS_ANIMATED(graphic))
6046 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6048 if (change->change_function)
6049 change->change_function(x, y);
6051 else /* finish element change */
6053 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6055 page = ChangePage[x][y];
6056 ChangePage[x][y] = -1;
6059 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6061 ChangeDelay[x][y] = 1; /* try change after next move step */
6062 ChangePage[x][y] = page; /* remember page to use for change */
6067 if (ChangeElementNow(x, y, element, page))
6069 if (change->post_change_function)
6070 change->post_change_function(x, y);
6075 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6076 int trigger_element,
6082 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6085 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6087 int element = EL_CUSTOM_START + i;
6089 boolean change_element = FALSE;
6092 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6095 for (j = 0; j < element_info[element].num_change_pages; j++)
6097 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6099 if (change->can_change &&
6101 change->events & CH_EVENT_BIT(trigger_event) &&
6103 change->sides & trigger_side &&
6104 change->trigger_element == trigger_element)
6107 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6108 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6109 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6112 change_element = TRUE;
6119 if (!change_element)
6122 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6125 if (x == lx && y == ly) /* do not change trigger element itself */
6129 if (Feld[x][y] == element)
6131 ChangeDelay[x][y] = 1;
6132 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6133 ChangeElement(x, y, page);
6141 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6144 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6148 static boolean CheckElementSideChange(int x, int y, int element, int side,
6149 int trigger_event, int page)
6151 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6154 if (Feld[x][y] == EL_BLOCKED)
6156 Blocked2Moving(x, y, &x, &y);
6157 element = Feld[x][y];
6161 page = element_info[element].event_page_nr[trigger_event];
6163 if (!(element_info[element].change_page[page].sides & side))
6166 ChangeDelay[x][y] = 1;
6167 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6168 ChangeElement(x, y, page);
6173 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6175 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6178 int GetPlayerAction(struct PlayerInfo *player, int move_dir)
6180 int jx = player->jx, jy = player->jy;
6181 int element = player->element_nr;
6182 int action = (player->is_pushing ? ACTION_PUSHING :
6183 player->is_digging ? ACTION_DIGGING :
6184 player->is_collecting ? ACTION_COLLECTING :
6185 player->is_moving ? ACTION_MOVING :
6186 player->is_snapping ? ACTION_SNAPPING :
6187 player->is_sleeping ? ACTION_SLEEPING :
6188 player->is_bored ? ACTION_BORING :
6189 player->is_waiting ? ACTION_WAITING : ACTION_DEFAULT);
6191 if (player->is_sleeping)
6193 if (player->num_special_action_sleeping > 0)
6195 int last_action = player->action_waiting;
6197 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6199 int last_special_action = player->special_action_sleeping;
6200 int num_special_action = player->num_special_action_sleeping;
6201 int special_action =
6202 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6203 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6204 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6205 last_special_action + 1 : ACTION_SLEEPING);
6206 int special_graphic =
6207 el_act_dir2img(player->element_nr, special_action, move_dir);
6209 player->anim_delay_counter =
6210 graphic_info[special_graphic].anim_delay_fixed +
6211 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6212 player->post_delay_counter =
6213 graphic_info[special_graphic].post_delay_fixed +
6214 SimpleRND(graphic_info[special_graphic].post_delay_random);
6216 player->special_action_sleeping = special_action;
6219 if (player->anim_delay_counter > 0)
6221 action = player->special_action_sleeping;
6222 player->anim_delay_counter--;
6224 else if (player->post_delay_counter > 0)
6226 player->post_delay_counter--;
6229 player->action_waiting = action;
6231 if (last_action != action)
6232 PlayLevelSoundElementAction(jx, jy, element, action);
6234 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6237 else if (player->is_bored)
6239 if (player->num_special_action_bored > 0)
6241 int last_action = player->action_waiting;
6243 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6245 int special_action =
6246 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6247 int special_graphic =
6248 el_act_dir2img(player->element_nr, special_action, move_dir);
6250 player->anim_delay_counter =
6251 graphic_info[special_graphic].anim_delay_fixed +
6252 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6253 player->post_delay_counter =
6254 graphic_info[special_graphic].post_delay_fixed +
6255 SimpleRND(graphic_info[special_graphic].post_delay_random);
6257 player->special_action_bored = special_action;
6260 if (player->anim_delay_counter > 0)
6262 action = player->special_action_bored;
6263 player->anim_delay_counter--;
6265 else if (player->post_delay_counter > 0)
6267 player->post_delay_counter--;
6270 player->action_waiting = action;
6272 if (last_action != action)
6273 PlayLevelSoundElementAction(jx, jy, element, action);
6275 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6282 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6284 int jx = player->jx, jy = player->jy;
6285 int element = player->element_nr;
6286 boolean was_waiting = player->is_waiting;
6290 int last_action, action;
6293 last_action = (player->is_sleeping ? ACTION_SLEEPING :
6294 player->is_bored ? ACTION_BORING : ACTION_WAITING);
6296 if (!was_waiting) /* not waiting -> waiting */
6298 player->is_waiting = TRUE;
6300 player->frame_counter_bored =
6302 game.player_boring_delay_fixed +
6303 SimpleRND(game.player_boring_delay_random);
6304 player->frame_counter_sleeping =
6306 game.player_sleeping_delay_fixed +
6307 SimpleRND(game.player_sleeping_delay_random);
6309 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6312 if (game.player_sleeping_delay_fixed +
6313 game.player_sleeping_delay_random > 0 &&
6314 player->anim_delay_counter == 0 &&
6315 player->post_delay_counter == 0 &&
6316 FrameCounter >= player->frame_counter_sleeping)
6317 player->is_sleeping = TRUE;
6318 else if (game.player_boring_delay_fixed +
6319 game.player_boring_delay_random > 0 &&
6320 FrameCounter >= player->frame_counter_bored)
6321 player->is_bored = TRUE;
6323 action = (player->is_sleeping ? ACTION_SLEEPING :
6324 player->is_bored ? ACTION_BORING : ACTION_WAITING);
6327 ((player->is_sleeping && player->num_special_action_sleeping == 0) ||
6328 (player->is_bored && player->num_special_action_bored == 0) ||
6329 (player->is_waiting && !player->is_sleeping && !player->is_bored));
6331 if (play_sound && action != last_action)
6332 PlayLevelSoundElementAction(jx, jy, element, action);
6334 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6336 else if (was_waiting) /* waiting -> not waiting */
6338 if (player->is_sleeping)
6339 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6341 player->is_waiting = FALSE;
6342 player->is_bored = FALSE;
6343 player->is_sleeping = FALSE;
6345 player->frame_counter_bored = -1;
6346 player->frame_counter_sleeping = -1;
6348 player->anim_delay_counter = 0;
6349 player->post_delay_counter = 0;
6351 player->special_action_bored = ACTION_DEFAULT;
6352 player->special_action_sleeping = ACTION_DEFAULT;
6357 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6360 static byte stored_player_action[MAX_PLAYERS];
6361 static int num_stored_actions = 0;
6363 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6364 int left = player_action & JOY_LEFT;
6365 int right = player_action & JOY_RIGHT;
6366 int up = player_action & JOY_UP;
6367 int down = player_action & JOY_DOWN;
6368 int button1 = player_action & JOY_BUTTON_1;
6369 int button2 = player_action & JOY_BUTTON_2;
6370 int dx = (left ? -1 : right ? 1 : 0);
6371 int dy = (up ? -1 : down ? 1 : 0);
6374 stored_player_action[player->index_nr] = 0;
6375 num_stored_actions++;
6379 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6382 if (!player->active || tape.pausing)
6388 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6392 snapped = SnapField(player, dx, dy);
6396 dropped = DropElement(player);
6398 moved = MovePlayer(player, dx, dy);
6401 if (tape.single_step && tape.recording && !tape.pausing)
6403 if (button1 || (dropped && !moved))
6405 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6406 SnapField(player, 0, 0); /* stop snapping */
6410 SetPlayerWaiting(player, FALSE);
6413 return player_action;
6415 stored_player_action[player->index_nr] = player_action;
6421 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6424 /* no actions for this player (no input at player's configured device) */
6426 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6427 SnapField(player, 0, 0);
6428 CheckGravityMovement(player);
6430 if (player->MovPos == 0)
6431 SetPlayerWaiting(player, TRUE);
6433 if (player->MovPos == 0) /* needed for tape.playing */
6434 player->is_moving = FALSE;
6440 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6442 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6444 TapeRecordAction(stored_player_action);
6445 num_stored_actions = 0;
6452 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6454 static byte stored_player_action[MAX_PLAYERS];
6455 static int num_stored_actions = 0;
6456 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6457 int left = player_action & JOY_LEFT;
6458 int right = player_action & JOY_RIGHT;
6459 int up = player_action & JOY_UP;
6460 int down = player_action & JOY_DOWN;
6461 int button1 = player_action & JOY_BUTTON_1;
6462 int button2 = player_action & JOY_BUTTON_2;
6463 int dx = (left ? -1 : right ? 1 : 0);
6464 int dy = (up ? -1 : down ? 1 : 0);
6466 stored_player_action[player->index_nr] = 0;
6467 num_stored_actions++;
6469 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6471 if (!player->active || tape.pausing)
6476 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6479 snapped = SnapField(player, dx, dy);
6483 dropped = DropElement(player);
6485 moved = MovePlayer(player, dx, dy);
6488 if (tape.single_step && tape.recording && !tape.pausing)
6490 if (button1 || (dropped && !moved))
6492 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6493 SnapField(player, 0, 0); /* stop snapping */
6497 stored_player_action[player->index_nr] = player_action;
6501 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6503 /* no actions for this player (no input at player's configured device) */
6505 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6506 SnapField(player, 0, 0);
6507 CheckGravityMovement(player);
6509 if (player->MovPos == 0)
6510 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6512 if (player->MovPos == 0) /* needed for tape.playing */
6513 player->is_moving = FALSE;
6516 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6518 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6520 TapeRecordAction(stored_player_action);
6521 num_stored_actions = 0;
6528 static unsigned long action_delay = 0;
6529 unsigned long action_delay_value;
6530 int magic_wall_x = 0, magic_wall_y = 0;
6531 int i, x, y, element, graphic;
6532 byte *recorded_player_action;
6533 byte summarized_player_action = 0;
6535 byte tape_action[MAX_PLAYERS];
6538 if (game_status != GAME_MODE_PLAYING)
6541 action_delay_value =
6542 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6544 if (tape.playing && tape.index_search && !tape.pausing)
6545 action_delay_value = 0;
6547 /* ---------- main game synchronization point ---------- */
6549 WaitUntilDelayReached(&action_delay, action_delay_value);
6551 if (network_playing && !network_player_action_received)
6555 printf("DEBUG: try to get network player actions in time\n");
6559 #if defined(PLATFORM_UNIX)
6560 /* last chance to get network player actions without main loop delay */
6564 if (game_status != GAME_MODE_PLAYING)
6567 if (!network_player_action_received)
6571 printf("DEBUG: failed to get network player actions in time\n");
6582 printf("::: getting new tape action [%d]\n", FrameCounter);
6585 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6587 for (i = 0; i < MAX_PLAYERS; i++)
6589 summarized_player_action |= stored_player[i].action;
6591 if (!network_playing)
6592 stored_player[i].effective_action = stored_player[i].action;
6595 #if defined(PLATFORM_UNIX)
6596 if (network_playing)
6597 SendToServer_MovePlayer(summarized_player_action);
6600 if (!options.network && !setup.team_mode)
6601 local_player->effective_action = summarized_player_action;
6603 for (i = 0; i < MAX_PLAYERS; i++)
6605 int actual_player_action = stored_player[i].effective_action;
6607 if (stored_player[i].programmed_action)
6608 actual_player_action = stored_player[i].programmed_action;
6610 if (recorded_player_action)
6611 actual_player_action = recorded_player_action[i];
6613 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6615 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6616 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6618 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6623 TapeRecordAction(tape_action);
6626 network_player_action_received = FALSE;
6628 ScrollScreen(NULL, SCROLL_GO_ON);
6634 for (i = 0; i < MAX_PLAYERS; i++)
6635 stored_player[i].Frame++;
6639 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6641 for (i = 0; i < MAX_PLAYERS; i++)
6643 struct PlayerInfo *player = &stored_player[i];
6647 if (player->active && player->is_pushing && player->is_moving &&
6650 ContinueMoving(x, y);
6652 /* continue moving after pushing (this is actually a bug) */
6653 if (!IS_MOVING(x, y))
6662 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6664 Changed[x][y] = CE_BITMASK_DEFAULT;
6665 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6668 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6670 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6671 printf("GameActions(): This should never happen!\n");
6673 ChangePage[x][y] = -1;
6678 if (WasJustMoving[x][y] > 0)
6679 WasJustMoving[x][y]--;
6680 if (WasJustFalling[x][y] > 0)
6681 WasJustFalling[x][y]--;
6686 /* reset finished pushing action (not done in ContinueMoving() to allow
6687 continous pushing animation for elements with zero push delay) */
6688 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6690 ResetGfxAnimation(x, y);
6691 DrawLevelField(x, y);
6696 if (IS_BLOCKED(x, y))
6700 Blocked2Moving(x, y, &oldx, &oldy);
6701 if (!IS_MOVING(oldx, oldy))
6703 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6704 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6705 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6706 printf("GameActions(): This should never happen!\n");
6712 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6714 element = Feld[x][y];
6716 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6718 graphic = el2img(element);
6724 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6726 element = graphic = 0;
6730 if (graphic_info[graphic].anim_global_sync)
6731 GfxFrame[x][y] = FrameCounter;
6733 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6734 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6735 ResetRandomAnimationValue(x, y);
6737 SetRandomAnimationValue(x, y);
6740 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6743 if (IS_INACTIVE(element))
6745 if (IS_ANIMATED(graphic))
6746 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6752 /* this may take place after moving, so 'element' may have changed */
6754 if (IS_CHANGING(x, y))
6756 if (IS_CHANGING(x, y) &&
6757 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6761 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6762 element_info[element].event_page_nr[CE_DELAY]);
6764 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6767 element = Feld[x][y];
6768 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6772 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6777 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6779 if (element == EL_MOLE)
6780 printf("::: %d, %d, %d [%d]\n",
6781 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6785 if (element == EL_YAMYAM)
6786 printf("::: %d, %d, %d\n",
6787 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6791 if (IS_ANIMATED(graphic) &&
6795 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6798 if (element == EL_BUG)
6799 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6803 if (element == EL_MOLE)
6804 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6808 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6809 EdelsteinFunkeln(x, y);
6811 else if ((element == EL_ACID ||
6812 element == EL_EXIT_OPEN ||
6813 element == EL_SP_EXIT_OPEN ||
6814 element == EL_SP_TERMINAL ||
6815 element == EL_SP_TERMINAL_ACTIVE ||
6816 element == EL_EXTRA_TIME ||
6817 element == EL_SHIELD_NORMAL ||
6818 element == EL_SHIELD_DEADLY) &&
6819 IS_ANIMATED(graphic))
6820 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6821 else if (IS_MOVING(x, y))
6822 ContinueMoving(x, y);
6823 else if (IS_ACTIVE_BOMB(element))
6824 CheckDynamite(x, y);
6826 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6827 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6829 else if (element == EL_AMOEBA_GROWING)
6830 AmoebeWaechst(x, y);
6831 else if (element == EL_AMOEBA_SHRINKING)
6832 AmoebaDisappearing(x, y);
6834 #if !USE_NEW_AMOEBA_CODE
6835 else if (IS_AMOEBALIVE(element))
6836 AmoebeAbleger(x, y);
6839 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6841 else if (element == EL_EXIT_CLOSED)
6843 else if (element == EL_SP_EXIT_CLOSED)
6845 else if (element == EL_EXPANDABLE_WALL_GROWING)
6847 else if (element == EL_EXPANDABLE_WALL ||
6848 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6849 element == EL_EXPANDABLE_WALL_VERTICAL ||
6850 element == EL_EXPANDABLE_WALL_ANY)
6852 else if (element == EL_FLAMES)
6853 CheckForDragon(x, y);
6855 else if (IS_AUTO_CHANGING(element))
6856 ChangeElement(x, y);
6858 else if (element == EL_EXPLOSION)
6859 ; /* drawing of correct explosion animation is handled separately */
6860 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6864 /* this may take place after moving, so 'element' may have changed */
6865 if (IS_AUTO_CHANGING(Feld[x][y]))
6866 ChangeElement(x, y);
6869 if (IS_BELT_ACTIVE(element))
6870 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6872 if (game.magic_wall_active)
6874 int jx = local_player->jx, jy = local_player->jy;
6876 /* play the element sound at the position nearest to the player */
6877 if ((element == EL_MAGIC_WALL_FULL ||
6878 element == EL_MAGIC_WALL_ACTIVE ||
6879 element == EL_MAGIC_WALL_EMPTYING ||
6880 element == EL_BD_MAGIC_WALL_FULL ||
6881 element == EL_BD_MAGIC_WALL_ACTIVE ||
6882 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6883 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6891 #if USE_NEW_AMOEBA_CODE
6892 /* new experimental amoeba growth stuff */
6894 if (!(FrameCounter % 8))
6897 static unsigned long random = 1684108901;
6899 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6902 x = (random >> 10) % lev_fieldx;
6903 y = (random >> 20) % lev_fieldy;
6905 x = RND(lev_fieldx);
6906 y = RND(lev_fieldy);
6908 element = Feld[x][y];
6910 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6911 if (!IS_PLAYER(x,y) &&
6912 (element == EL_EMPTY ||
6913 element == EL_SAND ||
6914 element == EL_QUICKSAND_EMPTY ||
6915 element == EL_ACID_SPLASH_LEFT ||
6916 element == EL_ACID_SPLASH_RIGHT))
6918 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6919 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6920 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6921 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6922 Feld[x][y] = EL_AMOEBA_DROP;
6925 random = random * 129 + 1;
6931 if (game.explosions_delayed)
6934 game.explosions_delayed = FALSE;
6936 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6938 element = Feld[x][y];
6940 if (ExplodeField[x][y])
6941 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6942 else if (element == EL_EXPLOSION)
6943 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6945 ExplodeField[x][y] = EX_NO_EXPLOSION;
6948 game.explosions_delayed = TRUE;
6951 if (game.magic_wall_active)
6953 if (!(game.magic_wall_time_left % 4))
6955 int element = Feld[magic_wall_x][magic_wall_y];
6957 if (element == EL_BD_MAGIC_WALL_FULL ||
6958 element == EL_BD_MAGIC_WALL_ACTIVE ||
6959 element == EL_BD_MAGIC_WALL_EMPTYING)
6960 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6962 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6965 if (game.magic_wall_time_left > 0)
6967 game.magic_wall_time_left--;
6968 if (!game.magic_wall_time_left)
6970 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6972 element = Feld[x][y];
6974 if (element == EL_MAGIC_WALL_ACTIVE ||
6975 element == EL_MAGIC_WALL_FULL)
6977 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6978 DrawLevelField(x, y);
6980 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6981 element == EL_BD_MAGIC_WALL_FULL)
6983 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6984 DrawLevelField(x, y);
6988 game.magic_wall_active = FALSE;
6993 if (game.light_time_left > 0)
6995 game.light_time_left--;
6997 if (game.light_time_left == 0)
6998 RedrawAllLightSwitchesAndInvisibleElements();
7001 if (game.timegate_time_left > 0)
7003 game.timegate_time_left--;
7005 if (game.timegate_time_left == 0)
7006 CloseAllOpenTimegates();
7009 for (i = 0; i < MAX_PLAYERS; i++)
7011 struct PlayerInfo *player = &stored_player[i];
7013 if (SHIELD_ON(player))
7015 if (player->shield_deadly_time_left)
7016 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7017 else if (player->shield_normal_time_left)
7018 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7022 if (TimeFrames >= FRAMES_PER_SECOND)
7027 for (i = 0; i < MAX_PLAYERS; i++)
7029 struct PlayerInfo *player = &stored_player[i];
7031 if (SHIELD_ON(player))
7033 player->shield_normal_time_left--;
7035 if (player->shield_deadly_time_left > 0)
7036 player->shield_deadly_time_left--;
7040 if (tape.recording || tape.playing)
7041 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7047 if (TimeLeft <= 10 && setup.time_limit)
7048 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7050 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7052 if (!TimeLeft && setup.time_limit)
7053 for (i = 0; i < MAX_PLAYERS; i++)
7054 KillHero(&stored_player[i]);
7056 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7057 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7062 if (options.debug) /* calculate frames per second */
7064 static unsigned long fps_counter = 0;
7065 static int fps_frames = 0;
7066 unsigned long fps_delay_ms = Counter() - fps_counter;
7070 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7072 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7075 fps_counter = Counter();
7078 redraw_mask |= REDRAW_FPS;
7082 if (stored_player[0].jx != stored_player[0].last_jx ||
7083 stored_player[0].jy != stored_player[0].last_jy)
7084 printf("::: %d, %d, %d, %d, %d\n",
7085 stored_player[0].MovDir,
7086 stored_player[0].MovPos,
7087 stored_player[0].GfxPos,
7088 stored_player[0].Frame,
7089 stored_player[0].StepFrame);
7096 for (i = 0; i < MAX_PLAYERS; i++)
7099 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7101 stored_player[i].Frame += move_frames;
7103 if (stored_player[i].MovPos != 0)
7104 stored_player[i].StepFrame += move_frames;
7109 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7111 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7113 local_player->show_envelope = 0;
7118 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7120 int min_x = x, min_y = y, max_x = x, max_y = y;
7123 for (i = 0; i < MAX_PLAYERS; i++)
7125 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7127 if (!stored_player[i].active || &stored_player[i] == player)
7130 min_x = MIN(min_x, jx);
7131 min_y = MIN(min_y, jy);
7132 max_x = MAX(max_x, jx);
7133 max_y = MAX(max_y, jy);
7136 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7139 static boolean AllPlayersInVisibleScreen()
7143 for (i = 0; i < MAX_PLAYERS; i++)
7145 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7147 if (!stored_player[i].active)
7150 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7157 void ScrollLevel(int dx, int dy)
7159 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7162 BlitBitmap(drawto_field, drawto_field,
7163 FX + TILEX * (dx == -1) - softscroll_offset,
7164 FY + TILEY * (dy == -1) - softscroll_offset,
7165 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7166 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7167 FX + TILEX * (dx == 1) - softscroll_offset,
7168 FY + TILEY * (dy == 1) - softscroll_offset);
7172 x = (dx == 1 ? BX1 : BX2);
7173 for (y = BY1; y <= BY2; y++)
7174 DrawScreenField(x, y);
7179 y = (dy == 1 ? BY1 : BY2);
7180 for (x = BX1; x <= BX2; x++)
7181 DrawScreenField(x, y);
7184 redraw_mask |= REDRAW_FIELD;
7187 static void CheckGravityMovement(struct PlayerInfo *player)
7189 if (game.gravity && !player->programmed_action)
7191 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7192 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7194 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7195 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7196 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7197 int jx = player->jx, jy = player->jy;
7198 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7199 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7200 int new_jx = jx + dx, new_jy = jy + dy;
7201 boolean field_under_player_is_free =
7202 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7203 boolean player_is_moving_to_valid_field =
7204 (IN_LEV_FIELD(new_jx, new_jy) &&
7205 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7206 Feld[new_jx][new_jy] == EL_SAND));
7207 /* !!! extend EL_SAND to anything diggable !!! */
7209 if (field_under_player_is_free &&
7210 !player_is_moving_to_valid_field &&
7211 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7212 player->programmed_action = MV_DOWN;
7218 -----------------------------------------------------------------------------
7219 dx, dy: direction (non-diagonal) to try to move the player to
7220 real_dx, real_dy: direction as read from input device (can be diagonal)
7223 boolean MovePlayerOneStep(struct PlayerInfo *player,
7224 int dx, int dy, int real_dx, int real_dy)
7227 static int change_sides[4][2] =
7229 /* enter side leave side */
7230 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7231 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7232 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7233 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7235 int move_direction = (dx == -1 ? MV_LEFT :
7236 dx == +1 ? MV_RIGHT :
7238 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7239 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7240 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7242 int jx = player->jx, jy = player->jy;
7243 int new_jx = jx + dx, new_jy = jy + dy;
7247 if (!player->active || (!dx && !dy))
7248 return MF_NO_ACTION;
7250 player->MovDir = (dx < 0 ? MV_LEFT :
7253 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7255 if (!IN_LEV_FIELD(new_jx, new_jy))
7256 return MF_NO_ACTION;
7258 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7259 return MF_NO_ACTION;
7262 element = MovingOrBlocked2Element(new_jx, new_jy);
7264 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7267 if (DONT_RUN_INTO(element))
7269 if (element == EL_ACID && dx == 0 && dy == 1)
7272 Feld[jx][jy] = EL_PLAYER_1;
7273 InitMovingField(jx, jy, MV_DOWN);
7274 Store[jx][jy] = EL_ACID;
7275 ContinueMoving(jx, jy);
7279 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7284 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7285 if (can_move != MF_MOVING)
7288 /* check if DigField() has caused relocation of the player */
7289 if (player->jx != jx || player->jy != jy)
7290 return MF_NO_ACTION;
7292 StorePlayer[jx][jy] = 0;
7293 player->last_jx = jx;
7294 player->last_jy = jy;
7295 player->jx = new_jx;
7296 player->jy = new_jy;
7297 StorePlayer[new_jx][new_jy] = player->element_nr;
7300 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7302 player->step_counter++;
7304 PlayerVisit[jx][jy] = FrameCounter;
7306 ScrollPlayer(player, SCROLL_INIT);
7309 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7311 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7312 CE_OTHER_GETS_LEFT);
7313 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7314 CE_LEFT_BY_PLAYER, -1);
7317 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7319 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7320 enter_side, CE_OTHER_GETS_ENTERED);
7321 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7322 CE_ENTERED_BY_PLAYER, -1);
7329 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7331 int jx = player->jx, jy = player->jy;
7332 int old_jx = jx, old_jy = jy;
7333 int moved = MF_NO_ACTION;
7335 if (!player->active || (!dx && !dy))
7339 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7343 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7344 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7348 /* remove the last programmed player action */
7349 player->programmed_action = 0;
7353 /* should only happen if pre-1.2 tape recordings are played */
7354 /* this is only for backward compatibility */
7356 int original_move_delay_value = player->move_delay_value;
7359 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7363 /* scroll remaining steps with finest movement resolution */
7364 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7366 while (player->MovPos)
7368 ScrollPlayer(player, SCROLL_GO_ON);
7369 ScrollScreen(NULL, SCROLL_GO_ON);
7375 player->move_delay_value = original_move_delay_value;
7378 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7380 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7381 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7385 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7386 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7392 if (moved & MF_MOVING && !ScreenMovPos &&
7393 (player == local_player || !options.network))
7395 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7396 int offset = (setup.scroll_delay ? 3 : 0);
7398 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7400 /* actual player has left the screen -- scroll in that direction */
7401 if (jx != old_jx) /* player has moved horizontally */
7402 scroll_x += (jx - old_jx);
7403 else /* player has moved vertically */
7404 scroll_y += (jy - old_jy);
7408 if (jx != old_jx) /* player has moved horizontally */
7410 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7411 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7412 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7414 /* don't scroll over playfield boundaries */
7415 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7416 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7418 /* don't scroll more than one field at a time */
7419 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7421 /* don't scroll against the player's moving direction */
7422 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7423 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7424 scroll_x = old_scroll_x;
7426 else /* player has moved vertically */
7428 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7429 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7430 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7432 /* don't scroll over playfield boundaries */
7433 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7434 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7436 /* don't scroll more than one field at a time */
7437 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7439 /* don't scroll against the player's moving direction */
7440 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7441 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7442 scroll_y = old_scroll_y;
7446 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7448 if (!options.network && !AllPlayersInVisibleScreen())
7450 scroll_x = old_scroll_x;
7451 scroll_y = old_scroll_y;
7455 ScrollScreen(player, SCROLL_INIT);
7456 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7463 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7465 if (!(moved & MF_MOVING) && !player->is_pushing)
7470 player->StepFrame = 0;
7472 if (moved & MF_MOVING)
7474 if (old_jx != jx && old_jy == jy)
7475 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7476 else if (old_jx == jx && old_jy != jy)
7477 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7479 DrawLevelField(jx, jy); /* for "crumbled sand" */
7481 player->last_move_dir = player->MovDir;
7482 player->is_moving = TRUE;
7484 player->is_snapping = FALSE;
7488 player->is_switching = FALSE;
7494 static int change_sides[4][2] =
7496 /* enter side leave side */
7497 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7498 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7499 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7500 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7502 int move_direction = player->MovDir;
7503 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7504 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7507 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7509 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7510 leave_side, CE_OTHER_GETS_LEFT);
7511 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7512 leave_side, CE_LEFT_BY_PLAYER, -1);
7515 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7517 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7518 enter_side, CE_OTHER_GETS_ENTERED);
7519 CheckElementSideChange(jx, jy, Feld[jx][jy],
7520 enter_side, CE_ENTERED_BY_PLAYER, -1);
7531 CheckGravityMovement(player);
7534 player->last_move_dir = MV_NO_MOVING;
7536 player->is_moving = FALSE;
7539 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7541 TestIfHeroTouchesBadThing(jx, jy);
7542 TestIfPlayerTouchesCustomElement(jx, jy);
7545 if (!player->active)
7551 void ScrollPlayer(struct PlayerInfo *player, int mode)
7553 int jx = player->jx, jy = player->jy;
7554 int last_jx = player->last_jx, last_jy = player->last_jy;
7555 int move_stepsize = TILEX / player->move_delay_value;
7557 if (!player->active || !player->MovPos)
7560 if (mode == SCROLL_INIT)
7562 player->actual_frame_counter = FrameCounter;
7563 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7565 if (Feld[last_jx][last_jy] == EL_EMPTY)
7566 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7573 else if (!FrameReached(&player->actual_frame_counter, 1))
7576 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7577 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7579 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7580 Feld[last_jx][last_jy] = EL_EMPTY;
7582 /* before DrawPlayer() to draw correct player graphic for this case */
7583 if (player->MovPos == 0)
7584 CheckGravityMovement(player);
7587 DrawPlayer(player); /* needed here only to cleanup last field */
7590 if (player->MovPos == 0) /* player reached destination field */
7592 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7594 /* continue with normal speed after quickly moving through gate */
7595 HALVE_PLAYER_SPEED(player);
7597 /* be able to make the next move without delay */
7598 player->move_delay = 0;
7601 player->last_jx = jx;
7602 player->last_jy = jy;
7604 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7605 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7606 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7608 DrawPlayer(player); /* needed here only to cleanup last field */
7611 if (local_player->friends_still_needed == 0 ||
7612 IS_SP_ELEMENT(Feld[jx][jy]))
7613 player->LevelSolved = player->GameOver = TRUE;
7616 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7618 TestIfHeroTouchesBadThing(jx, jy);
7619 TestIfPlayerTouchesCustomElement(jx, jy);
7621 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7624 if (!player->active)
7628 if (tape.single_step && tape.recording && !tape.pausing &&
7629 !player->programmed_action)
7630 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7634 void ScrollScreen(struct PlayerInfo *player, int mode)
7636 static unsigned long screen_frame_counter = 0;
7638 if (mode == SCROLL_INIT)
7640 /* set scrolling step size according to actual player's moving speed */
7641 ScrollStepSize = TILEX / player->move_delay_value;
7643 screen_frame_counter = FrameCounter;
7644 ScreenMovDir = player->MovDir;
7645 ScreenMovPos = player->MovPos;
7646 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7649 else if (!FrameReached(&screen_frame_counter, 1))
7654 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7655 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7656 redraw_mask |= REDRAW_FIELD;
7659 ScreenMovDir = MV_NO_MOVING;
7662 void TestIfPlayerTouchesCustomElement(int x, int y)
7664 static int xy[4][2] =
7671 static int change_sides[4][2] =
7673 /* center side border side */
7674 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7675 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7676 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7677 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7679 static int touch_dir[4] =
7686 int center_element = Feld[x][y]; /* should always be non-moving! */
7689 for (i = 0; i < 4; i++)
7691 int xx = x + xy[i][0];
7692 int yy = y + xy[i][1];
7693 int center_side = change_sides[i][0];
7694 int border_side = change_sides[i][1];
7697 if (!IN_LEV_FIELD(xx, yy))
7700 if (IS_PLAYER(x, y))
7702 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7703 border_element = Feld[xx][yy]; /* may be moving! */
7704 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7705 border_element = Feld[xx][yy];
7706 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7707 border_element = MovingOrBlocked2Element(xx, yy);
7709 continue; /* center and border element do not touch */
7711 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7712 CE_OTHER_GETS_TOUCHED);
7713 CheckElementSideChange(xx, yy, border_element, border_side,
7714 CE_TOUCHED_BY_PLAYER, -1);
7716 else if (IS_PLAYER(xx, yy))
7718 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7720 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7722 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7723 continue; /* center and border element do not touch */
7726 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7727 CE_OTHER_GETS_TOUCHED);
7728 CheckElementSideChange(x, y, center_element, center_side,
7729 CE_TOUCHED_BY_PLAYER, -1);
7736 void TestIfElementTouchesCustomElement(int x, int y)
7738 static int xy[4][2] =
7745 static int change_sides[4][2] =
7747 /* center side border side */
7748 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7749 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7750 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7751 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7753 static int touch_dir[4] =
7760 boolean change_center_element = FALSE;
7761 int center_element_change_page = 0;
7762 int center_element = Feld[x][y]; /* should always be non-moving! */
7765 for (i = 0; i < 4; i++)
7767 int xx = x + xy[i][0];
7768 int yy = y + xy[i][1];
7769 int center_side = change_sides[i][0];
7770 int border_side = change_sides[i][1];
7773 if (!IN_LEV_FIELD(xx, yy))
7776 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7777 border_element = Feld[xx][yy]; /* may be moving! */
7778 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7779 border_element = Feld[xx][yy];
7780 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7781 border_element = MovingOrBlocked2Element(xx, yy);
7783 continue; /* center and border element do not touch */
7785 /* check for change of center element (but change it only once) */
7786 if (IS_CUSTOM_ELEMENT(center_element) &&
7787 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7788 !change_center_element)
7790 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7792 struct ElementChangeInfo *change =
7793 &element_info[center_element].change_page[j];
7795 if (change->can_change &&
7796 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7797 change->sides & border_side &&
7798 change->trigger_element == border_element)
7800 change_center_element = TRUE;
7801 center_element_change_page = j;
7808 /* check for change of border element */
7809 if (IS_CUSTOM_ELEMENT(border_element) &&
7810 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7812 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7814 struct ElementChangeInfo *change =
7815 &element_info[border_element].change_page[j];
7817 if (change->can_change &&
7818 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7819 change->sides & center_side &&
7820 change->trigger_element == center_element)
7822 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7823 CE_OTHER_IS_TOUCHING, j);
7830 if (change_center_element)
7831 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7832 CE_OTHER_IS_TOUCHING, center_element_change_page);
7835 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7837 int i, kill_x = -1, kill_y = -1;
7838 static int test_xy[4][2] =
7845 static int test_dir[4] =
7853 for (i = 0; i < 4; i++)
7855 int test_x, test_y, test_move_dir, test_element;
7857 test_x = good_x + test_xy[i][0];
7858 test_y = good_y + test_xy[i][1];
7859 if (!IN_LEV_FIELD(test_x, test_y))
7863 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7866 test_element = Feld[test_x][test_y];
7868 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7871 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7872 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7874 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7875 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7883 if (kill_x != -1 || kill_y != -1)
7885 if (IS_PLAYER(good_x, good_y))
7887 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7889 if (player->shield_deadly_time_left > 0)
7890 Bang(kill_x, kill_y);
7891 else if (!PLAYER_PROTECTED(good_x, good_y))
7895 Bang(good_x, good_y);
7899 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7901 int i, kill_x = -1, kill_y = -1;
7902 int bad_element = Feld[bad_x][bad_y];
7903 static int test_xy[4][2] =
7910 static int touch_dir[4] =
7917 static int test_dir[4] =
7925 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7928 for (i = 0; i < 4; i++)
7930 int test_x, test_y, test_move_dir, test_element;
7932 test_x = bad_x + test_xy[i][0];
7933 test_y = bad_y + test_xy[i][1];
7934 if (!IN_LEV_FIELD(test_x, test_y))
7938 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7940 test_element = Feld[test_x][test_y];
7942 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7943 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7945 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7946 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7948 /* good thing is player or penguin that does not move away */
7949 if (IS_PLAYER(test_x, test_y))
7951 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7953 if (bad_element == EL_ROBOT && player->is_moving)
7954 continue; /* robot does not kill player if he is moving */
7956 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7958 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7959 continue; /* center and border element do not touch */
7966 else if (test_element == EL_PENGUIN)
7975 if (kill_x != -1 || kill_y != -1)
7977 if (IS_PLAYER(kill_x, kill_y))
7979 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7981 if (player->shield_deadly_time_left > 0)
7983 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7987 Bang(kill_x, kill_y);
7991 void TestIfHeroTouchesBadThing(int x, int y)
7993 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7996 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7998 TestIfGoodThingHitsBadThing(x, y, move_dir);
8001 void TestIfBadThingTouchesHero(int x, int y)
8003 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8006 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8008 TestIfBadThingHitsGoodThing(x, y, move_dir);
8011 void TestIfFriendTouchesBadThing(int x, int y)
8013 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8016 void TestIfBadThingTouchesFriend(int x, int y)
8018 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8021 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8023 int i, kill_x = bad_x, kill_y = bad_y;
8024 static int xy[4][2] =
8032 for (i = 0; i < 4; i++)
8036 x = bad_x + xy[i][0];
8037 y = bad_y + xy[i][1];
8038 if (!IN_LEV_FIELD(x, y))
8041 element = Feld[x][y];
8042 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8043 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8051 if (kill_x != bad_x || kill_y != bad_y)
8055 void KillHero(struct PlayerInfo *player)
8057 int jx = player->jx, jy = player->jy;
8059 if (!player->active)
8062 /* remove accessible field at the player's position */
8063 Feld[jx][jy] = EL_EMPTY;
8065 /* deactivate shield (else Bang()/Explode() would not work right) */
8066 player->shield_normal_time_left = 0;
8067 player->shield_deadly_time_left = 0;
8073 static void KillHeroUnlessProtected(int x, int y)
8075 if (!PLAYER_PROTECTED(x, y))
8076 KillHero(PLAYERINFO(x, y));
8079 void BuryHero(struct PlayerInfo *player)
8081 int jx = player->jx, jy = player->jy;
8083 if (!player->active)
8087 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8089 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8091 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8093 player->GameOver = TRUE;
8097 void RemoveHero(struct PlayerInfo *player)
8099 int jx = player->jx, jy = player->jy;
8100 int i, found = FALSE;
8102 player->present = FALSE;
8103 player->active = FALSE;
8105 if (!ExplodeField[jx][jy])
8106 StorePlayer[jx][jy] = 0;
8108 for (i = 0; i < MAX_PLAYERS; i++)
8109 if (stored_player[i].active)
8113 AllPlayersGone = TRUE;
8120 =============================================================================
8121 checkDiagonalPushing()
8122 -----------------------------------------------------------------------------
8123 check if diagonal input device direction results in pushing of object
8124 (by checking if the alternative direction is walkable, diggable, ...)
8125 =============================================================================
8128 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8129 int x, int y, int real_dx, int real_dy)
8131 int jx, jy, dx, dy, xx, yy;
8133 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8136 /* diagonal direction: check alternative direction */
8141 xx = jx + (dx == 0 ? real_dx : 0);
8142 yy = jy + (dy == 0 ? real_dy : 0);
8144 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8148 =============================================================================
8150 -----------------------------------------------------------------------------
8151 x, y: field next to player (non-diagonal) to try to dig to
8152 real_dx, real_dy: direction as read from input device (can be diagonal)
8153 =============================================================================
8156 int DigField(struct PlayerInfo *player,
8157 int x, int y, int real_dx, int real_dy, int mode)
8159 static int change_sides[4] =
8161 CH_SIDE_RIGHT, /* moving left */
8162 CH_SIDE_LEFT, /* moving right */
8163 CH_SIDE_BOTTOM, /* moving up */
8164 CH_SIDE_TOP, /* moving down */
8166 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8167 int jx = player->jx, jy = player->jy;
8168 int dx = x - jx, dy = y - jy;
8169 int nextx = x + dx, nexty = y + dy;
8170 int move_direction = (dx == -1 ? MV_LEFT :
8171 dx == +1 ? MV_RIGHT :
8173 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8174 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8177 if (player->MovPos == 0)
8179 player->is_digging = FALSE;
8180 player->is_collecting = FALSE;
8183 if (player->MovPos == 0) /* last pushing move finished */
8184 player->is_pushing = FALSE;
8186 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8188 player->is_switching = FALSE;
8189 player->push_delay = 0;
8191 return MF_NO_ACTION;
8194 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8195 return MF_NO_ACTION;
8198 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8200 if (IS_TUBE(Feld[jx][jy]) ||
8201 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8205 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8206 int tube_leave_directions[][2] =
8208 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8209 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8210 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8211 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8212 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8213 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8214 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8215 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8216 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8217 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8218 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8219 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8222 while (tube_leave_directions[i][0] != tube_element)
8225 if (tube_leave_directions[i][0] == -1) /* should not happen */
8229 if (!(tube_leave_directions[i][1] & move_direction))
8230 return MF_NO_ACTION; /* tube has no opening in this direction */
8233 element = Feld[x][y];
8235 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8236 game.engine_version >= VERSION_IDENT(2,2,0,0))
8237 return MF_NO_ACTION;
8241 case EL_SP_PORT_LEFT:
8242 case EL_SP_PORT_RIGHT:
8244 case EL_SP_PORT_DOWN:
8245 case EL_SP_PORT_HORIZONTAL:
8246 case EL_SP_PORT_VERTICAL:
8247 case EL_SP_PORT_ANY:
8248 case EL_SP_GRAVITY_PORT_LEFT:
8249 case EL_SP_GRAVITY_PORT_RIGHT:
8250 case EL_SP_GRAVITY_PORT_UP:
8251 case EL_SP_GRAVITY_PORT_DOWN:
8253 element != EL_SP_PORT_LEFT &&
8254 element != EL_SP_GRAVITY_PORT_LEFT &&
8255 element != EL_SP_PORT_HORIZONTAL &&
8256 element != EL_SP_PORT_ANY) ||
8258 element != EL_SP_PORT_RIGHT &&
8259 element != EL_SP_GRAVITY_PORT_RIGHT &&
8260 element != EL_SP_PORT_HORIZONTAL &&
8261 element != EL_SP_PORT_ANY) ||
8263 element != EL_SP_PORT_UP &&
8264 element != EL_SP_GRAVITY_PORT_UP &&
8265 element != EL_SP_PORT_VERTICAL &&
8266 element != EL_SP_PORT_ANY) ||
8268 element != EL_SP_PORT_DOWN &&
8269 element != EL_SP_GRAVITY_PORT_DOWN &&
8270 element != EL_SP_PORT_VERTICAL &&
8271 element != EL_SP_PORT_ANY) ||
8272 !IN_LEV_FIELD(nextx, nexty) ||
8273 !IS_FREE(nextx, nexty))
8274 return MF_NO_ACTION;
8276 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8277 element == EL_SP_GRAVITY_PORT_RIGHT ||
8278 element == EL_SP_GRAVITY_PORT_UP ||
8279 element == EL_SP_GRAVITY_PORT_DOWN)
8280 game.gravity = !game.gravity;
8282 /* automatically move to the next field with double speed */
8283 player->programmed_action = move_direction;
8284 DOUBLE_PLAYER_SPEED(player);
8286 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8290 case EL_TUBE_VERTICAL:
8291 case EL_TUBE_HORIZONTAL:
8292 case EL_TUBE_VERTICAL_LEFT:
8293 case EL_TUBE_VERTICAL_RIGHT:
8294 case EL_TUBE_HORIZONTAL_UP:
8295 case EL_TUBE_HORIZONTAL_DOWN:
8296 case EL_TUBE_LEFT_UP:
8297 case EL_TUBE_LEFT_DOWN:
8298 case EL_TUBE_RIGHT_UP:
8299 case EL_TUBE_RIGHT_DOWN:
8302 int tube_enter_directions[][2] =
8304 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8305 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8306 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8307 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8308 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8309 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8310 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8311 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8312 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8313 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8314 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8315 { -1, MV_NO_MOVING }
8318 while (tube_enter_directions[i][0] != element)
8321 if (tube_enter_directions[i][0] == -1) /* should not happen */
8325 if (!(tube_enter_directions[i][1] & move_direction))
8326 return MF_NO_ACTION; /* tube has no opening in this direction */
8328 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8334 if (IS_WALKABLE(element))
8336 int sound_action = ACTION_WALKING;
8338 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8340 if (!player->key[element - EL_GATE_1])
8341 return MF_NO_ACTION;
8343 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8345 if (!player->key[element - EL_GATE_1_GRAY])
8346 return MF_NO_ACTION;
8348 else if (element == EL_EXIT_OPEN ||
8349 element == EL_SP_EXIT_OPEN ||
8350 element == EL_SP_EXIT_OPENING)
8352 sound_action = ACTION_PASSING; /* player is passing exit */
8354 else if (element == EL_EMPTY)
8356 sound_action = ACTION_MOVING; /* nothing to walk on */
8359 /* play sound from background or player, whatever is available */
8360 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8361 PlayLevelSoundElementAction(x, y, element, sound_action);
8363 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8367 else if (IS_PASSABLE(element))
8369 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8370 return MF_NO_ACTION;
8373 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8374 return MF_NO_ACTION;
8377 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8379 if (!player->key[element - EL_EM_GATE_1])
8380 return MF_NO_ACTION;
8382 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8384 if (!player->key[element - EL_EM_GATE_1_GRAY])
8385 return MF_NO_ACTION;
8388 /* automatically move to the next field with double speed */
8389 player->programmed_action = move_direction;
8390 DOUBLE_PLAYER_SPEED(player);
8392 PlayLevelSoundAction(x, y, ACTION_PASSING);
8396 else if (IS_DIGGABLE(element))
8400 if (mode != DF_SNAP)
8403 GfxElement[x][y] = GFX_ELEMENT(element);
8406 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8408 player->is_digging = TRUE;
8411 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8413 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8416 if (mode == DF_SNAP)
8417 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8422 else if (IS_COLLECTIBLE(element))
8426 if (mode != DF_SNAP)
8428 GfxElement[x][y] = element;
8429 player->is_collecting = TRUE;
8432 if (element == EL_SPEED_PILL)
8433 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8434 else if (element == EL_EXTRA_TIME && level.time > 0)
8437 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8439 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8441 player->shield_normal_time_left += 10;
8442 if (element == EL_SHIELD_DEADLY)
8443 player->shield_deadly_time_left += 10;
8445 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8447 if (player->inventory_size < MAX_INVENTORY_SIZE)
8448 player->inventory_element[player->inventory_size++] = element;
8450 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8451 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8453 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8455 player->dynabomb_count++;
8456 player->dynabombs_left++;
8458 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8460 player->dynabomb_size++;
8462 else if (element == EL_DYNABOMB_INCREASE_POWER)
8464 player->dynabomb_xl = TRUE;
8466 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8467 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8469 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8470 element - EL_KEY_1 : element - EL_EM_KEY_1);
8472 player->key[key_nr] = TRUE;
8474 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8475 el2edimg(EL_KEY_1 + key_nr));
8476 redraw_mask |= REDRAW_DOOR_1;
8478 else if (IS_ENVELOPE(element))
8481 player->show_envelope = element;
8483 ShowEnvelope(element - EL_ENVELOPE_1);
8486 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8490 for (i = 0; i < element_info[element].collect_count; i++)
8491 if (player->inventory_size < MAX_INVENTORY_SIZE)
8492 player->inventory_element[player->inventory_size++] = element;
8494 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8495 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8497 else if (element_info[element].collect_count > 0)
8499 local_player->gems_still_needed -=
8500 element_info[element].collect_count;
8501 if (local_player->gems_still_needed < 0)
8502 local_player->gems_still_needed = 0;
8504 DrawText(DX_EMERALDS, DY_EMERALDS,
8505 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8508 RaiseScoreElement(element);
8509 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8511 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8514 if (mode == DF_SNAP)
8515 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8520 else if (IS_PUSHABLE(element))
8522 if (mode == DF_SNAP && element != EL_BD_ROCK)
8523 return MF_NO_ACTION;
8525 if (CAN_FALL(element) && dy)
8526 return MF_NO_ACTION;
8528 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8529 !(element == EL_SPRING && use_spring_bug))
8530 return MF_NO_ACTION;
8533 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8534 ((move_direction & MV_VERTICAL &&
8535 ((element_info[element].move_pattern & MV_LEFT &&
8536 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8537 (element_info[element].move_pattern & MV_RIGHT &&
8538 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8539 (move_direction & MV_HORIZONTAL &&
8540 ((element_info[element].move_pattern & MV_UP &&
8541 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8542 (element_info[element].move_pattern & MV_DOWN &&
8543 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8544 return MF_NO_ACTION;
8548 /* do not push elements already moving away faster than player */
8549 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8550 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8551 return MF_NO_ACTION;
8553 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8554 return MF_NO_ACTION;
8558 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8560 if (player->push_delay_value == -1)
8561 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8563 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8565 if (!player->is_pushing)
8566 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8570 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8571 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8572 !player_is_pushing))
8573 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8576 if (!player->is_pushing &&
8577 game.engine_version >= VERSION_IDENT(2,2,0,7))
8578 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8582 printf("::: push delay: %ld [%d, %d] [%d]\n",
8583 player->push_delay_value, FrameCounter, game.engine_version,
8584 player->is_pushing);
8587 player->is_pushing = TRUE;
8589 if (!(IN_LEV_FIELD(nextx, nexty) &&
8590 (IS_FREE(nextx, nexty) ||
8591 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8592 IS_SB_ELEMENT(element)))))
8593 return MF_NO_ACTION;
8595 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8596 return MF_NO_ACTION;
8598 if (player->push_delay == 0) /* new pushing; restart delay */
8599 player->push_delay = FrameCounter;
8601 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8602 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8603 element != EL_SPRING && element != EL_BALLOON)
8605 /* make sure that there is no move delay before next try to push */
8606 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8607 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8609 return MF_NO_ACTION;
8613 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8616 if (IS_SB_ELEMENT(element))
8618 if (element == EL_SOKOBAN_FIELD_FULL)
8620 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8621 local_player->sokobanfields_still_needed++;
8624 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8626 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8627 local_player->sokobanfields_still_needed--;
8630 Feld[x][y] = EL_SOKOBAN_OBJECT;
8632 if (Back[x][y] == Back[nextx][nexty])
8633 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8634 else if (Back[x][y] != 0)
8635 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8638 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8641 if (local_player->sokobanfields_still_needed == 0 &&
8642 game.emulation == EMU_SOKOBAN)
8644 player->LevelSolved = player->GameOver = TRUE;
8645 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8649 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8651 InitMovingField(x, y, move_direction);
8652 GfxAction[x][y] = ACTION_PUSHING;
8654 if (mode == DF_SNAP)
8655 ContinueMoving(x, y);
8657 MovPos[x][y] = (dx != 0 ? dx : dy);
8659 Pushed[x][y] = TRUE;
8660 Pushed[nextx][nexty] = TRUE;
8662 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8663 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8665 player->push_delay_value = -1; /* get new value later */
8667 CheckTriggeredElementSideChange(x, y, element, dig_side,
8668 CE_OTHER_GETS_PUSHED);
8669 CheckElementSideChange(x, y, element, dig_side,
8670 CE_PUSHED_BY_PLAYER, -1);
8674 else if (IS_SWITCHABLE(element))
8676 if (PLAYER_SWITCHING(player, x, y))
8679 player->is_switching = TRUE;
8680 player->switch_x = x;
8681 player->switch_y = y;
8683 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8685 if (element == EL_ROBOT_WHEEL)
8687 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8691 DrawLevelField(x, y);
8693 else if (element == EL_SP_TERMINAL)
8697 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8699 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8701 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8702 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8705 else if (IS_BELT_SWITCH(element))
8707 ToggleBeltSwitch(x, y);
8709 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8710 element == EL_SWITCHGATE_SWITCH_DOWN)
8712 ToggleSwitchgateSwitch(x, y);
8714 else if (element == EL_LIGHT_SWITCH ||
8715 element == EL_LIGHT_SWITCH_ACTIVE)
8717 ToggleLightSwitch(x, y);
8720 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8721 SND_LIGHT_SWITCH_ACTIVATING :
8722 SND_LIGHT_SWITCH_DEACTIVATING);
8725 else if (element == EL_TIMEGATE_SWITCH)
8727 ActivateTimegateSwitch(x, y);
8729 else if (element == EL_BALLOON_SWITCH_LEFT ||
8730 element == EL_BALLOON_SWITCH_RIGHT ||
8731 element == EL_BALLOON_SWITCH_UP ||
8732 element == EL_BALLOON_SWITCH_DOWN ||
8733 element == EL_BALLOON_SWITCH_ANY)
8735 if (element == EL_BALLOON_SWITCH_ANY)
8736 game.balloon_dir = move_direction;
8738 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8739 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8740 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8741 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8744 else if (element == EL_LAMP)
8746 Feld[x][y] = EL_LAMP_ACTIVE;
8747 local_player->lights_still_needed--;
8749 DrawLevelField(x, y);
8751 else if (element == EL_TIME_ORB_FULL)
8753 Feld[x][y] = EL_TIME_ORB_EMPTY;
8755 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8757 DrawLevelField(x, y);
8760 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8768 if (!PLAYER_SWITCHING(player, x, y))
8770 player->is_switching = TRUE;
8771 player->switch_x = x;
8772 player->switch_y = y;
8774 CheckTriggeredElementSideChange(x, y, element, dig_side,
8775 CE_OTHER_IS_SWITCHING);
8776 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8779 CheckTriggeredElementSideChange(x, y, element, dig_side,
8780 CE_OTHER_GETS_PRESSED);
8781 CheckElementSideChange(x, y, element, dig_side,
8782 CE_PRESSED_BY_PLAYER, -1);
8785 return MF_NO_ACTION;
8788 player->push_delay = 0;
8790 if (Feld[x][y] != element) /* really digged/collected something */
8791 player->is_collecting = !player->is_digging;
8796 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8798 int jx = player->jx, jy = player->jy;
8799 int x = jx + dx, y = jy + dy;
8800 int snap_direction = (dx == -1 ? MV_LEFT :
8801 dx == +1 ? MV_RIGHT :
8803 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8805 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8808 if (!player->active || !IN_LEV_FIELD(x, y))
8816 if (player->MovPos == 0)
8817 player->is_pushing = FALSE;
8819 player->is_snapping = FALSE;
8821 if (player->MovPos == 0)
8823 player->is_moving = FALSE;
8824 player->is_digging = FALSE;
8825 player->is_collecting = FALSE;
8831 if (player->is_snapping)
8834 player->MovDir = snap_direction;
8836 player->is_moving = FALSE;
8837 player->is_digging = FALSE;
8838 player->is_collecting = FALSE;
8840 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8843 player->is_snapping = TRUE;
8845 player->is_moving = FALSE;
8846 player->is_digging = FALSE;
8847 player->is_collecting = FALSE;
8849 DrawLevelField(x, y);
8855 boolean DropElement(struct PlayerInfo *player)
8857 int jx = player->jx, jy = player->jy;
8860 if (!player->active || player->MovPos)
8863 old_element = Feld[jx][jy];
8865 /* check if player has anything that can be dropped */
8866 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8869 /* check if anything can be dropped at the current position */
8870 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8873 /* collected custom elements can only be dropped on empty fields */
8874 if (player->inventory_size > 0 &&
8875 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8876 && old_element != EL_EMPTY)
8879 if (old_element != EL_EMPTY)
8880 Back[jx][jy] = old_element; /* store old element on this field */
8882 MovDelay[jx][jy] = 96;
8884 ResetGfxAnimation(jx, jy);
8885 ResetRandomAnimationValue(jx, jy);
8887 if (player->inventory_size > 0)
8889 int new_element = player->inventory_element[--player->inventory_size];
8891 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8892 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8895 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8896 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8898 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8899 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8901 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8903 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8904 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8906 TestIfElementTouchesCustomElement(jx, jy);
8908 else /* player is dropping a dyna bomb */
8910 player->dynabombs_left--;
8913 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8915 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8916 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8918 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8924 /* ------------------------------------------------------------------------- */
8925 /* game sound playing functions */
8926 /* ------------------------------------------------------------------------- */
8928 static int *loop_sound_frame = NULL;
8929 static int *loop_sound_volume = NULL;
8931 void InitPlayLevelSound()
8933 int num_sounds = getSoundListSize();
8935 if (loop_sound_frame != NULL)
8936 free(loop_sound_frame);
8938 if (loop_sound_volume != NULL)
8939 free(loop_sound_volume);
8941 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8942 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8945 static void PlayLevelSound(int x, int y, int nr)
8947 int sx = SCREENX(x), sy = SCREENY(y);
8948 int volume, stereo_position;
8949 int max_distance = 8;
8950 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8952 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8953 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8956 if (!IN_LEV_FIELD(x, y) ||
8957 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8958 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8961 volume = SOUND_MAX_VOLUME;
8963 if (!IN_SCR_FIELD(sx, sy))
8965 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8966 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8968 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8971 stereo_position = (SOUND_MAX_LEFT +
8972 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8973 (SCR_FIELDX + 2 * max_distance));
8975 if (IS_LOOP_SOUND(nr))
8977 /* This assures that quieter loop sounds do not overwrite louder ones,
8978 while restarting sound volume comparison with each new game frame. */
8980 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8983 loop_sound_volume[nr] = volume;
8984 loop_sound_frame[nr] = FrameCounter;
8987 PlaySoundExt(nr, volume, stereo_position, type);
8990 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8992 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8993 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8994 y < LEVELY(BY1) ? LEVELY(BY1) :
8995 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8999 static void PlayLevelSoundAction(int x, int y, int action)
9001 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9004 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9006 int sound_effect = element_info[element].sound[action];
9008 if (sound_effect != SND_UNDEFINED)
9009 PlayLevelSound(x, y, sound_effect);
9012 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9015 int sound_effect = element_info[element].sound[action];
9017 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9018 PlayLevelSound(x, y, sound_effect);
9021 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9023 int sound_effect = element_info[Feld[x][y]].sound[action];
9025 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9026 PlayLevelSound(x, y, sound_effect);
9029 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9031 int sound_effect = element_info[Feld[x][y]].sound[action];
9033 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9034 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
9037 static void PlayLevelMusic()
9039 if (levelset.music[level_nr] != MUS_UNDEFINED)
9040 PlayMusic(levelset.music[level_nr]); /* from config file */
9042 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9045 void RaiseScore(int value)
9047 local_player->score += value;
9048 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9051 void RaiseScoreElement(int element)
9057 case EL_EMERALD_YELLOW:
9058 case EL_EMERALD_RED:
9059 case EL_EMERALD_PURPLE:
9060 case EL_SP_INFOTRON:
9061 RaiseScore(level.score[SC_EMERALD]);
9064 RaiseScore(level.score[SC_DIAMOND]);
9067 RaiseScore(level.score[SC_CRYSTAL]);
9070 RaiseScore(level.score[SC_PEARL]);
9073 case EL_BD_BUTTERFLY:
9074 case EL_SP_ELECTRON:
9075 RaiseScore(level.score[SC_BUG]);
9079 case EL_SP_SNIKSNAK:
9080 RaiseScore(level.score[SC_SPACESHIP]);
9083 case EL_DARK_YAMYAM:
9084 RaiseScore(level.score[SC_YAMYAM]);
9087 RaiseScore(level.score[SC_ROBOT]);
9090 RaiseScore(level.score[SC_PACMAN]);
9093 RaiseScore(level.score[SC_NUT]);
9096 case EL_SP_DISK_RED:
9097 case EL_DYNABOMB_INCREASE_NUMBER:
9098 case EL_DYNABOMB_INCREASE_SIZE:
9099 case EL_DYNABOMB_INCREASE_POWER:
9100 RaiseScore(level.score[SC_DYNAMITE]);
9102 case EL_SHIELD_NORMAL:
9103 case EL_SHIELD_DEADLY:
9104 RaiseScore(level.score[SC_SHIELD]);
9107 RaiseScore(level.score[SC_TIME_BONUS]);
9113 RaiseScore(level.score[SC_KEY]);
9116 RaiseScore(element_info[element].collect_score);
9121 void RequestQuitGame(boolean ask_if_really_quit)
9123 if (AllPlayersGone ||
9124 !ask_if_really_quit ||
9125 level_editor_test_game ||
9126 Request("Do you really want to quit the game ?",
9127 REQ_ASK | REQ_STAY_CLOSED))
9129 #if defined(PLATFORM_UNIX)
9130 if (options.network)
9131 SendToServer_StopPlaying();
9135 game_status = GAME_MODE_MAIN;
9141 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9146 /* ---------- new game button stuff ---------------------------------------- */
9148 /* graphic position values for game buttons */
9149 #define GAME_BUTTON_XSIZE 30
9150 #define GAME_BUTTON_YSIZE 30
9151 #define GAME_BUTTON_XPOS 5
9152 #define GAME_BUTTON_YPOS 215
9153 #define SOUND_BUTTON_XPOS 5
9154 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9156 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9157 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9158 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9159 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9160 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9161 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9168 } gamebutton_info[NUM_GAME_BUTTONS] =
9171 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9176 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9181 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9186 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9187 SOUND_CTRL_ID_MUSIC,
9188 "background music on/off"
9191 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9192 SOUND_CTRL_ID_LOOPS,
9193 "sound loops on/off"
9196 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9197 SOUND_CTRL_ID_SIMPLE,
9198 "normal sounds on/off"
9202 void CreateGameButtons()
9206 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9208 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9209 struct GadgetInfo *gi;
9212 unsigned long event_mask;
9213 int gd_xoffset, gd_yoffset;
9214 int gd_x1, gd_x2, gd_y1, gd_y2;
9217 gd_xoffset = gamebutton_info[i].x;
9218 gd_yoffset = gamebutton_info[i].y;
9219 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9220 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9222 if (id == GAME_CTRL_ID_STOP ||
9223 id == GAME_CTRL_ID_PAUSE ||
9224 id == GAME_CTRL_ID_PLAY)
9226 button_type = GD_TYPE_NORMAL_BUTTON;
9228 event_mask = GD_EVENT_RELEASED;
9229 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9230 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9234 button_type = GD_TYPE_CHECK_BUTTON;
9236 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9237 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9238 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9239 event_mask = GD_EVENT_PRESSED;
9240 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9241 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9244 gi = CreateGadget(GDI_CUSTOM_ID, id,
9245 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9246 GDI_X, DX + gd_xoffset,
9247 GDI_Y, DY + gd_yoffset,
9248 GDI_WIDTH, GAME_BUTTON_XSIZE,
9249 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9250 GDI_TYPE, button_type,
9251 GDI_STATE, GD_BUTTON_UNPRESSED,
9252 GDI_CHECKED, checked,
9253 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9254 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9255 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9256 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9257 GDI_EVENT_MASK, event_mask,
9258 GDI_CALLBACK_ACTION, HandleGameButtons,
9262 Error(ERR_EXIT, "cannot create gadget");
9264 game_gadget[id] = gi;
9268 void FreeGameButtons()
9272 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9273 FreeGadget(game_gadget[i]);
9276 static void MapGameButtons()
9280 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9281 MapGadget(game_gadget[i]);
9284 void UnmapGameButtons()
9288 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9289 UnmapGadget(game_gadget[i]);
9292 static void HandleGameButtons(struct GadgetInfo *gi)
9294 int id = gi->custom_id;
9296 if (game_status != GAME_MODE_PLAYING)
9301 case GAME_CTRL_ID_STOP:
9302 RequestQuitGame(TRUE);
9305 case GAME_CTRL_ID_PAUSE:
9306 if (options.network)
9308 #if defined(PLATFORM_UNIX)
9310 SendToServer_ContinuePlaying();
9312 SendToServer_PausePlaying();
9316 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9319 case GAME_CTRL_ID_PLAY:
9322 #if defined(PLATFORM_UNIX)
9323 if (options.network)
9324 SendToServer_ContinuePlaying();
9328 tape.pausing = FALSE;
9329 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9334 case SOUND_CTRL_ID_MUSIC:
9335 if (setup.sound_music)
9337 setup.sound_music = FALSE;
9340 else if (audio.music_available)
9342 setup.sound = setup.sound_music = TRUE;
9344 SetAudioMode(setup.sound);
9350 case SOUND_CTRL_ID_LOOPS:
9351 if (setup.sound_loops)
9352 setup.sound_loops = FALSE;
9353 else if (audio.loops_available)
9355 setup.sound = setup.sound_loops = TRUE;
9356 SetAudioMode(setup.sound);
9360 case SOUND_CTRL_ID_SIMPLE:
9361 if (setup.sound_simple)
9362 setup.sound_simple = FALSE;
9363 else if (audio.sound_available)
9365 setup.sound = setup.sound_simple = TRUE;
9366 SetAudioMode(setup.sound);