cleanup of unnecessarily convoluted function call master-next-major-release 4.4.0.0-test-2
authorHolger Schemel <holger.schemel@virtion.de>
Tue, 18 Jun 2024 21:40:05 +0000 (23:40 +0200)
committerHolger Schemel <holger.schemel@virtion.de>
Tue, 18 Jun 2024 21:40:49 +0000 (23:40 +0200)
35 files changed:
build-projects/android/app/src/main/AndroidManifest.xml.tmpl
build-projects/android/app/src/main/java/org/artsoft/rocksndiamonds/rocksndiamonds.java
src/Android.mk
src/conf_gfx.c
src/conf_snd.c
src/editor.c
src/editor.h
src/events.c
src/files.c
src/game.c
src/game_bd/bd_cave.h
src/game_bd/bd_cavedb.c
src/game_bd/bd_caveengine.c
src/game_bd/bd_elements.h
src/game_bd/bd_gameplay.c
src/game_bd/bd_gameplay.h
src/game_bd/bd_graphics.c
src/game_bd/bd_graphics.h
src/game_bd/export_bd.h
src/game_bd/import_bd.h
src/game_bd/main_bd.c
src/init.c
src/libgame/misc.c
src/libgame/sdl.c
src/libgame/sdl.h
src/libgame/setup.c
src/libgame/setup.h
src/libgame/system.c
src/libgame/system.h
src/libgame/text.c
src/libgame/zip/ioapi.c
src/main.c
src/main.h
src/screens.c
src/tools.c

index 8331804d0c5a7ddd0730fed9e61a7c2f44832cf4..9a90444fc4247db3d8bd793a5a4d5717ab885f78 100644 (file)
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="application/zip" />
+            </intent-filter>
         </activity>
     </application>
 
index eeec2ea4a4bfe833e180bc2f8c4525200370c632..ee7aed022906797b6d2b2a04a4107a5dfe8724fd 100644 (file)
@@ -3,4 +3,118 @@ package org.artsoft.rocksndiamonds;
 
 import org.libsdl.app.SDLActivity;
 
-public class rocksndiamonds extends SDLActivity { }
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
+import android.util.Log;
+
+public class rocksndiamonds extends SDLActivity {
+    private static final String TAG = "RND";
+    private String[] args;
+
+    @Override
+    protected String[] getArguments() {
+        return args;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.d(TAG, "onCreate");
+
+        // init program arguments
+        args = new String[0];
+
+        // prevent SDL from sending "drop file" event on app start; use program arguments instead
+        Intent intent = getIntent();
+        handleIntent(intent, true);
+        intent.setData(null);
+
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        Log.d(TAG, "onNewIntent");
+
+        // handle file opened with "open with" when app is already running
+        handleIntent(intent, false);
+    }
+
+    private void handleIntent(Intent intent, boolean onCreate) {
+        Log.d(TAG, "handleIntent");
+
+        Uri uri = intent.getData();
+        if (uri == null) {
+            Log.d(TAG, "handleIntent: uri == null");
+            return;
+        }
+
+        if (onCreate) {
+            Log.d(TAG, "handleIntent: starting app with file as argument");
+
+            // app not running yet -- starting app with "--drop-file" argument
+            setProgramArgs(uri);
+        } else {
+            Log.d(TAG, "handleIntent: sending drop event to running app");
+
+            // app already running -- sending file as a "drop file" event
+            sendUriAsDroppedFile(uri);
+        }
+    }
+
+    public void sendUriAsDroppedFile(Uri uri) {
+        SDLActivity.onNativeDropFile(getFileDescriptorStringFromUri(uri));
+    }
+
+    private int getFileDescriptorFromUri(Uri uri) {
+        int fd = -1;
+
+        try {
+            ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r");
+            if (pfd == null) {
+                throw new RuntimeException("pfd is null");
+            }
+
+            fd = pfd.dup().detachFd();
+            pfd.close();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to convert URI " + uri.toString() + " to file descriptor", e);
+        }
+
+        return fd;
+    }
+
+    private String getFileDescriptorStringFromUri(Uri uri) {
+        return "fd:" + getFileDescriptorFromUri(uri);
+    }
+
+    private void setProgramArgs(Uri uri) {
+        Log.d(TAG, "setProgramArgs");
+
+        // log some file details
+        Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
+        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
+        returnCursor.moveToFirst();
+        Log.e(TAG, "setProgramArgs: file name: " + returnCursor.getString(nameIndex));
+        Log.e(TAG, "setProgramArgs: file size: " + Long.toString(returnCursor.getLong(sizeIndex)));
+
+        String scheme = uri.getScheme();
+        if (scheme != null) {
+            if (scheme.equals("content")) {
+                // convert URI to file descriptor
+                String fd = getFileDescriptorStringFromUri(uri);
+                Log.e(TAG, "setProgramArgs: setting argument to file descriptor: " + fd);
+                args = new String[]{ "--drop-file", fd };
+            } else if (scheme.equals("file")) {
+                // directly use regular file
+                String path = uri.getPath();
+                Log.e(TAG, "setProgramArgs: setting argument to file path: " + path);
+                args = new String[]{ "--drop-file", path };
+            }
+        }
+    }
+}
index a644d93a1b7c85d7ff8b93584a5386b81c326217..f076793a42f80251f41ace9db19f92951220f35c 100644 (file)
@@ -1,7 +1,7 @@
 # =============================================================================
 # Rocks'n'Diamonds - McDuffin Strikes Back!
 # -----------------------------------------------------------------------------
-# (c) 1995-2014 by Artsoft Entertainment
+# (c) 1995-2024 by Artsoft Entertainment
 #                  Holger Schemel
 #                  info@artsoft.org
 #                  https://www.artsoft.org/
@@ -46,6 +46,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
        libgame/image.c                 \
        libgame/random.c                \
        libgame/hash.c                  \
+       libgame/list.c                  \
        libgame/http.c                  \
        libgame/base64.c                \
        libgame/setup.c                 \
@@ -55,6 +56,19 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
        libgame/zip/iowin32.c           \
        libgame/zip/unzip.c             \
        libgame/zip/miniunz.c           \
+       game_bd/main_bd.c               \
+       game_bd/bd_cave.c               \
+       game_bd/bd_cavedb.c             \
+       game_bd/bd_caveengine.c         \
+       game_bd/bd_caveobject.c         \
+       game_bd/bd_bdcff.c              \
+       game_bd/bd_caveset.c            \
+       game_bd/bd_c64import.c          \
+       game_bd/bd_gameplay.c           \
+       game_bd/bd_graphics.c           \
+       game_bd/bd_colors.c             \
+       game_bd/bd_random.c             \
+       game_bd/bd_sound.c              \
        game_em/cave.c                  \
        game_em/convert.c               \
        game_em/graphics.c              \
index 7262ac97ca94a3cdcd916cbb925593b6eacb42ce..2981a5234099a08765c34e046d3cdd2b24d21be4 100644 (file)
@@ -331,6 +331,9 @@ struct ConfigInfo image_config[] =
   { "[bdx_default].growing.delay",                     "4"                             },
   { "[bdx_default].growing.anim_mode",                 "linear"                        },
 
+  { "bdx_empty_space",                                 UNDEFINED_FILENAME              },
+  { "bdx_empty_space.clone_from",                      "empty_space"                   },
+
   { "bdx_player",                                      UNDEFINED_FILENAME              },
   { "bdx_player.clone_from",                           "player_1"                      },
   { "bdx_player.down",                                 UNDEFINED_FILENAME              },
@@ -695,10 +698,10 @@ struct ConfigInfo image_config[] =
   { "bdx_steelwall_sloped_down_right.frames",          "1"                             },
 
   { "bdx_steelwall_explodable",                                UNDEFINED_FILENAME              },
-  { "bdx_steelwall_explodable.clone_from",             "bd_steelwall"                  },
+  { "bdx_steelwall_explodable.clone_from",             "bdx_steelwall"                 },
 
   { "bdx_steelwall_diggable",                          UNDEFINED_FILENAME              },
-  { "bdx_steelwall_diggable.clone_from",               "bd_steelwall"                  },
+  { "bdx_steelwall_diggable.clone_from",               "bdx_steelwall"                 },
   { "bdx_steelwall_diggable.EDITOR",                   "RocksBD2.png"                  },
   { "bdx_steelwall_diggable.EDITOR.xpos",              "6"                             },
   { "bdx_steelwall_diggable.EDITOR.ypos",              "0"                             },
@@ -722,19 +725,19 @@ struct ConfigInfo image_config[] =
   { "bdx_expandable_wall_any.EDITOR.ypos",             "1"                             },
 
   { "bdx_expandable_steelwall_horizontal",             UNDEFINED_FILENAME              },
-  { "bdx_expandable_steelwall_horizontal.clone_from",  "bd_steelwall"                  },
+  { "bdx_expandable_steelwall_horizontal.clone_from",  "bdx_steelwall"                 },
   { "bdx_expandable_steelwall_horizontal.EDITOR",      "RocksBD2.png"                  },
   { "bdx_expandable_steelwall_horizontal.EDITOR.xpos", "0"                             },
   { "bdx_expandable_steelwall_horizontal.EDITOR.ypos", "1"                             },
 
   { "bdx_expandable_steelwall_vertical",               UNDEFINED_FILENAME              },
-  { "bdx_expandable_steelwall_vertical.clone_from",    "bd_steelwall"                  },
+  { "bdx_expandable_steelwall_vertical.clone_from",    "bdx_steelwall"                 },
   { "bdx_expandable_steelwall_vertical.EDITOR",                "RocksBD2.png"                  },
   { "bdx_expandable_steelwall_vertical.EDITOR.xpos",   "1"                             },
   { "bdx_expandable_steelwall_vertical.EDITOR.ypos",   "1"                             },
 
   { "bdx_expandable_steelwall_any",                    UNDEFINED_FILENAME              },
-  { "bdx_expandable_steelwall_any.clone_from",         "bd_steelwall"                  },
+  { "bdx_expandable_steelwall_any.clone_from",         "bdx_steelwall"                 },
   { "bdx_expandable_steelwall_any.EDITOR",             "RocksBD2.png"                  },
   { "bdx_expandable_steelwall_any.EDITOR.xpos",                "2"                             },
   { "bdx_expandable_steelwall_any.EDITOR.ypos",                "1"                             },
@@ -782,13 +785,13 @@ struct ConfigInfo image_config[] =
   { "bdx_exit_open.EDITOR.ypos",                       "0"                             },
 
   { "bdx_invisible_exit_closed",                       UNDEFINED_FILENAME              },
-  { "bdx_invisible_exit_closed.clone_from",            "bd_steelwall"                  },
+  { "bdx_invisible_exit_closed.clone_from",            "bdx_steelwall"                 },
   { "bdx_invisible_exit_closed.EDITOR",                        "RocksBD2.png"                  },
   { "bdx_invisible_exit_closed.EDITOR.xpos",           "4"                             },
   { "bdx_invisible_exit_closed.EDITOR.ypos",           "0"                             },
 
   { "bdx_invisible_exit_open",                         UNDEFINED_FILENAME              },
-  { "bdx_invisible_exit_open.clone_from",              "bd_steelwall"                  },
+  { "bdx_invisible_exit_open.clone_from",              "bdx_steelwall"                 },
   { "bdx_invisible_exit_open.EDITOR",                  "RocksBD2.png"                  },
   { "bdx_invisible_exit_open.EDITOR.xpos",             "2"                             },
   { "bdx_invisible_exit_open.EDITOR.ypos",             "0"                             },
@@ -1363,6 +1366,9 @@ struct ConfigInfo image_config[] =
   { "[sp_default].exploding.delay",                    "4"                             },
   { "[sp_default].exploding.anim_mode",                        "linear"                        },
 
+  { "sp_empty_space",                                  UNDEFINED_FILENAME              },
+  { "sp_empty_space.clone_from",                       "empty_space"                   },
+
   { "sp_zonk",                                         "RocksSP.png"                   },
   { "sp_zonk.xpos",                                    "1"                             },
   { "sp_zonk.ypos",                                    "0"                             },
@@ -5514,6 +5520,9 @@ struct ConfigInfo image_config[] =
   { "emc_dripper.active.ypos",                         "8"                             },
   { "emc_dripper.active.frames",                       "1"                             },
 
+  { "mm_empty_space",                                  UNDEFINED_FILENAME              },
+  { "mm_empty_space.clone_from",                       "empty_space"                   },
+
   { "mm_mcduffin",                                     "RocksMM.png"                   },
   { "mm_mcduffin.xpos",                                        "4"                             },
   { "mm_mcduffin.ypos",                                        "1"                             },
@@ -6297,6 +6306,9 @@ struct ConfigInfo image_config[] =
   { "[mm_default].exploding.delay",                    "2"                             },
   { "[mm_default].exploding.anim_mode",                        "linear"                        },
 
+  { "df_empty_space",                                  UNDEFINED_FILENAME              },
+  { "df_empty_space.clone_from",                       "empty_space"                   },
+
   { "df_laser",                                                "RocksDF.png"                   },
   { "df_laser.xpos",                                   "0"                             },
   { "df_laser.ypos",                                   "9"                             },
index 3b38b50d499c1f5c90cab10b98ce52fa415bc061..103058e4f7b4c51b257fcdb02a3d331574aeeacb 100644 (file)
@@ -64,30 +64,30 @@ struct ConfigInfo sound_config[] =
   { "bd_firefly.waiting",                      "roehr.wav"                     },
 
   // sounds for Boulder Dash style elements and actions (native game engine)
-  { "bdx_sand_ball.falling",                   UNDEFINED_FILENAME              },
+  { "bdx_sand_ball.falling",                   "schlurf.wav"                   },
   { "bdx_sand_ball.impact",                    "schlurf.wav"                   },
-  { "bdx_sand_loose.falling",                  UNDEFINED_FILENAME              },
+  { "bdx_sand_loose.falling",                  "schlurf.wav"                   },
   { "bdx_sand_loose.impact",                   "schlurf.wav"                   },
   { "bdx_diamond.collecting",                  "pong.wav"                      },
-  { "bdx_diamond.falling",                     UNDEFINED_FILENAME              },
+  { "bdx_diamond.falling",                     "pling.wav"                     },
   { "bdx_diamond.impact",                      "pling.wav"                     },
   { "bdx_flying_diamond.collecting",           "pong.wav"                      },
-  { "bdx_flying_diamond.falling",              UNDEFINED_FILENAME              },
+  { "bdx_flying_diamond.falling",              "pling.wav"                     },
   { "bdx_flying_diamond.impact",               "pling.wav"                     },
   { "bdx_rock.pushing",                                "pusch.wav"                     },
-  { "bdx_rock.falling",                                UNDEFINED_FILENAME              },
+  { "bdx_rock.falling",                                "klopf.wav"                     },
   { "bdx_rock.impact",                         "klopf.wav"                     },
   { "bdx_flying_rock.pushing",                 "pusch.wav"                     },
-  { "bdx_flying_rock.falling",                 UNDEFINED_FILENAME              },
+  { "bdx_flying_rock.falling",                 "klopf.wav"                     },
   { "bdx_flying_rock.impact",                  "klopf.wav"                     },
   { "bdx_mega_rock.pushing",                   "pusch.wav"                     },
-  { "bdx_mega_rock.falling",                   UNDEFINED_FILENAME              },
+  { "bdx_mega_rock.falling",                   "klopf.wav"                     },
   { "bdx_mega_rock.impact",                    "klopf.wav"                     },
   { "bdx_waiting_rock.pushing",                        "pusch.wav"                     },
   { "bdx_chasing_rock.pushing",                        "pusch.wav"                     },
   { "bdx_nut.pushing",                         "knurk.wav"                     },
   { "bdx_nut.breaking",                                "knack.wav"                     },
-  { "bdx_nut.falling",                         UNDEFINED_FILENAME              },
+  { "bdx_nut.falling",                         "klumpf.wav"                    },
   { "bdx_nut.impact",                          "klumpf.wav"                    },
   { "bdx_nitro_pack.pushing",                  "pusch.wav"                     },
   { "bdx_nitro_pack.impact",                   "klopf.wav"                     },
index 0ab5722197222ae67171c807185645aeb65f146c..77f8d3289653c47ab389602b9d0d668ea8ea5679 100644 (file)
@@ -7389,9 +7389,9 @@ static struct XY xy_directions[] =
 // functions
 // ----------------------------------------------------------------------------
 
-boolean isLevelEditorTestGame(void)
+boolean isLevelEditorFastStart(void)
 {
-  return level_editor_test_game;
+  return (level_editor_test_game && setup.editor.fast_game_start);
 }
 
 static int getMaxInfoTextLength(void)
@@ -11451,6 +11451,20 @@ static void DrawEngineConfigColors(void)
 {
   int i;
 
+  if (!hasColorTemplate_BD())
+  {
+    int font_nr = FONT_TEXT_1;
+    int font_height = getFontHeight(font_nr);
+    int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+    int xpos = ED_ENGINE_SETTINGS_X(0);
+    int ypos = ED_ENGINE_SETTINGS_Y(0);
+
+    PrintInfoText("No level specific colors available.", font_nr, xpos, ypos - yoffset_above);
+    PrintInfoText("(Not supported by graphics set.)", font_nr, xpos, ypos);
+
+    return;
+  }
+
   if (bd_color_type_changed)
   {
     if (level.bd_color_type != GD_COLOR_TYPE_RGB && level.bd_color_type != GetCommonColorType_BD())
index 96fe6f29140077275d67e987c617f390216b2111..8dd3e390a46648c4408f6be943e7354b5e9db05d 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "main.h"
 
-boolean isLevelEditorTestGame(void);
+boolean isLevelEditorFastStart(void);
 
 void CreateLevelEditorGadgets(void);
 void FreeLevelEditorGadgets(void);
index 04f69108527feaca46f5eca02ce2fc30b82f3ad9..99b55fc85824f262bc15915ce469046a5c06a88a 100644 (file)
@@ -1505,7 +1505,8 @@ static int HandleDropFileEvent(char *filename)
   Debug("event:dropfile", "filename == '%s'", filename);
 
   // check and extract dropped zip files into correct user data directory
-  if (!strSuffixLower(filename, ".zip"))
+  if (!strSuffixLower(filename, ".zip") &&
+      !strPrefixLower(filename, "fd:"))
   {
     Warn("file '%s' not supported", filename);
 
@@ -1615,6 +1616,12 @@ static void HandleDropCompleteEvent(int num_level_sets_succeeded,
 
 void HandleDropEvent(Event *event)
 {
+  Debug("event:drop", (event->type == SDL_DROPBEGIN    ? "SDL_DROPBEGIN" :
+                      event->type == SDL_DROPFILE      ? "SDL_DROPFILE" :
+                      event->type == SDL_DROPTEXT      ? "SDL_DROPTEXT" :
+                      event->type == SDL_DROPCOMPLETE  ? "SDL_DROPCOMPLETE" :
+                      "(unknown drop event type)"));
+
   static boolean confirm_on_drop_complete = FALSE;
   static int num_level_sets_succeeded = 0;
   static int num_artwork_sets_succeeded = 0;
@@ -2095,14 +2102,40 @@ void HandleKey(Key key, int key_status)
     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
   };
+  boolean game_key_pressed = FALSE;
   int joy = 0;
   int i;
 
-  if (HandleKeysSpeed(key, key_status))
-    return;            // do not handle already processed keys again
+  // check if any game key is pressed (direction/snap/drop keys)
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    int pnr;
 
-  if (HandleKeysDebug(key, key_status))
-    return;            // do not handle already processed keys again
+    for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
+    {
+      ski = setup.input[pnr].key;
+
+      for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
+       if (key == *key_info[i].key_custom)
+          game_key_pressed = TRUE;
+    }
+
+    ssi = setup.shortcut;
+
+    for (i = 0; i < NUM_DIRECTIONS; i++)
+      if (key == *key_info[i].key_snap)
+        game_key_pressed = TRUE;
+  }
+
+  // only handle speed or debug keys if no game key is pressed
+  if (!game_key_pressed)
+  {
+    if (HandleKeysSpeed(key, key_status))
+      return;          // do not handle already processed keys again
+
+    if (HandleKeysDebug(key, key_status))
+      return;          // do not handle already processed keys again
+  }
 
   // map special keys (media keys / remote control buttons) to default keys
   if (key == KSYM_PlayPause)
index c620a531b37bf4e30517c01366137a61e78edf8e..940ce6edc91e1c1854b9a2fa1bea2b113c2f17d5 100644 (file)
@@ -274,7 +274,7 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
   {
     -1,                                        -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(6),
-    &li.bd_cycle_delay_ms,             200
+    &li.bd_cycle_delay_ms,             160
   },
   {
     -1,                                        -1,
@@ -2924,31 +2924,12 @@ static int getFiletypeFromID(char *filetype_id)
 
 char *getLocalLevelTemplateFilename(void)
 {
-  return getDefaultLevelFilename(-1);
+  return getLevelFilenameFromBasename(LEVELTEMPLATE_FILENAME);
 }
 
 char *getGlobalLevelTemplateFilename(void)
 {
-  // global variable "leveldir_current" must be modified in the loop below
-  LevelDirTree *leveldir_current_last = leveldir_current;
-  char *filename = NULL;
-
-  // check for template level in path from current to topmost tree node
-
-  while (leveldir_current != NULL)
-  {
-    filename = getDefaultLevelFilename(-1);
-
-    if (fileExists(filename))
-      break;
-
-    leveldir_current = leveldir_current->node_parent;
-  }
-
-  // restore global variable "leveldir_current" modified in above loop
-  leveldir_current = leveldir_current_last;
-
-  return filename;
+  return getFilenameFromCurrentLevelDirUpward(LEVELTEMPLATE_FILENAME);
 }
 
 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
@@ -4644,9 +4625,12 @@ static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
   SetDefaultLevelColors_BD();
 
   // level name
-  char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
+  char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
+  char *cave_name_final = (gd_caveset_has_levels() ?
+                           getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
+                           getStringCopy(cave_name_latin1));
 
-  strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
+  strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
   level->name[MAX_LEVEL_NAME_LEN] = '\0';
 
   // playfield elements
@@ -4654,7 +4638,8 @@ static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
     for (y = 0; y < level->fieldy; y++)
       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
 
-  checked_free(cave_name);
+  checked_free(cave_name_latin1);
+  checked_free(cave_name_final);
 }
 
 static void setTapeInfoToDefaults(void);
@@ -7547,6 +7532,66 @@ void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
 }
 
+static void LoadLevel_FixEnvelopes(struct LevelInfo *level, boolean skip_single_lines)
+{
+  // This function removes newlines in envelopes after lines of text ending in the last column
+  // of the envelope. In earlier versions, these newlines were removed when displaying envelopes,
+  // but caused trouble in the level editor. In version 4.3.2.3, this problem was partially
+  // fixed in the level editor (but only for single full-width text lines followed by a newline,
+  // not for multiple lines ending in the last column, followed by a newline), but now produced
+  // unwanted newlines in the game for envelopes stored by previous game versions, which was not
+  // intended by the level author (and sometimes caused text lines not being displayed anymore at
+  // the bottom of the envelope).
+  //
+  // This function should solve these problems by removing such newline characters from envelopes
+  // stored by older game versions.
+
+  int envelope_nr;
+
+  for (envelope_nr = 0; envelope_nr < NUM_ENVELOPES; envelope_nr++)
+  {
+    char *envelope_ptr = level->envelope[envelope_nr].text;
+    int envelope_xsize = level->envelope[envelope_nr].xsize;
+    int envelope_size = strlen(envelope_ptr);
+    int start = 0;
+    int i;
+
+    for (i = 0; i < envelope_size; i++)
+    {
+      // check for newlines in envelope
+      if (envelope_ptr[i] == '\n')
+      {
+        int line_length = i - start;
+
+        // check for (non-empty) lines that are a multiple of the envelope width,
+        // causing a line break inside the envelope (text area in editor and in game)
+        if (line_length > 0 && line_length % envelope_xsize == 0)
+        {
+          // special case: skip fixing single lines for newer versions
+          boolean skip_fixing_line = (line_length == 1 && skip_single_lines);
+
+          if (!skip_fixing_line)
+          {
+            int j;
+
+            // remove newline character from string
+            for (j = i; j < envelope_size; j++)
+              envelope_ptr[j] = envelope_ptr[j + 1];
+          }
+
+          // continue with next line (that was copied over the newline)
+          start = i;
+        }
+        else
+        {
+          // continue with next character after newline
+          start = i + 1;
+        }
+      }
+    }
+  }
+}
+
 static void LoadLevel_InitVersion(struct LevelInfo *level)
 {
   int i, j;
@@ -7738,6 +7783,10 @@ static void LoadLevel_InitVersion(struct LevelInfo *level)
   // CE changing to player was kept under the player if walkable up to 4.2.3.1
   if (level->game_version <= VERSION_IDENT(4,2,3,1))
     level->keep_walkable_ce = TRUE;
+
+  // envelopes may contain broken or too many line breaks before 4.4.0.0
+  if (level->game_version < VERSION_IDENT(4,4,0,0))
+    LoadLevel_FixEnvelopes(level, (level->game_version >= VERSION_IDENT(4,3,2,3)));
 }
 
 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
@@ -10842,7 +10891,7 @@ static struct TokenInfo global_setup_tokens[] =
   },
   {
     TYPE_SWITCH_3_STATES,
-    &setup.bd_skip_falling_sounds,             "bd_skip_falling_sounds"
+    &setup.bd_falling_sounds,                  "bd_falling_sounds"
   },
   {
     TYPE_INTEGER,
@@ -11060,6 +11109,10 @@ static struct TokenInfo editor_setup_tokens[] =
     TYPE_SWITCH,
     &setup.editor.show_element_token,          "editor.show_element_token"
   },
+  {
+    TYPE_SWITCH,
+    &setup.editor.fast_game_start,             "editor.fast_game_start"
+  },
   {
     TYPE_SWITCH,
     &setup.editor.show_read_only_warning,      "editor.show_read_only_warning"
@@ -11709,7 +11762,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->bd_smooth_movements = STATE_TRUE;
   si->bd_pushing_graphics = STATE_TRUE;
   si->bd_up_down_graphics = STATE_TRUE;
-  si->bd_skip_falling_sounds = STATE_TRUE;
+  si->bd_falling_sounds = STATE_AUTO;
   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
@@ -11822,6 +11875,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->editor.el_headlines              = TRUE;
 
   si->editor.show_element_token                = FALSE;
+  si->editor.fast_game_start           = FALSE;
 
   si->editor.show_read_only_warning    = TRUE;
 
index 831ec9010a5a0a4a9460d471bb8aaf2574b53045..a516155ce76b269b2704d5ecc3ffe2112899e4c1 100644 (file)
@@ -1840,13 +1840,30 @@ static void InitFieldForEngine_RND(int x, int y)
   int element = Tile[x][y];
 
   // convert BD engine elements to corresponding R'n'D engine elements
-  element = (element == EL_BDX_EMPTY           ? EL_EMPTY :
-            element == EL_BDX_PLAYER           ? EL_PLAYER_1 :
-            element == EL_BDX_INBOX            ? EL_PLAYER_1 :
-            element == EL_BDX_SAND_1           ? EL_SAND :
-            element == EL_BDX_STEELWALL        ? EL_STEELWALL :
-            element == EL_BDX_EXIT_CLOSED      ? EL_EXIT_CLOSED :
-            element == EL_BDX_EXIT_OPEN        ? EL_EXIT_OPEN :
+  element = (element == EL_BDX_EMPTY                           ? EL_EMPTY :
+            element == EL_BDX_PLAYER                           ? EL_PLAYER_1 :
+            element == EL_BDX_INBOX                            ? EL_PLAYER_1 :
+            element == EL_BDX_SAND_1                           ? EL_SAND :
+            element == EL_BDX_WALL                             ? EL_BD_WALL :
+            element == EL_BDX_STEELWALL                        ? EL_STEELWALL :
+            element == EL_BDX_ROCK                             ? EL_BD_ROCK :
+            element == EL_BDX_DIAMOND                          ? EL_BD_DIAMOND :
+            element == EL_BDX_AMOEBA_1                         ? EL_BD_AMOEBA :
+            element == EL_BDX_MAGIC_WALL                       ? EL_BD_MAGIC_WALL :
+            element == EL_BDX_BUTTERFLY_1_RIGHT                ? EL_BD_BUTTERFLY_RIGHT :
+            element == EL_BDX_BUTTERFLY_1_UP                   ? EL_BD_BUTTERFLY_UP :
+            element == EL_BDX_BUTTERFLY_1_LEFT                 ? EL_BD_BUTTERFLY_LEFT :
+            element == EL_BDX_BUTTERFLY_1_DOWN                 ? EL_BD_BUTTERFLY_DOWN :
+            element == EL_BDX_BUTTERFLY_1                      ? EL_BD_BUTTERFLY :
+            element == EL_BDX_FIREFLY_1_RIGHT                  ? EL_BD_FIREFLY_RIGHT :
+            element == EL_BDX_FIREFLY_1_UP                     ? EL_BD_FIREFLY_UP :
+            element == EL_BDX_FIREFLY_1_LEFT                   ? EL_BD_FIREFLY_LEFT :
+            element == EL_BDX_FIREFLY_1_DOWN                   ? EL_BD_FIREFLY_DOWN :
+            element == EL_BDX_FIREFLY_1                        ? EL_BD_FIREFLY :
+            element == EL_BDX_EXPANDABLE_WALL_HORIZONTAL       ? EL_BD_EXPANDABLE_WALL :
+            element == EL_BDX_WALL_DIAMOND                     ? EL_WALL_BD_DIAMOND :
+            element == EL_BDX_EXIT_CLOSED                      ? EL_EXIT_CLOSED :
+            element == EL_BDX_EXIT_OPEN                        ? EL_EXIT_OPEN :
             element);
 
   Tile[x][y] = element;
@@ -2286,7 +2303,7 @@ static void UpdateGameControlValues(void)
   int time = (game.LevelSolved ?
              game.LevelSolved_CountingTime :
              level.game_engine_type == GAME_ENGINE_TYPE_BD ?
-             game_bd.time_played :
+             game_bd.time_left :
              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
              game_em.lev->time :
              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
@@ -4862,9 +4879,10 @@ void InitAmoebaNr(int x, int y)
 
 static void LevelSolved_SetFinalGameValues(void)
 {
-  game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
+  game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_left :
                     game.no_level_time_limit ? TimePlayed : TimeLeft);
-  game.score_time_final = (level.use_step_counter ? TimePlayed :
+  game.score_time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.frames_played :
+                           level.use_step_counter ? TimePlayed :
                           TimePlayed * FRAMES_PER_SECOND + TimeFrames);
 
   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
@@ -11835,6 +11853,9 @@ static void CheckLevelTime(void)
     // if last second running, wait for native engine time to exactly reach zero
     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
       TimeFrames = frames_per_second - 1;
+
+    // needed to store final time after solving game (before counting down remaining time)
+    SetTimeFrames_BD(TimePlayed * FRAMES_PER_SECOND + TimeFrames);
   }
 
   if (TimeFrames >= frames_per_second)
@@ -16591,6 +16612,8 @@ static ListNode *SaveEngineSnapshotBuffers(void)
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     SaveEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
+    SaveEngineSnapshotValues_BD();
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     SaveEngineSnapshotValues_EM();
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
@@ -16602,6 +16625,8 @@ static ListNode *SaveEngineSnapshotBuffers(void)
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_bd));
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
@@ -16741,6 +16766,8 @@ static void LoadEngineSnapshotValues(void)
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     LoadEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
+    LoadEngineSnapshotValues_BD();
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     LoadEngineSnapshotValues_EM();
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
index 1a85776a68a4e17333308881e7c8807d1fe144c3..3dede416f53f3dc8e5702baa67447e9057595568 100644 (file)
@@ -121,6 +121,7 @@ enum _element_property
   E_P_MOVED_BY_CONVEYOR_TOP,    // can be moved by conveyor belt
   E_P_MOVED_BY_CONVEYOR_BOTTOM, // can be moved UNDER the conveyor belt
 
+  E_P_WALKABLE,                 // can be walked
   E_P_DIGGABLE,                 // can be digged
   E_P_COLLECTIBLE,              // can be collected
   E_P_PUSHABLE,                 // can be pushed
@@ -160,6 +161,7 @@ enum _element_property
 #define P_MOVED_BY_CONVEYOR_TOP                (1 << E_P_MOVED_BY_CONVEYOR_TOP)
 #define P_MOVED_BY_CONVEYOR_BOTTOM     (1 << E_P_MOVED_BY_CONVEYOR_BOTTOM)
 
+#define P_WALKABLE                     (1 << E_P_WALKABLE)
 #define P_DIGGABLE                     (1 << E_P_DIGGABLE)
 #define P_COLLECTIBLE                  (1 << E_P_COLLECTIBLE)
 #define P_PUSHABLE                     (1 << E_P_PUSHABLE)
index 45da297ee1ca8376262cc6a971db70cf5ab1c09b..e3290c78fb6bfc9aacc96968970941c209d3d02b 100644 (file)
@@ -118,7 +118,7 @@ enum _generated_cells_indexes
 */
 GdElements gd_elements[] =
 {
-  { O_SPACE, N_("Space"), P_AMOEBA_CONSUMES, "SPACE", ' ', 0, 0, 0 },
+  { O_SPACE, N_("Space"), P_AMOEBA_CONSUMES | P_WALKABLE, "SPACE", ' ', 0, 0, 0 },
   { O_DIRT, N_("Dirt"), P_AMOEBA_CONSUMES | P_VISUAL_EFFECT | P_DIRT | P_DIGGABLE, "DIRT", '.', 2, 2, 2 },
   { O_DIRT_SLOPED_UP_RIGHT, N_("Sloped dirt (up & right)"), P_DIRT | P_SLOPED_UP | P_SLOPED_RIGHT | P_AMOEBA_CONSUMES | P_DIGGABLE, "DIRTSLOPEDUPRIGHT", 0, 280, 280, 280 },
   { O_DIRT_SLOPED_UP_LEFT, N_("Sloped dirt (up & left)"), P_DIRT | P_SLOPED_UP | P_SLOPED_LEFT | P_AMOEBA_CONSUMES | P_DIGGABLE, "DIRTSLOPEDUPLEFT", 0, 281, 281, 281 },
@@ -196,7 +196,7 @@ GdElements gd_elements[] =
   { O_POT, N_("Pot"), 0, "POT", 0, 63, 63, 63 },
   { O_GRAVITY_SWITCH, N_("Gravity switch"), 0, "GRAVITY_SWITCH", 0, 274, 274, 274 },
   { O_PNEUMATIC_HAMMER, N_("Pneumatic hammer"), P_COLLECTIBLE, "PNEUMATIC_HAMMER", 0, 62, 62, 62 },
-  { O_TELEPORTER, N_("Teleporter"), 0, "TELEPORTER", 0, 61, 61, 61 },
+  { O_TELEPORTER, N_("Teleporter"), P_WALKABLE, "TELEPORTER", 0, 61, 61, 61 },
   { O_SKELETON, N_("Skeleton"), 0, "SKELETON", 0, 273, 273, 273 },
   { O_WATER, N_("Water"), 0, "WATER", 0, 96, -96, -96, 100 },    // has ckdelay
   { O_WATER_1, N_("Water (1)"), 0, "WATER1", 0, 96, -96, -96 },
index ee1b4ee2f9740ba97664dba40ad1a5324e59b0d3..9df249eba1fba020e9ef53063c92c0aed0b8dbe7 100644 (file)
@@ -136,7 +136,7 @@ static inline boolean el_can_fall(const int element)
 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
 {
   // check if sound should be skipped for falling elements (and only be played on impact)
-  if (el_can_fall(element) && skip_bd_falling_sounds())
+  if (el_can_fall(element) && !use_bd_falling_sounds())
     return;
 
   // stone and diamond fall sounds.
@@ -543,6 +543,9 @@ static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
 
 static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir)
 {
+  int old_x = x;
+  int old_y = y;
+
   // raw values without range correction
   int raw_x = x + gd_dx[dir];
   int raw_y = y + gd_dy[dir];
@@ -552,7 +555,18 @@ static inline void store_dir_buffer(GdCave *cave, const int x, const int y, cons
   int new_y = gety(cave, raw_x, raw_y);
   int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
 
-  game_bd.game->dir_buffer[new_y][new_x] = new_dir;
+  // if tile is moving two steps at once, correct old position
+  if (dir > GD_MV_TWICE)
+  {
+    raw_x = x + gd_dx[new_dir];
+    raw_y = y + gd_dy[new_dir];
+
+    old_x = getx(cave, raw_x, raw_y);
+    old_y = gety(cave, raw_x, raw_y);
+  }
+
+  game_bd.game->dir_buffer_from[old_y][old_x] = new_dir;
+  game_bd.game->dir_buffer_to[new_y][new_x] = new_dir;
 }
 
 // store an element at the given position
@@ -1570,6 +1584,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
 
   gd_cave_clear_sounds(cave);
 
+  game_bd.player_moving = FALSE;
+  game_bd.player_snapping = FALSE;
+
   // if diagonal movements not allowed,
   // horizontal movements have precedence. [BROADRIBB]
   if (!cave->diagonal_movements)
@@ -1835,15 +1852,25 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
              // if snapping anything and we have snapping explosions set.
              // but these is not true for pushing.
              if (remains == O_SPACE && player_fire && !push)
+              {
                remains = cave->snap_element;
 
+               game_bd.player_snapping = TRUE;
+              }
+
              if (remains != O_SPACE || player_fire)
+              {
                // if any other element than space, player cannot move.
                // also if pressing fire, will not move.
                store_dir(cave, x, y, player_move, remains);
+              }
              else
+              {
                // if space remains there, the player moves.
                move(cave, x, y, player_move, O_PLAYER);
+
+               game_bd.player_moving = TRUE;
+              }
            }
          }
          break;
index dd36b1ee0ff391b8661cac9c84cf8d333022567b..446dfc2d385c029ba2202b843a30ab6ad020d14b 100644 (file)
@@ -319,10 +319,9 @@ typedef enum _element
 
   SCANNED = 0x100,
   COVERED = 0x200,
-  SKIPPED = 0x400,
 
   // binary AND this to elements to get rid of properties above.
-  O_MASK = ~(SCANNED | COVERED | SKIPPED)
+  O_MASK = ~(SCANNED | COVERED)
 } GdElement;
 
 typedef enum _sound
index 12eb53bcca2ba1c7e8699e3af954f96907e75c33..0b4f48cdac4eab23a22b7d4bae0f441309ca132d 100644 (file)
@@ -26,8 +26,10 @@ void gd_game_free(GdGame *game)
     gd_cave_map_free(game->element_buffer);
   if (game->last_element_buffer)
     gd_cave_map_free(game->last_element_buffer);
-  if (game->dir_buffer)
-    gd_cave_map_free(game->dir_buffer);
+  if (game->dir_buffer_from)
+    gd_cave_map_free(game->dir_buffer_from);
+  if (game->dir_buffer_to)
+    gd_cave_map_free(game->dir_buffer_to);
   if (game->gfx_buffer)
     gd_cave_map_free(game->gfx_buffer);
 
@@ -89,10 +91,15 @@ static void load_cave(GdGame *game)
     gd_cave_map_free(game->last_element_buffer);
   game->last_element_buffer = NULL;
 
-  // delete direction buffer
-  if (game->dir_buffer)
-    gd_cave_map_free(game->dir_buffer);
-  game->dir_buffer = NULL;
+  // delete direction buffer (from)
+  if (game->dir_buffer_from)
+    gd_cave_map_free(game->dir_buffer_from);
+  game->dir_buffer_from = NULL;
+
+  // delete direction buffer (to)
+  if (game->dir_buffer_to)
+    gd_cave_map_free(game->dir_buffer_to);
+  game->dir_buffer_to = NULL;
 
   // delete gfx buffer
   if (game->gfx_buffer)
@@ -122,6 +129,8 @@ static void load_cave(GdGame *game)
   game->milliseconds_anim = 0;
   game->milliseconds_game = 0;        // set game timer to zero, too
 
+  game->cycle_counter = 0;
+
   // create new element buffer
   game->element_buffer = gd_cave_map_new(game->cave, int);
 
@@ -136,12 +145,19 @@ static void load_cave(GdGame *game)
     for (x = 0; x < game->cave->w; x++)
       game->last_element_buffer[y][x] = O_NONE;
 
-  // create new direction buffer
-  game->dir_buffer = gd_cave_map_new(game->cave, int);
+  // create new direction buffer (from)
+  game->dir_buffer_from = gd_cave_map_new(game->cave, int);
 
   for (y = 0; y < game->cave->h; y++)
     for (x = 0; x < game->cave->w; x++)
-      game->dir_buffer[y][x] = GD_MV_STILL;
+      game->dir_buffer_from[y][x] = GD_MV_STILL;
+
+  // create new direction buffer (to)
+  game->dir_buffer_to = gd_cave_map_new(game->cave, int);
+
+  for (y = 0; y < game->cave->h; y++)
+    for (x = 0; x < game->cave->w; x++)
+      game->dir_buffer_to[y][x] = GD_MV_STILL;
 
   // create new gfx buffer
   game->gfx_buffer = gd_cave_map_new(game->cave, int);
@@ -188,6 +204,13 @@ GdGame *gd_game_new(const int cave, const int level)
   return game;
 }
 
+boolean check_iteration_reached(GdGame *game)
+{
+  int millisecs_elapsed = 20;
+
+  return (game->milliseconds_game + millisecs_elapsed >= game->cave->speed);
+}
+
 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
 {
   boolean suicide = FALSE;
@@ -383,22 +406,28 @@ static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean
       {
        for (x = 0; x < game->cave->w; x++)
        {
-         game->last_element_buffer[y][x] = game->element_buffer[y][x] & ~SKIPPED;
-         game->dir_buffer[y][x] = GD_MV_STILL;
+         game->last_element_buffer[y][x] = game->element_buffer[y][x];
+         game->dir_buffer_from[y][x] = GD_MV_STILL;
+         game->dir_buffer_to[y][x]   = GD_MV_STILL;
        }
       }
 
-      // store last maximum number of cycles (to force redraw if changed)
+      // store last maximum number of cycle frames (to force redraw if changed)
       game->itermax_last = game->itermax;
 
-      // update maximum number of cycles (frame) per cave iteration
-      game->itermax = game->itercycle;
+      // store maximum number of cycle frames separately for even and odd cycles
+      game->itermax2[game->cycle_counter % 2] = game->itercycle;
+
+      // update maximum number of cycle frames per cave iteration
+      game->itermax = game->itermax2[!(game->cycle_counter % 2)];
 
-      // reset cycle (frame) counter for the next cave iteration
+      // reset cycle frame counter for the next cave iteration
       game->itercycle = 0;
 
       iterate_cave(game, game->player_move, game->player_fire);
 
+      game->cycle_counter++;
+
       if (game->player_move == GD_MV_STILL)
       {
        game->player_move_stick = FALSE;
index aa9a48de387be6ec127c0e09cc5853798353800f..2ea5d8c4bfc1929f6cf9c0c3151a98211d5fc470 100644 (file)
@@ -77,15 +77,18 @@ typedef struct _gd_game
   int state_counter;            // counter used to control the game flow, rendering of caves
   int **element_buffer;
   int **last_element_buffer;
-  int **dir_buffer;
+  int **dir_buffer_from;
+  int **dir_buffer_to;
   int **gfx_buffer;             // contains the indexes to the cells;
                                 // created by *start_level, deleted by *stop_game
   int itercycle;
   int itermax;
   int itermax_last;
+  int itermax2[2];
   int animcycle;
   int milliseconds_game;
   int milliseconds_anim;
+  int cycle_counter;
 
   int replay_no_more_movements;
   boolean show_story;          // to remember that story for a particular cave was already shown.
@@ -112,6 +115,8 @@ GdGame *gd_game_new(const int cave, const int level);
 GdGame *gd_game_new_snapshot(GdCave *snapshot);
 GdGame *gd_game_new_test(GdCave *cave, int level);
 
+boolean check_iteration_reached(GdGame *game);
+
 void play_game_func(GdGame *game, int action);
 
 #endif // BD_GAMEPLAY_H
index aa091ba131b44404613d50aebcdde97ce18ee4d6..fa139d0cec13b52f0f886f4165a9fe43df98b268 100644 (file)
@@ -363,6 +363,11 @@ static boolean surface_has_c64_colors(SDL_Surface *surface)
   return has_c64_colors;
 }
 
+boolean gd_bitmap_has_c64_colors(Bitmap *bitmap)
+{
+  return surface_has_c64_colors(bitmap->surface);
+}
+
 // sets one of the colors in the indexed palette of an sdl surface to a GdColor.
 static void set_surface_palette_color(SDL_Surface *surface, int index, GdColor col)
 {
@@ -466,8 +471,14 @@ static Bitmap *get_tile_bitmap_c64(GdCave *cave, SDL_Surface *surface)
   set_surface_palette_color(surface, 7, 0);
   set_surface_palette_color(surface, 8, 0);
 
+  // set background color to be transparent for masked tile bitmap
+  int bg_color = gd_color_get_rgb(cave->color0);
+  int bg_r = gd_color_get_r(bg_color);
+  int bg_g = gd_color_get_g(bg_color);
+  int bg_b = gd_color_get_b(bg_color);
+
   // create bitmap from C64 surface
-  tile_bitmap_c64 = SDLGetBitmapFromSurface(surface);
+  tile_bitmap_c64 = SDLGetBitmapFromSurface_WithMaskedColor(surface, bg_r, bg_g, bg_b);
 
   return tile_bitmap_c64;
 }
@@ -532,6 +543,12 @@ static inline boolean el_player(const int element)
   return (gd_elements[element & O_MASK].properties & P_PLAYER) != 0;
 }
 
+// returns true if the element is walkable
+static inline boolean el_walkable(const int element)
+{
+  return (gd_elements[element & O_MASK].properties & P_WALKABLE) != 0;
+}
+
 // returns true if the element is diggable
 static inline boolean el_diggable(const int element)
 {
@@ -544,7 +561,6 @@ static inline boolean el_collectible(const int element)
 {
   return (gd_elements[element & O_MASK].properties & P_COLLECTIBLE) != 0;
 }
-#endif
 
 // returns true if the element is pushable
 static inline boolean el_pushable(const int element)
@@ -563,6 +579,7 @@ static inline boolean el_falling(const int element)
 {
   return (gd_elements[element & O_MASK].properties & P_FALLING) != 0;
 }
+#endif
 
 // returns true if the element is growing
 static inline boolean el_growing(const int element)
@@ -583,51 +600,57 @@ static void gd_drawcave_tile(Bitmap *dest, GdGame *game, int x, int y, boolean d
   GdCave *cave = game->cave;
   int sx = x * cell_size - scroll_x;
   int sy = y * cell_size - scroll_y;
-  int dir = game->dir_buffer[y][x];
+  int dir_from = game->dir_buffer_from[y][x];
+  int dir_to = game->dir_buffer_to[y][x];
   int tile = game->element_buffer[y][x];
+  int tile_last = game->last_element_buffer[y][x];
+  int tile_from = O_NONE;      // source element if element is moving (will be set later)
+  int tile_to = tile;          // target element if element is moving
   int frame = game->animcycle;
-  struct GraphicInfo_BD *g = &graphic_info_bd_object[tile][frame];
-  Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap);
-  boolean is_movable = (el_can_move(tile) || el_falling(tile) || el_pushable(tile) ||
-                       el_player(tile));
-  boolean is_movable_or_diggable = (is_movable || el_diggable(game->last_element_buffer[y][x]));
-  boolean is_moving = (is_movable_or_diggable && dir != GD_MV_STILL);
+  boolean is_moving_from = (dir_from != GD_MV_STILL);
+  boolean is_moving_to   = (dir_to   != GD_MV_STILL);
+  boolean is_moving = (is_moving_from || is_moving_to);
   boolean use_smooth_movements = use_bd_smooth_movements();
 
-  // do not use smooth movement animation for growing or exploding game elements
-  if ((el_growing(tile) || el_explosion(tile)) && dir != GD_MV_STILL)
+  // if element is moving away from this tile, determine element that is moving
+  if (is_moving_from)
   {
-    int dx = (dir == GD_MV_LEFT ? +1 : dir == GD_MV_RIGHT ? -1 : 0);
-    int dy = (dir == GD_MV_UP   ? +1 : dir == GD_MV_DOWN  ? -1 : 0);
-    int old_x = cave->getx(cave, x + dx, y + dy);
-    int old_y = cave->gety(cave, x + dx, y + dy);
-    int last_tile_from = game->last_element_buffer[old_y][old_x] & ~SKIPPED;
-    boolean old_is_player = el_player(last_tile_from);
-
-    // check special case of player running into enemy from top or left side
-    if (old_is_player)
-    {
-      game->element_buffer[y][x] = (dir == GD_MV_LEFT  ? O_PLAYER_LEFT  :
-                                    dir == GD_MV_RIGHT ? O_PLAYER_RIGHT :
-                                    dir == GD_MV_UP    ? O_PLAYER_UP    :
-                                    dir == GD_MV_DOWN  ? O_PLAYER_DOWN  : O_PLAYER);
-
-      // draw player running into explosion (else player would disappear immediately)
-      gd_drawcave_tile(dest, game, x, y, draw_masked);
+    int dx = (dir_from == GD_MV_LEFT ? -1 : dir_from == GD_MV_RIGHT ? +1 : 0);
+    int dy = (dir_from == GD_MV_UP   ? -1 : dir_from == GD_MV_DOWN  ? +1 : 0);
+    int new_x = cave->getx(cave, x + dx, y + dy);
+    int new_y = cave->gety(cave, x + dx, y + dy);
 
-      game->element_buffer[y][x] = tile;
-    }
+    tile_from = game->element_buffer[new_y][new_x];
 
-    use_smooth_movements = FALSE;
+    // handle special case of player running into enemy/explosion from top or left side
+    if ((el_growing(tile_from) || el_explosion(tile_from)) && el_player(tile_last))
+      tile_from = tile_last;
   }
 
+  // --------------------------------------------------------------------------------
+  // check if we should use smooth movement animations or not
+  // --------------------------------------------------------------------------------
+
   // do not use smooth movement animation for player entering exit (engine stopped)
   if (cave->player_state == GD_PL_EXITED)
     use_smooth_movements = FALSE;
 
+  // never treat empty space as "moving" (source tile if player is snapping)
+  if (tile_from == O_SPACE)
+    use_smooth_movements = FALSE;
+
+  // do not use smooth movement animation for player stirring the pot
+  if (tile_from == O_PLAYER_STIRRING || tile_to == O_PLAYER_STIRRING)
+    use_smooth_movements = FALSE;
+
+  // do not use smooth movement animation for growing or exploding game elements
+  if (el_growing(tile) || el_explosion(tile))
+    use_smooth_movements = FALSE;
+
 #if DO_GFX_SANITY_CHECK
   if (use_native_bd_graphics_engine() && !setup.small_game_graphics && !program.headless)
   {
+    struct GraphicInfo_BD *g = &graphic_info_bd_object[tile][frame];
     int old_x = (game->gfx_buffer[y][x] % GD_NUM_OF_CELLS) % GD_NUM_OF_CELLS_X;
     int old_y = (game->gfx_buffer[y][x] % GD_NUM_OF_CELLS) / GD_NUM_OF_CELLS_X;
     int new_x = g->src_x / g->width;
@@ -647,74 +670,77 @@ static void gd_drawcave_tile(Bitmap *dest, GdGame *game, int x, int y, boolean d
   // if game element not moving (or no smooth movements requested), simply draw tile
   if (!is_moving || !use_smooth_movements)
   {
+    struct GraphicInfo_BD *g = &graphic_info_bd_object[tile][frame];
+    Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap);
+
     blit_bitmap(tile_bitmap, dest, g->src_x, g->src_y, cell_size, cell_size, sx, sy);
 
     return;
   }
 
+  // --------------------------------------------------------------------------------
   // draw smooth animation for game element moving between two cave tiles
+  // --------------------------------------------------------------------------------
 
-  if (!(game->last_element_buffer[y][x] & SKIPPED))
-  {
-    // redraw previous game element on the cave field the new element is moving to
-    int tile_last = game->last_element_buffer[y][x];
-
-    // only redraw previous game element if it is diggable (like dirt etc.)
-    if (!el_diggable(tile_last))
-      tile_last = O_SPACE;
-
-    struct GraphicInfo_BD *g_old = &graphic_info_bd_object[tile_last][frame];
-    Bitmap *tile_bitmap_old = gd_get_tile_bitmap(g_old->bitmap);
-
-    blit_bitmap(tile_bitmap_old, dest, g_old->src_x, g_old->src_y, cell_size, cell_size, sx, sy);
-  }
-
-  // get cave field position the game element is moving from
-  int dx = (dir == GD_MV_LEFT ? +1 : dir == GD_MV_RIGHT ? -1 : 0);
-  int dy = (dir == GD_MV_UP   ? +1 : dir == GD_MV_DOWN  ? -1 : 0);
-  int old_x = cave->getx(cave, x + dx, y + dy);
-  int old_y = cave->gety(cave, x + dx, y + dy);
-  int tile_from = game->element_buffer[old_y][old_x] & ~SKIPPED;   // should never be skipped
-  struct GraphicInfo_BD *g_from = &graphic_info_bd_object[tile_from][frame];
-  Bitmap *tile_bitmap_from = gd_get_tile_bitmap(g_from->bitmap);
-  boolean old_is_player = el_player(tile_from);
-  boolean old_is_moving = (game->dir_buffer[old_y][old_x] != GD_MV_STILL);
-  boolean old_is_visible = (old_x >= cave->x1 &&
-                           old_x <= cave->x2 &&
-                           old_y >= cave->y1 &&
-                           old_y <= cave->y2);
-  if (old_is_visible)
+  // ---------- 1st step: draw background element for this tile ----------
   {
-    if (!old_is_moving && !old_is_player)
-    {
-      // redraw game element on the cave field the element is moving from
-      blit_bitmap(tile_bitmap_from, dest, g_from->src_x, g_from->src_y, cell_size, cell_size,
-                 sx + dx * cell_size, sy + dy * cell_size);
+    int tile_back = (!is_moving_to ? tile : el_diggable(tile_last) ? tile_last : O_SPACE);
+    struct GraphicInfo_BD *g = &graphic_info_bd_object[tile_back][frame];
+    Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap);
 
-      game->element_buffer[old_y][old_x] |= SKIPPED;
-    }
-    else
-    {
-      // if old tile also moving (like pushing player), do not redraw tile background
-      // (but redraw if tile and old tile are moving/falling into different directions)
-      if (game->dir_buffer[old_y][old_x] == game->dir_buffer[y][x])
-       game->last_element_buffer[old_y][old_x] |= SKIPPED;
-    }
+    blit_bitmap(tile_bitmap, dest, g->src_x, g->src_y, cell_size, cell_size, sx, sy);
   }
 
   // get shifted position between cave fields the game element is moving from/to
   int itercycle = MIN(MAX(0, game->itermax - game->itercycle - 1), game->itermax);
   int shift = cell_size * itercycle / game->itermax;
 
-  blit_bitmap(tile_bitmap, dest, g->src_x, g->src_y, cell_size, cell_size,
-             sx + dx * shift, sy + dy * shift);
+  // ---------- 2nd step: draw element that is moving away from this tile  ----------
 
-  // special case: redraw player snapping a game element
-  if (old_is_visible && old_is_player && !old_is_moving)
+  if (is_moving_from)
   {
-    // redraw game element on the cave field the element is moving from
-    blit_bitmap(tile_bitmap_from, dest, g_from->src_x, g_from->src_y, cell_size, cell_size,
-               sx + dx * cell_size, sy + dy * cell_size);
+    struct GraphicInfo_BD *g = &graphic_info_bd_object[tile_from][frame];
+    Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap);
+    int dx = (dir_from == GD_MV_LEFT ? -1 : dir_from == GD_MV_RIGHT ? +1 : 0);
+    int dy = (dir_from == GD_MV_UP   ? -1 : dir_from == GD_MV_DOWN  ? +1 : 0);
+    int xsize = (dx != 0 ? shift : cell_size);
+    int ysize = (dy != 0 ? shift : cell_size);
+    int gx = g->src_x + (dx < 0 ? cell_size - shift : 0);
+    int gy = g->src_y + (dy < 0 ? cell_size - shift : 0);
+    int tx = sx + (dx < 0 ? 0 : dx > 0 ? cell_size - shift : 0);
+    int ty = sy + (dy < 0 ? 0 : dy > 0 ? cell_size - shift : 0);
+
+    if (el_walkable(tile))
+      blit_bitmap = BlitBitmapMasked;
+
+    blit_bitmap(tile_bitmap, dest, gx, gy, xsize, ysize, tx, ty);
+
+    // when using dynamic scheduling (mainly BD1 levels), redraw tile in next frame
+    game->gfx_buffer[y][x] |= GD_REDRAW;
+  }
+
+  // ---------- 3rd step: draw element that is moving towards this tile  ----------
+
+  if (is_moving_to)
+  {
+    struct GraphicInfo_BD *g = &graphic_info_bd_object[tile_to][frame];
+    Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap);
+    int dx = (dir_to == GD_MV_LEFT ? +1 : dir_to == GD_MV_RIGHT ? -1 : 0);
+    int dy = (dir_to == GD_MV_UP   ? +1 : dir_to == GD_MV_DOWN  ? -1 : 0);
+    int xsize = (dx != 0 ? cell_size - shift : cell_size);
+    int ysize = (dy != 0 ? cell_size - shift : cell_size);
+    int gx = g->src_x + (dx < 0 ? shift : 0);
+    int gy = g->src_y + (dy < 0 ? shift : 0);
+    int tx = sx + (dx < 0 ? 0 : dx > 0 ? shift : 0);
+    int ty = sy + (dy < 0 ? 0 : dy > 0 ? shift : 0);
+
+    if (is_moving_from)
+      blit_bitmap = BlitBitmapMasked;
+
+    blit_bitmap(tile_bitmap, dest, gx, gy, xsize, ysize, tx, ty);
+
+    // when using dynamic scheduling (mainly BD1 levels), redraw tile in next frame
+    game->gfx_buffer[y][x] |= GD_REDRAW;
   }
 }
 
@@ -759,12 +785,9 @@ int gd_drawcave(Bitmap *dest, GdGame *game, boolean force_redraw)
     {
       if (redraw_all ||
          game->gfx_buffer[y][x] & GD_REDRAW ||
-         game->dir_buffer[y][x] != GD_MV_STILL)
+         game->dir_buffer_from[y][x] != GD_MV_STILL ||
+         game->dir_buffer_to[y][x]   != GD_MV_STILL)
       {
-       // skip redrawing already drawn element with movement
-       if (game->element_buffer[y][x] & SKIPPED)
-         continue;
-
        // now we have drawn it
        game->gfx_buffer[y][x] = game->gfx_buffer[y][x] & ~GD_REDRAW;
 
index 6b522fcc504143d767ddbc23d864e2fdefbd1112..e52048adadf1f4891fb249189535286e85b1345c 100644 (file)
@@ -33,6 +33,7 @@ int get_play_area_h(void);
 
 void gd_init_play_area(void);
 
+boolean gd_bitmap_has_c64_colors(Bitmap *bitmap);
 void gd_prepare_tile_bitmap(GdCave *cave, Bitmap *bitmap, int scale_down_factor);
 void gd_set_tile_bitmap_reference(Bitmap *bitmap);
 Bitmap *gd_get_tile_bitmap(Bitmap *bitmap);
index a060e2e4be7f5f07990d10aa2560747968cf74ba..7778b066a4a8e37265111872ad1f0376d2283f5f 100644 (file)
@@ -43,10 +43,16 @@ struct GameInfo_BD
   boolean game_over;
   boolean cover_screen;
 
+  boolean player_moving;
+  boolean player_snapping;
+
   // needed for updating panel
-  int time_played;
+  int time_left;
   int gems_still_needed;
   int score;
+
+  // needed for saving score time
+  int frames_played;
 };
 
 struct LevelInfo_BD
@@ -72,6 +78,20 @@ struct GraphicInfo_BD
 
 struct EngineSnapshotInfo_BD
 {
+  GdGame game;
+
+  // data from pointers in game structure
+  int element_buffer[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+  int last_element_buffer[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+  int dir_buffer_from[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+  int dir_buffer_to[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+  int gfx_buffer[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+
+  GdCave cave;
+
+  // data from pointers in cave structure
+  short map[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
+  short hammered_reappear[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT];
 };
 
 
@@ -96,6 +116,7 @@ boolean checkGamePlaying_BD(void);
 boolean checkBonusTime_BD(void);
 int getFramesPerSecond_BD(void);
 int getTimeLeft_BD(void);
+void SetTimeFrames_BD(int);
 
 void InitGfxBuffers_BD(void);
 
@@ -117,7 +138,9 @@ boolean use_native_bd_graphics_engine(void);
 boolean use_bd_smooth_movements(void);
 boolean use_bd_pushing_graphics(void);
 boolean use_bd_up_down_graphics(void);
-boolean skip_bd_falling_sounds(void);
+boolean use_bd_falling_sounds(void);
+
+boolean hasColorTemplate_BD(void);
 
 Bitmap **GetTitleScreenBitmaps_BD(void);
 void CoverScreen_BD(void);
@@ -125,4 +148,7 @@ void CoverScreen_BD(void);
 void BlitScreenToBitmap_BD(Bitmap *);
 void RedrawPlayfield_BD(boolean);
 
+void SaveEngineSnapshotValues_BD(void);
+void LoadEngineSnapshotValues_BD(void);
+
 #endif // EXPORT_BD_H
index ace426dbfdcbfcb5833fe2470297bbf8687b81ca..75e801c00d6115a2db6eef52eef2b01406da33fc 100644 (file)
@@ -27,6 +27,7 @@
 // ----------------------------------------------------------------------------
 
 void InitGraphicInfo_BD(void);
+boolean CheckSingleStepMode_BD(boolean, boolean, boolean);
 
 void PlayLevelSound_BD(int, int, int, int);
 void StopSound_BD(int, int);
@@ -38,6 +39,6 @@ byte *TapePlayAction_BD(void);
 byte *TapeCorrectAction_BD(byte *);
 boolean TapeIsPlaying_ReplayBD(void);
 
-boolean isLevelEditorTestGame(void);
+boolean isLevelEditorFastStart(void);
 
 #endif // IMPORT_BD_H
index 06ecfd0458fa7cea799a9fe044236b56122a75b9..4404d0ab06d3765ffd1afb71ae93d1f3cdc04c36 100644 (file)
@@ -255,20 +255,28 @@ int getTimeLeft_BD(void)
   return 0;
 }
 
+void SetTimeFrames_BD(int frames_played)
+{
+  // needed to store final time after solving game (before counting down remaining time)
+  if (game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
+    game_bd.frames_played = frames_played;
+
+}
+
 static void UpdateGameDoorValues_BD(void)
 {
   GdCave *cave = game_bd.game->cave;
-  int time_secs = gd_cave_time_show(cave, cave->time);
+  int time_left = gd_cave_time_show(cave, cave->time);
   int gems_still_needed = MAX(0, (cave->diamonds_needed - cave->diamonds_collected));
 
-  game_bd.time_played = time_secs;
+  game_bd.time_left = time_left;
   game_bd.gems_still_needed = gems_still_needed;
   game_bd.score = game_bd.game->player_score;
 
   if (game.LevelSolved)
   {
     // update time and score in panel while counting bonus time
-    game.LevelSolved_CountingTime  = game_bd.time_played;
+    game.LevelSolved_CountingTime  = game_bd.time_left;
     game.LevelSolved_CountingScore = game_bd.score;
   }
 }
@@ -320,10 +328,6 @@ void InitGameEngine_BD(void)
   game_bd.game_over = FALSE;
   game_bd.cover_screen = FALSE;
 
-  game_bd.time_played = 0;
-  game_bd.gems_still_needed = 0;
-  game_bd.score = 0;
-
   gd_caveset_last_selected       = native_bd_level.cave_nr;
   gd_caveset_last_selected_level = native_bd_level.level_nr;
 
@@ -335,6 +339,11 @@ void InitGameEngine_BD(void)
   game_bd.game->itercycle = 0;
   game_bd.game->itermax = 8;   // default; dynamically changed at runtime
   game_bd.game->itermax_last = game_bd.game->itermax;
+  game_bd.game->itermax2[0] = game_bd.game->itermax;
+  game_bd.game->itermax2[1] = game_bd.game->itermax;
+
+  game_bd.player_moving = FALSE;
+  game_bd.player_snapping = FALSE;
 
   // default: start with completely covered playfield
   int next_state = GAME_INT_START_UNCOVER + 1;
@@ -356,10 +365,10 @@ void InitGameEngine_BD(void)
   // when skipping uncovering, continue with uncovered playfield
   if (setup.bd_skip_uncovering)
     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL + 1;
-  else if (isLevelEditorTestGame())
+  else if (isLevelEditorFastStart())
     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL - 8;
 
-  if (setup.bd_skip_uncovering || isLevelEditorTestGame())
+  if (setup.bd_skip_uncovering || isLevelEditorFastStart())
     gd_scroll(game_bd.game, TRUE, TRUE);
 
   ClearRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE);
@@ -433,6 +442,15 @@ void GameActions_BD(byte action[MAX_PLAYERS])
     play_game_func(game_bd.game, action[0]);
   }
 
+  boolean single_step_mode_paused =
+    CheckSingleStepMode_BD(check_iteration_reached(game_bd.game),
+                           game_bd.player_moving,
+                           game_bd.player_snapping);
+
+  // draw final movement animation frame before going to single step pause mode
+  if (single_step_mode_paused)
+    game_bd.game->itercycle = game_bd.game->itermax - 1;
+
   RedrawPlayfield_BD(FALSE);
 
   UpdateGameDoorValues_BD();
@@ -470,11 +488,16 @@ boolean use_bd_up_down_graphics(void)
          (setup.bd_up_down_graphics == STATE_AUTO && !use_native_bd_graphics_engine()));
 }
 
-// check if skipping falling sounds selected in setup menu
-boolean skip_bd_falling_sounds(void)
+// check if element falling sounds selected in setup menu
+boolean use_bd_falling_sounds(void)
+{
+  return ((setup.bd_falling_sounds == STATE_TRUE) ||
+         (setup.bd_falling_sounds == STATE_AUTO && game.use_native_bd_sound_engine));
+}
+
+boolean hasColorTemplate_BD(void)
 {
-  return ((setup.bd_skip_falling_sounds == STATE_TRUE) ||
-         (setup.bd_skip_falling_sounds == STATE_AUTO && !game.use_native_bd_sound_engine));
+  return gd_bitmap_has_c64_colors(graphic_info_bd_color_template.bitmap);
 }
 
 Bitmap **GetTitleScreenBitmaps_BD(void)
@@ -531,3 +554,99 @@ void RedrawPlayfield_BD(boolean force_redraw)
 {
   gd_drawcave(gd_screen_bitmap, game_bd.game, force_redraw);
 }
+
+
+// ============================================================================
+// snapshot functions
+// ============================================================================
+
+void SaveEngineSnapshotValues_BD(void)
+{
+  GdGame *game = game_bd.game;
+  GdCave *cave = game_bd.game->cave;
+  int x, y;
+
+  engine_snapshot_bd.game = *game;
+
+  for (y = 0; y < cave->h; y++)
+  {
+    for (x = 0; x < cave->w; x++)
+    {
+      engine_snapshot_bd.element_buffer[x][y]      = game->element_buffer[y][x];
+      engine_snapshot_bd.last_element_buffer[x][y] = game->last_element_buffer[y][x];
+      engine_snapshot_bd.dir_buffer_from[x][y]     = game->dir_buffer_from[y][x];
+      engine_snapshot_bd.dir_buffer_to[x][y]       = game->dir_buffer_to[y][x];
+      engine_snapshot_bd.gfx_buffer[x][y]          = game->gfx_buffer[y][x];
+    }
+  }
+
+  engine_snapshot_bd.cave = *cave;
+
+  for (y = 0; y < cave->h; y++)
+  {
+    for (x = 0; x < cave->w; x++)
+    {
+      engine_snapshot_bd.map[x][y] = cave->map[y][x];
+
+      if (cave->hammered_walls_reappear)
+        engine_snapshot_bd.hammered_reappear[x][y] = cave->hammered_reappear[y][x];
+    }
+  }
+}
+
+void LoadEngineSnapshotValues_BD(void)
+{
+  GdGame *game = game_bd.game;
+  GdCave *cave = game_bd.game->cave;
+  int x, y;
+
+  // copy pointers
+  engine_snapshot_bd.game.cave                = game->cave;
+  engine_snapshot_bd.game.original_cave       = game->original_cave;
+
+  engine_snapshot_bd.game.element_buffer      = game->element_buffer;
+  engine_snapshot_bd.game.last_element_buffer = game->last_element_buffer;
+  engine_snapshot_bd.game.dir_buffer_from     = game->dir_buffer_from;
+  engine_snapshot_bd.game.dir_buffer_to       = game->dir_buffer_to;
+  engine_snapshot_bd.game.gfx_buffer          = game->gfx_buffer;
+
+  *game = engine_snapshot_bd.game;
+
+  for (y = 0; y < cave->h; y++)
+  {
+    for (x = 0; x < cave->w; x++)
+    {
+      game->element_buffer[y][x]      = engine_snapshot_bd.element_buffer[x][y];
+      game->last_element_buffer[y][x] = engine_snapshot_bd.last_element_buffer[x][y];
+      game->dir_buffer_from[y][x]     = engine_snapshot_bd.dir_buffer_from[x][y];
+      game->dir_buffer_to[y][x]       = engine_snapshot_bd.dir_buffer_to[x][y];
+      game->gfx_buffer[y][x]          = engine_snapshot_bd.gfx_buffer[x][y];
+    }
+  }
+
+  // copy pointers
+  engine_snapshot_bd.cave.story             = cave->story;
+  engine_snapshot_bd.cave.remark            = cave->remark;
+  engine_snapshot_bd.cave.tags              = cave->tags;
+  engine_snapshot_bd.cave.map               = cave->map;
+  engine_snapshot_bd.cave.objects           = cave->objects;
+  engine_snapshot_bd.cave.replays           = cave->replays;
+  engine_snapshot_bd.cave.random            = cave->random;
+  engine_snapshot_bd.cave.objects_order     = cave->objects_order;
+  engine_snapshot_bd.cave.hammered_reappear = cave->hammered_reappear;
+
+  *cave = engine_snapshot_bd.cave;
+
+  for (y = 0; y < cave->h; y++)
+  {
+    for (x = 0; x < cave->w; x++)
+    {
+      cave->map[y][x] = engine_snapshot_bd.map[x][y];
+
+      if (cave->hammered_walls_reappear)
+        cave->hammered_reappear[y][x] = engine_snapshot_bd.hammered_reappear[x][y];
+    }
+  }
+
+  gd_scroll(game_bd.game, TRUE, TRUE);
+}
index 807a9a14e5d238bc94504f08e80c5b1956a937ae..e451d99ef862f9cd2d09d081c1ce136e0f8483db 100644 (file)
@@ -6752,6 +6752,9 @@ void OpenAll(void)
 
   DrawMainMenu();
 
+  if (options.drop_file != NULL)
+    PushDropEvent(options.drop_file);
+
 #if 0
   Debug("internal:path", "SDL_GetBasePath() == '%s'",
        SDL_GetBasePath());
index 5e63c31f93307102d0b9903d12bf8aaebf722d25..ed4bd5d62c991d7e94f72cb150ab904ee8c2935f 100644 (file)
@@ -1704,6 +1704,7 @@ void GetOptions(int argc, char *argv[],
   options.player_name = NULL;
   options.identifier = NULL;
   options.level_nr = NULL;
+  options.drop_file = NULL;
 
   options.display_nr = 0;
 
@@ -1870,6 +1871,15 @@ void GetOptions(int argc, char *argv[],
       if (option_arg == next_option)
        options_left++;
     }
+    else if (strncmp(option, "-drop-file", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      options.drop_file = getStringCopy(option_arg);
+      if (option_arg == next_option)
+       options_left++;
+    }
     else if (strncmp(option, "-verbose", option_len) == 0)
     {
       options.verbose = TRUE;
index a19defa37acc5a7f7cc8cc07eeb958db0eafe973..d664eecc01df267ae8a86aff5f04e29cb43abbdc 100644 (file)
@@ -502,7 +502,7 @@ SDL_Surface *SDLCreateNativeSurface(int width, int height, int depth)
   return surface;
 }
 
-Bitmap *SDLGetBitmapFromSurface(SDL_Surface *surface)
+Bitmap *SDLGetBitmapFromSurface_WithMaskedColor(SDL_Surface *surface, int r, int g, int b)
 {
   int width  = surface->w;
   int height = surface->h;
@@ -522,11 +522,16 @@ Bitmap *SDLGetBitmapFromSurface(SDL_Surface *surface)
   if (!SDLHasAlpha(bitmap->surface_masked) &&
       !SDLHasColorKey(bitmap->surface_masked))
     SDL_SetColorKey(bitmap->surface_masked, SET_TRANSPARENT_PIXEL,
-                   SDL_MapRGB(bitmap->surface_masked->format, 0x00, 0x00, 0x00));
+                   SDL_MapRGB(bitmap->surface_masked->format, r, g, b));
 
   return bitmap;
 }
 
+Bitmap *SDLGetBitmapFromSurface(SDL_Surface *surface)
+{
+  return SDLGetBitmapFromSurface_WithMaskedColor(surface, 0x00, 0x00, 0x00);
+}
+
 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
 {
   if (program.headless)
index bbbaa7675919640c264161d7f86a9a467b35669c..1545f6865fbc80317f3e53af68c6a6cf8b27da39 100644 (file)
@@ -400,6 +400,7 @@ const char *SDLGetRendererName(void);
 boolean SDLSetNativeSurface(SDL_Surface **);
 SDL_Surface *SDLGetNativeSurface(SDL_Surface *);
 SDL_Surface *SDLCreateNativeSurface(int, int, int);
+Bitmap *SDLGetBitmapFromSurface_WithMaskedColor(SDL_Surface *, int, int, int);
 Bitmap *SDLGetBitmapFromSurface(SDL_Surface *);
 void SDLCreateBitmapTextures(Bitmap *);
 void SDLFreeBitmapTextures(Bitmap *);
index 4104a042e8d1d901404d03998280d938b26feb7b..fb63321bd2499bebec9a95c4dc63055f9bd6e30f 100644 (file)
@@ -797,26 +797,68 @@ char *getEditorSetupFilename(void)
   return filename;
 }
 
-char *getHelpAnimFilename(void)
+char *getFilenameFromCurrentLevelDirUpward(char *basename)
 {
+  // global variable "leveldir_current" must be modified in the loop below
+  LevelDirTree *leveldir_current_last = leveldir_current;
   static char *filename = NULL;
 
-  checked_free(filename);
+  // check for filename in path from current to topmost tree node
+
+  while (leveldir_current != NULL)
+  {
+    checked_free(filename);
+
+    filename = getPath2(getCurrentLevelDir(), basename);
+
+    if (fileExists(filename))
+      break;
+
+    leveldir_current = leveldir_current->node_parent;
+  }
 
-  filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
+  // restore global variable "leveldir_current" modified in above loop
+  leveldir_current = leveldir_current_last;
 
   return filename;
 }
 
-char *getHelpTextFilename(void)
+static char *getHelpFilename(char *basename)
 {
   static char *filename = NULL;
 
   checked_free(filename);
 
-  filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
+  // 1st try: look for help filename in current level set directory
+  filename = getPath2(getCurrentLevelDir(), basename);
+  if (fileExists(filename))
+    return filename;
 
-  return filename;
+  free(filename);
+
+  // 2nd try: look for help filename in configured graphics set directory
+  filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
+  if (fileExists(filename))
+    return filename;
+
+  free(filename);
+
+  // 3rd try: look for help filename in path from current to topmost level set directory
+  filename = getStringCopy(getFilenameFromCurrentLevelDirUpward(basename));
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;
+}
+
+char *getHelpAnimFilename(void)
+{
+  return getHelpFilename(HELPANIM_FILENAME);
+}
+
+char *getHelpTextFilename(void)
+{
+  return getHelpFilename(HELPTEXT_FILENAME);
 }
 
 static char *getLevelSetInfoBasename(int nr)
index c7a5330267f819b55ac46af771399ed01885d1d7..5692c9da39e56c5c035563ca569f860335c841db 100644 (file)
@@ -279,6 +279,7 @@ char *getSetupFilename(void);
 char *getDefaultSetupFilename(void);
 char *getPlatformSetupFilename(void);
 char *getEditorSetupFilename(void);
+char *getFilenameFromCurrentLevelDirUpward(char *);
 char *getHelpAnimFilename(void);
 char *getHelpTextFilename(void);
 char *getLevelSetInfoFilename(int);
index a1f009f77c8470f5d0b7e0fcbe85eb97db53ecd0..e77e6fe67bfede76ebdcf56b619f281172bea158 100644 (file)
@@ -1843,6 +1843,28 @@ void PushUserEvent(int code, int value1, int value2)
   SDL_PushEvent((SDL_Event *)&event);
 }
 
+void PushDropEvent(char *file)
+{
+  SDL_DropEvent event;
+
+  SDL_memset(&event, 0, sizeof(event));
+
+  event.type = SDL_DROPBEGIN;
+  event.file = NULL;
+
+  SDL_PushEvent((SDL_Event *)&event);
+
+  event.type = SDL_DROPFILE;
+  event.file = getStringCopy(file);
+
+  SDL_PushEvent((SDL_Event *)&event);
+
+  event.type = SDL_DROPCOMPLETE;
+  event.file = NULL;
+
+  SDL_PushEvent((SDL_Event *)&event);
+}
+
 boolean PendingEscapeKeyEvent(void)
 {
   if (PendingEvent())
index dc6d0f363d69f438b6e4c52cbafb2e8a5508eea2..85c26a5f34fd84c1b50939b92bf603fff89972cb 100644 (file)
@@ -1063,6 +1063,8 @@ struct OptionInfo
   char *identifier;
   char *level_nr;
 
+  char *drop_file;
+
   int display_nr;
 
   boolean mytapes;
@@ -1320,6 +1322,7 @@ struct SetupEditorInfo
   boolean el_by_type;
 
   boolean show_element_token;
+  boolean fast_game_start;
 
   boolean show_read_only_warning;
 
@@ -1528,7 +1531,7 @@ struct SetupInfo
   int bd_smooth_movements;             // not boolean -- can also be "MODE_AUTO"
   int bd_pushing_graphics;             // not boolean -- can also be "MODE_AUTO"
   int bd_up_down_graphics;             // not boolean -- can also be "MODE_AUTO"
-  int bd_skip_falling_sounds;          // not boolean -- can also be "MODE_AUTO"
+  int bd_falling_sounds;               // not boolean -- can also be "MODE_AUTO"
   int bd_palette_c64;
   int bd_palette_c64dtv;
   int bd_palette_atari;
@@ -2058,6 +2061,7 @@ KeyMod GetKeyModStateFromEvents(void);
 void StartTextInput(int, int, int, int);
 void StopTextInput(void);
 void PushUserEvent(int, int, int);
+void PushDropEvent(char *);
 boolean PendingEscapeKeyEvent(void);
 
 void InitJoysticks(void);
index 446a873b23dcfd9c137ed1792b1399f1525ed1d6..1db4848f0170a0964414be29d4b64c784d13a3b3 100644 (file)
@@ -589,8 +589,8 @@ static int DrawTextBufferExt(int x, int y, char *text_buffer, int base_font_nr,
     {
       if ((line[i] = *text_buffer++) == '\n')
       {
-       // in text areas, 'line_length' sized lines cause additional empty line
-       if (i == line_length && is_text_area)
+       // in text areas, do not skip newline after text ending at last column
+       if (i > 0 && i % line_length == 0 && is_text_area)
          text_buffer--;
 
        break;
index 0aa6347ac4106620b6c3d3aa851dd5b7f08e57ec..5a31fcbfdfb6ae598eed85bfe6c9ec6891baf21d 100644 (file)
@@ -121,7 +121,11 @@ static voidpf ZCALLBACK fopen64_file_func(ZIP_UNUSED voidpf opaque, const void *
 
     if ((filename != NULL) && (mode_fopen != NULL))
     {
-        file = fopen64((const char*)filename, mode_fopen);
+        const char *fd_prefix = "fd:";
+        if (strncmp(filename, fd_prefix, strlen(fd_prefix)) == 0)
+            file = fdopen(dup(atoi(&((const char*)filename)[strlen(fd_prefix)])), mode_fopen);
+        else
+            file = fopen64((const char*)filename, mode_fopen);
         return file_build_ioposix(file, (const char*)filename);
     }
     return file;
index 8ab7313dba3a77667264417e627d886e78cd1035..973d0c900785f36e6a2a9bddf2677f6c9d5a0379 100644 (file)
@@ -9062,6 +9062,7 @@ static void print_usage(void)
        "  -g, --graphics DIRECTORY         alternative graphics DIRECTORY\n"
        "  -s, --sounds DIRECTORY           alternative sounds DIRECTORY\n"
        "  -m, --music DIRECTORY            alternative music DIRECTORY\n"
+       "      --drop-file FILE             drop FILE into program window\n"
        "      --display NR                 open program window on display NR\n"
        "      --mytapes                    use private tapes for tape tests\n"
        "  -n, --network                    network multiplayer game\n"
index 309229ee71c2fdce7670c5e19f65792c4ae6e009..cbd7c1a4319ba5723d4c0f785a3d2521827162db 100644 (file)
@@ -34,8 +34,6 @@
 
 #define IMG_UNDEFINED                  (-1)
 #define IMG_EMPTY                      IMG_EMPTY_SPACE
-#define IMG_SP_EMPTY                   IMG_EMPTY_SPACE
-#define IMG_SP_EMPTY_SPACE             IMG_EMPTY_SPACE
 #define IMG_EXPLOSION                  IMG_DEFAULT_EXPLODING
 #define IMG_CHAR_START                 IMG_CHAR_SPACE
 #define IMG_STEEL_CHAR_START           IMG_STEEL_CHAR_SPACE
 #define EL_DF_WALL_START                       EL_DF_WOODEN_WALL_START
 #define EL_DF_WALL_END                         EL_DF_STEEL_WALL_END
 
-#define EL_DF_EMPTY                            (EL_DF_START2 + 304)
+#define EL_DF_EMPTY_SPACE                      (EL_DF_START2 + 304)
+#define EL_DF_EMPTY                            EL_DF_EMPTY_SPACE
 #define EL_DF_CELL                             (EL_DF_START2 + 305)
 #define EL_DF_MINE                             (EL_DF_START2 + 306)
 #define EL_DF_REFRACTOR                                (EL_DF_START2 + 307)
@@ -3023,7 +3022,7 @@ enum
 #define PROGRAM_VERSION_MAJOR          4
 #define PROGRAM_VERSION_MINOR          0
 #define PROGRAM_VERSION_PATCH          0
-#define PROGRAM_VERSION_EXTRA          "-test-1"
+#define PROGRAM_VERSION_EXTRA          "-test-2"
 
 #define PROGRAM_TITLE_STRING           "Rocks'n'Diamonds"
 #define PROGRAM_AUTHOR_STRING          "Holger Schemel"
index a31e6121f4dd28b255bf69843a11cff503a0ce54..1708a0e7235136e294f298de9953ba053aedb9d2 100644 (file)
@@ -7991,9 +7991,9 @@ static struct TokenInfo setup_info_engines[] =
   { TYPE_SWITCH,       &setup.bd_skip_hatching,        "Skip hatching player:"         },
   { TYPE_SWITCH,       &setup.bd_scroll_delay,         "Scroll Delay:"                 },
   { TYPE_YES_NO_AUTO,  &setup.bd_smooth_movements,     "Smooth Element Movement:"      },
-  { TYPE_YES_NO_AUTO,  &setup.bd_pushing_graphics,     "Use Player Pushing Graphics:"  },
-  { TYPE_YES_NO_AUTO,  &setup.bd_up_down_graphics,     "Use Player Up/Down Graphics:"  },
-  { TYPE_YES_NO_AUTO,  &setup.bd_skip_falling_sounds,  "Mute Double Falling Sounds:"   },
+  { TYPE_YES_NO_AUTO,  &setup.bd_pushing_graphics,     "Player Pushing Graphics:"      },
+  { TYPE_YES_NO_AUTO,  &setup.bd_up_down_graphics,     "Player Up/Down Graphics:"      },
+  { TYPE_YES_NO_AUTO,  &setup.bd_falling_sounds,       "Double Falling Sounds:"        },
   { TYPE_SWITCH,       &setup.bd_show_invisible_outbox,"Show invisible outbox:"        },
   { TYPE_ENTER_LIST,   &execSetupChoosePaletteC64,     "Color Palette (C64):"          },
   { TYPE_STRING,       &bd_palette_c64_text,           ""                              },
@@ -8038,7 +8038,7 @@ static struct TokenInfo setup_info_editor[] =
 #if 0
   { TYPE_SWITCH,       &setup.editor.el_headlines,     "Headlines:"                    },
 #endif
-  { TYPE_SWITCH, &setup.editor.el_user_defined,                "User defined element list:"    },
+  { TYPE_SWITCH,       &setup.editor.el_user_defined,  "User defined element list:"    },
   { TYPE_SWITCH,       &setup.editor.el_dynamic,       "Dynamic level elements:"       },
   { TYPE_EMPTY,                NULL,                           ""                              },
 #if 0
@@ -8046,9 +8046,10 @@ static struct TokenInfo setup_info_editor[] =
   { TYPE_SWITCH,       &setup.editor.el_by_type,       "Show elements by type:"        },
   { TYPE_EMPTY,                NULL,                           ""                              },
 #endif
-  { TYPE_SWITCH, &setup.editor.show_element_token,     "Show element token:"           },
+  { TYPE_SWITCH,       &setup.editor.show_element_token, "Show element token:"         },
+  { TYPE_SWITCH,       &setup.editor.fast_game_start,  "Fast game start:"              },
   { TYPE_EMPTY,                NULL,                           ""                              },
-  { TYPE_SWITCH, &setup.editor.show_read_only_warning, "Show read-only warning:"       },
+  { TYPE_SWITCH,       &setup.editor.show_read_only_warning, "Show read-only warning:" },
   { TYPE_EMPTY,                NULL,                           ""                              },
   { TYPE_LEAVE_MENU,   execSetupMain,                  "Back"                          },
 
index 1881220a079c35dc07baa93eb65611af9040a447..d56532d189d8143d7c4ea734a624d615aad68114 100644 (file)
@@ -11215,6 +11215,26 @@ void InitGraphicInfo_EM(void)
   }
 }
 
+static void CheckSaveEngineSnapshot_BD(boolean frame_max,
+                                      boolean player_moving,
+                                      boolean player_snapping)
+{
+  if (frame_max)
+  {
+    if (!local_player->was_waiting)
+    {
+      if (!CheckSaveEngineSnapshotToList())
+       return;
+
+      local_player->was_waiting = TRUE;
+    }
+  }
+  else if (player_moving || player_snapping)
+  {
+    local_player->was_waiting = FALSE;
+  }
+}
+
 static void CheckSaveEngineSnapshot_EM(int frame,
                                       boolean any_player_moving,
                                       boolean any_player_snapping,
@@ -11272,6 +11292,19 @@ static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
   }
 }
 
+boolean CheckSingleStepMode_BD(boolean frame_max,
+                               boolean player_moving,
+                               boolean player_snapping)
+{
+  if (tape.single_step && tape.recording && !tape.pausing)
+    if (frame_max && FrameCounter > 6)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_BD(frame_max, player_moving, player_snapping);
+
+  return tape.pausing;
+}
+
 boolean CheckSingleStepMode_EM(int frame,
                               boolean any_player_moving,
                               boolean any_player_snapping,