+ if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
+ {
+ TapeRecordAction(stored_player_action);
+ num_stored_actions = 0;
+ save_tape_entry = FALSE;
+ }
+
+ if (tape.playing && !tape.pausing && !player_action &&
+ tape.counter < tape.length)
+ {
+ int next_joy =
+ tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
+
+ if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
+ (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
+ {
+ int dx = (next_joy == JOY_LEFT ? -1 : +1);
+
+ if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
+ {
+ int el = Feld[jx+dx][jy];
+ int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10);
+
+ if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
+ {
+ player->MovDir = next_joy;
+ player->Frame = FrameCounter % 4;
+ player->Pushing = TRUE;
+ }
+ }
+ }
+ }
+}
+
+void GameActions()
+{
+ static unsigned long action_delay = 0;
+ unsigned long action_delay_value;
+ int sieb_x = 0, sieb_y = 0;
+ int i, x, y, element;
+ byte *recorded_player_action;
+ byte summarized_player_action = 0;
+
+ if (game_status != PLAYING)
+ return;
+
+ action_delay_value =
+ (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
+
+ /*
+ if (tape.playing && tape.fast_forward)
+ {
+ char buf[100];
+
+ sprintf(buf, "FFWD: %ld ms", action_delay_value);
+ print_debug(buf);
+ }
+ */
+
+
+ /* main game synchronization point */
+
+
+
+
+#if 1
+ WaitUntilDelayReached(&action_delay, action_delay_value);
+#else
+
+ while (!DelayReached(&action_delay, action_delay_value))
+ {
+ char buf[100];
+
+ sprintf(buf, "%ld %ld %ld",
+ Counter(), action_delay, action_delay_value);
+ print_debug(buf);
+ }
+ print_debug("done");
+
+#endif
+
+
+
+
+ if (network_playing && !network_player_action_received)
+ {
+ /*
+#ifdef DEBUG
+ printf("DEBUG: try to get network player actions in time\n");
+#endif
+ */
+
+#ifndef MSDOS
+ /* last chance to get network player actions without main loop delay */
+ HandleNetworking();
+#endif
+
+ if (game_status != PLAYING)
+ return;
+
+ if (!network_player_action_received)
+ {
+ /*
+#ifdef DEBUG
+ printf("DEBUG: failed to get network player actions in time\n");
+#endif
+ */
+ return;
+ }
+ }
+
+
+ /*
+ if (tape.pausing || (tape.playing && !TapePlayDelay()))
+ return;
+ else if (tape.recording)
+ TapeRecordDelay();
+ */
+
+ if (tape.pausing)
+ return;
+
+ if (tape.playing)
+ TapePlayDelay();
+ else if (tape.recording)
+ TapeRecordDelay();
+
+ recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ summarized_player_action |= stored_player[i].action;
+
+ if (!network_playing)
+ stored_player[i].effective_action = stored_player[i].action;
+ }
+
+#ifndef MSDOS
+ if (network_playing)
+ SendToServer_MovePlayer(summarized_player_action);
+#endif
+
+ if (!options.network && !setup.team_mode)
+ local_player->effective_action = summarized_player_action;
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ int actual_player_action = stored_player[i].effective_action;
+
+ if (recorded_player_action)
+ actual_player_action = recorded_player_action[i];
+
+ PlayerActions(&stored_player[i], actual_player_action);
+ ScrollFigure(&stored_player[i], SCROLL_GO_ON);
+ }
+
+ network_player_action_received = FALSE;
+
+ ScrollScreen(NULL, SCROLL_GO_ON);
+
+
+ /*
+ if (tape.pausing || (tape.playing && !TapePlayDelay()))
+ return;
+ else if (tape.recording)
+ TapeRecordDelay();
+ */
+
+
+
+
+
+#ifdef DEBUG
+ /*
+ if (TimeFrames == 0 && !local_player->gone)
+ {
+ extern unsigned int last_RND();
+
+ printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
+ TimePlayed,
+ last_RND(),
+ getStateCheckSum(TimePlayed));
+ }
+ */
+#endif
+
+
+
+#ifdef DEBUG
+ /*
+ if (GameFrameDelay >= 500)
+ printf("FrameCounter == %d\n", FrameCounter);
+ */
+#endif
+
+
+
+
+
+ FrameCounter++;
+ TimeFrames++;
+
+ for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+ {
+ Stop[x][y] = FALSE;
+ if (JustHit[x][y]>0)
+ JustHit[x][y]--;
+
+#if DEBUG
+ if (IS_BLOCKED(x, y))
+ {
+ int oldx, oldy;
+
+ Blocked2Moving(x, y, &oldx, &oldy);
+ if (!IS_MOVING(oldx, oldy))
+ {
+ printf("GameActions(): (BLOCKED=>MOVING) context corrupted!\n");
+ printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
+ printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
+ printf("GameActions(): This should never happen!\n");
+ }
+ }
+#endif
+ }
+
+ for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+ {
+ element = Feld[x][y];
+
+ if (IS_INACTIVE(element))
+ continue;
+
+ if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+ {
+ StartMoving(x, y);
+
+ if (IS_GEM(element))
+ EdelsteinFunkeln(x, y);
+ }
+ else if (IS_MOVING(x, y))
+ ContinueMoving(x, y);
+ else if (element == EL_DYNAMIT || element == EL_DYNABOMB)
+ CheckDynamite(x, y);
+ else if (element == EL_EXPLODING)
+ Explode(x, y, Frame[x][y], EX_NORMAL);
+ else if (element == EL_AMOEBING)
+ AmoebeWaechst(x, y);
+ else if (IS_AMOEBALIVE(element))
+ AmoebeAbleger(x, y);
+ else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
+ Life(x, y);
+ else if (element == EL_ABLENK_EIN)
+ Ablenk(x, y);
+ else if (element == EL_SALZSAEURE)
+ Blubber(x, y);
+ else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
+ Blurb(x, y);
+ else if (element == EL_CRACKINGNUT)
+ NussKnacken(x, y);
+ else if (element == EL_AUSGANG_ZU)
+ AusgangstuerPruefen(x, y);
+ else if (element == EL_AUSGANG_ACT)
+ AusgangstuerOeffnen(x, y);
+ else if (element == EL_AUSGANG_AUF)
+ AusgangstuerBlinken(x, y);
+ else if (element == EL_MAUERND)
+ MauerWaechst(x, y);
+ else if (element == EL_MAUER_LEBT ||
+ element == EL_MAUER_X ||
+ element == EL_MAUER_Y ||
+ element == EL_MAUER_XY)
+ MauerAbleger(x, y);
+ else if (element == EL_BURNING)
+ CheckForDragon(x, y);
+ else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
+ CheckBuggyBase(x, y);
+ else if (element == EL_SP_TERMINAL)
+ DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
+ else if (element == EL_SP_TERMINAL_ACTIVE)
+ DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
+
+ if (SiebAktiv)
+ {
+ boolean sieb = FALSE;
+ int jx = local_player->jx, jy = local_player->jy;
+
+ if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL ||
+ Store[x][y] == EL_SIEB_LEER)
+ {
+ SiebAktivieren(x, y, 1);
+ sieb = TRUE;
+ }
+ else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL ||
+ Store[x][y] == EL_SIEB2_LEER)
+ {
+ SiebAktivieren(x, y, 2);
+ sieb = TRUE;
+ }
+
+ /* play the element sound at the position nearest to the player */
+ if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
+ {
+ sieb_x = x;
+ sieb_y = y;
+ }
+ }
+ }
+
+ if (SiebAktiv)
+ {
+ if (!(SiebCount % 4))
+ PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
+
+ if (SiebCount > 0)
+ {
+ SiebCount--;
+ if (!SiebCount)
+ {
+ for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+ {
+ element = Feld[x][y];
+ if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
+ {
+ Feld[x][y] = EL_SIEB_TOT;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
+ {
+ Feld[x][y] = EL_SIEB2_TOT;
+ DrawLevelField(x, y);
+ }
+ }
+
+ SiebAktiv = FALSE;
+ }
+ }
+ }
+
+ if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
+ {
+ TimeFrames = 0;
+ TimePlayed++;
+
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
+
+ if (TimeLeft > 0)
+ {
+ TimeLeft--;
+
+ if (TimeLeft <= 10)
+ PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+
+ DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+
+ if (!TimeLeft)
+ for (i=0; i<MAX_PLAYERS; i++)
+ KillHero(&stored_player[i]);
+ }
+ else if (level.time == 0) /* level without time limit */
+ DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
+ }
+
+ DrawAllPlayers();
+}
+
+static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
+{
+ int min_x = x, min_y = y, max_x = x, max_y = y;
+ int i;
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ int jx = stored_player[i].jx, jy = stored_player[i].jy;
+
+ if (!stored_player[i].active || stored_player[i].gone ||
+ &stored_player[i] == player)
+ continue;
+
+ min_x = MIN(min_x, jx);
+ min_y = MIN(min_y, jy);
+ max_x = MAX(max_x, jx);
+ max_y = MAX(max_y, jy);
+ }
+
+ return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
+}
+
+static boolean AllPlayersInVisibleScreen()
+{
+ int i;
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ {
+ int jx = stored_player[i].jx, jy = stored_player[i].jy;
+
+ if (!stored_player[i].active || stored_player[i].gone)
+ continue;
+
+ if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void ScrollLevel(int dx, int dy)
+{
+ int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+ int x, y;
+
+ XCopyArea(display, drawto_field, drawto_field, gc,
+ FX + TILEX*(dx == -1) - softscroll_offset,
+ FY + TILEY*(dy == -1) - softscroll_offset,
+ SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
+ SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
+ FX + TILEX*(dx == 1) - softscroll_offset,
+ FY + TILEY*(dy == 1) - softscroll_offset);
+
+ if (dx)
+ {
+ x = (dx == 1 ? BX1 : BX2);
+ for (y=BY1; y<=BY2; y++)
+ DrawScreenField(x, y);
+ }
+ if (dy)
+ {
+ y = (dy == 1 ? BY1 : BY2);
+ for (x=BX1; x<=BX2; x++)
+ DrawScreenField(x, y);
+ }
+
+ redraw_mask |= REDRAW_FIELD;
+}
+
+boolean MoveFigureOneStep(struct PlayerInfo *player,
+ int dx, int dy, int real_dx, int real_dy)
+{
+ int jx = player->jx, jy = player->jy;
+ int new_jx = jx+dx, new_jy = jy+dy;
+ int element;
+ int can_move;
+
+ if (player->gone || (!dx && !dy))
+ return MF_NO_ACTION;
+
+ player->MovDir = (dx < 0 ? MV_LEFT :
+ dx > 0 ? MV_RIGHT :
+ dy < 0 ? MV_UP :
+ dy > 0 ? MV_DOWN : MV_NO_MOVING);
+
+ if (!IN_LEV_FIELD(new_jx, new_jy))
+ return MF_NO_ACTION;
+
+ if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
+ return MF_NO_ACTION;
+
+ element = MovingOrBlocked2Element(new_jx, new_jy);
+
+ if (DONT_GO_TO(element))
+ {
+ if (element == EL_SALZSAEURE && dx == 0 && dy == 1)
+ {
+ Blurb(jx, jy);
+ Feld[jx][jy] = EL_SPIELFIGUR;
+ InitMovingField(jx, jy, MV_DOWN);
+ Store[jx][jy] = EL_SALZSAEURE;
+ ContinueMoving(jx, jy);
+ BuryHero(player);
+ }
+ else
+ KillHero(player);
+
+ return MF_MOVING;
+ }
+
+ can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
+ if (can_move != MF_MOVING)
+ return can_move;
+
+ StorePlayer[jx][jy] = 0;
+ player->last_jx = jx;
+ player->last_jy = jy;
+ jx = player->jx = new_jx;
+ jy = player->jy = new_jy;
+ StorePlayer[jx][jy] = player->element_nr;
+
+ player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / MoveSpeed);
+
+ ScrollFigure(player, SCROLL_INIT);
+
+ return MF_MOVING;
+}
+
+boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
+{
+ int jx = player->jx, jy = player->jy;
+ int old_jx = jx, old_jy = jy;
+ int moved = MF_NO_ACTION;
+
+ if (player->gone || (!dx && !dy))
+ return FALSE;
+
+ if (!FrameReached(&player->move_delay, MoveSpeed) && !tape.playing)
+ return FALSE;
+
+ if (player->MovPos)
+ {
+ /* should only happen if pre-1.2 tape recordings are played */
+ /* this is only for backward compatibility */
+
+ int old_move_speed = MoveSpeed;
+
+#if DEBUG
+ printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
+#endif
+
+ /* scroll remaining steps with finest movement resolution */
+ MoveSpeed = 8;
+
+ while (player->MovPos)
+ {
+ ScrollFigure(player, SCROLL_GO_ON);
+ ScrollScreen(NULL, SCROLL_GO_ON);
+ FrameCounter++;
+ DrawAllPlayers();
+ BackToFront();
+ }
+
+ MoveSpeed = old_move_speed;
+ }
+
+ if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
+ {
+ if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
+ moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
+ }
+ else
+ {
+ if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
+ moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
+ }
+
+ jx = player->jx;
+ jy = player->jy;
+
+ if (moved & MF_MOVING && !ScreenMovPos &&
+ (player == local_player || !options.network))
+ {
+ int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
+ int offset = (setup.scroll_delay ? 3 : 0);
+
+ if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+ {
+ /* actual player has left the screen -- scroll in that direction */
+ if (jx != old_jx) /* player has moved horizontally */
+ scroll_x += (jx - old_jx);
+ else /* player has moved vertically */
+ scroll_y += (jy - old_jy);
+ }
+ else
+ {
+ if (jx != old_jx) /* player has moved horizontally */
+ {
+ if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
+ (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
+ scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+ scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+ /* don't scroll more than one field at a time */
+ scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
+
+ /* don't scroll against the player's moving direction */
+ if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
+ (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
+ scroll_x = old_scroll_x;
+ }
+ else /* player has moved vertically */
+ {
+ if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
+ (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
+ scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+ scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+
+ /* don't scroll more than one field at a time */
+ scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
+
+ /* don't scroll against the player's moving direction */
+ if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
+ (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
+ scroll_y = old_scroll_y;
+ }
+ }
+
+ if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
+ {
+ if (!options.network && !AllPlayersInVisibleScreen())
+ {
+ scroll_x = old_scroll_x;
+ scroll_y = old_scroll_y;
+ }
+ else
+ {
+ ScrollScreen(player, SCROLL_INIT);
+ ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
+ }
+ }
+ }
+
+ if (!(moved & MF_MOVING) && !player->Pushing)
+ player->Frame = 0;
+ else
+ player->Frame = (player->Frame + 1) % 4;
+
+ if (moved & MF_MOVING)
+ {
+ if (old_jx != jx && old_jy == jy)
+ player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
+ else if (old_jx == jx && old_jy != jy)
+ player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
+
+ DrawLevelField(jx, jy); /* for "ErdreichAnbroeckeln()" */
+
+ player->last_move_dir = player->MovDir;
+ }
+ else
+ player->last_move_dir = MV_NO_MOVING;
+
+ TestIfHeroHitsBadThing(jx, jy);
+
+ if (player->gone)
+ RemoveHero(player);
+
+ return moved;
+}
+
+void ScrollFigure(struct PlayerInfo *player, int mode)
+{
+ int jx = player->jx, jy = player->jy;
+ int last_jx = player->last_jx, last_jy = player->last_jy;
+
+ if (!player->active || player->gone || !player->MovPos)
+ return;
+
+ if (mode == SCROLL_INIT)
+ {
+ player->actual_frame_counter = FrameCounter;
+ player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+
+ if (Feld[last_jx][last_jy] == EL_LEERRAUM)
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+
+ DrawPlayer(player);
+ return;
+ }
+ else if (!FrameReached(&player->actual_frame_counter, 1))
+ return;
+
+ player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
+ player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+
+ if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ Feld[last_jx][last_jy] = EL_LEERRAUM;
+
+ DrawPlayer(player);
+
+ if (!player->MovPos)
+ {
+ player->last_jx = jx;
+ player->last_jy = jy;
+
+ if (Feld[jx][jy] == EL_AUSGANG_AUF)
+ {
+ RemoveHero(player);
+
+ if (!local_player->friends_still_needed)
+ player->LevelSolved = player->GameOver = TRUE;
+ }
+ }
+}
+
+void ScrollScreen(struct PlayerInfo *player, int mode)
+{
+ static unsigned long screen_frame_counter = 0;
+
+ if (mode == SCROLL_INIT)
+ {
+ screen_frame_counter = FrameCounter;
+ ScreenMovDir = player->MovDir;
+ ScreenMovPos = player->MovPos;
+ ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+ return;
+ }
+ else if (!FrameReached(&screen_frame_counter, 1))
+ return;
+
+ if (ScreenMovPos)
+ {
+ ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
+ ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+ redraw_mask |= REDRAW_FIELD;
+ }
+ else
+ ScreenMovDir = MV_NO_MOVING;
+}
+
+void TestIfGoodThingHitsBadThing(int goodx, int goody)
+{
+ int i, killx = goodx, killy = goody;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int harmless[4] =
+ {
+ MV_UP,
+ MV_LEFT,
+ MV_RIGHT,
+ MV_DOWN
+ };
+
+ for (i=0; i<4; i++)
+ {
+ int x, y, element;
+
+ x = goodx + xy[i][0];
+ y = goody + xy[i][1];
+ if (!IN_LEV_FIELD(x, y))
+ continue;
+
+ element = Feld[x][y];
+
+ if (DONT_TOUCH(element))
+ {
+ if (MovDir[x][y] == harmless[i])
+ continue;
+
+ killx = x;
+ killy = y;
+ break;
+ }
+ }
+
+ if (killx != goodx || killy != goody)
+ {
+ if (IS_PLAYER(goodx, goody))
+ KillHero(PLAYERINFO(goodx, goody));
+ else
+ Bang(goodx, goody);
+ }
+}
+
+void TestIfBadThingHitsGoodThing(int badx, int bady)
+{
+ int i, killx = badx, killy = bady;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int harmless[4] =
+ {
+ MV_UP,
+ MV_LEFT,
+ MV_RIGHT,
+ MV_DOWN
+ };
+
+ for (i=0; i<4; i++)
+ {
+ int x, y, element;
+
+ x = badx + xy[i][0];
+ y = bady + xy[i][1];
+ if (!IN_LEV_FIELD(x, y))
+ continue;
+
+ element = Feld[x][y];
+
+ if (IS_PLAYER(x, y))
+ {
+ killx = x;
+ killy = y;
+ break;
+ }
+ else if (element == EL_PINGUIN)
+ {
+ if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
+ continue;
+
+ killx = x;
+ killy = y;
+ break;
+ }
+ }
+
+ if (killx != badx || killy != bady)
+ {
+ if (IS_PLAYER(killx, killy))
+ KillHero(PLAYERINFO(killx, killy));
+ else
+ Bang(killx, killy);
+ }
+}
+
+void TestIfHeroHitsBadThing(int x, int y)
+{
+ TestIfGoodThingHitsBadThing(x, y);
+}
+
+void TestIfBadThingHitsHero(int x, int y)
+{
+ TestIfBadThingHitsGoodThing(x, y);
+}
+
+void TestIfFriendHitsBadThing(int x, int y)
+{
+ TestIfGoodThingHitsBadThing(x, y);
+}
+
+void TestIfBadThingHitsFriend(int x, int y)
+{
+ TestIfBadThingHitsGoodThing(x, y);
+}
+
+void TestIfBadThingHitsOtherBadThing(int badx, int bady)
+{
+ int i, killx = badx, killy = bady;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+
+ for (i=0; i<4; i++)
+ {
+ int x, y, element;
+
+ x=badx + xy[i][0];
+ y=bady + xy[i][1];
+ if (!IN_LEV_FIELD(x, y))
+ continue;
+
+ element = Feld[x][y];
+ if (IS_AMOEBOID(element) || element == EL_LIFE ||
+ element == EL_AMOEBING || element == EL_TROPFEN)
+ {
+ killx = x;
+ killy = y;
+ break;
+ }
+ }
+
+ if (killx != badx || killy != bady)
+ Bang(badx, bady);
+}
+
+void KillHero(struct PlayerInfo *player)
+{
+ int jx = player->jx, jy = player->jy;
+
+ if (player->gone)
+ return;
+
+ if (IS_PFORTE(Feld[jx][jy]))
+ Feld[jx][jy] = EL_LEERRAUM;
+
+ Bang(jx, jy);
+ BuryHero(player);
+}
+
+void BuryHero(struct PlayerInfo *player)
+{
+ int jx = player->jx, jy = player->jy;
+
+ if (player->gone)
+ return;
+
+ PlaySoundLevel(jx, jy, SND_AUTSCH);
+ PlaySoundLevel(jx, jy, SND_LACHEN);
+
+ player->GameOver = TRUE;
+ RemoveHero(player);
+}
+
+void RemoveHero(struct PlayerInfo *player)
+{
+ int jx = player->jx, jy = player->jy;
+ int i, found = FALSE;
+
+ player->gone = TRUE;
+ StorePlayer[jx][jy] = 0;
+
+ for (i=0; i<MAX_PLAYERS; i++)
+ if (stored_player[i].active && !stored_player[i].gone)
+ found = TRUE;
+
+ if (!found)
+ AllPlayersGone = TRUE;
+
+ ExitX = ZX = jx;
+ ExitY = ZY = jy;
+}
+
+int DigField(struct PlayerInfo *player,
+ int x, int y, int real_dx, int real_dy, int mode)
+{
+ int jx = player->jx, jy = player->jy;
+ int dx = x - jx, dy = y - jy;
+ int element;
+
+ if (!player->MovPos)
+ player->Pushing = FALSE;
+
+ if (mode == DF_NO_PUSH)
+ {
+ player->push_delay = 0;
+ return MF_NO_ACTION;
+ }
+
+ if (IS_MOVING(x, y) || IS_PLAYER(x, y))
+ return MF_NO_ACTION;
+