rnd-19981017-1
[rocksndiamonds.git] / src / game.c
index 0a7bbd4d7dbcd4a41430c65de4e6a6bbf9841aa5..4851f4aa7745267c5226d1012c325fba70dcac4f 100644 (file)
@@ -1,13 +1,12 @@
 /***********************************************************
 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
 *----------------------------------------------------------*
-*  ©1995 Artsoft Development                               *
-*        Holger Schemel                                    *
-*        33659 Bielefeld-Senne                             *
-*        Telefon: (0521) 493245                            *
-*        eMail: aeglos@valinor.owl.de                      *
-*               aeglos@uni-paderborn.de                    *
-*               q99492@pbhrzx.uni-paderborn.de             *
+*  (c) 1995-98 Artsoft Entertainment                       *
+*              Holger Schemel                              *
+*              Oststrasse 11a                              *
+*              33604 Bielefeld                             *
+*              phone: ++49 +521 290471                     *
+*              email: aeglos@valinor.owl.de                *
 *----------------------------------------------------------*
 *  game.c                                                  *
 ***********************************************************/
 #include "files.h"
 #include "tape.h"
 #include "joystick.h"
+#include "network.h"
 
 void GetPlayerConfig()
 {
-  int old_joystick_nr = joystick_nr;
+  int old_joystick_nr = setup.joystick_nr;
 
   if (sound_status==SOUND_OFF)
-    player.setup &= ~SETUP_SOUND;
+    local_player->setup &= ~SETUP_SOUND;
   if (!sound_loops_allowed)
   {
-    player.setup &= ~SETUP_SOUND_LOOPS;
-    player.setup &= ~SETUP_SOUND_MUSIC;
+    local_player->setup &= ~SETUP_SOUND_LOOPS;
+    local_player->setup &= ~SETUP_SOUND_MUSIC;
   }
 
-  sound_on = sound_simple_on = SETUP_SOUND_ON(player.setup);
-  sound_loops_on = SETUP_SOUND_LOOPS_ON(player.setup);
-  sound_music_on = SETUP_SOUND_MUSIC_ON(player.setup);
-  toons_on = SETUP_TOONS_ON(player.setup);
-  direct_draw_on = SETUP_DIRECT_DRAW_ON(player.setup);
-  fading_on = SETUP_FADING_ON(player.setup);
-  autorecord_on = SETUP_AUTO_RECORD_ON(player.setup);
-  joystick_nr = SETUP_2ND_JOYSTICK_ON(player.setup);
-  quick_doors = SETUP_QUICK_DOORS_ON(player.setup);
-  scroll_delay_on = SETUP_SCROLL_DELAY_ON(player.setup);
-  soft_scrolling_on = SETUP_SOFT_SCROLL_ON(player.setup);
+  setup.sound_on = setup.sound_simple_on = SETUP_SOUND_ON(local_player->setup);
+  setup.sound_loops_on = SETUP_SOUND_LOOPS_ON(local_player->setup);
+  setup.sound_music_on = SETUP_SOUND_MUSIC_ON(local_player->setup);
+  setup.toons_on = SETUP_TOONS_ON(local_player->setup);
+  setup.direct_draw_on = SETUP_DIRECT_DRAW_ON(local_player->setup);
+  setup.fading_on = SETUP_FADING_ON(local_player->setup);
+  setup.autorecord_on = SETUP_AUTO_RECORD_ON(local_player->setup);
+  setup.joystick_nr = SETUP_2ND_JOYSTICK_ON(local_player->setup);
+  setup.quick_doors = SETUP_QUICK_DOORS_ON(local_player->setup);
+  setup.scroll_delay_on = SETUP_SCROLL_DELAY_ON(local_player->setup);
+  setup.soft_scrolling_on = SETUP_SOFT_SCROLL_ON(local_player->setup);
 
 #ifndef MSDOS
-  if (joystick_nr != old_joystick_nr)
+  if (setup.joystick_nr != old_joystick_nr)
   {
     if (joystick_device)
       close(joystick_device);
@@ -59,32 +59,104 @@ void GetPlayerConfig()
 
 void InitGame()
 {
-  int i,x,y;
-  BOOL emulate_bd = TRUE;      /* unless non-BOULDERDASH elements found */
-  BOOL emulate_sb = TRUE;      /* unless non-SOKOBAN     elements found */
-
-  Dynamite = Score = 0;
-  Gems = level.edelsteine;
-  SokobanFields = Lights = Friends = 0;
-  DynaBombCount = DynaBombSize = DynaBombsLeft = 0;
-  DynaBombXL = FALSE;
-  Key[0] = Key[1] = Key[2] = Key[3] = FALSE;
+  int i,j, x,y;
+  boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
+  boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
+
+  /* don't play tapes over network */
+  network_playing = (options.network && !tape.playing);
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    player->index_nr = i;
+    player->element_nr = EL_SPIELER1 + i;
+
+    player->present = FALSE;
+    player->active = FALSE;
+
+    /*
+    player->local = FALSE;
+    */
+
+    player->score = 0;
+    player->gems_still_needed = level.edelsteine;
+    player->sokobanfields_still_needed = 0;
+    player->lights_still_needed = 0;
+    player->friends_still_needed = 0;
+
+    for(j=0; j<4; j++)
+      player->key[j] = FALSE;
+
+    player->dynamite = 0;
+    player->dynabomb_count = 0;
+    player->dynabomb_size = 0;
+    player->dynabombs_left = 0;
+    player->dynabomb_xl = FALSE;
+
+    player->MovDir = MV_NO_MOVING;
+    player->MovPos = 0;
+    player->Pushing = FALSE;
+    player->GfxPos = 0;
+    player->Frame = 0;
+
+    player->actual_frame_counter = 0;
+
+    player->frame_reset_delay = 0;
+
+    player->push_delay = 0;
+    player->push_delay_value = 5;
+
+    player->move_delay = 0;
+    player->last_move_dir = MV_NO_MOVING;
+
+    player->snapped = FALSE;
+
+    player->gone = FALSE;
+
+    player->last_jx = player->last_jy = 0;
+    player->jx = player->jy = 0;
+
+    DigField(player, 0,0,0,0,DF_NO_PUSH);
+    SnapField(player, 0,0);
+
+
+    /* TEST TEST TEST */
+
+    /*
+    stored_player[i].active = TRUE;
+    */
+
+    /* TEST TEST TEST */
+
+    player->LevelSolved = FALSE;
+    player->GameOver = FALSE;
+  }
+
+  /*
+  local_player->active = TRUE;
+  local_player->local = TRUE;
+  */
+
+  network_player_action_received = FALSE;
+
+  /* initial null action */
+  if (network_playing)
+    SendToServer_MovePlayer(MV_NO_MOVING);
+
+  ZX = ZY = -1;
+
   MampferNr = 0;
   FrameCounter = 0;
   TimeFrames = 0;
   TimeLeft = level.time;
+
+  ScreenMovDir = MV_NO_MOVING;
   ScreenMovPos = 0;
-  PlayerMovDir = MV_NO_MOVING;
-  PlayerMovPos = 0;
-  PlayerGfxPos = 0;
-  PlayerFrame = 0;
-  PlayerPushing = FALSE;
-  PlayerGone = LevelSolved = GameOver = SiebAktiv = FALSE;
-  JX = JY = 0;
-  ZX = ZY = -1;
+  ScreenGfxPos = 0;
 
-  DigField(0,0,0,0,DF_NO_PUSH);
-  SnapField(0,0);
+  AllPlayersGone = SiebAktiv = FALSE;
 
   for(i=0;i<MAX_NUM_AMOEBA;i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
@@ -93,7 +165,9 @@ void InitGame()
   {
     Feld[x][y] = Ur[x][y];
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
-    Store[x][y] = Store2[x][y] = Frame[x][y] = AmoebaNr[x][y] = 0;
+    Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
+    Frame[x][y] = 0;
+    AmoebaNr[x][y] = 0;
     JustHit[x][y] = 0;
 
     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
@@ -104,14 +178,42 @@ void InitGame()
     switch(Feld[x][y])
     {
       case EL_SPIELFIGUR:
+       Feld[x][y] = EL_SPIELER1;
+       /* no break! */
       case EL_SPIELER1:
-       Feld[x][y] = EL_LEERRAUM;
-       JX = lastJX = x;
-       JY = lastJY = y;
-       break;
       case EL_SPIELER2:
+      case EL_SPIELER3:
+      case EL_SPIELER4:
+      {
+       struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1];
+       int jx = player->jx, jy = player->jy;
+
+       /*
+       player->active = TRUE;
+       */
+
+       player->present = TRUE;
+       if (player->connected)
+       {
+         player->active = TRUE;
+
+         printf("Player %d activated.\n", player->element_nr);
+         printf("[Local player is %d and currently %s.]\n",
+                local_player->element_nr,
+                local_player->active ? "active" : "not active");
+       }
+
+       /* remove potentially duplicate players */
+       if (StorePlayer[jx][jy] == Feld[x][y])
+         StorePlayer[jx][jy] = 0;
+
+       StorePlayer[x][y] = Feld[x][y];
        Feld[x][y] = EL_LEERRAUM;
+       player->jx = player->last_jx = x;
+       player->jy = player->last_jy = y;
+
        break;
+      }
       case EL_BADEWANNE:
        if (x<lev_fieldx-1 && Feld[x+1][y]==EL_SALZSAEURE)
          Feld[x][y] = EL_BADEWANNE1;
@@ -169,14 +271,14 @@ void InitGame()
        MovDelay[x][y] = 96;
        break;
       case EL_BIRNE_AUS:
-       Lights++;
+       local_player->lights_still_needed++;
        break;
       case EL_SOKOBAN_FELD_LEER:
-       SokobanFields++;
+       local_player->sokobanfields_still_needed++;
        break;
       case EL_MAULWURF:
       case EL_PINGUIN:
-       Friends++;
+       local_player->friends_still_needed++;
        break;
       case EL_SCHWEIN:
       case EL_DRACHE:
@@ -187,21 +289,69 @@ void InitGame()
     }
   }
 
+  /* check if any connected player was not found in playfield */
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->connected && !player->present)
+    {
+      printf("Oops!\n");
+
+
+      for(j=0; j<MAX_PLAYERS; j++)
+      {
+       struct PlayerInfo *some_player = &stored_player[j];
+       int jx = some_player->jx, jy = some_player->jy;
+
+       /* assign first free player found that is present in the playfield */
+       if (some_player->present && !some_player->connected)
+       {
+         player->present = TRUE;
+         player->active = TRUE;
+         some_player->present = FALSE;
+
+         StorePlayer[jx][jy] = player->element_nr;
+         player->jx = player->last_jx = jx;
+         player->jy = player->last_jy = jy;
+
+         break;
+       }
+      }
+    }
+  }
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    printf("Player %d: present == %d, connected == %d, active == %d.\n",
+          i+1,
+          player->present,
+          player->connected,
+          player->active);
+    if (local_player == player)
+      printf("Player %d is local player.\n", i+1);
+  }
+
+
   game_emulation = (emulate_bd ? EMU_BOULDERDASH :
                    emulate_sb ? EMU_SOKOBAN : EMU_NONE);
 
   scroll_x = scroll_y = -1;
-  if (JX>=MIDPOSX-1)
-    scroll_x =
-      (JX<=lev_fieldx-MIDPOSX ? JX-MIDPOSX : lev_fieldx-SCR_FIELDX+1);
-  if (JY>=MIDPOSY-1)
-    scroll_y =
-      (JY<=lev_fieldy-MIDPOSY ? JY-MIDPOSY : lev_fieldy-SCR_FIELDY+1);
+  if (local_player->jx >= MIDPOSX-1)
+    scroll_x = (local_player->jx <= lev_fieldx-MIDPOSX ?
+               local_player->jx - MIDPOSX :
+               lev_fieldx - SCR_FIELDX + 1);
+  if (local_player->jy >= MIDPOSY-1)
+    scroll_y = (local_player->jy <= lev_fieldy-MIDPOSY ?
+               local_player->jy - MIDPOSY :
+               lev_fieldy - SCR_FIELDY + 1);
 
   CloseDoor(DOOR_CLOSE_1);
 
   DrawLevel();
-  DrawLevelElement(JX,JY,EL_SPIELFIGUR);
+  DrawAllPlayers();
   FadeToFront();
 
   XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
@@ -212,13 +362,13 @@ void InitGame()
              int2str(level_nr,2),FS_SMALL,FC_YELLOW);
   DrawTextExt(pix[PIX_DB_DOOR],gc,
              DOOR_GFX_PAGEX1+XX_EMERALDS,DOOR_GFX_PAGEY1+YY_EMERALDS,
-             int2str(Gems,3),FS_SMALL,FC_YELLOW);
+             int2str(local_player->gems_still_needed,3),FS_SMALL,FC_YELLOW);
   DrawTextExt(pix[PIX_DB_DOOR],gc,
              DOOR_GFX_PAGEX1+XX_DYNAMITE,DOOR_GFX_PAGEY1+YY_DYNAMITE,
-             int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
+             int2str(local_player->dynamite,3),FS_SMALL,FC_YELLOW);
   DrawTextExt(pix[PIX_DB_DOOR],gc,
              DOOR_GFX_PAGEX1+XX_SCORE,DOOR_GFX_PAGEY1+YY_SCORE,
-             int2str(Score,5),FS_SMALL,FC_YELLOW);
+             int2str(local_player->score,5),FS_SMALL,FC_YELLOW);
   DrawTextExt(pix[PIX_DB_DOOR],gc,
              DOOR_GFX_PAGEX1+XX_TIME,DOOR_GFX_PAGEY1+YY_TIME,
              int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
@@ -226,9 +376,9 @@ void InitGame()
   DrawGameButton(BUTTON_GAME_STOP);
   DrawGameButton(BUTTON_GAME_PAUSE);
   DrawGameButton(BUTTON_GAME_PLAY);
-  DrawSoundDisplay(BUTTON_SOUND_MUSIC  | (BUTTON_ON * sound_music_on));
-  DrawSoundDisplay(BUTTON_SOUND_LOOPS  | (BUTTON_ON * sound_loops_on));
-  DrawSoundDisplay(BUTTON_SOUND_SIMPLE | (BUTTON_ON * sound_simple_on));
+  DrawSoundDisplay(BUTTON_SOUND_MUSIC  | (BUTTON_ON * setup.sound_music_on));
+  DrawSoundDisplay(BUTTON_SOUND_LOOPS  | (BUTTON_ON * setup.sound_loops_on));
+  DrawSoundDisplay(BUTTON_SOUND_SIMPLE | (BUTTON_ON * setup.sound_simple_on));
   XCopyArea(display,drawto,pix[PIX_DB_DOOR],gc,
            DX+GAME_CONTROL_XPOS,DY+GAME_CONTROL_YPOS,
            GAME_CONTROL_XSIZE,2*GAME_CONTROL_YSIZE,
@@ -237,10 +387,16 @@ void InitGame()
 
   OpenDoor(DOOR_OPEN_1);
 
-  if (sound_music_on)
+  if (setup.sound_music_on)
     PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
 
   XAutoRepeatOff(display);
+
+
+  for (i=0;i<4;i++)
+    printf("Spieler %d %saktiv.\n",
+          i+1, (stored_player[i].active ? "" : "nicht "));
+
 }
 
 void InitMovDir(int x, int y)
@@ -356,36 +512,36 @@ void GameWon()
   int hi_pos;
   int bumplevel = FALSE;
 
-  LevelSolved = FALSE;
+  local_player->LevelSolved = FALSE;
 
   if (TimeLeft)
   {
-    if (sound_loops_on)
-      PlaySoundExt(SND_SIRR,PSND_MAX_VOLUME,PSND_MAX_RIGHT,PSND_LOOP);
+    if (setup.sound_loops_on)
+      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
 
-    while(TimeLeft>0)
+    while(TimeLeft > 0)
     {
-      if (!sound_loops_on)
-       PlaySoundStereo(SND_SIRR,PSND_MAX_RIGHT);
+      if (!setup.sound_loops_on)
+       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
       if (TimeLeft && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
        TimeLeft -= 10;
       else
        TimeLeft--;
-      DrawText(DX_TIME,DY_TIME,int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
+      DrawText(DX_TIME, DY_TIME, int2str(TimeLeft,3), FS_SMALL, FC_YELLOW);
       BackToFront();
       Delay(10);
     }
 
-    if (sound_loops_on)
+    if (setup.sound_loops_on)
       StopSound(SND_SIRR);
   }
 
   FadeSounds();
 
   /* Hero disappears */
-  DrawLevelElement(ExitX,ExitY,Feld[ExitX][ExitY]);
+  DrawLevelField(ExitX, ExitY);
   BackToFront();
 
   if (tape.playing)
@@ -399,15 +555,15 @@ void GameWon()
     SaveLevelTape(tape.level_nr);      /* Ask to save tape */
   }
 
-  if (level_nr==player.handicap &&
-      level_nr<leveldir[leveldir_nr].levels-1)
+  if (level_nr == local_player->handicap &&
+      level_nr < leveldir[leveldir_nr].levels-1)
   { 
-    player.handicap++; 
+    local_player->handicap++; 
     bumplevel = TRUE;
     SavePlayerInfo(PLAYER_LEVEL);
   }
 
-  if ((hi_pos=NewHiScore())>=0) 
+  if ((hi_pos=NewHiScore()) >= 0) 
   {
     game_status = HALLOFFAME;
     DrawHallOfFame(hi_pos);
@@ -425,28 +581,30 @@ void GameWon()
   BackToFront();
 }
 
-BOOL NewHiScore()
+boolean NewHiScore()
 {
   int k,l;
   int position = -1;
 
   LoadScore(level_nr);
 
-  if (!strcmp(player.alias_name,EMPTY_ALIAS) ||
-      Score<highscore[MAX_SCORE_ENTRIES-1].Score) 
+  if (!strcmp(local_player->alias_name,EMPTY_ALIAS) ||
+      local_player->score < highscore[MAX_SCORE_ENTRIES-1].Score) 
     return(-1);
 
   for(k=0;k<MAX_SCORE_ENTRIES;k++) 
   {
-    if (Score>highscore[k].Score)      /* Spieler kommt in Highscore-Liste */
+    if (local_player->score > highscore[k].Score)
     {
+      /* Spieler kommt in Highscore-Liste */
+
       if (k<MAX_SCORE_ENTRIES-1)
       {
        int m = MAX_SCORE_ENTRIES-1;
 
 #ifdef ONE_PER_NAME
        for(l=k;l<MAX_SCORE_ENTRIES;l++)
-         if (!strcmp(player.alias_name,highscore[l].Name))
+         if (!strcmp(local_player->alias_name,highscore[l].Name))
            m = l;
        if (m==k)       /* Spieler überschreibt seine alte Position */
          goto put_into_list;
@@ -462,14 +620,14 @@ BOOL NewHiScore()
 #ifdef ONE_PER_NAME
       put_into_list:
 #endif
-      sprintf(highscore[k].Name,player.alias_name);
-      highscore[k].Score = Score; 
+      sprintf(highscore[k].Name,local_player->alias_name);
+      highscore[k].Score = local_player->score; 
       position = k;
       break;
     }
 
 #ifdef ONE_PER_NAME
-    else if (!strcmp(player.alias_name,highscore[k].Name))
+    else if (!strcmp(local_player->alias_name,highscore[k].Name))
       break;   /* Spieler schon mit besserer Punktzahl in der Liste */
 #endif
 
@@ -535,6 +693,14 @@ int MovingOrBlocked2Element(int x, int y)
     return(element);
 }
 
+static void RemoveField(int x, int y)
+{
+  Feld[x][y] = EL_LEERRAUM;
+  MovPos[x][y] = 0;
+  MovDir[x][y] = 0;
+  MovDelay[x][y] = 0;
+}
+
 void RemoveMovingField(int x, int y)
 {
   int oldx = x,oldy = y, newx = x,newy = y;
@@ -577,7 +743,7 @@ void RemoveMovingField(int x, int y)
 
 void DrawDynamite(int x, int y)
 {
-  int sx = SCROLLX(x), sy = SCROLLY(y);
+  int sx = SCREENX(x), sy = SCREENY(y);
   int graphic = el2gfx(Feld[x][y]);
   int phase;
 
@@ -665,7 +831,24 @@ void Explode(int ex, int ey, int phase, int mode)
        element = Store2[x][y];
 
       if (IS_PLAYER(ex,ey))
-       Store[x][y] = EL_EDELSTEIN_GELB;
+      {
+       switch(StorePlayer[ex][ey])
+       {
+         case EL_SPIELER2:
+           Store[x][y] = EL_EDELSTEIN_ROT;
+           break;
+         case EL_SPIELER3:
+           Store[x][y] = EL_EDELSTEIN;
+           break;
+         case EL_SPIELER4:
+           Store[x][y] = EL_EDELSTEIN_LILA;
+           break;
+         case EL_SPIELER1:
+         default:
+           Store[x][y] = EL_EDELSTEIN_GELB;
+           break;
+       }
+      }
       else if (center_element==EL_MAULWURF)
        Store[x][y] = EL_EDELSTEIN_ROT;
       else if (center_element==EL_PINGUIN)
@@ -731,7 +914,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int element = Store2[x][y];
 
     if (IS_PLAYER(x,y))
-      KillHero();
+      KillHero(PLAYERINFO(x,y));
     else if (IS_EXPLOSIVE(element))
     {
       Feld[x][y] = Store2[x][y];
@@ -753,18 +936,19 @@ void Explode(int ex, int ey, int phase, int mode)
       InitMovDir(x,y);
     DrawLevelField(x,y);
   }
-  else if (!(phase%delay) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
+  else if (!(phase%delay) && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
   {
     if (phase==delay)
-      ErdreichAnbroeckeln(SCROLLX(x),SCROLLY(y));
+      ErdreichAnbroeckeln(SCREENX(x),SCREENY(y));
 
-    DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_EXPLOSION+(phase/delay-1));
+    DrawGraphic(SCREENX(x),SCREENY(y),GFX_EXPLOSION+(phase/delay-1));
   }
 }
 
-void DynaExplode(int ex, int ey, int size)
+void DynaExplode(int ex, int ey)
 {
   int i,j;
+  struct PlayerInfo *player = &stored_player[Store2[ex][ey] - EL_SPIELER1];
   static int xy[4][2] =
   {
     { 0,-1 },
@@ -773,11 +957,13 @@ void DynaExplode(int ex, int ey, int size)
     { 0,+1 }
   };
 
+  Store2[ex][ey] = 0;  /* delete player information */
+
   Explode(ex,ey,0,EX_CENTER);
 
   for(i=0;i<4;i++)
   {
-    for(j=1;j<=size;j++)
+    for(j=1; j<=player->dynabomb_size; j++)
     {
       int x = ex+j*xy[i%4][0];
       int y = ey+j*xy[i%4][1];
@@ -792,12 +978,12 @@ void DynaExplode(int ex, int ey, int size)
       if (element != EL_LEERRAUM &&
          element != EL_ERDREICH &&
          element != EL_EXPLODING &&
-         !DynaBombXL)
+         !player->dynabomb_xl)
        break;
     }
   }
 
-  DynaBombsLeft++;
+  player->dynabombs_left++;
 }
 
 void Bang(int x, int y)
@@ -823,7 +1009,7 @@ void Bang(int x, int y)
     case EL_DYNABOMB_NR:
     case EL_DYNABOMB_SZ:
     case EL_DYNABOMB_XL:
-      DynaExplode(x,y,DynaBombSize);
+      DynaExplode(x,y);
       break;
     case EL_BIRNE_AUS:
     case EL_BIRNE_EIN:
@@ -865,8 +1051,8 @@ void Blurb(int x, int y)
     if (MovDelay[x][y])                /* neue Phase / in Wartezustand */
     {
       MovDelay[x][y]--;
-      if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-       DrawGraphic(SCROLLX(x),SCROLLY(y),graphic+4-MovDelay[x][y]/2);
+      if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+       DrawGraphic(SCREENX(x),SCREENY(y),graphic+4-MovDelay[x][y]/2);
 
       if (!MovDelay[x][y])
       {
@@ -879,14 +1065,17 @@ void Blurb(int x, int y)
 
 void Impact(int x, int y)
 {
-  BOOL lastline = (y==lev_fieldy-1);
-  BOOL object_hit = FALSE;
+  boolean lastline = (y==lev_fieldy-1);
+  boolean object_hit = FALSE;
   int element = Feld[x][y];
   int smashed = 0;
 
   /* Element darunter berührt? */
   if (!lastline)
   {
+    if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
+      return;
+
     object_hit = (!IS_FREE(x,y+1) && (!IS_MOVING(x,y+1) ||
                                      MovDir[x][y+1]!=MV_DOWN ||
                                      MovPos[x][y+1]<=TILEY/2));
@@ -912,7 +1101,7 @@ void Impact(int x, int y)
   if (element==EL_TROPFEN && (lastline || object_hit))
   {
     if (object_hit && IS_PLAYER(x,y+1))
-      KillHero();
+      KillHero(PLAYERINFO(x,y+1));
     else if (object_hit && (smashed==EL_MAULWURF || smashed==EL_PINGUIN))
       Bang(x,y+1);
     else
@@ -932,7 +1121,7 @@ void Impact(int x, int y)
 
     if (IS_PLAYER(x,y+1))
     {
-      KillHero();
+      KillHero(PLAYERINFO(x,y+1));
       return;
     }
     else if (smashed==EL_MAULWURF || smashed==EL_PINGUIN)
@@ -1107,7 +1296,7 @@ void TurnRound(int x, int y)
   }
   else if (element==EL_MAMPFER)
   {
-    BOOL can_turn_left = FALSE, can_turn_right = FALSE;
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
 
     if (IN_LEV_FIELD(left_x,left_y) &&
        (IS_FREE_OR_PLAYER(left_x,left_y) ||
@@ -1131,7 +1320,7 @@ void TurnRound(int x, int y)
   }
   else if (element==EL_MAMPFER2)
   {
-    BOOL can_turn_left = FALSE, can_turn_right = FALSE;
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
 
     if (IN_LEV_FIELD(left_x,left_y) &&
        (IS_FREE_OR_PLAYER(left_x,left_y) ||
@@ -1155,7 +1344,7 @@ void TurnRound(int x, int y)
   }
   else if (element==EL_PACMAN)
   {
-    BOOL can_turn_left = FALSE, can_turn_right = FALSE;
+    boolean can_turn_left = FALSE, can_turn_right = FALSE;
 
     if (IN_LEV_FIELD(left_x,left_y) &&
        (IS_FREE_OR_PLAYER(left_x,left_y) ||
@@ -1179,9 +1368,9 @@ void TurnRound(int x, int y)
   }
   else if (element==EL_SCHWEIN)
   {
-    BOOL can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
-    BOOL should_turn_left = FALSE, should_turn_right = FALSE;
-    BOOL should_move_on = FALSE;
+    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+    boolean should_turn_left = FALSE, should_turn_right = FALSE;
+    boolean should_move_on = FALSE;
     int rnd_value = 24;
     int rnd = RND(rnd_value);
 
@@ -1251,7 +1440,7 @@ void TurnRound(int x, int y)
   }
   else if (element==EL_DRACHE)
   {
-    BOOL can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
     int rnd_value = 24;
     int rnd = RND(rnd_value);
 
@@ -1281,14 +1470,32 @@ void TurnRound(int x, int y)
   else if (element==EL_ROBOT || element==EL_SONDE ||
           element==EL_MAULWURF || element==EL_PINGUIN)
   {
-    int attr_x = JX, attr_y = JY;
-    int newx, newy;
+    int attr_x = -1, attr_y = -1;
 
-    if (PlayerGone)
+    if (AllPlayersGone)
     {
       attr_x = ExitX;
       attr_y = ExitY;
     }
+    else
+    {
+      int i;
+
+      for(i=0; i<MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+       if (!player->active || player->gone)
+         continue;
+
+       if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
+       {
+         attr_x = jx;
+         attr_y = jy;
+       }
+      }
+    }
 
     if (element==EL_ROBOT && ZX>=0 && ZY>=0)
     {
@@ -1309,8 +1516,8 @@ void TurnRound(int x, int y)
 
       for(i=0;i<4;i++)
       {
-       int ex = x+xy[i%4][0];
-       int ey = y+xy[i%4][1];
+       int ex = x + xy[i%4][0];
+       int ey = y + xy[i%4][1];
 
        if (IN_LEV_FIELD(ex,ey) && Feld[ex][ey] == EL_AUSGANG_AUF)
        {
@@ -1323,16 +1530,18 @@ void TurnRound(int x, int y)
 
     MovDir[x][y] = MV_NO_MOVING;
     if (attr_x<x)
-      MovDir[x][y] |= (GameOver ? MV_RIGHT : MV_LEFT);
+      MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
     else if (attr_x>x)
-      MovDir[x][y] |= (GameOver ? MV_LEFT : MV_RIGHT);
+      MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
     if (attr_y<y)
-      MovDir[x][y] |= (GameOver ? MV_DOWN : MV_UP);
+      MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
     else if (attr_y>y)
-      MovDir[x][y] |= (GameOver ? MV_UP : MV_DOWN);
+      MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
 
     if (element==EL_ROBOT)
     {
+      int newx, newy;
+
       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
        MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
       Moving2Blocked(x,y,&newx,&newy);
@@ -1344,11 +1553,13 @@ void TurnRound(int x, int y)
     }
     else
     {
+      int newx, newy;
+
       MovDelay[x][y] = 1;
 
       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
       {
-       BOOL first_horiz = RND(2);
+       boolean first_horiz = RND(2);
        int new_move_dir = MovDir[x][y];
 
        MovDir[x][y] =
@@ -1382,6 +1593,28 @@ void TurnRound(int x, int y)
   }
 }
 
+static boolean JustBeingPushed(int x, int y)
+{
+  int i;
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->active && !player->gone &&
+       player->Pushing && player->MovPos)
+    {
+      int next_jx = player->jx + (player->jx - player->last_jx);
+      int next_jy = player->jy + (player->jy - player->last_jy);
+
+      if (x == next_jx && y == next_jy)
+       return(TRUE);
+    }
+  }
+
+  return(FALSE);
+}
+
 void StartMoving(int x, int y)
 {
   int element = Feld[x][y];
@@ -1391,14 +1624,9 @@ void StartMoving(int x, int y)
 
   if (CAN_FALL(element) && y<lev_fieldy-1)
   {
-    if (PlayerPushing && PlayerMovPos)
-    {
-      int nextJX = JX + (JX - lastJX);
-      int nextJY = JY + (JY - lastJY);
-
-      if (x == nextJX && y == nextJY)
+    if ((x>0 && IS_PLAYER(x-1,y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1,y)))
+      if (JustBeingPushed(x,y))
        return;
-    }
 
     if (element==EL_MORAST_VOLL)
     {
@@ -1510,10 +1738,10 @@ void StartMoving(int x, int y)
     }
     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
     {
-      BOOL left  = (x>0 && IS_FREE(x-1,y) &&
-                   (IS_FREE(x-1,y+1) || Feld[x-1][y+1]==EL_SALZSAEURE));
-      BOOL right = (x<lev_fieldx-1 && IS_FREE(x+1,y) &&
-                   (IS_FREE(x+1,y+1) || Feld[x+1][y+1]==EL_SALZSAEURE));
+      boolean left  = (x>0 && IS_FREE(x-1,y) &&
+                      (IS_FREE(x-1,y+1) || Feld[x-1][y+1]==EL_SALZSAEURE));
+      boolean right = (x<lev_fieldx-1 && IS_FREE(x+1,y) &&
+                      (IS_FREE(x+1,y+1) || Feld[x+1][y+1]==EL_SALZSAEURE));
 
       if (left || right)
       {
@@ -1528,6 +1756,9 @@ void StartMoving(int x, int y)
   {
     int newx,newy;
 
+    if (element == EL_SONDE && JustBeingPushed(x,y))
+      return;
+
     if (!MovDelay[x][y])       /* neuer Schritt / noch nicht gewartet */
     {
       /* Alle Figuren, die nach jeden Schritt die Richtung wechseln können.
@@ -1553,8 +1784,8 @@ void StartMoving(int x, int y)
        if (phase>3)
          phase = 7-phase;
 
-       if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-         DrawGraphic(SCROLLX(x),SCROLLY(y), el2gfx(element)+phase);
+       if (IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+         DrawGraphic(SCREENX(x),SCREENY(y), el2gfx(element)+phase);
 
        if ((element==EL_MAMPFER || element==EL_MAMPFER2)
            && MovDelay[x][y]%4==3)
@@ -1575,7 +1806,7 @@ void StartMoving(int x, int y)
        for(i=1;i<=3;i++)
        {
          int xx = x + i*dx, yy = y + i*dy;
-         int sx = SCROLLX(xx), sy = SCROLLY(yy);
+         int sx = SCREENX(xx), sy = SCREENY(yy);
 
          if (!IN_LEV_FIELD(xx,yy) ||
              IS_SOLID(Feld[xx][yy]) || Feld[xx][yy]==EL_EXPLODING)
@@ -1624,7 +1855,7 @@ void StartMoving(int x, int y)
     {
       /* Spieler erwischt */
       MovDir[x][y] = 0;
-      KillHero();
+      KillHero(PLAYERINFO(newx,newy));
       return;
     }
     else if ((element == EL_MAULWURF || element == EL_PINGUIN ||
@@ -1644,18 +1875,19 @@ void StartMoving(int x, int y)
        DrawLevelField(x,y);
 
        PlaySoundLevel(newx,newy,SND_BUING);
-       if (IN_SCR_FIELD(SCROLLX(newx),SCROLLY(newy)))
-         DrawGraphicThruMask(SCROLLX(newx),SCROLLY(newy),el2gfx(element));
+       if (IN_SCR_FIELD(SCREENX(newx),SCREENY(newy)))
+         DrawGraphicThruMask(SCREENX(newx),SCREENY(newy),el2gfx(element));
 
-       Friends--;
-       if (!Friends && PlayerGone && !GameOver)
-         LevelSolved = GameOver = TRUE;
+       local_player->friends_still_needed--;
+       if (!local_player->friends_still_needed &&
+           !local_player->GameOver && AllPlayersGone)
+         local_player->LevelSolved = local_player->GameOver = TRUE;
 
        return;
       }
       else if (IS_MAMPF3(Feld[newx][newy]))
       {
-       if (DigField(newx,newy, 0,0, DF_DIG) == MF_MOVING)
+       if (DigField(local_player, newx,newy, 0,0, DF_DIG) == MF_MOVING)
          DrawLevelField(newx,newy);
        else
          MovDir[x][y] = MV_NO_MOVING;
@@ -1663,7 +1895,7 @@ void StartMoving(int x, int y)
       else if (!IS_FREE(newx,newy))
       {
        if (IS_PLAYER(x,y))
-         DrawPlayerField();
+         DrawPlayerField(x,y);
        else
          DrawLevelField(x,y);
        return;
@@ -1684,7 +1916,7 @@ void StartMoving(int x, int y)
       else if (!IS_FREE(newx,newy))
       {
        if (IS_PLAYER(x,y))
-         DrawPlayerField();
+         DrawPlayerField(x,y);
        else
          DrawLevelField(x,y);
        return;
@@ -1695,14 +1927,14 @@ void StartMoving(int x, int y)
       if (!IS_FREE(newx,newy))
       {
        if (IS_PLAYER(x,y))
-         DrawPlayerField();
+         DrawPlayerField(x,y);
        else
          DrawLevelField(x,y);
        return;
       }
       else
       {
-       BOOL wanna_flame = !RND(10);
+       boolean wanna_flame = !RND(10);
        int dx = newx - x, dy = newy - y;
        int newx1 = newx+1*dx, newy1 = newy+1*dy;
        int newx2 = newx+2*dx, newy2 = newy+2*dy;
@@ -1716,7 +1948,7 @@ void StartMoving(int x, int y)
            element1 != EL_BURNING && element2 != EL_BURNING)
        {
          if (IS_PLAYER(x,y))
-           DrawPlayerField();
+           DrawPlayerField(x,y);
          else
            DrawLevelField(x,y);
 
@@ -1781,13 +2013,7 @@ void StartMoving(int x, int y)
       else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
        DrawGraphicAnimation(x,y, el2gfx(element), 2, 4, ANIM_NORMAL);
       else if (element==EL_SONDE)
-      {
-       int nextJX = JX + (JX - lastJX);
-       int nextJY = JY + (JY - lastJY);
-
-       if (!(PlayerPushing && PlayerGfxPos && x == nextJX && y == nextJY))
-         DrawGraphicAnimation(x,y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
-      }
+       DrawGraphicAnimation(x,y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
 
       return;
     }
@@ -1883,7 +2109,7 @@ void ContinueMoving(int x, int y)
 
     if (DONT_TOUCH(element))   /* Käfer oder Flieger */
     {
-      TestIfBadThingHitsHero();
+      TestIfBadThingHitsHero(newx,newy);
       TestIfBadThingHitsFriend(newx,newy);
       TestIfBadThingHitsOtherBadThing(newx,newy);
     }
@@ -2012,7 +2238,7 @@ void AmoebeUmwandeln2(int ax, int ay, int new_element)
 {
   int x,y;
   int group_nr = AmoebaNr[ax][ay];
-  BOOL done = FALSE;
+  boolean done = FALSE;
 
   for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
   {
@@ -2051,8 +2277,8 @@ void AmoebeWaechst(int x, int y)
   if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
   {
     MovDelay[x][y]--;
-    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_AMOEBING+3-MovDelay[x][y]/2);
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+      DrawGraphic(SCREENX(x),SCREENY(y),GFX_AMOEBING+3-MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
@@ -2115,7 +2341,7 @@ void AmoebeAbleger(int ax, int ay)
   else                         /* normale oder "gefüllte" Amöbe */
   {
     int start = RND(4);
-    BOOL waiting_for_player = FALSE;
+    boolean waiting_for_player = FALSE;
 
     for(i=0;i<4;i++)
     {
@@ -2267,8 +2493,8 @@ void Ablenk(int x, int y)
     MovDelay[x][y]--;
     if (MovDelay[x][y])
     {
-      if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-       DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_ABLENK+MovDelay[x][y]%4);
+      if (IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+       DrawGraphic(SCREENX(x),SCREENY(y),GFX_ABLENK+MovDelay[x][y]%4);
       if (!(MovDelay[x][y]%4))
        PlaySoundLevel(x,y,SND_MIEP);
       return;
@@ -2326,8 +2552,8 @@ void NussKnacken(int x, int y)
   if (MovDelay[x][y])          /* neue Phase / in Wartezustand */
   {
     MovDelay[x][y]--;
-    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+      DrawGraphic(SCREENX(x),SCREENY(y),GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
@@ -2339,21 +2565,24 @@ void NussKnacken(int x, int y)
 
 void SiebAktivieren(int x, int y, int typ)
 {
-  if (!(SiebAktiv % 4) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-    DrawGraphic(SCROLLX(x),SCROLLY(y),
+  if (!(SiebAktiv % 4) && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+    DrawGraphic(SCREENX(x),SCREENY(y),
                (typ==1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL)+3-(SiebAktiv%16)/4);
 }
 
 void AusgangstuerPruefen(int x, int y)
 {
-  if (!Gems && !SokobanFields && !Lights)
+  if (!local_player->gems_still_needed &&
+      !local_player->sokobanfields_still_needed &&
+      !local_player->lights_still_needed)
   {
     Feld[x][y] = EL_AUSGANG_ACT;
 
-    if (IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      PlaySoundLevel(x,y,SND_OEFFNEN);
-    else
-      PlaySoundLevel(JX,JY,SND_OEFFNEN);
+    PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
+                  (x > LEVELX(BX2) ? LEVELX(BX2) : x),
+                  y < LEVELY(BY1) ? LEVELY(BY1) :
+                  (y > LEVELY(BY2) ? LEVELY(BY2) : y),
+                  SND_OEFFNEN);
   }
 }
 
@@ -2370,8 +2599,8 @@ void AusgangstuerOeffnen(int x, int y)
 
     MovDelay[x][y]--;
     tuer = MovDelay[x][y]/delay;
-    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),GFX_AUSGANG_AUF-tuer);
+    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+      DrawGraphic(SCREENX(x),SCREENY(y),GFX_AUSGANG_AUF-tuer);
 
     if (!MovDelay[x][y])
     {
@@ -2388,7 +2617,7 @@ void AusgangstuerBlinken(int x, int y)
 
 void EdelsteinFunkeln(int x, int y)
 {
-  if (!IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)) || IS_MOVING(x,y))
+  if (!IN_SCR_FIELD(SCREENX(x),SCREENY(y)) || IS_MOVING(x,y))
     return;
 
   if (Feld[x][y] == EL_EDELSTEIN_BD)
@@ -2402,38 +2631,27 @@ void EdelsteinFunkeln(int x, int y)
     {
       MovDelay[x][y]--;
 
-      if (direct_draw_on && MovDelay[x][y])
+      if (setup.direct_draw_on && MovDelay[x][y])
        SetDrawtoField(DRAW_BUFFERED);
 
-      DrawGraphic(SCROLLX(x),SCROLLY(y), el2gfx(Feld[x][y]));
+      DrawGraphic(SCREENX(x),SCREENY(y), el2gfx(Feld[x][y]));
 
       if (MovDelay[x][y])
       {
-       int src_x,src_y, dest_x,dest_y;
        int phase = (MovDelay[x][y]-1)/2;
 
        if (phase > 2)
          phase = 4-phase;
 
-       src_x  = SX + GFX_PER_LINE*TILEX;
-       src_y  = SY + phase*TILEY;
-       dest_x = FX + SCROLLX(x)*TILEX;
-       dest_y = FY + SCROLLY(y)*TILEY;
+       DrawGraphicThruMask(SCREENX(x),SCREENY(y), GFX_FUNKELN_WEISS + phase);
 
-       /*
-       XSetClipOrigin(display,clip_gc[PIX_BACK],dest_x-src_x,dest_y-src_y);
-       XCopyArea(display,pix[PIX_BACK],drawto_field,clip_gc[PIX_BACK],
-                 src_x,src_y, TILEX,TILEY, dest_x,dest_y);
-                 */
-
-       XSetClipMask(display, tile_clip_gc,
-                    tile_clipmask[GFX_MASK_SPARKLING + phase]);
-       XSetClipOrigin(display, tile_clip_gc, dest_x,dest_y);
-       XCopyArea(display, pix[PIX_BACK], drawto_field, tile_clip_gc,
-                 src_x,src_y, TILEX,TILEY, dest_x,dest_y);
-
-       if (direct_draw_on)
+       if (setup.direct_draw_on)
        {
+         int dest_x,dest_y;
+
+         dest_x = FX + SCREENX(x)*TILEX;
+         dest_y = FY + SCREENY(y)*TILEY;
+
          XCopyArea(display,drawto_field,window,gc,
                    dest_x,dest_y, TILEX,TILEY, dest_x,dest_y);
          SetDrawtoField(DRAW_DIRECT);
@@ -2456,25 +2674,39 @@ void MauerWaechst(int x, int y)
 
     MovDelay[x][y]--;
     phase = 2-MovDelay[x][y]/delay;
-    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCROLLX(x),SCROLLY(y)))
-      DrawGraphic(SCROLLX(x),SCROLLY(y),
-                 (Store[x][y]==MV_LEFT ? GFX_MAUER_L1 : GFX_MAUER_R1)+phase);
+    if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x),SCREENY(y)))
+      DrawGraphic(SCREENX(x),SCREENY(y),
+                 (MovDir[x][y] == MV_LEFT  ? GFX_MAUER_LEFT  :
+                  MovDir[x][y] == MV_RIGHT ? GFX_MAUER_RIGHT :
+                  MovDir[x][y] == MV_UP    ? GFX_MAUER_UP    :
+                                             GFX_MAUER_DOWN  ) + phase);
 
     if (!MovDelay[x][y])
     {
-      if (Store[x][y]==MV_LEFT)
+      if (MovDir[x][y] == MV_LEFT)
       {
        if (IN_LEV_FIELD(x-1,y) && IS_MAUER(Feld[x-1][y]))
          DrawLevelField(x-1,y);
       }
-      else
+      else if (MovDir[x][y] == MV_RIGHT)
       {
        if (IN_LEV_FIELD(x+1,y) && IS_MAUER(Feld[x+1][y]))
          DrawLevelField(x+1,y);
       }
+      else if (MovDir[x][y] == MV_UP)
+      {
+       if (IN_LEV_FIELD(x,y-1) && IS_MAUER(Feld[x][y-1]))
+         DrawLevelField(x,y-1);
+      }
+      else
+      {
+       if (IN_LEV_FIELD(x,y+1) && IS_MAUER(Feld[x][y+1]))
+         DrawLevelField(x,y+1);
+      }
 
-      Feld[x][y] = EL_MAUER_LEBT;
+      Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
+      MovDir[x][y] = MV_NO_MOVING;
       DrawLevelField(x,y);
     }
   }
@@ -2482,8 +2714,11 @@ void MauerWaechst(int x, int y)
 
 void MauerAbleger(int ax, int ay)
 {
-  BOOL links_frei = FALSE, rechts_frei = FALSE;
-  BOOL links_massiv = FALSE, rechts_massiv = FALSE;
+  int element = Feld[ax][ay];
+  boolean oben_frei = FALSE, unten_frei = FALSE;
+  boolean links_frei = FALSE, rechts_frei = FALSE;
+  boolean oben_massiv = FALSE, unten_massiv = FALSE;
+  boolean links_massiv = FALSE, rechts_massiv = FALSE;
 
   if (!MovDelay[ax][ay])       /* neue Mauer / noch nicht gewartet */
     MovDelay[ax][ay] = 6;
@@ -2495,42 +2730,79 @@ void MauerAbleger(int ax, int ay)
       return;
   }
 
+  if (IN_LEV_FIELD(ax,ay-1) && IS_FREE(ax,ay-1))
+    oben_frei = TRUE;
+  if (IN_LEV_FIELD(ax,ay+1) && IS_FREE(ax,ay+1))
+    unten_frei = TRUE;
   if (IN_LEV_FIELD(ax-1,ay) && IS_FREE(ax-1,ay))
     links_frei = TRUE;
   if (IN_LEV_FIELD(ax+1,ay) && IS_FREE(ax+1,ay))
     rechts_frei = TRUE;
 
-  if (links_frei)
+  if (element == EL_MAUER_Y || element == EL_MAUER_XY)
   {
-    Feld[ax-1][ay] = EL_MAUERND;
-    Store[ax-1][ay] = MV_LEFT;
-    if (IN_SCR_FIELD(SCROLLX(ax-1),SCROLLY(ay)))
-      DrawGraphic(SCROLLX(ax-1),SCROLLY(ay),GFX_MAUER_L1);
+    if (oben_frei)
+    {
+      Feld[ax][ay-1] = EL_MAUERND;
+      Store[ax][ay-1] = element;
+      MovDir[ax][ay-1] = MV_UP;
+      if (IN_SCR_FIELD(SCREENX(ax),SCREENY(ay-1)))
+       DrawGraphic(SCREENX(ax),SCREENY(ay-1),GFX_MAUER_UP);
+    }
+    if (unten_frei)
+    {
+      Feld[ax][ay+1] = EL_MAUERND;
+      Store[ax][ay+1] = element;
+      MovDir[ax][ay+1] = MV_DOWN;
+      if (IN_SCR_FIELD(SCREENX(ax),SCREENY(ay+1)))
+       DrawGraphic(SCREENX(ax),SCREENY(ay+1),GFX_MAUER_DOWN);
+    }
   }
-  if (rechts_frei)
+
+  if (element == EL_MAUER_X || element == EL_MAUER_XY ||
+      element == EL_MAUER_LEBT)
   {
-    Feld[ax+1][ay] = EL_MAUERND;
-    Store[ax+1][ay] = MV_RIGHT;
-    if (IN_SCR_FIELD(SCROLLX(ax+1),SCROLLY(ay)))
-      DrawGraphic(SCROLLX(ax+1),SCROLLY(ay),GFX_MAUER_R1);
+    if (links_frei)
+    {
+      Feld[ax-1][ay] = EL_MAUERND;
+      Store[ax-1][ay] = element;
+      MovDir[ax-1][ay] = MV_LEFT;
+      if (IN_SCR_FIELD(SCREENX(ax-1),SCREENY(ay)))
+       DrawGraphic(SCREENX(ax-1),SCREENY(ay),GFX_MAUER_LEFT);
+    }
+    if (rechts_frei)
+    {
+      Feld[ax+1][ay] = EL_MAUERND;
+      Store[ax+1][ay] = element;
+      MovDir[ax+1][ay] = MV_RIGHT;
+      if (IN_SCR_FIELD(SCREENX(ax+1),SCREENY(ay)))
+       DrawGraphic(SCREENX(ax+1),SCREENY(ay),GFX_MAUER_RIGHT);
+    }
   }
 
-  if (links_frei || rechts_frei)
+  if (element == EL_MAUER_LEBT && (links_frei || rechts_frei))
     DrawLevelField(ax,ay);
 
+  if (!IN_LEV_FIELD(ax,ay-1) || IS_MAUER(Feld[ax][ay-1]))
+    oben_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax,ay+1) || IS_MAUER(Feld[ax][ay+1]))
+    unten_massiv = TRUE;
   if (!IN_LEV_FIELD(ax-1,ay) || IS_MAUER(Feld[ax-1][ay]))
     links_massiv = TRUE;
   if (!IN_LEV_FIELD(ax+1,ay) || IS_MAUER(Feld[ax+1][ay]))
     rechts_massiv = TRUE;
 
-  if (links_massiv && rechts_massiv)
+  if (((oben_massiv && unten_massiv) ||
+       element == EL_MAUER_X || element == EL_MAUER_LEBT) &&
+      ((links_massiv && rechts_massiv) ||
+       element == EL_MAUER_Y))
     Feld[ax][ay] = EL_MAUERWERK;
 }
 
 void CheckForDragon(int x, int y)
 {
   int i,j;
-  BOOL dragon_found = FALSE;
+  boolean dragon_found = FALSE;
   static int xy[4][2] =
   {
     { 0,-1 },
@@ -2576,12 +2848,98 @@ void CheckForDragon(int x, int y)
   }
 }
 
-void GameActions()
+void PlayerActions(struct PlayerInfo *player, byte player_action)
+{
+  static byte stored_player_action[MAX_PLAYERS];
+  static int num_stored_actions = 0;
+  boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
+  int jx = player->jx, jy = player->jy;
+  int left     = player_action & JOY_LEFT;
+  int right    = player_action & JOY_RIGHT;
+  int up       = player_action & JOY_UP;
+  int down     = player_action & JOY_DOWN;
+  int button1  = player_action & JOY_BUTTON_1;
+  int button2  = player_action & JOY_BUTTON_2;
+  int dx       = (left ? -1    : right ? 1     : 0);
+  int dy       = (up   ? -1    : down  ? 1     : 0);
+
+  stored_player_action[player->index_nr] = 0;
+  num_stored_actions++;
+
+  if (!player->active || player->gone)
+    return;
+
+  if (player_action)
+  {
+    player->frame_reset_delay = 0;
+
+    if (button1)
+      snapped = SnapField(player, dx,dy);
+    else
+    {
+      if (button2)
+       bombed = PlaceBomb(player);
+      moved = MoveFigure(player, dx,dy);
+    }
+
+    if (tape.recording && (moved || snapped || bombed))
+    {
+      if (bombed && !moved)
+       player_action &= JOY_BUTTON;
+
+      stored_player_action[player->index_nr] = player_action;
+
+      /* this allows cycled sequences of PlayerActions() */
+      if (num_stored_actions >= MAX_PLAYERS)
+      {
+       TapeRecordAction(stored_player_action);
+       num_stored_actions = 0;
+      }
+    }
+    else if (tape.playing && snapped)
+      SnapField(player, 0,0);                  /* stop snapping */
+  }
+  else
+  {
+    DigField(player, 0,0, 0,0, DF_NO_PUSH);
+    SnapField(player, 0,0);
+    if (++player->frame_reset_delay > MoveSpeed)
+      player->Frame = 0;
+  }
+
+  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)
+    {
+      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(byte player_action)
 {
   static long action_delay = 0;
   long action_delay_value;
   int sieb_x = 0, sieb_y = 0;
-  int x, y, element;
+  int i, x,y, element;
+  int *recorded_player_action;
 
   if (game_status != PLAYING)
     return;
@@ -2594,20 +2952,108 @@ void GameActions()
     (tape.playing && tape.fast_forward ? FFWD_FRAME_DELAY : GAME_FRAME_DELAY);
 #endif
 
-  if (PlayerMovPos)
-    ScrollFigure(0);
-
   /* main game synchronization point */
   WaitUntilDelayReached(&action_delay, action_delay_value);
 
+  if (network_playing && !network_player_action_received)
+  {
+    /*
+#ifdef DEBUG
+    printf("DEBUG: try to get network player actions in time\n");
+#endif
+    */
+
+    /* last chance to get network player actions without main loop delay */
+    HandleNetworking();
+
+    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.playing)
+    recorded_player_action = TapePlayAction();
+  else
+    recorded_player_action = NULL;
+
+  if (network_playing)
+    SendToServer_MovePlayer(player_action);
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    /*
+    int actual_player_action =
+      (options.network ? network_player_action[i] : player_action);
+      */
+
+    int actual_player_action =
+      (network_playing ? network_player_action[i] : player_action);
+
+    /*
+    int actual_player_action = network_player_action[i];
+    */
+
+    /*
+    int actual_player_action = player_action;
+    */
+
+    /* TEST TEST TEST */
+
+    /*
+    if (i != TestPlayer && !stored_player[i].MovPos)
+      actual_player_action = 0;
+    */
+
+    if (!options.network && i != TestPlayer)
+      actual_player_action = 0;
+
+    /* TEST TEST TEST */
+
+    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[i] = 0;
+  }
+
+  network_player_action_received = FALSE;
+
+  ScrollScreen(NULL, SCROLL_GO_ON);
+
+  /*
+  if (tape.pausing || (tape.playing && !TapePlayDelay()))
+    return;
+  else if (tape.recording)
+    TapeRecordDelay();
+  */
+
   FrameCounter++;
   TimeFrames++;
 
+
+  /*
+  printf("FrameCounter == %d, RND(100) == %d\n", FrameCounter, RND(100));
+  */
+
+
   for(y=0;y<lev_fieldy;y++) for(x=0;x<lev_fieldx;x++)
   {
     Stop[x][y] = FALSE;
@@ -2673,14 +3119,18 @@ void GameActions()
       AusgangstuerBlinken(x,y);
     else if (element==EL_MAUERND)
       MauerWaechst(x,y);
-    else if (element==EL_MAUER_LEBT)
+    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);
 
     if (SiebAktiv)
     {
-      BOOL sieb = FALSE;
+      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)
@@ -2695,7 +3145,8 @@ void GameActions()
        sieb = TRUE;
       }
 
-      if (sieb && ABS(x-JX)+ABS(y-JY) < ABS(sieb_x-JX)+ABS(sieb_y-JY))
+      /* 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;
@@ -2741,18 +3192,61 @@ void GameActions()
     DrawText(DX_TIME,DY_TIME,int2str(TimeLeft,3),FS_SMALL,FC_YELLOW);
 
     if (!TimeLeft)
-      KillHero();
+      for(i=0; i<MAX_PLAYERS; i++)
+       KillHero(&stored_player[i]);
+  }
+
+  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);
   }
 
-  DrawPlayerField();
+  return(TRUE);
 }
 
 void ScrollLevel(int dx, int dy)
 {
-  int softscroll_offset = (soft_scrolling_on ? TILEX : 0);
+  int softscroll_offset = (setup.soft_scrolling_on ? TILEX : 0);
   int x,y;
 
-  ScreenMovPos = PlayerGfxPos;
+  /*
+  ScreenGfxPos = local_player->GfxPos;
+  */
 
   XCopyArea(display,drawto_field,drawto_field,gc,
            FX + TILEX*(dx==-1) - softscroll_offset,
@@ -2778,169 +3272,276 @@ void ScrollLevel(int dx, int dy)
   redraw_mask |= REDRAW_FIELD;
 }
 
-BOOL MoveFigureOneStep(int dx, int dy, int real_dx, int real_dy)
+boolean MoveFigureOneStep(struct PlayerInfo *player,
+                         int dx, int dy, int real_dx, int real_dy)
 {
-  int newJX = JX+dx, newJY = JY+dy;
+  int jx = player->jx, jy = player->jy;
+  int new_jx = jx+dx, new_jy = jy+dy;
   int element;
   int can_move;
 
-  if (PlayerGone || (!dx && !dy))
+  if (player->gone || (!dx && !dy))
     return(MF_NO_ACTION);
 
-  PlayerMovDir = (dx < 0 ? MV_LEFT :
-                 dx > 0 ? MV_RIGHT :
-                 dy < 0 ? MV_UP :
-                 dy > 0 ? MV_DOWN :    MV_NO_MOVING);
+  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(newJX,newJY))
+  if (!IN_LEV_FIELD(new_jx,new_jy))
     return(MF_NO_ACTION);
 
-  element = MovingOrBlocked2Element(newJX,newJY);
+  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();
+      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();
+      KillHero(player);
 
     return(MF_MOVING);
   }
 
-  can_move = DigField(newJX,newJY, real_dx,real_dy, DF_DIG);
+  can_move = DigField(player, new_jx,new_jy, real_dx,real_dy, DF_DIG);
   if (can_move != MF_MOVING)
     return(can_move);
 
-  lastJX = JX;
-  lastJY = JY;
-  JX = newJX;
-  JY = newJY;
+  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;
 
-  PlayerMovPos = (dx > 0 || dy > 0 ? -1 : 1) * 7*TILEX/8;
+  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * 7*TILEX/8;
 
-  ScrollFigure(-1);
+  ScrollFigure(player, SCROLL_INIT);
 
   return(MF_MOVING);
 }
 
-BOOL MoveFigure(int dx, int dy)
+boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
 {
-  static long move_delay = 0;
-  static int last_move_dir = MV_NO_MOVING;
+  int jx = player->jx, jy = player->jy;
+  int old_jx = jx, old_jy = jy;
   int moved = MF_NO_ACTION;
-  int oldJX = JX, oldJY = JY;
 
-  if (PlayerGone || (!dx && !dy))
+  if (player->gone || (!dx && !dy))
     return(FALSE);
 
-  if (!FrameReached(&move_delay,MoveSpeed) && !tape.playing)
+  if (!FrameReached(&player->move_delay,MoveSpeed) && !tape.playing)
     return(FALSE);
 
-  if (last_move_dir & (MV_LEFT | MV_RIGHT))
+  if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
   {
-    if (!(moved |= MoveFigureOneStep(0,dy, dx,dy)))
-      moved |= MoveFigureOneStep(dx,0, dx,dy);
+    if (!(moved |= MoveFigureOneStep(player, 0,dy, dx,dy)))
+      moved |= MoveFigureOneStep(player, dx,0, dx,dy);
   }
   else
   {
-    if (!(moved |= MoveFigureOneStep(dx,0, dx,dy)))
-      moved |= MoveFigureOneStep(0,dy, dx,dy);
+    if (!(moved |= MoveFigureOneStep(player, dx,0, dx,dy)))
+      moved |= MoveFigureOneStep(player, 0,dy, dx,dy);
   }
 
-  last_move_dir = MV_NO_MOVING;
+  jx = player->jx;
+  jy = player->jy;
 
-  if (moved & MF_MOVING)
+
+
+  /*
+  if (moved & MF_MOVING && player == local_player)
+  */
+
+  if (moved & MF_MOVING && !ScreenMovPos &&
+      (player == local_player || !options.network))
   {
     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
-    int offset = (scroll_delay_on ? 3 : 0);
+    int offset = (setup.scroll_delay_on ? 3 : 0);
+
+    /*
+    if (player == local_player)
+    {
+      printf("MOVING LOCAL PLAYER && SCROLLING\n");
+    }
+    */
+
+    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 ((scroll_x < jx-MIDPOSX-offset || scroll_x > jx-MIDPOSX+offset) &&
+           jx >= MIDPOSX-1-offset && jx <= lev_fieldx-(MIDPOSX-offset))
+         scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : offset);
+
+       /* 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 ((scroll_y < jy-MIDPOSY-offset || scroll_y > jy-MIDPOSY+offset) &&
+           jy >= MIDPOSY-1-offset && jy <= lev_fieldy-(MIDPOSY-offset))
+         scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : offset);
+
+       /* 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 < JX-MIDPOSX-offset || scroll_x > JX-MIDPOSX+offset) &&
-       JX >= MIDPOSX-1-offset && JX <= lev_fieldx-(MIDPOSX-offset))
-      scroll_x = JX-MIDPOSX + (scroll_x < JX-MIDPOSX ? -offset : offset);
-    if ((scroll_y < JY-MIDPOSY-offset || scroll_y > JY-MIDPOSY+offset) &&
-       JY >= MIDPOSY-1-offset && JY <= lev_fieldy-(MIDPOSY-offset))
-      scroll_y = JY-MIDPOSY + (scroll_y < JY-MIDPOSY ? -offset : offset);
+#if 0
+    if (player == local_player)
+    {
+      if ((scroll_x < jx-MIDPOSX-offset || scroll_x > jx-MIDPOSX+offset) &&
+         jx >= MIDPOSX-1-offset && jx <= lev_fieldx-(MIDPOSX-offset))
+       scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : offset);
+      if ((scroll_y < jy-MIDPOSY-offset || scroll_y > jy-MIDPOSY+offset) &&
+         jy >= MIDPOSY-1-offset && jy <= lev_fieldy-(MIDPOSY-offset))
+       scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : offset);
+
+      /* don't scroll more than one field at a time */
+      scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
+      scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
+    }
+#endif
 
     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
-      ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - 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) && !PlayerPushing)
-    PlayerFrame = 0;
+  if (!(moved & MF_MOVING) && !player->Pushing)
+    player->Frame = 0;
   else
-    PlayerFrame = (PlayerFrame + 1) % 4;
+    player->Frame = (player->Frame + 1) % 4;
 
   if (moved & MF_MOVING)
   {
-    if (oldJX != JX && oldJY == JY)
-      PlayerMovDir = (oldJX < JX ? MV_RIGHT : MV_LEFT);
-    else if (oldJX == JX && oldJY != JY)
-      PlayerMovDir = (oldJY < JY ? MV_DOWN : MV_UP);
+    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);     /* für "ErdreichAnbroeckeln()" */
+    DrawLevelField(jx,jy);     /* für "ErdreichAnbroeckeln()" */
 
-    last_move_dir = PlayerMovDir;
+    player->last_move_dir = player->MovDir;
   }
+  else
+    player->last_move_dir = MV_NO_MOVING;
 
-  TestIfHeroHitsBadThing();
+  TestIfHeroHitsBadThing(jx,jy);
 
-  if (PlayerGone)
-    RemoveHero();
+  if (player->gone)
+    RemoveHero(player);
 
   return(moved);
 }
 
-void ScrollFigure(int init)
+void ScrollFigure(struct PlayerInfo *player, int mode)
 {
-  static long actual_frame_counter = 0;
+  int jx = player->jx, jy = player->jy;
+  int last_jx = player->last_jx, last_jy = player->last_jy;
 
-  if (init)
-  {
-    PlayerGfxPos = ScrollStepSize * (PlayerMovPos / ScrollStepSize);
-    actual_frame_counter = FrameCounter;
+  if (!player->active || player->gone || !player->MovPos)
+    return;
 
-    /*
-    if (Feld[lastJX][lastJY] == EL_LEERRAUM &&
-       IN_LEV_FIELD(lastJX,lastJY-1) &&
-       CAN_FALL(Feld[lastJX][lastJY-1]))
-      Feld[lastJX][lastJY] = EL_PLAYER_IS_LEAVING;
-      */
+  if (mode == SCROLL_INIT)
+  {
+    player->actual_frame_counter = FrameCounter;
+    player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
 
-    if (Feld[lastJX][lastJY] == EL_LEERRAUM)
-      Feld[lastJX][lastJY] = EL_PLAYER_IS_LEAVING;
+    if (Feld[last_jx][last_jy] == EL_LEERRAUM)
+      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
 
-    DrawPlayerField();
+    DrawPlayer(player);
     return;
   }
-  else if (!FrameReached(&actual_frame_counter,1))
+  else if (!FrameReached(&player->actual_frame_counter,1))
     return;
 
-  PlayerMovPos += (PlayerMovPos > 0 ? -1 : 1) * TILEX/8;
-  PlayerGfxPos = ScrollStepSize * (PlayerMovPos / ScrollStepSize);
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX/8;
+  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 (ScreenMovPos && ScreenMovPos != PlayerGfxPos)
+  if (!player->MovPos)
   {
-    ScreenMovPos = PlayerGfxPos;
-    redraw_mask |= REDRAW_FIELD;
+    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;
+    }
   }
+}
 
-  if (Feld[lastJX][lastJY] == EL_PLAYER_IS_LEAVING)
-    Feld[lastJX][lastJY] = EL_LEERRAUM;
+void ScrollScreen(struct PlayerInfo *player, int mode)
+{
+  static long screen_frame_counter = 0;
 
-  DrawPlayerField();
+  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 (!PlayerMovPos)
+  if (ScreenMovPos)
   {
-    lastJX = JX;
-    lastJY = JY;
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX/8;
+    ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+    redraw_mask |= REDRAW_FIELD;
   }
+  else
+    ScreenMovDir = MV_NO_MOVING;
 }
 
 void TestIfGoodThingHitsBadThing(int goodx, int goody)
@@ -2961,12 +3562,12 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     MV_DOWN
   };
 
-  for(i=0;i<4;i++)
+  for(i=0; i<4; i++)
   {
-    int x,y,element;
+    int x,y, element;
 
-    x = goodx+xy[i][0];
-    y = goody+xy[i][1];
+    x = goodx + xy[i][0];
+    y = goody + xy[i][1];
     if (!IN_LEV_FIELD(x,y))
       continue;
 
@@ -2974,7 +3575,7 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
 
     if (DONT_TOUCH(element))
     {
-      if (MovDir[x][y]==harmless[i])
+      if (MovDir[x][y] == harmless[i])
        continue;
 
       killx = x;
@@ -2983,10 +3584,10 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     }
   }
 
-  if (killx!=goodx || killy!=goody)
+  if (killx != goodx || killy != goody)
   {
     if (IS_PLAYER(goodx,goody))
-      KillHero();
+      KillHero(PLAYERINFO(goodx,goody));
     else
       Bang(goodx,goody);
   }
@@ -3010,20 +3611,26 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     MV_DOWN
   };
 
-  for(i=0;i<4;i++)
+  for(i=0; i<4; i++)
   {
-    int x,y,element;
+    int x,y, element;
 
-    x = badx+xy[i][0];
-    y = bady+xy[i][1];
+    x = badx + xy[i][0];
+    y = bady + xy[i][1];
     if (!IN_LEV_FIELD(x,y))
       continue;
 
     element = Feld[x][y];
 
-    if (element==EL_PINGUIN)
+    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))
+      if (MovDir[x][y] == harmless[i] && IS_MOVING(x,y))
        continue;
 
       killx = x;
@@ -3032,19 +3639,27 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     }
   }
 
-  if (killx!=badx || killy!=bady)
-    Bang(killx,killy);
+  if (killx != badx || killy != bady)
+  {
+    if (IS_PLAYER(killx,killy))
+      KillHero(PLAYERINFO(killx,killy));
+    else
+      Bang(killx,killy);
+  }
 }
 
-void TestIfHeroHitsBadThing()
+void TestIfHeroHitsBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(JX,JY);
+  TestIfGoodThingHitsBadThing(x,y);
 }
 
-void TestIfBadThingHitsHero()
+void TestIfBadThingHitsHero(int x, int y)
 {
+  /*
   TestIfGoodThingHitsBadThing(JX,JY);
-  /* (no typo!) */
+  */
+
+  TestIfBadThingHitsGoodThing(x,y);
 }
 
 void TestIfFriendHitsBadThing(int x, int y)
@@ -3059,7 +3674,7 @@ void TestIfBadThingHitsFriend(int x, int y)
 
 void TestIfBadThingHitsOtherBadThing(int badx, int bady)
 {
-  int i, killx=badx, killy=bady;
+  int i, killx = badx, killy = bady;
   static int xy[4][2] =
   {
     { 0,-1 },
@@ -3068,79 +3683,93 @@ void TestIfBadThingHitsOtherBadThing(int badx, int bady)
     { 0,+1 }
   };
 
-  for(i=0;i<4;i++)
+  for(i=0; i<4; i++)
   {
-    int x,y,element;
+    int x,y, element;
 
-    x=badx+xy[i][0];
-    y=bady+xy[i][1];
+    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)
+    element = Feld[x][y];
+    if (IS_AMOEBOID(element) || element == EL_LIFE ||
+       element == EL_AMOEBING || element == EL_TROPFEN)
     {
-      killx=x;
-      killy=y;
+      killx = x;
+      killy = y;
       break;
     }
   }
 
-  if (killx!=badx || killy!=bady)
+  if (killx != badx || killy != bady)
     Bang(badx,bady);
 }
 
-void KillHero()
+void KillHero(struct PlayerInfo *player)
 {
-  if (PlayerGone)
+  int jx = player->jx, jy = player->jy;
+
+  if (player->gone)
     return;
 
-  if (IS_PFORTE(Feld[JX][JY]))
-    Feld[JX][JY] = EL_LEERRAUM;
+  if (IS_PFORTE(Feld[jx][jy]))
+    Feld[jx][jy] = EL_LEERRAUM;
 
-  Bang(JX,JY);
-  BuryHero();
+  Bang(jx,jy);
+  BuryHero(player);
 }
 
-void BuryHero()
+void BuryHero(struct PlayerInfo *player)
 {
-  if (PlayerGone)
+  int jx = player->jx, jy = player->jy;
+
+  if (player->gone)
     return;
 
-  PlaySoundLevel(JX,JY,SND_AUTSCH);
-  PlaySoundLevel(JX,JY,SND_LACHEN);
+  PlaySoundLevel(jx,jy, SND_AUTSCH);
+  PlaySoundLevel(jx,jy, SND_LACHEN);
 
-  GameOver = TRUE;
-  RemoveHero();
+  player->GameOver = TRUE;
+  RemoveHero(player);
 }
 
-void RemoveHero()
+void RemoveHero(struct PlayerInfo *player)
 {
-  PlayerGone = TRUE;
+  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;
-  JX = JY = -1;
+  ExitX = ZX = jx;
+  ExitY = ZY = jy;
 }
 
-int DigField(int x, int y, int real_dx, int real_dy, int mode)
+int DigField(struct PlayerInfo *player,
+            int x, int y, int real_dx, int real_dy, int mode)
 {
-  int dx = x-JX, dy = y-JY;
+  int jx = player->jx, jy = player->jy;
+  int dx = x - jx, dy = y - jy;
   int element;
-  static long push_delay = 0;
-  static int push_delay_value = 5;
 
-  if (!PlayerMovPos)
-    PlayerPushing = FALSE;
+  if (!player->MovPos)
+    player->Pushing = FALSE;
 
   if (mode == DF_NO_PUSH)
   {
-    push_delay = 0;
+    player->push_delay = 0;
     return(MF_NO_ACTION);
   }
 
-  if (IS_MOVING(x,y))
+  if (IS_MOVING(x,y) || IS_PLAYER(x,y))
     return(MF_NO_ACTION);
 
   element = Feld[x][y];
@@ -3159,51 +3788,46 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
     case EL_EDELSTEIN_GELB:
     case EL_EDELSTEIN_ROT:
     case EL_EDELSTEIN_LILA:
-      Feld[x][y] = EL_LEERRAUM;
-      MovDelay[x][y] = 0;      /* wegen EDELSTEIN_BD-Funkeln! */
-      if (Gems>0)
-       Gems--;
-      RaiseScoreElement(EL_EDELSTEIN);
-      DrawText(DX_EMERALDS,DY_EMERALDS,int2str(Gems,3),FS_SMALL,FC_YELLOW);
-      PlaySoundLevel(x,y,SND_PONG);
-      break;
-
     case EL_DIAMANT:
-      Feld[x][y] = EL_LEERRAUM;
-      Gems -= 3;
-      if (Gems<0)
-       Gems=0;
-      RaiseScoreElement(EL_DIAMANT);
-      DrawText(DX_EMERALDS,DY_EMERALDS,int2str(Gems,3),FS_SMALL,FC_YELLOW);
-      PlaySoundLevel(x,y,SND_PONG);
+      RemoveField(x,y);
+      local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
+      if (local_player->gems_still_needed < 0)
+       local_player->gems_still_needed = 0;
+      RaiseScoreElement(element);
+      DrawText(DX_EMERALDS, DY_EMERALDS,
+              int2str(local_player->gems_still_needed, 3),
+              FS_SMALL, FC_YELLOW);
+      PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNAMIT_AUS:
-      Feld[x][y] = EL_LEERRAUM;
-      Dynamite++;
+      RemoveField(x,y);
+      player->dynamite++;
       RaiseScoreElement(EL_DYNAMIT);
-      DrawText(DX_DYNAMITE,DY_DYNAMITE,int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
+      DrawText(DX_DYNAMITE, DY_DYNAMITE,
+              int2str(local_player->dynamite, 3),
+              FS_SMALL, FC_YELLOW);
       PlaySoundLevel(x,y,SND_PONG);
       break;
 
     case EL_DYNABOMB_NR:
-      Feld[x][y] = EL_LEERRAUM;
-      DynaBombCount++;
-      DynaBombsLeft++;
+      RemoveField(x,y);
+      player->dynabomb_count++;
+      player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMIT);
       PlaySoundLevel(x,y,SND_PONG);
       break;
-    case EL_DYNABOMB_SZ:
 
-      Feld[x][y] = EL_LEERRAUM;
-      DynaBombSize++;
+    case EL_DYNABOMB_SZ:
+      RemoveField(x,y);
+      player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMIT);
       PlaySoundLevel(x,y,SND_PONG);
       break;
 
     case EL_DYNABOMB_XL:
-      Feld[x][y] = EL_LEERRAUM;
-      DynaBombXL = TRUE;
+      RemoveField(x,y);
+      player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMIT);
       PlaySoundLevel(x,y,SND_PONG);
       break;
@@ -3215,8 +3839,8 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
     {
       int key_nr = element-EL_SCHLUESSEL1;
 
-      Feld[x][y] = EL_LEERRAUM;
-      Key[key_nr] = TRUE;
+      RemoveField(x,y);
+      player->key[key_nr] = TRUE;
       RaiseScoreElement(EL_SCHLUESSEL);
       DrawMiniGraphicExt(drawto,gc,
                         DX_KEYS+key_nr*MINI_TILEX,DY_KEYS,
@@ -3243,26 +3867,27 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
       if (dy || mode==DF_SNAP)
        return(MF_NO_ACTION);
 
-      PlayerPushing = TRUE;
+      player->Pushing = TRUE;
 
-      if (!IN_LEV_FIELD(x+dx,y+dy) || Feld[x+dx][y+dy] != EL_LEERRAUM)
+      if (!IN_LEV_FIELD(x+dx,y+dy) || !IS_FREE(x+dx,y+dy))
        return(MF_NO_ACTION);
 
       if (real_dy)
       {
-       if (IN_LEV_FIELD(JX,JY+real_dy) && !IS_SOLID(Feld[JX][JY+real_dy]))
+       if (IN_LEV_FIELD(jx,jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
          return(MF_NO_ACTION);
       }
 
-      if (push_delay == 0)
-       push_delay = FrameCounter;
-      if (!FrameReached(&push_delay,push_delay_value) && !tape.playing)
+      if (player->push_delay == 0)
+       player->push_delay = FrameCounter;
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !tape.playing)
        return(MF_NO_ACTION);
 
-      Feld[x][y] = EL_LEERRAUM;
+      RemoveField(x,y);
       Feld[x+dx][y+dy] = element;
 
-      push_delay_value = 2+RND(8);
+      player->push_delay_value = 2+RND(8);
 
       DrawLevelField(x+dx,y+dy);
       if (element==EL_FELSBROCKEN)
@@ -3277,7 +3902,7 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
     case EL_PFORTE2:
     case EL_PFORTE3:
     case EL_PFORTE4:
-      if (!Key[element-EL_PFORTE1])
+      if (!player->key[element-EL_PFORTE1])
        return(MF_NO_ACTION);
       break;
 
@@ -3285,7 +3910,7 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
     case EL_PFORTE2X:
     case EL_PFORTE3X:
     case EL_PFORTE4X:
-      if (!Key[element-EL_PFORTE1X])
+      if (!player->key[element-EL_PFORTE1X])
        return(MF_NO_ACTION);
       break;
 
@@ -3299,17 +3924,21 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
       if (mode==DF_SNAP)
        return(MF_NO_ACTION);
 
-      PlayerGone = TRUE;
       PlaySoundLevel(x,y,SND_BUING);
 
-      if (!Friends)
-       LevelSolved = GameOver = TRUE;
+      /*
+      player->gone = TRUE;
+      PlaySoundLevel(x,y,SND_BUING);
+
+      if (!local_player->friends_still_needed)
+       player->LevelSolved = player->GameOver = TRUE;
+      */
 
       break;
 
     case EL_BIRNE_AUS:
       Feld[x][y] = EL_BIRNE_EIN;
-      Lights--;
+      local_player->lights_still_needed--;
       DrawLevelField(x,y);
       PlaySoundLevel(x,y,SND_DENG);
       return(MF_ACTION);
@@ -3333,28 +3962,29 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
       if (mode==DF_SNAP)
        return(MF_NO_ACTION);
 
-      PlayerPushing = TRUE;
+      player->Pushing = TRUE;
 
       if (!IN_LEV_FIELD(x+dx,y+dy)
-         || (Feld[x+dx][y+dy] != EL_LEERRAUM
+         || (!IS_FREE(x+dx,y+dy)
              && (Feld[x+dx][y+dy] != EL_SOKOBAN_FELD_LEER
                  || !IS_SB_ELEMENT(element))))
        return(MF_NO_ACTION);
 
       if (dx && real_dy)
       {
-       if (IN_LEV_FIELD(JX,JY+real_dy) && !IS_SOLID(Feld[JX][JY+real_dy]))
+       if (IN_LEV_FIELD(jx,jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
          return(MF_NO_ACTION);
       }
       else if (dy && real_dx)
       {
-       if (IN_LEV_FIELD(JX+real_dx,JY) && !IS_SOLID(Feld[JX+real_dx][JY]))
+       if (IN_LEV_FIELD(jx+real_dx,jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
          return(MF_NO_ACTION);
       }
 
-      if (push_delay == 0)
-       push_delay = FrameCounter;
-      if (!FrameReached(&push_delay,push_delay_value) && !tape.playing)
+      if (player->push_delay == 0)
+       player->push_delay = FrameCounter;
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !tape.playing)
        return(MF_NO_ACTION);
 
       if (IS_SB_ELEMENT(element))
@@ -3362,15 +3992,15 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
        if (element == EL_SOKOBAN_FELD_VOLL)
        {
          Feld[x][y] = EL_SOKOBAN_FELD_LEER;
-         SokobanFields++;
+         local_player->sokobanfields_still_needed++;
        }
        else
-         Feld[x][y] = EL_LEERRAUM;
+         RemoveField(x,y);
 
        if (Feld[x+dx][y+dy] == EL_SOKOBAN_FELD_LEER)
        {
          Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
-         SokobanFields--;
+         local_player->sokobanfields_still_needed--;
          if (element == EL_SOKOBAN_OBJEKT)
            PlaySoundLevel(x,y,SND_DENG);
        }
@@ -3379,20 +4009,21 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
       }
       else
       {
-       Feld[x][y] = EL_LEERRAUM;
+       RemoveField(x,y);
        Feld[x+dx][y+dy] = element;
       }
 
-      push_delay_value = 2;
+      player->push_delay_value = 2;
 
       DrawLevelField(x,y);
       DrawLevelField(x+dx,y+dy);
       PlaySoundLevel(x+dx,y+dy,SND_PUSCH);
 
       if (IS_SB_ELEMENT(element) &&
-         SokobanFields == 0 && game_emulation == EMU_SOKOBAN)
+         local_player->sokobanfields_still_needed == 0 &&
+         game_emulation == EMU_SOKOBAN)
       {
-       LevelSolved = GameOver = TRUE;
+       player->LevelSolved = player->GameOver = TRUE;
        PlaySoundLevel(x,y,SND_BUING);
       }
 
@@ -3409,73 +4040,81 @@ int DigField(int x, int y, int real_dx, int real_dy, int mode)
       break;
   }
 
-  push_delay = 0;
+  player->push_delay = 0;
 
   return(MF_MOVING);
 }
 
-BOOL SnapField(int dx, int dy)
+boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
-  int x = JX+dx, y = JY+dy;
-  static int snapped = FALSE;
+  int jx = player->jx, jy = player->jy;
+  int x = jx + dx, y = jy + dy;
 
-  if (PlayerGone || !IN_LEV_FIELD(x,y))
+  if (player->gone || !IN_LEV_FIELD(x,y))
     return(FALSE);
+
   if (dx && dy)
     return(FALSE);
+
   if (!dx && !dy)
   {
-    snapped = FALSE;
+    player->snapped = FALSE;
     return(FALSE);
   }
-  if (snapped)
+
+  if (player->snapped)
     return(FALSE);
 
-  PlayerMovDir = (dx < 0 ? MV_LEFT :
-                 dx > 0 ? MV_RIGHT :
-                 dy < 0 ? MV_UP :
-                 dy > 0 ? MV_DOWN :    MV_NO_MOVING);
+  player->MovDir = (dx < 0 ? MV_LEFT :
+                   dx > 0 ? MV_RIGHT :
+                   dy < 0 ? MV_UP :
+                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
 
-  if (!DigField(x,y, 0,0, DF_SNAP))
+  if (!DigField(player, x,y, 0,0, DF_SNAP))
     return(FALSE);
 
-  snapped = TRUE;
+  player->snapped = TRUE;
   DrawLevelField(x,y);
   BackToFront();
 
   return(TRUE);
 }
 
-BOOL PlaceBomb(void)
+boolean PlaceBomb(struct PlayerInfo *player)
 {
+  int jx = player->jx, jy = player->jy;
   int element;
 
-  if (PlayerGone || PlayerMovPos)
+  if (player->gone || player->MovPos)
     return(FALSE);
 
-  element = Feld[JX][JY];
+  element = Feld[jx][jy];
 
-  if ((Dynamite==0 && DynaBombsLeft==0) ||
+  if ((player->dynamite==0 && player->dynabombs_left==0) ||
       element==EL_DYNAMIT || element==EL_DYNABOMB || element==EL_EXPLODING)
     return(FALSE);
 
   if (element != EL_LEERRAUM)
-    Store[JX][JY] = element;
+    Store[jx][jy] = element;
 
-  if (Dynamite)
+  if (player->dynamite)
   {
-    Feld[JX][JY] = EL_DYNAMIT;
-    MovDelay[JX][JY] = 96;
-    Dynamite--;
-    DrawText(DX_DYNAMITE,DY_DYNAMITE,int2str(Dynamite,3),FS_SMALL,FC_YELLOW);
-    DrawGraphicThruMask(SCROLLX(JX),SCROLLY(JY),GFX_DYNAMIT);
+    Feld[jx][jy] = EL_DYNAMIT;
+    MovDelay[jx][jy] = 96;
+    player->dynamite--;
+    DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
+            FS_SMALL, FC_YELLOW);
+    if (IN_SCR_FIELD(SCREENX(jx),SCREENY(jy)))
+      DrawGraphicThruMask(SCREENX(jx),SCREENY(jy),GFX_DYNAMIT);
   }
   else
   {
-    Feld[JX][JY] = EL_DYNABOMB;
-    MovDelay[JX][JY] = 96;
-    DynaBombsLeft--;
-    DrawGraphicThruMask(SCROLLX(JX),SCROLLY(JY),GFX_DYNABOMB);
+    Feld[jx][jy] = EL_DYNABOMB;
+    Store2[jx][jy] = player->element_nr;       /* for DynaExplode() */
+    MovDelay[jx][jy] = 96;
+    player->dynabombs_left--;
+    if (IN_SCR_FIELD(SCREENX(jx),SCREENY(jy)))
+      DrawGraphicThruMask(SCREENX(jx),SCREENY(jy),GFX_DYNABOMB);
   }
 
   return(TRUE);
@@ -3483,12 +4122,12 @@ BOOL PlaceBomb(void)
 
 void PlaySoundLevel(int x, int y, int sound_nr)
 {
-  int sx = SCROLLX(x), sy = SCROLLY(y);
+  int sx = SCREENX(x), sy = SCREENY(y);
   int volume, stereo;
   int silence_distance = 8;
 
-  if ((!sound_simple_on && !IS_LOOP_SOUND(sound_nr)) ||
-      (!sound_loops_on && IS_LOOP_SOUND(sound_nr)))
+  if ((!setup.sound_simple_on && !IS_LOOP_SOUND(sound_nr)) ||
+      (!setup.sound_loops_on && IS_LOOP_SOUND(sound_nr)))
     return;
 
   if (!IN_LEV_FIELD(x,y) ||
@@ -3518,8 +4157,9 @@ void PlaySoundLevel(int x, int y, int sound_nr)
 
 void RaiseScore(int value)
 {
-  Score += value;
-  DrawText(DX_SCORE,DY_SCORE,int2str(Score,5),FS_SMALL,FC_YELLOW);
+  local_player->score += value;
+  DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
+          FS_SMALL, FC_YELLOW);
 }
 
 void RaiseScoreElement(int element)
@@ -3527,6 +4167,10 @@ void RaiseScoreElement(int element)
   switch(element)
   {
     case EL_EDELSTEIN:
+    case EL_EDELSTEIN_BD:
+    case EL_EDELSTEIN_GELB:
+    case EL_EDELSTEIN_ROT:
+    case EL_EDELSTEIN_LILA:
       RaiseScore(level.score[SC_EDELSTEIN]);
       break;
     case EL_DIAMANT: