rnd-19990213-1
[rocksndiamonds.git] / src / game.c
index 4514b646f2ca304e1f51546cab27cd22dcf5a6dc..b233fa55c1ef299c7d1fe3976d5d91a94ddf784e 100644 (file)
 #define SC_SCHLUESSEL          9
 #define SC_ZEITBONUS           10
 
+/* game button identifiers */
+#define GAME_CTRL_ID_STOP              0
+#define GAME_CTRL_ID_PAUSE             1
+#define GAME_CTRL_ID_PLAY              2
+#define SOUND_CTRL_ID_MUSIC            3
+#define SOUND_CTRL_ID_LOOPS            4
+#define SOUND_CTRL_ID_SIMPLE           5
+
+#define NUM_GAME_BUTTONS               6
+
+/* forward declaration for internal use */
+static void MapGameButtons();
+static void HandleGameButtons(struct GadgetInfo *);
+
+static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 
 
@@ -324,6 +339,7 @@ void InitGame()
 
     player->action = 0;
     player->effective_action = 0;
+    player->programmed_action = 0;
 
     player->score = 0;
     player->gems_still_needed = level.edelsteine;
@@ -356,6 +372,7 @@ void InitGame()
     player->move_delay = 0;
     player->last_move_dir = MV_NO_MOVING;
 
+    player->move_speed = (level.double_speed ? 4 : 8);
     player->snapped = FALSE;
 
     player->gone = FALSE;
@@ -390,16 +407,12 @@ void InitGame()
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
-  if (level.high_speed)
-  {
-    MoveSpeed = 4;
-    ScrollStepSize = TILEX/4;
-  }
-  else
-  {
-    MoveSpeed = 8;
-    ScrollStepSize = TILEX/8;
-  }
+  /*
+  MoveSpeed = (level.double_speed ? 4 : 8);
+  ScrollStepSize = TILEX / MoveSpeed;
+  */
+
+  ScrollStepSize = 0;
 
   AllPlayersGone = FALSE;
   SiebAktiv = FALSE;
@@ -553,10 +566,10 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
-  if (lev_fieldx < SCR_FIELDX)
+  if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
 
-  if (lev_fieldy < SCR_FIELDY)
+  if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
 
   scroll_x = SBX_Left;
@@ -595,18 +608,32 @@ void InitGame()
              DOOR_GFX_PAGEX1 + XX_TIME, DOOR_GFX_PAGEY1 + YY_TIME,
              int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
+
+
+#if 0
   DrawGameButton(BUTTON_GAME_STOP);
   DrawGameButton(BUTTON_GAME_PAUSE);
   DrawGameButton(BUTTON_GAME_PLAY);
   DrawSoundDisplay(BUTTON_SOUND_MUSIC  | (setup.sound_music  ? BUTTON_ON : 0));
   DrawSoundDisplay(BUTTON_SOUND_LOOPS  | (setup.sound_loops  ? BUTTON_ON : 0));
   DrawSoundDisplay(BUTTON_SOUND_SIMPLE | (setup.sound_simple ? BUTTON_ON : 0));
+#else
+  UnmapGameButtons();
+  game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
+  game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
+  game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+  MapGameButtons();
+  MapTapeButtons();
+#endif
+
   XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
            DX + GAME_CONTROL_XPOS, DY + GAME_CONTROL_YPOS,
            GAME_CONTROL_XSIZE, 2 * GAME_CONTROL_YSIZE,
            DOOR_GFX_PAGEX1 + GAME_CONTROL_XPOS,
            DOOR_GFX_PAGEY1 + GAME_CONTROL_YPOS);
 
+
+
   OpenDoor(DOOR_OPEN_ALL);
 
   if (setup.sound_music)
@@ -1055,6 +1082,7 @@ void Explode(int ex, int ey, int phase, int mode)
   int num_phase = 9, delay = 2;
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
+  int first_phase_after_start = EX_PHASE_START + 1;
 
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
@@ -1169,9 +1197,20 @@ void Explode(int ex, int ey, int phase, int mode)
   x = ex;
   y = ey;
 
-  Frame[x][y] = (phase<last_phase ? phase+1 : 0);
+  Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
 
-  if (phase == half_phase)
+  if (phase == first_phase_after_start)
+  {
+    int element = Store2[x][y];
+
+    if (element == EL_BLACK_ORB)
+    {
+      Feld[x][y] = Store2[x][y];
+      Store2[x][y] = 0;
+      Bang(x, y);
+    }
+  }
+  else if (phase == half_phase)
   {
     int element = Store2[x][y];
 
@@ -3328,8 +3367,14 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
   {
     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
+
+    /*
     if (++player->frame_reset_delay > MoveSpeed)
       player->Frame = 0;
+    */
+
+    if (++player->frame_reset_delay > player->move_speed)
+      player->Frame = 0;
   }
 
   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
@@ -3481,6 +3526,22 @@ void GameActions()
   {
     int actual_player_action = stored_player[i].effective_action;
 
+    if (stored_player[i].programmed_action)
+    {
+      /* this is very bad and need to be fixed!!! */
+      unsigned long move_delay = stored_player[i].move_delay;
+
+      /*
+      if (FrameReached(&move_delay, MoveSpeed))
+      */
+
+      if (FrameReached(&move_delay, stored_player[i].move_speed))
+      {
+       actual_player_action = stored_player[i].programmed_action;
+       stored_player[i].programmed_action = 0;
+      }
+    }
+
     if (recorded_player_action)
       actual_player_action = recorded_player_action[i];
 
@@ -3819,7 +3880,12 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   jy = player->jy = new_jy;
   StorePlayer[jx][jy] = player->element_nr;
 
+  /*
   player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / MoveSpeed);
+  */
+
+  player->MovPos =
+    (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_speed);
 
   ScrollFigure(player, SCROLL_INIT);
 
@@ -3835,22 +3901,36 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   if (player->gone || (!dx && !dy))
     return FALSE;
 
+  /*
   if (!FrameReached(&player->move_delay, MoveSpeed) && !tape.playing)
     return FALSE;
+  */
+
+  if (!FrameReached(&player->move_delay, player->move_speed) && !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;
+    */
+
+    int old_move_speed = player->move_speed;
 
 #if DEBUG
     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
 #endif
 
     /* scroll remaining steps with finest movement resolution */
+
+    /*
     MoveSpeed = 8;
+    */
+
+    player->move_speed = 8;
 
     while (player->MovPos)
     {
@@ -3861,7 +3941,12 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       BackToFront();
     }
 
+    /*
     MoveSpeed = old_move_speed;
+    */
+
+    player->move_speed = old_move_speed;
+
   }
 
   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
@@ -3978,6 +4063,7 @@ 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;
+  int move_stepsize = TILEX / player->move_speed;
 
   if (!player->active || player->gone || !player->MovPos)
     return;
@@ -3985,7 +4071,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
   if (mode == SCROLL_INIT)
   {
     player->actual_frame_counter = FrameCounter;
-    player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+    player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
     if (Feld[last_jx][last_jy] == EL_LEERRAUM)
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
@@ -3996,8 +4082,8 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
-  player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+  player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
     Feld[last_jx][last_jy] = EL_LEERRAUM;
@@ -4006,6 +4092,23 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
 
   if (!player->MovPos)
   {
+    if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
+    {
+      /* continue with normal speed after moving through port */
+      /* FIX THIS: what about player already having eaten a speed pill? */
+
+      /*
+      MoveSpeed = 8;
+      ScrollStepSize = TILEX / MoveSpeed;
+      */
+
+      player->move_speed = 8;
+
+      /* don't wait for the next move -- the whole move delay stuff
+        is worse at the moment; FIX THIS! ;-) */
+      player->move_delay = 0;
+    }
+
     player->last_jx = jx;
     player->last_jy = jy;
 
@@ -4025,6 +4128,9 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (mode == SCROLL_INIT)
   {
+    /* set scrolling step size according to actual player's moving speed */
+    ScrollStepSize = TILEX / player->move_speed;
+
     screen_frame_counter = FrameCounter;
     ScreenMovDir = player->MovDir;
     ScreenMovPos = player->MovPos;
@@ -4036,7 +4142,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (ScreenMovPos)
   {
-    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
     redraw_mask |= REDRAW_FIELD;
   }
@@ -4254,6 +4360,10 @@ int DigField(struct PlayerInfo *player,
 {
   int jx = player->jx, jy = player->jy;
   int dx = x - jx, dy = y - jy;
+  int move_direction = (dx == -1 ? MV_LEFT :
+                       dx == +1 ? MV_RIGHT :
+                       dy == -1 ? MV_UP :
+                       dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
   if (!player->MovPos)
@@ -4310,8 +4420,14 @@ int DigField(struct PlayerInfo *player,
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
+
+      player->move_speed = 4;
+
+      /*
       MoveSpeed = 4;
-      ScrollStepSize = TILEX/4;
+      ScrollStepSize = TILEX / MoveSpeed;
+      */
+
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
@@ -4452,7 +4568,7 @@ int DigField(struct PlayerInfo *player,
     case EL_PFORTE2:
     case EL_PFORTE3:
     case EL_PFORTE4:
-      if (!player->key[element-EL_PFORTE1])
+      if (!player->key[element - EL_PFORTE1])
        return MF_NO_ACTION;
       break;
 
@@ -4460,8 +4576,40 @@ int DigField(struct PlayerInfo *player,
     case EL_PFORTE2X:
     case EL_PFORTE3X:
     case EL_PFORTE4X:
-      if (!player->key[element-EL_PFORTE1X])
+      if (!player->key[element - EL_PFORTE1X])
+       return MF_NO_ACTION;
+      break;
+
+    case EL_EM_GATE_1:
+    case EL_EM_GATE_2:
+    case EL_EM_GATE_3:
+    case EL_EM_GATE_4:
+      if (!player->key[element - EL_EM_GATE_1])
+       return MF_NO_ACTION;
+
+      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
        return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      player->move_speed = 4;
+
+      break;
+
+    case EL_EM_GATE_1X:
+    case EL_EM_GATE_2X:
+    case EL_EM_GATE_3X:
+    case EL_EM_GATE_4X:
+      if (!player->key[element - EL_EM_GATE_1X])
+       return MF_NO_ACTION;
+
+      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
+       return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      player->move_speed = 4;
+
       break;
 
     case EL_SP_PORT1_LEFT:
@@ -4498,6 +4646,16 @@ int DigField(struct PlayerInfo *player,
          !IN_LEV_FIELD(x + dx, y + dy) ||
          !IS_FREE(x + dx, y + dy))
        return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      player->move_speed = 4;
+
+      /*
+      MoveSpeed = 4;
+      ScrollStepSize = TILEX / MoveSpeed;
+      */
+
       break;
 
     case EL_AUSGANG_ZU:
@@ -4800,3 +4958,237 @@ void RaiseScoreElement(int element)
       break;
   }
 }
+
+/* ---------- new game button stuff ---------------------------------------- */
+
+/* graphic position values for game buttons */
+#define GAME_BUTTON_XSIZE      30
+#define GAME_BUTTON_YSIZE      30
+#define GAME_BUTTON_XPOS       5
+#define GAME_BUTTON_YPOS       215
+#define SOUND_BUTTON_XPOS      5
+#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
+
+#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  char *infotext;
+} gamebutton_info[NUM_GAME_BUTTONS] =
+{
+  {
+    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_STOP,
+    "stop game"
+  },
+  {
+    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PAUSE,
+    "pause game"
+  },
+  {
+    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PLAY,
+    "play game"
+  },
+  {
+    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_MUSIC,
+    "background music on/off"
+  },
+  {
+    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_LOOPS,
+    "sound loops on/off"
+  },
+  {
+    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_SIMPLE,
+    "normal sounds on/off"
+  }
+};
+
+void CreateGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    struct GadgetInfo *gi;
+    int button_type;
+    boolean checked;
+    unsigned long event_mask;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    int id = i;
+
+    gd_xoffset = gamebutton_info[i].x;
+    gd_yoffset = gamebutton_info[i].y;
+    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+
+    if (id == GAME_CTRL_ID_STOP ||
+       id == GAME_CTRL_ID_PAUSE ||
+       id == GAME_CTRL_ID_PLAY)
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      checked = FALSE;
+      event_mask = GD_EVENT_RELEASED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+    else
+    {
+      button_type = GD_TYPE_CHECK_BUTTON;
+      checked =
+       ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
+        (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
+        (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
+      event_mask = GD_EVENT_PRESSED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, gamebutton_info[i].infotext,
+                     GDI_X, DX + gd_xoffset,
+                     GDI_Y, DY + gd_yoffset,
+                     GDI_WIDTH, GAME_BUTTON_XSIZE,
+                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_TYPE, button_type,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_CHECKED, checked,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleGameButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    game_gadget[id] = gi;
+  }
+}
+
+static void MapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    MapGadget(game_gadget[i]);
+}
+
+void UnmapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    UnmapGadget(game_gadget[i]);
+}
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+
+  if (game_status != PLAYING)
+    return;
+
+  switch (id)
+  {
+    case GAME_CTRL_ID_STOP:
+      if (AllPlayersGone)
+      {
+       CloseDoor(DOOR_CLOSE_1);
+       game_status = MAINMENU;
+       DrawMainMenu();
+       break;
+      }
+
+      if (Request("Do you really want to quit the game ?",
+                 REQ_ASK | REQ_STAY_CLOSED))
+      { 
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_StopPlaying();
+       else
+#endif
+       {
+         game_status = MAINMENU;
+         DrawMainMenu();
+       }
+      }
+      else
+       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+      break;
+
+    case GAME_CTRL_ID_PAUSE:
+      if (options.network)
+      {
+#ifndef MSDOS
+       if (tape.pausing)
+         SendToServer_ContinuePlaying();
+       else
+         SendToServer_PausePlaying();
+#endif
+      }
+      else
+       TapeTogglePause();
+      break;
+
+    case GAME_CTRL_ID_PLAY:
+      if (tape.pausing)
+      {
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_ContinuePlaying();
+       else
+#endif
+       {
+         tape.pausing = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+       }
+      }
+      break;
+
+    case SOUND_CTRL_ID_MUSIC:
+      if (setup.sound_music)
+      { 
+       setup.sound_music = FALSE;
+       FadeSound(background_loop[level_nr % num_bg_loops]);
+      }
+      else if (sound_loops_allowed)
+      { 
+       setup.sound = setup.sound_music = TRUE;
+       PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+      }
+      break;
+
+    case SOUND_CTRL_ID_LOOPS:
+      if (setup.sound_loops)
+       setup.sound_loops = FALSE;
+      else if (sound_loops_allowed)
+       setup.sound = setup.sound_loops = TRUE;
+      break;
+
+    case SOUND_CTRL_ID_SIMPLE:
+      if (setup.sound_simple)
+       setup.sound_simple = FALSE;
+      else if (sound_status==SOUND_AVAILABLE)
+       setup.sound = setup.sound_simple = TRUE;
+      break;
+
+    default:
+      break;
+  }
+}