changed how the native BD game engine handles scanned game elements
authorHolger Schemel <holger.schemel@virtion.de>
Mon, 2 Sep 2024 23:54:30 +0000 (01:54 +0200)
committerHolger Schemel <holger.schemel@virtion.de>
Tue, 3 Sep 2024 00:11:43 +0000 (02:11 +0200)
After scanning (processing) game elements on the playfield using the
native BD game engine, game elements are marked as "scanned" to
prevent processing them again (for example, newly created elements in
the next row that are just about to be scanned). Previously, elements
were marked by setting a "scanned" bit. Now, elements are marked by
using special "scanned" variants for the already processed element.

There is another change that breaks old cave replays (BD style tapes):
When checking game element properties, the old engine always checked
the non-scanned elements, while the new engine checks the element just
as it is (that is, either scanned or non-scanned), which causes some
game elements (like the amoeba) to behave slightly differently.

This is handled by a special flag to choose the old or new behaviour,
which is currently still set to use the old behaviour.

src/game_bd/bd_cave.c
src/game_bd/bd_caveengine.c
src/game_bd/bd_gameplay.c

index d062b3045ca73584db8bbe44783c740b3f4a0391..b56e2ac780a371b7e1eb09030ce71d88ccd7459b 100644 (file)
@@ -1186,7 +1186,7 @@ static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
   {
     for (x = 0; x < cave->w; x++)
     {
-      switch (cave->map[y][x] & ~SCANNED)
+      switch (non_scanned_pair(cave->map[y][x]))
       {
        case O_FIREFLY_1:
        case O_FIREFLY_2:
index f73da218ed422b9f0f4190c6cfd4ac9bf01af252..ab9b3d6107a7d88765b9e7f0163581d2d1340046 100644 (file)
@@ -33,6 +33,9 @@
 #include "main_bd.h"
 
 
+// for compatibility with old game engine
+static boolean use_old_game_engine = TRUE;
+
 // for gravity
 static const GdDirection ccw_eighth[] =
 {
@@ -127,8 +130,11 @@ void gd_cave_set_seconds_sound(GdCave *cave)
 }
 
 // returns true if the element has a certain property
-static inline boolean has_property(const int element, const int property)
+static inline boolean has_property(int element, const int property)
 {
+  if (use_old_game_engine)
+    element = non_scanned_pair(element);
+
   return (gd_element_properties[element & O_MASK].properties & property) != 0;
 }
 
@@ -546,10 +552,15 @@ GdElement non_scanned_pair(GdElement of_what)
   return gd_element_properties[of_what].pair;
 }
 
+static inline boolean is_scanned(const GdCave *cave, const int x, const int y)
+{
+  return is_scanned_element(get(cave, x, y));
+}
+
 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
                                     const GdDirection dir)
 {
-  return (get_dir(cave, x, y, dir) & SCANNED) != 0;
+  return is_scanned_element(get_dir(cave, x, y, dir));
 }
 
 // returns true if neighbouring element is "e"
@@ -623,13 +634,13 @@ static inline void store(GdCave *cave, const int x, const int y, const GdElement
     return;
   }
 
-  *e = element;
+  *e = scanned_pair(element);
 }
 
 // store an element with SCANNED flag turned on
 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
 {
-  store(cave, x, y, element | SCANNED);
+  store(cave, x, y, scanned_pair(element));
 }
 
 // store an element to a neighbouring cell
@@ -637,7 +648,7 @@ static inline void store_dir(GdCave *cave, const int x, const int y,
                             const GdDirection dir, const GdElement element)
 {
   store_dir_buffer(cave, x, y, dir);
-  store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
+  store(cave, x + gd_dx[dir], y + gd_dy[dir], scanned_pair(element));
 }
 
 // store an element to a neighbouring cell
@@ -676,6 +687,16 @@ static inline void next(GdCave *cave, const int x, const int y)
   (*getp(cave, x, y))++;
 }
 
+// Remove the scanned "bit" from an element.
+// To be called only for scanned elements!!!
+static inline void unscan(GdCave *cave, const int x, const int y)
+{
+  GdElement *e = getp(cave, x, y);
+
+  if (is_scanned_element(*e))
+    *e = gd_element_properties[*e].pair;
+}
+
 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
 {
   if (non_explodable (cave, x, y))
@@ -1760,9 +1781,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
     {
       // if we find a scanned element, change it to the normal one, and that's all.
       // this is required, for example for chasing stones, which have moved, always passing slime!
-      if (get(cave, x, y) & SCANNED)
+      if (is_scanned(cave, x, y))
       {
-       store(cave, x, y, get(cave, x, y) & ~SCANNED);
+        unscan(cave, x, y);
 
        continue;
       }
@@ -3625,6 +3646,12 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
          // other inanimate elements that do nothing
          break;
       }
+
+      // after processing, check the current coordinate, if it became scanned.
+      // the scanned bit can be cleared, as it will not be processed again.
+      // and, it must be cleared, as it should not be scanned; for example,
+      // if it is, a replicator will not replicate it!
+      unscan(cave, x, y);
     }
   }
 
@@ -3640,8 +3667,7 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
   {
     for (x = 0; x < cave->w; x++)
     {
-      if (get(cave, x, y) & SCANNED)
-       store(cave, x, y, get(cave, x, y) & ~SCANNED);
+      unscan(cave, x, y);
 
       if (get(cave, x, y) == O_TIME_PENALTY)
       {
@@ -3668,7 +3694,7 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
          next(cave, x, y);
 
          // forget scanned flag immediately
-         store(cave, x, y, get(cave, x, y) & ~SCANNED);
+          unscan(cave, x, y);
        }
       }
     }
index 1fb424d947fe6ae8f37c8b387a9a263382dcb662..629e03620da17c6fcd295f93cdff9aecef01a3a2 100644 (file)
@@ -140,9 +140,6 @@ static void load_cave(GdGame *game)
 
   game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
 
-  // temporary workaround: set all elements in a cave to their non-scanned counterparts
-  unscan_cave(game->cave);
-
   // if requested, recolor cave (cave is a copy only, so no worries)
   if (setup.bd_random_colors)
     gd_cave_set_random_colors(game->cave, setup.bd_default_color_type);