rnd-20091011-1-src
authorHolger Schemel <info@artsoft.org>
Sun, 11 Oct 2009 20:18:38 +0000 (22:18 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:57:39 +0000 (10:57 +0200)
* fixed another translation problem from VisualBasic to C (where "int"
  should be "short") causing unsolvable demos with bugs and terminals
  ("bugs" being related to the Supaplex "buggy base" element here ;-) )

16 files changed:
ChangeLog
src/conftime.h
src/files.c
src/game_sp/ASM.c
src/game_sp/ASM.h
src/game_sp/Globals.c
src/game_sp/MainForm.c
src/game_sp/MainGameLoop.c
src/game_sp/MainGameLoop.h
src/game_sp/Makefile
src/game_sp/Murphy.c
src/game_sp/export.h
src/game_sp/file.c [new file with mode: 0644]
src/game_sp/main.c
src/game_sp/modAnimations.c
src/main.h

index 1e307c07ad4dc7e049e79228883443b230856e2b..39a8bcd8b88f7b16b02836de817368271e6e313c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-09-25
+       * fixed another translation problem from VisualBasic to C (where "int"
+         should be "short") causing unsolvable demos with bugs and terminals
+         ("bugs" being related to the Supaplex "buggy base" element here ;-) )
+
 2009-09-23
        * fixed bug when reading Supaplex single level files (preventing loader
          from seeking to level position like in Supaplex level package files)
index 847d17c62623cd41f7487e8784f3e904cb3f09ae..a00952ee912e77828976725748000c2645d5a8e7 100644 (file)
@@ -1 +1 @@
-#define COMPILE_DATE_STRING "2009-09-25 20:53"
+#define COMPILE_DATE_STRING "2009-10-11 22:17"
index 863ac2036459f5a820f3205435c8a45a5253a234..85a4b23d9adbe64f2a60081fff538bdf61da7c7b 100644 (file)
@@ -1587,8 +1587,10 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
   *level = li;         /* copy temporary buffer back to level data */
 
   setLevelInfoToDefaults_EM();
+  setLevelInfoToDefaults_SP();
 
   level->native_em_level = &native_em_level;
+  level->native_sp_level = &native_sp_level;
 
   level->file_version = FILE_VERSION_ACTUAL;
   level->game_version = GAME_VERSION_ACTUAL;
@@ -3799,30 +3801,13 @@ void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   }
 }
 
-static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
-                                    struct LevelFileInfo *level_file_info)
-{
-  if (!LoadNativeLevel_EM(level_file_info->filename))
-    level->no_valid_file = TRUE;
-}
-
-void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
-{
-  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
-    CopyNativeLevel_RND_to_EM(level);
-}
-
-void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
-{
-  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
-    CopyNativeLevel_EM_to_RND(level);
-}
-
 
 /* ------------------------------------------------------------------------- */
 /* functions for loading SP level                                            */
 /* ------------------------------------------------------------------------- */
 
+#if 0
+
 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE        111
 #define SP_LEVEL_SIZE                  1536
 #define SP_LEVEL_XSIZE                 60
@@ -4168,6 +4153,121 @@ static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
     *level = multipart_level;
 }
 
+#endif
+
+void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
+{
+  /* ... yet to be written ... */
+}
+
+void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
+{
+  LevelInfoType *header = &native_sp_level.header;
+  int i, x, y;
+
+  level->fieldx = native_sp_level.width;
+  level->fieldy = native_sp_level.height;
+
+  for (x = 0; x < level->fieldx; x++)
+  {
+    for (y = 0; y < level->fieldy; y++)
+    {
+      int element_old = native_sp_level.playfield[x][y];
+      int element_new;
+
+      if (element_old <= 0x27)
+       element_new = getMappedElement(EL_SP_START + element_old);
+      else if (element_old == 0x28)
+       element_new = EL_INVISIBLE_WALL;
+      else
+      {
+       Error(ERR_WARN, "invalid element %d at position %d, %d",
+             element_old, x, y);
+
+       element_new = EL_UNKNOWN;
+      }
+
+      level->field[x][y] = element_new;
+    }
+  }
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    level->initial_player_gravity[i] =
+      (header->InitialGravity == 1 ? TRUE : FALSE);
+
+  for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+    level->name[i] = header->LevelTitle[i];
+  level->name[SP_LEVEL_NAME_LEN] = '\0';
+
+  level->gems_needed = header->InfotronsNeeded;
+
+  for (i = 0; i < header->SpecialPortCount; i++)
+  {
+    SpecialPortType *port = &header->SpecialPort[i];
+    int port_location = port->PortLocation;
+    int gravity = port->Gravity;
+    int port_x, port_y, port_element;
+
+    port_x = (port_location / 2) % level->fieldx;
+    port_y = (port_location / 2) / level->fieldx;
+
+    if (port_x < 0 || port_x >= level->fieldx ||
+       port_y < 0 || port_y >= level->fieldy)
+    {
+      Error(ERR_WARN, "special port position (%d, %d) out of bounds",
+           port_x, port_y);
+
+      continue;
+    }
+
+    port_element = level->field[port_x][port_y];
+
+    if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
+       port_element > EL_SP_GRAVITY_PORT_UP)
+    {
+      Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
+
+      continue;
+    }
+
+    /* change previous (wrong) gravity inverting special port to either
+       gravity enabling special port or gravity disabling special port */
+    level->field[port_x][port_y] +=
+      (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
+       EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
+  }
+
+  /* change special gravity ports without database entries to normal ports */
+  for (x = 0; x < level->fieldx; x++)
+    for (y = 0; y < level->fieldy; y++)
+      if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
+         level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
+       level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
+
+  level->time = 0;                     /* no time limit */
+  level->amoeba_speed = 0;
+  level->time_magic_wall = 0;
+  level->time_wheel = 0;
+  level->amoeba_content = EL_EMPTY;
+
+#if 1
+  /* original Supaplex does not use score values -- use default values */
+#else
+  for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
+    level->score[i] = 0;
+#endif
+
+  /* there are no yamyams in supaplex levels */
+  for (i = 0; i < level->num_yamyam_contents; i++)
+    for (x = 0; x < 3; x++)
+      for (y = 0; y < 3; y++)
+       level->yamyam_content[i].e[x][y] = EL_EMPTY;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading DC level                                            */
+/* ------------------------------------------------------------------------- */
 
 #define DC_LEVEL_HEADER_SIZE           344
 
@@ -6138,6 +6238,47 @@ static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
 #endif
 
 
+/* ------------------------------------------------------------------------- */
+/* functions for handling native levels                                      */
+/* ------------------------------------------------------------------------- */
+
+static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info)
+{
+  if (!LoadNativeLevel_EM(level_file_info->filename))
+    level->no_valid_file = TRUE;
+}
+
+static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
+                                    struct LevelFileInfo *level_file_info)
+{
+  int pos = 0;
+
+  /* determine position of requested level inside level package */
+  if (level_file_info->packed)
+    pos = level_file_info->nr - leveldir_current->first_level;
+
+  if (!LoadNativeLevel_SP(level_file_info->filename, pos))
+    level->no_valid_file = TRUE;
+}
+
+void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_RND_to_EM(level);
+  else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+    CopyNativeLevel_RND_to_SP(level);
+}
+
+void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
+    CopyNativeLevel_EM_to_RND(level);
+  else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+    CopyNativeLevel_SP_to_RND(level);
+}
+
+
 /* ------------------------------------------------------------------------- */
 /* functions for loading generic level                                       */
 /* ------------------------------------------------------------------------- */
index 24d4c2f65d8c64d02a3e81be762ee76edea5b7b8..9cad09d548a65f105ce0c765b3d27142486c994c 100644 (file)
@@ -129,30 +129,30 @@ void MySub(int *A, int B)
   *A = *A - B;
 }
 
-int SHR(int Var, int Count)
+int SHR(int *Var, int Count)
 {
   int SHR;
 
   int i;
 
-  if (Var & 0x8000)
+  if (*Var & 0x8000)
   {
-    Var = ((Var & 0x7FFF) / 2) | 0x4000;
+    *Var = ((*Var & 0x7FFF) / 2) | 0x4000;
   }
   else
   {
-    Var = Var / 2;
+    *Var = *Var / 2;
   }
 
   for (i = 2; i <= Count; i++)
   {
-    Var = Var / 2;
+    *Var = *Var / 2;
   }
 
   return SHR;
 }
 
-int SHL(int Var, int Count)
+int SHL(int *Var, int Count)
 {
   int SHL;
 
@@ -160,14 +160,14 @@ int SHL(int Var, int Count)
 
   for (i = 1; i <= Count; i++)
   {
-    Var = Var & 0x7FFF;
-    if ((Var & 0x4000) != 0)
+    *Var = *Var & 0x7FFF;
+    if ((*Var & 0x4000) != 0)
     {
-      Var = (2 * (Var & 0x3FFF)) | 0x8000;
+      *Var = (2 * (*Var & 0x3FFF)) | 0x8000;
     }
     else
     {
-      Var = 2 * Var;
+      *Var = 2 * *Var;
     }
   }
 
index 3b2773224be9222daa91e6d3fb34bebd582d97be..09bb3938ba9b3c638a595c758f66fbf41b43c780 100644 (file)
@@ -28,8 +28,8 @@ extern void MovHighByte(int *Var, int Val);
 extern void MovLowByte(int *Var, int Val);
 extern void MySub(int *A, int B);
 extern void Neg(int *Val);
-extern int SHL(int Var, int Count);
-extern int SHR(int Var, int Count);
+extern int SHL(int *Var, int Count);
+extern int SHR(int *Var, int Count);
 extern int SgnHighByte(int Var);
 extern void XCHG(int A, int B);
 
index 9dbe3138901d24bd2ee90e58207ab56f4de9f3b1..e73a7d534505e022f74eeb9490bef89a5caf8df9 100644 (file)
@@ -329,7 +329,7 @@ int GetStretchY(int si)
   return GetStretchY;
 }
 
-void ReadLevel()
+void OLD_ReadLevel()
 {
 #if 1
   static char CurPathTEST[1024];
@@ -580,6 +580,18 @@ static void ReadDemo()
   printf("::: LInfo.DemoRandomSeed == %d\n", LInfo.DemoRandomSeed);
 #endif
 
+#if 0
+  printf("::: LInfo.SpecialPortCount == %d\n", LInfo.SpecialPortCount);
+  for (i = 0; i < LInfo.SpecialPortCount; i++)
+  {
+    int port_x = (LInfo.SpecialPort[i].PortLocation / 2) % FieldWidth;
+    int port_y = (LInfo.SpecialPort[i].PortLocation / 2) / FieldWidth;
+
+    printf("::: %d: port_location == %d => (%d, %d)\n",
+          i, LInfo.SpecialPort[i].PortLocation, port_x, port_y);
+  }
+#endif
+
   RandomSeed = LInfo.DemoRandomSeed;
 
 #if 0
@@ -607,3 +619,21 @@ ReadDemoEH:
   Close();
 #endif
 }
+
+void ReadLevel()
+{
+#if 0
+  OLD_ReadLevel();
+
+  // return;
+#endif
+
+  copyInternalEngineVars_SP();
+
+  LevelNumber = level_nr;
+
+  if (!DemoFlag || !DemoAvailable)
+    subRandomize();
+
+  LevelLoaded = True;
+}
index c3d7476e481fcacab1cd00714922f611e234708b..1358ab0278fe819540ac5c5d8593c1b25f54b691 100644 (file)
@@ -2523,6 +2523,8 @@ void menPlay_Click()
 
 }
 
+#if 0
+
 // static void menPlayDemo_Click()
 void menPlayDemo_Click()
 {
@@ -2535,6 +2537,10 @@ void menPlayDemo_Click()
 
   menPlay_Click();
 
+#if 1
+  return;
+#endif
+
 #if 0
   if (LevelStatus != 1)
     lblStatus = "Demo Failed";
@@ -2543,6 +2549,30 @@ void menPlayDemo_Click()
   DemoFlag = 0;
 }
 
+#else
+
+// static void menPlayDemo_Click()
+void menPlayDemo_Click()
+{
+  DemoFlag = 1;
+  RecordDemoFlag = 0;
+
+#if 0
+  lblStatus = "Demo Playback";
+#endif
+
+  menPlay_Click();
+
+#if 0
+  if (LevelStatus != 1)
+    lblStatus = "Demo Failed";
+#endif
+
+  DemoFlag = 0;
+}
+
+#endif
+
 #if 0
 
 static void menRec_Click()
index ddf3d4e236b9317f80918bb03c2ae2645984a8d1..99c5dbe4496203bdde12c10eac4b96a40707ecaf 100644 (file)
@@ -22,6 +22,243 @@ boolean AutoScrollFlag;
 // Play a game/demo
 // ==========================================================================
 
+int subMainGameLoop_Init()
+{
+  int subMainGameLoop;
+
+  // int al, bx;
+  // int bx;
+#if 0
+  TickCountObject Clock;
+  currency LastFrame;
+#endif
+
+  if (DemoFlag != 0)
+  {
+#if 1
+    printf("::: playing demo ...\n");
+#endif
+
+    // EP set level success byte: demo, not game
+    WasDemoFlag = 1;
+    EP_GameDemoVar0DAA = 0; // demo
+  }
+  else // loc_g_1836:
+  {
+#if 1
+    printf("::: playing game ...\n");
+#endif
+
+    // EP set level success byte: game, not demo
+    WasDemoFlag = 0;
+    EP_GameDemoVar0DAA = 1; // game
+  }
+
+  // RestartGameLoop:
+  //  If RecordDemoFlag = 1 Then
+  //    RecordDemoFlag = 0 ' clear Demo Recording flag
+  //    Call subDisplayPlayingTime                 ' playing time on screen
+  //    ' Record key still pressed?' >= (Ctrl-)F1 and <= (Ctrl-)F10
+  //    While &H3B <= KeyScanCode7 And KeyScanCode7 <= &H44
+  //      ' yes -> wait until released
+  //      ' should we DoEvents here???? ... depends on how ... but yes!
+  //      ' ...or we can rather poll the keyboardstate inside this loop???
+  //    Wend
+  //    Call subInitGameConditions     ' Init game conditions (vars)
+  //    If MusicOnFlag = 0 Then Call subMusicInit
+  //    WasDemoFlag = 0          ' no demo anymore
+  //    EP_GameDemoVar0DAA = 1 ' force game
+  //  End If
+
+  // This was a bug in the original Supaplex: sometimes red disks could not
+  // be released.  This happened If Murphy was killed DURING a red disk release
+  // and the next try started.
+
+  RedDiskReleasePhase = 0; // (re-)enable red disk release
+  UpdatedFlag = 0;
+  GameLoopRunning = 1;
+  LevelStatus = 0;
+
+  return subMainGameLoop;
+}
+
+int subMainGameLoop_Main()
+{
+  int subMainGameLoop;
+  int bx;
+
+  // ----------------------------------------------------------------------------
+  // --------------------- START OF GAME-BUSY LOOP ------------------------------
+  // ----------------------------------------------------------------------------
+
+locRepeatMainGameLoop:                           // start repeating game loop
+
+  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  // FS   synchronization
+  while (PauseMode != 0)
+  {
+    DoEvents();
+  }
+
+  do
+  {
+    DoEvents(); // user may klick on menus or move the window here ...
+  }
+#if 1
+  while (0);
+#else
+  while (Clock.TickDiffUS(LastFrame) < DeltaT); // wait till its time for the next frame
+#endif
+
+  //   never any additional code between here!
+#if 0
+  LastFrame = Clock.TickNow(); // store the frame time
+#endif
+  //   never any additional code between here!
+  if (! NoDisplayFlag) // copy the BackBuffer(=Stage) to visible screen
+    Stage.Blt();
+
+  // FS   end of synchronization
+  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  if (EndFlag)
+    goto locExitMainGameLoop;
+
+  // If DemoFlag = 0 Then Call subCheckJoystick    ' check joystick
+  // bx = subCheckRightMouseButton()                ' check (right) mouse button
+  // If bx = 2 And LeadOutCounter < 1 Then KillMurphyFlag = 1 ' lead-out busy after quit? -> kill Murphy!
+
+  //  If DebugVersionFlag <> 0 Then ' debug mode on?
+  //    If Data_SubRest <> 0 Then Data_SubRest = Data_SubRest - 1
+  //    If keyEnter <> 0 Then GoTo loc_g_186F ' Was it the Enter key? -> yes--skip! No mouse!
+  //                                          ' fixes ENTER bug If no mouse driver!
+  //    If bx <> 1 Then GoTo loc_g_186F  ' Left button=Init game field
+  //                                    ' Also Enter If no mouse!
+  //    If Data_SubRest <> 0 Then GoTo loc_g_186F
+  //    Data_SubRest = 10
+  //    Call subRestoreFancy
+  //    Call subDisplayLevel         ' Paint (Init) game field
+  //    Call subConvertToEasySymbols ' Convert to easy symbols
+  //  End If
+
+  // loc_g_186F:
+
+  subProcessKeyboardInput();                 // Check keyboard, act on keys
+
+  // 'HACK:
+  //  TimerVar = TimerVar + 1
+  //  DoEvents
+  //  GoTo loc_g_186F
+  // 'END HACK
+  // If RecordDemoFlag = 1 Then GoTo RestartGameLoop
+
+  // ----------------------------------------------------------------------------
+  //
+
+#if 0
+  printf("::: >>>>>>>>>> MainGameLoop.c: subDoGameStuff() START\n");
+#endif
+
+  subDoGameStuff();                 // do all game stuff
+
+#if 0
+  printf("::: <<<<<<<<<< MainGameLoop.c: subDoGameStuff() END\n");
+#endif
+
+  //
+  // ----------------------------------------------------------------------------
+
+  //  Call subDisplayPlayingTime                 ' playing time on screen
+
+  subCheckRestoreRedDiskCountDisplay();    // Restore panel: red-disk hole
+
+  subRedDiskReleaseExplosion();       // Red Disk release and explode
+  subFollowUpExplosions();  // every explosion may cause up to 8 following explosions
+
+  bx = subCalculateScreenScrollPos();     // calculate screen start addrs
+
+  ScreenPosition = bx;
+
+  // Now new X and new Y are calculated, and bx = screen position = ScreenPosition
+  data_h_Ytmp = ScreenScrollYPos; // copy Y for next soft scroll
+  data_h_Xtmp = ScreenScrollXPos; // copy X for next soft scroll
+  if ((! UserDragFlag) && AutoScrollFlag)
+  {
+#if 0
+    printf("::: MainGameLoop.c: subMainGameLoop(): %d, %d\n", ScreenScrollXPos, ScreenScrollYPos);
+#endif
+
+    ScrollTowards(ScreenScrollXPos, ScreenScrollYPos);
+  }
+
+  if (ForcedExitFlag != 0) // Forced Exit?' yes--exit!
+    goto locExitMainGameLoop;
+
+  TimerVar = TimerVar + 1;
+
+#if 0
+  if (bCapturePane)
+    MainForm.SaveSnapshot(TimerVar);
+#endif
+
+  //  If Not NoDisplayFlag Then
+  //    With MainForm.lblFrameCount
+  //      .Caption = TimerVar
+  //      .Refresh
+  //    End With
+  //  End If
+  if (ExitToMenuFlag == 1)
+    goto locExitMainGameLoop;
+
+  if (LeadOutCounter == 0) // no lead-out: game busy
+    goto locRepeatMainGameLoop;
+
+  // ----------------------------------------------------------------------------
+  // ---------------------- END OF GAME-BUSY LOOP -------------------------------
+  // ----------------------------------------------------------------------------
+  LeadOutCounter = LeadOutCounter - 1;             // do more lead-out after quit
+  if (LeadOutCounter != 0) // lead-out not ready: more
+    goto locRepeatMainGameLoop;
+
+  // lead-out done: exit now
+  // ---------------------- END OF GAME-BUSY LOOP (including lead-out) ----------
+
+locExitMainGameLoop:
+  do
+  {
+    DoEvents(); // user may klick on menus or move the window here ...
+  }
+#if 1
+  while (0);
+#else
+  while (Clock.TickDiffUS(LastFrame) < DeltaT); // wait till its time for the next frame
+#endif
+
+  Stage.Blt(); // blit the last frame
+  GameLoopRunning = 0;
+
+#if 0
+  MainForm.menStop_Click();
+  MainForm.PanelVisible = True;
+#endif
+
+  // If DemoRecordingFlag <> 0 Then Call subCloseDemoRecordingFile ' Demo recording on? -> close opened demo file (w)
+  if (SavedGameFlag != 0) // after savegame: no update!
+  {
+    SavedGameFlag = 0;
+    return subMainGameLoop;
+  }
+
+  SavedGameFlag = 0;
+  if (UpdateTimeFlag == 0) // update time?
+    return subMainGameLoop;
+
+  if (UpdatedFlag == 0) // update playing time
+    subUpdatePlayingTime();
+
+
+  return subMainGameLoop;
+} // subMainGameLoop
+
 int subMainGameLoop()
 {
   int subMainGameLoop;
index 71fd4301e98867d9e6d9883e754ba3ddff1082d1..a686bcaf61fdedf5ac0364d5da7d1c27c7c17f4d 100644 (file)
@@ -14,6 +14,8 @@
 
 extern int subCalculateScreenScrollPos();
 extern int subMainGameLoop();
+extern int subMainGameLoop_Init();
+extern int subMainGameLoop_Main();
 extern void subUpdatePlayingTime();
 
 extern boolean AutoScrollFlag;
index 97311dc0432fed7c936cbb8ce84554b83a792fcd..13a7a917fbe46f3dac0cb29540fe9a1a93a1cd62 100644 (file)
@@ -13,6 +13,7 @@
 # -----------------------------------------------------------------------------
 
 SRCS = init.c                  \
+       file.c                  \
        main.c                  \
        vb_lib.c                \
        vb_vars.c               \
@@ -57,6 +58,7 @@ SRCS =        init.c                  \
        modMPX.c
 
 OBJS = init.o                  \
+       file.o                  \
        main.o                  \
        vb_lib.o                \
        vb_vars.o               \
index bfe6854a6264ffac33e667d0b9f2da128b2b6e5a..bcd61200f89d6973bab19df1fdf29d10ea7d16fa 100644 (file)
@@ -2333,8 +2333,14 @@ int subSpPortTest(int si)
 
   for (i = 0; i < cx; i++)
   {
+#if 1
+    /* this assumes that PortLocation is stored as big endian */
+    bx = LInfo.SpecialPort[i].PortLocation;
+#else
+    /* this assumes that PortLocation is stored as little endian */
     bx = HighByte(LInfo.SpecialPort[i].PortLocation);
     MovHighByte(&bx, LowByte(LInfo.SpecialPort[i].PortLocation));
+#endif
 
     if (bx / 2 == si)
     {
index 37af76fb867304084c40b71bc43e16655f45a943..ffd787839b2e2b0be04ccd905108ae39e1852dad 100644 (file)
@@ -9,11 +9,71 @@
 /* constant definitions                                                      */
 /* ------------------------------------------------------------------------- */
 
+#define SP_MAX_PLAYFIELD_WIDTH         MAX_PLAYFIELD_WIDTH
+#define SP_MAX_PLAYFIELD_HEIGHT                MAX_PLAYFIELD_HEIGHT
+
+#define SP_NUM_LEVELS_PER_PACKAGE      111
+
+#define SP_PLAYFIELD_WIDTH             60
+#define SP_PLAYFIELD_HEIGHT            24
+#define SP_LEVEL_NAME_LEN              23
+#define SP_MAX_SPECIAL_PORTS           10
+
+#define SP_HEADER_SIZE                 96
+#define SP_PLAYFIELD_SIZE              (SP_PLAYFIELD_WIDTH *           \
+                                        SP_PLAYFIELD_HEIGHT)
+#define SP_LEVEL_SIZE                  (SP_HEADER_SIZE + SP_PLAYFIELD_SIZE)
+
+#define SP_FRAMES_PER_SECOND           35
+#define SP_MAX_TAPE_LEN                        64010   /* (see "spfix63.doc") */
+
 
 /* ------------------------------------------------------------------------- */
 /* data structure definitions                                                */
 /* ------------------------------------------------------------------------- */
 
+#ifndef HAS_SpecialPortType
+typedef struct
+{
+#if 1
+  short PortLocation; // = 2*(x+(y*60))                /* big endian format */
+#else
+  int PortLocation; // = 2*(x+(y*60))
+#endif
+  byte Gravity; // 1 = turn on, anything else (0) = turn off
+  byte FreezeZonks; // 2 = turn on, anything else (0) = turn off  (1=off!)
+  byte FreezeEnemies; // 1 = turn on, anything else (0) = turn off
+  byte UnUsed;
+} SpecialPortType;
+#define HAS_SpecialPortType
+#endif
+
+#ifndef HAS_LevelInfoType
+typedef struct
+{
+  byte UnUsed[4];
+  byte InitialGravity; // 1=on, anything else (0) = off
+  byte Version; // SpeedFixVersion XOR &H20
+  char LevelTitle[23];
+  byte InitialFreezeZonks; // 2=on, anything else (0) = off.  (1=off too!)
+  byte InfotronsNeeded;
+
+  // Number of Infotrons needed. 0 means that Supaplex will count the total
+  // amount of Infotrons in the level, and use the low byte of that number.
+  // (A multiple of 256 Infotrons will then result in 0-to-eat, etc.!)
+  byte SpecialPortCount; // Maximum 10 allowed!
+  SpecialPortType SpecialPort[10];
+  byte SpeedByte; // = Speed XOR Highbyte(RandomSeed)
+  byte CheckSumByte; // = CheckSum XOR SpeedByte
+#if 1
+  short DemoRandomSeed;                                /* little endian format */
+#else
+  int DemoRandomSeed;
+#endif
+} LevelInfoType;
+#define HAS_LevelInfoType
+#endif
+
 struct GlobalInfo_SP
 {
 };
@@ -24,7 +84,16 @@ struct GameInfo_SP
 
 struct LevelInfo_SP
 {
-  int file_version;
+  LevelInfoType header;
+
+  byte playfield[SP_MAX_PLAYFIELD_WIDTH][SP_MAX_PLAYFIELD_HEIGHT];
+
+  int width, height;
+
+  boolean demo_available;
+
+  byte demo[SP_MAX_TAPE_LEN];
+  int demo_length;
 };
 
 struct GraphicInfo_SP
@@ -70,7 +139,8 @@ extern void GameActions_SP(byte *, boolean);
 extern unsigned int InitEngineRandom_SP(long);
 
 extern void setLevelInfoToDefaults_SP();
-extern boolean LoadNativeLevel_SP(char *);
+extern void copyInternalEngineVars_SP();
+extern boolean LoadNativeLevel_SP(char *, int);
 
 extern void BackToFront_SP(void);
 extern void BlitScreenToBitmap_SP(Bitmap *);
diff --git a/src/game_sp/file.c b/src/game_sp/file.c
new file mode 100644 (file)
index 0000000..cd183a2
--- /dev/null
@@ -0,0 +1,405 @@
+
+#include "main_sp.h"
+#include "global.h"
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading Supaplex level                                      */
+/* ------------------------------------------------------------------------- */
+
+void setLevelInfoToDefaults_SP()
+{
+  LevelInfoType *header = &native_sp_level.header;
+  char *empty_title = "-------- EMPTY --------";
+  int i, x, y;
+
+  native_sp_level.width  = SP_PLAYFIELD_WIDTH;
+  native_sp_level.height = SP_PLAYFIELD_HEIGHT;
+
+  for (x = 0; x < native_sp_level.width; x++)
+    for (y = 0; y < native_sp_level.height; y++)
+      native_sp_level.playfield[x][y] = fiSpace;
+
+  /* copy string (without terminating '\0' character!) */
+  for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+    header->LevelTitle[i] = empty_title[i];
+
+  header->InitialGravity = 0;
+  header->Version = 0;
+  header->InitialFreezeZonks = 0;
+  header->InfotronsNeeded = 0;
+  header->SpecialPortCount = 0;
+  header->SpeedByte = 0;
+  header->CheckSumByte = 0;
+  header->DemoRandomSeed = 0;
+
+  for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
+  {
+    SpecialPortType *port = &header->SpecialPort[i];
+
+    port->PortLocation = 0;
+    port->Gravity = 0;
+    port->FreezeZonks = 0;
+    port->FreezeEnemies = 0;
+  }
+
+  native_sp_level.demo_available = FALSE;
+  native_sp_level.demo_length = 0;
+}
+
+void copyInternalEngineVars_SP()
+{
+  int i, x, y;
+
+  LInfo = native_sp_level.header;
+
+  FieldWidth  = native_sp_level.width;
+  FieldHeight = native_sp_level.height;
+  HeaderSize = 96;
+
+  FieldMax = (FieldWidth * FieldHeight) + HeaderSize - 1;
+  LevelMax = (FieldWidth * FieldHeight) - 1;
+
+  FileMax = FieldMax + native_sp_level.demo_length;
+
+  PlayField8 = REDIM_1D(sizeof(byte), 0, FileMax + 1 - 1);
+  DisPlayField = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
+  PlayField16 = REDIM_1D(sizeof(int), -FieldWidth, FieldMax);
+
+  for (i = 0, y = 0; y < native_sp_level.height; y++)
+  {
+    for (x = 0; x < native_sp_level.width; x++)
+    {
+      PlayField8[i] = native_sp_level.playfield[x][y];
+
+      PlayField16[i] = PlayField8[i];
+      DisPlayField[i] = PlayField8[i];
+      PlayField8[i] = 0;
+
+      i++;
+    }
+  }
+
+  if (native_sp_level.demo_available)
+  {
+    DemoAvailable = True;
+
+    for (i = 0; i < native_sp_level.demo_length; i++)
+      PlayField8[FieldMax + i + 1] = native_sp_level.demo[i];
+  }
+
+  AnimationPosTable = REDIM_1D(sizeof(int), 0, LevelMax - 2 * FieldWidth);
+  AnimationSubTable = REDIM_1D(sizeof(byte), 0, LevelMax - 2 * FieldWidth);
+  TerminalState = REDIM_1D(sizeof(byte), 0, FieldMax + 1 - 1);
+
+  DemoPointer = FieldMax + 1;
+  DemoOffset = DemoPointer;
+  DemoKeyRepeatCounter = 0;
+
+  GravityFlag = LInfo.InitialGravity;
+  FreezeZonks = LInfo.InitialFreezeZonks;
+
+  RandomSeed = LInfo.DemoRandomSeed;
+
+  LevelLoaded = True;
+}
+
+static void LoadNativeLevelFromFileStream_SP(FILE *file, boolean demo_available)
+{
+  LevelInfoType *header = &native_sp_level.header;
+  int i, x, y;
+
+  /* for details of the Supaplex level format, see Herman Perk's Supaplex
+     documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
+
+  native_sp_level.width  = SP_PLAYFIELD_WIDTH;
+  native_sp_level.height = SP_PLAYFIELD_HEIGHT;
+
+  /* read level playfield (width * height == 60 * 24 tiles == 1440 bytes) */
+  for (y = 0; y < native_sp_level.height; y++)
+    for (x = 0; x < native_sp_level.width; x++)
+      native_sp_level.playfield[x][y] = getFile8Bit(file);
+
+  /* read level header (96 bytes) */
+
+  ReadUnusedBytesFromFile(file, 4);    /* (not used by Supaplex engine) */
+
+  /* initial gravity: 1 == "on", anything else (0) == "off" */
+  header->InitialGravity = getFile8Bit(file);
+
+  /* SpeedFixVersion XOR 0x20 */
+  header->Version = getFile8Bit(file);
+
+  /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
+  for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
+    header->LevelTitle[i] = getFile8Bit(file);
+
+  /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
+  header->InitialFreezeZonks = getFile8Bit(file);
+
+  /* number of infotrons needed; 0 means that Supaplex will count the total
+     amount of infotrons in the level and use the low byte of that number
+     (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
+  header->InfotronsNeeded = getFile8Bit(file);
+
+  /* number of special ("gravity") port entries below (maximum 10 allowed) */
+  header->SpecialPortCount = getFile8Bit(file);
+
+#if 0
+  printf("::: num_special_ports == %d\n", header->SpecialPortCount);
+#endif
+
+  /* database of properties of up to 10 special ports (6 bytes per port) */
+  for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++)
+  {
+    SpecialPortType *port = &header->SpecialPort[i];
+
+    /* high and low byte of the location of a special port; if (x, y) are the
+       coordinates of a port in the field and (0, 0) is the top-left corner,
+       the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
+       of what may be expected: Supaplex works with a game field in memory
+       which is 2 bytes per tile) */
+    port->PortLocation = getFile16BitBE(file);
+
+#if 0
+    {
+      int port_x = (port->PortLocation / 2) % SP_PLAYFIELD_WIDTH;
+      int port_y = (port->PortLocation / 2) / SP_PLAYFIELD_WIDTH;
+
+      printf("::: %d: port_location == %d => (%d, %d)\n",
+            i, port->PortLocation, port_x, port_y);
+    }
+#endif
+
+    /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
+    port->Gravity = getFile8Bit(file);
+
+    /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
+    port->FreezeZonks = getFile8Bit(file);
+
+    /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
+    port->FreezeEnemies = getFile8Bit(file);
+
+    ReadUnusedBytesFromFile(file, 1);  /* (not used by Supaplex engine) */
+  }
+
+  /* SpeedByte XOR Highbyte(RandomSeed) */
+  header->SpeedByte = getFile8Bit(file);
+
+  /* CheckSum XOR SpeedByte */
+  header->CheckSumByte = getFile8Bit(file);
+
+  /* random seed used for recorded demos */
+  header->DemoRandomSeed = getFile16BitLE(file);
+
+#if 1
+  printf("::: file.c: DemoRandomSeed == %d\n", header->DemoRandomSeed);
+#endif
+
+  /* auto-determine number of infotrons if it was stored as "0" -- see above */
+  if (header->InfotronsNeeded == 0)
+  {
+    for (x = 0; x < native_sp_level.width; x++)
+      for (y = 0; y < native_sp_level.height; y++)
+       if (native_sp_level.playfield[x][y] == fiInfotron)
+         header->InfotronsNeeded++;
+
+    header->InfotronsNeeded &= 0xff;   /* only use low byte -- see above */
+  }
+
+  /* also load demo tape, if available */
+
+  if (demo_available)
+  {
+    for (i = 0; i < SP_MAX_TAPE_LEN && !feof(file); i++)
+    {
+      native_sp_level.demo[i] = getFile8Bit(file);
+
+      if (native_sp_level.demo[i] == 0xff)     /* "end of demo" byte */
+      {
+       i++;
+
+       break;
+      }
+    }
+
+    native_sp_level.demo_length = i;
+    native_sp_level.demo_available = (native_sp_level.demo_length > 0);
+  }
+}
+
+boolean LoadNativeLevel_SP(char *filename, int pos)
+{
+  FILE *file;
+  int i, l, x, y;
+  char name_first, name_last;
+  struct LevelInfo_SP multipart_level;
+  int multipart_xpos, multipart_ypos;
+  boolean is_multipart_level;
+  boolean is_first_part;
+  boolean reading_multipart_level = FALSE;
+  boolean use_empty_level = FALSE;
+  LevelInfoType *header = &native_sp_level.header;
+  boolean demo_available = (strSuffix(filename, ".sp") ||
+                           strSuffix(filename, ".SP"));
+
+  /* always start with reliable default values */
+  setLevelInfoToDefaults_SP();
+  copyInternalEngineVars_SP();
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    Error(ERR_WARN, "cannot open level '%s' -- using empty level", filename);
+
+    return FALSE;
+  }
+
+  /* position file stream to the requested level (in case of level package) */
+  if (fseek(file, pos * SP_LEVEL_SIZE, SEEK_SET) != 0)
+  {
+    Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level", filename);
+
+    return FALSE;
+  }
+
+  /* there exist Supaplex level package files with multi-part levels which
+     can be detected as follows: instead of leading and trailing dashes ('-')
+     to pad the level name, they have leading and trailing numbers which are
+     the x and y coordinations of the current part of the multi-part level;
+     if there are '?' characters instead of numbers on the left or right side
+     of the level name, the multi-part level consists of only horizontal or
+     vertical parts */
+
+  for (l = pos; l < SP_NUM_LEVELS_PER_PACKAGE; l++)
+  {
+    LoadNativeLevelFromFileStream_SP(file, demo_available);
+
+    /* check if this level is a part of a bigger multi-part level */
+
+    name_first = header->LevelTitle[0];
+    name_last  = header->LevelTitle[SP_LEVEL_NAME_LEN - 1];
+
+    is_multipart_level =
+      ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
+       (name_last  == '?' || (name_last  >= '0' && name_last  <= '9')));
+
+    is_first_part =
+      ((name_first == '?' || name_first == '1') &&
+       (name_last  == '?' || name_last  == '1'));
+
+    /* correct leading multipart level meta information in level name */
+    for (i = 0; i < SP_LEVEL_NAME_LEN && header->LevelTitle[i] == name_first; i++)
+      header->LevelTitle[i] = '-';
+
+    /* correct trailing multipart level meta information in level name */
+    for (i = SP_LEVEL_NAME_LEN - 1; i >= 0 && header->LevelTitle[i] == name_last; i--)
+      header->LevelTitle[i] = '-';
+
+    /* ---------- check for normal single level ---------- */
+
+    if (!reading_multipart_level && !is_multipart_level)
+    {
+      /* the current level is simply a normal single-part level, and we are
+        not reading a multi-part level yet, so return the level as it is */
+
+      break;
+    }
+
+    /* ---------- check for empty level (unused multi-part) ---------- */
+
+    if (!reading_multipart_level && is_multipart_level && !is_first_part)
+    {
+      /* this is a part of a multi-part level, but not the first part
+        (and we are not already reading parts of a multi-part level);
+        in this case, use an empty level instead of the single part */
+
+      use_empty_level = TRUE;
+
+      break;
+    }
+
+    /* ---------- check for finished multi-part level ---------- */
+
+    if (reading_multipart_level &&
+       (!is_multipart_level ||
+        !strEqualN(header->LevelTitle, multipart_level.header.LevelTitle,
+                   SP_LEVEL_NAME_LEN)))
+    {
+      /* we are already reading parts of a multi-part level, but this level is
+        either not a multi-part level, or a part of a different multi-part
+        level; in both cases, the multi-part level seems to be complete */
+
+      break;
+    }
+
+    /* ---------- here we have one part of a multi-part level ---------- */
+
+    reading_multipart_level = TRUE;
+
+    if (is_first_part) /* start with first part of new multi-part level */
+    {
+      /* copy level info structure from first part */
+      multipart_level = native_sp_level;
+
+      /* clear playfield of new multi-part level */
+      for (x = 0; x < SP_MAX_PLAYFIELD_WIDTH; x++)
+       for (y = 0; y < SP_MAX_PLAYFIELD_HEIGHT; y++)
+         multipart_level.playfield[x][y] = fiSpace;
+    }
+
+    if (name_first == '?')
+      name_first = '1';
+    if (name_last == '?')
+      name_last = '1';
+
+    multipart_xpos = (int)(name_first - '0');
+    multipart_ypos = (int)(name_last  - '0');
+
+#if 0
+    printf("----------> part (%d/%d) of multi-part level '%s'\n",
+          multipart_xpos, multipart_ypos, multipart_level.header.LevelTitle);
+#endif
+
+    if (multipart_xpos * SP_PLAYFIELD_WIDTH  > SP_MAX_PLAYFIELD_WIDTH ||
+       multipart_ypos * SP_PLAYFIELD_HEIGHT > SP_MAX_PLAYFIELD_HEIGHT)
+    {
+      Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
+
+      break;
+    }
+
+    multipart_level.width  = MAX(multipart_level.width,
+                                multipart_xpos * SP_PLAYFIELD_WIDTH);
+    multipart_level.height = MAX(multipart_level.height,
+                                multipart_ypos * SP_PLAYFIELD_HEIGHT);
+
+    /* copy level part at the right position of multi-part level */
+    for (x = 0; x < SP_PLAYFIELD_WIDTH; x++)
+    {
+      for (y = 0; y < SP_PLAYFIELD_HEIGHT; y++)
+      {
+       int start_x = (multipart_xpos - 1) * SP_PLAYFIELD_WIDTH;
+       int start_y = (multipart_ypos - 1) * SP_PLAYFIELD_HEIGHT;
+
+       multipart_level.playfield[start_x + x][start_y + y] =
+         native_sp_level.playfield[x][y];
+      }
+    }
+  }
+
+  fclose(file);
+
+  if (use_empty_level)
+  {
+    setLevelInfoToDefaults_SP();
+
+    Error(ERR_WARN, "single part of multi-part level -- using empty level");
+  }
+
+  if (reading_multipart_level)
+    native_sp_level = multipart_level;
+
+  copyInternalEngineVars_SP();
+
+  return TRUE;
+}
index ce409eb4551e3fe882a64f3af7ba37adbb003573..702cb7d36e0367a7752df1e4b56cf5b4b3c304c7 100644 (file)
@@ -3,6 +3,8 @@
 #include "global.h"
 
 
+struct LevelInfo_SP native_sp_level;
+
 void InitGameEngine_SP()
 {
 #if 0
index 4b42b65aa007f3fb3b88daa20f97c02844a53466..3f9a1e882e865a3a9a779addc92b52986b37aac8 100644 (file)
@@ -70,5 +70,9 @@ void GoPlay()
   //  Call subFetchAndInitLevelB
   EndFlag = False;
 
+#if 0
+  subMainGameLoop_Init();
+#else
   subMainGameLoop();
+#endif
 }
index 38a5bcbf7900b82cc6f01578cf648f97b84a6b05..2614bbb6a60d229f58394c5dc79a7dae1557eb83 100644 (file)
@@ -2293,6 +2293,7 @@ struct LevelInfo
 
   /* level stored in native format for the alternative native game engines */
   struct LevelInfo_EM *native_em_level;
+  struct LevelInfo_SP *native_sp_level;
 
   int file_version;    /* file format version the level is stored with    */
   int game_version;    /* game release version the level was created with */