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->last_action_waiting = ACTION_DEFAULT;
1080 player->special_action_bored = ACTION_DEFAULT;
1081 player->special_action_sleeping = ACTION_DEFAULT;
1083 player->num_special_action_bored = 0;
1084 player->num_special_action_sleeping = 0;
1086 /* determine number of special actions for bored and sleeping animation */
1087 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1089 boolean found = FALSE;
1091 for (k = 0; k < NUM_DIRECTIONS; k++)
1092 if (el_act_dir2img(player->element_nr, j, k) !=
1093 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1097 player->num_special_action_bored++;
1101 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1103 boolean found = FALSE;
1105 for (k = 0; k < NUM_DIRECTIONS; k++)
1106 if (el_act_dir2img(player->element_nr, j, k) !=
1107 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1111 player->num_special_action_sleeping++;
1116 player->switch_x = -1;
1117 player->switch_y = -1;
1119 player->show_envelope = 0;
1121 player->move_delay = game.initial_move_delay;
1122 player->move_delay_value = game.initial_move_delay_value;
1124 player->push_delay = 0;
1125 player->push_delay_value = game.initial_push_delay_value;
1127 player->last_jx = player->last_jy = 0;
1128 player->jx = player->jy = 0;
1130 player->shield_normal_time_left = 0;
1131 player->shield_deadly_time_left = 0;
1133 player->inventory_size = 0;
1135 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1136 SnapField(player, 0, 0);
1138 player->LevelSolved = FALSE;
1139 player->GameOver = FALSE;
1142 network_player_action_received = FALSE;
1144 #if defined(PLATFORM_UNIX)
1145 /* initial null action */
1146 if (network_playing)
1147 SendToServer_MovePlayer(MV_NO_MOVING);
1155 TimeLeft = level.time;
1157 ScreenMovDir = MV_NO_MOVING;
1161 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1163 AllPlayersGone = FALSE;
1165 game.yamyam_content_nr = 0;
1166 game.magic_wall_active = FALSE;
1167 game.magic_wall_time_left = 0;
1168 game.light_time_left = 0;
1169 game.timegate_time_left = 0;
1170 game.switchgate_pos = 0;
1171 game.balloon_dir = MV_NO_MOVING;
1172 game.gravity = level.initial_gravity;
1173 game.explosions_delayed = TRUE;
1175 game.envelope_active = FALSE;
1177 for (i = 0; i < 4; i++)
1179 game.belt_dir[i] = MV_NO_MOVING;
1180 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1183 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1184 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1186 for (x = 0; x < lev_fieldx; x++)
1188 for (y = 0; y < lev_fieldy; y++)
1190 Feld[x][y] = level.field[x][y];
1191 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1192 ChangeDelay[x][y] = 0;
1193 ChangePage[x][y] = -1;
1194 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1196 WasJustMoving[x][y] = 0;
1197 WasJustFalling[x][y] = 0;
1199 Pushed[x][y] = FALSE;
1201 Changed[x][y] = CE_BITMASK_DEFAULT;
1202 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1204 ExplodePhase[x][y] = 0;
1205 ExplodeField[x][y] = EX_NO_EXPLOSION;
1207 RunnerVisit[x][y] = 0;
1208 PlayerVisit[x][y] = 0;
1211 GfxRandom[x][y] = INIT_GFX_RANDOM();
1212 GfxElement[x][y] = EL_UNDEFINED;
1213 GfxAction[x][y] = ACTION_DEFAULT;
1214 GfxDir[x][y] = MV_NO_MOVING;
1218 for (y = 0; y < lev_fieldy; y++)
1220 for (x = 0; x < lev_fieldx; x++)
1222 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1224 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1226 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1229 InitField(x, y, TRUE);
1235 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1236 emulate_sb ? EMU_SOKOBAN :
1237 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1239 /* correct non-moving belts to start moving left */
1240 for (i = 0; i < 4; i++)
1241 if (game.belt_dir[i] == MV_NO_MOVING)
1242 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1244 /* check if any connected player was not found in playfield */
1245 for (i = 0; i < MAX_PLAYERS; i++)
1247 struct PlayerInfo *player = &stored_player[i];
1249 if (player->connected && !player->present)
1251 for (j = 0; j < MAX_PLAYERS; j++)
1253 struct PlayerInfo *some_player = &stored_player[j];
1254 int jx = some_player->jx, jy = some_player->jy;
1256 /* assign first free player found that is present in the playfield */
1257 if (some_player->present && !some_player->connected)
1259 player->present = TRUE;
1260 player->active = TRUE;
1261 some_player->present = FALSE;
1263 StorePlayer[jx][jy] = player->element_nr;
1264 player->jx = player->last_jx = jx;
1265 player->jy = player->last_jy = jy;
1275 /* when playing a tape, eliminate all players who do not participate */
1277 for (i = 0; i < MAX_PLAYERS; i++)
1279 if (stored_player[i].active && !tape.player_participates[i])
1281 struct PlayerInfo *player = &stored_player[i];
1282 int jx = player->jx, jy = player->jy;
1284 player->active = FALSE;
1285 StorePlayer[jx][jy] = 0;
1286 Feld[jx][jy] = EL_EMPTY;
1290 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1292 /* when in single player mode, eliminate all but the first active player */
1294 for (i = 0; i < MAX_PLAYERS; i++)
1296 if (stored_player[i].active)
1298 for (j = i + 1; j < MAX_PLAYERS; j++)
1300 if (stored_player[j].active)
1302 struct PlayerInfo *player = &stored_player[j];
1303 int jx = player->jx, jy = player->jy;
1305 player->active = FALSE;
1306 StorePlayer[jx][jy] = 0;
1307 Feld[jx][jy] = EL_EMPTY;
1314 /* when recording the game, store which players take part in the game */
1317 for (i = 0; i < MAX_PLAYERS; i++)
1318 if (stored_player[i].active)
1319 tape.player_participates[i] = TRUE;
1324 for (i = 0; i < MAX_PLAYERS; i++)
1326 struct PlayerInfo *player = &stored_player[i];
1328 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1333 if (local_player == player)
1334 printf("Player %d is local player.\n", i+1);
1338 if (BorderElement == EL_EMPTY)
1341 SBX_Right = lev_fieldx - SCR_FIELDX;
1343 SBY_Lower = lev_fieldy - SCR_FIELDY;
1348 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1350 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1353 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1354 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1356 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1357 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1359 /* if local player not found, look for custom element that might create
1360 the player (make some assumptions about the right custom element) */
1361 if (!local_player->present)
1363 int start_x = 0, start_y = 0;
1364 int found_rating = 0;
1365 int found_element = EL_UNDEFINED;
1367 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1369 int element = Feld[x][y];
1374 if (!IS_CUSTOM_ELEMENT(element))
1377 if (CAN_CHANGE(element))
1379 for (i = 0; i < element_info[element].num_change_pages; i++)
1381 content = element_info[element].change_page[i].target_element;
1382 is_player = ELEM_IS_PLAYER(content);
1384 if (is_player && (found_rating < 3 || element < found_element))
1390 found_element = element;
1395 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1397 content = element_info[element].content[xx][yy];
1398 is_player = ELEM_IS_PLAYER(content);
1400 if (is_player && (found_rating < 2 || element < found_element))
1402 start_x = x + xx - 1;
1403 start_y = y + yy - 1;
1406 found_element = element;
1409 if (!CAN_CHANGE(element))
1412 for (i = 0; i < element_info[element].num_change_pages; i++)
1414 content = element_info[element].change_page[i].content[xx][yy];
1415 is_player = ELEM_IS_PLAYER(content);
1417 if (is_player && (found_rating < 1 || element < found_element))
1419 start_x = x + xx - 1;
1420 start_y = y + yy - 1;
1423 found_element = element;
1429 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1430 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1433 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1434 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1440 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1441 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1442 local_player->jx - MIDPOSX);
1444 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1445 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1446 local_player->jy - MIDPOSY);
1448 scroll_x = SBX_Left;
1449 scroll_y = SBY_Upper;
1450 if (local_player->jx >= SBX_Left + MIDPOSX)
1451 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1452 local_player->jx - MIDPOSX :
1454 if (local_player->jy >= SBY_Upper + MIDPOSY)
1455 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1456 local_player->jy - MIDPOSY :
1461 CloseDoor(DOOR_CLOSE_1);
1466 /* after drawing the level, correct some elements */
1467 if (game.timegate_time_left == 0)
1468 CloseAllOpenTimegates();
1470 if (setup.soft_scrolling)
1471 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1473 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1476 /* copy default game door content to main double buffer */
1477 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1478 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1481 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1484 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1485 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1486 BlitBitmap(drawto, drawto,
1487 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1488 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1489 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1490 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1493 DrawGameDoorValues();
1497 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1498 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1499 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1503 /* copy actual game door content to door double buffer for OpenDoor() */
1504 BlitBitmap(drawto, bitmap_db_door,
1505 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1507 OpenDoor(DOOR_OPEN_ALL);
1509 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1511 if (setup.sound_music)
1514 KeyboardAutoRepeatOffUnlessAutoplay();
1518 for (i = 0; i < 4; i++)
1519 printf("Player %d %sactive.\n",
1520 i + 1, (stored_player[i].active ? "" : "not "));
1524 printf("::: starting game [%d]\n", FrameCounter);
1528 void InitMovDir(int x, int y)
1530 int i, element = Feld[x][y];
1531 static int xy[4][2] =
1538 static int direction[3][4] =
1540 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1541 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1542 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1551 Feld[x][y] = EL_BUG;
1552 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1555 case EL_SPACESHIP_RIGHT:
1556 case EL_SPACESHIP_UP:
1557 case EL_SPACESHIP_LEFT:
1558 case EL_SPACESHIP_DOWN:
1559 Feld[x][y] = EL_SPACESHIP;
1560 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1563 case EL_BD_BUTTERFLY_RIGHT:
1564 case EL_BD_BUTTERFLY_UP:
1565 case EL_BD_BUTTERFLY_LEFT:
1566 case EL_BD_BUTTERFLY_DOWN:
1567 Feld[x][y] = EL_BD_BUTTERFLY;
1568 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1571 case EL_BD_FIREFLY_RIGHT:
1572 case EL_BD_FIREFLY_UP:
1573 case EL_BD_FIREFLY_LEFT:
1574 case EL_BD_FIREFLY_DOWN:
1575 Feld[x][y] = EL_BD_FIREFLY;
1576 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1579 case EL_PACMAN_RIGHT:
1581 case EL_PACMAN_LEFT:
1582 case EL_PACMAN_DOWN:
1583 Feld[x][y] = EL_PACMAN;
1584 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1587 case EL_SP_SNIKSNAK:
1588 MovDir[x][y] = MV_UP;
1591 case EL_SP_ELECTRON:
1592 MovDir[x][y] = MV_LEFT;
1599 Feld[x][y] = EL_MOLE;
1600 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1604 if (IS_CUSTOM_ELEMENT(element))
1606 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1607 MovDir[x][y] = element_info[element].move_direction_initial;
1608 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1609 element_info[element].move_pattern == MV_TURNING_LEFT ||
1610 element_info[element].move_pattern == MV_TURNING_RIGHT)
1611 MovDir[x][y] = 1 << RND(4);
1612 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1613 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1614 else if (element_info[element].move_pattern == MV_VERTICAL)
1615 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1616 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1617 MovDir[x][y] = element_info[element].move_pattern;
1618 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1619 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1621 for (i = 0; i < 4; i++)
1623 int x1 = x + xy[i][0];
1624 int y1 = y + xy[i][1];
1626 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1628 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1629 MovDir[x][y] = direction[0][i];
1631 MovDir[x][y] = direction[1][i];
1640 MovDir[x][y] = 1 << RND(4);
1642 if (element != EL_BUG &&
1643 element != EL_SPACESHIP &&
1644 element != EL_BD_BUTTERFLY &&
1645 element != EL_BD_FIREFLY)
1648 for (i = 0; i < 4; i++)
1650 int x1 = x + xy[i][0];
1651 int y1 = y + xy[i][1];
1653 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1655 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1657 MovDir[x][y] = direction[0][i];
1660 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1661 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1663 MovDir[x][y] = direction[1][i];
1672 GfxDir[x][y] = MovDir[x][y];
1675 void InitAmoebaNr(int x, int y)
1678 int group_nr = AmoebeNachbarNr(x, y);
1682 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1684 if (AmoebaCnt[i] == 0)
1692 AmoebaNr[x][y] = group_nr;
1693 AmoebaCnt[group_nr]++;
1694 AmoebaCnt2[group_nr]++;
1700 boolean raise_level = FALSE;
1702 if (local_player->MovPos)
1706 if (tape.auto_play) /* tape might already be stopped here */
1707 tape.auto_play_level_solved = TRUE;
1709 if (tape.playing && tape.auto_play)
1710 tape.auto_play_level_solved = TRUE;
1713 local_player->LevelSolved = FALSE;
1715 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1719 if (!tape.playing && setup.sound_loops)
1720 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1721 SND_CTRL_PLAY_LOOP);
1723 while (TimeLeft > 0)
1725 if (!tape.playing && !setup.sound_loops)
1726 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1727 if (TimeLeft > 0 && !(TimeLeft % 10))
1728 RaiseScore(level.score[SC_TIME_BONUS]);
1729 if (TimeLeft > 100 && !(TimeLeft % 10))
1733 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1740 if (!tape.playing && setup.sound_loops)
1741 StopSound(SND_GAME_LEVELTIME_BONUS);
1743 else if (level.time == 0) /* level without time limit */
1745 if (!tape.playing && setup.sound_loops)
1746 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1747 SND_CTRL_PLAY_LOOP);
1749 while (TimePlayed < 999)
1751 if (!tape.playing && !setup.sound_loops)
1752 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1753 if (TimePlayed < 999 && !(TimePlayed % 10))
1754 RaiseScore(level.score[SC_TIME_BONUS]);
1755 if (TimePlayed < 900 && !(TimePlayed % 10))
1759 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1766 if (!tape.playing && setup.sound_loops)
1767 StopSound(SND_GAME_LEVELTIME_BONUS);
1770 /* close exit door after last player */
1771 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1772 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1774 int element = Feld[ExitX][ExitY];
1776 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1777 EL_SP_EXIT_CLOSING);
1779 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1782 /* Hero disappears */
1783 DrawLevelField(ExitX, ExitY);
1789 CloseDoor(DOOR_CLOSE_1);
1794 SaveTape(tape.level_nr); /* Ask to save tape */
1797 if (level_nr == leveldir_current->handicap_level)
1799 leveldir_current->handicap_level++;
1800 SaveLevelSetup_SeriesInfo();
1803 if (level_editor_test_game)
1804 local_player->score = -1; /* no highscore when playing from editor */
1805 else if (level_nr < leveldir_current->last_level)
1806 raise_level = TRUE; /* advance to next level */
1808 if ((hi_pos = NewHiScore()) >= 0)
1810 game_status = GAME_MODE_SCORES;
1811 DrawHallOfFame(hi_pos);
1820 game_status = GAME_MODE_MAIN;
1837 LoadScore(level_nr);
1839 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1840 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1843 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1845 if (local_player->score > highscore[k].Score)
1847 /* player has made it to the hall of fame */
1849 if (k < MAX_SCORE_ENTRIES - 1)
1851 int m = MAX_SCORE_ENTRIES - 1;
1854 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1855 if (!strcmp(setup.player_name, highscore[l].Name))
1857 if (m == k) /* player's new highscore overwrites his old one */
1861 for (l = m; l > k; l--)
1863 strcpy(highscore[l].Name, highscore[l - 1].Name);
1864 highscore[l].Score = highscore[l - 1].Score;
1871 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1872 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1873 highscore[k].Score = local_player->score;
1879 else if (!strncmp(setup.player_name, highscore[k].Name,
1880 MAX_PLAYER_NAME_LEN))
1881 break; /* player already there with a higher score */
1887 SaveScore(level_nr);
1892 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1894 if (player->GfxAction != action || player->GfxDir != dir)
1897 printf("Player frame reset! (%d => %d, %d => %d)\n",
1898 player->GfxAction, action, player->GfxDir, dir);
1901 player->GfxAction = action;
1902 player->GfxDir = dir;
1904 player->StepFrame = 0;
1908 static void ResetRandomAnimationValue(int x, int y)
1910 GfxRandom[x][y] = INIT_GFX_RANDOM();
1913 static void ResetGfxAnimation(int x, int y)
1916 GfxAction[x][y] = ACTION_DEFAULT;
1917 GfxDir[x][y] = MovDir[x][y];
1920 void InitMovingField(int x, int y, int direction)
1922 int element = Feld[x][y];
1923 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1924 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1928 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1929 ResetGfxAnimation(x, y);
1931 MovDir[newx][newy] = MovDir[x][y] = direction;
1932 GfxDir[x][y] = direction;
1934 if (Feld[newx][newy] == EL_EMPTY)
1935 Feld[newx][newy] = EL_BLOCKED;
1937 if (direction == MV_DOWN && CAN_FALL(element))
1938 GfxAction[x][y] = ACTION_FALLING;
1940 GfxAction[x][y] = ACTION_MOVING;
1942 GfxFrame[newx][newy] = GfxFrame[x][y];
1943 GfxRandom[newx][newy] = GfxRandom[x][y];
1944 GfxAction[newx][newy] = GfxAction[x][y];
1945 GfxDir[newx][newy] = GfxDir[x][y];
1948 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1950 int direction = MovDir[x][y];
1951 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1952 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1958 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1960 int oldx = x, oldy = y;
1961 int direction = MovDir[x][y];
1963 if (direction == MV_LEFT)
1965 else if (direction == MV_RIGHT)
1967 else if (direction == MV_UP)
1969 else if (direction == MV_DOWN)
1972 *comes_from_x = oldx;
1973 *comes_from_y = oldy;
1976 int MovingOrBlocked2Element(int x, int y)
1978 int element = Feld[x][y];
1980 if (element == EL_BLOCKED)
1984 Blocked2Moving(x, y, &oldx, &oldy);
1985 return Feld[oldx][oldy];
1991 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1993 /* like MovingOrBlocked2Element(), but if element is moving
1994 and (x,y) is the field the moving element is just leaving,
1995 return EL_BLOCKED instead of the element value */
1996 int element = Feld[x][y];
1998 if (IS_MOVING(x, y))
2000 if (element == EL_BLOCKED)
2004 Blocked2Moving(x, y, &oldx, &oldy);
2005 return Feld[oldx][oldy];
2014 static void RemoveField(int x, int y)
2016 Feld[x][y] = EL_EMPTY;
2023 ChangeDelay[x][y] = 0;
2024 ChangePage[x][y] = -1;
2025 Pushed[x][y] = FALSE;
2027 GfxElement[x][y] = EL_UNDEFINED;
2028 GfxAction[x][y] = ACTION_DEFAULT;
2029 GfxDir[x][y] = MV_NO_MOVING;
2032 void RemoveMovingField(int x, int y)
2034 int oldx = x, oldy = y, newx = x, newy = y;
2035 int element = Feld[x][y];
2036 int next_element = EL_UNDEFINED;
2038 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2041 if (IS_MOVING(x, y))
2043 Moving2Blocked(x, y, &newx, &newy);
2044 if (Feld[newx][newy] != EL_BLOCKED)
2047 else if (element == EL_BLOCKED)
2049 Blocked2Moving(x, y, &oldx, &oldy);
2050 if (!IS_MOVING(oldx, oldy))
2054 if (element == EL_BLOCKED &&
2055 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2056 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2057 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2058 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2059 next_element = get_next_element(Feld[oldx][oldy]);
2061 RemoveField(oldx, oldy);
2062 RemoveField(newx, newy);
2064 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2066 if (next_element != EL_UNDEFINED)
2067 Feld[oldx][oldy] = next_element;
2069 DrawLevelField(oldx, oldy);
2070 DrawLevelField(newx, newy);
2073 void DrawDynamite(int x, int y)
2075 int sx = SCREENX(x), sy = SCREENY(y);
2076 int graphic = el2img(Feld[x][y]);
2079 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2082 if (IS_WALKABLE_INSIDE(Back[x][y]))
2086 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2087 else if (Store[x][y])
2088 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2090 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2093 if (Back[x][y] || Store[x][y])
2094 DrawGraphicThruMask(sx, sy, graphic, frame);
2096 DrawGraphic(sx, sy, graphic, frame);
2098 if (game.emulation == EMU_SUPAPLEX)
2099 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2100 else if (Store[x][y])
2101 DrawGraphicThruMask(sx, sy, graphic, frame);
2103 DrawGraphic(sx, sy, graphic, frame);
2107 void CheckDynamite(int x, int y)
2109 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2113 if (MovDelay[x][y] != 0)
2116 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2123 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2125 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2126 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2127 StopSound(SND_DYNAMITE_ACTIVE);
2129 StopSound(SND_DYNABOMB_ACTIVE);
2135 void RelocatePlayer(int x, int y, int element)
2137 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2140 RemoveField(x, y); /* temporarily remove newly placed player */
2141 DrawLevelField(x, y);
2144 if (player->present)
2146 while (player->MovPos)
2148 ScrollPlayer(player, SCROLL_GO_ON);
2149 ScrollScreen(NULL, SCROLL_GO_ON);
2155 Delay(GAME_FRAME_DELAY);
2158 DrawPlayer(player); /* needed here only to cleanup last field */
2159 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2161 player->is_moving = FALSE;
2164 Feld[x][y] = element;
2165 InitPlayerField(x, y, element, TRUE);
2167 if (player == local_player)
2169 int scroll_xx = -999, scroll_yy = -999;
2171 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2174 int fx = FX, fy = FY;
2176 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2177 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2178 local_player->jx - MIDPOSX);
2180 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2181 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2182 local_player->jy - MIDPOSY);
2184 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2185 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2190 fx += dx * TILEX / 2;
2191 fy += dy * TILEY / 2;
2193 ScrollLevel(dx, dy);
2196 /* scroll in two steps of half tile size to make things smoother */
2197 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2199 Delay(GAME_FRAME_DELAY);
2201 /* scroll second step to align at full tile size */
2203 Delay(GAME_FRAME_DELAY);
2208 void Explode(int ex, int ey, int phase, int mode)
2212 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2213 int last_phase = num_phase * delay;
2214 int half_phase = (num_phase / 2) * delay;
2215 int first_phase_after_start = EX_PHASE_START + 1;
2217 if (game.explosions_delayed)
2219 ExplodeField[ex][ey] = mode;
2223 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2225 int center_element = Feld[ex][ey];
2228 /* --- This is only really needed (and now handled) in "Impact()". --- */
2229 /* do not explode moving elements that left the explode field in time */
2230 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2231 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2235 if (mode == EX_NORMAL || mode == EX_CENTER)
2236 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2238 /* remove things displayed in background while burning dynamite */
2239 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2242 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2244 /* put moving element to center field (and let it explode there) */
2245 center_element = MovingOrBlocked2Element(ex, ey);
2246 RemoveMovingField(ex, ey);
2247 Feld[ex][ey] = center_element;
2250 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2252 int xx = x - ex + 1;
2253 int yy = y - ey + 1;
2256 if (!IN_LEV_FIELD(x, y) ||
2257 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2258 (x != ex || y != ey)))
2261 element = Feld[x][y];
2263 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2265 element = MovingOrBlocked2Element(x, y);
2267 if (!IS_EXPLOSION_PROOF(element))
2268 RemoveMovingField(x, y);
2274 if (IS_EXPLOSION_PROOF(element))
2277 /* indestructible elements can only explode in center (but not flames) */
2278 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2279 element == EL_FLAMES)
2284 if ((IS_INDESTRUCTIBLE(element) &&
2285 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2286 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2287 element == EL_FLAMES)
2291 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2293 if (IS_ACTIVE_BOMB(element))
2295 /* re-activate things under the bomb like gate or penguin */
2296 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2303 /* save walkable background elements while explosion on same tile */
2305 if (IS_INDESTRUCTIBLE(element))
2306 Back[x][y] = element;
2308 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2309 Back[x][y] = element;
2312 /* ignite explodable elements reached by other explosion */
2313 if (element == EL_EXPLOSION)
2314 element = Store2[x][y];
2317 if (AmoebaNr[x][y] &&
2318 (element == EL_AMOEBA_FULL ||
2319 element == EL_BD_AMOEBA ||
2320 element == EL_AMOEBA_GROWING))
2322 AmoebaCnt[AmoebaNr[x][y]]--;
2323 AmoebaCnt2[AmoebaNr[x][y]]--;
2329 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2331 switch(StorePlayer[ex][ey])
2334 Store[x][y] = EL_EMERALD_RED;
2337 Store[x][y] = EL_EMERALD;
2340 Store[x][y] = EL_EMERALD_PURPLE;
2344 Store[x][y] = EL_EMERALD_YELLOW;
2348 if (game.emulation == EMU_SUPAPLEX)
2349 Store[x][y] = EL_EMPTY;
2351 else if (center_element == EL_MOLE)
2352 Store[x][y] = EL_EMERALD_RED;
2353 else if (center_element == EL_PENGUIN)
2354 Store[x][y] = EL_EMERALD_PURPLE;
2355 else if (center_element == EL_BUG)
2356 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2357 else if (center_element == EL_BD_BUTTERFLY)
2358 Store[x][y] = EL_BD_DIAMOND;
2359 else if (center_element == EL_SP_ELECTRON)
2360 Store[x][y] = EL_SP_INFOTRON;
2361 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2362 Store[x][y] = level.amoeba_content;
2363 else if (center_element == EL_YAMYAM)
2364 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2365 else if (IS_CUSTOM_ELEMENT(center_element) &&
2366 element_info[center_element].content[xx][yy] != EL_EMPTY)
2367 Store[x][y] = element_info[center_element].content[xx][yy];
2368 else if (element == EL_WALL_EMERALD)
2369 Store[x][y] = EL_EMERALD;
2370 else if (element == EL_WALL_DIAMOND)
2371 Store[x][y] = EL_DIAMOND;
2372 else if (element == EL_WALL_BD_DIAMOND)
2373 Store[x][y] = EL_BD_DIAMOND;
2374 else if (element == EL_WALL_EMERALD_YELLOW)
2375 Store[x][y] = EL_EMERALD_YELLOW;
2376 else if (element == EL_WALL_EMERALD_RED)
2377 Store[x][y] = EL_EMERALD_RED;
2378 else if (element == EL_WALL_EMERALD_PURPLE)
2379 Store[x][y] = EL_EMERALD_PURPLE;
2380 else if (element == EL_WALL_PEARL)
2381 Store[x][y] = EL_PEARL;
2382 else if (element == EL_WALL_CRYSTAL)
2383 Store[x][y] = EL_CRYSTAL;
2384 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2385 Store[x][y] = element_info[element].content[1][1];
2387 Store[x][y] = EL_EMPTY;
2389 if (x != ex || y != ey ||
2390 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2391 Store2[x][y] = element;
2394 if (AmoebaNr[x][y] &&
2395 (element == EL_AMOEBA_FULL ||
2396 element == EL_BD_AMOEBA ||
2397 element == EL_AMOEBA_GROWING))
2399 AmoebaCnt[AmoebaNr[x][y]]--;
2400 AmoebaCnt2[AmoebaNr[x][y]]--;
2406 MovDir[x][y] = MovPos[x][y] = 0;
2407 GfxDir[x][y] = MovDir[x][y];
2412 Feld[x][y] = EL_EXPLOSION;
2414 GfxElement[x][y] = center_element;
2416 GfxElement[x][y] = EL_UNDEFINED;
2419 ExplodePhase[x][y] = 1;
2423 if (center_element == EL_YAMYAM)
2424 game.yamyam_content_nr =
2425 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2436 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2440 /* activate this even in non-DEBUG version until cause for crash in
2441 getGraphicAnimationFrame() (see below) is found and eliminated */
2445 if (GfxElement[x][y] == EL_UNDEFINED)
2448 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2449 printf("Explode(): This should never happen!\n");
2452 GfxElement[x][y] = EL_EMPTY;
2456 if (phase == first_phase_after_start)
2458 int element = Store2[x][y];
2460 if (element == EL_BLACK_ORB)
2462 Feld[x][y] = Store2[x][y];
2467 else if (phase == half_phase)
2469 int element = Store2[x][y];
2471 if (IS_PLAYER(x, y))
2472 KillHeroUnlessProtected(x, y);
2473 else if (CAN_EXPLODE_BY_FIRE(element))
2475 Feld[x][y] = Store2[x][y];
2479 else if (element == EL_AMOEBA_TO_DIAMOND)
2480 AmoebeUmwandeln(x, y);
2483 if (phase == last_phase)
2487 element = Feld[x][y] = Store[x][y];
2488 Store[x][y] = Store2[x][y] = 0;
2489 GfxElement[x][y] = EL_UNDEFINED;
2491 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2492 element = Feld[x][y] = Back[x][y];
2495 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2496 GfxDir[x][y] = MV_NO_MOVING;
2497 ChangeDelay[x][y] = 0;
2498 ChangePage[x][y] = -1;
2500 InitField(x, y, FALSE);
2501 if (CAN_MOVE(element))
2503 DrawLevelField(x, y);
2505 TestIfElementTouchesCustomElement(x, y);
2507 if (GFX_CRUMBLED(element))
2508 DrawLevelFieldCrumbledSandNeighbours(x, y);
2510 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2511 StorePlayer[x][y] = 0;
2513 if (ELEM_IS_PLAYER(element))
2514 RelocatePlayer(x, y, element);
2516 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2519 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2521 int stored = Store[x][y];
2522 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2523 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2526 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2529 DrawLevelFieldCrumbledSand(x, y);
2531 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2533 DrawLevelElement(x, y, Back[x][y]);
2534 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2536 else if (IS_WALKABLE_UNDER(Back[x][y]))
2538 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2539 DrawLevelElementThruMask(x, y, Back[x][y]);
2541 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2542 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2546 void DynaExplode(int ex, int ey)
2549 int dynabomb_size = 1;
2550 boolean dynabomb_xl = FALSE;
2551 struct PlayerInfo *player;
2552 static int xy[4][2] =
2560 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2562 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2563 dynabomb_size = player->dynabomb_size;
2564 dynabomb_xl = player->dynabomb_xl;
2565 player->dynabombs_left++;
2568 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2570 for (i = 0; i < 4; i++)
2572 for (j = 1; j <= dynabomb_size; j++)
2574 int x = ex + j * xy[i % 4][0];
2575 int y = ey + j * xy[i % 4][1];
2578 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2581 element = Feld[x][y];
2583 /* do not restart explosions of fields with active bombs */
2584 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2587 Explode(x, y, EX_PHASE_START, EX_BORDER);
2589 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2590 if (element != EL_EMPTY &&
2591 element != EL_SAND &&
2592 element != EL_EXPLOSION &&
2599 void Bang(int x, int y)
2602 int element = MovingOrBlocked2Element(x, y);
2604 int element = Feld[x][y];
2608 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2610 if (IS_PLAYER(x, y))
2613 struct PlayerInfo *player = PLAYERINFO(x, y);
2615 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2616 player->element_nr);
2621 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2623 if (game.emulation == EMU_SUPAPLEX)
2624 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2626 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2631 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2639 case EL_BD_BUTTERFLY:
2642 case EL_DARK_YAMYAM:
2646 RaiseScoreElement(element);
2647 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2649 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2650 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2651 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2652 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2653 case EL_DYNABOMB_INCREASE_NUMBER:
2654 case EL_DYNABOMB_INCREASE_SIZE:
2655 case EL_DYNABOMB_INCREASE_POWER:
2660 case EL_LAMP_ACTIVE:
2661 if (IS_PLAYER(x, y))
2662 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2664 Explode(x, y, EX_PHASE_START, EX_CENTER);
2667 if (CAN_EXPLODE_1X1(element))
2668 Explode(x, y, EX_PHASE_START, EX_CENTER);
2670 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2674 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2677 void SplashAcid(int x, int y)
2679 int element = Feld[x][y];
2681 if (element != EL_ACID_SPLASH_LEFT &&
2682 element != EL_ACID_SPLASH_RIGHT)
2684 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2686 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2687 (!IN_LEV_FIELD(x-1, y-1) ||
2688 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2689 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2691 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2692 (!IN_LEV_FIELD(x+1, y-1) ||
2693 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2694 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2698 static void InitBeltMovement()
2700 static int belt_base_element[4] =
2702 EL_CONVEYOR_BELT_1_LEFT,
2703 EL_CONVEYOR_BELT_2_LEFT,
2704 EL_CONVEYOR_BELT_3_LEFT,
2705 EL_CONVEYOR_BELT_4_LEFT
2707 static int belt_base_active_element[4] =
2709 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2710 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2711 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2712 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2717 /* set frame order for belt animation graphic according to belt direction */
2718 for (i = 0; i < 4; i++)
2722 for (j = 0; j < 3; j++)
2724 int element = belt_base_active_element[belt_nr] + j;
2725 int graphic = el2img(element);
2727 if (game.belt_dir[i] == MV_LEFT)
2728 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2730 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2734 for (y = 0; y < lev_fieldy; y++)
2736 for (x = 0; x < lev_fieldx; x++)
2738 int element = Feld[x][y];
2740 for (i = 0; i < 4; i++)
2742 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2744 int e_belt_nr = getBeltNrFromBeltElement(element);
2747 if (e_belt_nr == belt_nr)
2749 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2751 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2759 static void ToggleBeltSwitch(int x, int y)
2761 static int belt_base_element[4] =
2763 EL_CONVEYOR_BELT_1_LEFT,
2764 EL_CONVEYOR_BELT_2_LEFT,
2765 EL_CONVEYOR_BELT_3_LEFT,
2766 EL_CONVEYOR_BELT_4_LEFT
2768 static int belt_base_active_element[4] =
2770 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2771 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2772 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2773 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2775 static int belt_base_switch_element[4] =
2777 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2778 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2779 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2780 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2782 static int belt_move_dir[4] =
2790 int element = Feld[x][y];
2791 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2792 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2793 int belt_dir = belt_move_dir[belt_dir_nr];
2796 if (!IS_BELT_SWITCH(element))
2799 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2800 game.belt_dir[belt_nr] = belt_dir;
2802 if (belt_dir_nr == 3)
2805 /* set frame order for belt animation graphic according to belt direction */
2806 for (i = 0; i < 3; i++)
2808 int element = belt_base_active_element[belt_nr] + i;
2809 int graphic = el2img(element);
2811 if (belt_dir == MV_LEFT)
2812 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2814 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2817 for (yy = 0; yy < lev_fieldy; yy++)
2819 for (xx = 0; xx < lev_fieldx; xx++)
2821 int element = Feld[xx][yy];
2823 if (IS_BELT_SWITCH(element))
2825 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2827 if (e_belt_nr == belt_nr)
2829 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2830 DrawLevelField(xx, yy);
2833 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2835 int e_belt_nr = getBeltNrFromBeltElement(element);
2837 if (e_belt_nr == belt_nr)
2839 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2841 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2842 DrawLevelField(xx, yy);
2845 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2847 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2849 if (e_belt_nr == belt_nr)
2851 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2853 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2854 DrawLevelField(xx, yy);
2861 static void ToggleSwitchgateSwitch(int x, int y)
2865 game.switchgate_pos = !game.switchgate_pos;
2867 for (yy = 0; yy < lev_fieldy; yy++)
2869 for (xx = 0; xx < lev_fieldx; xx++)
2871 int element = Feld[xx][yy];
2873 if (element == EL_SWITCHGATE_SWITCH_UP ||
2874 element == EL_SWITCHGATE_SWITCH_DOWN)
2876 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2877 DrawLevelField(xx, yy);
2879 else if (element == EL_SWITCHGATE_OPEN ||
2880 element == EL_SWITCHGATE_OPENING)
2882 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2884 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
2886 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
2889 else if (element == EL_SWITCHGATE_CLOSED ||
2890 element == EL_SWITCHGATE_CLOSING)
2892 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2894 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
2896 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
2903 static int getInvisibleActiveFromInvisibleElement(int element)
2905 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2906 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2907 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2911 static int getInvisibleFromInvisibleActiveElement(int element)
2913 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2914 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2915 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2919 static void RedrawAllLightSwitchesAndInvisibleElements()
2923 for (y = 0; y < lev_fieldy; y++)
2925 for (x = 0; x < lev_fieldx; x++)
2927 int element = Feld[x][y];
2929 if (element == EL_LIGHT_SWITCH &&
2930 game.light_time_left > 0)
2932 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2933 DrawLevelField(x, y);
2935 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2936 game.light_time_left == 0)
2938 Feld[x][y] = EL_LIGHT_SWITCH;
2939 DrawLevelField(x, y);
2941 else if (element == EL_INVISIBLE_STEELWALL ||
2942 element == EL_INVISIBLE_WALL ||
2943 element == EL_INVISIBLE_SAND)
2945 if (game.light_time_left > 0)
2946 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2948 DrawLevelField(x, y);
2950 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2951 element == EL_INVISIBLE_WALL_ACTIVE ||
2952 element == EL_INVISIBLE_SAND_ACTIVE)
2954 if (game.light_time_left == 0)
2955 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2957 DrawLevelField(x, y);
2963 static void ToggleLightSwitch(int x, int y)
2965 int element = Feld[x][y];
2967 game.light_time_left =
2968 (element == EL_LIGHT_SWITCH ?
2969 level.time_light * FRAMES_PER_SECOND : 0);
2971 RedrawAllLightSwitchesAndInvisibleElements();
2974 static void ActivateTimegateSwitch(int x, int y)
2978 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2980 for (yy = 0; yy < lev_fieldy; yy++)
2982 for (xx = 0; xx < lev_fieldx; xx++)
2984 int element = Feld[xx][yy];
2986 if (element == EL_TIMEGATE_CLOSED ||
2987 element == EL_TIMEGATE_CLOSING)
2989 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2990 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
2994 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2996 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2997 DrawLevelField(xx, yy);
3004 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3007 inline static int getElementMoveStepsize(int x, int y)
3009 int element = Feld[x][y];
3010 int direction = MovDir[x][y];
3011 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3012 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3013 int horiz_move = (dx != 0);
3014 int sign = (horiz_move ? dx : dy);
3015 int step = sign * element_info[element].move_stepsize;
3017 /* special values for move stepsize for spring and things on conveyor belt */
3020 if (CAN_FALL(element) &&
3021 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3022 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3023 else if (element == EL_SPRING)
3024 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3030 void Impact(int x, int y)
3032 boolean lastline = (y == lev_fieldy-1);
3033 boolean object_hit = FALSE;
3034 boolean impact = (lastline || object_hit);
3035 int element = Feld[x][y];
3036 int smashed = EL_UNDEFINED;
3038 if (!lastline) /* check if element below was hit */
3040 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3043 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3044 MovDir[x][y + 1] != MV_DOWN ||
3045 MovPos[x][y + 1] <= TILEY / 2));
3047 /* do not smash moving elements that left the smashed field in time */
3048 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3049 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3053 smashed = MovingOrBlocked2Element(x, y + 1);
3055 impact = (lastline || object_hit);
3058 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3064 /* only reset graphic animation if graphic really changes after impact */
3066 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3068 ResetGfxAnimation(x, y);
3069 DrawLevelField(x, y);
3072 if (impact && CAN_EXPLODE_IMPACT(element))
3077 else if (impact && element == EL_PEARL)
3079 Feld[x][y] = EL_PEARL_BREAKING;
3080 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3083 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3085 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3090 if (impact && element == EL_AMOEBA_DROP)
3092 if (object_hit && IS_PLAYER(x, y + 1))
3093 KillHeroUnlessProtected(x, y + 1);
3094 else if (object_hit && smashed == EL_PENGUIN)
3098 Feld[x][y] = EL_AMOEBA_GROWING;
3099 Store[x][y] = EL_AMOEBA_WET;
3101 ResetRandomAnimationValue(x, y);
3106 if (object_hit) /* check which object was hit */
3108 if (CAN_PASS_MAGIC_WALL(element) &&
3109 (smashed == EL_MAGIC_WALL ||
3110 smashed == EL_BD_MAGIC_WALL))
3113 int activated_magic_wall =
3114 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3115 EL_BD_MAGIC_WALL_ACTIVE);
3117 /* activate magic wall / mill */
3118 for (yy = 0; yy < lev_fieldy; yy++)
3119 for (xx = 0; xx < lev_fieldx; xx++)
3120 if (Feld[xx][yy] == smashed)
3121 Feld[xx][yy] = activated_magic_wall;
3123 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3124 game.magic_wall_active = TRUE;
3126 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3127 SND_MAGIC_WALL_ACTIVATING :
3128 SND_BD_MAGIC_WALL_ACTIVATING));
3131 if (IS_PLAYER(x, y + 1))
3133 if (CAN_SMASH_PLAYER(element))
3135 KillHeroUnlessProtected(x, y + 1);
3139 else if (smashed == EL_PENGUIN)
3141 if (CAN_SMASH_PLAYER(element))
3147 else if (element == EL_BD_DIAMOND)
3149 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3155 else if ((element == EL_SP_INFOTRON ||
3156 element == EL_SP_ZONK) &&
3157 (smashed == EL_SP_SNIKSNAK ||
3158 smashed == EL_SP_ELECTRON ||
3159 smashed == EL_SP_DISK_ORANGE))
3165 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3171 else if (CAN_SMASH_EVERYTHING(element))
3173 if (IS_CLASSIC_ENEMY(smashed) ||
3174 CAN_EXPLODE_SMASHED(smashed))
3179 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3181 if (smashed == EL_LAMP ||
3182 smashed == EL_LAMP_ACTIVE)
3187 else if (smashed == EL_NUT)
3189 Feld[x][y + 1] = EL_NUT_BREAKING;
3190 PlayLevelSound(x, y, SND_NUT_BREAKING);
3191 RaiseScoreElement(EL_NUT);
3194 else if (smashed == EL_PEARL)
3196 Feld[x][y + 1] = EL_PEARL_BREAKING;
3197 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3200 else if (smashed == EL_DIAMOND)
3202 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3203 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3206 else if (IS_BELT_SWITCH(smashed))
3208 ToggleBeltSwitch(x, y + 1);
3210 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3211 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3213 ToggleSwitchgateSwitch(x, y + 1);
3215 else if (smashed == EL_LIGHT_SWITCH ||
3216 smashed == EL_LIGHT_SWITCH_ACTIVE)
3218 ToggleLightSwitch(x, y + 1);
3222 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3224 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3225 CE_OTHER_IS_SWITCHING);
3226 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3232 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3237 /* play sound of magic wall / mill */
3239 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3240 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3242 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3243 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3244 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3245 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3250 /* play sound of object that hits the ground */
3251 if (lastline || object_hit)
3252 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3255 inline static void TurnRoundExt(int x, int y)
3267 { 0, 0 }, { 0, 0 }, { 0, 0 },
3272 int left, right, back;
3276 { MV_DOWN, MV_UP, MV_RIGHT },
3277 { MV_UP, MV_DOWN, MV_LEFT },
3279 { MV_LEFT, MV_RIGHT, MV_DOWN },
3283 { MV_RIGHT, MV_LEFT, MV_UP }
3286 int element = Feld[x][y];
3287 int move_pattern = element_info[element].move_pattern;
3289 int old_move_dir = MovDir[x][y];
3290 int left_dir = turn[old_move_dir].left;
3291 int right_dir = turn[old_move_dir].right;
3292 int back_dir = turn[old_move_dir].back;
3294 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3295 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3296 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3297 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3299 int left_x = x + left_dx, left_y = y + left_dy;
3300 int right_x = x + right_dx, right_y = y + right_dy;
3301 int move_x = x + move_dx, move_y = y + move_dy;
3305 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3307 TestIfBadThingTouchesOtherBadThing(x, y);
3309 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3310 MovDir[x][y] = right_dir;
3311 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3312 MovDir[x][y] = left_dir;
3314 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3316 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3319 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3320 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3322 TestIfBadThingTouchesOtherBadThing(x, y);
3324 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3325 MovDir[x][y] = left_dir;
3326 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3327 MovDir[x][y] = right_dir;
3329 if ((element == EL_SPACESHIP ||
3330 element == EL_SP_SNIKSNAK ||
3331 element == EL_SP_ELECTRON)
3332 && MovDir[x][y] != old_move_dir)
3334 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3337 else if (element == EL_YAMYAM)
3339 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3340 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3342 if (can_turn_left && can_turn_right)
3343 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3344 else if (can_turn_left)
3345 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3346 else if (can_turn_right)
3347 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3349 MovDir[x][y] = back_dir;
3351 MovDelay[x][y] = 16 + 16 * RND(3);
3353 else if (element == EL_DARK_YAMYAM)
3355 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3356 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3358 if (can_turn_left && can_turn_right)
3359 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3360 else if (can_turn_left)
3361 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3362 else if (can_turn_right)
3363 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3365 MovDir[x][y] = back_dir;
3367 MovDelay[x][y] = 16 + 16 * RND(3);
3369 else if (element == EL_PACMAN)
3371 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3372 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3374 if (can_turn_left && can_turn_right)
3375 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3376 else if (can_turn_left)
3377 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3378 else if (can_turn_right)
3379 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3381 MovDir[x][y] = back_dir;
3383 MovDelay[x][y] = 6 + RND(40);
3385 else if (element == EL_PIG)
3387 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3388 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3389 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3390 boolean should_turn_left, should_turn_right, should_move_on;
3392 int rnd = RND(rnd_value);
3394 should_turn_left = (can_turn_left &&
3396 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3397 y + back_dy + left_dy)));
3398 should_turn_right = (can_turn_right &&
3400 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3401 y + back_dy + right_dy)));
3402 should_move_on = (can_move_on &&
3405 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3406 y + move_dy + left_dy) ||
3407 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3408 y + move_dy + right_dy)));
3410 if (should_turn_left || should_turn_right || should_move_on)
3412 if (should_turn_left && should_turn_right && should_move_on)
3413 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3414 rnd < 2 * rnd_value / 3 ? right_dir :
3416 else if (should_turn_left && should_turn_right)
3417 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3418 else if (should_turn_left && should_move_on)
3419 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3420 else if (should_turn_right && should_move_on)
3421 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3422 else if (should_turn_left)
3423 MovDir[x][y] = left_dir;
3424 else if (should_turn_right)
3425 MovDir[x][y] = right_dir;
3426 else if (should_move_on)
3427 MovDir[x][y] = old_move_dir;
3429 else if (can_move_on && rnd > rnd_value / 8)
3430 MovDir[x][y] = old_move_dir;
3431 else if (can_turn_left && can_turn_right)
3432 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3433 else if (can_turn_left && rnd > rnd_value / 8)
3434 MovDir[x][y] = left_dir;
3435 else if (can_turn_right && rnd > rnd_value/8)
3436 MovDir[x][y] = right_dir;
3438 MovDir[x][y] = back_dir;
3440 xx = x + move_xy[MovDir[x][y]].x;
3441 yy = y + move_xy[MovDir[x][y]].y;
3443 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3444 MovDir[x][y] = old_move_dir;
3448 else if (element == EL_DRAGON)
3450 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3451 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3452 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3454 int rnd = RND(rnd_value);
3457 if (FrameCounter < 1 && x == 0 && y == 29)
3458 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3461 if (can_move_on && rnd > rnd_value / 8)
3462 MovDir[x][y] = old_move_dir;
3463 else if (can_turn_left && can_turn_right)
3464 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3465 else if (can_turn_left && rnd > rnd_value / 8)
3466 MovDir[x][y] = left_dir;
3467 else if (can_turn_right && rnd > rnd_value / 8)
3468 MovDir[x][y] = right_dir;
3470 MovDir[x][y] = back_dir;
3472 xx = x + move_xy[MovDir[x][y]].x;
3473 yy = y + move_xy[MovDir[x][y]].y;
3476 if (FrameCounter < 1 && x == 0 && y == 29)
3477 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3478 xx, yy, Feld[xx][yy],
3483 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3484 MovDir[x][y] = old_move_dir;
3486 if (!IS_FREE(xx, yy))
3487 MovDir[x][y] = old_move_dir;
3491 if (FrameCounter < 1 && x == 0 && y == 29)
3492 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3497 else if (element == EL_MOLE)
3499 boolean can_move_on =
3500 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3501 IS_AMOEBOID(Feld[move_x][move_y]) ||
3502 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3505 boolean can_turn_left =
3506 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3507 IS_AMOEBOID(Feld[left_x][left_y])));
3509 boolean can_turn_right =
3510 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3511 IS_AMOEBOID(Feld[right_x][right_y])));
3513 if (can_turn_left && can_turn_right)
3514 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3515 else if (can_turn_left)
3516 MovDir[x][y] = left_dir;
3518 MovDir[x][y] = right_dir;
3521 if (MovDir[x][y] != old_move_dir)
3524 else if (element == EL_BALLOON)
3526 MovDir[x][y] = game.balloon_dir;
3529 else if (element == EL_SPRING)
3531 if (MovDir[x][y] & MV_HORIZONTAL &&
3532 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3533 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3534 MovDir[x][y] = MV_NO_MOVING;
3538 else if (element == EL_ROBOT ||
3539 element == EL_SATELLITE ||
3540 element == EL_PENGUIN)
3542 int attr_x = -1, attr_y = -1;
3553 for (i = 0; i < MAX_PLAYERS; i++)
3555 struct PlayerInfo *player = &stored_player[i];
3556 int jx = player->jx, jy = player->jy;
3558 if (!player->active)
3562 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3570 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3576 if (element == EL_PENGUIN)
3579 static int xy[4][2] =
3587 for (i = 0; i < 4; i++)
3589 int ex = x + xy[i % 4][0];
3590 int ey = y + xy[i % 4][1];
3592 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3601 MovDir[x][y] = MV_NO_MOVING;
3603 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3604 else if (attr_x > x)
3605 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3607 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3608 else if (attr_y > y)
3609 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3611 if (element == EL_ROBOT)
3615 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3616 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3617 Moving2Blocked(x, y, &newx, &newy);
3619 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3620 MovDelay[x][y] = 8 + 8 * !RND(3);
3622 MovDelay[x][y] = 16;
3624 else if (element == EL_PENGUIN)
3630 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3632 boolean first_horiz = RND(2);
3633 int new_move_dir = MovDir[x][y];
3636 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3637 Moving2Blocked(x, y, &newx, &newy);
3639 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3643 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3644 Moving2Blocked(x, y, &newx, &newy);
3646 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3649 MovDir[x][y] = old_move_dir;
3653 else /* (element == EL_SATELLITE) */
3659 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3661 boolean first_horiz = RND(2);
3662 int new_move_dir = MovDir[x][y];
3665 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3666 Moving2Blocked(x, y, &newx, &newy);
3668 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3672 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3673 Moving2Blocked(x, y, &newx, &newy);
3675 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3678 MovDir[x][y] = old_move_dir;
3683 else if (move_pattern == MV_ALL_DIRECTIONS ||
3684 move_pattern == MV_TURNING_LEFT ||
3685 move_pattern == MV_TURNING_RIGHT)
3687 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3688 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3690 if (move_pattern == MV_TURNING_LEFT)
3691 MovDir[x][y] = left_dir;
3692 else if (move_pattern == MV_TURNING_RIGHT)
3693 MovDir[x][y] = right_dir;
3694 else if (can_turn_left && can_turn_right)
3695 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3696 else if (can_turn_left)
3697 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3698 else if (can_turn_right)
3699 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3701 MovDir[x][y] = back_dir;
3703 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3705 else if (move_pattern == MV_HORIZONTAL ||
3706 move_pattern == MV_VERTICAL)
3708 if (move_pattern & old_move_dir)
3709 MovDir[x][y] = back_dir;
3710 else if (move_pattern == MV_HORIZONTAL)
3711 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3712 else if (move_pattern == MV_VERTICAL)
3713 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3715 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3717 else if (move_pattern & MV_ANY_DIRECTION)
3719 MovDir[x][y] = move_pattern;
3720 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3722 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3724 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3725 MovDir[x][y] = left_dir;
3726 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3727 MovDir[x][y] = right_dir;
3729 if (MovDir[x][y] != old_move_dir)
3730 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3732 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3734 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3735 MovDir[x][y] = right_dir;
3736 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3737 MovDir[x][y] = left_dir;
3739 if (MovDir[x][y] != old_move_dir)
3740 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3742 else if (move_pattern == MV_TOWARDS_PLAYER ||
3743 move_pattern == MV_AWAY_FROM_PLAYER)
3745 int attr_x = -1, attr_y = -1;
3747 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3758 for (i = 0; i < MAX_PLAYERS; i++)
3760 struct PlayerInfo *player = &stored_player[i];
3761 int jx = player->jx, jy = player->jy;
3763 if (!player->active)
3767 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3775 MovDir[x][y] = MV_NO_MOVING;
3777 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3778 else if (attr_x > x)
3779 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3781 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3782 else if (attr_y > y)
3783 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3785 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3787 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3789 boolean first_horiz = RND(2);
3790 int new_move_dir = MovDir[x][y];
3793 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3794 Moving2Blocked(x, y, &newx, &newy);
3796 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3800 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3801 Moving2Blocked(x, y, &newx, &newy);
3803 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3806 MovDir[x][y] = old_move_dir;
3809 else if (move_pattern == MV_WHEN_PUSHED)
3811 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3812 MovDir[x][y] = MV_NO_MOVING;
3816 else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
3817 element == EL_MAZE_RUNNER)
3819 static int test_xy[7][2] =
3829 static int test_dir[7] =
3839 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3840 int move_preference = -1000000; /* start with very low preference */
3841 int new_move_dir = MV_NO_MOVING;
3842 int start_test = RND(4);
3845 for (i = 0; i < 4; i++)
3847 int move_dir = test_dir[start_test + i];
3848 int move_dir_preference;
3850 xx = x + test_xy[start_test + i][0];
3851 yy = y + test_xy[start_test + i][1];
3853 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3854 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3856 new_move_dir = move_dir;
3861 if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
3864 move_dir_preference = -1 * RunnerVisit[xx][yy];
3865 if (hunter_mode && PlayerVisit[xx][yy] > 0)
3866 move_dir_preference = PlayerVisit[xx][yy];
3868 if (move_dir_preference > move_preference)
3870 /* prefer field that has not been visited for the longest time */
3871 move_preference = move_dir_preference;
3872 new_move_dir = move_dir;
3874 else if (move_dir_preference == move_preference &&
3875 move_dir == old_move_dir)
3877 /* prefer last direction when all directions are preferred equally */
3878 move_preference = move_dir_preference;
3879 new_move_dir = move_dir;
3883 MovDir[x][y] = new_move_dir;
3884 if (old_move_dir != new_move_dir)
3889 static void TurnRound(int x, int y)
3891 int direction = MovDir[x][y];
3894 GfxDir[x][y] = MovDir[x][y];
3900 GfxDir[x][y] = MovDir[x][y];
3903 if (direction != MovDir[x][y])
3908 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
3911 GfxAction[x][y] = ACTION_WAITING;
3915 static boolean JustBeingPushed(int x, int y)
3919 for (i = 0; i < MAX_PLAYERS; i++)
3921 struct PlayerInfo *player = &stored_player[i];
3923 if (player->active && player->is_pushing && player->MovPos)
3925 int next_jx = player->jx + (player->jx - player->last_jx);
3926 int next_jy = player->jy + (player->jy - player->last_jy);
3928 if (x == next_jx && y == next_jy)
3936 void StartMoving(int x, int y)
3938 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3939 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3940 int element = Feld[x][y];
3946 if (MovDelay[x][y] == 0)
3947 GfxAction[x][y] = ACTION_DEFAULT;
3949 /* !!! this should be handled more generic (not only for mole) !!! */
3950 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3951 GfxAction[x][y] = ACTION_DEFAULT;
3954 if (CAN_FALL(element) && y < lev_fieldy - 1)
3956 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3957 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3958 if (JustBeingPushed(x, y))
3961 if (element == EL_QUICKSAND_FULL)
3963 if (IS_FREE(x, y + 1))
3965 InitMovingField(x, y, MV_DOWN);
3966 started_moving = TRUE;
3968 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3969 Store[x][y] = EL_ROCK;
3971 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
3973 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
3976 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3978 if (!MovDelay[x][y])
3979 MovDelay[x][y] = TILEY + 1;
3988 Feld[x][y] = EL_QUICKSAND_EMPTY;
3989 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3990 Store[x][y + 1] = Store[x][y];
3993 PlayLevelSoundAction(x, y, ACTION_FILLING);
3995 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
3999 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4000 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4002 InitMovingField(x, y, MV_DOWN);
4003 started_moving = TRUE;
4005 Feld[x][y] = EL_QUICKSAND_FILLING;
4006 Store[x][y] = element;
4008 PlayLevelSoundAction(x, y, ACTION_FILLING);
4010 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4013 else if (element == EL_MAGIC_WALL_FULL)
4015 if (IS_FREE(x, y + 1))
4017 InitMovingField(x, y, MV_DOWN);
4018 started_moving = TRUE;
4020 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4021 Store[x][y] = EL_CHANGED(Store[x][y]);
4023 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4025 if (!MovDelay[x][y])
4026 MovDelay[x][y] = TILEY/4 + 1;
4035 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4036 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4037 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4041 else if (element == EL_BD_MAGIC_WALL_FULL)
4043 if (IS_FREE(x, y + 1))
4045 InitMovingField(x, y, MV_DOWN);
4046 started_moving = TRUE;
4048 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4049 Store[x][y] = EL_CHANGED2(Store[x][y]);
4051 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4053 if (!MovDelay[x][y])
4054 MovDelay[x][y] = TILEY/4 + 1;
4063 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4064 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4065 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4069 else if (CAN_PASS_MAGIC_WALL(element) &&
4070 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4071 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4073 InitMovingField(x, y, MV_DOWN);
4074 started_moving = TRUE;
4077 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4078 EL_BD_MAGIC_WALL_FILLING);
4079 Store[x][y] = element;
4082 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4084 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4089 InitMovingField(x, y, MV_DOWN);
4090 started_moving = TRUE;
4092 Store[x][y] = EL_ACID;
4094 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4095 GfxAction[x][y + 1] = ACTION_ACTIVE;
4099 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4100 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4101 (Feld[x][y + 1] == EL_BLOCKED)) ||
4102 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4103 CAN_SMASH(element) && WasJustFalling[x][y] &&
4104 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4108 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4109 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4110 WasJustMoving[x][y] && !Pushed[x][y + 1])
4112 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4113 WasJustMoving[x][y])
4118 /* this is needed for a special case not covered by calling "Impact()"
4119 from "ContinueMoving()": if an element moves to a tile directly below
4120 another element which was just falling on that tile (which was empty
4121 in the previous frame), the falling element above would just stop
4122 instead of smashing the element below (in previous version, the above
4123 element was just checked for "moving" instead of "falling", resulting
4124 in incorrect smashes caused by horizontal movement of the above
4125 element; also, the case of the player being the element to smash was
4126 simply not covered here... :-/ ) */
4130 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4132 if (MovDir[x][y] == MV_NO_MOVING)
4134 InitMovingField(x, y, MV_DOWN);
4135 started_moving = TRUE;
4138 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4140 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4141 MovDir[x][y] = MV_DOWN;
4143 InitMovingField(x, y, MV_DOWN);
4144 started_moving = TRUE;
4146 else if (element == EL_AMOEBA_DROP)
4148 Feld[x][y] = EL_AMOEBA_GROWING;
4149 Store[x][y] = EL_AMOEBA_WET;
4151 /* Store[x][y + 1] must be zero, because:
4152 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4155 #if OLD_GAME_BEHAVIOUR
4156 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4158 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4159 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4160 element != EL_DX_SUPABOMB)
4163 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4164 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4165 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4166 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4169 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4170 (IS_FREE(x - 1, y + 1) ||
4171 Feld[x - 1][y + 1] == EL_ACID));
4172 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4173 (IS_FREE(x + 1, y + 1) ||
4174 Feld[x + 1][y + 1] == EL_ACID));
4175 boolean can_fall_any = (can_fall_left || can_fall_right);
4176 boolean can_fall_both = (can_fall_left && can_fall_right);
4178 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4180 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4182 if (slippery_type == SLIPPERY_ONLY_LEFT)
4183 can_fall_right = FALSE;
4184 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4185 can_fall_left = FALSE;
4186 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4187 can_fall_right = FALSE;
4188 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4189 can_fall_left = FALSE;
4191 can_fall_any = (can_fall_left || can_fall_right);
4192 can_fall_both = (can_fall_left && can_fall_right);
4197 if (can_fall_both &&
4198 (game.emulation != EMU_BOULDERDASH &&
4199 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4200 can_fall_left = !(can_fall_right = RND(2));
4202 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4203 started_moving = TRUE;
4206 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4208 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4209 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4210 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4211 int belt_dir = game.belt_dir[belt_nr];
4213 if ((belt_dir == MV_LEFT && left_is_free) ||
4214 (belt_dir == MV_RIGHT && right_is_free))
4216 InitMovingField(x, y, belt_dir);
4217 started_moving = TRUE;
4219 GfxAction[x][y] = ACTION_DEFAULT;
4224 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4225 if (CAN_MOVE(element) && !started_moving)
4227 int move_pattern = element_info[element].move_pattern;
4231 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4234 if ((element == EL_SATELLITE ||
4235 element == EL_BALLOON ||
4236 element == EL_SPRING)
4237 && JustBeingPushed(x, y))
4243 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4244 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4246 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4248 Moving2Blocked(x, y, &newx, &newy);
4249 if (Feld[newx][newy] == EL_BLOCKED)
4250 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4256 if (FrameCounter < 1 && x == 0 && y == 29)
4257 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4260 if (!MovDelay[x][y]) /* start new movement phase */
4262 /* all objects that can change their move direction after each step
4263 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4265 if (element != EL_YAMYAM &&
4266 element != EL_DARK_YAMYAM &&
4267 element != EL_PACMAN &&
4268 !(move_pattern & MV_ANY_DIRECTION) &&
4269 move_pattern != MV_TURNING_LEFT &&
4270 move_pattern != MV_TURNING_RIGHT)
4275 if (FrameCounter < 1 && x == 0 && y == 29)
4276 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4279 if (MovDelay[x][y] && (element == EL_BUG ||
4280 element == EL_SPACESHIP ||
4281 element == EL_SP_SNIKSNAK ||
4282 element == EL_SP_ELECTRON ||
4283 element == EL_MOLE))
4284 DrawLevelField(x, y);
4288 if (MovDelay[x][y]) /* wait some time before next movement */
4293 if (element == EL_YAMYAM)
4296 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4297 DrawLevelElementAnimation(x, y, element);
4301 if (MovDelay[x][y]) /* element still has to wait some time */
4304 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4305 ResetGfxAnimation(x, y);
4309 if (GfxAction[x][y] != ACTION_WAITING)
4310 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4312 GfxAction[x][y] = ACTION_WAITING;
4316 if (element == EL_ROBOT ||
4318 element == EL_PACMAN ||
4320 element == EL_YAMYAM ||
4321 element == EL_DARK_YAMYAM)
4324 DrawLevelElementAnimation(x, y, element);
4326 DrawLevelElementAnimationIfNeeded(x, y, element);
4328 PlayLevelSoundAction(x, y, ACTION_WAITING);
4330 else if (element == EL_SP_ELECTRON)
4331 DrawLevelElementAnimationIfNeeded(x, y, element);
4332 else if (element == EL_DRAGON)
4335 int dir = MovDir[x][y];
4336 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4337 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4338 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4339 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4340 dir == MV_UP ? IMG_FLAMES_1_UP :
4341 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4342 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4345 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4348 GfxAction[x][y] = ACTION_ATTACKING;
4350 if (IS_PLAYER(x, y))
4351 DrawPlayerField(x, y);
4353 DrawLevelField(x, y);
4355 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4357 for (i = 1; i <= 3; i++)
4359 int xx = x + i * dx;
4360 int yy = y + i * dy;
4361 int sx = SCREENX(xx);
4362 int sy = SCREENY(yy);
4363 int flame_graphic = graphic + (i - 1);
4365 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4370 int flamed = MovingOrBlocked2Element(xx, yy);
4372 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4375 RemoveMovingField(xx, yy);
4377 Feld[xx][yy] = EL_FLAMES;
4378 if (IN_SCR_FIELD(sx, sy))
4380 DrawLevelFieldCrumbledSand(xx, yy);
4381 DrawGraphic(sx, sy, flame_graphic, frame);
4386 if (Feld[xx][yy] == EL_FLAMES)
4387 Feld[xx][yy] = EL_EMPTY;
4388 DrawLevelField(xx, yy);
4393 if (MovDelay[x][y]) /* element still has to wait some time */
4395 PlayLevelSoundAction(x, y, ACTION_WAITING);
4401 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4402 for all other elements GfxAction will be set by InitMovingField() */
4403 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4404 GfxAction[x][y] = ACTION_MOVING;
4408 /* now make next step */
4410 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4412 if (DONT_COLLIDE_WITH(element) &&
4413 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4414 !PLAYER_PROTECTED(newx, newy))
4417 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4420 /* player killed by element which is deadly when colliding with */
4422 KillHero(PLAYERINFO(newx, newy));
4427 else if ((element == EL_PENGUIN ||
4428 element == EL_ROBOT ||
4429 element == EL_SATELLITE ||
4430 element == EL_BALLOON ||
4431 IS_CUSTOM_ELEMENT(element)) &&
4432 IN_LEV_FIELD(newx, newy) &&
4433 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4436 Store[x][y] = EL_ACID;
4438 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4440 if (Feld[newx][newy] == EL_EXIT_OPEN)
4442 Feld[x][y] = EL_EMPTY;
4443 DrawLevelField(x, y);
4445 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4446 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4447 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4449 local_player->friends_still_needed--;
4450 if (!local_player->friends_still_needed &&
4451 !local_player->GameOver && AllPlayersGone)
4452 local_player->LevelSolved = local_player->GameOver = TRUE;
4456 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4458 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4459 DrawLevelField(newx, newy);
4461 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4463 else if (!IS_FREE(newx, newy))
4465 GfxAction[x][y] = ACTION_WAITING;
4467 if (IS_PLAYER(x, y))
4468 DrawPlayerField(x, y);
4470 DrawLevelField(x, y);
4474 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4476 if (IS_FOOD_PIG(Feld[newx][newy]))
4478 if (IS_MOVING(newx, newy))
4479 RemoveMovingField(newx, newy);
4482 Feld[newx][newy] = EL_EMPTY;
4483 DrawLevelField(newx, newy);
4486 PlayLevelSound(x, y, SND_PIG_DIGGING);
4488 else if (!IS_FREE(newx, newy))
4490 if (IS_PLAYER(x, y))
4491 DrawPlayerField(x, y);
4493 DrawLevelField(x, y);
4497 else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4498 element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4500 if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4502 if (IS_MOVING(newx, newy))
4503 RemoveMovingField(newx, newy);
4506 Feld[newx][newy] = EL_EMPTY;
4507 DrawLevelField(newx, newy);
4510 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4512 else if (!IS_FREE(newx, newy))
4515 if (IS_PLAYER(x, y))
4516 DrawPlayerField(x, y);
4518 DrawLevelField(x, y);
4523 RunnerVisit[x][y] = FrameCounter;
4524 PlayerVisit[x][y] /= 8; /* expire player visit path */
4526 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4528 if (!IS_FREE(newx, newy))
4530 if (IS_PLAYER(x, y))
4531 DrawPlayerField(x, y);
4533 DrawLevelField(x, y);
4539 boolean wanna_flame = !RND(10);
4540 int dx = newx - x, dy = newy - y;
4541 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4542 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4543 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4544 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4545 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4546 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4549 IS_CLASSIC_ENEMY(element1) ||
4550 IS_CLASSIC_ENEMY(element2)) &&
4551 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4552 element1 != EL_FLAMES && element2 != EL_FLAMES)
4555 ResetGfxAnimation(x, y);
4556 GfxAction[x][y] = ACTION_ATTACKING;
4559 if (IS_PLAYER(x, y))
4560 DrawPlayerField(x, y);
4562 DrawLevelField(x, y);
4564 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4566 MovDelay[x][y] = 50;
4568 Feld[newx][newy] = EL_FLAMES;
4569 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4570 Feld[newx1][newy1] = EL_FLAMES;
4571 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4572 Feld[newx2][newy2] = EL_FLAMES;
4578 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4579 Feld[newx][newy] == EL_DIAMOND)
4581 if (IS_MOVING(newx, newy))
4582 RemoveMovingField(newx, newy);
4585 Feld[newx][newy] = EL_EMPTY;
4586 DrawLevelField(newx, newy);
4589 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4591 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4592 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4594 if (AmoebaNr[newx][newy])
4596 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4597 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4598 Feld[newx][newy] == EL_BD_AMOEBA)
4599 AmoebaCnt[AmoebaNr[newx][newy]]--;
4602 if (IS_MOVING(newx, newy))
4603 RemoveMovingField(newx, newy);
4606 Feld[newx][newy] = EL_EMPTY;
4607 DrawLevelField(newx, newy);
4610 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4612 else if ((element == EL_PACMAN || element == EL_MOLE)
4613 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4615 if (AmoebaNr[newx][newy])
4617 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4618 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4619 Feld[newx][newy] == EL_BD_AMOEBA)
4620 AmoebaCnt[AmoebaNr[newx][newy]]--;
4623 if (element == EL_MOLE)
4625 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4626 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4628 ResetGfxAnimation(x, y);
4629 GfxAction[x][y] = ACTION_DIGGING;
4630 DrawLevelField(x, y);
4632 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4633 return; /* wait for shrinking amoeba */
4635 else /* element == EL_PACMAN */
4637 Feld[newx][newy] = EL_EMPTY;
4638 DrawLevelField(newx, newy);
4639 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4642 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4643 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4644 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4646 /* wait for shrinking amoeba to completely disappear */
4649 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4651 /* object was running against a wall */
4656 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4657 DrawLevelElementAnimation(x, y, element);
4659 if (element == EL_BUG ||
4660 element == EL_SPACESHIP ||
4661 element == EL_SP_SNIKSNAK)
4662 DrawLevelField(x, y);
4663 else if (element == EL_MOLE)
4664 DrawLevelField(x, y);
4665 else if (element == EL_BD_BUTTERFLY ||
4666 element == EL_BD_FIREFLY)
4667 DrawLevelElementAnimationIfNeeded(x, y, element);
4668 else if (element == EL_SATELLITE)
4669 DrawLevelElementAnimationIfNeeded(x, y, element);
4670 else if (element == EL_SP_ELECTRON)
4671 DrawLevelElementAnimationIfNeeded(x, y, element);
4674 if (DONT_TOUCH(element))
4675 TestIfBadThingTouchesHero(x, y);
4678 PlayLevelSoundAction(x, y, ACTION_WAITING);
4684 InitMovingField(x, y, MovDir[x][y]);
4686 PlayLevelSoundAction(x, y, ACTION_MOVING);
4690 ContinueMoving(x, y);
4693 void ContinueMoving(int x, int y)
4695 int element = Feld[x][y];
4696 int direction = MovDir[x][y];
4697 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4698 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4699 int newx = x + dx, newy = y + dy;
4700 int nextx = newx + dx, nexty = newy + dy;
4701 boolean pushed = Pushed[x][y];
4703 MovPos[x][y] += getElementMoveStepsize(x, y);
4705 if (pushed) /* special case: moving object pushed by player */
4706 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4708 if (ABS(MovPos[x][y]) < TILEX)
4710 DrawLevelField(x, y);
4712 return; /* element is still moving */
4715 /* element reached destination field */
4717 Feld[x][y] = EL_EMPTY;
4718 Feld[newx][newy] = element;
4719 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4721 if (element == EL_MOLE)
4723 Feld[x][y] = EL_SAND;
4725 DrawLevelFieldCrumbledSandNeighbours(x, y);
4727 else if (element == EL_QUICKSAND_FILLING)
4729 element = Feld[newx][newy] = get_next_element(element);
4730 Store[newx][newy] = Store[x][y];
4732 else if (element == EL_QUICKSAND_EMPTYING)
4734 Feld[x][y] = get_next_element(element);
4735 element = Feld[newx][newy] = Store[x][y];
4737 else if (element == EL_MAGIC_WALL_FILLING)
4739 element = Feld[newx][newy] = get_next_element(element);
4740 if (!game.magic_wall_active)
4741 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4742 Store[newx][newy] = Store[x][y];
4744 else if (element == EL_MAGIC_WALL_EMPTYING)
4746 Feld[x][y] = get_next_element(element);
4747 if (!game.magic_wall_active)
4748 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4749 element = Feld[newx][newy] = Store[x][y];
4751 else if (element == EL_BD_MAGIC_WALL_FILLING)
4753 element = Feld[newx][newy] = get_next_element(element);
4754 if (!game.magic_wall_active)
4755 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4756 Store[newx][newy] = Store[x][y];
4758 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4760 Feld[x][y] = get_next_element(element);
4761 if (!game.magic_wall_active)
4762 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4763 element = Feld[newx][newy] = Store[x][y];
4765 else if (element == EL_AMOEBA_DROPPING)
4767 Feld[x][y] = get_next_element(element);
4768 element = Feld[newx][newy] = Store[x][y];
4770 else if (element == EL_SOKOBAN_OBJECT)
4773 Feld[x][y] = Back[x][y];
4775 if (Back[newx][newy])
4776 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4778 Back[x][y] = Back[newx][newy] = 0;
4780 else if (Store[x][y] == EL_ACID)
4782 element = Feld[newx][newy] = EL_ACID;
4786 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4787 MovDelay[newx][newy] = 0;
4789 /* copy element change control values to new field */
4790 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4791 ChangePage[newx][newy] = ChangePage[x][y];
4792 Changed[newx][newy] = Changed[x][y];
4793 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4795 ChangeDelay[x][y] = 0;
4796 ChangePage[x][y] = -1;
4797 Changed[x][y] = CE_BITMASK_DEFAULT;
4798 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4800 /* copy animation control values to new field */
4801 GfxFrame[newx][newy] = GfxFrame[x][y];
4802 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4803 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4804 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
4806 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4808 ResetGfxAnimation(x, y); /* reset animation values for old field */
4811 /* 2.1.1 (does not work correctly for spring) */
4812 if (!CAN_MOVE(element))
4813 MovDir[newx][newy] = 0;
4817 /* (does not work for falling objects that slide horizontally) */
4818 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4819 MovDir[newx][newy] = 0;
4822 if (!CAN_MOVE(element) ||
4823 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4824 MovDir[newx][newy] = 0;
4827 if (!CAN_MOVE(element) ||
4828 (CAN_FALL(element) && direction == MV_DOWN))
4829 GfxDir[x][y] = MovDir[newx][newy] = 0;
4834 DrawLevelField(x, y);
4835 DrawLevelField(newx, newy);
4837 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4839 /* prevent pushed element from moving on in pushed direction */
4840 if (pushed && CAN_MOVE(element) &&
4841 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4842 !(element_info[element].move_pattern & direction))
4843 TurnRound(newx, newy);
4845 if (!pushed) /* special case: moving object pushed by player */
4847 WasJustMoving[newx][newy] = 3;
4849 if (CAN_FALL(element) && direction == MV_DOWN)
4850 WasJustFalling[newx][newy] = 3;
4853 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4855 TestIfBadThingTouchesHero(newx, newy);
4856 TestIfBadThingTouchesFriend(newx, newy);
4858 if (!IS_CUSTOM_ELEMENT(element))
4859 TestIfBadThingTouchesOtherBadThing(newx, newy);
4861 else if (element == EL_PENGUIN)
4862 TestIfFriendTouchesBadThing(newx, newy);
4864 if (CAN_FALL(element) && direction == MV_DOWN &&
4865 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4869 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4873 if (ChangePage[newx][newy] != -1) /* delayed change */
4874 ChangeElement(newx, newy, ChangePage[newx][newy]);
4877 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4879 /* !!! fix side (direction) orientation here and elsewhere !!! */
4880 CheckElementSideChange(newx, newy, Feld[newx][newy],
4881 direction, CE_COLLISION_ACTIVE, -1);
4884 if (IN_LEV_FIELD(nextx, nexty))
4886 static int opposite_directions[] =
4893 int move_dir_bit = MV_DIR_BIT(direction);
4894 int opposite_direction = opposite_directions[move_dir_bit];
4895 int hitting_side = direction;
4896 int touched_side = opposite_direction;
4897 int hitting_element = Feld[newx][newy];
4898 int touched_element = MovingOrBlocked2Element(nextx, nexty);
4899 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4900 MovDir[nextx][nexty] != direction ||
4901 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4907 CheckElementSideChange(nextx, nexty, Feld[nextx][nexty],
4908 opposite_direction, CE_COLLISION_PASSIVE, -1);
4910 if (IS_CUSTOM_ELEMENT(hitting_element) &&
4911 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4913 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4915 struct ElementChangeInfo *change =
4916 &element_info[hitting_element].change_page[i];
4918 if (change->can_change &&
4919 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4920 change->sides & touched_side &&
4921 change->trigger_element == touched_element)
4923 CheckElementSideChange(newx, newy, hitting_element,
4924 CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4930 if (IS_CUSTOM_ELEMENT(touched_element) &&
4931 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4933 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4935 struct ElementChangeInfo *change =
4936 &element_info[touched_element].change_page[i];
4938 if (change->can_change &&
4939 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4940 change->sides & hitting_side &&
4941 change->trigger_element == hitting_element)
4943 CheckElementSideChange(nextx, nexty, touched_element,
4944 CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4954 TestIfPlayerTouchesCustomElement(newx, newy);
4955 TestIfElementTouchesCustomElement(newx, newy);
4958 int AmoebeNachbarNr(int ax, int ay)
4961 int element = Feld[ax][ay];
4963 static int xy[4][2] =
4971 for (i = 0; i < 4; i++)
4973 int x = ax + xy[i][0];
4974 int y = ay + xy[i][1];
4976 if (!IN_LEV_FIELD(x, y))
4979 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4980 group_nr = AmoebaNr[x][y];
4986 void AmoebenVereinigen(int ax, int ay)
4988 int i, x, y, xx, yy;
4989 int new_group_nr = AmoebaNr[ax][ay];
4990 static int xy[4][2] =
4998 if (new_group_nr == 0)
5001 for (i = 0; i < 4; i++)
5006 if (!IN_LEV_FIELD(x, y))
5009 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5010 Feld[x][y] == EL_BD_AMOEBA ||
5011 Feld[x][y] == EL_AMOEBA_DEAD) &&
5012 AmoebaNr[x][y] != new_group_nr)
5014 int old_group_nr = AmoebaNr[x][y];
5016 if (old_group_nr == 0)
5019 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5020 AmoebaCnt[old_group_nr] = 0;
5021 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5022 AmoebaCnt2[old_group_nr] = 0;
5024 for (yy = 0; yy < lev_fieldy; yy++)
5026 for (xx = 0; xx < lev_fieldx; xx++)
5028 if (AmoebaNr[xx][yy] == old_group_nr)
5029 AmoebaNr[xx][yy] = new_group_nr;
5036 void AmoebeUmwandeln(int ax, int ay)
5040 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5042 int group_nr = AmoebaNr[ax][ay];
5047 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5048 printf("AmoebeUmwandeln(): This should never happen!\n");
5053 for (y = 0; y < lev_fieldy; y++)
5055 for (x = 0; x < lev_fieldx; x++)
5057 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5060 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5064 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5065 SND_AMOEBA_TURNING_TO_GEM :
5066 SND_AMOEBA_TURNING_TO_ROCK));
5071 static int xy[4][2] =
5079 for (i = 0; i < 4; i++)
5084 if (!IN_LEV_FIELD(x, y))
5087 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5089 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5090 SND_AMOEBA_TURNING_TO_GEM :
5091 SND_AMOEBA_TURNING_TO_ROCK));
5098 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5101 int group_nr = AmoebaNr[ax][ay];
5102 boolean done = FALSE;
5107 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5108 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5113 for (y = 0; y < lev_fieldy; y++)
5115 for (x = 0; x < lev_fieldx; x++)
5117 if (AmoebaNr[x][y] == group_nr &&
5118 (Feld[x][y] == EL_AMOEBA_DEAD ||
5119 Feld[x][y] == EL_BD_AMOEBA ||
5120 Feld[x][y] == EL_AMOEBA_GROWING))
5123 Feld[x][y] = new_element;
5124 InitField(x, y, FALSE);
5125 DrawLevelField(x, y);
5132 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5133 SND_BD_AMOEBA_TURNING_TO_ROCK :
5134 SND_BD_AMOEBA_TURNING_TO_GEM));
5137 void AmoebeWaechst(int x, int y)
5139 static unsigned long sound_delay = 0;
5140 static unsigned long sound_delay_value = 0;
5142 if (!MovDelay[x][y]) /* start new growing cycle */
5146 if (DelayReached(&sound_delay, sound_delay_value))
5149 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5151 if (Store[x][y] == EL_BD_AMOEBA)
5152 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5154 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5156 sound_delay_value = 30;
5160 if (MovDelay[x][y]) /* wait some time before growing bigger */
5163 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5165 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5166 6 - MovDelay[x][y]);
5168 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5171 if (!MovDelay[x][y])
5173 Feld[x][y] = Store[x][y];
5175 DrawLevelField(x, y);
5180 void AmoebaDisappearing(int x, int y)
5182 static unsigned long sound_delay = 0;
5183 static unsigned long sound_delay_value = 0;
5185 if (!MovDelay[x][y]) /* start new shrinking cycle */
5189 if (DelayReached(&sound_delay, sound_delay_value))
5190 sound_delay_value = 30;
5193 if (MovDelay[x][y]) /* wait some time before shrinking */
5196 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5198 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5199 6 - MovDelay[x][y]);
5201 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5204 if (!MovDelay[x][y])
5206 Feld[x][y] = EL_EMPTY;
5207 DrawLevelField(x, y);
5209 /* don't let mole enter this field in this cycle;
5210 (give priority to objects falling to this field from above) */
5216 void AmoebeAbleger(int ax, int ay)
5219 int element = Feld[ax][ay];
5220 int graphic = el2img(element);
5221 int newax = ax, neway = ay;
5222 static int xy[4][2] =
5230 if (!level.amoeba_speed)
5232 Feld[ax][ay] = EL_AMOEBA_DEAD;
5233 DrawLevelField(ax, ay);
5237 if (IS_ANIMATED(graphic))
5238 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5240 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5241 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5243 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5246 if (MovDelay[ax][ay])
5250 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5253 int x = ax + xy[start][0];
5254 int y = ay + xy[start][1];
5256 if (!IN_LEV_FIELD(x, y))
5259 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5260 if (IS_FREE(x, y) ||
5261 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5267 if (newax == ax && neway == ay)
5270 else /* normal or "filled" (BD style) amoeba */
5273 boolean waiting_for_player = FALSE;
5275 for (i = 0; i < 4; i++)
5277 int j = (start + i) % 4;
5278 int x = ax + xy[j][0];
5279 int y = ay + xy[j][1];
5281 if (!IN_LEV_FIELD(x, y))
5284 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5285 if (IS_FREE(x, y) ||
5286 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5292 else if (IS_PLAYER(x, y))
5293 waiting_for_player = TRUE;
5296 if (newax == ax && neway == ay) /* amoeba cannot grow */
5298 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5300 Feld[ax][ay] = EL_AMOEBA_DEAD;
5301 DrawLevelField(ax, ay);
5302 AmoebaCnt[AmoebaNr[ax][ay]]--;
5304 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5306 if (element == EL_AMOEBA_FULL)
5307 AmoebeUmwandeln(ax, ay);
5308 else if (element == EL_BD_AMOEBA)
5309 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5314 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5316 /* amoeba gets larger by growing in some direction */
5318 int new_group_nr = AmoebaNr[ax][ay];
5321 if (new_group_nr == 0)
5323 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5324 printf("AmoebeAbleger(): This should never happen!\n");
5329 AmoebaNr[newax][neway] = new_group_nr;
5330 AmoebaCnt[new_group_nr]++;
5331 AmoebaCnt2[new_group_nr]++;
5333 /* if amoeba touches other amoeba(s) after growing, unify them */
5334 AmoebenVereinigen(newax, neway);
5336 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5338 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5344 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5345 (neway == lev_fieldy - 1 && newax != ax))
5347 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5348 Store[newax][neway] = element;
5350 else if (neway == ay)
5352 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5354 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5356 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5361 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5362 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5363 Store[ax][ay] = EL_AMOEBA_DROP;
5364 ContinueMoving(ax, ay);
5368 DrawLevelField(newax, neway);
5371 void Life(int ax, int ay)
5374 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5376 int element = Feld[ax][ay];
5377 int graphic = el2img(element);
5378 boolean changed = FALSE;
5380 if (IS_ANIMATED(graphic))
5381 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5386 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5387 MovDelay[ax][ay] = life_time;
5389 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5392 if (MovDelay[ax][ay])
5396 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5398 int xx = ax+x1, yy = ay+y1;
5401 if (!IN_LEV_FIELD(xx, yy))
5404 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5406 int x = xx+x2, y = yy+y2;
5408 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5411 if (((Feld[x][y] == element ||
5412 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5414 (IS_FREE(x, y) && Stop[x][y]))
5418 if (xx == ax && yy == ay) /* field in the middle */
5420 if (nachbarn < life[0] || nachbarn > life[1])
5422 Feld[xx][yy] = EL_EMPTY;
5424 DrawLevelField(xx, yy);
5425 Stop[xx][yy] = TRUE;
5429 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5430 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5431 { /* free border field */
5432 if (nachbarn >= life[2] && nachbarn <= life[3])
5434 Feld[xx][yy] = element;
5435 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5437 DrawLevelField(xx, yy);
5438 Stop[xx][yy] = TRUE;
5445 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5446 SND_GAME_OF_LIFE_GROWING);
5449 static void InitRobotWheel(int x, int y)
5451 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5454 static void RunRobotWheel(int x, int y)
5456 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5459 static void StopRobotWheel(int x, int y)
5461 if (ZX == x && ZY == y)
5465 static void InitTimegateWheel(int x, int y)
5467 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5470 static void RunTimegateWheel(int x, int y)
5472 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5475 void CheckExit(int x, int y)
5477 if (local_player->gems_still_needed > 0 ||
5478 local_player->sokobanfields_still_needed > 0 ||
5479 local_player->lights_still_needed > 0)
5481 int element = Feld[x][y];
5482 int graphic = el2img(element);
5484 if (IS_ANIMATED(graphic))
5485 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5490 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5493 Feld[x][y] = EL_EXIT_OPENING;
5495 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5498 void CheckExitSP(int x, int y)
5500 if (local_player->gems_still_needed > 0)
5502 int element = Feld[x][y];
5503 int graphic = el2img(element);
5505 if (IS_ANIMATED(graphic))
5506 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5511 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5514 Feld[x][y] = EL_SP_EXIT_OPENING;
5516 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5519 static void CloseAllOpenTimegates()
5523 for (y = 0; y < lev_fieldy; y++)
5525 for (x = 0; x < lev_fieldx; x++)
5527 int element = Feld[x][y];
5529 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5531 Feld[x][y] = EL_TIMEGATE_CLOSING;
5533 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5535 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5542 void EdelsteinFunkeln(int x, int y)
5544 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5547 if (Feld[x][y] == EL_BD_DIAMOND)
5550 if (MovDelay[x][y] == 0) /* next animation frame */
5551 MovDelay[x][y] = 11 * !SimpleRND(500);
5553 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5557 if (setup.direct_draw && MovDelay[x][y])
5558 SetDrawtoField(DRAW_BUFFERED);
5560 DrawLevelElementAnimation(x, y, Feld[x][y]);
5562 if (MovDelay[x][y] != 0)
5564 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5565 10 - MovDelay[x][y]);
5567 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5569 if (setup.direct_draw)
5573 dest_x = FX + SCREENX(x) * TILEX;
5574 dest_y = FY + SCREENY(y) * TILEY;
5576 BlitBitmap(drawto_field, window,
5577 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5578 SetDrawtoField(DRAW_DIRECT);
5584 void MauerWaechst(int x, int y)
5588 if (!MovDelay[x][y]) /* next animation frame */
5589 MovDelay[x][y] = 3 * delay;
5591 if (MovDelay[x][y]) /* wait some time before next frame */
5595 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5597 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5598 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5600 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5603 if (!MovDelay[x][y])
5605 if (MovDir[x][y] == MV_LEFT)
5607 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5608 DrawLevelField(x - 1, y);
5610 else if (MovDir[x][y] == MV_RIGHT)
5612 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5613 DrawLevelField(x + 1, y);
5615 else if (MovDir[x][y] == MV_UP)
5617 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5618 DrawLevelField(x, y - 1);
5622 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5623 DrawLevelField(x, y + 1);
5626 Feld[x][y] = Store[x][y];
5628 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5629 DrawLevelField(x, y);
5634 void MauerAbleger(int ax, int ay)
5636 int element = Feld[ax][ay];
5637 int graphic = el2img(element);
5638 boolean oben_frei = FALSE, unten_frei = FALSE;
5639 boolean links_frei = FALSE, rechts_frei = FALSE;
5640 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5641 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5642 boolean new_wall = FALSE;
5644 if (IS_ANIMATED(graphic))
5645 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5647 if (!MovDelay[ax][ay]) /* start building new wall */
5648 MovDelay[ax][ay] = 6;
5650 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5653 if (MovDelay[ax][ay])
5657 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5659 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5661 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5663 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5666 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5667 element == EL_EXPANDABLE_WALL_ANY)
5671 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5672 Store[ax][ay-1] = element;
5673 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5674 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5675 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5676 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5681 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5682 Store[ax][ay+1] = element;
5683 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5684 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5685 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5686 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5691 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5692 element == EL_EXPANDABLE_WALL_ANY ||
5693 element == EL_EXPANDABLE_WALL)
5697 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5698 Store[ax-1][ay] = element;
5699 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5700 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5701 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5702 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5708 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5709 Store[ax+1][ay] = element;
5710 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5711 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5712 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5713 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5718 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5719 DrawLevelField(ax, ay);
5721 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5723 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5724 unten_massiv = TRUE;
5725 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5726 links_massiv = TRUE;
5727 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5728 rechts_massiv = TRUE;
5730 if (((oben_massiv && unten_massiv) ||
5731 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5732 element == EL_EXPANDABLE_WALL) &&
5733 ((links_massiv && rechts_massiv) ||
5734 element == EL_EXPANDABLE_WALL_VERTICAL))
5735 Feld[ax][ay] = EL_WALL;
5739 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5741 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5745 void CheckForDragon(int x, int y)
5748 boolean dragon_found = FALSE;
5749 static int xy[4][2] =
5757 for (i = 0; i < 4; i++)
5759 for (j = 0; j < 4; j++)
5761 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5763 if (IN_LEV_FIELD(xx, yy) &&
5764 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5766 if (Feld[xx][yy] == EL_DRAGON)
5767 dragon_found = TRUE;
5776 for (i = 0; i < 4; i++)
5778 for (j = 0; j < 3; j++)
5780 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5782 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5784 Feld[xx][yy] = EL_EMPTY;
5785 DrawLevelField(xx, yy);
5794 static void InitBuggyBase(int x, int y)
5796 int element = Feld[x][y];
5797 int activating_delay = FRAMES_PER_SECOND / 4;
5800 (element == EL_SP_BUGGY_BASE ?
5801 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5802 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5804 element == EL_SP_BUGGY_BASE_ACTIVE ?
5805 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5808 static void WarnBuggyBase(int x, int y)
5811 static int xy[4][2] =
5819 for (i = 0; i < 4; i++)
5821 int xx = x + xy[i][0], yy = y + xy[i][1];
5823 if (IS_PLAYER(xx, yy))
5825 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5832 static void InitTrap(int x, int y)
5834 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5837 static void ActivateTrap(int x, int y)
5839 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5842 static void ChangeActiveTrap(int x, int y)
5844 int graphic = IMG_TRAP_ACTIVE;
5846 /* if new animation frame was drawn, correct crumbled sand border */
5847 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5848 DrawLevelFieldCrumbledSand(x, y);
5851 static void ChangeElementNowExt(int x, int y, int target_element)
5853 /* check if element under player changes from accessible to unaccessible
5854 (needed for special case of dropping element which then changes) */
5855 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5856 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5863 Feld[x][y] = target_element;
5865 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5867 ResetGfxAnimation(x, y);
5868 ResetRandomAnimationValue(x, y);
5870 InitField(x, y, FALSE);
5871 if (CAN_MOVE(Feld[x][y]))
5874 DrawLevelField(x, y);
5876 if (GFX_CRUMBLED(Feld[x][y]))
5877 DrawLevelFieldCrumbledSandNeighbours(x, y);
5879 TestIfBadThingTouchesHero(x, y);
5880 TestIfPlayerTouchesCustomElement(x, y);
5881 TestIfElementTouchesCustomElement(x, y);
5883 if (ELEM_IS_PLAYER(target_element))
5884 RelocatePlayer(x, y, target_element);
5887 static boolean ChangeElementNow(int x, int y, int element, int page)
5889 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5891 /* always use default change event to prevent running into a loop */
5892 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5893 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5895 /* do not change already changed elements with same change event */
5897 if (Changed[x][y] & ChangeEvent[x][y])
5904 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5906 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5908 if (change->explode)
5915 if (change->use_content)
5917 boolean complete_change = TRUE;
5918 boolean can_change[3][3];
5921 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5923 boolean half_destructible;
5924 int ex = x + xx - 1;
5925 int ey = y + yy - 1;
5928 can_change[xx][yy] = TRUE;
5930 if (ex == x && ey == y) /* do not check changing element itself */
5933 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5935 can_change[xx][yy] = FALSE; /* do not change empty borders */
5940 if (!IN_LEV_FIELD(ex, ey))
5942 can_change[xx][yy] = FALSE;
5943 complete_change = FALSE;
5950 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5951 e = MovingOrBlocked2Element(ex, ey);
5953 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5955 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5956 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5957 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5959 can_change[xx][yy] = FALSE;
5960 complete_change = FALSE;
5964 if (!change->only_complete || complete_change)
5966 boolean something_has_changed = FALSE;
5968 if (change->only_complete && change->use_random_change &&
5969 RND(100) < change->random)
5972 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5974 int ex = x + xx - 1;
5975 int ey = y + yy - 1;
5977 if (can_change[xx][yy] && (!change->use_random_change ||
5978 RND(100) < change->random))
5980 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5981 RemoveMovingField(ex, ey);
5983 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5985 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5987 something_has_changed = TRUE;
5989 /* for symmetry reasons, freeze newly created border elements */
5990 if (ex != x || ey != y)
5991 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5995 if (something_has_changed)
5996 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6001 ChangeElementNowExt(x, y, change->target_element);
6003 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6009 static void ChangeElement(int x, int y, int page)
6011 int element = MovingOrBlocked2Element(x, y);
6012 struct ElementInfo *ei = &element_info[element];
6013 struct ElementChangeInfo *change = &ei->change_page[page];
6017 if (!CAN_CHANGE(element))
6020 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6021 x, y, element, element_info[element].token_name);
6022 printf("ChangeElement(): This should never happen!\n");
6028 if (ChangeDelay[x][y] == 0) /* initialize element change */
6030 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6031 RND(change->delay_random * change->delay_frames)) + 1;
6033 ResetGfxAnimation(x, y);
6034 ResetRandomAnimationValue(x, y);
6036 if (change->pre_change_function)
6037 change->pre_change_function(x, y);
6040 ChangeDelay[x][y]--;
6042 if (ChangeDelay[x][y] != 0) /* continue element change */
6044 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6046 if (IS_ANIMATED(graphic))
6047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6049 if (change->change_function)
6050 change->change_function(x, y);
6052 else /* finish element change */
6054 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6056 page = ChangePage[x][y];
6057 ChangePage[x][y] = -1;
6060 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6062 ChangeDelay[x][y] = 1; /* try change after next move step */
6063 ChangePage[x][y] = page; /* remember page to use for change */
6068 if (ChangeElementNow(x, y, element, page))
6070 if (change->post_change_function)
6071 change->post_change_function(x, y);
6076 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6077 int trigger_element,
6083 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6086 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6088 int element = EL_CUSTOM_START + i;
6090 boolean change_element = FALSE;
6093 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6096 for (j = 0; j < element_info[element].num_change_pages; j++)
6098 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6100 if (change->can_change &&
6102 change->events & CH_EVENT_BIT(trigger_event) &&
6104 change->sides & trigger_side &&
6105 change->trigger_element == trigger_element)
6108 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6109 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6110 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6113 change_element = TRUE;
6120 if (!change_element)
6123 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6126 if (x == lx && y == ly) /* do not change trigger element itself */
6130 if (Feld[x][y] == element)
6132 ChangeDelay[x][y] = 1;
6133 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6134 ChangeElement(x, y, page);
6142 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6145 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6149 static boolean CheckElementSideChange(int x, int y, int element, int side,
6150 int trigger_event, int page)
6152 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6155 if (Feld[x][y] == EL_BLOCKED)
6157 Blocked2Moving(x, y, &x, &y);
6158 element = Feld[x][y];
6162 page = element_info[element].event_page_nr[trigger_event];
6164 if (!(element_info[element].change_page[page].sides & side))
6167 ChangeDelay[x][y] = 1;
6168 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6169 ChangeElement(x, y, page);
6174 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6176 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6179 static void PlayPlayerSound(struct PlayerInfo *player)
6181 int jx = player->jx, jy = player->jy;
6182 int element = player->element_nr;
6183 int last_action = player->last_action_waiting;
6184 int action = player->action_waiting;
6186 if (player->is_waiting)
6188 if (action != last_action)
6189 PlayLevelSoundElementAction(jx, jy, element, action);
6191 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6195 if (action != last_action)
6196 StopSound(element_info[element].sound[last_action]);
6198 if (last_action == ACTION_SLEEPING)
6199 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6203 static void PlayAllPlayersSound()
6207 for (i = 0; i < MAX_PLAYERS; i++)
6208 if (stored_player[i].active)
6209 PlayPlayerSound(&stored_player[i]);
6212 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6214 boolean last_waiting = player->is_waiting;
6215 int move_dir = player->MovDir;
6217 player->last_action_waiting = player->action_waiting;
6221 if (!last_waiting) /* not waiting -> waiting */
6223 player->is_waiting = TRUE;
6225 player->frame_counter_bored =
6227 game.player_boring_delay_fixed +
6228 SimpleRND(game.player_boring_delay_random);
6229 player->frame_counter_sleeping =
6231 game.player_sleeping_delay_fixed +
6232 SimpleRND(game.player_sleeping_delay_random);
6234 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6237 if (game.player_sleeping_delay_fixed +
6238 game.player_sleeping_delay_random > 0 &&
6239 player->anim_delay_counter == 0 &&
6240 player->post_delay_counter == 0 &&
6241 FrameCounter >= player->frame_counter_sleeping)
6242 player->is_sleeping = TRUE;
6243 else if (game.player_boring_delay_fixed +
6244 game.player_boring_delay_random > 0 &&
6245 FrameCounter >= player->frame_counter_bored)
6246 player->is_bored = TRUE;
6248 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6249 player->is_bored ? ACTION_BORING :
6252 if (player->is_sleeping)
6254 if (player->num_special_action_sleeping > 0)
6256 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6258 int last_special_action = player->special_action_sleeping;
6259 int num_special_action = player->num_special_action_sleeping;
6260 int special_action =
6261 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6262 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6263 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6264 last_special_action + 1 : ACTION_SLEEPING);
6265 int special_graphic =
6266 el_act_dir2img(player->element_nr, special_action, move_dir);
6268 player->anim_delay_counter =
6269 graphic_info[special_graphic].anim_delay_fixed +
6270 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6271 player->post_delay_counter =
6272 graphic_info[special_graphic].post_delay_fixed +
6273 SimpleRND(graphic_info[special_graphic].post_delay_random);
6275 player->special_action_sleeping = special_action;
6278 if (player->anim_delay_counter > 0)
6280 player->action_waiting = player->special_action_sleeping;
6281 player->anim_delay_counter--;
6283 else if (player->post_delay_counter > 0)
6285 player->post_delay_counter--;
6289 else if (player->is_bored)
6291 if (player->num_special_action_bored > 0)
6293 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6295 int special_action =
6296 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6297 int special_graphic =
6298 el_act_dir2img(player->element_nr, special_action, move_dir);
6300 player->anim_delay_counter =
6301 graphic_info[special_graphic].anim_delay_fixed +
6302 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6303 player->post_delay_counter =
6304 graphic_info[special_graphic].post_delay_fixed +
6305 SimpleRND(graphic_info[special_graphic].post_delay_random);
6307 player->special_action_bored = special_action;
6310 if (player->anim_delay_counter > 0)
6312 player->action_waiting = player->special_action_bored;
6313 player->anim_delay_counter--;
6315 else if (player->post_delay_counter > 0)
6317 player->post_delay_counter--;
6322 else if (last_waiting) /* waiting -> not waiting */
6324 player->is_waiting = FALSE;
6325 player->is_bored = FALSE;
6326 player->is_sleeping = FALSE;
6328 player->frame_counter_bored = -1;
6329 player->frame_counter_sleeping = -1;
6331 player->anim_delay_counter = 0;
6332 player->post_delay_counter = 0;
6334 player->action_waiting = ACTION_DEFAULT;
6336 player->special_action_bored = ACTION_DEFAULT;
6337 player->special_action_sleeping = ACTION_DEFAULT;
6342 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6345 static byte stored_player_action[MAX_PLAYERS];
6346 static int num_stored_actions = 0;
6348 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6349 int left = player_action & JOY_LEFT;
6350 int right = player_action & JOY_RIGHT;
6351 int up = player_action & JOY_UP;
6352 int down = player_action & JOY_DOWN;
6353 int button1 = player_action & JOY_BUTTON_1;
6354 int button2 = player_action & JOY_BUTTON_2;
6355 int dx = (left ? -1 : right ? 1 : 0);
6356 int dy = (up ? -1 : down ? 1 : 0);
6359 stored_player_action[player->index_nr] = 0;
6360 num_stored_actions++;
6364 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6367 if (!player->active || tape.pausing)
6373 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6377 snapped = SnapField(player, dx, dy);
6381 dropped = DropElement(player);
6383 moved = MovePlayer(player, dx, dy);
6386 if (tape.single_step && tape.recording && !tape.pausing)
6388 if (button1 || (dropped && !moved))
6390 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6391 SnapField(player, 0, 0); /* stop snapping */
6395 SetPlayerWaiting(player, FALSE);
6398 return player_action;
6400 stored_player_action[player->index_nr] = player_action;
6406 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6409 /* no actions for this player (no input at player's configured device) */
6411 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6412 SnapField(player, 0, 0);
6413 CheckGravityMovement(player);
6415 if (player->MovPos == 0)
6416 SetPlayerWaiting(player, TRUE);
6418 if (player->MovPos == 0) /* needed for tape.playing */
6419 player->is_moving = FALSE;
6425 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6427 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6429 TapeRecordAction(stored_player_action);
6430 num_stored_actions = 0;
6437 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6439 static byte stored_player_action[MAX_PLAYERS];
6440 static int num_stored_actions = 0;
6441 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6442 int left = player_action & JOY_LEFT;
6443 int right = player_action & JOY_RIGHT;
6444 int up = player_action & JOY_UP;
6445 int down = player_action & JOY_DOWN;
6446 int button1 = player_action & JOY_BUTTON_1;
6447 int button2 = player_action & JOY_BUTTON_2;
6448 int dx = (left ? -1 : right ? 1 : 0);
6449 int dy = (up ? -1 : down ? 1 : 0);
6451 stored_player_action[player->index_nr] = 0;
6452 num_stored_actions++;
6454 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6456 if (!player->active || tape.pausing)
6461 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6464 snapped = SnapField(player, dx, dy);
6468 dropped = DropElement(player);
6470 moved = MovePlayer(player, dx, dy);
6473 if (tape.single_step && tape.recording && !tape.pausing)
6475 if (button1 || (dropped && !moved))
6477 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6478 SnapField(player, 0, 0); /* stop snapping */
6482 stored_player_action[player->index_nr] = player_action;
6486 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6488 /* no actions for this player (no input at player's configured device) */
6490 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6491 SnapField(player, 0, 0);
6492 CheckGravityMovement(player);
6494 if (player->MovPos == 0)
6495 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6497 if (player->MovPos == 0) /* needed for tape.playing */
6498 player->is_moving = FALSE;
6501 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6503 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6505 TapeRecordAction(stored_player_action);
6506 num_stored_actions = 0;
6513 static unsigned long action_delay = 0;
6514 unsigned long action_delay_value;
6515 int magic_wall_x = 0, magic_wall_y = 0;
6516 int i, x, y, element, graphic;
6517 byte *recorded_player_action;
6518 byte summarized_player_action = 0;
6520 byte tape_action[MAX_PLAYERS];
6523 if (game_status != GAME_MODE_PLAYING)
6526 action_delay_value =
6527 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6529 if (tape.playing && tape.index_search && !tape.pausing)
6530 action_delay_value = 0;
6532 /* ---------- main game synchronization point ---------- */
6534 WaitUntilDelayReached(&action_delay, action_delay_value);
6536 if (network_playing && !network_player_action_received)
6540 printf("DEBUG: try to get network player actions in time\n");
6544 #if defined(PLATFORM_UNIX)
6545 /* last chance to get network player actions without main loop delay */
6549 if (game_status != GAME_MODE_PLAYING)
6552 if (!network_player_action_received)
6556 printf("DEBUG: failed to get network player actions in time\n");
6567 printf("::: getting new tape action [%d]\n", FrameCounter);
6570 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6572 for (i = 0; i < MAX_PLAYERS; i++)
6574 summarized_player_action |= stored_player[i].action;
6576 if (!network_playing)
6577 stored_player[i].effective_action = stored_player[i].action;
6580 #if defined(PLATFORM_UNIX)
6581 if (network_playing)
6582 SendToServer_MovePlayer(summarized_player_action);
6585 if (!options.network && !setup.team_mode)
6586 local_player->effective_action = summarized_player_action;
6588 for (i = 0; i < MAX_PLAYERS; i++)
6590 int actual_player_action = stored_player[i].effective_action;
6592 if (stored_player[i].programmed_action)
6593 actual_player_action = stored_player[i].programmed_action;
6595 if (recorded_player_action)
6596 actual_player_action = recorded_player_action[i];
6598 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6600 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6601 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6603 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6608 TapeRecordAction(tape_action);
6611 network_player_action_received = FALSE;
6613 ScrollScreen(NULL, SCROLL_GO_ON);
6619 for (i = 0; i < MAX_PLAYERS; i++)
6620 stored_player[i].Frame++;
6624 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6626 for (i = 0; i < MAX_PLAYERS; i++)
6628 struct PlayerInfo *player = &stored_player[i];
6632 if (player->active && player->is_pushing && player->is_moving &&
6635 ContinueMoving(x, y);
6637 /* continue moving after pushing (this is actually a bug) */
6638 if (!IS_MOVING(x, y))
6647 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6649 Changed[x][y] = CE_BITMASK_DEFAULT;
6650 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6653 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6655 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6656 printf("GameActions(): This should never happen!\n");
6658 ChangePage[x][y] = -1;
6663 if (WasJustMoving[x][y] > 0)
6664 WasJustMoving[x][y]--;
6665 if (WasJustFalling[x][y] > 0)
6666 WasJustFalling[x][y]--;
6671 /* reset finished pushing action (not done in ContinueMoving() to allow
6672 continous pushing animation for elements with zero push delay) */
6673 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6675 ResetGfxAnimation(x, y);
6676 DrawLevelField(x, y);
6681 if (IS_BLOCKED(x, y))
6685 Blocked2Moving(x, y, &oldx, &oldy);
6686 if (!IS_MOVING(oldx, oldy))
6688 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6689 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6690 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6691 printf("GameActions(): This should never happen!\n");
6697 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6699 element = Feld[x][y];
6701 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6703 graphic = el2img(element);
6709 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6711 element = graphic = 0;
6715 if (graphic_info[graphic].anim_global_sync)
6716 GfxFrame[x][y] = FrameCounter;
6718 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6719 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6720 ResetRandomAnimationValue(x, y);
6722 SetRandomAnimationValue(x, y);
6725 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6728 if (IS_INACTIVE(element))
6730 if (IS_ANIMATED(graphic))
6731 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6737 /* this may take place after moving, so 'element' may have changed */
6739 if (IS_CHANGING(x, y))
6741 if (IS_CHANGING(x, y) &&
6742 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6746 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6747 element_info[element].event_page_nr[CE_DELAY]);
6749 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6752 element = Feld[x][y];
6753 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6757 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6762 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6764 if (element == EL_MOLE)
6765 printf("::: %d, %d, %d [%d]\n",
6766 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6770 if (element == EL_YAMYAM)
6771 printf("::: %d, %d, %d\n",
6772 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6776 if (IS_ANIMATED(graphic) &&
6780 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6783 if (element == EL_BUG)
6784 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6788 if (element == EL_MOLE)
6789 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6793 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6794 EdelsteinFunkeln(x, y);
6796 else if ((element == EL_ACID ||
6797 element == EL_EXIT_OPEN ||
6798 element == EL_SP_EXIT_OPEN ||
6799 element == EL_SP_TERMINAL ||
6800 element == EL_SP_TERMINAL_ACTIVE ||
6801 element == EL_EXTRA_TIME ||
6802 element == EL_SHIELD_NORMAL ||
6803 element == EL_SHIELD_DEADLY) &&
6804 IS_ANIMATED(graphic))
6805 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6806 else if (IS_MOVING(x, y))
6807 ContinueMoving(x, y);
6808 else if (IS_ACTIVE_BOMB(element))
6809 CheckDynamite(x, y);
6811 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6812 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6814 else if (element == EL_AMOEBA_GROWING)
6815 AmoebeWaechst(x, y);
6816 else if (element == EL_AMOEBA_SHRINKING)
6817 AmoebaDisappearing(x, y);
6819 #if !USE_NEW_AMOEBA_CODE
6820 else if (IS_AMOEBALIVE(element))
6821 AmoebeAbleger(x, y);
6824 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6826 else if (element == EL_EXIT_CLOSED)
6828 else if (element == EL_SP_EXIT_CLOSED)
6830 else if (element == EL_EXPANDABLE_WALL_GROWING)
6832 else if (element == EL_EXPANDABLE_WALL ||
6833 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6834 element == EL_EXPANDABLE_WALL_VERTICAL ||
6835 element == EL_EXPANDABLE_WALL_ANY)
6837 else if (element == EL_FLAMES)
6838 CheckForDragon(x, y);
6840 else if (IS_AUTO_CHANGING(element))
6841 ChangeElement(x, y);
6843 else if (element == EL_EXPLOSION)
6844 ; /* drawing of correct explosion animation is handled separately */
6845 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6846 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6849 /* this may take place after moving, so 'element' may have changed */
6850 if (IS_AUTO_CHANGING(Feld[x][y]))
6851 ChangeElement(x, y);
6854 if (IS_BELT_ACTIVE(element))
6855 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6857 if (game.magic_wall_active)
6859 int jx = local_player->jx, jy = local_player->jy;
6861 /* play the element sound at the position nearest to the player */
6862 if ((element == EL_MAGIC_WALL_FULL ||
6863 element == EL_MAGIC_WALL_ACTIVE ||
6864 element == EL_MAGIC_WALL_EMPTYING ||
6865 element == EL_BD_MAGIC_WALL_FULL ||
6866 element == EL_BD_MAGIC_WALL_ACTIVE ||
6867 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6868 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6876 #if USE_NEW_AMOEBA_CODE
6877 /* new experimental amoeba growth stuff */
6879 if (!(FrameCounter % 8))
6882 static unsigned long random = 1684108901;
6884 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6887 x = (random >> 10) % lev_fieldx;
6888 y = (random >> 20) % lev_fieldy;
6890 x = RND(lev_fieldx);
6891 y = RND(lev_fieldy);
6893 element = Feld[x][y];
6895 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6896 if (!IS_PLAYER(x,y) &&
6897 (element == EL_EMPTY ||
6898 element == EL_SAND ||
6899 element == EL_QUICKSAND_EMPTY ||
6900 element == EL_ACID_SPLASH_LEFT ||
6901 element == EL_ACID_SPLASH_RIGHT))
6903 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6904 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6905 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6906 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6907 Feld[x][y] = EL_AMOEBA_DROP;
6910 random = random * 129 + 1;
6916 if (game.explosions_delayed)
6919 game.explosions_delayed = FALSE;
6921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6923 element = Feld[x][y];
6925 if (ExplodeField[x][y])
6926 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6927 else if (element == EL_EXPLOSION)
6928 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6930 ExplodeField[x][y] = EX_NO_EXPLOSION;
6933 game.explosions_delayed = TRUE;
6936 if (game.magic_wall_active)
6938 if (!(game.magic_wall_time_left % 4))
6940 int element = Feld[magic_wall_x][magic_wall_y];
6942 if (element == EL_BD_MAGIC_WALL_FULL ||
6943 element == EL_BD_MAGIC_WALL_ACTIVE ||
6944 element == EL_BD_MAGIC_WALL_EMPTYING)
6945 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6947 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6950 if (game.magic_wall_time_left > 0)
6952 game.magic_wall_time_left--;
6953 if (!game.magic_wall_time_left)
6955 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6957 element = Feld[x][y];
6959 if (element == EL_MAGIC_WALL_ACTIVE ||
6960 element == EL_MAGIC_WALL_FULL)
6962 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6963 DrawLevelField(x, y);
6965 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6966 element == EL_BD_MAGIC_WALL_FULL)
6968 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6969 DrawLevelField(x, y);
6973 game.magic_wall_active = FALSE;
6978 if (game.light_time_left > 0)
6980 game.light_time_left--;
6982 if (game.light_time_left == 0)
6983 RedrawAllLightSwitchesAndInvisibleElements();
6986 if (game.timegate_time_left > 0)
6988 game.timegate_time_left--;
6990 if (game.timegate_time_left == 0)
6991 CloseAllOpenTimegates();
6994 for (i = 0; i < MAX_PLAYERS; i++)
6996 struct PlayerInfo *player = &stored_player[i];
6998 if (SHIELD_ON(player))
7000 if (player->shield_deadly_time_left)
7001 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7002 else if (player->shield_normal_time_left)
7003 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7007 if (TimeFrames >= FRAMES_PER_SECOND)
7012 for (i = 0; i < MAX_PLAYERS; i++)
7014 struct PlayerInfo *player = &stored_player[i];
7016 if (SHIELD_ON(player))
7018 player->shield_normal_time_left--;
7020 if (player->shield_deadly_time_left > 0)
7021 player->shield_deadly_time_left--;
7025 if (tape.recording || tape.playing)
7026 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7032 if (TimeLeft <= 10 && setup.time_limit)
7033 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7035 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7037 if (!TimeLeft && setup.time_limit)
7038 for (i = 0; i < MAX_PLAYERS; i++)
7039 KillHero(&stored_player[i]);
7041 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7042 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7046 PlayAllPlayersSound();
7048 if (options.debug) /* calculate frames per second */
7050 static unsigned long fps_counter = 0;
7051 static int fps_frames = 0;
7052 unsigned long fps_delay_ms = Counter() - fps_counter;
7056 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7058 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7061 fps_counter = Counter();
7064 redraw_mask |= REDRAW_FPS;
7068 if (stored_player[0].jx != stored_player[0].last_jx ||
7069 stored_player[0].jy != stored_player[0].last_jy)
7070 printf("::: %d, %d, %d, %d, %d\n",
7071 stored_player[0].MovDir,
7072 stored_player[0].MovPos,
7073 stored_player[0].GfxPos,
7074 stored_player[0].Frame,
7075 stored_player[0].StepFrame);
7082 for (i = 0; i < MAX_PLAYERS; i++)
7085 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7087 stored_player[i].Frame += move_frames;
7089 if (stored_player[i].MovPos != 0)
7090 stored_player[i].StepFrame += move_frames;
7095 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7097 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7099 local_player->show_envelope = 0;
7104 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7106 int min_x = x, min_y = y, max_x = x, max_y = y;
7109 for (i = 0; i < MAX_PLAYERS; i++)
7111 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7113 if (!stored_player[i].active || &stored_player[i] == player)
7116 min_x = MIN(min_x, jx);
7117 min_y = MIN(min_y, jy);
7118 max_x = MAX(max_x, jx);
7119 max_y = MAX(max_y, jy);
7122 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7125 static boolean AllPlayersInVisibleScreen()
7129 for (i = 0; i < MAX_PLAYERS; i++)
7131 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7133 if (!stored_player[i].active)
7136 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7143 void ScrollLevel(int dx, int dy)
7145 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7148 BlitBitmap(drawto_field, drawto_field,
7149 FX + TILEX * (dx == -1) - softscroll_offset,
7150 FY + TILEY * (dy == -1) - softscroll_offset,
7151 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7152 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7153 FX + TILEX * (dx == 1) - softscroll_offset,
7154 FY + TILEY * (dy == 1) - softscroll_offset);
7158 x = (dx == 1 ? BX1 : BX2);
7159 for (y = BY1; y <= BY2; y++)
7160 DrawScreenField(x, y);
7165 y = (dy == 1 ? BY1 : BY2);
7166 for (x = BX1; x <= BX2; x++)
7167 DrawScreenField(x, y);
7170 redraw_mask |= REDRAW_FIELD;
7173 static void CheckGravityMovement(struct PlayerInfo *player)
7175 if (game.gravity && !player->programmed_action)
7177 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7178 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7180 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7181 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7182 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7183 int jx = player->jx, jy = player->jy;
7184 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7185 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7186 int new_jx = jx + dx, new_jy = jy + dy;
7187 boolean field_under_player_is_free =
7188 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7189 boolean player_is_moving_to_valid_field =
7190 (IN_LEV_FIELD(new_jx, new_jy) &&
7191 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7192 Feld[new_jx][new_jy] == EL_SAND));
7193 /* !!! extend EL_SAND to anything diggable !!! */
7195 if (field_under_player_is_free &&
7196 !player_is_moving_to_valid_field &&
7197 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7198 player->programmed_action = MV_DOWN;
7204 -----------------------------------------------------------------------------
7205 dx, dy: direction (non-diagonal) to try to move the player to
7206 real_dx, real_dy: direction as read from input device (can be diagonal)
7209 boolean MovePlayerOneStep(struct PlayerInfo *player,
7210 int dx, int dy, int real_dx, int real_dy)
7213 static int change_sides[4][2] =
7215 /* enter side leave side */
7216 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7217 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7218 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7219 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7221 int move_direction = (dx == -1 ? MV_LEFT :
7222 dx == +1 ? MV_RIGHT :
7224 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7225 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7226 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7228 int jx = player->jx, jy = player->jy;
7229 int new_jx = jx + dx, new_jy = jy + dy;
7233 if (!player->active || (!dx && !dy))
7234 return MF_NO_ACTION;
7236 player->MovDir = (dx < 0 ? MV_LEFT :
7239 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7241 if (!IN_LEV_FIELD(new_jx, new_jy))
7242 return MF_NO_ACTION;
7244 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7245 return MF_NO_ACTION;
7248 element = MovingOrBlocked2Element(new_jx, new_jy);
7250 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7253 if (DONT_RUN_INTO(element))
7255 if (element == EL_ACID && dx == 0 && dy == 1)
7258 Feld[jx][jy] = EL_PLAYER_1;
7259 InitMovingField(jx, jy, MV_DOWN);
7260 Store[jx][jy] = EL_ACID;
7261 ContinueMoving(jx, jy);
7265 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7270 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7271 if (can_move != MF_MOVING)
7274 /* check if DigField() has caused relocation of the player */
7275 if (player->jx != jx || player->jy != jy)
7276 return MF_NO_ACTION;
7278 StorePlayer[jx][jy] = 0;
7279 player->last_jx = jx;
7280 player->last_jy = jy;
7281 player->jx = new_jx;
7282 player->jy = new_jy;
7283 StorePlayer[new_jx][new_jy] = player->element_nr;
7286 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7288 player->step_counter++;
7290 PlayerVisit[jx][jy] = FrameCounter;
7292 ScrollPlayer(player, SCROLL_INIT);
7295 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7297 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7298 CE_OTHER_GETS_LEFT);
7299 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7300 CE_LEFT_BY_PLAYER, -1);
7303 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7305 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7306 enter_side, CE_OTHER_GETS_ENTERED);
7307 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7308 CE_ENTERED_BY_PLAYER, -1);
7315 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7317 int jx = player->jx, jy = player->jy;
7318 int old_jx = jx, old_jy = jy;
7319 int moved = MF_NO_ACTION;
7321 if (!player->active || (!dx && !dy))
7325 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7329 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7330 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7334 /* remove the last programmed player action */
7335 player->programmed_action = 0;
7339 /* should only happen if pre-1.2 tape recordings are played */
7340 /* this is only for backward compatibility */
7342 int original_move_delay_value = player->move_delay_value;
7345 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7349 /* scroll remaining steps with finest movement resolution */
7350 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7352 while (player->MovPos)
7354 ScrollPlayer(player, SCROLL_GO_ON);
7355 ScrollScreen(NULL, SCROLL_GO_ON);
7361 player->move_delay_value = original_move_delay_value;
7364 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7366 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7367 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7371 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7372 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7378 if (moved & MF_MOVING && !ScreenMovPos &&
7379 (player == local_player || !options.network))
7381 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7382 int offset = (setup.scroll_delay ? 3 : 0);
7384 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7386 /* actual player has left the screen -- scroll in that direction */
7387 if (jx != old_jx) /* player has moved horizontally */
7388 scroll_x += (jx - old_jx);
7389 else /* player has moved vertically */
7390 scroll_y += (jy - old_jy);
7394 if (jx != old_jx) /* player has moved horizontally */
7396 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7397 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7398 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7400 /* don't scroll over playfield boundaries */
7401 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7402 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7404 /* don't scroll more than one field at a time */
7405 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7407 /* don't scroll against the player's moving direction */
7408 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7409 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7410 scroll_x = old_scroll_x;
7412 else /* player has moved vertically */
7414 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7415 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7416 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7418 /* don't scroll over playfield boundaries */
7419 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7420 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7422 /* don't scroll more than one field at a time */
7423 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7425 /* don't scroll against the player's moving direction */
7426 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7427 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7428 scroll_y = old_scroll_y;
7432 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7434 if (!options.network && !AllPlayersInVisibleScreen())
7436 scroll_x = old_scroll_x;
7437 scroll_y = old_scroll_y;
7441 ScrollScreen(player, SCROLL_INIT);
7442 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7449 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7451 if (!(moved & MF_MOVING) && !player->is_pushing)
7456 player->StepFrame = 0;
7458 if (moved & MF_MOVING)
7460 if (old_jx != jx && old_jy == jy)
7461 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7462 else if (old_jx == jx && old_jy != jy)
7463 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7465 DrawLevelField(jx, jy); /* for "crumbled sand" */
7467 player->last_move_dir = player->MovDir;
7468 player->is_moving = TRUE;
7470 player->is_snapping = FALSE;
7474 player->is_switching = FALSE;
7480 static int change_sides[4][2] =
7482 /* enter side leave side */
7483 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7484 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7485 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7486 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7488 int move_direction = player->MovDir;
7489 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7490 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7493 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7495 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7496 leave_side, CE_OTHER_GETS_LEFT);
7497 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7498 leave_side, CE_LEFT_BY_PLAYER, -1);
7501 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7503 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7504 enter_side, CE_OTHER_GETS_ENTERED);
7505 CheckElementSideChange(jx, jy, Feld[jx][jy],
7506 enter_side, CE_ENTERED_BY_PLAYER, -1);
7517 CheckGravityMovement(player);
7520 player->last_move_dir = MV_NO_MOVING;
7522 player->is_moving = FALSE;
7525 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7527 TestIfHeroTouchesBadThing(jx, jy);
7528 TestIfPlayerTouchesCustomElement(jx, jy);
7531 if (!player->active)
7537 void ScrollPlayer(struct PlayerInfo *player, int mode)
7539 int jx = player->jx, jy = player->jy;
7540 int last_jx = player->last_jx, last_jy = player->last_jy;
7541 int move_stepsize = TILEX / player->move_delay_value;
7543 if (!player->active || !player->MovPos)
7546 if (mode == SCROLL_INIT)
7548 player->actual_frame_counter = FrameCounter;
7549 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7551 if (Feld[last_jx][last_jy] == EL_EMPTY)
7552 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7559 else if (!FrameReached(&player->actual_frame_counter, 1))
7562 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7563 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7565 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7566 Feld[last_jx][last_jy] = EL_EMPTY;
7568 /* before DrawPlayer() to draw correct player graphic for this case */
7569 if (player->MovPos == 0)
7570 CheckGravityMovement(player);
7573 DrawPlayer(player); /* needed here only to cleanup last field */
7576 if (player->MovPos == 0) /* player reached destination field */
7578 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7580 /* continue with normal speed after quickly moving through gate */
7581 HALVE_PLAYER_SPEED(player);
7583 /* be able to make the next move without delay */
7584 player->move_delay = 0;
7587 player->last_jx = jx;
7588 player->last_jy = jy;
7590 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7591 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7592 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7594 DrawPlayer(player); /* needed here only to cleanup last field */
7597 if (local_player->friends_still_needed == 0 ||
7598 IS_SP_ELEMENT(Feld[jx][jy]))
7599 player->LevelSolved = player->GameOver = TRUE;
7602 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7604 TestIfHeroTouchesBadThing(jx, jy);
7605 TestIfPlayerTouchesCustomElement(jx, jy);
7607 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7610 if (!player->active)
7614 if (tape.single_step && tape.recording && !tape.pausing &&
7615 !player->programmed_action)
7616 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7620 void ScrollScreen(struct PlayerInfo *player, int mode)
7622 static unsigned long screen_frame_counter = 0;
7624 if (mode == SCROLL_INIT)
7626 /* set scrolling step size according to actual player's moving speed */
7627 ScrollStepSize = TILEX / player->move_delay_value;
7629 screen_frame_counter = FrameCounter;
7630 ScreenMovDir = player->MovDir;
7631 ScreenMovPos = player->MovPos;
7632 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7635 else if (!FrameReached(&screen_frame_counter, 1))
7640 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7641 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7642 redraw_mask |= REDRAW_FIELD;
7645 ScreenMovDir = MV_NO_MOVING;
7648 void TestIfPlayerTouchesCustomElement(int x, int y)
7650 static int xy[4][2] =
7657 static int change_sides[4][2] =
7659 /* center side border side */
7660 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7661 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7662 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7663 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7665 static int touch_dir[4] =
7672 int center_element = Feld[x][y]; /* should always be non-moving! */
7675 for (i = 0; i < 4; i++)
7677 int xx = x + xy[i][0];
7678 int yy = y + xy[i][1];
7679 int center_side = change_sides[i][0];
7680 int border_side = change_sides[i][1];
7683 if (!IN_LEV_FIELD(xx, yy))
7686 if (IS_PLAYER(x, y))
7688 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7689 border_element = Feld[xx][yy]; /* may be moving! */
7690 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7691 border_element = Feld[xx][yy];
7692 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7693 border_element = MovingOrBlocked2Element(xx, yy);
7695 continue; /* center and border element do not touch */
7697 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7698 CE_OTHER_GETS_TOUCHED);
7699 CheckElementSideChange(xx, yy, border_element, border_side,
7700 CE_TOUCHED_BY_PLAYER, -1);
7702 else if (IS_PLAYER(xx, yy))
7704 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7706 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7708 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7709 continue; /* center and border element do not touch */
7712 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7713 CE_OTHER_GETS_TOUCHED);
7714 CheckElementSideChange(x, y, center_element, center_side,
7715 CE_TOUCHED_BY_PLAYER, -1);
7722 void TestIfElementTouchesCustomElement(int x, int y)
7724 static int xy[4][2] =
7731 static int change_sides[4][2] =
7733 /* center side border side */
7734 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7735 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7736 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7737 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7739 static int touch_dir[4] =
7746 boolean change_center_element = FALSE;
7747 int center_element_change_page = 0;
7748 int center_element = Feld[x][y]; /* should always be non-moving! */
7751 for (i = 0; i < 4; i++)
7753 int xx = x + xy[i][0];
7754 int yy = y + xy[i][1];
7755 int center_side = change_sides[i][0];
7756 int border_side = change_sides[i][1];
7759 if (!IN_LEV_FIELD(xx, yy))
7762 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7763 border_element = Feld[xx][yy]; /* may be moving! */
7764 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7765 border_element = Feld[xx][yy];
7766 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7767 border_element = MovingOrBlocked2Element(xx, yy);
7769 continue; /* center and border element do not touch */
7771 /* check for change of center element (but change it only once) */
7772 if (IS_CUSTOM_ELEMENT(center_element) &&
7773 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7774 !change_center_element)
7776 for (j = 0; j < element_info[center_element].num_change_pages; j++)
7778 struct ElementChangeInfo *change =
7779 &element_info[center_element].change_page[j];
7781 if (change->can_change &&
7782 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7783 change->sides & border_side &&
7784 change->trigger_element == border_element)
7786 change_center_element = TRUE;
7787 center_element_change_page = j;
7794 /* check for change of border element */
7795 if (IS_CUSTOM_ELEMENT(border_element) &&
7796 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7798 for (j = 0; j < element_info[border_element].num_change_pages; j++)
7800 struct ElementChangeInfo *change =
7801 &element_info[border_element].change_page[j];
7803 if (change->can_change &&
7804 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7805 change->sides & center_side &&
7806 change->trigger_element == center_element)
7808 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7809 CE_OTHER_IS_TOUCHING, j);
7816 if (change_center_element)
7817 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7818 CE_OTHER_IS_TOUCHING, center_element_change_page);
7821 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7823 int i, kill_x = -1, kill_y = -1;
7824 static int test_xy[4][2] =
7831 static int test_dir[4] =
7839 for (i = 0; i < 4; i++)
7841 int test_x, test_y, test_move_dir, test_element;
7843 test_x = good_x + test_xy[i][0];
7844 test_y = good_y + test_xy[i][1];
7845 if (!IN_LEV_FIELD(test_x, test_y))
7849 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7852 test_element = Feld[test_x][test_y];
7854 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7857 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7858 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7860 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7861 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7869 if (kill_x != -1 || kill_y != -1)
7871 if (IS_PLAYER(good_x, good_y))
7873 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7875 if (player->shield_deadly_time_left > 0)
7876 Bang(kill_x, kill_y);
7877 else if (!PLAYER_PROTECTED(good_x, good_y))
7881 Bang(good_x, good_y);
7885 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7887 int i, kill_x = -1, kill_y = -1;
7888 int bad_element = Feld[bad_x][bad_y];
7889 static int test_xy[4][2] =
7896 static int touch_dir[4] =
7903 static int test_dir[4] =
7911 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7914 for (i = 0; i < 4; i++)
7916 int test_x, test_y, test_move_dir, test_element;
7918 test_x = bad_x + test_xy[i][0];
7919 test_y = bad_y + test_xy[i][1];
7920 if (!IN_LEV_FIELD(test_x, test_y))
7924 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7926 test_element = Feld[test_x][test_y];
7928 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7929 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7931 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7932 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7934 /* good thing is player or penguin that does not move away */
7935 if (IS_PLAYER(test_x, test_y))
7937 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7939 if (bad_element == EL_ROBOT && player->is_moving)
7940 continue; /* robot does not kill player if he is moving */
7942 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7944 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7945 continue; /* center and border element do not touch */
7952 else if (test_element == EL_PENGUIN)
7961 if (kill_x != -1 || kill_y != -1)
7963 if (IS_PLAYER(kill_x, kill_y))
7965 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7967 if (player->shield_deadly_time_left > 0)
7969 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7973 Bang(kill_x, kill_y);
7977 void TestIfHeroTouchesBadThing(int x, int y)
7979 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7982 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7984 TestIfGoodThingHitsBadThing(x, y, move_dir);
7987 void TestIfBadThingTouchesHero(int x, int y)
7989 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7992 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7994 TestIfBadThingHitsGoodThing(x, y, move_dir);
7997 void TestIfFriendTouchesBadThing(int x, int y)
7999 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8002 void TestIfBadThingTouchesFriend(int x, int y)
8004 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8007 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8009 int i, kill_x = bad_x, kill_y = bad_y;
8010 static int xy[4][2] =
8018 for (i = 0; i < 4; i++)
8022 x = bad_x + xy[i][0];
8023 y = bad_y + xy[i][1];
8024 if (!IN_LEV_FIELD(x, y))
8027 element = Feld[x][y];
8028 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8029 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8037 if (kill_x != bad_x || kill_y != bad_y)
8041 void KillHero(struct PlayerInfo *player)
8043 int jx = player->jx, jy = player->jy;
8045 if (!player->active)
8048 /* remove accessible field at the player's position */
8049 Feld[jx][jy] = EL_EMPTY;
8051 /* deactivate shield (else Bang()/Explode() would not work right) */
8052 player->shield_normal_time_left = 0;
8053 player->shield_deadly_time_left = 0;
8059 static void KillHeroUnlessProtected(int x, int y)
8061 if (!PLAYER_PROTECTED(x, y))
8062 KillHero(PLAYERINFO(x, y));
8065 void BuryHero(struct PlayerInfo *player)
8067 int jx = player->jx, jy = player->jy;
8069 if (!player->active)
8073 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8075 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8077 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8079 player->GameOver = TRUE;
8083 void RemoveHero(struct PlayerInfo *player)
8085 int jx = player->jx, jy = player->jy;
8086 int i, found = FALSE;
8088 player->present = FALSE;
8089 player->active = FALSE;
8091 if (!ExplodeField[jx][jy])
8092 StorePlayer[jx][jy] = 0;
8094 for (i = 0; i < MAX_PLAYERS; i++)
8095 if (stored_player[i].active)
8099 AllPlayersGone = TRUE;
8106 =============================================================================
8107 checkDiagonalPushing()
8108 -----------------------------------------------------------------------------
8109 check if diagonal input device direction results in pushing of object
8110 (by checking if the alternative direction is walkable, diggable, ...)
8111 =============================================================================
8114 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8115 int x, int y, int real_dx, int real_dy)
8117 int jx, jy, dx, dy, xx, yy;
8119 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8122 /* diagonal direction: check alternative direction */
8127 xx = jx + (dx == 0 ? real_dx : 0);
8128 yy = jy + (dy == 0 ? real_dy : 0);
8130 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8134 =============================================================================
8136 -----------------------------------------------------------------------------
8137 x, y: field next to player (non-diagonal) to try to dig to
8138 real_dx, real_dy: direction as read from input device (can be diagonal)
8139 =============================================================================
8142 int DigField(struct PlayerInfo *player,
8143 int x, int y, int real_dx, int real_dy, int mode)
8145 static int change_sides[4] =
8147 CH_SIDE_RIGHT, /* moving left */
8148 CH_SIDE_LEFT, /* moving right */
8149 CH_SIDE_BOTTOM, /* moving up */
8150 CH_SIDE_TOP, /* moving down */
8152 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8153 int jx = player->jx, jy = player->jy;
8154 int dx = x - jx, dy = y - jy;
8155 int nextx = x + dx, nexty = y + dy;
8156 int move_direction = (dx == -1 ? MV_LEFT :
8157 dx == +1 ? MV_RIGHT :
8159 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8160 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8163 if (player->MovPos == 0)
8165 player->is_digging = FALSE;
8166 player->is_collecting = FALSE;
8169 if (player->MovPos == 0) /* last pushing move finished */
8170 player->is_pushing = FALSE;
8172 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8174 player->is_switching = FALSE;
8175 player->push_delay = 0;
8177 return MF_NO_ACTION;
8180 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8181 return MF_NO_ACTION;
8184 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8186 if (IS_TUBE(Feld[jx][jy]) ||
8187 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8191 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8192 int tube_leave_directions[][2] =
8194 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8195 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8196 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8197 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8198 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8199 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8200 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8201 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8202 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8203 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8204 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8205 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8208 while (tube_leave_directions[i][0] != tube_element)
8211 if (tube_leave_directions[i][0] == -1) /* should not happen */
8215 if (!(tube_leave_directions[i][1] & move_direction))
8216 return MF_NO_ACTION; /* tube has no opening in this direction */
8219 element = Feld[x][y];
8221 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8222 game.engine_version >= VERSION_IDENT(2,2,0,0))
8223 return MF_NO_ACTION;
8227 case EL_SP_PORT_LEFT:
8228 case EL_SP_PORT_RIGHT:
8230 case EL_SP_PORT_DOWN:
8231 case EL_SP_PORT_HORIZONTAL:
8232 case EL_SP_PORT_VERTICAL:
8233 case EL_SP_PORT_ANY:
8234 case EL_SP_GRAVITY_PORT_LEFT:
8235 case EL_SP_GRAVITY_PORT_RIGHT:
8236 case EL_SP_GRAVITY_PORT_UP:
8237 case EL_SP_GRAVITY_PORT_DOWN:
8239 element != EL_SP_PORT_LEFT &&
8240 element != EL_SP_GRAVITY_PORT_LEFT &&
8241 element != EL_SP_PORT_HORIZONTAL &&
8242 element != EL_SP_PORT_ANY) ||
8244 element != EL_SP_PORT_RIGHT &&
8245 element != EL_SP_GRAVITY_PORT_RIGHT &&
8246 element != EL_SP_PORT_HORIZONTAL &&
8247 element != EL_SP_PORT_ANY) ||
8249 element != EL_SP_PORT_UP &&
8250 element != EL_SP_GRAVITY_PORT_UP &&
8251 element != EL_SP_PORT_VERTICAL &&
8252 element != EL_SP_PORT_ANY) ||
8254 element != EL_SP_PORT_DOWN &&
8255 element != EL_SP_GRAVITY_PORT_DOWN &&
8256 element != EL_SP_PORT_VERTICAL &&
8257 element != EL_SP_PORT_ANY) ||
8258 !IN_LEV_FIELD(nextx, nexty) ||
8259 !IS_FREE(nextx, nexty))
8260 return MF_NO_ACTION;
8262 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8263 element == EL_SP_GRAVITY_PORT_RIGHT ||
8264 element == EL_SP_GRAVITY_PORT_UP ||
8265 element == EL_SP_GRAVITY_PORT_DOWN)
8266 game.gravity = !game.gravity;
8268 /* automatically move to the next field with double speed */
8269 player->programmed_action = move_direction;
8270 DOUBLE_PLAYER_SPEED(player);
8272 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8276 case EL_TUBE_VERTICAL:
8277 case EL_TUBE_HORIZONTAL:
8278 case EL_TUBE_VERTICAL_LEFT:
8279 case EL_TUBE_VERTICAL_RIGHT:
8280 case EL_TUBE_HORIZONTAL_UP:
8281 case EL_TUBE_HORIZONTAL_DOWN:
8282 case EL_TUBE_LEFT_UP:
8283 case EL_TUBE_LEFT_DOWN:
8284 case EL_TUBE_RIGHT_UP:
8285 case EL_TUBE_RIGHT_DOWN:
8288 int tube_enter_directions[][2] =
8290 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8291 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8292 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8293 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8294 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8295 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8296 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8297 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8298 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8299 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8300 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8301 { -1, MV_NO_MOVING }
8304 while (tube_enter_directions[i][0] != element)
8307 if (tube_enter_directions[i][0] == -1) /* should not happen */
8311 if (!(tube_enter_directions[i][1] & move_direction))
8312 return MF_NO_ACTION; /* tube has no opening in this direction */
8314 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8320 if (IS_WALKABLE(element))
8322 int sound_action = ACTION_WALKING;
8324 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8326 if (!player->key[element - EL_GATE_1])
8327 return MF_NO_ACTION;
8329 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8331 if (!player->key[element - EL_GATE_1_GRAY])
8332 return MF_NO_ACTION;
8334 else if (element == EL_EXIT_OPEN ||
8335 element == EL_SP_EXIT_OPEN ||
8336 element == EL_SP_EXIT_OPENING)
8338 sound_action = ACTION_PASSING; /* player is passing exit */
8340 else if (element == EL_EMPTY)
8342 sound_action = ACTION_MOVING; /* nothing to walk on */
8345 /* play sound from background or player, whatever is available */
8346 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8347 PlayLevelSoundElementAction(x, y, element, sound_action);
8349 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8353 else if (IS_PASSABLE(element))
8355 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8356 return MF_NO_ACTION;
8359 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8360 return MF_NO_ACTION;
8363 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8365 if (!player->key[element - EL_EM_GATE_1])
8366 return MF_NO_ACTION;
8368 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8370 if (!player->key[element - EL_EM_GATE_1_GRAY])
8371 return MF_NO_ACTION;
8374 /* automatically move to the next field with double speed */
8375 player->programmed_action = move_direction;
8376 DOUBLE_PLAYER_SPEED(player);
8378 PlayLevelSoundAction(x, y, ACTION_PASSING);
8382 else if (IS_DIGGABLE(element))
8386 if (mode != DF_SNAP)
8389 GfxElement[x][y] = GFX_ELEMENT(element);
8392 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8394 player->is_digging = TRUE;
8397 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8399 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8402 if (mode == DF_SNAP)
8403 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8408 else if (IS_COLLECTIBLE(element))
8412 if (mode != DF_SNAP)
8414 GfxElement[x][y] = element;
8415 player->is_collecting = TRUE;
8418 if (element == EL_SPEED_PILL)
8419 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8420 else if (element == EL_EXTRA_TIME && level.time > 0)
8423 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8425 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8427 player->shield_normal_time_left += 10;
8428 if (element == EL_SHIELD_DEADLY)
8429 player->shield_deadly_time_left += 10;
8431 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8433 if (player->inventory_size < MAX_INVENTORY_SIZE)
8434 player->inventory_element[player->inventory_size++] = element;
8436 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8437 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8439 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8441 player->dynabomb_count++;
8442 player->dynabombs_left++;
8444 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8446 player->dynabomb_size++;
8448 else if (element == EL_DYNABOMB_INCREASE_POWER)
8450 player->dynabomb_xl = TRUE;
8452 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8453 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8455 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8456 element - EL_KEY_1 : element - EL_EM_KEY_1);
8458 player->key[key_nr] = TRUE;
8460 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8461 el2edimg(EL_KEY_1 + key_nr));
8462 redraw_mask |= REDRAW_DOOR_1;
8464 else if (IS_ENVELOPE(element))
8467 player->show_envelope = element;
8469 ShowEnvelope(element - EL_ENVELOPE_1);
8472 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8476 for (i = 0; i < element_info[element].collect_count; i++)
8477 if (player->inventory_size < MAX_INVENTORY_SIZE)
8478 player->inventory_element[player->inventory_size++] = element;
8480 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8481 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8483 else if (element_info[element].collect_count > 0)
8485 local_player->gems_still_needed -=
8486 element_info[element].collect_count;
8487 if (local_player->gems_still_needed < 0)
8488 local_player->gems_still_needed = 0;
8490 DrawText(DX_EMERALDS, DY_EMERALDS,
8491 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8494 RaiseScoreElement(element);
8495 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8497 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8500 if (mode == DF_SNAP)
8501 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8506 else if (IS_PUSHABLE(element))
8508 if (mode == DF_SNAP && element != EL_BD_ROCK)
8509 return MF_NO_ACTION;
8511 if (CAN_FALL(element) && dy)
8512 return MF_NO_ACTION;
8514 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8515 !(element == EL_SPRING && use_spring_bug))
8516 return MF_NO_ACTION;
8519 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8520 ((move_direction & MV_VERTICAL &&
8521 ((element_info[element].move_pattern & MV_LEFT &&
8522 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8523 (element_info[element].move_pattern & MV_RIGHT &&
8524 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8525 (move_direction & MV_HORIZONTAL &&
8526 ((element_info[element].move_pattern & MV_UP &&
8527 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8528 (element_info[element].move_pattern & MV_DOWN &&
8529 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8530 return MF_NO_ACTION;
8534 /* do not push elements already moving away faster than player */
8535 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8536 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8537 return MF_NO_ACTION;
8539 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8540 return MF_NO_ACTION;
8544 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8546 if (player->push_delay_value == -1)
8547 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8549 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8551 if (!player->is_pushing)
8552 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8556 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8557 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8558 !player_is_pushing))
8559 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8562 if (!player->is_pushing &&
8563 game.engine_version >= VERSION_IDENT(2,2,0,7))
8564 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8568 printf("::: push delay: %ld [%d, %d] [%d]\n",
8569 player->push_delay_value, FrameCounter, game.engine_version,
8570 player->is_pushing);
8573 player->is_pushing = TRUE;
8575 if (!(IN_LEV_FIELD(nextx, nexty) &&
8576 (IS_FREE(nextx, nexty) ||
8577 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8578 IS_SB_ELEMENT(element)))))
8579 return MF_NO_ACTION;
8581 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8582 return MF_NO_ACTION;
8584 if (player->push_delay == 0) /* new pushing; restart delay */
8585 player->push_delay = FrameCounter;
8587 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8588 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8589 element != EL_SPRING && element != EL_BALLOON)
8591 /* make sure that there is no move delay before next try to push */
8592 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8593 player->move_delay = INITIAL_MOVE_DELAY_OFF;
8595 return MF_NO_ACTION;
8599 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8602 if (IS_SB_ELEMENT(element))
8604 if (element == EL_SOKOBAN_FIELD_FULL)
8606 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8607 local_player->sokobanfields_still_needed++;
8610 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8612 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8613 local_player->sokobanfields_still_needed--;
8616 Feld[x][y] = EL_SOKOBAN_OBJECT;
8618 if (Back[x][y] == Back[nextx][nexty])
8619 PlayLevelSoundAction(x, y, ACTION_PUSHING);
8620 else if (Back[x][y] != 0)
8621 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8624 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8627 if (local_player->sokobanfields_still_needed == 0 &&
8628 game.emulation == EMU_SOKOBAN)
8630 player->LevelSolved = player->GameOver = TRUE;
8631 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8635 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8637 InitMovingField(x, y, move_direction);
8638 GfxAction[x][y] = ACTION_PUSHING;
8640 if (mode == DF_SNAP)
8641 ContinueMoving(x, y);
8643 MovPos[x][y] = (dx != 0 ? dx : dy);
8645 Pushed[x][y] = TRUE;
8646 Pushed[nextx][nexty] = TRUE;
8648 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8649 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8651 player->push_delay_value = -1; /* get new value later */
8653 CheckTriggeredElementSideChange(x, y, element, dig_side,
8654 CE_OTHER_GETS_PUSHED);
8655 CheckElementSideChange(x, y, element, dig_side,
8656 CE_PUSHED_BY_PLAYER, -1);
8660 else if (IS_SWITCHABLE(element))
8662 if (PLAYER_SWITCHING(player, x, y))
8665 player->is_switching = TRUE;
8666 player->switch_x = x;
8667 player->switch_y = y;
8669 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8671 if (element == EL_ROBOT_WHEEL)
8673 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8677 DrawLevelField(x, y);
8679 else if (element == EL_SP_TERMINAL)
8683 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8685 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8687 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8688 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8691 else if (IS_BELT_SWITCH(element))
8693 ToggleBeltSwitch(x, y);
8695 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8696 element == EL_SWITCHGATE_SWITCH_DOWN)
8698 ToggleSwitchgateSwitch(x, y);
8700 else if (element == EL_LIGHT_SWITCH ||
8701 element == EL_LIGHT_SWITCH_ACTIVE)
8703 ToggleLightSwitch(x, y);
8706 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8707 SND_LIGHT_SWITCH_ACTIVATING :
8708 SND_LIGHT_SWITCH_DEACTIVATING);
8711 else if (element == EL_TIMEGATE_SWITCH)
8713 ActivateTimegateSwitch(x, y);
8715 else if (element == EL_BALLOON_SWITCH_LEFT ||
8716 element == EL_BALLOON_SWITCH_RIGHT ||
8717 element == EL_BALLOON_SWITCH_UP ||
8718 element == EL_BALLOON_SWITCH_DOWN ||
8719 element == EL_BALLOON_SWITCH_ANY)
8721 if (element == EL_BALLOON_SWITCH_ANY)
8722 game.balloon_dir = move_direction;
8724 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8725 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8726 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8727 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8730 else if (element == EL_LAMP)
8732 Feld[x][y] = EL_LAMP_ACTIVE;
8733 local_player->lights_still_needed--;
8735 DrawLevelField(x, y);
8737 else if (element == EL_TIME_ORB_FULL)
8739 Feld[x][y] = EL_TIME_ORB_EMPTY;
8741 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8743 DrawLevelField(x, y);
8746 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8754 if (!PLAYER_SWITCHING(player, x, y))
8756 player->is_switching = TRUE;
8757 player->switch_x = x;
8758 player->switch_y = y;
8760 CheckTriggeredElementSideChange(x, y, element, dig_side,
8761 CE_OTHER_IS_SWITCHING);
8762 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8765 CheckTriggeredElementSideChange(x, y, element, dig_side,
8766 CE_OTHER_GETS_PRESSED);
8767 CheckElementSideChange(x, y, element, dig_side,
8768 CE_PRESSED_BY_PLAYER, -1);
8771 return MF_NO_ACTION;
8774 player->push_delay = 0;
8776 if (Feld[x][y] != element) /* really digged/collected something */
8777 player->is_collecting = !player->is_digging;
8782 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8784 int jx = player->jx, jy = player->jy;
8785 int x = jx + dx, y = jy + dy;
8786 int snap_direction = (dx == -1 ? MV_LEFT :
8787 dx == +1 ? MV_RIGHT :
8789 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8791 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8794 if (!player->active || !IN_LEV_FIELD(x, y))
8802 if (player->MovPos == 0)
8803 player->is_pushing = FALSE;
8805 player->is_snapping = FALSE;
8807 if (player->MovPos == 0)
8809 player->is_moving = FALSE;
8810 player->is_digging = FALSE;
8811 player->is_collecting = FALSE;
8817 if (player->is_snapping)
8820 player->MovDir = snap_direction;
8822 player->is_moving = FALSE;
8823 player->is_digging = FALSE;
8824 player->is_collecting = FALSE;
8826 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8829 player->is_snapping = TRUE;
8831 player->is_moving = FALSE;
8832 player->is_digging = FALSE;
8833 player->is_collecting = FALSE;
8835 DrawLevelField(x, y);
8841 boolean DropElement(struct PlayerInfo *player)
8843 int jx = player->jx, jy = player->jy;
8846 if (!player->active || player->MovPos)
8849 old_element = Feld[jx][jy];
8851 /* check if player has anything that can be dropped */
8852 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8855 /* check if anything can be dropped at the current position */
8856 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8859 /* collected custom elements can only be dropped on empty fields */
8860 if (player->inventory_size > 0 &&
8861 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8862 && old_element != EL_EMPTY)
8865 if (old_element != EL_EMPTY)
8866 Back[jx][jy] = old_element; /* store old element on this field */
8868 MovDelay[jx][jy] = 96;
8870 ResetGfxAnimation(jx, jy);
8871 ResetRandomAnimationValue(jx, jy);
8873 if (player->inventory_size > 0)
8875 int new_element = player->inventory_element[--player->inventory_size];
8877 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8878 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8881 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8882 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8884 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8885 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8887 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8889 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8890 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8892 TestIfElementTouchesCustomElement(jx, jy);
8894 else /* player is dropping a dyna bomb */
8896 player->dynabombs_left--;
8899 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8901 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8902 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8904 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8910 /* ------------------------------------------------------------------------- */
8911 /* game sound playing functions */
8912 /* ------------------------------------------------------------------------- */
8914 static int *loop_sound_frame = NULL;
8915 static int *loop_sound_volume = NULL;
8917 void InitPlayLevelSound()
8919 int num_sounds = getSoundListSize();
8921 checked_free(loop_sound_frame);
8922 checked_free(loop_sound_volume);
8924 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8925 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8928 static void PlayLevelSound(int x, int y, int nr)
8930 int sx = SCREENX(x), sy = SCREENY(y);
8931 int volume, stereo_position;
8932 int max_distance = 8;
8933 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8935 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8936 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8939 if (!IN_LEV_FIELD(x, y) ||
8940 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8941 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8944 volume = SOUND_MAX_VOLUME;
8946 if (!IN_SCR_FIELD(sx, sy))
8948 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8949 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8951 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8954 stereo_position = (SOUND_MAX_LEFT +
8955 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8956 (SCR_FIELDX + 2 * max_distance));
8958 if (IS_LOOP_SOUND(nr))
8960 /* This assures that quieter loop sounds do not overwrite louder ones,
8961 while restarting sound volume comparison with each new game frame. */
8963 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8966 loop_sound_volume[nr] = volume;
8967 loop_sound_frame[nr] = FrameCounter;
8970 PlaySoundExt(nr, volume, stereo_position, type);
8973 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8975 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8976 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8977 y < LEVELY(BY1) ? LEVELY(BY1) :
8978 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8982 static void PlayLevelSoundAction(int x, int y, int action)
8984 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8987 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8989 int sound_effect = element_info[element].sound[action];
8991 if (sound_effect != SND_UNDEFINED)
8992 PlayLevelSound(x, y, sound_effect);
8995 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
8998 int sound_effect = element_info[element].sound[action];
9000 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9001 PlayLevelSound(x, y, sound_effect);
9004 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9006 int sound_effect = element_info[Feld[x][y]].sound[action];
9008 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9009 PlayLevelSound(x, y, sound_effect);
9012 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9014 int sound_effect = element_info[Feld[x][y]].sound[action];
9016 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9017 StopSound(sound_effect);
9020 static void PlayLevelMusic()
9022 if (levelset.music[level_nr] != MUS_UNDEFINED)
9023 PlayMusic(levelset.music[level_nr]); /* from config file */
9025 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9028 void RaiseScore(int value)
9030 local_player->score += value;
9031 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9034 void RaiseScoreElement(int element)
9040 case EL_EMERALD_YELLOW:
9041 case EL_EMERALD_RED:
9042 case EL_EMERALD_PURPLE:
9043 case EL_SP_INFOTRON:
9044 RaiseScore(level.score[SC_EMERALD]);
9047 RaiseScore(level.score[SC_DIAMOND]);
9050 RaiseScore(level.score[SC_CRYSTAL]);
9053 RaiseScore(level.score[SC_PEARL]);
9056 case EL_BD_BUTTERFLY:
9057 case EL_SP_ELECTRON:
9058 RaiseScore(level.score[SC_BUG]);
9062 case EL_SP_SNIKSNAK:
9063 RaiseScore(level.score[SC_SPACESHIP]);
9066 case EL_DARK_YAMYAM:
9067 RaiseScore(level.score[SC_YAMYAM]);
9070 RaiseScore(level.score[SC_ROBOT]);
9073 RaiseScore(level.score[SC_PACMAN]);
9076 RaiseScore(level.score[SC_NUT]);
9079 case EL_SP_DISK_RED:
9080 case EL_DYNABOMB_INCREASE_NUMBER:
9081 case EL_DYNABOMB_INCREASE_SIZE:
9082 case EL_DYNABOMB_INCREASE_POWER:
9083 RaiseScore(level.score[SC_DYNAMITE]);
9085 case EL_SHIELD_NORMAL:
9086 case EL_SHIELD_DEADLY:
9087 RaiseScore(level.score[SC_SHIELD]);
9090 RaiseScore(level.score[SC_TIME_BONUS]);
9096 RaiseScore(level.score[SC_KEY]);
9099 RaiseScore(element_info[element].collect_score);
9104 void RequestQuitGame(boolean ask_if_really_quit)
9106 if (AllPlayersGone ||
9107 !ask_if_really_quit ||
9108 level_editor_test_game ||
9109 Request("Do you really want to quit the game ?",
9110 REQ_ASK | REQ_STAY_CLOSED))
9112 #if defined(PLATFORM_UNIX)
9113 if (options.network)
9114 SendToServer_StopPlaying();
9118 game_status = GAME_MODE_MAIN;
9124 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9129 /* ---------- new game button stuff ---------------------------------------- */
9131 /* graphic position values for game buttons */
9132 #define GAME_BUTTON_XSIZE 30
9133 #define GAME_BUTTON_YSIZE 30
9134 #define GAME_BUTTON_XPOS 5
9135 #define GAME_BUTTON_YPOS 215
9136 #define SOUND_BUTTON_XPOS 5
9137 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9139 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9140 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9141 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9142 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9143 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9144 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9151 } gamebutton_info[NUM_GAME_BUTTONS] =
9154 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9159 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9164 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9169 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9170 SOUND_CTRL_ID_MUSIC,
9171 "background music on/off"
9174 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9175 SOUND_CTRL_ID_LOOPS,
9176 "sound loops on/off"
9179 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9180 SOUND_CTRL_ID_SIMPLE,
9181 "normal sounds on/off"
9185 void CreateGameButtons()
9189 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9191 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9192 struct GadgetInfo *gi;
9195 unsigned long event_mask;
9196 int gd_xoffset, gd_yoffset;
9197 int gd_x1, gd_x2, gd_y1, gd_y2;
9200 gd_xoffset = gamebutton_info[i].x;
9201 gd_yoffset = gamebutton_info[i].y;
9202 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9203 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9205 if (id == GAME_CTRL_ID_STOP ||
9206 id == GAME_CTRL_ID_PAUSE ||
9207 id == GAME_CTRL_ID_PLAY)
9209 button_type = GD_TYPE_NORMAL_BUTTON;
9211 event_mask = GD_EVENT_RELEASED;
9212 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9213 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9217 button_type = GD_TYPE_CHECK_BUTTON;
9219 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9220 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9221 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9222 event_mask = GD_EVENT_PRESSED;
9223 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9224 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9227 gi = CreateGadget(GDI_CUSTOM_ID, id,
9228 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9229 GDI_X, DX + gd_xoffset,
9230 GDI_Y, DY + gd_yoffset,
9231 GDI_WIDTH, GAME_BUTTON_XSIZE,
9232 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9233 GDI_TYPE, button_type,
9234 GDI_STATE, GD_BUTTON_UNPRESSED,
9235 GDI_CHECKED, checked,
9236 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9237 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9238 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9239 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9240 GDI_EVENT_MASK, event_mask,
9241 GDI_CALLBACK_ACTION, HandleGameButtons,
9245 Error(ERR_EXIT, "cannot create gadget");
9247 game_gadget[id] = gi;
9251 void FreeGameButtons()
9255 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9256 FreeGadget(game_gadget[i]);
9259 static void MapGameButtons()
9263 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9264 MapGadget(game_gadget[i]);
9267 void UnmapGameButtons()
9271 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9272 UnmapGadget(game_gadget[i]);
9275 static void HandleGameButtons(struct GadgetInfo *gi)
9277 int id = gi->custom_id;
9279 if (game_status != GAME_MODE_PLAYING)
9284 case GAME_CTRL_ID_STOP:
9285 RequestQuitGame(TRUE);
9288 case GAME_CTRL_ID_PAUSE:
9289 if (options.network)
9291 #if defined(PLATFORM_UNIX)
9293 SendToServer_ContinuePlaying();
9295 SendToServer_PausePlaying();
9299 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9302 case GAME_CTRL_ID_PLAY:
9305 #if defined(PLATFORM_UNIX)
9306 if (options.network)
9307 SendToServer_ContinuePlaying();
9311 tape.pausing = FALSE;
9312 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9317 case SOUND_CTRL_ID_MUSIC:
9318 if (setup.sound_music)
9320 setup.sound_music = FALSE;
9323 else if (audio.music_available)
9325 setup.sound = setup.sound_music = TRUE;
9327 SetAudioMode(setup.sound);
9333 case SOUND_CTRL_ID_LOOPS:
9334 if (setup.sound_loops)
9335 setup.sound_loops = FALSE;
9336 else if (audio.loops_available)
9338 setup.sound = setup.sound_loops = TRUE;
9339 SetAudioMode(setup.sound);
9343 case SOUND_CTRL_ID_SIMPLE:
9344 if (setup.sound_simple)
9345 setup.sound_simple = FALSE;
9346 else if (audio.sound_available)
9348 setup.sound = setup.sound_simple = TRUE;
9349 SetAudioMode(setup.sound);