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))
102 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
103 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
105 (DONT_COLLIDE_WITH(e) && \
106 IS_FREE_OR_PLAYER(x, y))))
108 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
109 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
112 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
113 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
115 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
116 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
118 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
119 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
121 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
123 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
124 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
125 Feld[x][y] == EL_DIAMOND))
127 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
128 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
129 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
131 #define PACMAN_CAN_ENTER_FIELD(x, y) \
132 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
133 IS_AMOEBOID(Feld[x][y])))
135 #define PIG_CAN_ENTER_FIELD(x, y) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
137 IS_FOOD_PIG(Feld[x][y])))
139 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 IS_FOOD_PENGUIN(Feld[x][y]) || \
142 Feld[x][y] == EL_EXIT_OPEN || \
143 Feld[x][y] == EL_ACID))
145 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
148 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
149 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
151 /* game button identifiers */
152 #define GAME_CTRL_ID_STOP 0
153 #define GAME_CTRL_ID_PAUSE 1
154 #define GAME_CTRL_ID_PLAY 2
155 #define SOUND_CTRL_ID_MUSIC 3
156 #define SOUND_CTRL_ID_LOOPS 4
157 #define SOUND_CTRL_ID_SIMPLE 5
159 #define NUM_GAME_BUTTONS 6
162 /* forward declaration for internal use */
164 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
165 static boolean MovePlayer(struct PlayerInfo *, int, int);
166 static void ScrollPlayer(struct PlayerInfo *, int);
167 static void ScrollScreen(struct PlayerInfo *, int);
169 static void InitBeltMovement(void);
170 static void CloseAllOpenTimegates(void);
171 static void CheckGravityMovement(struct PlayerInfo *);
172 static void KillHeroUnlessProtected(int, int);
174 static void TestIfPlayerTouchesCustomElement(int, int);
175 static void TestIfElementTouchesCustomElement(int, int);
177 static void ChangeElement(int, int, int);
178 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
179 static boolean CheckTriggeredElementChange(int, int, int, int);
180 static boolean CheckElementSideChange(int, int, int, int, int, int);
181 static boolean CheckElementChange(int, int, int, int);
183 static void PlaySoundLevel(int, int, int);
184 static void PlaySoundLevelNearest(int, int, int);
185 static void PlaySoundLevelAction(int, int, int);
186 static void PlaySoundLevelElementAction(int, int, int, int);
187 static void PlaySoundLevelActionIfLoop(int, int, int);
188 static void StopSoundLevelActionIfLoop(int, int, int);
190 static void MapGameButtons();
191 static void HandleGameButtons(struct GadgetInfo *);
193 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
196 /* ------------------------------------------------------------------------- */
197 /* definition of elements that automatically change to other elements after */
198 /* a specified time, eventually calling a function when changing */
199 /* ------------------------------------------------------------------------- */
201 /* forward declaration for changer functions */
202 static void InitBuggyBase(int x, int y);
203 static void WarnBuggyBase(int x, int y);
205 static void InitTrap(int x, int y);
206 static void ActivateTrap(int x, int y);
207 static void ChangeActiveTrap(int x, int y);
209 static void InitRobotWheel(int x, int y);
210 static void RunRobotWheel(int x, int y);
211 static void StopRobotWheel(int x, int y);
213 static void InitTimegateWheel(int x, int y);
214 static void RunTimegateWheel(int x, int y);
216 struct ChangingElementInfo
221 void (*pre_change_function)(int x, int y);
222 void (*change_function)(int x, int y);
223 void (*post_change_function)(int x, int y);
226 static struct ChangingElementInfo change_delay_list[] =
277 EL_SWITCHGATE_OPENING,
285 EL_SWITCHGATE_CLOSING,
286 EL_SWITCHGATE_CLOSED,
318 EL_ACID_SPLASH_RIGHT,
327 EL_SP_BUGGY_BASE_ACTIVATING,
334 EL_SP_BUGGY_BASE_ACTIVATING,
335 EL_SP_BUGGY_BASE_ACTIVE,
342 EL_SP_BUGGY_BASE_ACTIVE,
366 EL_ROBOT_WHEEL_ACTIVE,
374 EL_TIMEGATE_SWITCH_ACTIVE,
395 int push_delay_fixed, push_delay_random;
400 { EL_BALLOON, 0, 0 },
402 { EL_SOKOBAN_OBJECT, 2, 0 },
403 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
404 { EL_SATELLITE, 2, 0 },
405 { EL_SP_DISK_YELLOW, 2, 0 },
407 { EL_UNDEFINED, 0, 0 },
415 move_stepsize_list[] =
417 { EL_AMOEBA_DROP, 2 },
418 { EL_AMOEBA_DROPPING, 2 },
419 { EL_QUICKSAND_FILLING, 1 },
420 { EL_QUICKSAND_EMPTYING, 1 },
421 { EL_MAGIC_WALL_FILLING, 2 },
422 { EL_BD_MAGIC_WALL_FILLING, 2 },
423 { EL_MAGIC_WALL_EMPTYING, 2 },
424 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
434 collect_count_list[] =
437 { EL_BD_DIAMOND, 1 },
438 { EL_EMERALD_YELLOW, 1 },
439 { EL_EMERALD_RED, 1 },
440 { EL_EMERALD_PURPLE, 1 },
442 { EL_SP_INFOTRON, 1 },
449 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
451 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
452 CH_EVENT_BIT(CE_DELAY))
453 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
454 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
455 IS_JUST_CHANGING(x, y))
457 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
460 void GetPlayerConfig()
462 if (!audio.sound_available)
463 setup.sound_simple = FALSE;
465 if (!audio.loops_available)
466 setup.sound_loops = FALSE;
468 if (!audio.music_available)
469 setup.sound_music = FALSE;
471 if (!video.fullscreen_available)
472 setup.fullscreen = FALSE;
474 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
476 SetAudioMode(setup.sound);
480 static int getBeltNrFromBeltElement(int element)
482 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
483 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
484 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
487 static int getBeltNrFromBeltActiveElement(int element)
489 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
490 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
491 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
494 static int getBeltNrFromBeltSwitchElement(int element)
496 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
497 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
498 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
501 static int getBeltDirNrFromBeltSwitchElement(int element)
503 static int belt_base_element[4] =
505 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
506 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
507 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_4_SWITCH_LEFT
511 int belt_nr = getBeltNrFromBeltSwitchElement(element);
512 int belt_dir_nr = element - belt_base_element[belt_nr];
514 return (belt_dir_nr % 3);
517 static int getBeltDirFromBeltSwitchElement(int element)
519 static int belt_move_dir[3] =
526 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
528 return belt_move_dir[belt_dir_nr];
531 static void InitPlayerField(int x, int y, int element, boolean init_game)
533 if (element == EL_SP_MURPHY)
537 if (stored_player[0].present)
539 Feld[x][y] = EL_SP_MURPHY_CLONE;
545 stored_player[0].use_murphy_graphic = TRUE;
548 Feld[x][y] = EL_PLAYER_1;
554 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
555 int jx = player->jx, jy = player->jy;
557 player->present = TRUE;
559 if (!options.network || player->connected)
561 player->active = TRUE;
563 /* remove potentially duplicate players */
564 if (StorePlayer[jx][jy] == Feld[x][y])
565 StorePlayer[jx][jy] = 0;
567 StorePlayer[x][y] = Feld[x][y];
571 printf("Player %d activated.\n", player->element_nr);
572 printf("[Local player is %d and currently %s.]\n",
573 local_player->element_nr,
574 local_player->active ? "active" : "not active");
578 Feld[x][y] = EL_EMPTY;
579 player->jx = player->last_jx = x;
580 player->jy = player->last_jy = y;
584 static void InitField(int x, int y, boolean init_game)
586 int element = Feld[x][y];
595 InitPlayerField(x, y, element, init_game);
599 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
600 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
601 else if (x > 0 && Feld[x-1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
603 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
604 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID)
606 Feld[x][y] = EL_ACID_POOL_BOTTOM;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
608 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
616 case EL_SPACESHIP_RIGHT:
617 case EL_SPACESHIP_UP:
618 case EL_SPACESHIP_LEFT:
619 case EL_SPACESHIP_DOWN:
621 case EL_BD_BUTTERFLY_RIGHT:
622 case EL_BD_BUTTERFLY_UP:
623 case EL_BD_BUTTERFLY_LEFT:
624 case EL_BD_BUTTERFLY_DOWN:
625 case EL_BD_BUTTERFLY:
626 case EL_BD_FIREFLY_RIGHT:
627 case EL_BD_FIREFLY_UP:
628 case EL_BD_FIREFLY_LEFT:
629 case EL_BD_FIREFLY_DOWN:
631 case EL_PACMAN_RIGHT:
655 if (y == lev_fieldy - 1)
657 Feld[x][y] = EL_AMOEBA_GROWING;
658 Store[x][y] = EL_AMOEBA_WET;
662 case EL_DYNAMITE_ACTIVE:
667 local_player->lights_still_needed++;
670 case EL_SOKOBAN_FIELD_EMPTY:
671 local_player->sokobanfields_still_needed++;
675 local_player->friends_still_needed++;
680 MovDir[x][y] = 1 << RND(4);
685 Feld[x][y] = EL_EMPTY;
690 case EL_EM_KEY_1_FILE:
691 Feld[x][y] = EL_EM_KEY_1;
693 case EL_EM_KEY_2_FILE:
694 Feld[x][y] = EL_EM_KEY_2;
696 case EL_EM_KEY_3_FILE:
697 Feld[x][y] = EL_EM_KEY_3;
699 case EL_EM_KEY_4_FILE:
700 Feld[x][y] = EL_EM_KEY_4;
704 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
705 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
706 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
707 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
708 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
709 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
710 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
711 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
712 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
713 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
714 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
715 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
718 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
719 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
720 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
722 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
724 game.belt_dir[belt_nr] = belt_dir;
725 game.belt_dir_nr[belt_nr] = belt_dir_nr;
727 else /* more than one switch -- set it like the first switch */
729 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
734 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
736 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
739 case EL_LIGHT_SWITCH_ACTIVE:
741 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
745 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
751 void DrawGameDoorValues()
755 for (i=0; i<MAX_PLAYERS; i++)
757 if (stored_player[i].key[j])
758 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
759 el2edimg(EL_KEY_1 + j));
761 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
762 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
763 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
764 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
765 DrawText(DX + XX_SCORE, DY + YY_SCORE,
766 int2str(local_player->score, 5), FONT_TEXT_2);
767 DrawText(DX + XX_TIME, DY + YY_TIME,
768 int2str(TimeLeft, 3), FONT_TEXT_2);
773 =============================================================================
775 -----------------------------------------------------------------------------
776 initialize game engine due to level / tape version number
777 =============================================================================
780 static void InitGameEngine()
784 /* set game engine from tape file when re-playing, else from level file */
785 game.engine_version = (tape.playing ? tape.engine_version :
788 /* dynamically adjust element properties according to game engine version */
789 InitElementPropertiesEngine(game.engine_version);
792 printf("level %d: level version == %06d\n", level_nr, level.game_version);
793 printf(" tape version == %06d [%s] [file: %06d]\n",
794 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
796 printf(" => game.engine_version == %06d\n", game.engine_version);
799 /* ---------- initialize player's initial move delay --------------------- */
801 /* dynamically adjust player properties according to game engine version */
802 game.initial_move_delay =
803 (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
804 INITIAL_MOVE_DELAY_OFF);
806 /* dynamically adjust player properties according to level information */
807 game.initial_move_delay_value =
808 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
810 /* ---------- initialize changing elements ------------------------------- */
812 /* initialize changing elements information */
813 for (i=0; i < MAX_NUM_ELEMENTS; i++)
815 struct ElementInfo *ei = &element_info[i];
817 /* this pointer might have been changed in the level editor */
818 ei->change = &ei->change_page[0];
820 if (!IS_CUSTOM_ELEMENT(i))
822 ei->change->target_element = EL_EMPTY_SPACE;
823 ei->change->delay_fixed = 0;
824 ei->change->delay_random = 0;
825 ei->change->delay_frames = 1;
828 ei->change_events = CE_BITMASK_DEFAULT;
829 for (j=0; j < NUM_CHANGE_EVENTS; j++)
831 ei->event_page_nr[j] = 0;
832 ei->event_page[j] = &ei->change_page[0];
836 /* add changing elements from pre-defined list */
837 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
839 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
840 struct ElementInfo *ei = &element_info[ch_delay->element];
842 ei->change->target_element = ch_delay->target_element;
843 ei->change->delay_fixed = ch_delay->change_delay;
845 ei->change->pre_change_function = ch_delay->pre_change_function;
846 ei->change->change_function = ch_delay->change_function;
847 ei->change->post_change_function = ch_delay->post_change_function;
849 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
853 /* add change events from custom element configuration */
854 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
856 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
858 for (j=0; j < ei->num_change_pages; j++)
860 if (!ei->change_page[j].can_change)
863 for (k=0; k < NUM_CHANGE_EVENTS; k++)
865 /* only add event page for the first page found with this event */
866 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
867 !(ei->change_events & CH_EVENT_BIT(k)))
869 ei->change_events |= CH_EVENT_BIT(k);
870 ei->event_page_nr[k] = j;
871 ei->event_page[k] = &ei->change_page[j];
879 /* add change events from custom element configuration */
880 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
882 int element = EL_CUSTOM_START + i;
884 /* only add custom elements that change after fixed/random frame delay */
885 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
886 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
890 /* ---------- initialize trigger events ---------------------------------- */
892 /* initialize trigger events information */
893 for (i=0; i<MAX_NUM_ELEMENTS; i++)
894 trigger_events[i] = EP_BITMASK_DEFAULT;
897 /* add trigger events from element change event properties */
898 for (i=0; i<MAX_NUM_ELEMENTS; i++)
900 struct ElementInfo *ei = &element_info[i];
902 for (j=0; j < ei->num_change_pages; j++)
904 if (!ei->change_page[j].can_change)
907 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
909 int trigger_element = ei->change_page[j].trigger_element;
911 trigger_events[trigger_element] |= ei->change_page[j].events;
916 /* add trigger events from element change event properties */
917 for (i=0; i<MAX_NUM_ELEMENTS; i++)
918 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
919 trigger_events[element_info[i].change->trigger_element] |=
920 element_info[i].change->events;
923 /* ---------- initialize push delay -------------------------------------- */
925 /* initialize push delay values to default */
926 for (i=0; i<MAX_NUM_ELEMENTS; i++)
928 if (!IS_CUSTOM_ELEMENT(i))
930 element_info[i].push_delay_fixed = 2;
931 element_info[i].push_delay_random = 8;
935 /* set push delay value for certain elements from pre-defined list */
936 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
938 int e = push_delay_list[i].element;
940 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
941 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
944 /* ---------- initialize move stepsize ----------------------------------- */
946 /* initialize move stepsize values to default */
947 for (i=0; i<MAX_NUM_ELEMENTS; i++)
948 if (!IS_CUSTOM_ELEMENT(i))
949 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
951 /* set move stepsize value for certain elements from pre-defined list */
952 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
954 int e = move_stepsize_list[i].element;
956 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
959 /* ---------- initialize gem count --------------------------------------- */
961 /* initialize gem count values for each element */
962 for (i=0; i<MAX_NUM_ELEMENTS; i++)
963 if (!IS_CUSTOM_ELEMENT(i))
964 element_info[i].collect_count = 0;
966 /* add gem count values for all elements from pre-defined list */
967 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
968 element_info[collect_count_list[i].element].collect_count =
969 collect_count_list[i].count;
974 =============================================================================
976 -----------------------------------------------------------------------------
977 initialize and start new game
978 =============================================================================
983 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
984 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
985 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
992 #if USE_NEW_AMOEBA_CODE
993 printf("Using new amoeba code.\n");
995 printf("Using old amoeba code.\n");
1000 /* don't play tapes over network */
1001 network_playing = (options.network && !tape.playing);
1003 for (i=0; i<MAX_PLAYERS; i++)
1005 struct PlayerInfo *player = &stored_player[i];
1007 player->index_nr = i;
1008 player->element_nr = EL_PLAYER_1 + i;
1010 player->present = FALSE;
1011 player->active = FALSE;
1014 player->effective_action = 0;
1015 player->programmed_action = 0;
1018 player->gems_still_needed = level.gems_needed;
1019 player->sokobanfields_still_needed = 0;
1020 player->lights_still_needed = 0;
1021 player->friends_still_needed = 0;
1024 player->key[j] = FALSE;
1026 player->dynabomb_count = 0;
1027 player->dynabomb_size = 1;
1028 player->dynabombs_left = 0;
1029 player->dynabomb_xl = FALSE;
1031 player->MovDir = MV_NO_MOVING;
1034 player->GfxDir = MV_NO_MOVING;
1035 player->GfxAction = ACTION_DEFAULT;
1037 player->StepFrame = 0;
1039 player->use_murphy_graphic = FALSE;
1041 player->actual_frame_counter = 0;
1043 player->last_move_dir = MV_NO_MOVING;
1045 player->is_waiting = FALSE;
1046 player->is_moving = FALSE;
1047 player->is_digging = FALSE;
1048 player->is_snapping = FALSE;
1049 player->is_collecting = FALSE;
1050 player->is_pushing = FALSE;
1051 player->is_switching = FALSE;
1053 player->switch_x = -1;
1054 player->switch_y = -1;
1056 player->show_envelope = 0;
1058 player->move_delay = game.initial_move_delay;
1059 player->move_delay_value = game.initial_move_delay_value;
1061 player->push_delay = 0;
1062 player->push_delay_value = 5;
1064 player->last_jx = player->last_jy = 0;
1065 player->jx = player->jy = 0;
1067 player->shield_normal_time_left = 0;
1068 player->shield_deadly_time_left = 0;
1070 player->inventory_size = 0;
1072 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1073 SnapField(player, 0, 0);
1075 player->LevelSolved = FALSE;
1076 player->GameOver = FALSE;
1079 network_player_action_received = FALSE;
1081 #if defined(PLATFORM_UNIX)
1082 /* initial null action */
1083 if (network_playing)
1084 SendToServer_MovePlayer(MV_NO_MOVING);
1092 TimeLeft = level.time;
1094 ScreenMovDir = MV_NO_MOVING;
1098 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1100 AllPlayersGone = FALSE;
1102 game.yamyam_content_nr = 0;
1103 game.magic_wall_active = FALSE;
1104 game.magic_wall_time_left = 0;
1105 game.light_time_left = 0;
1106 game.timegate_time_left = 0;
1107 game.switchgate_pos = 0;
1108 game.balloon_dir = MV_NO_MOVING;
1109 game.gravity = level.initial_gravity;
1110 game.explosions_delayed = TRUE;
1112 game.envelope_active = FALSE;
1116 game.belt_dir[i] = MV_NO_MOVING;
1117 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1120 for (i=0; i<MAX_NUM_AMOEBA; i++)
1121 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1123 for (x=0; x<lev_fieldx; x++)
1125 for (y=0; y<lev_fieldy; y++)
1127 Feld[x][y] = level.field[x][y];
1128 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1129 ChangeDelay[x][y] = 0;
1130 ChangePage[x][y] = -1;
1131 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1133 WasJustMoving[x][y] = 0;
1134 WasJustFalling[x][y] = 0;
1136 Pushed[x][y] = FALSE;
1138 Changed[x][y] = CE_BITMASK_DEFAULT;
1139 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1141 ExplodePhase[x][y] = 0;
1142 ExplodeField[x][y] = EX_NO_EXPLOSION;
1145 GfxAction[x][y] = ACTION_DEFAULT;
1146 GfxRandom[x][y] = INIT_GFX_RANDOM();
1147 GfxElement[x][y] = EL_UNDEFINED;
1151 for(y=0; y<lev_fieldy; y++)
1153 for(x=0; x<lev_fieldx; x++)
1155 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1157 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1159 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1162 InitField(x, y, TRUE);
1168 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1169 emulate_sb ? EMU_SOKOBAN :
1170 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1172 /* correct non-moving belts to start moving left */
1174 if (game.belt_dir[i] == MV_NO_MOVING)
1175 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1177 /* check if any connected player was not found in playfield */
1178 for (i=0; i<MAX_PLAYERS; i++)
1180 struct PlayerInfo *player = &stored_player[i];
1182 if (player->connected && !player->present)
1184 for (j=0; j<MAX_PLAYERS; j++)
1186 struct PlayerInfo *some_player = &stored_player[j];
1187 int jx = some_player->jx, jy = some_player->jy;
1189 /* assign first free player found that is present in the playfield */
1190 if (some_player->present && !some_player->connected)
1192 player->present = TRUE;
1193 player->active = TRUE;
1194 some_player->present = FALSE;
1196 StorePlayer[jx][jy] = player->element_nr;
1197 player->jx = player->last_jx = jx;
1198 player->jy = player->last_jy = jy;
1208 /* when playing a tape, eliminate all players who do not participate */
1210 for (i=0; i<MAX_PLAYERS; i++)
1212 if (stored_player[i].active && !tape.player_participates[i])
1214 struct PlayerInfo *player = &stored_player[i];
1215 int jx = player->jx, jy = player->jy;
1217 player->active = FALSE;
1218 StorePlayer[jx][jy] = 0;
1219 Feld[jx][jy] = EL_EMPTY;
1223 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1225 /* when in single player mode, eliminate all but the first active player */
1227 for (i=0; i<MAX_PLAYERS; i++)
1229 if (stored_player[i].active)
1231 for (j=i+1; j<MAX_PLAYERS; j++)
1233 if (stored_player[j].active)
1235 struct PlayerInfo *player = &stored_player[j];
1236 int jx = player->jx, jy = player->jy;
1238 player->active = FALSE;
1239 StorePlayer[jx][jy] = 0;
1240 Feld[jx][jy] = EL_EMPTY;
1247 /* when recording the game, store which players take part in the game */
1250 for (i=0; i<MAX_PLAYERS; i++)
1251 if (stored_player[i].active)
1252 tape.player_participates[i] = TRUE;
1257 for (i=0; i<MAX_PLAYERS; i++)
1259 struct PlayerInfo *player = &stored_player[i];
1261 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1266 if (local_player == player)
1267 printf("Player %d is local player.\n", i+1);
1271 if (BorderElement == EL_EMPTY)
1274 SBX_Right = lev_fieldx - SCR_FIELDX;
1276 SBY_Lower = lev_fieldy - SCR_FIELDY;
1281 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1283 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1286 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1287 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1289 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1290 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1292 /* if local player not found, look for custom element that might create
1293 the player (make some assumptions about the right custom element) */
1294 if (!local_player->present)
1296 int start_x = 0, start_y = 0;
1297 int found_rating = 0;
1298 int found_element = EL_UNDEFINED;
1300 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1302 int element = Feld[x][y];
1307 if (!IS_CUSTOM_ELEMENT(element))
1310 if (CAN_CHANGE(element))
1312 for (i=0; i < element_info[element].num_change_pages; i++)
1314 content = element_info[element].change_page[i].target_element;
1315 is_player = ELEM_IS_PLAYER(content);
1317 if (is_player && (found_rating < 3 || element < found_element))
1323 found_element = element;
1328 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1330 content = element_info[element].content[xx][yy];
1331 is_player = ELEM_IS_PLAYER(content);
1333 if (is_player && (found_rating < 2 || element < found_element))
1335 start_x = x + xx - 1;
1336 start_y = y + yy - 1;
1339 found_element = element;
1342 if (!CAN_CHANGE(element))
1345 for (i=0; i < element_info[element].num_change_pages; i++)
1347 content = element_info[element].change_page[i].content[xx][yy];
1348 is_player = ELEM_IS_PLAYER(content);
1350 if (is_player && (found_rating < 1 || element < found_element))
1352 start_x = x + xx - 1;
1353 start_y = y + yy - 1;
1356 found_element = element;
1362 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1363 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1366 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1367 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1373 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1374 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1375 local_player->jx - MIDPOSX);
1377 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1378 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1379 local_player->jy - MIDPOSY);
1381 scroll_x = SBX_Left;
1382 scroll_y = SBY_Upper;
1383 if (local_player->jx >= SBX_Left + MIDPOSX)
1384 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1385 local_player->jx - MIDPOSX :
1387 if (local_player->jy >= SBY_Upper + MIDPOSY)
1388 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1389 local_player->jy - MIDPOSY :
1394 CloseDoor(DOOR_CLOSE_1);
1399 /* after drawing the level, correct some elements */
1400 if (game.timegate_time_left == 0)
1401 CloseAllOpenTimegates();
1403 if (setup.soft_scrolling)
1404 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1406 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1409 /* copy default game door content to main double buffer */
1410 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1411 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1414 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1417 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1418 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1419 BlitBitmap(drawto, drawto,
1420 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1421 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1422 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1423 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1426 DrawGameDoorValues();
1430 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1431 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1432 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1436 /* copy actual game door content to door double buffer for OpenDoor() */
1437 BlitBitmap(drawto, bitmap_db_door,
1438 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1440 OpenDoor(DOOR_OPEN_ALL);
1442 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1443 if (setup.sound_music)
1444 PlayMusic(level_nr);
1446 KeyboardAutoRepeatOffUnlessAutoplay();
1451 printf("Player %d %sactive.\n",
1452 i + 1, (stored_player[i].active ? "" : "not "));
1456 void InitMovDir(int x, int y)
1458 int i, element = Feld[x][y];
1459 static int xy[4][2] =
1466 static int direction[3][4] =
1468 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1469 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1470 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1479 Feld[x][y] = EL_BUG;
1480 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1483 case EL_SPACESHIP_RIGHT:
1484 case EL_SPACESHIP_UP:
1485 case EL_SPACESHIP_LEFT:
1486 case EL_SPACESHIP_DOWN:
1487 Feld[x][y] = EL_SPACESHIP;
1488 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1491 case EL_BD_BUTTERFLY_RIGHT:
1492 case EL_BD_BUTTERFLY_UP:
1493 case EL_BD_BUTTERFLY_LEFT:
1494 case EL_BD_BUTTERFLY_DOWN:
1495 Feld[x][y] = EL_BD_BUTTERFLY;
1496 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1499 case EL_BD_FIREFLY_RIGHT:
1500 case EL_BD_FIREFLY_UP:
1501 case EL_BD_FIREFLY_LEFT:
1502 case EL_BD_FIREFLY_DOWN:
1503 Feld[x][y] = EL_BD_FIREFLY;
1504 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1507 case EL_PACMAN_RIGHT:
1509 case EL_PACMAN_LEFT:
1510 case EL_PACMAN_DOWN:
1511 Feld[x][y] = EL_PACMAN;
1512 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1515 case EL_SP_SNIKSNAK:
1516 MovDir[x][y] = MV_UP;
1519 case EL_SP_ELECTRON:
1520 MovDir[x][y] = MV_LEFT;
1527 Feld[x][y] = EL_MOLE;
1528 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1532 if (IS_CUSTOM_ELEMENT(element))
1534 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1535 MovDir[x][y] = element_info[element].move_direction_initial;
1536 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1537 element_info[element].move_pattern == MV_TURNING_LEFT ||
1538 element_info[element].move_pattern == MV_TURNING_RIGHT)
1539 MovDir[x][y] = 1 << RND(4);
1540 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1541 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1542 else if (element_info[element].move_pattern == MV_VERTICAL)
1543 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1544 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1545 MovDir[x][y] = element_info[element].move_pattern;
1546 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1547 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1551 int x1 = x + xy[i][0];
1552 int y1 = y + xy[i][1];
1554 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1556 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1557 MovDir[x][y] = direction[0][i];
1559 MovDir[x][y] = direction[1][i];
1568 MovDir[x][y] = 1 << RND(4);
1570 if (element != EL_BUG &&
1571 element != EL_SPACESHIP &&
1572 element != EL_BD_BUTTERFLY &&
1573 element != EL_BD_FIREFLY)
1578 int x1 = x + xy[i][0];
1579 int y1 = y + xy[i][1];
1581 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1583 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1585 MovDir[x][y] = direction[0][i];
1588 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1589 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1591 MovDir[x][y] = direction[1][i];
1601 void InitAmoebaNr(int x, int y)
1604 int group_nr = AmoebeNachbarNr(x, y);
1608 for (i=1; i<MAX_NUM_AMOEBA; i++)
1610 if (AmoebaCnt[i] == 0)
1618 AmoebaNr[x][y] = group_nr;
1619 AmoebaCnt[group_nr]++;
1620 AmoebaCnt2[group_nr]++;
1626 boolean raise_level = FALSE;
1628 if (local_player->MovPos)
1632 if (tape.auto_play) /* tape might already be stopped here */
1633 tape.auto_play_level_solved = TRUE;
1635 if (tape.playing && tape.auto_play)
1636 tape.auto_play_level_solved = TRUE;
1639 local_player->LevelSolved = FALSE;
1641 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1645 if (!tape.playing && setup.sound_loops)
1646 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1647 SND_CTRL_PLAY_LOOP);
1649 while (TimeLeft > 0)
1651 if (!tape.playing && !setup.sound_loops)
1652 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1653 if (TimeLeft > 0 && !(TimeLeft % 10))
1654 RaiseScore(level.score[SC_TIME_BONUS]);
1655 if (TimeLeft > 100 && !(TimeLeft % 10))
1659 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1666 if (!tape.playing && setup.sound_loops)
1667 StopSound(SND_GAME_LEVELTIME_BONUS);
1669 else if (level.time == 0) /* level without time limit */
1671 if (!tape.playing && setup.sound_loops)
1672 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1673 SND_CTRL_PLAY_LOOP);
1675 while (TimePlayed < 999)
1677 if (!tape.playing && !setup.sound_loops)
1678 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1679 if (TimePlayed < 999 && !(TimePlayed % 10))
1680 RaiseScore(level.score[SC_TIME_BONUS]);
1681 if (TimePlayed < 900 && !(TimePlayed % 10))
1685 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1692 if (!tape.playing && setup.sound_loops)
1693 StopSound(SND_GAME_LEVELTIME_BONUS);
1696 /* close exit door after last player */
1697 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1698 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1700 int element = Feld[ExitX][ExitY];
1702 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1703 EL_SP_EXIT_CLOSING);
1705 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1708 /* Hero disappears */
1709 DrawLevelField(ExitX, ExitY);
1715 CloseDoor(DOOR_CLOSE_1);
1720 SaveTape(tape.level_nr); /* Ask to save tape */
1723 if (level_nr == leveldir_current->handicap_level)
1725 leveldir_current->handicap_level++;
1726 SaveLevelSetup_SeriesInfo();
1729 if (level_editor_test_game)
1730 local_player->score = -1; /* no highscore when playing from editor */
1731 else if (level_nr < leveldir_current->last_level)
1732 raise_level = TRUE; /* advance to next level */
1734 if ((hi_pos = NewHiScore()) >= 0)
1736 game_status = GAME_MODE_SCORES;
1737 DrawHallOfFame(hi_pos);
1746 game_status = GAME_MODE_MAIN;
1763 LoadScore(level_nr);
1765 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1766 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1769 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1771 if (local_player->score > highscore[k].Score)
1773 /* player has made it to the hall of fame */
1775 if (k < MAX_SCORE_ENTRIES - 1)
1777 int m = MAX_SCORE_ENTRIES - 1;
1780 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1781 if (!strcmp(setup.player_name, highscore[l].Name))
1783 if (m == k) /* player's new highscore overwrites his old one */
1789 strcpy(highscore[l].Name, highscore[l - 1].Name);
1790 highscore[l].Score = highscore[l - 1].Score;
1797 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1798 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1799 highscore[k].Score = local_player->score;
1805 else if (!strncmp(setup.player_name, highscore[k].Name,
1806 MAX_PLAYER_NAME_LEN))
1807 break; /* player already there with a higher score */
1813 SaveScore(level_nr);
1818 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1820 if (player->GfxAction != action || player->GfxDir != dir)
1823 printf("Player frame reset! (%d => %d, %d => %d)\n",
1824 player->GfxAction, action, player->GfxDir, dir);
1827 player->GfxAction = action;
1828 player->GfxDir = dir;
1830 player->StepFrame = 0;
1834 static void ResetRandomAnimationValue(int x, int y)
1836 GfxRandom[x][y] = INIT_GFX_RANDOM();
1839 static void ResetGfxAnimation(int x, int y)
1842 GfxAction[x][y] = ACTION_DEFAULT;
1845 void InitMovingField(int x, int y, int direction)
1847 int element = Feld[x][y];
1848 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1849 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1853 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1854 ResetGfxAnimation(x, y);
1856 MovDir[newx][newy] = MovDir[x][y] = direction;
1858 if (Feld[newx][newy] == EL_EMPTY)
1859 Feld[newx][newy] = EL_BLOCKED;
1861 if (direction == MV_DOWN && CAN_FALL(element))
1862 GfxAction[x][y] = ACTION_FALLING;
1864 GfxAction[x][y] = ACTION_MOVING;
1866 GfxFrame[newx][newy] = GfxFrame[x][y];
1867 GfxAction[newx][newy] = GfxAction[x][y];
1868 GfxRandom[newx][newy] = GfxRandom[x][y];
1871 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1873 int direction = MovDir[x][y];
1874 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1875 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1881 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1883 int oldx = x, oldy = y;
1884 int direction = MovDir[x][y];
1886 if (direction == MV_LEFT)
1888 else if (direction == MV_RIGHT)
1890 else if (direction == MV_UP)
1892 else if (direction == MV_DOWN)
1895 *comes_from_x = oldx;
1896 *comes_from_y = oldy;
1899 int MovingOrBlocked2Element(int x, int y)
1901 int element = Feld[x][y];
1903 if (element == EL_BLOCKED)
1907 Blocked2Moving(x, y, &oldx, &oldy);
1908 return Feld[oldx][oldy];
1914 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1916 /* like MovingOrBlocked2Element(), but if element is moving
1917 and (x,y) is the field the moving element is just leaving,
1918 return EL_BLOCKED instead of the element value */
1919 int element = Feld[x][y];
1921 if (IS_MOVING(x, y))
1923 if (element == EL_BLOCKED)
1927 Blocked2Moving(x, y, &oldx, &oldy);
1928 return Feld[oldx][oldy];
1937 static void RemoveField(int x, int y)
1939 Feld[x][y] = EL_EMPTY;
1946 ChangeDelay[x][y] = 0;
1947 ChangePage[x][y] = -1;
1948 Pushed[x][y] = FALSE;
1950 GfxElement[x][y] = EL_UNDEFINED;
1951 GfxAction[x][y] = ACTION_DEFAULT;
1954 void RemoveMovingField(int x, int y)
1956 int oldx = x, oldy = y, newx = x, newy = y;
1957 int element = Feld[x][y];
1958 int next_element = EL_UNDEFINED;
1960 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1963 if (IS_MOVING(x, y))
1965 Moving2Blocked(x, y, &newx, &newy);
1966 if (Feld[newx][newy] != EL_BLOCKED)
1969 else if (element == EL_BLOCKED)
1971 Blocked2Moving(x, y, &oldx, &oldy);
1972 if (!IS_MOVING(oldx, oldy))
1976 if (element == EL_BLOCKED &&
1977 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1978 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1979 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1980 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1981 next_element = get_next_element(Feld[oldx][oldy]);
1983 RemoveField(oldx, oldy);
1984 RemoveField(newx, newy);
1986 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1988 if (next_element != EL_UNDEFINED)
1989 Feld[oldx][oldy] = next_element;
1991 DrawLevelField(oldx, oldy);
1992 DrawLevelField(newx, newy);
1995 void DrawDynamite(int x, int y)
1997 int sx = SCREENX(x), sy = SCREENY(y);
1998 int graphic = el2img(Feld[x][y]);
2001 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2004 if (IS_WALKABLE_INSIDE(Back[x][y]))
2008 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2009 else if (Store[x][y])
2010 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2012 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2015 if (Back[x][y] || Store[x][y])
2016 DrawGraphicThruMask(sx, sy, graphic, frame);
2018 DrawGraphic(sx, sy, graphic, frame);
2020 if (game.emulation == EMU_SUPAPLEX)
2021 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2022 else if (Store[x][y])
2023 DrawGraphicThruMask(sx, sy, graphic, frame);
2025 DrawGraphic(sx, sy, graphic, frame);
2029 void CheckDynamite(int x, int y)
2031 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2035 if (MovDelay[x][y] != 0)
2038 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2045 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2047 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2048 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2049 StopSound(SND_DYNAMITE_ACTIVE);
2051 StopSound(SND_DYNABOMB_ACTIVE);
2057 void RelocatePlayer(int x, int y, int element)
2059 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2062 RemoveField(x, y); /* temporarily remove newly placed player */
2063 DrawLevelField(x, y);
2066 if (player->present)
2068 while (player->MovPos)
2070 ScrollPlayer(player, SCROLL_GO_ON);
2071 ScrollScreen(NULL, SCROLL_GO_ON);
2077 Delay(GAME_FRAME_DELAY);
2080 DrawPlayer(player); /* needed here only to cleanup last field */
2081 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2083 player->is_moving = FALSE;
2086 Feld[x][y] = element;
2087 InitPlayerField(x, y, element, TRUE);
2089 if (player == local_player)
2091 int scroll_xx = -999, scroll_yy = -999;
2093 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2096 int fx = FX, fy = FY;
2098 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2099 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2100 local_player->jx - MIDPOSX);
2102 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2103 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2104 local_player->jy - MIDPOSY);
2106 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2107 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2112 fx += dx * TILEX / 2;
2113 fy += dy * TILEY / 2;
2115 ScrollLevel(dx, dy);
2118 /* scroll in two steps of half tile size to make things smoother */
2119 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2121 Delay(GAME_FRAME_DELAY);
2123 /* scroll second step to align at full tile size */
2125 Delay(GAME_FRAME_DELAY);
2130 void Explode(int ex, int ey, int phase, int mode)
2134 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2135 int last_phase = num_phase * delay;
2136 int half_phase = (num_phase / 2) * delay;
2137 int first_phase_after_start = EX_PHASE_START + 1;
2139 if (game.explosions_delayed)
2141 ExplodeField[ex][ey] = mode;
2145 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2147 int center_element = Feld[ex][ey];
2150 /* --- This is only really needed (and now handled) in "Impact()". --- */
2151 /* do not explode moving elements that left the explode field in time */
2152 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) &&
2153 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2157 if (mode == EX_NORMAL || mode == EX_CENTER)
2158 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2160 /* remove things displayed in background while burning dynamite */
2161 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2164 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2166 /* put moving element to center field (and let it explode there) */
2167 center_element = MovingOrBlocked2Element(ex, ey);
2168 RemoveMovingField(ex, ey);
2169 Feld[ex][ey] = center_element;
2172 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2174 int xx = x - ex + 1;
2175 int yy = y - ey + 1;
2178 if (!IN_LEV_FIELD(x, y) ||
2179 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2180 (x != ex || y != ey)))
2183 element = Feld[x][y];
2185 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2187 element = MovingOrBlocked2Element(x, y);
2189 if (!IS_EXPLOSION_PROOF(element))
2190 RemoveMovingField(x, y);
2196 if (IS_EXPLOSION_PROOF(element))
2199 /* indestructible elements can only explode in center (but not flames) */
2200 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2201 element == EL_FLAMES)
2206 if ((IS_INDESTRUCTIBLE(element) &&
2207 (game.engine_version < VERSION_IDENT(2,2,0) ||
2208 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2209 element == EL_FLAMES)
2213 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2215 if (IS_ACTIVE_BOMB(element))
2217 /* re-activate things under the bomb like gate or penguin */
2218 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2225 /* save walkable background elements while explosion on same tile */
2227 if (IS_INDESTRUCTIBLE(element))
2228 Back[x][y] = element;
2230 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2231 Back[x][y] = element;
2234 /* ignite explodable elements reached by other explosion */
2235 if (element == EL_EXPLOSION)
2236 element = Store2[x][y];
2239 if (AmoebaNr[x][y] &&
2240 (element == EL_AMOEBA_FULL ||
2241 element == EL_BD_AMOEBA ||
2242 element == EL_AMOEBA_GROWING))
2244 AmoebaCnt[AmoebaNr[x][y]]--;
2245 AmoebaCnt2[AmoebaNr[x][y]]--;
2251 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2253 switch(StorePlayer[ex][ey])
2256 Store[x][y] = EL_EMERALD_RED;
2259 Store[x][y] = EL_EMERALD;
2262 Store[x][y] = EL_EMERALD_PURPLE;
2266 Store[x][y] = EL_EMERALD_YELLOW;
2270 if (game.emulation == EMU_SUPAPLEX)
2271 Store[x][y] = EL_EMPTY;
2273 else if (center_element == EL_MOLE)
2274 Store[x][y] = EL_EMERALD_RED;
2275 else if (center_element == EL_PENGUIN)
2276 Store[x][y] = EL_EMERALD_PURPLE;
2277 else if (center_element == EL_BUG)
2278 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2279 else if (center_element == EL_BD_BUTTERFLY)
2280 Store[x][y] = EL_BD_DIAMOND;
2281 else if (center_element == EL_SP_ELECTRON)
2282 Store[x][y] = EL_SP_INFOTRON;
2283 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2284 Store[x][y] = level.amoeba_content;
2285 else if (center_element == EL_YAMYAM)
2286 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2287 else if (IS_CUSTOM_ELEMENT(center_element) &&
2288 element_info[center_element].content[xx][yy] != EL_EMPTY)
2289 Store[x][y] = element_info[center_element].content[xx][yy];
2290 else if (element == EL_WALL_EMERALD)
2291 Store[x][y] = EL_EMERALD;
2292 else if (element == EL_WALL_DIAMOND)
2293 Store[x][y] = EL_DIAMOND;
2294 else if (element == EL_WALL_BD_DIAMOND)
2295 Store[x][y] = EL_BD_DIAMOND;
2296 else if (element == EL_WALL_EMERALD_YELLOW)
2297 Store[x][y] = EL_EMERALD_YELLOW;
2298 else if (element == EL_WALL_EMERALD_RED)
2299 Store[x][y] = EL_EMERALD_RED;
2300 else if (element == EL_WALL_EMERALD_PURPLE)
2301 Store[x][y] = EL_EMERALD_PURPLE;
2302 else if (element == EL_WALL_PEARL)
2303 Store[x][y] = EL_PEARL;
2304 else if (element == EL_WALL_CRYSTAL)
2305 Store[x][y] = EL_CRYSTAL;
2306 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2307 Store[x][y] = element_info[element].content[1][1];
2309 Store[x][y] = EL_EMPTY;
2311 if (x != ex || y != ey ||
2312 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2313 Store2[x][y] = element;
2316 if (AmoebaNr[x][y] &&
2317 (element == EL_AMOEBA_FULL ||
2318 element == EL_BD_AMOEBA ||
2319 element == EL_AMOEBA_GROWING))
2321 AmoebaCnt[AmoebaNr[x][y]]--;
2322 AmoebaCnt2[AmoebaNr[x][y]]--;
2328 MovDir[x][y] = MovPos[x][y] = 0;
2333 Feld[x][y] = EL_EXPLOSION;
2335 GfxElement[x][y] = center_element;
2337 GfxElement[x][y] = EL_UNDEFINED;
2340 ExplodePhase[x][y] = 1;
2344 if (center_element == EL_YAMYAM)
2345 game.yamyam_content_nr =
2346 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2357 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2361 /* activate this even in non-DEBUG version until cause for crash in
2362 getGraphicAnimationFrame() (see below) is found and eliminated */
2366 if (GfxElement[x][y] == EL_UNDEFINED)
2369 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2370 printf("Explode(): This should never happen!\n");
2373 GfxElement[x][y] = EL_EMPTY;
2377 if (phase == first_phase_after_start)
2379 int element = Store2[x][y];
2381 if (element == EL_BLACK_ORB)
2383 Feld[x][y] = Store2[x][y];
2388 else if (phase == half_phase)
2390 int element = Store2[x][y];
2392 if (IS_PLAYER(x, y))
2393 KillHeroUnlessProtected(x, y);
2394 else if (CAN_EXPLODE_BY_FIRE(element))
2396 Feld[x][y] = Store2[x][y];
2400 else if (element == EL_AMOEBA_TO_DIAMOND)
2401 AmoebeUmwandeln(x, y);
2404 if (phase == last_phase)
2408 element = Feld[x][y] = Store[x][y];
2409 Store[x][y] = Store2[x][y] = 0;
2410 GfxElement[x][y] = EL_UNDEFINED;
2412 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2413 element = Feld[x][y] = Back[x][y];
2416 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2417 ChangeDelay[x][y] = 0;
2418 ChangePage[x][y] = -1;
2420 InitField(x, y, FALSE);
2421 if (CAN_MOVE(element))
2423 DrawLevelField(x, y);
2425 TestIfElementTouchesCustomElement(x, y);
2427 if (GFX_CRUMBLED(element))
2428 DrawLevelFieldCrumbledSandNeighbours(x, y);
2430 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2431 StorePlayer[x][y] = 0;
2433 if (ELEM_IS_PLAYER(element))
2434 RelocatePlayer(x, y, element);
2436 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2439 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2441 int stored = Store[x][y];
2442 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2443 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2446 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2449 DrawLevelFieldCrumbledSand(x, y);
2451 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2453 DrawLevelElement(x, y, Back[x][y]);
2454 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2456 else if (IS_WALKABLE_UNDER(Back[x][y]))
2458 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2459 DrawLevelElementThruMask(x, y, Back[x][y]);
2461 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2462 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2466 void DynaExplode(int ex, int ey)
2469 int dynabomb_size = 1;
2470 boolean dynabomb_xl = FALSE;
2471 struct PlayerInfo *player;
2472 static int xy[4][2] =
2480 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2482 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2483 dynabomb_size = player->dynabomb_size;
2484 dynabomb_xl = player->dynabomb_xl;
2485 player->dynabombs_left++;
2488 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2492 for (j=1; j<=dynabomb_size; j++)
2494 int x = ex + j * xy[i % 4][0];
2495 int y = ey + j * xy[i % 4][1];
2498 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2501 element = Feld[x][y];
2503 /* do not restart explosions of fields with active bombs */
2504 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2507 Explode(x, y, EX_PHASE_START, EX_BORDER);
2509 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2510 if (element != EL_EMPTY &&
2511 element != EL_SAND &&
2512 element != EL_EXPLOSION &&
2519 void Bang(int x, int y)
2522 int element = MovingOrBlocked2Element(x, y);
2524 int element = Feld[x][y];
2528 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2530 if (IS_PLAYER(x, y))
2533 struct PlayerInfo *player = PLAYERINFO(x, y);
2535 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2536 player->element_nr);
2541 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2543 if (game.emulation == EMU_SUPAPLEX)
2544 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2546 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2551 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2559 case EL_BD_BUTTERFLY:
2562 case EL_DARK_YAMYAM:
2566 RaiseScoreElement(element);
2567 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2569 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2570 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2571 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2572 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2573 case EL_DYNABOMB_INCREASE_NUMBER:
2574 case EL_DYNABOMB_INCREASE_SIZE:
2575 case EL_DYNABOMB_INCREASE_POWER:
2580 case EL_LAMP_ACTIVE:
2581 if (IS_PLAYER(x, y))
2582 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2584 Explode(x, y, EX_PHASE_START, EX_CENTER);
2587 if (CAN_EXPLODE_1X1(element))
2588 Explode(x, y, EX_PHASE_START, EX_CENTER);
2590 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2594 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2597 void SplashAcid(int x, int y)
2599 int element = Feld[x][y];
2601 if (element != EL_ACID_SPLASH_LEFT &&
2602 element != EL_ACID_SPLASH_RIGHT)
2604 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2606 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2607 (!IN_LEV_FIELD(x-1, y-1) ||
2608 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2609 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2611 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2612 (!IN_LEV_FIELD(x+1, y-1) ||
2613 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2614 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2618 static void InitBeltMovement()
2620 static int belt_base_element[4] =
2622 EL_CONVEYOR_BELT_1_LEFT,
2623 EL_CONVEYOR_BELT_2_LEFT,
2624 EL_CONVEYOR_BELT_3_LEFT,
2625 EL_CONVEYOR_BELT_4_LEFT
2627 static int belt_base_active_element[4] =
2629 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2630 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2631 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2632 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2637 /* set frame order for belt animation graphic according to belt direction */
2644 int element = belt_base_active_element[belt_nr] + j;
2645 int graphic = el2img(element);
2647 if (game.belt_dir[i] == MV_LEFT)
2648 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2650 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2654 for(y=0; y<lev_fieldy; y++)
2656 for(x=0; x<lev_fieldx; x++)
2658 int element = Feld[x][y];
2662 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2664 int e_belt_nr = getBeltNrFromBeltElement(element);
2667 if (e_belt_nr == belt_nr)
2669 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2671 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2679 static void ToggleBeltSwitch(int x, int y)
2681 static int belt_base_element[4] =
2683 EL_CONVEYOR_BELT_1_LEFT,
2684 EL_CONVEYOR_BELT_2_LEFT,
2685 EL_CONVEYOR_BELT_3_LEFT,
2686 EL_CONVEYOR_BELT_4_LEFT
2688 static int belt_base_active_element[4] =
2690 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2691 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2692 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2693 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2695 static int belt_base_switch_element[4] =
2697 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2698 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2699 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2700 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2702 static int belt_move_dir[4] =
2710 int element = Feld[x][y];
2711 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2712 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2713 int belt_dir = belt_move_dir[belt_dir_nr];
2716 if (!IS_BELT_SWITCH(element))
2719 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2720 game.belt_dir[belt_nr] = belt_dir;
2722 if (belt_dir_nr == 3)
2725 /* set frame order for belt animation graphic according to belt direction */
2728 int element = belt_base_active_element[belt_nr] + i;
2729 int graphic = el2img(element);
2731 if (belt_dir == MV_LEFT)
2732 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2734 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2737 for (yy=0; yy<lev_fieldy; yy++)
2739 for (xx=0; xx<lev_fieldx; xx++)
2741 int element = Feld[xx][yy];
2743 if (IS_BELT_SWITCH(element))
2745 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2747 if (e_belt_nr == belt_nr)
2749 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2750 DrawLevelField(xx, yy);
2753 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2755 int e_belt_nr = getBeltNrFromBeltElement(element);
2757 if (e_belt_nr == belt_nr)
2759 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2761 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2762 DrawLevelField(xx, yy);
2765 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2767 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2769 if (e_belt_nr == belt_nr)
2771 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2773 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2774 DrawLevelField(xx, yy);
2781 static void ToggleSwitchgateSwitch(int x, int y)
2785 game.switchgate_pos = !game.switchgate_pos;
2787 for (yy=0; yy<lev_fieldy; yy++)
2789 for (xx=0; xx<lev_fieldx; xx++)
2791 int element = Feld[xx][yy];
2793 if (element == EL_SWITCHGATE_SWITCH_UP ||
2794 element == EL_SWITCHGATE_SWITCH_DOWN)
2796 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2797 DrawLevelField(xx, yy);
2799 else if (element == EL_SWITCHGATE_OPEN ||
2800 element == EL_SWITCHGATE_OPENING)
2802 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2804 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2806 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2809 else if (element == EL_SWITCHGATE_CLOSED ||
2810 element == EL_SWITCHGATE_CLOSING)
2812 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2814 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2816 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2823 static int getInvisibleActiveFromInvisibleElement(int element)
2825 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2826 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2827 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2831 static int getInvisibleFromInvisibleActiveElement(int element)
2833 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2834 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2835 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2839 static void RedrawAllLightSwitchesAndInvisibleElements()
2843 for (y=0; y<lev_fieldy; y++)
2845 for (x=0; x<lev_fieldx; x++)
2847 int element = Feld[x][y];
2849 if (element == EL_LIGHT_SWITCH &&
2850 game.light_time_left > 0)
2852 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2853 DrawLevelField(x, y);
2855 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2856 game.light_time_left == 0)
2858 Feld[x][y] = EL_LIGHT_SWITCH;
2859 DrawLevelField(x, y);
2861 else if (element == EL_INVISIBLE_STEELWALL ||
2862 element == EL_INVISIBLE_WALL ||
2863 element == EL_INVISIBLE_SAND)
2865 if (game.light_time_left > 0)
2866 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2868 DrawLevelField(x, y);
2870 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2871 element == EL_INVISIBLE_WALL_ACTIVE ||
2872 element == EL_INVISIBLE_SAND_ACTIVE)
2874 if (game.light_time_left == 0)
2875 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2877 DrawLevelField(x, y);
2883 static void ToggleLightSwitch(int x, int y)
2885 int element = Feld[x][y];
2887 game.light_time_left =
2888 (element == EL_LIGHT_SWITCH ?
2889 level.time_light * FRAMES_PER_SECOND : 0);
2891 RedrawAllLightSwitchesAndInvisibleElements();
2894 static void ActivateTimegateSwitch(int x, int y)
2898 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2900 for (yy=0; yy<lev_fieldy; yy++)
2902 for (xx=0; xx<lev_fieldx; xx++)
2904 int element = Feld[xx][yy];
2906 if (element == EL_TIMEGATE_CLOSED ||
2907 element == EL_TIMEGATE_CLOSING)
2909 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2910 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2914 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2916 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2917 DrawLevelField(xx, yy);
2924 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2927 inline static int getElementMoveStepsize(int x, int y)
2929 int element = Feld[x][y];
2930 int direction = MovDir[x][y];
2931 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2932 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2933 int horiz_move = (dx != 0);
2934 int sign = (horiz_move ? dx : dy);
2935 int step = sign * element_info[element].move_stepsize;
2937 /* special values for move stepsize for spring and things on conveyor belt */
2940 if (CAN_FALL(element) &&
2941 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2942 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2943 else if (element == EL_SPRING)
2944 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2950 void Impact(int x, int y)
2952 boolean lastline = (y == lev_fieldy-1);
2953 boolean object_hit = FALSE;
2954 boolean impact = (lastline || object_hit);
2955 int element = Feld[x][y];
2956 int smashed = EL_UNDEFINED;
2958 if (!lastline) /* check if element below was hit */
2960 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2963 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2964 MovDir[x][y + 1] != MV_DOWN ||
2965 MovPos[x][y + 1] <= TILEY / 2));
2967 /* do not smash moving elements that left the smashed field in time */
2968 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2969 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2973 smashed = MovingOrBlocked2Element(x, y + 1);
2975 impact = (lastline || object_hit);
2978 if (!lastline && smashed == EL_ACID) /* element falls into acid */
2984 /* only reset graphic animation if graphic really changes after impact */
2986 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2988 ResetGfxAnimation(x, y);
2989 DrawLevelField(x, y);
2992 if (impact && CAN_EXPLODE_IMPACT(element))
2997 else if (impact && element == EL_PEARL)
2999 Feld[x][y] = EL_PEARL_BREAKING;
3000 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3003 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3005 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3010 if (impact && element == EL_AMOEBA_DROP)
3012 if (object_hit && IS_PLAYER(x, y + 1))
3013 KillHeroUnlessProtected(x, y + 1);
3014 else if (object_hit && smashed == EL_PENGUIN)
3018 Feld[x][y] = EL_AMOEBA_GROWING;
3019 Store[x][y] = EL_AMOEBA_WET;
3021 ResetRandomAnimationValue(x, y);
3026 if (object_hit) /* check which object was hit */
3028 if (CAN_PASS_MAGIC_WALL(element) &&
3029 (smashed == EL_MAGIC_WALL ||
3030 smashed == EL_BD_MAGIC_WALL))
3033 int activated_magic_wall =
3034 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3035 EL_BD_MAGIC_WALL_ACTIVE);
3037 /* activate magic wall / mill */
3038 for (yy=0; yy<lev_fieldy; yy++)
3039 for (xx=0; xx<lev_fieldx; xx++)
3040 if (Feld[xx][yy] == smashed)
3041 Feld[xx][yy] = activated_magic_wall;
3043 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3044 game.magic_wall_active = TRUE;
3046 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3047 SND_MAGIC_WALL_ACTIVATING :
3048 SND_BD_MAGIC_WALL_ACTIVATING));
3051 if (IS_PLAYER(x, y + 1))
3053 if (CAN_SMASH_PLAYER(element))
3055 KillHeroUnlessProtected(x, y + 1);
3059 else if (smashed == EL_PENGUIN)
3061 if (CAN_SMASH_PLAYER(element))
3067 else if (element == EL_BD_DIAMOND)
3069 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3075 else if ((element == EL_SP_INFOTRON ||
3076 element == EL_SP_ZONK) &&
3077 (smashed == EL_SP_SNIKSNAK ||
3078 smashed == EL_SP_ELECTRON ||
3079 smashed == EL_SP_DISK_ORANGE))
3085 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3091 else if (CAN_SMASH_EVERYTHING(element))
3093 if (IS_CLASSIC_ENEMY(smashed) ||
3094 CAN_EXPLODE_SMASHED(smashed))
3099 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3101 if (smashed == EL_LAMP ||
3102 smashed == EL_LAMP_ACTIVE)
3107 else if (smashed == EL_NUT)
3109 Feld[x][y + 1] = EL_NUT_BREAKING;
3110 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3111 RaiseScoreElement(EL_NUT);
3114 else if (smashed == EL_PEARL)
3116 Feld[x][y + 1] = EL_PEARL_BREAKING;
3117 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3120 else if (smashed == EL_DIAMOND)
3122 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3123 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3126 else if (IS_BELT_SWITCH(smashed))
3128 ToggleBeltSwitch(x, y + 1);
3130 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3131 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3133 ToggleSwitchgateSwitch(x, y + 1);
3135 else if (smashed == EL_LIGHT_SWITCH ||
3136 smashed == EL_LIGHT_SWITCH_ACTIVE)
3138 ToggleLightSwitch(x, y + 1);
3142 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3144 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3145 CE_OTHER_IS_SWITCHING);
3146 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3152 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3157 /* play sound of magic wall / mill */
3159 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3160 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3162 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3163 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3164 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3165 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3170 /* play sound of object that hits the ground */
3171 if (lastline || object_hit)
3172 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3175 void TurnRound(int x, int y)
3187 { 0, 0 }, { 0, 0 }, { 0, 0 },
3192 int left, right, back;
3196 { MV_DOWN, MV_UP, MV_RIGHT },
3197 { MV_UP, MV_DOWN, MV_LEFT },
3199 { MV_LEFT, MV_RIGHT, MV_DOWN },
3203 { MV_RIGHT, MV_LEFT, MV_UP }
3206 int element = Feld[x][y];
3207 int old_move_dir = MovDir[x][y];
3208 int left_dir = turn[old_move_dir].left;
3209 int right_dir = turn[old_move_dir].right;
3210 int back_dir = turn[old_move_dir].back;
3212 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3213 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3214 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3215 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3217 int left_x = x + left_dx, left_y = y + left_dy;
3218 int right_x = x + right_dx, right_y = y + right_dy;
3219 int move_x = x + move_dx, move_y = y + move_dy;
3223 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3225 TestIfBadThingTouchesOtherBadThing(x, y);
3227 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3228 MovDir[x][y] = right_dir;
3229 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3230 MovDir[x][y] = left_dir;
3232 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3234 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3237 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3238 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3240 TestIfBadThingTouchesOtherBadThing(x, y);
3242 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3243 MovDir[x][y] = left_dir;
3244 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3245 MovDir[x][y] = right_dir;
3247 if ((element == EL_SPACESHIP ||
3248 element == EL_SP_SNIKSNAK ||
3249 element == EL_SP_ELECTRON)
3250 && MovDir[x][y] != old_move_dir)
3252 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3255 else if (element == EL_YAMYAM)
3257 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3258 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3260 if (can_turn_left && can_turn_right)
3261 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3262 else if (can_turn_left)
3263 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3264 else if (can_turn_right)
3265 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3267 MovDir[x][y] = back_dir;
3269 MovDelay[x][y] = 16 + 16 * RND(3);
3271 else if (element == EL_DARK_YAMYAM)
3273 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3274 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3276 if (can_turn_left && can_turn_right)
3277 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3278 else if (can_turn_left)
3279 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3280 else if (can_turn_right)
3281 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3283 MovDir[x][y] = back_dir;
3285 MovDelay[x][y] = 16 + 16 * RND(3);
3287 else if (element == EL_PACMAN)
3289 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3290 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3292 if (can_turn_left && can_turn_right)
3293 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3294 else if (can_turn_left)
3295 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3296 else if (can_turn_right)
3297 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3299 MovDir[x][y] = back_dir;
3301 MovDelay[x][y] = 6 + RND(40);
3303 else if (element == EL_PIG)
3305 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3306 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3307 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3308 boolean should_turn_left, should_turn_right, should_move_on;
3310 int rnd = RND(rnd_value);
3312 should_turn_left = (can_turn_left &&
3314 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3315 y + back_dy + left_dy)));
3316 should_turn_right = (can_turn_right &&
3318 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3319 y + back_dy + right_dy)));
3320 should_move_on = (can_move_on &&
3323 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3324 y + move_dy + left_dy) ||
3325 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3326 y + move_dy + right_dy)));
3328 if (should_turn_left || should_turn_right || should_move_on)
3330 if (should_turn_left && should_turn_right && should_move_on)
3331 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3332 rnd < 2 * rnd_value / 3 ? right_dir :
3334 else if (should_turn_left && should_turn_right)
3335 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3336 else if (should_turn_left && should_move_on)
3337 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3338 else if (should_turn_right && should_move_on)
3339 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3340 else if (should_turn_left)
3341 MovDir[x][y] = left_dir;
3342 else if (should_turn_right)
3343 MovDir[x][y] = right_dir;
3344 else if (should_move_on)
3345 MovDir[x][y] = old_move_dir;
3347 else if (can_move_on && rnd > rnd_value / 8)
3348 MovDir[x][y] = old_move_dir;
3349 else if (can_turn_left && can_turn_right)
3350 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3351 else if (can_turn_left && rnd > rnd_value / 8)
3352 MovDir[x][y] = left_dir;
3353 else if (can_turn_right && rnd > rnd_value/8)
3354 MovDir[x][y] = right_dir;
3356 MovDir[x][y] = back_dir;
3358 xx = x + move_xy[MovDir[x][y]].x;
3359 yy = y + move_xy[MovDir[x][y]].y;
3361 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3362 MovDir[x][y] = old_move_dir;
3366 else if (element == EL_DRAGON)
3368 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3369 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3370 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3372 int rnd = RND(rnd_value);
3374 if (can_move_on && rnd > rnd_value / 8)
3375 MovDir[x][y] = old_move_dir;
3376 else if (can_turn_left && can_turn_right)
3377 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3378 else if (can_turn_left && rnd > rnd_value / 8)
3379 MovDir[x][y] = left_dir;
3380 else if (can_turn_right && rnd > rnd_value / 8)
3381 MovDir[x][y] = right_dir;
3383 MovDir[x][y] = back_dir;
3385 xx = x + move_xy[MovDir[x][y]].x;
3386 yy = y + move_xy[MovDir[x][y]].y;
3388 if (!IS_FREE(xx, yy))
3389 MovDir[x][y] = old_move_dir;
3393 else if (element == EL_MOLE)
3395 boolean can_move_on =
3396 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3397 IS_AMOEBOID(Feld[move_x][move_y]) ||
3398 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3401 boolean can_turn_left =
3402 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3403 IS_AMOEBOID(Feld[left_x][left_y])));
3405 boolean can_turn_right =
3406 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3407 IS_AMOEBOID(Feld[right_x][right_y])));
3409 if (can_turn_left && can_turn_right)
3410 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3411 else if (can_turn_left)
3412 MovDir[x][y] = left_dir;
3414 MovDir[x][y] = right_dir;
3417 if (MovDir[x][y] != old_move_dir)
3420 else if (element == EL_BALLOON)
3422 MovDir[x][y] = game.balloon_dir;
3425 else if (element == EL_SPRING)
3427 if (MovDir[x][y] & MV_HORIZONTAL &&
3428 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3429 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3430 MovDir[x][y] = MV_NO_MOVING;
3434 else if (element == EL_ROBOT ||
3435 element == EL_SATELLITE ||
3436 element == EL_PENGUIN)
3438 int attr_x = -1, attr_y = -1;
3449 for (i=0; i<MAX_PLAYERS; i++)
3451 struct PlayerInfo *player = &stored_player[i];
3452 int jx = player->jx, jy = player->jy;
3454 if (!player->active)
3458 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3466 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3472 if (element == EL_PENGUIN)
3475 static int xy[4][2] =
3485 int ex = x + xy[i % 4][0];
3486 int ey = y + xy[i % 4][1];
3488 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3497 MovDir[x][y] = MV_NO_MOVING;
3499 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3500 else if (attr_x > x)
3501 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3503 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3504 else if (attr_y > y)
3505 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3507 if (element == EL_ROBOT)
3511 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3512 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3513 Moving2Blocked(x, y, &newx, &newy);
3515 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3516 MovDelay[x][y] = 8 + 8 * !RND(3);
3518 MovDelay[x][y] = 16;
3520 else if (element == EL_PENGUIN)
3526 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3528 boolean first_horiz = RND(2);
3529 int new_move_dir = MovDir[x][y];
3532 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3533 Moving2Blocked(x, y, &newx, &newy);
3535 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3539 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3540 Moving2Blocked(x, y, &newx, &newy);
3542 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3545 MovDir[x][y] = old_move_dir;
3549 else /* (element == EL_SATELLITE) */
3555 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3557 boolean first_horiz = RND(2);
3558 int new_move_dir = MovDir[x][y];
3561 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3562 Moving2Blocked(x, y, &newx, &newy);
3564 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3568 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3569 Moving2Blocked(x, y, &newx, &newy);
3571 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3574 MovDir[x][y] = old_move_dir;
3579 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3580 element_info[element].move_pattern == MV_TURNING_LEFT ||
3581 element_info[element].move_pattern == MV_TURNING_RIGHT)
3583 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3584 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3586 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3587 MovDir[x][y] = left_dir;
3588 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3589 MovDir[x][y] = right_dir;
3590 else if (can_turn_left && can_turn_right)
3591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3592 else if (can_turn_left)
3593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3594 else if (can_turn_right)
3595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3597 MovDir[x][y] = back_dir;
3599 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3601 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3602 element_info[element].move_pattern == MV_VERTICAL)
3604 if (element_info[element].move_pattern & old_move_dir)
3605 MovDir[x][y] = back_dir;
3606 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3607 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3608 else if (element_info[element].move_pattern == MV_VERTICAL)
3609 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3611 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3613 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3615 MovDir[x][y] = element_info[element].move_pattern;
3616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3618 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3620 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3621 MovDir[x][y] = left_dir;
3622 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3623 MovDir[x][y] = right_dir;
3625 if (MovDir[x][y] != old_move_dir)
3626 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3628 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3630 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3631 MovDir[x][y] = right_dir;
3632 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3633 MovDir[x][y] = left_dir;
3635 if (MovDir[x][y] != old_move_dir)
3636 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3638 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3639 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3641 int attr_x = -1, attr_y = -1;
3644 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3655 for (i=0; i<MAX_PLAYERS; i++)
3657 struct PlayerInfo *player = &stored_player[i];
3658 int jx = player->jx, jy = player->jy;
3660 if (!player->active)
3664 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3672 MovDir[x][y] = MV_NO_MOVING;
3674 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3675 else if (attr_x > x)
3676 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3678 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3679 else if (attr_y > y)
3680 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3682 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3684 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3686 boolean first_horiz = RND(2);
3687 int new_move_dir = MovDir[x][y];
3690 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3691 Moving2Blocked(x, y, &newx, &newy);
3693 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3697 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3698 Moving2Blocked(x, y, &newx, &newy);
3700 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3703 MovDir[x][y] = old_move_dir;
3706 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3708 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3709 MovDir[x][y] = MV_NO_MOVING;
3715 static boolean JustBeingPushed(int x, int y)
3719 for (i=0; i<MAX_PLAYERS; i++)
3721 struct PlayerInfo *player = &stored_player[i];
3723 if (player->active && player->is_pushing && player->MovPos)
3725 int next_jx = player->jx + (player->jx - player->last_jx);
3726 int next_jy = player->jy + (player->jy - player->last_jy);
3728 if (x == next_jx && y == next_jy)
3736 void StartMoving(int x, int y)
3738 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
3739 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3740 int element = Feld[x][y];
3745 /* !!! this should be handled more generic (not only for mole) !!! */
3746 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3747 GfxAction[x][y] = ACTION_DEFAULT;
3749 if (CAN_FALL(element) && y < lev_fieldy - 1)
3751 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3752 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3753 if (JustBeingPushed(x, y))
3756 if (element == EL_QUICKSAND_FULL)
3758 if (IS_FREE(x, y + 1))
3760 InitMovingField(x, y, MV_DOWN);
3761 started_moving = TRUE;
3763 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3764 Store[x][y] = EL_ROCK;
3766 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3768 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3771 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3773 if (!MovDelay[x][y])
3774 MovDelay[x][y] = TILEY + 1;
3783 Feld[x][y] = EL_QUICKSAND_EMPTY;
3784 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3785 Store[x][y + 1] = Store[x][y];
3788 PlaySoundLevelAction(x, y, ACTION_FILLING);
3790 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3794 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3795 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3797 InitMovingField(x, y, MV_DOWN);
3798 started_moving = TRUE;
3800 Feld[x][y] = EL_QUICKSAND_FILLING;
3801 Store[x][y] = element;
3803 PlaySoundLevelAction(x, y, ACTION_FILLING);
3805 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3808 else if (element == EL_MAGIC_WALL_FULL)
3810 if (IS_FREE(x, y + 1))
3812 InitMovingField(x, y, MV_DOWN);
3813 started_moving = TRUE;
3815 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3816 Store[x][y] = EL_CHANGED(Store[x][y]);
3818 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3820 if (!MovDelay[x][y])
3821 MovDelay[x][y] = TILEY/4 + 1;
3830 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3831 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3832 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3836 else if (element == EL_BD_MAGIC_WALL_FULL)
3838 if (IS_FREE(x, y + 1))
3840 InitMovingField(x, y, MV_DOWN);
3841 started_moving = TRUE;
3843 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3844 Store[x][y] = EL_CHANGED2(Store[x][y]);
3846 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3848 if (!MovDelay[x][y])
3849 MovDelay[x][y] = TILEY/4 + 1;
3858 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3859 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3860 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3864 else if (CAN_PASS_MAGIC_WALL(element) &&
3865 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3866 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3868 InitMovingField(x, y, MV_DOWN);
3869 started_moving = TRUE;
3872 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3873 EL_BD_MAGIC_WALL_FILLING);
3874 Store[x][y] = element;
3877 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3879 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3884 InitMovingField(x, y, MV_DOWN);
3885 started_moving = TRUE;
3887 Store[x][y] = EL_ACID;
3889 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3890 GfxAction[x][y + 1] = ACTION_ACTIVE;
3894 else if ((game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3895 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3896 (Feld[x][y + 1] == EL_BLOCKED)) ||
3897 (game.engine_version >= VERSION_IDENT(3,0,7) &&
3898 CAN_SMASH(element) && WasJustFalling[x][y] &&
3899 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3903 else if (game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3904 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3905 WasJustMoving[x][y] && !Pushed[x][y + 1])
3907 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3908 WasJustMoving[x][y])
3913 /* this is needed for a special case not covered by calling "Impact()"
3914 from "ContinueMoving()": if an element moves to a tile directly below
3915 another element which was just falling on that tile (which was empty
3916 in the previous frame), the falling element above would just stop
3917 instead of smashing the element below (in previous version, the above
3918 element was just checked for "moving" instead of "falling", resulting
3919 in incorrect smashes caused by horizontal movement of the above
3920 element; also, the case of the player being the element to smash was
3921 simply not covered here... :-/ ) */
3925 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3927 if (MovDir[x][y] == MV_NO_MOVING)
3929 InitMovingField(x, y, MV_DOWN);
3930 started_moving = TRUE;
3933 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3935 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3936 MovDir[x][y] = MV_DOWN;
3938 InitMovingField(x, y, MV_DOWN);
3939 started_moving = TRUE;
3941 else if (element == EL_AMOEBA_DROP)
3943 Feld[x][y] = EL_AMOEBA_GROWING;
3944 Store[x][y] = EL_AMOEBA_WET;
3946 /* Store[x][y + 1] must be zero, because:
3947 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3950 #if OLD_GAME_BEHAVIOUR
3951 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3953 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3954 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3955 element != EL_DX_SUPABOMB)
3958 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3959 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3960 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3961 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3964 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
3965 (IS_FREE(x - 1, y + 1) ||
3966 Feld[x - 1][y + 1] == EL_ACID));
3967 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3968 (IS_FREE(x + 1, y + 1) ||
3969 Feld[x + 1][y + 1] == EL_ACID));
3970 boolean can_fall_any = (can_fall_left || can_fall_right);
3971 boolean can_fall_both = (can_fall_left && can_fall_right);
3973 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3975 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3977 if (slippery_type == SLIPPERY_ONLY_LEFT)
3978 can_fall_right = FALSE;
3979 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3980 can_fall_left = FALSE;
3981 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3982 can_fall_right = FALSE;
3983 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3984 can_fall_left = FALSE;
3986 can_fall_any = (can_fall_left || can_fall_right);
3987 can_fall_both = (can_fall_left && can_fall_right);
3992 if (can_fall_both &&
3993 (game.emulation != EMU_BOULDERDASH &&
3994 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
3995 can_fall_left = !(can_fall_right = RND(2));
3997 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
3998 started_moving = TRUE;
4001 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4003 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4004 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4005 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4006 int belt_dir = game.belt_dir[belt_nr];
4008 if ((belt_dir == MV_LEFT && left_is_free) ||
4009 (belt_dir == MV_RIGHT && right_is_free))
4011 InitMovingField(x, y, belt_dir);
4012 started_moving = TRUE;
4014 GfxAction[x][y] = ACTION_DEFAULT;
4019 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4020 if (CAN_MOVE(element) && !started_moving)
4025 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4028 if ((element == EL_SATELLITE ||
4029 element == EL_BALLOON ||
4030 element == EL_SPRING)
4031 && JustBeingPushed(x, y))
4037 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4038 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4040 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4042 Moving2Blocked(x, y, &newx, &newy);
4043 if (Feld[newx][newy] == EL_BLOCKED)
4044 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4049 if (!MovDelay[x][y]) /* start new movement phase */
4051 /* all objects that can change their move direction after each step
4052 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4054 if (element != EL_YAMYAM &&
4055 element != EL_DARK_YAMYAM &&
4056 element != EL_PACMAN &&
4057 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4058 element_info[element].move_pattern != MV_TURNING_LEFT &&
4059 element_info[element].move_pattern != MV_TURNING_RIGHT)
4063 if (MovDelay[x][y] && (element == EL_BUG ||
4064 element == EL_SPACESHIP ||
4065 element == EL_SP_SNIKSNAK ||
4066 element == EL_SP_ELECTRON ||
4067 element == EL_MOLE))
4068 DrawLevelField(x, y);
4072 if (MovDelay[x][y]) /* wait some time before next movement */
4077 if (element == EL_YAMYAM)
4080 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4081 DrawLevelElementAnimation(x, y, element);
4085 if (MovDelay[x][y]) /* element still has to wait some time */
4088 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4089 ResetGfxAnimation(x, y);
4091 GfxAction[x][y] = ACTION_WAITING;
4094 if (element == EL_ROBOT ||
4096 element == EL_PACMAN ||
4098 element == EL_YAMYAM ||
4099 element == EL_DARK_YAMYAM)
4102 DrawLevelElementAnimation(x, y, element);
4104 DrawLevelElementAnimationIfNeeded(x, y, element);
4106 PlaySoundLevelAction(x, y, ACTION_WAITING);
4108 else if (element == EL_SP_ELECTRON)
4109 DrawLevelElementAnimationIfNeeded(x, y, element);
4110 else if (element == EL_DRAGON)
4113 int dir = MovDir[x][y];
4114 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4115 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4116 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4117 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4118 dir == MV_UP ? IMG_FLAMES_1_UP :
4119 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4120 int frame = getGraphicAnimationFrame(graphic, -1);
4122 for (i=1; i<=3; i++)
4124 int xx = x + i*dx, yy = y + i*dy;
4125 int sx = SCREENX(xx), sy = SCREENY(yy);
4126 int flame_graphic = graphic + (i - 1);
4128 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4133 int flamed = MovingOrBlocked2Element(xx, yy);
4135 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4138 RemoveMovingField(xx, yy);
4140 Feld[xx][yy] = EL_FLAMES;
4141 if (IN_SCR_FIELD(sx, sy))
4142 DrawGraphic(sx, sy, flame_graphic, frame);
4146 if (Feld[xx][yy] == EL_FLAMES)
4147 Feld[xx][yy] = EL_EMPTY;
4148 DrawLevelField(xx, yy);
4153 if (MovDelay[x][y]) /* element still has to wait some time */
4155 PlaySoundLevelAction(x, y, ACTION_WAITING);
4160 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4161 for all other elements GfxAction will be set by InitMovingField() */
4162 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4163 GfxAction[x][y] = ACTION_MOVING;
4166 /* now make next step */
4168 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4170 if (DONT_COLLIDE_WITH(element) &&
4171 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4172 !PLAYER_PROTECTED(newx, newy))
4175 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4178 /* player killed by element which is deadly when colliding with */
4180 KillHero(PLAYERINFO(newx, newy));
4185 else if ((element == EL_PENGUIN ||
4186 element == EL_ROBOT ||
4187 element == EL_SATELLITE ||
4188 element == EL_BALLOON ||
4189 IS_CUSTOM_ELEMENT(element)) &&
4190 IN_LEV_FIELD(newx, newy) &&
4191 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4194 Store[x][y] = EL_ACID;
4196 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4198 if (Feld[newx][newy] == EL_EXIT_OPEN)
4200 Feld[x][y] = EL_EMPTY;
4201 DrawLevelField(x, y);
4203 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4204 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4205 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4207 local_player->friends_still_needed--;
4208 if (!local_player->friends_still_needed &&
4209 !local_player->GameOver && AllPlayersGone)
4210 local_player->LevelSolved = local_player->GameOver = TRUE;
4214 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4216 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4217 DrawLevelField(newx, newy);
4219 MovDir[x][y] = MV_NO_MOVING;
4221 else if (!IS_FREE(newx, newy))
4223 GfxAction[x][y] = ACTION_WAITING;
4225 if (IS_PLAYER(x, y))
4226 DrawPlayerField(x, y);
4228 DrawLevelField(x, y);
4232 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4234 if (IS_FOOD_PIG(Feld[newx][newy]))
4236 if (IS_MOVING(newx, newy))
4237 RemoveMovingField(newx, newy);
4240 Feld[newx][newy] = EL_EMPTY;
4241 DrawLevelField(newx, newy);
4244 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4246 else if (!IS_FREE(newx, newy))
4248 if (IS_PLAYER(x, y))
4249 DrawPlayerField(x, y);
4251 DrawLevelField(x, y);
4255 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4257 if (!IS_FREE(newx, newy))
4259 if (IS_PLAYER(x, y))
4260 DrawPlayerField(x, y);
4262 DrawLevelField(x, y);
4267 boolean wanna_flame = !RND(10);
4268 int dx = newx - x, dy = newy - y;
4269 int newx1 = newx+1*dx, newy1 = newy+1*dy;
4270 int newx2 = newx+2*dx, newy2 = newy+2*dy;
4271 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4272 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4273 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4274 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4277 IS_CLASSIC_ENEMY(element1) ||
4278 IS_CLASSIC_ENEMY(element2)) &&
4279 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4280 element1 != EL_FLAMES && element2 != EL_FLAMES)
4283 ResetGfxAnimation(x, y);
4284 GfxAction[x][y] = ACTION_ATTACKING;
4287 if (IS_PLAYER(x, y))
4288 DrawPlayerField(x, y);
4290 DrawLevelField(x, y);
4292 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4294 MovDelay[x][y] = 50;
4295 Feld[newx][newy] = EL_FLAMES;
4296 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4297 Feld[newx1][newy1] = EL_FLAMES;
4298 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4299 Feld[newx2][newy2] = EL_FLAMES;
4305 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4306 Feld[newx][newy] == EL_DIAMOND)
4308 if (IS_MOVING(newx, newy))
4309 RemoveMovingField(newx, newy);
4312 Feld[newx][newy] = EL_EMPTY;
4313 DrawLevelField(newx, newy);
4316 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4318 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4319 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4321 if (AmoebaNr[newx][newy])
4323 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4324 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4325 Feld[newx][newy] == EL_BD_AMOEBA)
4326 AmoebaCnt[AmoebaNr[newx][newy]]--;
4329 if (IS_MOVING(newx, newy))
4330 RemoveMovingField(newx, newy);
4333 Feld[newx][newy] = EL_EMPTY;
4334 DrawLevelField(newx, newy);
4337 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4339 else if ((element == EL_PACMAN || element == EL_MOLE)
4340 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4342 if (AmoebaNr[newx][newy])
4344 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4345 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4346 Feld[newx][newy] == EL_BD_AMOEBA)
4347 AmoebaCnt[AmoebaNr[newx][newy]]--;
4350 if (element == EL_MOLE)
4352 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4353 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4355 ResetGfxAnimation(x, y);
4356 GfxAction[x][y] = ACTION_DIGGING;
4357 DrawLevelField(x, y);
4359 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4360 return; /* wait for shrinking amoeba */
4362 else /* element == EL_PACMAN */
4364 Feld[newx][newy] = EL_EMPTY;
4365 DrawLevelField(newx, newy);
4366 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4369 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4370 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4371 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4373 /* wait for shrinking amoeba to completely disappear */
4376 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4378 /* object was running against a wall */
4383 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4384 DrawLevelElementAnimation(x, y, element);
4386 if (element == EL_BUG ||
4387 element == EL_SPACESHIP ||
4388 element == EL_SP_SNIKSNAK)
4389 DrawLevelField(x, y);
4390 else if (element == EL_MOLE)
4391 DrawLevelField(x, y);
4392 else if (element == EL_BD_BUTTERFLY ||
4393 element == EL_BD_FIREFLY)
4394 DrawLevelElementAnimationIfNeeded(x, y, element);
4395 else if (element == EL_SATELLITE)
4396 DrawLevelElementAnimationIfNeeded(x, y, element);
4397 else if (element == EL_SP_ELECTRON)
4398 DrawLevelElementAnimationIfNeeded(x, y, element);
4401 if (DONT_TOUCH(element))
4402 TestIfBadThingTouchesHero(x, y);
4405 PlaySoundLevelAction(x, y, ACTION_WAITING);
4411 InitMovingField(x, y, MovDir[x][y]);
4413 PlaySoundLevelAction(x, y, ACTION_MOVING);
4417 ContinueMoving(x, y);
4420 void ContinueMoving(int x, int y)
4422 int element = Feld[x][y];
4423 int direction = MovDir[x][y];
4424 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4425 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4426 int newx = x + dx, newy = y + dy;
4427 int nextx = newx + dx, nexty = newy + dy;
4428 boolean pushed = Pushed[x][y];
4430 MovPos[x][y] += getElementMoveStepsize(x, y);
4432 if (pushed) /* special case: moving object pushed by player */
4433 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4435 if (ABS(MovPos[x][y]) < TILEX)
4437 DrawLevelField(x, y);
4439 return; /* element is still moving */
4442 /* element reached destination field */
4444 Feld[x][y] = EL_EMPTY;
4445 Feld[newx][newy] = element;
4446 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4448 if (element == EL_MOLE)
4450 Feld[x][y] = EL_SAND;
4452 DrawLevelFieldCrumbledSandNeighbours(x, y);
4454 else if (element == EL_QUICKSAND_FILLING)
4456 element = Feld[newx][newy] = get_next_element(element);
4457 Store[newx][newy] = Store[x][y];
4459 else if (element == EL_QUICKSAND_EMPTYING)
4461 Feld[x][y] = get_next_element(element);
4462 element = Feld[newx][newy] = Store[x][y];
4464 else if (element == EL_MAGIC_WALL_FILLING)
4466 element = Feld[newx][newy] = get_next_element(element);
4467 if (!game.magic_wall_active)
4468 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4469 Store[newx][newy] = Store[x][y];
4471 else if (element == EL_MAGIC_WALL_EMPTYING)
4473 Feld[x][y] = get_next_element(element);
4474 if (!game.magic_wall_active)
4475 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4476 element = Feld[newx][newy] = Store[x][y];
4478 else if (element == EL_BD_MAGIC_WALL_FILLING)
4480 element = Feld[newx][newy] = get_next_element(element);
4481 if (!game.magic_wall_active)
4482 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4483 Store[newx][newy] = Store[x][y];
4485 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4487 Feld[x][y] = get_next_element(element);
4488 if (!game.magic_wall_active)
4489 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4490 element = Feld[newx][newy] = Store[x][y];
4492 else if (element == EL_AMOEBA_DROPPING)
4494 Feld[x][y] = get_next_element(element);
4495 element = Feld[newx][newy] = Store[x][y];
4497 else if (element == EL_SOKOBAN_OBJECT)
4500 Feld[x][y] = Back[x][y];
4502 if (Back[newx][newy])
4503 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4505 Back[x][y] = Back[newx][newy] = 0;
4507 else if (Store[x][y] == EL_ACID)
4509 element = Feld[newx][newy] = EL_ACID;
4513 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4514 MovDelay[newx][newy] = 0;
4516 /* copy element change control values to new field */
4517 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4518 ChangePage[newx][newy] = ChangePage[x][y];
4519 Changed[newx][newy] = Changed[x][y];
4520 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4522 ChangeDelay[x][y] = 0;
4523 ChangePage[x][y] = -1;
4524 Changed[x][y] = CE_BITMASK_DEFAULT;
4525 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4527 /* copy animation control values to new field */
4528 GfxFrame[newx][newy] = GfxFrame[x][y];
4529 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4530 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4532 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4534 ResetGfxAnimation(x, y); /* reset animation values for old field */
4537 /* 2.1.1 (does not work correctly for spring) */
4538 if (!CAN_MOVE(element))
4539 MovDir[newx][newy] = 0;
4543 /* (does not work for falling objects that slide horizontally) */
4544 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4545 MovDir[newx][newy] = 0;
4548 if (!CAN_MOVE(element) ||
4549 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4550 MovDir[newx][newy] = 0;
4553 if (!CAN_MOVE(element) ||
4554 (CAN_FALL(element) && direction == MV_DOWN))
4555 MovDir[newx][newy] = 0;
4560 DrawLevelField(x, y);
4561 DrawLevelField(newx, newy);
4563 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4565 /* prevent pushed element from moving on in pushed direction */
4566 if (pushed && CAN_MOVE(element) &&
4567 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4568 !(element_info[element].move_pattern & direction))
4569 TurnRound(newx, newy);
4571 if (!pushed) /* special case: moving object pushed by player */
4573 WasJustMoving[newx][newy] = 3;
4575 if (CAN_FALL(element) && direction == MV_DOWN)
4576 WasJustFalling[newx][newy] = 3;
4579 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4581 TestIfBadThingTouchesHero(newx, newy);
4582 TestIfBadThingTouchesFriend(newx, newy);
4583 TestIfBadThingTouchesOtherBadThing(newx, newy);
4585 else if (element == EL_PENGUIN)
4586 TestIfFriendTouchesBadThing(newx, newy);
4588 if (CAN_FALL(element) && direction == MV_DOWN &&
4589 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4593 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4597 if (ChangePage[newx][newy] != -1) /* delayed change */
4598 ChangeElement(newx, newy, ChangePage[newx][newy]);
4601 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4602 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4605 TestIfPlayerTouchesCustomElement(newx, newy);
4606 TestIfElementTouchesCustomElement(newx, newy);
4609 int AmoebeNachbarNr(int ax, int ay)
4612 int element = Feld[ax][ay];
4614 static int xy[4][2] =
4624 int x = ax + xy[i][0];
4625 int y = ay + xy[i][1];
4627 if (!IN_LEV_FIELD(x, y))
4630 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4631 group_nr = AmoebaNr[x][y];
4637 void AmoebenVereinigen(int ax, int ay)
4639 int i, x, y, xx, yy;
4640 int new_group_nr = AmoebaNr[ax][ay];
4641 static int xy[4][2] =
4649 if (new_group_nr == 0)
4657 if (!IN_LEV_FIELD(x, y))
4660 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4661 Feld[x][y] == EL_BD_AMOEBA ||
4662 Feld[x][y] == EL_AMOEBA_DEAD) &&
4663 AmoebaNr[x][y] != new_group_nr)
4665 int old_group_nr = AmoebaNr[x][y];
4667 if (old_group_nr == 0)
4670 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4671 AmoebaCnt[old_group_nr] = 0;
4672 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4673 AmoebaCnt2[old_group_nr] = 0;
4675 for (yy=0; yy<lev_fieldy; yy++)
4677 for (xx=0; xx<lev_fieldx; xx++)
4679 if (AmoebaNr[xx][yy] == old_group_nr)
4680 AmoebaNr[xx][yy] = new_group_nr;
4687 void AmoebeUmwandeln(int ax, int ay)
4691 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4693 int group_nr = AmoebaNr[ax][ay];
4698 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4699 printf("AmoebeUmwandeln(): This should never happen!\n");
4704 for (y=0; y<lev_fieldy; y++)
4706 for (x=0; x<lev_fieldx; x++)
4708 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4711 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4715 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4716 SND_AMOEBA_TURNING_TO_GEM :
4717 SND_AMOEBA_TURNING_TO_ROCK));
4722 static int xy[4][2] =
4735 if (!IN_LEV_FIELD(x, y))
4738 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4740 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4741 SND_AMOEBA_TURNING_TO_GEM :
4742 SND_AMOEBA_TURNING_TO_ROCK));
4749 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4752 int group_nr = AmoebaNr[ax][ay];
4753 boolean done = FALSE;
4758 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4759 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4764 for (y=0; y<lev_fieldy; y++)
4766 for (x=0; x<lev_fieldx; x++)
4768 if (AmoebaNr[x][y] == group_nr &&
4769 (Feld[x][y] == EL_AMOEBA_DEAD ||
4770 Feld[x][y] == EL_BD_AMOEBA ||
4771 Feld[x][y] == EL_AMOEBA_GROWING))
4774 Feld[x][y] = new_element;
4775 InitField(x, y, FALSE);
4776 DrawLevelField(x, y);
4783 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4784 SND_BD_AMOEBA_TURNING_TO_ROCK :
4785 SND_BD_AMOEBA_TURNING_TO_GEM));
4788 void AmoebeWaechst(int x, int y)
4790 static unsigned long sound_delay = 0;
4791 static unsigned long sound_delay_value = 0;
4793 if (!MovDelay[x][y]) /* start new growing cycle */
4797 if (DelayReached(&sound_delay, sound_delay_value))
4800 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4802 if (Store[x][y] == EL_BD_AMOEBA)
4803 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4805 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4807 sound_delay_value = 30;
4811 if (MovDelay[x][y]) /* wait some time before growing bigger */
4814 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4816 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4817 6 - MovDelay[x][y]);
4819 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4822 if (!MovDelay[x][y])
4824 Feld[x][y] = Store[x][y];
4826 DrawLevelField(x, y);
4831 void AmoebaDisappearing(int x, int y)
4833 static unsigned long sound_delay = 0;
4834 static unsigned long sound_delay_value = 0;
4836 if (!MovDelay[x][y]) /* start new shrinking cycle */
4840 if (DelayReached(&sound_delay, sound_delay_value))
4841 sound_delay_value = 30;
4844 if (MovDelay[x][y]) /* wait some time before shrinking */
4847 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4849 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4850 6 - MovDelay[x][y]);
4852 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4855 if (!MovDelay[x][y])
4857 Feld[x][y] = EL_EMPTY;
4858 DrawLevelField(x, y);
4860 /* don't let mole enter this field in this cycle;
4861 (give priority to objects falling to this field from above) */
4867 void AmoebeAbleger(int ax, int ay)
4870 int element = Feld[ax][ay];
4871 int graphic = el2img(element);
4872 int newax = ax, neway = ay;
4873 static int xy[4][2] =
4881 if (!level.amoeba_speed)
4883 Feld[ax][ay] = EL_AMOEBA_DEAD;
4884 DrawLevelField(ax, ay);
4888 if (IS_ANIMATED(graphic))
4889 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4891 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4892 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4894 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4897 if (MovDelay[ax][ay])
4901 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4904 int x = ax + xy[start][0];
4905 int y = ay + xy[start][1];
4907 if (!IN_LEV_FIELD(x, y))
4910 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4911 if (IS_FREE(x, y) ||
4912 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4918 if (newax == ax && neway == ay)
4921 else /* normal or "filled" (BD style) amoeba */
4924 boolean waiting_for_player = FALSE;
4928 int j = (start + i) % 4;
4929 int x = ax + xy[j][0];
4930 int y = ay + xy[j][1];
4932 if (!IN_LEV_FIELD(x, y))
4935 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4936 if (IS_FREE(x, y) ||
4937 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4943 else if (IS_PLAYER(x, y))
4944 waiting_for_player = TRUE;
4947 if (newax == ax && neway == ay) /* amoeba cannot grow */
4949 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4951 Feld[ax][ay] = EL_AMOEBA_DEAD;
4952 DrawLevelField(ax, ay);
4953 AmoebaCnt[AmoebaNr[ax][ay]]--;
4955 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
4957 if (element == EL_AMOEBA_FULL)
4958 AmoebeUmwandeln(ax, ay);
4959 else if (element == EL_BD_AMOEBA)
4960 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4965 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4967 /* amoeba gets larger by growing in some direction */
4969 int new_group_nr = AmoebaNr[ax][ay];
4972 if (new_group_nr == 0)
4974 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
4975 printf("AmoebeAbleger(): This should never happen!\n");
4980 AmoebaNr[newax][neway] = new_group_nr;
4981 AmoebaCnt[new_group_nr]++;
4982 AmoebaCnt2[new_group_nr]++;
4984 /* if amoeba touches other amoeba(s) after growing, unify them */
4985 AmoebenVereinigen(newax, neway);
4987 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
4989 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
4995 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
4996 (neway == lev_fieldy - 1 && newax != ax))
4998 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
4999 Store[newax][neway] = element;
5001 else if (neway == ay)
5003 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5005 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5007 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5012 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5013 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5014 Store[ax][ay] = EL_AMOEBA_DROP;
5015 ContinueMoving(ax, ay);
5019 DrawLevelField(newax, neway);
5022 void Life(int ax, int ay)
5025 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5027 int element = Feld[ax][ay];
5028 int graphic = el2img(element);
5029 boolean changed = FALSE;
5031 if (IS_ANIMATED(graphic))
5032 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5037 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5038 MovDelay[ax][ay] = life_time;
5040 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5043 if (MovDelay[ax][ay])
5047 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5049 int xx = ax+x1, yy = ay+y1;
5052 if (!IN_LEV_FIELD(xx, yy))
5055 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5057 int x = xx+x2, y = yy+y2;
5059 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5062 if (((Feld[x][y] == element ||
5063 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5065 (IS_FREE(x, y) && Stop[x][y]))
5069 if (xx == ax && yy == ay) /* field in the middle */
5071 if (nachbarn < life[0] || nachbarn > life[1])
5073 Feld[xx][yy] = EL_EMPTY;
5075 DrawLevelField(xx, yy);
5076 Stop[xx][yy] = TRUE;
5080 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5081 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5082 { /* free border field */
5083 if (nachbarn >= life[2] && nachbarn <= life[3])
5085 Feld[xx][yy] = element;
5086 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5088 DrawLevelField(xx, yy);
5089 Stop[xx][yy] = TRUE;
5096 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5097 SND_GAME_OF_LIFE_GROWING);
5100 static void InitRobotWheel(int x, int y)
5102 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5105 static void RunRobotWheel(int x, int y)
5107 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5110 static void StopRobotWheel(int x, int y)
5112 if (ZX == x && ZY == y)
5116 static void InitTimegateWheel(int x, int y)
5118 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5121 static void RunTimegateWheel(int x, int y)
5123 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5126 void CheckExit(int x, int y)
5128 if (local_player->gems_still_needed > 0 ||
5129 local_player->sokobanfields_still_needed > 0 ||
5130 local_player->lights_still_needed > 0)
5132 int element = Feld[x][y];
5133 int graphic = el2img(element);
5135 if (IS_ANIMATED(graphic))
5136 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5141 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5144 Feld[x][y] = EL_EXIT_OPENING;
5146 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5149 void CheckExitSP(int x, int y)
5151 if (local_player->gems_still_needed > 0)
5153 int element = Feld[x][y];
5154 int graphic = el2img(element);
5156 if (IS_ANIMATED(graphic))
5157 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5162 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5165 Feld[x][y] = EL_SP_EXIT_OPENING;
5167 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5170 static void CloseAllOpenTimegates()
5174 for (y=0; y<lev_fieldy; y++)
5176 for (x=0; x<lev_fieldx; x++)
5178 int element = Feld[x][y];
5180 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5182 Feld[x][y] = EL_TIMEGATE_CLOSING;
5184 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5186 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5193 void EdelsteinFunkeln(int x, int y)
5195 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5198 if (Feld[x][y] == EL_BD_DIAMOND)
5201 if (MovDelay[x][y] == 0) /* next animation frame */
5202 MovDelay[x][y] = 11 * !SimpleRND(500);
5204 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5208 if (setup.direct_draw && MovDelay[x][y])
5209 SetDrawtoField(DRAW_BUFFERED);
5211 DrawLevelElementAnimation(x, y, Feld[x][y]);
5213 if (MovDelay[x][y] != 0)
5215 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5216 10 - MovDelay[x][y]);
5218 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5220 if (setup.direct_draw)
5224 dest_x = FX + SCREENX(x) * TILEX;
5225 dest_y = FY + SCREENY(y) * TILEY;
5227 BlitBitmap(drawto_field, window,
5228 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5229 SetDrawtoField(DRAW_DIRECT);
5235 void MauerWaechst(int x, int y)
5239 if (!MovDelay[x][y]) /* next animation frame */
5240 MovDelay[x][y] = 3 * delay;
5242 if (MovDelay[x][y]) /* wait some time before next frame */
5246 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5248 int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5249 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5251 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5254 if (!MovDelay[x][y])
5256 if (MovDir[x][y] == MV_LEFT)
5258 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5259 DrawLevelField(x - 1, y);
5261 else if (MovDir[x][y] == MV_RIGHT)
5263 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5264 DrawLevelField(x + 1, y);
5266 else if (MovDir[x][y] == MV_UP)
5268 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5269 DrawLevelField(x, y - 1);
5273 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5274 DrawLevelField(x, y + 1);
5277 Feld[x][y] = Store[x][y];
5279 MovDir[x][y] = MV_NO_MOVING;
5280 DrawLevelField(x, y);
5285 void MauerAbleger(int ax, int ay)
5287 int element = Feld[ax][ay];
5288 int graphic = el2img(element);
5289 boolean oben_frei = FALSE, unten_frei = FALSE;
5290 boolean links_frei = FALSE, rechts_frei = FALSE;
5291 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5292 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5293 boolean new_wall = FALSE;
5295 if (IS_ANIMATED(graphic))
5296 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5298 if (!MovDelay[ax][ay]) /* start building new wall */
5299 MovDelay[ax][ay] = 6;
5301 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5304 if (MovDelay[ax][ay])
5308 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5310 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5312 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5314 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5317 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5318 element == EL_EXPANDABLE_WALL_ANY)
5322 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5323 Store[ax][ay-1] = element;
5324 MovDir[ax][ay-1] = MV_UP;
5325 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5326 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5327 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5332 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5333 Store[ax][ay+1] = element;
5334 MovDir[ax][ay+1] = MV_DOWN;
5335 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5336 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5337 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5342 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5343 element == EL_EXPANDABLE_WALL_ANY ||
5344 element == EL_EXPANDABLE_WALL)
5348 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5349 Store[ax-1][ay] = element;
5350 MovDir[ax-1][ay] = MV_LEFT;
5351 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5352 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5353 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5359 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5360 Store[ax+1][ay] = element;
5361 MovDir[ax+1][ay] = MV_RIGHT;
5362 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5363 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5364 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5369 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5370 DrawLevelField(ax, ay);
5372 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5374 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5375 unten_massiv = TRUE;
5376 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5377 links_massiv = TRUE;
5378 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5379 rechts_massiv = TRUE;
5381 if (((oben_massiv && unten_massiv) ||
5382 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5383 element == EL_EXPANDABLE_WALL) &&
5384 ((links_massiv && rechts_massiv) ||
5385 element == EL_EXPANDABLE_WALL_VERTICAL))
5386 Feld[ax][ay] = EL_WALL;
5390 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5392 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5396 void CheckForDragon(int x, int y)
5399 boolean dragon_found = FALSE;
5400 static int xy[4][2] =
5412 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5414 if (IN_LEV_FIELD(xx, yy) &&
5415 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5417 if (Feld[xx][yy] == EL_DRAGON)
5418 dragon_found = TRUE;
5431 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5433 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5435 Feld[xx][yy] = EL_EMPTY;
5436 DrawLevelField(xx, yy);
5445 static void InitBuggyBase(int x, int y)
5447 int element = Feld[x][y];
5448 int activating_delay = FRAMES_PER_SECOND / 4;
5451 (element == EL_SP_BUGGY_BASE ?
5452 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5453 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5455 element == EL_SP_BUGGY_BASE_ACTIVE ?
5456 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5459 static void WarnBuggyBase(int x, int y)
5462 static int xy[4][2] =
5472 int xx = x + xy[i][0], yy = y + xy[i][1];
5474 if (IS_PLAYER(xx, yy))
5476 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5483 static void InitTrap(int x, int y)
5485 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5488 static void ActivateTrap(int x, int y)
5490 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5493 static void ChangeActiveTrap(int x, int y)
5495 int graphic = IMG_TRAP_ACTIVE;
5497 /* if new animation frame was drawn, correct crumbled sand border */
5498 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5499 DrawLevelFieldCrumbledSand(x, y);
5502 static void ChangeElementNowExt(int x, int y, int target_element)
5504 /* check if element under player changes from accessible to unaccessible
5505 (needed for special case of dropping element which then changes) */
5506 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5507 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5514 Feld[x][y] = target_element;
5516 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5518 ResetGfxAnimation(x, y);
5519 ResetRandomAnimationValue(x, y);
5521 InitField(x, y, FALSE);
5522 if (CAN_MOVE(Feld[x][y]))
5525 DrawLevelField(x, y);
5527 if (GFX_CRUMBLED(Feld[x][y]))
5528 DrawLevelFieldCrumbledSandNeighbours(x, y);
5530 TestIfBadThingTouchesHero(x, y);
5531 TestIfPlayerTouchesCustomElement(x, y);
5532 TestIfElementTouchesCustomElement(x, y);
5534 if (ELEM_IS_PLAYER(target_element))
5535 RelocatePlayer(x, y, target_element);
5538 static boolean ChangeElementNow(int x, int y, int element, int page)
5540 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5542 /* always use default change event to prevent running into a loop */
5543 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5544 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5546 /* do not change already changed elements with same change event */
5548 if (Changed[x][y] & ChangeEvent[x][y])
5555 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5557 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5559 if (change->explode)
5566 if (change->use_content)
5568 boolean complete_change = TRUE;
5569 boolean can_change[3][3];
5572 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5574 boolean half_destructible;
5575 int ex = x + xx - 1;
5576 int ey = y + yy - 1;
5579 can_change[xx][yy] = TRUE;
5581 if (ex == x && ey == y) /* do not check changing element itself */
5584 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5586 can_change[xx][yy] = FALSE; /* do not change empty borders */
5591 if (!IN_LEV_FIELD(ex, ey))
5593 can_change[xx][yy] = FALSE;
5594 complete_change = FALSE;
5601 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5602 e = MovingOrBlocked2Element(ex, ey);
5604 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5606 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5607 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5608 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5610 can_change[xx][yy] = FALSE;
5611 complete_change = FALSE;
5615 if (!change->only_complete || complete_change)
5617 boolean something_has_changed = FALSE;
5619 if (change->only_complete && change->use_random_change &&
5620 RND(100) < change->random)
5623 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5625 int ex = x + xx - 1;
5626 int ey = y + yy - 1;
5628 if (can_change[xx][yy] && (!change->use_random_change ||
5629 RND(100) < change->random))
5631 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5632 RemoveMovingField(ex, ey);
5634 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5636 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5638 something_has_changed = TRUE;
5640 /* for symmetry reasons, freeze newly created border elements */
5641 if (ex != x || ey != y)
5642 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5646 if (something_has_changed)
5647 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5652 ChangeElementNowExt(x, y, change->target_element);
5654 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5660 static void ChangeElement(int x, int y, int page)
5662 int element = MovingOrBlocked2Element(x, y);
5663 struct ElementInfo *ei = &element_info[element];
5664 struct ElementChangeInfo *change = &ei->change_page[page];
5668 if (!CAN_CHANGE(element))
5671 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5672 x, y, element, element_info[element].token_name);
5673 printf("ChangeElement(): This should never happen!\n");
5679 if (ChangeDelay[x][y] == 0) /* initialize element change */
5681 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5682 RND(change->delay_random * change->delay_frames)) + 1;
5684 ResetGfxAnimation(x, y);
5685 ResetRandomAnimationValue(x, y);
5687 if (change->pre_change_function)
5688 change->pre_change_function(x, y);
5691 ChangeDelay[x][y]--;
5693 if (ChangeDelay[x][y] != 0) /* continue element change */
5695 int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5697 if (IS_ANIMATED(graphic))
5698 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5700 if (change->change_function)
5701 change->change_function(x, y);
5703 else /* finish element change */
5705 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5707 page = ChangePage[x][y];
5708 ChangePage[x][y] = -1;
5711 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5713 ChangeDelay[x][y] = 1; /* try change after next move step */
5714 ChangePage[x][y] = page; /* remember page to use for change */
5719 if (ChangeElementNow(x, y, element, page))
5721 if (change->post_change_function)
5722 change->post_change_function(x, y);
5727 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5728 int trigger_element,
5734 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5737 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5739 int element = EL_CUSTOM_START + i;
5741 boolean change_element = FALSE;
5744 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5747 for (j=0; j < element_info[element].num_change_pages; j++)
5749 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5751 if (change->can_change &&
5752 change->sides & trigger_side &&
5753 change->trigger_element == trigger_element)
5755 change_element = TRUE;
5762 if (!change_element)
5765 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5768 if (x == lx && y == ly) /* do not change trigger element itself */
5772 if (Feld[x][y] == element)
5774 ChangeDelay[x][y] = 1;
5775 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5776 ChangeElement(x, y, page);
5784 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5787 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5791 static boolean CheckElementSideChange(int x, int y, int element, int side,
5792 int trigger_event, int page)
5794 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5797 if (Feld[x][y] == EL_BLOCKED)
5799 Blocked2Moving(x, y, &x, &y);
5800 element = Feld[x][y];
5804 page = element_info[element].event_page_nr[trigger_event];
5806 if (!(element_info[element].change_page[page].sides & side))
5809 ChangeDelay[x][y] = 1;
5810 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5811 ChangeElement(x, y, page);
5816 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5818 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5821 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5823 static byte stored_player_action[MAX_PLAYERS];
5824 static int num_stored_actions = 0;
5825 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5826 int left = player_action & JOY_LEFT;
5827 int right = player_action & JOY_RIGHT;
5828 int up = player_action & JOY_UP;
5829 int down = player_action & JOY_DOWN;
5830 int button1 = player_action & JOY_BUTTON_1;
5831 int button2 = player_action & JOY_BUTTON_2;
5832 int dx = (left ? -1 : right ? 1 : 0);
5833 int dy = (up ? -1 : down ? 1 : 0);
5835 stored_player_action[player->index_nr] = 0;
5836 num_stored_actions++;
5838 if (!player->active || tape.pausing)
5844 snapped = SnapField(player, dx, dy);
5848 dropped = DropElement(player);
5850 moved = MovePlayer(player, dx, dy);
5853 if (tape.single_step && tape.recording && !tape.pausing)
5855 if (button1 || (dropped && !moved))
5857 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5858 SnapField(player, 0, 0); /* stop snapping */
5862 stored_player_action[player->index_nr] = player_action;
5866 /* no actions for this player (no input at player's configured device) */
5868 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5869 SnapField(player, 0, 0);
5870 CheckGravityMovement(player);
5872 if (player->MovPos == 0)
5873 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5875 if (player->MovPos == 0) /* needed for tape.playing */
5876 player->is_moving = FALSE;
5879 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5881 TapeRecordAction(stored_player_action);
5882 num_stored_actions = 0;
5888 static unsigned long action_delay = 0;
5889 unsigned long action_delay_value;
5890 int magic_wall_x = 0, magic_wall_y = 0;
5891 int i, x, y, element, graphic;
5892 byte *recorded_player_action;
5893 byte summarized_player_action = 0;
5895 if (game_status != GAME_MODE_PLAYING)
5898 action_delay_value =
5899 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5901 if (tape.playing && tape.index_search && !tape.pausing)
5902 action_delay_value = 0;
5904 /* ---------- main game synchronization point ---------- */
5906 WaitUntilDelayReached(&action_delay, action_delay_value);
5908 if (network_playing && !network_player_action_received)
5912 printf("DEBUG: try to get network player actions in time\n");
5916 #if defined(PLATFORM_UNIX)
5917 /* last chance to get network player actions without main loop delay */
5921 if (game_status != GAME_MODE_PLAYING)
5924 if (!network_player_action_received)
5928 printf("DEBUG: failed to get network player actions in time\n");
5938 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5940 for (i=0; i<MAX_PLAYERS; i++)
5942 summarized_player_action |= stored_player[i].action;
5944 if (!network_playing)
5945 stored_player[i].effective_action = stored_player[i].action;
5948 #if defined(PLATFORM_UNIX)
5949 if (network_playing)
5950 SendToServer_MovePlayer(summarized_player_action);
5953 if (!options.network && !setup.team_mode)
5954 local_player->effective_action = summarized_player_action;
5956 for (i=0; i<MAX_PLAYERS; i++)
5958 int actual_player_action = stored_player[i].effective_action;
5960 if (stored_player[i].programmed_action)
5961 actual_player_action = stored_player[i].programmed_action;
5963 if (recorded_player_action)
5964 actual_player_action = recorded_player_action[i];
5966 PlayerActions(&stored_player[i], actual_player_action);
5967 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
5970 network_player_action_received = FALSE;
5972 ScrollScreen(NULL, SCROLL_GO_ON);
5978 for (i=0; i<MAX_PLAYERS; i++)
5979 stored_player[i].Frame++;
5983 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
5985 for (i=0; i<MAX_PLAYERS; i++)
5987 struct PlayerInfo *player = &stored_player[i];
5991 if (player->active && player->is_pushing && player->is_moving &&
5994 ContinueMoving(x, y);
5996 /* continue moving after pushing (this is actually a bug) */
5997 if (!IS_MOVING(x, y))
6006 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6008 Changed[x][y] = CE_BITMASK_DEFAULT;
6009 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6012 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6014 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6015 printf("GameActions(): This should never happen!\n");
6017 ChangePage[x][y] = -1;
6022 if (WasJustMoving[x][y] > 0)
6023 WasJustMoving[x][y]--;
6024 if (WasJustFalling[x][y] > 0)
6025 WasJustFalling[x][y]--;
6030 /* reset finished pushing action (not done in ContinueMoving() to allow
6031 continous pushing animation for elements with zero push delay) */
6032 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6034 ResetGfxAnimation(x, y);
6035 DrawLevelField(x, y);
6040 if (IS_BLOCKED(x, y))
6044 Blocked2Moving(x, y, &oldx, &oldy);
6045 if (!IS_MOVING(oldx, oldy))
6047 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6048 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6049 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6050 printf("GameActions(): This should never happen!\n");
6056 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6058 element = Feld[x][y];
6060 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6062 graphic = el2img(element);
6068 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6070 element = graphic = 0;
6074 if (graphic_info[graphic].anim_global_sync)
6075 GfxFrame[x][y] = FrameCounter;
6077 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6078 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6079 ResetRandomAnimationValue(x, y);
6081 SetRandomAnimationValue(x, y);
6084 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6087 if (IS_INACTIVE(element))
6089 if (IS_ANIMATED(graphic))
6090 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6096 /* this may take place after moving, so 'element' may have changed */
6097 if (IS_CHANGING(x, y))
6100 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6101 element_info[element].event_page_nr[CE_DELAY]);
6103 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6106 element = Feld[x][y];
6107 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6111 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6116 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6118 if (element == EL_MOLE)
6119 printf("::: %d, %d, %d [%d]\n",
6120 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6124 if (element == EL_YAMYAM)
6125 printf("::: %d, %d, %d\n",
6126 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6130 if (IS_ANIMATED(graphic) &&
6134 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6137 if (element == EL_MOLE)
6138 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6142 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6143 EdelsteinFunkeln(x, y);
6145 else if ((element == EL_ACID ||
6146 element == EL_EXIT_OPEN ||
6147 element == EL_SP_EXIT_OPEN ||
6148 element == EL_SP_TERMINAL ||
6149 element == EL_SP_TERMINAL_ACTIVE ||
6150 element == EL_EXTRA_TIME ||
6151 element == EL_SHIELD_NORMAL ||
6152 element == EL_SHIELD_DEADLY) &&
6153 IS_ANIMATED(graphic))
6154 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6155 else if (IS_MOVING(x, y))
6156 ContinueMoving(x, y);
6157 else if (IS_ACTIVE_BOMB(element))
6158 CheckDynamite(x, y);
6160 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6161 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6163 else if (element == EL_AMOEBA_GROWING)
6164 AmoebeWaechst(x, y);
6165 else if (element == EL_AMOEBA_SHRINKING)
6166 AmoebaDisappearing(x, y);
6168 #if !USE_NEW_AMOEBA_CODE
6169 else if (IS_AMOEBALIVE(element))
6170 AmoebeAbleger(x, y);
6173 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6175 else if (element == EL_EXIT_CLOSED)
6177 else if (element == EL_SP_EXIT_CLOSED)
6179 else if (element == EL_EXPANDABLE_WALL_GROWING)
6181 else if (element == EL_EXPANDABLE_WALL ||
6182 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6183 element == EL_EXPANDABLE_WALL_VERTICAL ||
6184 element == EL_EXPANDABLE_WALL_ANY)
6186 else if (element == EL_FLAMES)
6187 CheckForDragon(x, y);
6189 else if (IS_AUTO_CHANGING(element))
6190 ChangeElement(x, y);
6192 else if (element == EL_EXPLOSION)
6193 ; /* drawing of correct explosion animation is handled separately */
6194 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6195 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6198 /* this may take place after moving, so 'element' may have changed */
6199 if (IS_AUTO_CHANGING(Feld[x][y]))
6200 ChangeElement(x, y);
6203 if (IS_BELT_ACTIVE(element))
6204 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6206 if (game.magic_wall_active)
6208 int jx = local_player->jx, jy = local_player->jy;
6210 /* play the element sound at the position nearest to the player */
6211 if ((element == EL_MAGIC_WALL_FULL ||
6212 element == EL_MAGIC_WALL_ACTIVE ||
6213 element == EL_MAGIC_WALL_EMPTYING ||
6214 element == EL_BD_MAGIC_WALL_FULL ||
6215 element == EL_BD_MAGIC_WALL_ACTIVE ||
6216 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6217 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6225 #if USE_NEW_AMOEBA_CODE
6226 /* new experimental amoeba growth stuff */
6228 if (!(FrameCounter % 8))
6231 static unsigned long random = 1684108901;
6233 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6236 x = (random >> 10) % lev_fieldx;
6237 y = (random >> 20) % lev_fieldy;
6239 x = RND(lev_fieldx);
6240 y = RND(lev_fieldy);
6242 element = Feld[x][y];
6244 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6245 if (!IS_PLAYER(x,y) &&
6246 (element == EL_EMPTY ||
6247 element == EL_SAND ||
6248 element == EL_QUICKSAND_EMPTY ||
6249 element == EL_ACID_SPLASH_LEFT ||
6250 element == EL_ACID_SPLASH_RIGHT))
6252 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6253 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6254 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6255 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6256 Feld[x][y] = EL_AMOEBA_DROP;
6259 random = random * 129 + 1;
6265 if (game.explosions_delayed)
6268 game.explosions_delayed = FALSE;
6270 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6272 element = Feld[x][y];
6274 if (ExplodeField[x][y])
6275 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6276 else if (element == EL_EXPLOSION)
6277 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6279 ExplodeField[x][y] = EX_NO_EXPLOSION;
6282 game.explosions_delayed = TRUE;
6285 if (game.magic_wall_active)
6287 if (!(game.magic_wall_time_left % 4))
6289 int element = Feld[magic_wall_x][magic_wall_y];
6291 if (element == EL_BD_MAGIC_WALL_FULL ||
6292 element == EL_BD_MAGIC_WALL_ACTIVE ||
6293 element == EL_BD_MAGIC_WALL_EMPTYING)
6294 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6296 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6299 if (game.magic_wall_time_left > 0)
6301 game.magic_wall_time_left--;
6302 if (!game.magic_wall_time_left)
6304 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6306 element = Feld[x][y];
6308 if (element == EL_MAGIC_WALL_ACTIVE ||
6309 element == EL_MAGIC_WALL_FULL)
6311 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6312 DrawLevelField(x, y);
6314 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6315 element == EL_BD_MAGIC_WALL_FULL)
6317 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6318 DrawLevelField(x, y);
6322 game.magic_wall_active = FALSE;
6327 if (game.light_time_left > 0)
6329 game.light_time_left--;
6331 if (game.light_time_left == 0)
6332 RedrawAllLightSwitchesAndInvisibleElements();
6335 if (game.timegate_time_left > 0)
6337 game.timegate_time_left--;
6339 if (game.timegate_time_left == 0)
6340 CloseAllOpenTimegates();
6343 for (i=0; i<MAX_PLAYERS; i++)
6345 struct PlayerInfo *player = &stored_player[i];
6347 if (SHIELD_ON(player))
6349 if (player->shield_deadly_time_left)
6350 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6351 else if (player->shield_normal_time_left)
6352 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6356 if (TimeFrames >= FRAMES_PER_SECOND)
6361 for (i=0; i<MAX_PLAYERS; i++)
6363 struct PlayerInfo *player = &stored_player[i];
6365 if (SHIELD_ON(player))
6367 player->shield_normal_time_left--;
6369 if (player->shield_deadly_time_left > 0)
6370 player->shield_deadly_time_left--;
6374 if (tape.recording || tape.playing)
6375 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6381 if (TimeLeft <= 10 && setup.time_limit)
6382 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6384 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6386 if (!TimeLeft && setup.time_limit)
6387 for (i=0; i<MAX_PLAYERS; i++)
6388 KillHero(&stored_player[i]);
6390 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6391 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6396 if (options.debug) /* calculate frames per second */
6398 static unsigned long fps_counter = 0;
6399 static int fps_frames = 0;
6400 unsigned long fps_delay_ms = Counter() - fps_counter;
6404 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6406 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6409 fps_counter = Counter();
6412 redraw_mask |= REDRAW_FPS;
6416 if (stored_player[0].jx != stored_player[0].last_jx ||
6417 stored_player[0].jy != stored_player[0].last_jy)
6418 printf("::: %d, %d, %d, %d, %d\n",
6419 stored_player[0].MovDir,
6420 stored_player[0].MovPos,
6421 stored_player[0].GfxPos,
6422 stored_player[0].Frame,
6423 stored_player[0].StepFrame);
6430 for (i=0; i<MAX_PLAYERS; i++)
6433 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6435 stored_player[i].Frame += move_frames;
6437 if (stored_player[i].MovPos != 0)
6438 stored_player[i].StepFrame += move_frames;
6443 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6445 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6447 local_player->show_envelope = 0;
6452 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6454 int min_x = x, min_y = y, max_x = x, max_y = y;
6457 for (i=0; i<MAX_PLAYERS; i++)
6459 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6461 if (!stored_player[i].active || &stored_player[i] == player)
6464 min_x = MIN(min_x, jx);
6465 min_y = MIN(min_y, jy);
6466 max_x = MAX(max_x, jx);
6467 max_y = MAX(max_y, jy);
6470 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6473 static boolean AllPlayersInVisibleScreen()
6477 for (i=0; i<MAX_PLAYERS; i++)
6479 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6481 if (!stored_player[i].active)
6484 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6491 void ScrollLevel(int dx, int dy)
6493 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6496 BlitBitmap(drawto_field, drawto_field,
6497 FX + TILEX * (dx == -1) - softscroll_offset,
6498 FY + TILEY * (dy == -1) - softscroll_offset,
6499 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6500 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6501 FX + TILEX * (dx == 1) - softscroll_offset,
6502 FY + TILEY * (dy == 1) - softscroll_offset);
6506 x = (dx == 1 ? BX1 : BX2);
6507 for (y=BY1; y <= BY2; y++)
6508 DrawScreenField(x, y);
6513 y = (dy == 1 ? BY1 : BY2);
6514 for (x=BX1; x <= BX2; x++)
6515 DrawScreenField(x, y);
6518 redraw_mask |= REDRAW_FIELD;
6521 static void CheckGravityMovement(struct PlayerInfo *player)
6523 if (game.gravity && !player->programmed_action)
6525 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6526 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6528 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6529 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6530 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6531 int jx = player->jx, jy = player->jy;
6532 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6533 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6534 int new_jx = jx + dx, new_jy = jy + dy;
6535 boolean field_under_player_is_free =
6536 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6537 boolean player_is_moving_to_valid_field =
6538 (IN_LEV_FIELD(new_jx, new_jy) &&
6539 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6540 Feld[new_jx][new_jy] == EL_SAND));
6541 /* !!! extend EL_SAND to anything diggable !!! */
6543 if (field_under_player_is_free &&
6544 !player_is_moving_to_valid_field &&
6545 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6546 player->programmed_action = MV_DOWN;
6552 -----------------------------------------------------------------------------
6553 dx, dy: direction (non-diagonal) to try to move the player to
6554 real_dx, real_dy: direction as read from input device (can be diagonal)
6557 boolean MovePlayerOneStep(struct PlayerInfo *player,
6558 int dx, int dy, int real_dx, int real_dy)
6561 static int change_sides[4][2] =
6563 /* enter side leave side */
6564 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6565 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6566 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6567 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6569 int move_direction = (dx == -1 ? MV_LEFT :
6570 dx == +1 ? MV_RIGHT :
6572 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6573 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6574 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6576 int jx = player->jx, jy = player->jy;
6577 int new_jx = jx + dx, new_jy = jy + dy;
6581 if (!player->active || (!dx && !dy))
6582 return MF_NO_ACTION;
6584 player->MovDir = (dx < 0 ? MV_LEFT :
6587 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6589 if (!IN_LEV_FIELD(new_jx, new_jy))
6590 return MF_NO_ACTION;
6592 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6593 return MF_NO_ACTION;
6596 element = MovingOrBlocked2Element(new_jx, new_jy);
6598 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6601 if (DONT_RUN_INTO(element))
6603 if (element == EL_ACID && dx == 0 && dy == 1)
6606 Feld[jx][jy] = EL_PLAYER_1;
6607 InitMovingField(jx, jy, MV_DOWN);
6608 Store[jx][jy] = EL_ACID;
6609 ContinueMoving(jx, jy);
6613 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6618 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6619 if (can_move != MF_MOVING)
6622 /* check if DigField() has caused relocation of the player */
6623 if (player->jx != jx || player->jy != jy)
6624 return MF_NO_ACTION;
6626 StorePlayer[jx][jy] = 0;
6627 player->last_jx = jx;
6628 player->last_jy = jy;
6629 player->jx = new_jx;
6630 player->jy = new_jy;
6631 StorePlayer[new_jx][new_jy] = player->element_nr;
6634 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6636 ScrollPlayer(player, SCROLL_INIT);
6639 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6641 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6642 CE_OTHER_GETS_LEFT);
6643 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6644 CE_LEFT_BY_PLAYER, -1);
6647 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6649 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6650 enter_side, CE_OTHER_GETS_ENTERED);
6651 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6652 CE_ENTERED_BY_PLAYER, -1);
6659 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6661 int jx = player->jx, jy = player->jy;
6662 int old_jx = jx, old_jy = jy;
6663 int moved = MF_NO_ACTION;
6665 if (!player->active || (!dx && !dy))
6669 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6673 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6674 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6678 /* remove the last programmed player action */
6679 player->programmed_action = 0;
6683 /* should only happen if pre-1.2 tape recordings are played */
6684 /* this is only for backward compatibility */
6686 int original_move_delay_value = player->move_delay_value;
6689 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6693 /* scroll remaining steps with finest movement resolution */
6694 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6696 while (player->MovPos)
6698 ScrollPlayer(player, SCROLL_GO_ON);
6699 ScrollScreen(NULL, SCROLL_GO_ON);
6705 player->move_delay_value = original_move_delay_value;
6708 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6710 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6711 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6715 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6716 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6722 if (moved & MF_MOVING && !ScreenMovPos &&
6723 (player == local_player || !options.network))
6725 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6726 int offset = (setup.scroll_delay ? 3 : 0);
6728 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6730 /* actual player has left the screen -- scroll in that direction */
6731 if (jx != old_jx) /* player has moved horizontally */
6732 scroll_x += (jx - old_jx);
6733 else /* player has moved vertically */
6734 scroll_y += (jy - old_jy);
6738 if (jx != old_jx) /* player has moved horizontally */
6740 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6741 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6742 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6744 /* don't scroll over playfield boundaries */
6745 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6746 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6748 /* don't scroll more than one field at a time */
6749 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6751 /* don't scroll against the player's moving direction */
6752 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6753 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6754 scroll_x = old_scroll_x;
6756 else /* player has moved vertically */
6758 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6759 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6760 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6762 /* don't scroll over playfield boundaries */
6763 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6764 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6766 /* don't scroll more than one field at a time */
6767 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6769 /* don't scroll against the player's moving direction */
6770 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6771 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6772 scroll_y = old_scroll_y;
6776 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6778 if (!options.network && !AllPlayersInVisibleScreen())
6780 scroll_x = old_scroll_x;
6781 scroll_y = old_scroll_y;
6785 ScrollScreen(player, SCROLL_INIT);
6786 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6793 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6795 if (!(moved & MF_MOVING) && !player->is_pushing)
6800 player->StepFrame = 0;
6802 if (moved & MF_MOVING)
6804 if (old_jx != jx && old_jy == jy)
6805 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6806 else if (old_jx == jx && old_jy != jy)
6807 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6809 DrawLevelField(jx, jy); /* for "crumbled sand" */
6811 player->last_move_dir = player->MovDir;
6812 player->is_moving = TRUE;
6814 player->is_snapping = FALSE;
6818 player->is_switching = FALSE;
6824 static int change_sides[4][2] =
6826 /* enter side leave side */
6827 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6828 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6829 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6830 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6832 int move_direction = player->MovDir;
6833 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6834 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6837 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6839 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6840 leave_side, CE_OTHER_GETS_LEFT);
6841 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6842 leave_side, CE_LEFT_BY_PLAYER, -1);
6845 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6847 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6848 enter_side, CE_OTHER_GETS_ENTERED);
6849 CheckElementSideChange(jx, jy, Feld[jx][jy],
6850 enter_side, CE_ENTERED_BY_PLAYER, -1);
6861 CheckGravityMovement(player);
6864 player->last_move_dir = MV_NO_MOVING;
6866 player->is_moving = FALSE;
6869 if (game.engine_version < VERSION_IDENT(3,0,7))
6871 TestIfHeroTouchesBadThing(jx, jy);
6872 TestIfPlayerTouchesCustomElement(jx, jy);
6875 if (!player->active)
6881 void ScrollPlayer(struct PlayerInfo *player, int mode)
6883 int jx = player->jx, jy = player->jy;
6884 int last_jx = player->last_jx, last_jy = player->last_jy;
6885 int move_stepsize = TILEX / player->move_delay_value;
6887 if (!player->active || !player->MovPos)
6890 if (mode == SCROLL_INIT)
6892 player->actual_frame_counter = FrameCounter;
6893 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6895 if (Feld[last_jx][last_jy] == EL_EMPTY)
6896 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6903 else if (!FrameReached(&player->actual_frame_counter, 1))
6906 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6907 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6909 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6910 Feld[last_jx][last_jy] = EL_EMPTY;
6912 /* before DrawPlayer() to draw correct player graphic for this case */
6913 if (player->MovPos == 0)
6914 CheckGravityMovement(player);
6917 DrawPlayer(player); /* needed here only to cleanup last field */
6920 if (player->MovPos == 0) /* player reached destination field */
6922 if (IS_PASSABLE(Feld[last_jx][last_jy]))
6924 /* continue with normal speed after quickly moving through gate */
6925 HALVE_PLAYER_SPEED(player);
6927 /* be able to make the next move without delay */
6928 player->move_delay = 0;
6931 player->last_jx = jx;
6932 player->last_jy = jy;
6934 if (Feld[jx][jy] == EL_EXIT_OPEN ||
6935 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6936 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
6938 DrawPlayer(player); /* needed here only to cleanup last field */
6941 if (local_player->friends_still_needed == 0 ||
6942 IS_SP_ELEMENT(Feld[jx][jy]))
6943 player->LevelSolved = player->GameOver = TRUE;
6946 if (game.engine_version >= VERSION_IDENT(3,0,7))
6948 TestIfHeroTouchesBadThing(jx, jy);
6949 TestIfPlayerTouchesCustomElement(jx, jy);
6951 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
6954 if (!player->active)
6958 if (tape.single_step && tape.recording && !tape.pausing &&
6959 !player->programmed_action)
6960 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6964 void ScrollScreen(struct PlayerInfo *player, int mode)
6966 static unsigned long screen_frame_counter = 0;
6968 if (mode == SCROLL_INIT)
6970 /* set scrolling step size according to actual player's moving speed */
6971 ScrollStepSize = TILEX / player->move_delay_value;
6973 screen_frame_counter = FrameCounter;
6974 ScreenMovDir = player->MovDir;
6975 ScreenMovPos = player->MovPos;
6976 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6979 else if (!FrameReached(&screen_frame_counter, 1))
6984 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
6985 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6986 redraw_mask |= REDRAW_FIELD;
6989 ScreenMovDir = MV_NO_MOVING;
6992 void TestIfPlayerTouchesCustomElement(int x, int y)
6994 static int xy[4][2] =
7001 static int change_sides[4][2] =
7003 /* center side border side */
7004 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7005 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7006 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7007 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7009 static int touch_dir[4] =
7016 int center_element = Feld[x][y]; /* should always be non-moving! */
7021 int xx = x + xy[i][0];
7022 int yy = y + xy[i][1];
7023 int center_side = change_sides[i][0];
7024 int border_side = change_sides[i][1];
7027 if (!IN_LEV_FIELD(xx, yy))
7030 if (IS_PLAYER(x, y))
7032 if (game.engine_version < VERSION_IDENT(3,0,7))
7033 border_element = Feld[xx][yy]; /* may be moving! */
7034 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7035 border_element = Feld[xx][yy];
7036 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7037 border_element = MovingOrBlocked2Element(xx, yy);
7039 continue; /* center and border element do not touch */
7041 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7042 CE_OTHER_GETS_TOUCHED);
7043 CheckElementSideChange(xx, yy, border_element, border_side,
7044 CE_TOUCHED_BY_PLAYER, -1);
7046 else if (IS_PLAYER(xx, yy))
7048 if (game.engine_version >= VERSION_IDENT(3,0,7))
7050 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7052 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7053 continue; /* center and border element do not touch */
7056 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7057 CE_OTHER_GETS_TOUCHED);
7058 CheckElementSideChange(x, y, center_element, center_side,
7059 CE_TOUCHED_BY_PLAYER, -1);
7066 void TestIfElementTouchesCustomElement(int x, int y)
7068 static int xy[4][2] =
7075 static int change_sides[4][2] =
7077 /* center side border side */
7078 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7079 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7080 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7081 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7083 static int touch_dir[4] =
7090 boolean change_center_element = FALSE;
7091 int center_element_change_page = 0;
7092 int center_element = Feld[x][y]; /* should always be non-moving! */
7097 int xx = x + xy[i][0];
7098 int yy = y + xy[i][1];
7099 int center_side = change_sides[i][0];
7100 int border_side = change_sides[i][1];
7103 if (!IN_LEV_FIELD(xx, yy))
7106 if (game.engine_version < VERSION_IDENT(3,0,7))
7107 border_element = Feld[xx][yy]; /* may be moving! */
7108 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7109 border_element = Feld[xx][yy];
7110 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7111 border_element = MovingOrBlocked2Element(xx, yy);
7113 continue; /* center and border element do not touch */
7115 /* check for change of center element (but change it only once) */
7116 if (IS_CUSTOM_ELEMENT(center_element) &&
7117 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7118 !change_center_element)
7120 for (j=0; j < element_info[center_element].num_change_pages; j++)
7122 struct ElementChangeInfo *change =
7123 &element_info[center_element].change_page[j];
7125 if (change->can_change &&
7126 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7127 change->sides & border_side &&
7128 change->trigger_element == border_element)
7130 change_center_element = TRUE;
7131 center_element_change_page = j;
7138 /* check for change of border element */
7139 if (IS_CUSTOM_ELEMENT(border_element) &&
7140 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7142 for (j=0; j < element_info[border_element].num_change_pages; j++)
7144 struct ElementChangeInfo *change =
7145 &element_info[border_element].change_page[j];
7147 if (change->can_change &&
7148 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7149 change->sides & center_side &&
7150 change->trigger_element == center_element)
7152 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7153 CE_OTHER_IS_TOUCHING, j);
7160 if (change_center_element)
7161 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7162 CE_OTHER_IS_TOUCHING, center_element_change_page);
7165 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7167 int i, kill_x = -1, kill_y = -1;
7168 static int test_xy[4][2] =
7175 static int test_dir[4] =
7185 int test_x, test_y, test_move_dir, test_element;
7187 test_x = good_x + test_xy[i][0];
7188 test_y = good_y + test_xy[i][1];
7189 if (!IN_LEV_FIELD(test_x, test_y))
7193 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7196 test_element = Feld[test_x][test_y];
7198 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7201 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7202 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7204 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7205 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7213 if (kill_x != -1 || kill_y != -1)
7215 if (IS_PLAYER(good_x, good_y))
7217 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7219 if (player->shield_deadly_time_left > 0)
7220 Bang(kill_x, kill_y);
7221 else if (!PLAYER_PROTECTED(good_x, good_y))
7225 Bang(good_x, good_y);
7229 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7231 int i, kill_x = -1, kill_y = -1;
7232 int bad_element = Feld[bad_x][bad_y];
7233 static int test_xy[4][2] =
7240 static int touch_dir[4] =
7247 static int test_dir[4] =
7255 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7260 int test_x, test_y, test_move_dir, test_element;
7262 test_x = bad_x + test_xy[i][0];
7263 test_y = bad_y + test_xy[i][1];
7264 if (!IN_LEV_FIELD(test_x, test_y))
7268 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7270 test_element = Feld[test_x][test_y];
7272 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7273 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7275 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7276 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7278 /* good thing is player or penguin that does not move away */
7279 if (IS_PLAYER(test_x, test_y))
7281 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7283 if (bad_element == EL_ROBOT && player->is_moving)
7284 continue; /* robot does not kill player if he is moving */
7286 if (game.engine_version >= VERSION_IDENT(3,0,7))
7288 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7289 continue; /* center and border element do not touch */
7296 else if (test_element == EL_PENGUIN)
7305 if (kill_x != -1 || kill_y != -1)
7307 if (IS_PLAYER(kill_x, kill_y))
7309 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7311 if (player->shield_deadly_time_left > 0)
7313 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7317 Bang(kill_x, kill_y);
7321 void TestIfHeroTouchesBadThing(int x, int y)
7323 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7326 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7328 TestIfGoodThingHitsBadThing(x, y, move_dir);
7331 void TestIfBadThingTouchesHero(int x, int y)
7333 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7336 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7338 TestIfBadThingHitsGoodThing(x, y, move_dir);
7341 void TestIfFriendTouchesBadThing(int x, int y)
7343 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7346 void TestIfBadThingTouchesFriend(int x, int y)
7348 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7351 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7353 int i, kill_x = bad_x, kill_y = bad_y;
7354 static int xy[4][2] =
7366 x = bad_x + xy[i][0];
7367 y = bad_y + xy[i][1];
7368 if (!IN_LEV_FIELD(x, y))
7371 element = Feld[x][y];
7372 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7373 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7381 if (kill_x != bad_x || kill_y != bad_y)
7385 void KillHero(struct PlayerInfo *player)
7387 int jx = player->jx, jy = player->jy;
7389 if (!player->active)
7392 /* remove accessible field at the player's position */
7393 Feld[jx][jy] = EL_EMPTY;
7395 /* deactivate shield (else Bang()/Explode() would not work right) */
7396 player->shield_normal_time_left = 0;
7397 player->shield_deadly_time_left = 0;
7403 static void KillHeroUnlessProtected(int x, int y)
7405 if (!PLAYER_PROTECTED(x, y))
7406 KillHero(PLAYERINFO(x, y));
7409 void BuryHero(struct PlayerInfo *player)
7411 int jx = player->jx, jy = player->jy;
7413 if (!player->active)
7417 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7419 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7421 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7423 player->GameOver = TRUE;
7427 void RemoveHero(struct PlayerInfo *player)
7429 int jx = player->jx, jy = player->jy;
7430 int i, found = FALSE;
7432 player->present = FALSE;
7433 player->active = FALSE;
7435 if (!ExplodeField[jx][jy])
7436 StorePlayer[jx][jy] = 0;
7438 for (i=0; i<MAX_PLAYERS; i++)
7439 if (stored_player[i].active)
7443 AllPlayersGone = TRUE;
7450 =============================================================================
7451 checkDiagonalPushing()
7452 -----------------------------------------------------------------------------
7453 check if diagonal input device direction results in pushing of object
7454 (by checking if the alternative direction is walkable, diggable, ...)
7455 =============================================================================
7458 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7459 int x, int y, int real_dx, int real_dy)
7461 int jx, jy, dx, dy, xx, yy;
7463 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7466 /* diagonal direction: check alternative direction */
7471 xx = jx + (dx == 0 ? real_dx : 0);
7472 yy = jy + (dy == 0 ? real_dy : 0);
7474 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7478 =============================================================================
7480 -----------------------------------------------------------------------------
7481 x, y: field next to player (non-diagonal) to try to dig to
7482 real_dx, real_dy: direction as read from input device (can be diagonal)
7483 =============================================================================
7486 int DigField(struct PlayerInfo *player,
7487 int x, int y, int real_dx, int real_dy, int mode)
7489 static int change_sides[4] =
7491 CH_SIDE_RIGHT, /* moving left */
7492 CH_SIDE_LEFT, /* moving right */
7493 CH_SIDE_BOTTOM, /* moving up */
7494 CH_SIDE_TOP, /* moving down */
7496 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
7497 int jx = player->jx, jy = player->jy;
7498 int dx = x - jx, dy = y - jy;
7499 int nextx = x + dx, nexty = y + dy;
7500 int move_direction = (dx == -1 ? MV_LEFT :
7501 dx == +1 ? MV_RIGHT :
7503 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7504 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7507 if (player->MovPos == 0)
7509 player->is_digging = FALSE;
7510 player->is_collecting = FALSE;
7513 if (player->MovPos == 0) /* last pushing move finished */
7514 player->is_pushing = FALSE;
7516 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7518 player->is_switching = FALSE;
7519 player->push_delay = 0;
7521 return MF_NO_ACTION;
7524 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7525 return MF_NO_ACTION;
7528 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7530 if (IS_TUBE(Feld[jx][jy]) ||
7531 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
7535 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7536 int tube_leave_directions[][2] =
7538 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7539 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7540 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7541 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7542 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7543 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7544 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7545 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7546 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7547 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7548 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7549 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7552 while (tube_leave_directions[i][0] != tube_element)
7555 if (tube_leave_directions[i][0] == -1) /* should not happen */
7559 if (!(tube_leave_directions[i][1] & move_direction))
7560 return MF_NO_ACTION; /* tube has no opening in this direction */
7563 element = Feld[x][y];
7565 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7566 game.engine_version >= VERSION_IDENT(2,2,0))
7567 return MF_NO_ACTION;
7571 case EL_SP_PORT_LEFT:
7572 case EL_SP_PORT_RIGHT:
7574 case EL_SP_PORT_DOWN:
7575 case EL_SP_PORT_HORIZONTAL:
7576 case EL_SP_PORT_VERTICAL:
7577 case EL_SP_PORT_ANY:
7578 case EL_SP_GRAVITY_PORT_LEFT:
7579 case EL_SP_GRAVITY_PORT_RIGHT:
7580 case EL_SP_GRAVITY_PORT_UP:
7581 case EL_SP_GRAVITY_PORT_DOWN:
7583 element != EL_SP_PORT_LEFT &&
7584 element != EL_SP_GRAVITY_PORT_LEFT &&
7585 element != EL_SP_PORT_HORIZONTAL &&
7586 element != EL_SP_PORT_ANY) ||
7588 element != EL_SP_PORT_RIGHT &&
7589 element != EL_SP_GRAVITY_PORT_RIGHT &&
7590 element != EL_SP_PORT_HORIZONTAL &&
7591 element != EL_SP_PORT_ANY) ||
7593 element != EL_SP_PORT_UP &&
7594 element != EL_SP_GRAVITY_PORT_UP &&
7595 element != EL_SP_PORT_VERTICAL &&
7596 element != EL_SP_PORT_ANY) ||
7598 element != EL_SP_PORT_DOWN &&
7599 element != EL_SP_GRAVITY_PORT_DOWN &&
7600 element != EL_SP_PORT_VERTICAL &&
7601 element != EL_SP_PORT_ANY) ||
7602 !IN_LEV_FIELD(nextx, nexty) ||
7603 !IS_FREE(nextx, nexty))
7604 return MF_NO_ACTION;
7606 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7607 element == EL_SP_GRAVITY_PORT_RIGHT ||
7608 element == EL_SP_GRAVITY_PORT_UP ||
7609 element == EL_SP_GRAVITY_PORT_DOWN)
7610 game.gravity = !game.gravity;
7612 /* automatically move to the next field with double speed */
7613 player->programmed_action = move_direction;
7614 DOUBLE_PLAYER_SPEED(player);
7616 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7620 case EL_TUBE_VERTICAL:
7621 case EL_TUBE_HORIZONTAL:
7622 case EL_TUBE_VERTICAL_LEFT:
7623 case EL_TUBE_VERTICAL_RIGHT:
7624 case EL_TUBE_HORIZONTAL_UP:
7625 case EL_TUBE_HORIZONTAL_DOWN:
7626 case EL_TUBE_LEFT_UP:
7627 case EL_TUBE_LEFT_DOWN:
7628 case EL_TUBE_RIGHT_UP:
7629 case EL_TUBE_RIGHT_DOWN:
7632 int tube_enter_directions[][2] =
7634 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7635 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7636 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7637 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7638 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7639 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7640 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7641 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7642 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7643 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7644 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7645 { -1, MV_NO_MOVING }
7648 while (tube_enter_directions[i][0] != element)
7651 if (tube_enter_directions[i][0] == -1) /* should not happen */
7655 if (!(tube_enter_directions[i][1] & move_direction))
7656 return MF_NO_ACTION; /* tube has no opening in this direction */
7658 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7664 if (IS_WALKABLE(element))
7666 int sound_action = ACTION_WALKING;
7668 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7670 if (!player->key[element - EL_GATE_1])
7671 return MF_NO_ACTION;
7673 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7675 if (!player->key[element - EL_GATE_1_GRAY])
7676 return MF_NO_ACTION;
7678 else if (element == EL_EXIT_OPEN ||
7679 element == EL_SP_EXIT_OPEN ||
7680 element == EL_SP_EXIT_OPENING)
7682 sound_action = ACTION_PASSING; /* player is passing exit */
7684 else if (element == EL_EMPTY)
7686 sound_action = ACTION_MOVING; /* nothing to walk on */
7689 /* play sound from background or player, whatever is available */
7690 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7691 PlaySoundLevelElementAction(x, y, element, sound_action);
7693 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7697 else if (IS_PASSABLE(element))
7699 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7700 return MF_NO_ACTION;
7703 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7704 return MF_NO_ACTION;
7707 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7709 if (!player->key[element - EL_EM_GATE_1])
7710 return MF_NO_ACTION;
7712 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7714 if (!player->key[element - EL_EM_GATE_1_GRAY])
7715 return MF_NO_ACTION;
7718 /* automatically move to the next field with double speed */
7719 player->programmed_action = move_direction;
7720 DOUBLE_PLAYER_SPEED(player);
7722 PlaySoundLevelAction(x, y, ACTION_PASSING);
7726 else if (IS_DIGGABLE(element))
7730 if (mode != DF_SNAP)
7733 GfxElement[x][y] = GFX_ELEMENT(element);
7736 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7738 player->is_digging = TRUE;
7741 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7743 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7746 if (mode == DF_SNAP)
7747 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7752 else if (IS_COLLECTIBLE(element))
7756 if (mode != DF_SNAP)
7758 GfxElement[x][y] = element;
7759 player->is_collecting = TRUE;
7762 if (element == EL_SPEED_PILL)
7763 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7764 else if (element == EL_EXTRA_TIME && level.time > 0)
7767 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7769 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7771 player->shield_normal_time_left += 10;
7772 if (element == EL_SHIELD_DEADLY)
7773 player->shield_deadly_time_left += 10;
7775 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7777 if (player->inventory_size < MAX_INVENTORY_SIZE)
7778 player->inventory_element[player->inventory_size++] = element;
7780 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7781 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7783 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7785 player->dynabomb_count++;
7786 player->dynabombs_left++;
7788 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7790 player->dynabomb_size++;
7792 else if (element == EL_DYNABOMB_INCREASE_POWER)
7794 player->dynabomb_xl = TRUE;
7796 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7797 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7799 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7800 element - EL_KEY_1 : element - EL_EM_KEY_1);
7802 player->key[key_nr] = TRUE;
7804 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7805 el2edimg(EL_KEY_1 + key_nr));
7806 redraw_mask |= REDRAW_DOOR_1;
7808 else if (IS_ENVELOPE(element))
7811 player->show_envelope = element;
7813 ShowEnvelope(element - EL_ENVELOPE_1);
7816 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7820 for (i=0; i < element_info[element].collect_count; i++)
7821 if (player->inventory_size < MAX_INVENTORY_SIZE)
7822 player->inventory_element[player->inventory_size++] = element;
7824 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7825 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7827 else if (element_info[element].collect_count > 0)
7829 local_player->gems_still_needed -=
7830 element_info[element].collect_count;
7831 if (local_player->gems_still_needed < 0)
7832 local_player->gems_still_needed = 0;
7834 DrawText(DX_EMERALDS, DY_EMERALDS,
7835 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7838 RaiseScoreElement(element);
7839 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7841 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7844 if (mode == DF_SNAP)
7845 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7850 else if (IS_PUSHABLE(element))
7852 if (mode == DF_SNAP && element != EL_BD_ROCK)
7853 return MF_NO_ACTION;
7855 if (CAN_FALL(element) && dy)
7856 return MF_NO_ACTION;
7858 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7859 !(element == EL_SPRING && use_spring_bug))
7860 return MF_NO_ACTION;
7863 /* do not push elements already moving away faster than player */
7864 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7865 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7866 return MF_NO_ACTION;
7868 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7869 return MF_NO_ACTION;
7871 if (!player->is_pushing &&
7872 game.engine_version >= RELEASE_IDENT(2,2,0,7))
7873 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7875 player->is_pushing = TRUE;
7877 if (!(IN_LEV_FIELD(nextx, nexty) &&
7878 (IS_FREE(nextx, nexty) ||
7879 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7880 IS_SB_ELEMENT(element)))))
7881 return MF_NO_ACTION;
7883 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7884 return MF_NO_ACTION;
7886 if (player->push_delay == 0) /* new pushing; restart delay */
7887 player->push_delay = FrameCounter;
7889 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7890 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7891 element != EL_SPRING && element != EL_BALLOON)
7892 return MF_NO_ACTION;
7894 if (IS_SB_ELEMENT(element))
7896 if (element == EL_SOKOBAN_FIELD_FULL)
7898 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7899 local_player->sokobanfields_still_needed++;
7902 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7904 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7905 local_player->sokobanfields_still_needed--;
7908 Feld[x][y] = EL_SOKOBAN_OBJECT;
7910 if (Back[x][y] == Back[nextx][nexty])
7911 PlaySoundLevelAction(x, y, ACTION_PUSHING);
7912 else if (Back[x][y] != 0)
7913 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7916 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7919 if (local_player->sokobanfields_still_needed == 0 &&
7920 game.emulation == EMU_SOKOBAN)
7922 player->LevelSolved = player->GameOver = TRUE;
7923 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7927 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
7929 InitMovingField(x, y, move_direction);
7930 GfxAction[x][y] = ACTION_PUSHING;
7932 if (mode == DF_SNAP)
7933 ContinueMoving(x, y);
7935 MovPos[x][y] = (dx != 0 ? dx : dy);
7937 Pushed[x][y] = TRUE;
7938 Pushed[nextx][nexty] = TRUE;
7940 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
7941 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7943 CheckTriggeredElementSideChange(x, y, element, dig_side,
7944 CE_OTHER_GETS_PUSHED);
7945 CheckElementSideChange(x, y, element, dig_side,
7946 CE_PUSHED_BY_PLAYER, -1);
7950 else if (IS_SWITCHABLE(element))
7952 if (PLAYER_SWITCHING(player, x, y))
7955 player->is_switching = TRUE;
7956 player->switch_x = x;
7957 player->switch_y = y;
7959 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
7961 if (element == EL_ROBOT_WHEEL)
7963 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
7967 DrawLevelField(x, y);
7969 else if (element == EL_SP_TERMINAL)
7973 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
7975 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
7977 else if (Feld[xx][yy] == EL_SP_TERMINAL)
7978 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
7981 else if (IS_BELT_SWITCH(element))
7983 ToggleBeltSwitch(x, y);
7985 else if (element == EL_SWITCHGATE_SWITCH_UP ||
7986 element == EL_SWITCHGATE_SWITCH_DOWN)
7988 ToggleSwitchgateSwitch(x, y);
7990 else if (element == EL_LIGHT_SWITCH ||
7991 element == EL_LIGHT_SWITCH_ACTIVE)
7993 ToggleLightSwitch(x, y);
7996 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
7997 SND_LIGHT_SWITCH_ACTIVATING :
7998 SND_LIGHT_SWITCH_DEACTIVATING);
8001 else if (element == EL_TIMEGATE_SWITCH)
8003 ActivateTimegateSwitch(x, y);
8005 else if (element == EL_BALLOON_SWITCH_LEFT ||
8006 element == EL_BALLOON_SWITCH_RIGHT ||
8007 element == EL_BALLOON_SWITCH_UP ||
8008 element == EL_BALLOON_SWITCH_DOWN ||
8009 element == EL_BALLOON_SWITCH_ANY)
8011 if (element == EL_BALLOON_SWITCH_ANY)
8012 game.balloon_dir = move_direction;
8014 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8015 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8016 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8017 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8020 else if (element == EL_LAMP)
8022 Feld[x][y] = EL_LAMP_ACTIVE;
8023 local_player->lights_still_needed--;
8025 DrawLevelField(x, y);
8027 else if (element == EL_TIME_ORB_FULL)
8029 Feld[x][y] = EL_TIME_ORB_EMPTY;
8031 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8033 DrawLevelField(x, y);
8036 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8044 if (!PLAYER_SWITCHING(player, x, y))
8046 player->is_switching = TRUE;
8047 player->switch_x = x;
8048 player->switch_y = y;
8050 CheckTriggeredElementSideChange(x, y, element, dig_side,
8051 CE_OTHER_IS_SWITCHING);
8052 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8055 CheckTriggeredElementSideChange(x, y, element, dig_side,
8056 CE_OTHER_GETS_PRESSED);
8057 CheckElementSideChange(x, y, element, dig_side,
8058 CE_PRESSED_BY_PLAYER, -1);
8061 return MF_NO_ACTION;
8064 player->push_delay = 0;
8066 if (Feld[x][y] != element) /* really digged/collected something */
8067 player->is_collecting = !player->is_digging;
8072 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8074 int jx = player->jx, jy = player->jy;
8075 int x = jx + dx, y = jy + dy;
8076 int snap_direction = (dx == -1 ? MV_LEFT :
8077 dx == +1 ? MV_RIGHT :
8079 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8081 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
8084 if (!player->active || !IN_LEV_FIELD(x, y))
8092 if (player->MovPos == 0)
8093 player->is_pushing = FALSE;
8095 player->is_snapping = FALSE;
8097 if (player->MovPos == 0)
8099 player->is_moving = FALSE;
8100 player->is_digging = FALSE;
8101 player->is_collecting = FALSE;
8107 if (player->is_snapping)
8110 player->MovDir = snap_direction;
8112 player->is_moving = FALSE;
8113 player->is_digging = FALSE;
8114 player->is_collecting = FALSE;
8116 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8119 player->is_snapping = TRUE;
8121 player->is_moving = FALSE;
8122 player->is_digging = FALSE;
8123 player->is_collecting = FALSE;
8125 DrawLevelField(x, y);
8131 boolean DropElement(struct PlayerInfo *player)
8133 int jx = player->jx, jy = player->jy;
8136 if (!player->active || player->MovPos)
8139 old_element = Feld[jx][jy];
8141 /* check if player has anything that can be dropped */
8142 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8145 /* check if anything can be dropped at the current position */
8146 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8149 /* collected custom elements can only be dropped on empty fields */
8150 if (player->inventory_size > 0 &&
8151 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8152 && old_element != EL_EMPTY)
8155 if (old_element != EL_EMPTY)
8156 Back[jx][jy] = old_element; /* store old element on this field */
8158 MovDelay[jx][jy] = 96;
8160 ResetGfxAnimation(jx, jy);
8161 ResetRandomAnimationValue(jx, jy);
8163 if (player->inventory_size > 0)
8165 int new_element = player->inventory_element[--player->inventory_size];
8167 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8168 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8171 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8172 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8174 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8175 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8177 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8179 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8180 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8182 TestIfElementTouchesCustomElement(jx, jy);
8184 else /* player is dropping a dyna bomb */
8186 player->dynabombs_left--;
8189 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8191 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8192 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8194 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8200 /* ------------------------------------------------------------------------- */
8201 /* game sound playing functions */
8202 /* ------------------------------------------------------------------------- */
8204 static int *loop_sound_frame = NULL;
8205 static int *loop_sound_volume = NULL;
8207 void InitPlaySoundLevel()
8209 int num_sounds = getSoundListSize();
8211 if (loop_sound_frame != NULL)
8212 free(loop_sound_frame);
8214 if (loop_sound_volume != NULL)
8215 free(loop_sound_volume);
8217 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8218 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8221 static void PlaySoundLevel(int x, int y, int nr)
8223 int sx = SCREENX(x), sy = SCREENY(y);
8224 int volume, stereo_position;
8225 int max_distance = 8;
8226 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8228 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8229 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8232 if (!IN_LEV_FIELD(x, y) ||
8233 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8234 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8237 volume = SOUND_MAX_VOLUME;
8239 if (!IN_SCR_FIELD(sx, sy))
8241 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8242 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8244 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8247 stereo_position = (SOUND_MAX_LEFT +
8248 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8249 (SCR_FIELDX + 2 * max_distance));
8251 if (IS_LOOP_SOUND(nr))
8253 /* This assures that quieter loop sounds do not overwrite louder ones,
8254 while restarting sound volume comparison with each new game frame. */
8256 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8259 loop_sound_volume[nr] = volume;
8260 loop_sound_frame[nr] = FrameCounter;
8263 PlaySoundExt(nr, volume, stereo_position, type);
8266 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8268 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8269 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8270 y < LEVELY(BY1) ? LEVELY(BY1) :
8271 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8275 static void PlaySoundLevelAction(int x, int y, int action)
8277 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8280 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8282 int sound_effect = element_info[element].sound[action];
8284 if (sound_effect != SND_UNDEFINED)
8285 PlaySoundLevel(x, y, sound_effect);
8288 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8290 int sound_effect = element_info[Feld[x][y]].sound[action];
8292 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8293 PlaySoundLevel(x, y, sound_effect);
8296 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8298 int sound_effect = element_info[Feld[x][y]].sound[action];
8300 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8301 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8304 void RaiseScore(int value)
8306 local_player->score += value;
8307 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8310 void RaiseScoreElement(int element)
8316 case EL_EMERALD_YELLOW:
8317 case EL_EMERALD_RED:
8318 case EL_EMERALD_PURPLE:
8319 case EL_SP_INFOTRON:
8320 RaiseScore(level.score[SC_EMERALD]);
8323 RaiseScore(level.score[SC_DIAMOND]);
8326 RaiseScore(level.score[SC_CRYSTAL]);
8329 RaiseScore(level.score[SC_PEARL]);
8332 case EL_BD_BUTTERFLY:
8333 case EL_SP_ELECTRON:
8334 RaiseScore(level.score[SC_BUG]);
8338 case EL_SP_SNIKSNAK:
8339 RaiseScore(level.score[SC_SPACESHIP]);
8342 case EL_DARK_YAMYAM:
8343 RaiseScore(level.score[SC_YAMYAM]);
8346 RaiseScore(level.score[SC_ROBOT]);
8349 RaiseScore(level.score[SC_PACMAN]);
8352 RaiseScore(level.score[SC_NUT]);
8355 case EL_SP_DISK_RED:
8356 case EL_DYNABOMB_INCREASE_NUMBER:
8357 case EL_DYNABOMB_INCREASE_SIZE:
8358 case EL_DYNABOMB_INCREASE_POWER:
8359 RaiseScore(level.score[SC_DYNAMITE]);
8361 case EL_SHIELD_NORMAL:
8362 case EL_SHIELD_DEADLY:
8363 RaiseScore(level.score[SC_SHIELD]);
8366 RaiseScore(level.score[SC_TIME_BONUS]);
8372 RaiseScore(level.score[SC_KEY]);
8375 RaiseScore(element_info[element].collect_score);
8380 void RequestQuitGame(boolean ask_if_really_quit)
8382 if (AllPlayersGone ||
8383 !ask_if_really_quit ||
8384 level_editor_test_game ||
8385 Request("Do you really want to quit the game ?",
8386 REQ_ASK | REQ_STAY_CLOSED))
8388 #if defined(PLATFORM_UNIX)
8389 if (options.network)
8390 SendToServer_StopPlaying();
8394 game_status = GAME_MODE_MAIN;
8400 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8405 /* ---------- new game button stuff ---------------------------------------- */
8407 /* graphic position values for game buttons */
8408 #define GAME_BUTTON_XSIZE 30
8409 #define GAME_BUTTON_YSIZE 30
8410 #define GAME_BUTTON_XPOS 5
8411 #define GAME_BUTTON_YPOS 215
8412 #define SOUND_BUTTON_XPOS 5
8413 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8415 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8416 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8417 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8418 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8419 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8420 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8427 } gamebutton_info[NUM_GAME_BUTTONS] =
8430 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8435 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8440 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8445 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8446 SOUND_CTRL_ID_MUSIC,
8447 "background music on/off"
8450 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8451 SOUND_CTRL_ID_LOOPS,
8452 "sound loops on/off"
8455 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8456 SOUND_CTRL_ID_SIMPLE,
8457 "normal sounds on/off"
8461 void CreateGameButtons()
8465 for (i=0; i<NUM_GAME_BUTTONS; i++)
8467 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8468 struct GadgetInfo *gi;
8471 unsigned long event_mask;
8472 int gd_xoffset, gd_yoffset;
8473 int gd_x1, gd_x2, gd_y1, gd_y2;
8476 gd_xoffset = gamebutton_info[i].x;
8477 gd_yoffset = gamebutton_info[i].y;
8478 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8479 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8481 if (id == GAME_CTRL_ID_STOP ||
8482 id == GAME_CTRL_ID_PAUSE ||
8483 id == GAME_CTRL_ID_PLAY)
8485 button_type = GD_TYPE_NORMAL_BUTTON;
8487 event_mask = GD_EVENT_RELEASED;
8488 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8489 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8493 button_type = GD_TYPE_CHECK_BUTTON;
8495 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8496 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8497 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8498 event_mask = GD_EVENT_PRESSED;
8499 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8500 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8503 gi = CreateGadget(GDI_CUSTOM_ID, id,
8504 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8505 GDI_X, DX + gd_xoffset,
8506 GDI_Y, DY + gd_yoffset,
8507 GDI_WIDTH, GAME_BUTTON_XSIZE,
8508 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8509 GDI_TYPE, button_type,
8510 GDI_STATE, GD_BUTTON_UNPRESSED,
8511 GDI_CHECKED, checked,
8512 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8513 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8514 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8515 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8516 GDI_EVENT_MASK, event_mask,
8517 GDI_CALLBACK_ACTION, HandleGameButtons,
8521 Error(ERR_EXIT, "cannot create gadget");
8523 game_gadget[id] = gi;
8527 void FreeGameButtons()
8531 for (i=0; i<NUM_GAME_BUTTONS; i++)
8532 FreeGadget(game_gadget[i]);
8535 static void MapGameButtons()
8539 for (i=0; i<NUM_GAME_BUTTONS; i++)
8540 MapGadget(game_gadget[i]);
8543 void UnmapGameButtons()
8547 for (i=0; i<NUM_GAME_BUTTONS; i++)
8548 UnmapGadget(game_gadget[i]);
8551 static void HandleGameButtons(struct GadgetInfo *gi)
8553 int id = gi->custom_id;
8555 if (game_status != GAME_MODE_PLAYING)
8560 case GAME_CTRL_ID_STOP:
8561 RequestQuitGame(TRUE);
8564 case GAME_CTRL_ID_PAUSE:
8565 if (options.network)
8567 #if defined(PLATFORM_UNIX)
8569 SendToServer_ContinuePlaying();
8571 SendToServer_PausePlaying();
8575 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8578 case GAME_CTRL_ID_PLAY:
8581 #if defined(PLATFORM_UNIX)
8582 if (options.network)
8583 SendToServer_ContinuePlaying();
8587 tape.pausing = FALSE;
8588 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8593 case SOUND_CTRL_ID_MUSIC:
8594 if (setup.sound_music)
8596 setup.sound_music = FALSE;
8599 else if (audio.music_available)
8601 setup.sound = setup.sound_music = TRUE;
8603 SetAudioMode(setup.sound);
8604 PlayMusic(level_nr);
8608 case SOUND_CTRL_ID_LOOPS:
8609 if (setup.sound_loops)
8610 setup.sound_loops = FALSE;
8611 else if (audio.loops_available)
8613 setup.sound = setup.sound_loops = TRUE;
8614 SetAudioMode(setup.sound);
8618 case SOUND_CTRL_ID_SIMPLE:
8619 if (setup.sound_simple)
8620 setup.sound_simple = FALSE;
8621 else if (audio.sound_available)
8623 setup.sound = setup.sound_simple = TRUE;
8624 SetAudioMode(setup.sound);