From: Holger Schemel Date: Fri, 5 Jul 2024 06:47:44 +0000 (+0200) Subject: added pushable/movable properties for BD bubble (bladder) X-Git-Tag: 4.4.0.0-test-3^0 X-Git-Url: https://git.artsoft.org/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster-next-major-release;hp=1f90d27f076aa0971ab263f1c6c7d387c6f9b9fd;p=rocksndiamonds.git added pushable/movable properties for BD bubble (bladder) --- diff --git a/build-projects/android/app/src/main/AndroidManifest.xml.tmpl b/build-projects/android/app/src/main/AndroidManifest.xml.tmpl index 8331804d..9a90444f 100644 --- a/build-projects/android/app/src/main/AndroidManifest.xml.tmpl +++ b/build-projects/android/app/src/main/AndroidManifest.xml.tmpl @@ -55,6 +55,11 @@ + + + + + diff --git a/build-projects/android/app/src/main/java/org/artsoft/rocksndiamonds/rocksndiamonds.java b/build-projects/android/app/src/main/java/org/artsoft/rocksndiamonds/rocksndiamonds.java index eeec2ea4..ee7aed02 100644 --- a/build-projects/android/app/src/main/java/org/artsoft/rocksndiamonds/rocksndiamonds.java +++ b/build-projects/android/app/src/main/java/org/artsoft/rocksndiamonds/rocksndiamonds.java @@ -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 }; + } + } + } +} diff --git a/docs/elements/bdx_bladder.txt b/docs/elements/bdx_bladder.txt deleted file mode 100644 index d4fc8349..00000000 --- a/docs/elements/bdx_bladder.txt +++ /dev/null @@ -1,2 +0,0 @@ -Bladders can be pushed around easily. They slowly climb up; if they touch a -voodoo doll, they convert into clocks. They can also pass slime. diff --git a/docs/elements/bdx_bladder_spender.txt b/docs/elements/bdx_bladder_spender.txt deleted file mode 100644 index 389a72c7..00000000 --- a/docs/elements/bdx_bladder_spender.txt +++ /dev/null @@ -1 +0,0 @@ -If there is space above it, the bladder spender turns to a bladder. diff --git a/docs/elements/bdx_bubble.txt b/docs/elements/bdx_bubble.txt new file mode 100644 index 00000000..20bab528 --- /dev/null +++ b/docs/elements/bdx_bubble.txt @@ -0,0 +1,2 @@ +Bubbles can be pushed around easily. They slowly move up; if they touch a +voodoo doll, they convert into clocks. They can also pass slime. diff --git a/docs/elements/bdx_diamond_key.txt b/docs/elements/bdx_diamond_key.txt index 61180d66..d48974d4 100644 --- a/docs/elements/bdx_diamond_key.txt +++ b/docs/elements/bdx_diamond_key.txt @@ -1 +1 @@ -If you get this key, all doors will convert into diamonds you can collect. +If you collect this key, all trapped diamonds will convert into diamonds. diff --git a/docs/elements/bdx_gravity_switch.txt b/docs/elements/bdx_gravity_switch.txt index 6e7d3076..bad7ed5e 100644 --- a/docs/elements/bdx_gravity_switch.txt +++ b/docs/elements/bdx_gravity_switch.txt @@ -1,3 +1,4 @@ -When this switch is active, you can use it to change the gravitation. -The direction from which you use it will determine the direction the +When this switch is active, you can push it to change the gravitation. +The direction into which you push it will determine the direction the gravitation will change to. +To activate the gravitation switch, the pot has to be stirred first. diff --git a/docs/elements/bdx_pot.txt b/docs/elements/bdx_pot.txt index a0239714..d85e6f9a 100644 --- a/docs/elements/bdx_pot.txt +++ b/docs/elements/bdx_pot.txt @@ -1,2 +1,3 @@ Stir the pot, and you will be able to use the gravitation switch. While you are stirring the pot, there is no gravitation at all. Press fire after using the pot. +Before using the pot, a certain number of skeletons have to be collected. diff --git a/docs/elements/bdx_trapped_bubble.txt b/docs/elements/bdx_trapped_bubble.txt new file mode 100644 index 00000000..34d8ae89 --- /dev/null +++ b/docs/elements/bdx_trapped_bubble.txt @@ -0,0 +1,2 @@ +If there is space above it, the trapped bubble turns into a bubble that can move +up and that can be pushed by the player. diff --git a/docs/elements/bdx_trapped_diamond.txt b/docs/elements/bdx_trapped_diamond.txt index 484c2cfd..4344730a 100644 --- a/docs/elements/bdx_trapped_diamond.txt +++ b/docs/elements/bdx_trapped_diamond.txt @@ -1 +1,2 @@ -This is an indestructible door with a diamond. +This is an indestructible door with a trapped diamond. +To free all trapped diamonds, collect a diamond key. diff --git a/graphics/gfx_classic/RocksBD.png b/graphics/gfx_classic/RocksBD.png index 639080c5..51dd2b6e 100644 Binary files a/graphics/gfx_classic/RocksBD.png and b/graphics/gfx_classic/RocksBD.png differ diff --git a/graphics/gfx_classic/RocksBD2.png b/graphics/gfx_classic/RocksBD2.png index 9d937f4c..434e6848 100644 Binary files a/graphics/gfx_classic/RocksBD2.png and b/graphics/gfx_classic/RocksBD2.png differ diff --git a/src/Android.mk b/src/Android.mk index a644d93a..f076793a 100644 --- a/src/Android.mk +++ b/src/Android.mk @@ -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 \ diff --git a/src/conf_gfx.c b/src/conf_gfx.c index 7262ac97..2bc30921 100644 --- a/src/conf_gfx.c +++ b/src/conf_gfx.c @@ -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" }, @@ -850,23 +853,31 @@ struct ConfigInfo image_config[] = { "bdx_trapped_diamond.ypos", "6" }, { "bdx_trapped_diamond.frames", "1" }, - { "bdx_nut", UNDEFINED_FILENAME }, - { "bdx_nut.clone_from", "nut" }, + { "bdx_nut", "RocksBD.png" }, + { "bdx_nut.xpos", "0" }, + { "bdx_nut.ypos", "13" }, + { "bdx_nut.frames", "1" }, + { "bdx_nut.breaking", "RocksBD.png" }, + { "bdx_nut.breaking.xpos", "0" }, + { "bdx_nut.breaking.ypos", "13" }, + { "bdx_nut.breaking.frames", "4" }, + { "bdx_nut.breaking.delay", "8" }, + { "bdx_nut.breaking.anim_mode", "linear" }, { "bdx_nut.falling.EDITOR", "RocksBD2.png" }, { "bdx_nut.falling.EDITOR.xpos", "5" }, { "bdx_nut.falling.EDITOR.ypos", "2" }, - { "bdx_bladder", "RocksBD.png" }, - { "bdx_bladder.xpos", "8" }, - { "bdx_bladder.ypos", "12" }, - { "bdx_bladder.frames", "4" }, - { "bdx_bladder.delay", "4" }, - { "bdx_bladder.anim_mode", "pingpong2" }, + { "bdx_bubble", "RocksBD.png" }, + { "bdx_bubble.xpos", "8" }, + { "bdx_bubble.ypos", "12" }, + { "bdx_bubble.frames", "4" }, + { "bdx_bubble.delay", "4" }, + { "bdx_bubble.anim_mode", "pingpong2" }, - { "bdx_bladder_spender", "RocksBD.png" }, - { "bdx_bladder_spender.xpos", "11" }, - { "bdx_bladder_spender.ypos", "10" }, - { "bdx_bladder_spender.frames", "1" }, + { "bdx_trapped_bubble", "RocksBD.png" }, + { "bdx_trapped_bubble.xpos", "11" }, + { "bdx_trapped_bubble.ypos", "10" }, + { "bdx_trapped_bubble.frames", "1" }, { "bdx_creature_switch", "RocksBD.png" }, { "bdx_creature_switch.xpos", "9" }, @@ -918,7 +929,7 @@ struct ConfigInfo image_config[] = { "bdx_conveyor_left.active.xpos", "0" }, { "bdx_conveyor_left.active.ypos", "3" }, { "bdx_conveyor_left.active.frames", "8" }, - { "bdx_conveyor_left.active.delay", "2" }, + { "bdx_conveyor_left.active.delay", "1" }, { "bdx_conveyor_right", "RocksDC.png" }, { "bdx_conveyor_right.xpos", "0" }, @@ -931,7 +942,7 @@ struct ConfigInfo image_config[] = { "bdx_conveyor_right.active.xpos", "0" }, { "bdx_conveyor_right.active.ypos", "3" }, { "bdx_conveyor_right.active.frames", "8" }, - { "bdx_conveyor_right.active.delay", "2" }, + { "bdx_conveyor_right.active.delay", "1" }, { "bdx_conveyor_right.active.anim_mode", "reverse" }, { "bdx_conveyor_switch", "RocksBD.png" }, @@ -1085,8 +1096,10 @@ struct ConfigInfo image_config[] = { "bdx_waiting_rock.EDITOR.xpos", "4" }, { "bdx_waiting_rock.EDITOR.ypos", "2" }, - { "bdx_chasing_rock", UNDEFINED_FILENAME }, - { "bdx_chasing_rock.clone_from", "bdx_rock" }, + { "bdx_chasing_rock", "RocksBD.png" }, + { "bdx_chasing_rock.xpos", "0" }, + { "bdx_chasing_rock.ypos", "4" }, + { "bdx_chasing_rock.frames", "1" }, { "bdx_ghost", "RocksBD.png" }, { "bdx_ghost.xpos", "0" }, @@ -1363,6 +1376,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 +5530,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 +6316,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" }, diff --git a/src/conf_snd.c b/src/conf_snd.c index 3b38b50d..103058e4 100644 --- a/src/conf_snd.c +++ b/src/conf_snd.c @@ -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" }, diff --git a/src/editor.c b/src/editor.c index 0ab57221..d2d020fa 100644 --- a/src/editor.c +++ b/src/editor.c @@ -640,7 +640,7 @@ enum GADGET_ID_BD_ACID_EATS_ELEMENT, GADGET_ID_BD_ACID_TURNS_TO_ELEMENT, GADGET_ID_BD_BITER_EATS_ELEMENT, - GADGET_ID_BD_BLADDER_CONVERTS_BY_ELEMENT, + GADGET_ID_BD_BUBBLE_CONVERTS_BY_ELEMENT, GADGET_ID_BD_NUT_CONTENT, GADGET_ID_BD_EXPANDING_WALL_LOOKS_LIKE, GADGET_ID_BD_SAND_LOOKS_LIKE, @@ -1354,7 +1354,7 @@ enum ED_DRAWING_ID_BD_ACID_EATS_ELEMENT, ED_DRAWING_ID_BD_ACID_TURNS_TO_ELEMENT, ED_DRAWING_ID_BD_BITER_EATS_ELEMENT, - ED_DRAWING_ID_BD_BLADDER_CONVERTS_BY_ELEMENT, + ED_DRAWING_ID_BD_BUBBLE_CONVERTS_BY_ELEMENT, ED_DRAWING_ID_BD_NUT_CONTENT, ED_DRAWING_ID_BD_EXPANDING_WALL_LOOKS_LIKE, ED_DRAWING_ID_BD_SAND_LOOKS_LIKE, @@ -5009,11 +5009,11 @@ static struct "Can eat:", NULL, NULL, NULL, "Eats this element when moving" }, { - ED_DRAWING_ID_BD_BLADDER_CONVERTS_BY_ELEMENT, + ED_DRAWING_ID_BD_BUBBLE_CONVERTS_BY_ELEMENT, ED_AREA_1X1_SETTINGS_XPOS(0), ED_AREA_1X1_SETTINGS_YPOS(1), ED_AREA_1X1_SETTINGS_XOFF, ED_AREA_1X1_SETTINGS_YOFF, - GADGET_ID_BD_BLADDER_CONVERTS_BY_ELEMENT, GADGET_ID_NONE, - &level.bd_bladder_converts_by_element, 1, 1, + GADGET_ID_BD_BUBBLE_CONVERTS_BY_ELEMENT, GADGET_ID_NONE, + &level.bd_bubble_converts_by_element, 1, 1, "Turns to clock by touching:", NULL, NULL, NULL, "Turns to clock by touching element" }, { @@ -5600,8 +5600,8 @@ static int editor_el_boulderdash_native[] = EL_BDX_EXPANDABLE_STEELWALL_ANY, EL_BDX_CREATURE_SWITCH, - EL_BDX_BLADDER, - EL_BDX_BLADDER_SPENDER, + EL_BDX_BUBBLE, + EL_BDX_TRAPPED_BUBBLE, EL_BDX_REPLICATOR, EL_BDX_REPLICATOR_SWITCH, @@ -5685,15 +5685,15 @@ static int editor_el_boulderdash_effects[] = EL_BDX_EXIT_OPEN, EL_BDX_INVISIBLE_EXIT_OPEN, - EL_BDX_BLADDER_1, - EL_BDX_BLADDER_2, - EL_BDX_BLADDER_3, - EL_BDX_BLADDER_4, + EL_BDX_BUBBLE_1, + EL_BDX_BUBBLE_2, + EL_BDX_BUBBLE_3, + EL_BDX_BUBBLE_4, - EL_BDX_BLADDER_5, - EL_BDX_BLADDER_6, - EL_BDX_BLADDER_7, - EL_BDX_BLADDER_8, + EL_BDX_BUBBLE_5, + EL_BDX_BUBBLE_6, + EL_BDX_BUBBLE_7, + EL_BDX_BUBBLE_8, EL_BDX_SAND_2, EL_BDX_COW_ENCLOSED_1, @@ -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()) @@ -12185,7 +12199,7 @@ static struct 0, 10 }, { EL_BDX_CREATURE_SWITCH, &level.bd_creatures_auto_turn_delay, TEXT_AUTO_TURN_DELAY }, { EL_BDX_GRAVITY_SWITCH, &level.bd_gravity_switch_delay, TEXT_GRAVITY_DELAY, - 1, 60 }, + 0, 60 }, { EL_EXTRA_TIME, &level.extra_time, TEXT_TIME_BONUS }, { EL_TIME_ORB_FULL, &level.time_orb_time, TEXT_TIME_BONUS }, { EL_GAME_OF_LIFE, &level.game_of_life[0], TEXT_GAME_OF_LIFE_1,0,8 }, @@ -12451,9 +12465,9 @@ static void DrawPropertiesConfig(void) { MapDrawingArea(ED_DRAWING_ID_BD_BITER_EATS_ELEMENT); } - else if (properties_element == EL_BDX_BLADDER) + else if (properties_element == EL_BDX_BUBBLE) { - MapDrawingArea(ED_DRAWING_ID_BD_BLADDER_CONVERTS_BY_ELEMENT); + MapDrawingArea(ED_DRAWING_ID_BD_BUBBLE_CONVERTS_BY_ELEMENT); } else if (properties_element == EL_YAMYAM || properties_element == EL_YAMYAM_LEFT || diff --git a/src/editor.h b/src/editor.h index 96fe6f29..8dd3e390 100644 --- a/src/editor.h +++ b/src/editor.h @@ -14,7 +14,7 @@ #include "main.h" -boolean isLevelEditorTestGame(void); +boolean isLevelEditorFastStart(void); void CreateLevelEditorGadgets(void); void FreeLevelEditorGadgets(void); diff --git a/src/events.c b/src/events.c index 04f69108..99b55fc8 100644 --- a/src/events.c +++ b/src/events.c @@ -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) diff --git a/src/files.c b/src/files.c index c620a531..589c34dc 100644 --- a/src/files.c +++ b/src/files.c @@ -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, @@ -935,9 +935,9 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] = }, { - EL_BDX_BLADDER, -1, + EL_BDX_BUBBLE, -1, TYPE_ELEMENT, CONF_VALUE_16_BIT(1), - &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL + &li.bd_bubble_converts_by_element, EL_BDX_VOODOO_DOLL }, { @@ -948,7 +948,7 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] = { EL_BDX_EXPANDABLE_WALL_ANY, -1, TYPE_ELEMENT, CONF_VALUE_16_BIT(1), - &li.bd_expanding_wall_looks_like, EL_BDX_WALL + &li.bd_expanding_wall_looks_like, EL_BDX_EXPANDABLE_WALL_ANY }, { @@ -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) @@ -4406,7 +4387,7 @@ static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level) cave->biter_delay_frame = level->bd_biter_move_delay; cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element); - cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element); + cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bubble_converts_by_element); cave->expanding_wall_changed = level->bd_change_expanding_wall; @@ -4579,7 +4560,7 @@ static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level) level->bd_biter_move_delay = cave->biter_delay_frame; level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat); - level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by); + level->bd_bubble_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by); level->bd_change_expanding_wall = cave->expanding_wall_changed; @@ -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; diff --git a/src/game.c b/src/game.c index 831ec901..a516155c 100644 --- a/src/game.c +++ b/src/game.c @@ -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) diff --git a/src/game_bd/bd_c64import.c b/src/game_bd/bd_c64import.c index b3e0310b..c89fdd02 100644 --- a/src/game_bd/bd_c64import.c +++ b/src/game_bd/bd_c64import.c @@ -573,6 +573,29 @@ GdEngine gd_cave_get_engine_from_string(const char *param) // // ============================================================================ +/* + * Checksum function for cave import routines. + * Used to recognize caves which need some hacks added, + * besides normal importing. + * @param data The input array of bytes + * @param length The size + * @return 16-bit checksum + */ + +static unsigned int checksum(const byte *data, int length) +{ + unsigned int a = 1, b = 0; + int i; + + for (i = 0; i < length; i++) + { + a = (a + data[i]) % 251; // the prime closest to (and less than) 256 + b = (b + a) % 251; + } + + return b * 256 + a; +} + /* take care of required diamonds values == 0 or > 100. in original bd, the counter was only two-digit. so bd3 cave f @@ -1983,6 +2006,46 @@ static int cave_copy_from_crdr_7(GdCave *cave, const byte *data, int remaining_b return 15 + 0x49 + index; } +// Deluxe Caves 3 hacks +static void deluxe_caves_3_add_specials(GdCave *cave, const int cavenum) +{ + cave->snap_element = O_EXPLODE_1; + cave->diagonal_movements = TRUE; + + switch (cavenum) + { + case 6: // cave f + cave->stone_bouncing_effect = O_BUTTER_1; + cave->diamond_falling_effect = O_EXPLODE_3; + break; + + case 7: // cave g + Warn("effects not supported"); + break; + + case 13: // cave l + Warn("effects not working perfectly"); + cave->stone_bouncing_effect = O_FIREFLY_1; + break; + + case 18: + cave->diamond_bouncing_effect = O_STONE; + break; + + default: + break; + } +} + +// Crazy Dream 7 hacks +static void crazy_dream_7_add_specials(GdCave *cave) +{ + if (strEqual(cave->name, "Crazy maze")) + cave->skeletons_needed_for_pot = 0; +} + +// This function adds some hardcoded elements to a Crazy Dream 9 cave. +// Crazy Dream 9 had some caves and random fills, which were not encoded in the cave data. static void crazy_dream_9_add_specials(GdCave *cave, const byte *buf, const int length) { byte checksum; @@ -2061,6 +2124,23 @@ static void crazy_dream_9_add_specials(GdCave *cave, const byte *buf, const int } } +// Masters Boulder hacks +static void masters_boulder_add_hack(GdCave *cave, const int cavenum) +{ + int i; + + switch (cavenum) + { + case 1: // cave b + for (i = 0; i < 5; i++) + cave->level_hatching_delay_time[i] = 3; // secs + break; + + default: + break; + } +} + // crazy light contruction kit static int cave_copy_from_crli(GdCave *cave, const byte *data, int remaining_bytes) { @@ -2421,6 +2501,23 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) buf += 12; length = encodedlength; + // check for hacks. + GdImportHack hack = GD_HACK_NONE; + unsigned int cs = checksum(buf, length); + + if (format == GD_FORMAT_PLC && length == 10240 && cs == 0xbdec) + hack = GD_HACK_CRDR_1; + if (format == GD_FORMAT_CRLI && length == 9895 && cs == 0xbc4e) + hack = GD_HACK_CRDR_9; + if (format == GD_FORMAT_BD1 && length == 1634 && cs == 0xf725) + hack = GD_HACK_DC1; + if (format == GD_FORMAT_BD1 && length == 1452 && cs == 0xb4a6) + hack = GD_HACK_DC3; + if (format == GD_FORMAT_CRDR_7 && length == 3759 && cs == 0x16b5) + hack = GD_HACK_CRDR_7; + if (format == GD_FORMAT_BD1 && length == 1241 && cs == 0x926f) + hack = GD_HACK_MB; + bufp = 0; cavenum = 0; @@ -2457,6 +2554,10 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) case GD_FORMAT_BD1_ATARI: case GD_FORMAT_DC1: cavelength = cave_copy_from_bd1(newcave, buf + bufp, length - bufp, format); + if (cavelength != -1 && hack == GD_HACK_DC3) + deluxe_caves_3_add_specials(newcave, cavenum); + if (cavelength != -1 && hack == GD_HACK_MB) + masters_boulder_add_hack(newcave, cavenum); break; case GD_FORMAT_BD2: case GD_FORMAT_BD2_ATARI: @@ -2483,6 +2584,8 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) case GD_FORMAT_PLC: // peter liepa construction kit case GD_FORMAT_PLC_ATARI: // peter liepa construction kit, atari version cavelength = cave_copy_from_plck(newcave, buf + bufp, length - bufp, format); + if (cavelength != -1 && hack == GD_HACK_CRDR_1) + newcave->diagonal_movements = TRUE; break; case GD_FORMAT_DLB: @@ -2515,11 +2618,13 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) case GD_FORMAT_CRDR_7: cavelength = cave_copy_from_crdr_7 (newcave, buf + bufp, length - bufp); + if (cavelength != -1 && hack == GD_HACK_CRDR_7) + crazy_dream_7_add_specials(newcave); break; case GD_FORMAT_CRDR_9: cavelength = cave_copy_from_crli (newcave, buf + bufp, length - bufp); - if (cavelength != -1) + if (cavelength != -1 && hack == GD_HACK_CRDR_9) crazy_dream_9_add_specials(newcave, buf, cavelength); break; @@ -2554,6 +2659,7 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) // try to detect if plc caves are in standard layout. // that is, caveset looks like an original, (4 cave,1 intermission)+ if (format == GD_FORMAT_PLC) + { // if no selection table stored by any2gdash if ((buf[2 + 0x1f0] != buf[2 + 0x1f1] - 1) || (buf[2 + 0x1f0] != 0x19 && buf[2 + 0x1f0] != 0x0e)) @@ -2583,6 +2689,7 @@ List *gd_caveset_import_from_buffer (const byte *buf, size_t length) cave->selectable = (n % 5) == 0; } } + } // try to give some names for the caves cavenum = 1; diff --git a/src/game_bd/bd_c64import.h b/src/game_bd/bd_c64import.h index 979b4254..65f91e16 100644 --- a/src/game_bd/bd_c64import.h +++ b/src/game_bd/bd_c64import.h @@ -41,6 +41,18 @@ typedef enum _gd_cavefile_format GD_FORMAT_FIRSTB, // first boulder } GdCavefileFormat; +// import hacks +typedef enum _gd_import_hack +{ + GD_HACK_NONE, // no hack + GD_HACK_CRDR_1, // crazy dream 1 + GD_HACK_CRDR_7, // crazy dream 7 + GD_HACK_CRDR_9, // crazy dream 9 + GD_HACK_DC1, // deluxe caves 1 + GD_HACK_DC3, // deluxe caves 3 + GD_HACK_MB, // masters boulder +} GdImportHack; + // engines typedef enum _gd_engine { diff --git a/src/game_bd/bd_cave.c b/src/game_bd/bd_cave.c index 6a22a637..7c958619 100644 --- a/src/game_bd/bd_cave.c +++ b/src/game_bd/bd_cave.c @@ -278,20 +278,23 @@ void gd_cave_init(void) for (i = 0; i < O_MAX; i++) { - char *key; + char *key_1 = getStringToUpper(gd_elements[i].filename); - key = getStringToUpper(gd_elements[i].filename); + if (hashtable_exists(name_to_element, key_1)) // hash value may be 0 + Warn("Name %s already used for element %x", key_1, i); - if (hashtable_exists(name_to_element, key)) // hash value may be 0 - Warn("Name %s already used for element %x", key, i); + hashtable_insert(name_to_element, key_1, INT_TO_PTR(i)); + // ^^^ do not free "key_1", as hash table needs it during the whole time! - hashtable_insert(name_to_element, key, INT_TO_PTR(i)); - // ^^^ do not free "key", as hash table needs it during the whole time! + char *key_2 = getStringCat2("SCANNED_", key_1); // new string - key = getStringCat2("SCANNED_", key); // new string + hashtable_insert(name_to_element, key_2, INT_TO_PTR(i)); + // once again, do not free "key_2" ^^^ - hashtable_insert(name_to_element, key, INT_TO_PTR(i)); - // once again, do not free "key" ^^^ + char *key_3 = getStringCat2("SCANN_", key_1); // new string + + hashtable_insert(name_to_element, key_3, INT_TO_PTR(i)); + // once again, do not free "key_3" ^^^ } // for compatibility with tim stridmann's memorydump->bdcff converter... .... ... @@ -1317,7 +1320,8 @@ void gd_cave_count_diamonds(GdCave *cave) by the caller. */ void gd_drawcave_game(const GdCave *cave, - int **element_buffer, int **last_element_buffer, int **gfx_buffer, + int **element_buffer, int **last_element_buffer, + int **drawing_buffer, int **last_drawing_buffer, int **gfx_buffer, boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox) { static int player_blinking = 0; @@ -1581,8 +1585,11 @@ void gd_drawcave_game(const GdCave *cave, draw += GD_NUM_OF_CELLS; // set to buffer, with caching - if (element_buffer[y][x] != map) - element_buffer[y][x] = map; + if (element_buffer[y][x] != actual) + element_buffer[y][x] = actual; + + if (drawing_buffer[y][x] != map) + drawing_buffer[y][x] = map; if (gfx_buffer[y][x] != draw) gfx_buffer[y][x] = draw | GD_REDRAW; @@ -1661,7 +1668,7 @@ void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b) for (y = 0; y < cave->h; y++) for (x = 0; x < cave->w; x++) { - *a += gd_elements[cave->map[y][x]].character; + *a += gd_elements[cave->map[y][x] & O_MASK].character; *b += *a; *a %= 65521; diff --git a/src/game_bd/bd_cave.h b/src/game_bd/bd_cave.h index 1a85776a..becee295 100644 --- a/src/game_bd/bd_cave.h +++ b/src/game_bd/bd_cave.h @@ -121,11 +121,14 @@ 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 E_P_CAN_MOVE, // can move E_P_CAN_FALL, // can fall + E_P_CAN_GROW, // can grow + E_P_CAN_DIG, // can dig E_P_FALLING, // falling E_P_GROWING, // growing (element birth) }; @@ -160,11 +163,14 @@ 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) #define P_CAN_MOVE (1 << E_P_CAN_MOVE) #define P_CAN_FALL (1 << E_P_CAN_FALL) +#define P_CAN_GROW (1 << E_P_CAN_GROW) +#define P_CAN_DIG (1 << E_P_CAN_DIG) #define P_FALLING (1 << E_P_FALLING) #define P_GROWING (1 << E_P_GROWING) @@ -702,7 +708,8 @@ GdScheduling gd_scheduling_from_string(const char *str); #define GD_REDRAW (1 << 10) void gd_drawcave_game(const GdCave *cave, - int **element_buffer, int **last_element_buffer, int **gfx_buffer, + int **element_buffer, int **last_element_buffer, + int **drawing_buffer, int **last_drawing_buffer, int **gfx_buffer, boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox); // function to copy a GdString diff --git a/src/game_bd/bd_cavedb.c b/src/game_bd/bd_cavedb.c index 45da297e..9c3b02a3 100644 --- a/src/game_bd/bd_cavedb.c +++ b/src/game_bd/bd_cavedb.c @@ -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 }, @@ -162,12 +162,12 @@ GdElements gd_elements[] = { O_NUT_F, N_("Nut, falling"), P_FALLING, "NUTf", 0, i_nut_f, i_nut_f, 358, 156 }, // has ckdelay { O_BLADDER_SPENDER, N_("Bladder Spender"), P_PUSHABLE, "BLADDERSPENDER", 0, 6, 6, 6, 20 }, // has ckdelay { O_INBOX, N_("Inbox"), 0, "INBOX", 'P', 35, 35, 22 }, - { O_H_EXPANDING_WALL, N_("Expanding wall, horizontal"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED, "HEXPANDINGWALL", 'x', i_h_expanding_wall, i_h_expanding_wall, 5, 111 }, // has ckdelay - { O_V_EXPANDING_WALL, N_("Expanding wall, vertical"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED, "VEXPANDINGWALL", 'v', i_v_expanding_wall, i_v_expanding_wall, 5, 111 }, // has ckdelay - { O_EXPANDING_WALL, N_("Expanding wall"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED, "EXPANDINGWALL", 'e', i_expanding_wall, i_expanding_wall, 5, 111 }, // has ckdelay - { O_H_EXPANDING_STEEL_WALL, N_("Expanding steel wall, horizontal"), P_NON_EXPLODABLE, "HEXPANDINGSTEELWALL", 0, i_h_expanding_steel_wall, i_h_expanding_steel_wall, 4, 111 }, // has ckdelay - { O_V_EXPANDING_STEEL_WALL, N_("Expanding steel wall, vertical"), P_NON_EXPLODABLE, "VEXPANDINGSTEELWALL", 0, i_v_expanding_steel_wall, i_v_expanding_steel_wall, 4, 111 }, // has ckdelay - { O_EXPANDING_STEEL_WALL, N_("Expanding steel wall"), P_NON_EXPLODABLE, "EXPANDINGSTEELWALL", 0, i_expanding_steel_wall, i_expanding_steel_wall, 4, 111 }, // has ckdelay + { O_H_EXPANDING_WALL, N_("Expanding wall, horizontal"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED | P_CAN_GROW, "HEXPANDINGWALL", 'x', i_h_expanding_wall, i_h_expanding_wall, 5, 111 }, // has ckdelay + { O_V_EXPANDING_WALL, N_("Expanding wall, vertical"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED | P_CAN_GROW, "VEXPANDINGWALL", 'v', i_v_expanding_wall, i_v_expanding_wall, 5, 111 }, // has ckdelay + { O_EXPANDING_WALL, N_("Expanding wall"), P_VISUAL_EFFECT | P_CAN_BE_HAMMERED | P_CAN_GROW, "EXPANDINGWALL", 'e', i_expanding_wall, i_expanding_wall, 5, 111 }, // has ckdelay + { O_H_EXPANDING_STEEL_WALL, N_("Expanding steel wall, horizontal"), P_NON_EXPLODABLE | P_CAN_GROW, "HEXPANDINGSTEELWALL", 0, i_h_expanding_steel_wall, i_h_expanding_steel_wall, 4, 111 }, // has ckdelay + { O_V_EXPANDING_STEEL_WALL, N_("Expanding steel wall, vertical"), P_NON_EXPLODABLE | P_CAN_GROW, "VEXPANDINGSTEELWALL", 0, i_v_expanding_steel_wall, i_v_expanding_steel_wall, 4, 111 }, // has ckdelay + { O_EXPANDING_STEEL_WALL, N_("Expanding steel wall"), P_NON_EXPLODABLE | P_CAN_GROW, "EXPANDINGSTEELWALL", 0, i_expanding_steel_wall, i_expanding_steel_wall, 4, 111 }, // has ckdelay { O_EXPANDING_WALL_SWITCH, N_("Expanding wall switch"), 0, "EXPANDINGWALLSWITCH", 0, 40, 40, 40 }, { O_CREATURE_SWITCH, N_("Creature direction switch"), 0, "FIREFLYBUTTERFLYSWITCH", 0, 18, 18, 18 }, { O_BITER_SWITCH, N_("Biter switch"), 0, "BITERSWITCH", 0, 12, 12, 12 }, @@ -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 }, @@ -231,8 +231,8 @@ GdElements gd_elements[] = { O_WALLED_KEY_2, N_("Walled key 2"), P_CAN_BE_HAMMERED, "WALLED_KEY2", 0, i_walled_key_2, i_walled_key_2, 5 }, { O_WALLED_KEY_3, N_("Walled key 3"), P_CAN_BE_HAMMERED, "WALLED_KEY3", 0, i_walled_key_3, i_walled_key_3, 5 }, - { O_AMOEBA, N_("Amoeba"), P_BLOWS_UP_FLIES | P_CAN_MOVE, "AMOEBA", 'a', 192, -192, -192, 260 }, // has ckdelay - { O_AMOEBA_2, N_("Amoeba 2"), P_BLOWS_UP_FLIES | P_CAN_MOVE | P_VISUAL_EFFECT, "AMOEBA2", 0, 296, -296, -296, 260 }, // has ckdelay + { O_AMOEBA, N_("Amoeba"), P_BLOWS_UP_FLIES | P_CAN_GROW | P_CAN_DIG, "AMOEBA", 'a', 192, -192, -192, 260 }, // has ckdelay + { O_AMOEBA_2, N_("Amoeba 2"), P_BLOWS_UP_FLIES | P_CAN_GROW | P_CAN_DIG | P_VISUAL_EFFECT, "AMOEBA2", 0, 296, -296, -296, 260 }, // has ckdelay { O_REPLICATOR, N_("Replicator"), P_NON_EXPLODABLE, "REPLICATOR", 0, 304, -304, -304, 210 }, // has ckdelay { O_CONVEYOR_LEFT, N_("Conveyor belt (left)"), P_NON_EXPLODABLE, "CONVEYORLEFT", 0, i_conveyor_left, -328, -328, 256 }, // has ckdelay { O_CONVEYOR_RIGHT, N_("Conveyor belt (right)"), P_NON_EXPLODABLE, "CONVEYORRIGHT", 0, i_conveyor_right, -320, -320 }, @@ -240,15 +240,15 @@ GdElements gd_elements[] = { O_SWEET, N_("Sweet"), P_COLLECTIBLE, "SWEET", 0, 8, 8, 8 }, { O_VOODOO, N_("Voodoo doll"), P_BLOWS_UP_FLIES, "DUMMY", 'F', 7, 7, 7 }, { O_SLIME, N_("Slime"), 0, "SLIME", 's', 200, -200, -200, 211 }, // has ckdelay - { O_BLADDER, N_("Bladder"), 0, "BLADDER", 0, 176, -176, -176, 267 }, // has ckdelay - { O_BLADDER_1, N_("Bladder (1)"), 0, "BLADDERd1", 0, 176, -176, -176 }, - { O_BLADDER_2, N_("Bladder (2)"), 0, "BLADDERd2", 0, 176, -176, -176 }, - { O_BLADDER_3, N_("Bladder (3)"), 0, "BLADDERd3", 0, 176, -176, -176 }, - { O_BLADDER_4, N_("Bladder (4)"), 0, "BLADDERd4", 0, 176, -176, -176 }, - { O_BLADDER_5, N_("Bladder (5)"), 0, "BLADDERd5", 0, 176, -176, -176 }, - { O_BLADDER_6, N_("Bladder (6)"), 0, "BLADDERd6", 0, 176, -176, -176 }, - { O_BLADDER_7, N_("Bladder (7)"), 0, "BLADDERd7", 0, 176, -176, -176 }, - { O_BLADDER_8, N_("Bladder (8)"), 0, "BLADDERd8", 0, 176, -176, -176 }, + { O_BLADDER, N_("Bladder"), P_PUSHABLE | P_CAN_MOVE, "BLADDER", 0, 176, -176, -176, 267 }, // has ckdelay + { O_BLADDER_1, N_("Bladder (1)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd1", 0, 176, -176, -176 }, + { O_BLADDER_2, N_("Bladder (2)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd2", 0, 176, -176, -176 }, + { O_BLADDER_3, N_("Bladder (3)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd3", 0, 176, -176, -176 }, + { O_BLADDER_4, N_("Bladder (4)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd4", 0, 176, -176, -176 }, + { O_BLADDER_5, N_("Bladder (5)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd5", 0, 176, -176, -176 }, + { O_BLADDER_6, N_("Bladder (6)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd6", 0, 176, -176, -176 }, + { O_BLADDER_7, N_("Bladder (7)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd7", 0, 176, -176, -176 }, + { O_BLADDER_8, N_("Bladder (8)"), P_PUSHABLE | P_CAN_MOVE, "BLADDERd8", 0, 176, -176, -176 }, { O_WAITING_STONE, N_("Waiting stone"), P_SLOPED | P_PUSHABLE, "WAITINGBOULDER", 0, i_waiting_stone, i_waiting_stone, 1, 176 }, // has ckdelay { O_CHASING_STONE, N_("Chasing stone"), P_SLOPED | P_CAN_MOVE | P_PUSHABLE, "CHASINGBOULDER", 0, 17, 17, 17, 269 }, // has ckdelay @@ -285,21 +285,21 @@ GdElements gd_elements[] = { O_PRE_PL_1, N_("Player birth (1)"), P_GROWING, "GUYBIRTH1", 0, 32, 32, 32 }, { O_PRE_PL_2, N_("Player birth (2)"), P_GROWING, "GUYBIRTH2", 0, 33, 33, 33 }, { O_PRE_PL_3, N_("Player birth (3)"), P_GROWING, "GUYBIRTH3", 0, 34, 34, 34 }, - { O_PLAYER, N_("Player"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUY", 0, i_player, i_player, 35, 32 }, // has ckdelay - { O_PLAYER_BOMB, N_("Player with bomb"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUYBOMB", 0, 42, 42, 42, 25 }, // has ckdelay - { O_PLAYER_ROCKET_LAUNCHER, N_("Player with rocket launcher"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUYROCKETLAUNCER", 0, 369, 369, 369, 25 }, // has ckdelay + { O_PLAYER, N_("Player"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER | P_CAN_DIG, "GUY", 0, i_player, i_player, 35, 32 }, // has ckdelay + { O_PLAYER_BOMB, N_("Player with bomb"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER | P_CAN_DIG, "GUYBOMB", 0, 42, 42, 42, 25 }, // has ckdelay + { O_PLAYER_ROCKET_LAUNCHER, N_("Player with rocket launcher"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER | P_CAN_DIG, "GUYROCKETLAUNCER", 0, 369, 369, 369, 25 }, // has ckdelay { O_PLAYER_GLUED, N_("Glued player"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT, "GUYGLUED", 0, i_player_glued, i_player_glued, 35 }, // is not a real player! so active x, y will not find it. no P_PLAYER bit! { O_PLAYER_STIRRING, N_("Player stirring"), P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUYSTIRRING", 0, 256, -256, -256 }, { O_ROCKET_LAUNCHER, N_("Rocket launcher"), 0, "ROCKET_LAUNCHER", 0, 368, 368, 368 }, - { O_ROCKET_1, N_("Rocket (right)"), 0, "ROCKETr", 0, 364, 364, 364, 40 }, // has ckdelay - { O_ROCKET_2, N_("Rocket (up)"), 0, "ROCKETu", 0, 365, 365, 365, 40 }, // has ckdelay - { O_ROCKET_3, N_("Rocket (left)"), 0, "ROCKETl", 0, 366, 366, 366, 40 }, // has ckdelay - { O_ROCKET_4, N_("Rocket (down)"), 0, "ROCKETd", 0, 367, 367, 367, 40 }, // has ckdelay - - { O_BOMB, N_("Bomb"), P_COLLECTIBLE, "BOMB", 0, 48, 48, 48 }, - { O_BOMB_TICK_1, N_("Ticking bomb (1)"), P_EXPLOSION_FIRST_STAGE, "IGNITEDBOMB1", 0, 49, 49, 49 }, - { O_BOMB_TICK_2, N_("Ticking bomb (2)"), 0, "IGNITEDBOMB2", 0, 50, 50, 50 }, + { O_ROCKET_1, N_("Rocket (right)"), P_CAN_MOVE, "ROCKETr", 0, 364, 364, 364, 40 }, // has ckdelay + { O_ROCKET_2, N_("Rocket (up)"), P_CAN_MOVE, "ROCKETu", 0, 365, 365, 365, 40 }, // has ckdelay + { O_ROCKET_3, N_("Rocket (left)"), P_CAN_MOVE, "ROCKETl", 0, 366, 366, 366, 40 }, // has ckdelay + { O_ROCKET_4, N_("Rocket (down)"), P_CAN_MOVE, "ROCKETd", 0, 367, 367, 367, 40 }, // has ckdelay + + { O_BOMB, N_("Bomb"), P_COLLECTIBLE | P_CAN_MOVE, "BOMB", 0, 48, 48, 48 }, + { O_BOMB_TICK_1, N_("Ticking bomb (1)"), P_EXPLOSION_FIRST_STAGE | P_CAN_DIG, "IGNITEDBOMB1", 0, 49, 49, 49 }, + { O_BOMB_TICK_2, N_("Ticking bomb (2)"), P_CAN_DIG, "IGNITEDBOMB2", 0, 50, 50, 50 }, { O_BOMB_TICK_3, N_("Ticking bomb (3)"), 0, "IGNITEDBOMB3", 0, 51, 51, 51 }, { O_BOMB_TICK_4, N_("Ticking bomb (4)"), 0, "IGNITEDBOMB4", 0, 52, 52, 52 }, { O_BOMB_TICK_5, N_("Ticking bomb (5)"), 0, "IGNITEDBOMB5", 0, 53, 53, 53 }, @@ -353,8 +353,8 @@ GdElements gd_elements[] = { O_NUT_EXPL_3, N_("Nut explosion (3)"), P_SLOPED | P_EXPLOSION, "NUTEXPLOSION3", 0, 362, 362, 362, 280 }, // has ckdelay { O_NUT_EXPL_4, N_("Nut explosion (4)"), P_SLOPED | P_EXPLOSION, "NUTEXPLOSION4", 0, 363, 363, 363, 280 }, // has ckdelay - { O_PLAYER_PNEUMATIC_LEFT, NULL /* Player using hammer, left */, P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUYHAMMERl", 0, 265, 265, 265 }, - { O_PLAYER_PNEUMATIC_RIGHT, NULL /* Player using hammer, right */, P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER, "GUYHAMMERr", 0, 268, 268, 268 }, + { O_PLAYER_PNEUMATIC_LEFT, NULL /* Player using hammer, left */, P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER | P_CAN_DIG, "GUYHAMMERl", 0, 265, 265, 265 }, + { O_PLAYER_PNEUMATIC_RIGHT, NULL /* Player using hammer, right */, P_BLOWS_UP_FLIES | P_EXPLODES_BY_HIT | P_PLAYER | P_CAN_DIG, "GUYHAMMERr", 0, 268, 268, 268 }, { O_PNEUMATIC_ACTIVE_LEFT, NULL /* Active hammer, left */, 0, "HAMMERACTIVEl", 0, 264, 264, 264 }, { O_PNEUMATIC_ACTIVE_RIGHT, NULL /* Active hammer, right */, 0, "HAMMERACTIVEr", 0, 269, 269, 269 }, @@ -372,10 +372,10 @@ GdElements gd_elements[] = { O_OUTBOX_CLOSED, NULL, 0, NULL, 0, 22, 22, 22 }, { O_OUTBOX_OPEN, NULL, 0, NULL, 0, 23, 23, 23 }, { O_COVERED, NULL, 0, NULL, 0, 128, -128, -128 }, - { O_PLAYER_LEFT, NULL, P_PLAYER, NULL, 0, 232, -232, -232 }, - { O_PLAYER_RIGHT, NULL, P_PLAYER, NULL, 0, 240, -240, -240 }, - { O_PLAYER_UP, NULL, P_PLAYER, NULL, 0, 376, -376, -376 }, - { O_PLAYER_DOWN, NULL, P_PLAYER, NULL, 0, 384, -384, -384 }, + { O_PLAYER_LEFT, NULL, P_PLAYER | P_CAN_DIG, NULL, 0, 232, -232, -232 }, + { O_PLAYER_RIGHT, NULL, P_PLAYER | P_CAN_DIG, NULL, 0, 240, -240, -240 }, + { O_PLAYER_UP, NULL, P_PLAYER | P_CAN_DIG, NULL, 0, 376, -376, -376 }, + { O_PLAYER_DOWN, NULL, P_PLAYER | P_CAN_DIG, NULL, 0, 384, -384, -384 }, { O_PLAYER_TAP, NULL, P_PLAYER, NULL, 0, 216, -216, -216 }, { O_PLAYER_BLINK, NULL, P_PLAYER, NULL, 0, 208, -208, -208 }, { O_PLAYER_TAP_BLINK, NULL, P_PLAYER, NULL, 0, 224, -224, -224 }, diff --git a/src/game_bd/bd_caveengine.c b/src/game_bd/bd_caveengine.c index ee1b4ee2..4c3b7b0a 100644 --- a/src/game_bd/bd_caveengine.c +++ b/src/game_bd/bd_caveengine.c @@ -43,7 +43,8 @@ static const GdDirection ccw_eighth[] = GD_MV_RIGHT, GD_MV_DOWN_RIGHT, GD_MV_DOWN, - GD_MV_DOWN_LEFT + GD_MV_DOWN_LEFT, + GD_MV_LEFT }; static const GdDirection ccw_fourth[] = @@ -56,8 +57,7 @@ static const GdDirection ccw_fourth[] = GD_MV_RIGHT, GD_MV_DOWN_RIGHT, GD_MV_DOWN, - GD_MV_DOWN_LEFT, - GD_MV_LEFT + GD_MV_DOWN_LEFT }; static const GdDirection cw_eighth[] = @@ -132,11 +132,23 @@ static inline boolean el_can_fall(const int element) return (gd_elements[element & O_MASK].properties & P_CAN_FALL) != 0; } +// returns true if the element is diggable +static inline boolean el_diggable(const int element) +{ + return (gd_elements[element & O_MASK].properties & P_DIGGABLE) != 0; +} + +// returns true if the element can smash the player +static inline boolean el_can_smash_player(const int element) +{ + return (el_can_fall(element) && !el_diggable(element)); +} + // play diamond or stone sound of given 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 +555,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 +567,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 @@ -596,6 +622,19 @@ static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y, static inline void move(GdCave *cave, const int x, const int y, const GdDirection dir, const GdElement e) { + // falling/flying game elements at wrap-around cave position should not kill player instantly + if ((x + gd_dx[dir] == cave->w && dir == GD_MV_RIGHT) || + (y + gd_dy[dir] == cave->h && dir == GD_MV_DOWN)) + { + // cave width/height out of bounds, but due to wrap-around it's the first column/row again + if (el_can_smash_player(get(cave, x, y))) + { + store(cave, x, y, e); // change to falling element ... + + return; // ... but do not move element + } + } + store_dir(cave, x, y, dir, e); store(cave, x, y, O_SPACE); } @@ -1570,6 +1609,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) @@ -1696,7 +1738,7 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, } // add the ckdelay correction value for every element seen. - cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay; + cave->ckdelay += gd_elements[get(cave, x, y) & O_MASK].ckdelay; switch (get(cave, x, y)) { @@ -1813,8 +1855,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, player_move == GD_MV_DOWN)) { gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y); + // (use 1 instead of 0 for immediate gravitation change) cave->gravity_will_change = - cave->gravity_change_time * cave->timing_factor; + MAX(1, cave->gravity_change_time * cave->timing_factor); cave->gravity_next_direction = player_move; cave->gravity_switch_active = FALSE; } @@ -1835,15 +1878,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; @@ -1909,8 +1962,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, player_move == GD_MV_DOWN)) { gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y); + // (use 1 instead of 0 for immediate gravitation change) cave->gravity_will_change = - cave->gravity_change_time * cave->timing_factor; + MAX(1, cave->gravity_change_time * cave->timing_factor); cave->gravity_next_direction = player_move; cave->gravity_switch_active = FALSE; } @@ -3291,8 +3345,27 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, if (!is_scanned_dir(cave, x, y, GD_MV_UP) && is_space_dir(cave, x, y, dir[GD_MV_UP])) { - store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move - store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space. + // to allow smooth movement of game elements on conveyor belts, + // the moving direction set by "store_dir()" must be set to the + // direction the game element on the conveyor belt is moving; + // without smooth movement, the following lines would do it: + // + // store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move + // store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space. + + int tile = get_dir(cave, x, y, GD_MV_UP); + int move_dir = (left ? GD_MV_LEFT : GD_MV_RIGHT); // top side direction + + // raw values without range correction + int raw_x = x + gd_dx[GD_MV_UP]; + int raw_y = y + gd_dy[GD_MV_UP]; + + // final values with range correction + int old_x = getx(cave, raw_x, raw_y); + int old_y = gety(cave, raw_x, raw_y); + + store_dir(cave, x, y, GD_MV_UP, O_SPACE); // place a space ... + store_dir(cave, old_x, old_y, move_dir, tile); // and move element. } } @@ -3305,8 +3378,27 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) && is_space_dir(cave, x, y, dir[GD_MV_DOWN])) { - store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move - store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear. + // to allow smooth movement of game elements on conveyor belts, + // the moving direction set by "store_dir()" must be set to the + // direction the game element on the conveyor belt is moving; + // without smooth movement, the following lines would do it: + // + // store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move + // store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear. + + int tile = get_dir(cave, x, y, GD_MV_DOWN); + int move_dir = (left ? GD_MV_RIGHT : GD_MV_LEFT); // bottom side direction + + // raw values without range correction + int raw_x = x + gd_dx[GD_MV_DOWN]; + int raw_y = y + gd_dy[GD_MV_DOWN]; + + // final values with range correction + int old_x = getx(cave, raw_x, raw_y); + int old_y = gety(cave, raw_x, raw_y); + + store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // place a space ... + store_dir(cave, old_x, old_y, move_dir, tile); // and move element. } } } @@ -3720,7 +3812,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, if (cave->gravity_will_change == 0) { cave->gravity = cave->gravity_next_direction; - gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound + + // takes precedence over amoeba and magic wall sound + gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); } } @@ -3910,7 +4004,7 @@ void set_initial_cave_speed(GdCave *cave) for (x = 0; x < cave->w; x++) { // add the ckdelay correction value for every element seen. - cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay; + cave->ckdelay += gd_elements[get(cave, x, y) & O_MASK].ckdelay; } } diff --git a/src/game_bd/bd_elements.h b/src/game_bd/bd_elements.h index dd36b1ee..9beef2fe 100644 --- a/src/game_bd/bd_elements.h +++ b/src/game_bd/bd_elements.h @@ -317,12 +317,11 @@ typedef enum _element O_MAX_ALL, - SCANNED = 0x100, - COVERED = 0x200, - SKIPPED = 0x400, + SCANNED = 0x400, + COVERED = 0x800, // binary AND this to elements to get rid of properties above. - O_MASK = ~(SCANNED | COVERED | SKIPPED) + O_MASK = ~(SCANNED | COVERED) } GdElement; typedef enum _sound diff --git a/src/game_bd/bd_gameplay.c b/src/game_bd/bd_gameplay.c index 12eb53bc..b157bdf0 100644 --- a/src/game_bd/bd_gameplay.c +++ b/src/game_bd/bd_gameplay.c @@ -26,8 +26,14 @@ 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->drawing_buffer) + gd_cave_map_free(game->drawing_buffer); + if (game->last_drawing_buffer) + gd_cave_map_free(game->last_drawing_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 +95,25 @@ 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 drawing buffer + if (game->drawing_buffer) + gd_cave_map_free(game->drawing_buffer); + game->drawing_buffer = NULL; + + // delete last drawing buffer + if (game->last_drawing_buffer) + gd_cave_map_free(game->last_drawing_buffer); + game->last_drawing_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 +143,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 +159,33 @@ 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 drawing buffer + game->drawing_buffer = 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->drawing_buffer[y][x] = O_NONE; + + // create new last drawing buffer + game->last_drawing_buffer = gd_cave_map_new(game->cave, int); + + for (y = 0; y < game->cave->h; y++) + for (x = 0; x < game->cave->w; x++) + game->last_drawing_buffer[y][x] = O_NONE; + + // 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_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 +232,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; @@ -364,12 +415,15 @@ static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean // normally nothing happes. but if we iterate, this might change. return_state = GD_GAME_NOTHING; - // if allowing cave movements, add elapsed time to timer. and then we can check what to do. + // if allowing cave movements, ... if (allow_iterate) + { + // ... add elapsed time to timer. and then we can check what to do game->milliseconds_game += millisecs_elapsed; - // increment cycle (frame) counter for the current cave iteration - game->itercycle++; + // ... increment cycle (frame) counter for the current cave iteration + game->itercycle++; + } if (game->milliseconds_game >= cavespeed) { @@ -383,22 +437,29 @@ 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->last_drawing_buffer[y][x] = game->drawing_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; @@ -566,8 +627,9 @@ static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean // always render the cave to the gfx buffer; // however it may do nothing if animcycle was not changed. - if (game->element_buffer && game->gfx_buffer) - gd_drawcave_game(game->cave, game->element_buffer, game->last_element_buffer, game->gfx_buffer, + if (game->element_buffer && game->drawing_buffer && game->gfx_buffer) + gd_drawcave_game(game->cave, game->element_buffer, game->last_element_buffer, + game->drawing_buffer, game->last_drawing_buffer, game->gfx_buffer, game->bonus_life_flash != 0, game->animcycle, setup.bd_show_invisible_outbox); game->state_counter = counter_next; @@ -621,7 +683,7 @@ void play_game_func(GdGame *game, int action) // if drawcave was before scrolling, it would draw, scroll would invalidate, // and then it should be drawn again // only do the drawing if the cave already exists. - if (game->cave && game->element_buffer && game->gfx_buffer) + if (game->cave && game->element_buffer && game->drawing_buffer && game->gfx_buffer) { // if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz. // do the scrolling. scroll exactly, if player is not yet alive diff --git a/src/game_bd/bd_gameplay.h b/src/game_bd/bd_gameplay.h index aa9a48de..fbeeecfc 100644 --- a/src/game_bd/bd_gameplay.h +++ b/src/game_bd/bd_gameplay.h @@ -77,15 +77,20 @@ 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 **drawing_buffer; + int **last_drawing_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 +117,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 diff --git a/src/game_bd/bd_graphics.c b/src/game_bd/bd_graphics.c index aa091ba1..793987e2 100644 --- a/src/game_bd/bd_graphics.c +++ b/src/game_bd/bd_graphics.c @@ -334,6 +334,11 @@ boolean gd_scroll(GdGame *game, boolean exact_scroll, boolean immediate) scroll_speed_last = -1; // check if active player is visible at the moment. + // but only if scrolling happened at all! + if (!changed) + return FALSE; + + // check if active player is outside drawing area. if yes, we should wait for scrolling. return player_out_of_window(game, player_x, player_y); } @@ -363,6 +368,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 +476,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 +548,14 @@ static inline boolean el_player(const int element) return (gd_elements[element & O_MASK].properties & P_PLAYER) != 0; } +#if 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; +} +#endif + // returns true if the element is diggable static inline boolean el_diggable(const int element) { @@ -558,6 +582,24 @@ static inline boolean el_can_move(const int element) return (gd_elements[element & O_MASK].properties & P_CAN_MOVE) != 0; } +// returns true if the element can fall +static inline boolean el_can_fall(const int element) +{ + return (gd_elements[element & O_MASK].properties & P_CAN_FALL) != 0; +} + +// returns true if the element can grow +static inline boolean el_can_grow(const int element) +{ + return (gd_elements[element & O_MASK].properties & P_CAN_GROW) != 0; +} + +// returns true if the element can dig +static inline boolean el_can_dig(const int element) +{ + return (gd_elements[element & O_MASK].properties & P_CAN_DIG) != 0; +} + // returns true if the element can fall static inline boolean el_falling(const int element) { @@ -576,6 +618,22 @@ static inline boolean el_explosion(const int element) return (gd_elements[element & O_MASK].properties & P_EXPLOSION) != 0; } +static inline boolean el_smooth_movable(const int element) +{ + // special case of non-moving player + if (element == O_PLAYER_PNEUMATIC_LEFT || + element == O_PLAYER_PNEUMATIC_RIGHT) + return FALSE; + + return (el_player(element) || + el_can_move(element) || + el_can_fall(element) || + el_can_grow(element) || + el_can_dig(element) || + el_falling(element) || + el_pushable(element)); +} + static void gd_drawcave_tile(Bitmap *dest, GdGame *game, int x, int y, boolean draw_masked) { void (*blit_bitmap)(Bitmap *, Bitmap *, int, int, int, int, int, int) = @@ -583,51 +641,102 @@ 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 draw = game->drawing_buffer[y][x]; + int tile_last = game->last_element_buffer[y][x]; + int draw_last = game->last_drawing_buffer[y][x]; + int tile_from = O_NONE; // source element if element is moving (will be set later) + int draw_from = O_NONE; // source element if element is moving (will be set later) + int tile_to = tile; // target element if element is moving + int draw_to = draw; // 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) + 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); + int new_dir_to = game->dir_buffer_to[new_y][new_x]; + + tile_from = game->element_buffer[new_y][new_x]; + draw_from = game->drawing_buffer[new_y][new_x]; + + // handle special case of player running into enemy/explosion from top or left side + if (el_player(tile_last) && (el_growing(tile_from) || el_explosion(tile_from))) + tile_from = tile_last; + + // handle special case of player digging or snapping clock (which gets replaced by sand) + if (el_diggable(tile_from) && el_player(tile)) + use_smooth_movements = FALSE; + + // do not use smooth movement if from and to directions are different; for example, + // player has snapped field, but another element immediately moved to that field + if (dir_from != new_dir_to) + use_smooth_movements = FALSE; + + // handle special case of element falling or moving into lava + if (tile_from == O_LAVA) { - 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); + // show element that is moving/falling into lava + tile_from = tile_last; + draw_from = draw_last; - game->element_buffer[y][x] = tile; + // do not use smooth movement if element not moving into lava (like player snapping lava) + if (tile == tile_last) + use_smooth_movements = FALSE; } - use_smooth_movements = FALSE; + // player killed by lava or explosion was standing still before moving into lava or enemy + if (el_player(tile_from)) + draw_from = (dir_from == GD_MV_LEFT ? O_PLAYER_LEFT : + dir_from == GD_MV_RIGHT ? O_PLAYER_RIGHT : + dir_from == GD_MV_UP ? O_PLAYER_UP : + dir_from == GD_MV_DOWN ? O_PLAYER_DOWN : O_PLAYER); + + // do not use smooth movement if tile has stopped falling and crashed something (like a nut) + if (el_can_fall(tile) && el_explosion(tile_from)) + use_smooth_movements = FALSE; } + // -------------------------------------------------------------------------------- + // 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; + + // do not use smooth movement animation for game elements that cannot move smoothly + // (but handle special case of player digging or snapping diggable element, like sand) + if (!el_smooth_movable(tile_from) && + !el_smooth_movable(tile_to) && + !el_diggable(tile_last)) + 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[draw][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; @@ -636,10 +745,10 @@ static void gd_drawcave_tile(Bitmap *dest, GdGame *game, int x, int y, boolean d if (new_x != old_x || new_y != old_y) { printf("::: BAD ANIMATION FOR TILE %d, FRAME %d [NEW(%d, %d) != OLD(%d, %d)] ['%s']\n", - tile, frame, + draw, frame, new_x, new_y, old_x, old_y, - gd_elements[tile].name); + gd_elements[draw].name); } } #endif @@ -647,74 +756,79 @@ 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[draw][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)) + // ---------- 1st step: draw background element for this tile ---------- { - // redraw previous game element on the cave field the new element is moving to - int tile_last = game->last_element_buffer[y][x]; + // check if player or amoeba is digging or player is snapping a diggable game element + boolean digging_tile = ((el_can_dig(tile) || tile == O_SPACE) && el_diggable(tile_last)); + int draw_back = (!is_moving_to ? draw : digging_tile ? draw_last : O_SPACE); + struct GraphicInfo_BD *g = &graphic_info_bd_object[draw_back][frame]; + Bitmap *tile_bitmap = gd_get_tile_bitmap(g->bitmap); - // 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) - { - 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); - - 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[draw_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_diggable(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[draw_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 +873,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; diff --git a/src/game_bd/bd_graphics.h b/src/game_bd/bd_graphics.h index 6b522fcc..e52048ad 100644 --- a/src/game_bd/bd_graphics.h +++ b/src/game_bd/bd_graphics.h @@ -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); diff --git a/src/game_bd/export_bd.h b/src/game_bd/export_bd.h index a060e2e4..a50b9e9f 100644 --- a/src/game_bd/export_bd.h +++ b/src/game_bd/export_bd.h @@ -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,22 @@ 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 drawing_buffer[MAX_PLAYFIELD_WIDTH][MAX_PLAYFIELD_HEIGHT]; + int last_drawing_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 +118,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 +140,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 +150,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 diff --git a/src/game_bd/import_bd.h b/src/game_bd/import_bd.h index ace426db..75e801c0 100644 --- a/src/game_bd/import_bd.h +++ b/src/game_bd/import_bd.h @@ -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 diff --git a/src/game_bd/main_bd.c b/src/game_bd/main_bd.c index 06ecfd04..9f94b3b6 100644 --- a/src/game_bd/main_bd.c +++ b/src/game_bd/main_bd.c @@ -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,105 @@ 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.drawing_buffer[x][y] = game->drawing_buffer[y][x]; + engine_snapshot_bd.last_drawing_buffer[x][y] = game->last_drawing_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.drawing_buffer = game->drawing_buffer; + engine_snapshot_bd.game.last_drawing_buffer = game->last_drawing_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->drawing_buffer[y][x] = engine_snapshot_bd.drawing_buffer[x][y]; + game->last_drawing_buffer[y][x] = engine_snapshot_bd.last_drawing_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); +} diff --git a/src/init.c b/src/init.c index 807a9a14..a6666d5d 100644 --- a/src/init.c +++ b/src/init.c @@ -4314,7 +4314,7 @@ void InitElementPropertiesStatic(void) EL_BDX_BITER_UP, EL_BDX_BITER_LEFT, EL_BDX_BITER_DOWN, - EL_BDX_BLADDER, + EL_BDX_BUBBLE, EL_BDX_NUT, EL_EMC_MAGIC_BALL, EL_EMC_ANDROID, @@ -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()); diff --git a/src/libgame/misc.c b/src/libgame/misc.c index 5e63c31f..6b4acba8 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -1222,6 +1222,9 @@ char *getStringCopyNStatic(const char *s, int n) char *getStringToUpper(const char *s) { + if (s == NULL) + return NULL; + char *s_copy = checked_malloc(strlen(s) + 1); char *s_ptr = s_copy; @@ -1234,6 +1237,9 @@ char *getStringToUpper(const char *s) char *getStringToLower(const char *s) { + if (s == NULL) + return NULL; + char *s_copy = checked_malloc(strlen(s) + 1); char *s_ptr = s_copy; @@ -1704,6 +1710,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 +1877,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; diff --git a/src/libgame/sdl.c b/src/libgame/sdl.c index a19defa3..d664eecc 100644 --- a/src/libgame/sdl.c +++ b/src/libgame/sdl.c @@ -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) diff --git a/src/libgame/sdl.h b/src/libgame/sdl.h index bbbaa767..1545f686 100644 --- a/src/libgame/sdl.h +++ b/src/libgame/sdl.h @@ -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 *); diff --git a/src/libgame/setup.c b/src/libgame/setup.c index 4104a042..fb63321b 100644 --- a/src/libgame/setup.c +++ b/src/libgame/setup.c @@ -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) diff --git a/src/libgame/setup.h b/src/libgame/setup.h index c7a53302..5692c9da 100644 --- a/src/libgame/setup.h +++ b/src/libgame/setup.h @@ -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); diff --git a/src/libgame/system.c b/src/libgame/system.c index a1f009f7..e77e6fe6 100644 --- a/src/libgame/system.c +++ b/src/libgame/system.c @@ -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()) diff --git a/src/libgame/system.h b/src/libgame/system.h index dc6d0f36..85c26a5f 100644 --- a/src/libgame/system.h +++ b/src/libgame/system.h @@ -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); diff --git a/src/libgame/text.c b/src/libgame/text.c index 446a873b..1db4848f 100644 --- a/src/libgame/text.c +++ b/src/libgame/text.c @@ -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; diff --git a/src/libgame/zip/ioapi.c b/src/libgame/zip/ioapi.c index 0aa6347a..5a31fcbf 100644 --- a/src/libgame/zip/ioapi.c +++ b/src/libgame/zip/ioapi.c @@ -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; diff --git a/src/main.c b/src/main.c index 8ab7313d..0375a9c5 100644 --- a/src/main.c +++ b/src/main.c @@ -6549,17 +6549,17 @@ struct ElementNameInfo element_name_info[MAX_NUM_ELEMENTS + 1] = }, { "bdx_wall_key_1", - "bdx_wall", + "bdx_wall_key", "Wall with key 1" }, { "bdx_wall_key_2", - "bdx_wall", + "bdx_wall_key", "Wall with key 2" }, { "bdx_wall_key_3", - "bdx_wall", + "bdx_wall_key", "Wall with key 3" }, { @@ -6718,14 +6718,14 @@ struct ElementNameInfo element_name_info[MAX_NUM_ELEMENTS + 1] = "Amoeba 2" }, { - "bdx_bladder", - "bdx_bladder", - "Bladder" + "bdx_bubble", + "bdx_bubble", + "Bubble" }, { - "bdx_bladder_spender", - "bdx_bladder_spender", - "Bladder spender" + "bdx_trapped_bubble", + "bdx_trapped_bubble", + "Trapped bubble" }, { "bdx_creature_switch", @@ -7403,44 +7403,44 @@ struct ElementNameInfo element_name_info[MAX_NUM_ELEMENTS + 1] = "Enclosed cow (7)" }, { - "bdx_bladder_1", - "bdx_bladder", - "Bladder (1)" + "bdx_bubble_1", + "bdx_bubble", + "Bubble (1)" }, { - "bdx_bladder_2", - "bdx_bladder", - "Bladder (2)" + "bdx_bubble_2", + "bdx_bubble", + "Bubble (2)" }, { - "bdx_bladder_3", - "bdx_bladder", - "Bladder (3)" + "bdx_bubble_3", + "bdx_bubble", + "Bubble (3)" }, { - "bdx_bladder_4", - "bdx_bladder", - "Bladder (4)" + "bdx_bubble_4", + "bdx_bubble", + "Bubble (4)" }, { - "bdx_bladder_5", - "bdx_bladder", - "Bladder (5)" + "bdx_bubble_5", + "bdx_bubble", + "Bubble (5)" }, { - "bdx_bladder_6", - "bdx_bladder", - "Bladder (6)" + "bdx_bubble_6", + "bdx_bubble", + "Bubble (6)" }, { - "bdx_bladder_7", - "bdx_bladder", - "Bladder (7)" + "bdx_bubble_7", + "bdx_bubble", + "Bubble (7)" }, { - "bdx_bladder_8", - "bdx_bladder", - "Bladder (8)" + "bdx_bubble_8", + "bdx_bubble", + "Bubble (8)" }, { "bdx_player.growing_1", @@ -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" diff --git a/src/main.h b/src/main.h index 309229ee..0deb4de8 100644 --- a/src/main.h +++ b/src/main.h @@ -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 @@ -1873,7 +1871,8 @@ #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) @@ -2135,8 +2134,8 @@ #define EL_BDX_NUT 1301 #define EL_BDX_AMOEBA_1 1302 #define EL_BDX_AMOEBA_2 1303 -#define EL_BDX_BLADDER 1304 -#define EL_BDX_BLADDER_SPENDER 1305 +#define EL_BDX_BUBBLE 1304 +#define EL_BDX_TRAPPED_BUBBLE 1305 #define EL_BDX_CREATURE_SWITCH 1306 #define EL_BDX_CREATURE_SWITCH_ACTIVE 1307 #define EL_BDX_BITER_SWITCH_1 1308 @@ -2275,14 +2274,14 @@ #define EL_BDX_COW_ENCLOSED_5 1438 #define EL_BDX_COW_ENCLOSED_6 1439 #define EL_BDX_COW_ENCLOSED_7 1440 -#define EL_BDX_BLADDER_1 1441 -#define EL_BDX_BLADDER_2 1442 -#define EL_BDX_BLADDER_3 1443 -#define EL_BDX_BLADDER_4 1444 -#define EL_BDX_BLADDER_5 1445 -#define EL_BDX_BLADDER_6 1446 -#define EL_BDX_BLADDER_7 1447 -#define EL_BDX_BLADDER_8 1448 +#define EL_BDX_BUBBLE_1 1441 +#define EL_BDX_BUBBLE_2 1442 +#define EL_BDX_BUBBLE_3 1443 +#define EL_BDX_BUBBLE_4 1444 +#define EL_BDX_BUBBLE_5 1445 +#define EL_BDX_BUBBLE_6 1446 +#define EL_BDX_BUBBLE_7 1447 +#define EL_BDX_BUBBLE_8 1448 #define EL_BDX_PLAYER_GROWING_1 1449 #define EL_BDX_PLAYER_GROWING_2 1450 #define EL_BDX_PLAYER_GROWING_3 1451 @@ -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-3" #define PROGRAM_TITLE_STRING "Rocks'n'Diamonds" #define PROGRAM_AUTHOR_STRING "Holger Schemel" @@ -3754,7 +3753,7 @@ struct LevelInfo int bd_acid_turns_to_element; // BD acid target element after spreading int bd_biter_move_delay; // BD biter delay between movements (in BD frames) int bd_biter_eats_element; // BD biter eats this game element when moving - int bd_bladder_converts_by_element; // BD bladder converts to clock by touching this element + int bd_bubble_converts_by_element; // BD bubble converts to clock by touching this element boolean bd_change_expanding_wall; // BD expanding wall direction is changed if enabled boolean bd_replicators_active; // BD replicators start in active state if enabled int bd_replicator_create_delay; // BD replicator delay between replications (in BD frames) diff --git a/src/screens.c b/src/screens.c index a31e6121..e52fbea6 100644 --- a/src/screens.c +++ b/src/screens.c @@ -4715,9 +4715,6 @@ static void DrawChooseTree(TreeInfo **ti_ptr) UnmapAllGadgets(); - FreeScreenGadgets(); - CreateScreenGadgets(); - if (restart_music) FadeMenuSoundsAndMusic(); @@ -4735,6 +4732,9 @@ static void DrawChooseTree(TreeInfo **ti_ptr) else if (game_status == GAME_MODE_SCORES) SetMainBackgroundImage(IMG_BACKGROUND_SCORES); + FreeScreenGadgets(); + CreateScreenGadgets(); + ClearField(); OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); @@ -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" }, diff --git a/src/tools.c b/src/tools.c index 1881220a..09933f1d 100644 --- a/src/tools.c +++ b/src/tools.c @@ -6354,7 +6354,7 @@ bd_object_mapping_list[] = }, { O_BLADDER_SPENDER, TRUE, - EL_BDX_BLADDER_SPENDER, -1, -1 + EL_BDX_TRAPPED_BUBBLE, -1, -1 }, { O_INBOX, TRUE, @@ -6762,71 +6762,71 @@ bd_object_mapping_list[] = }, { O_BLADDER, TRUE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_1, TRUE, - EL_BDX_BLADDER_1, -1, -1 + EL_BDX_BUBBLE_1, -1, -1 }, { O_BLADDER_1, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_2, TRUE, - EL_BDX_BLADDER_2, -1, -1 + EL_BDX_BUBBLE_2, -1, -1 }, { O_BLADDER_2, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_3, TRUE, - EL_BDX_BLADDER_3, -1, -1 + EL_BDX_BUBBLE_3, -1, -1 }, { O_BLADDER_3, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_4, TRUE, - EL_BDX_BLADDER_4, -1, -1 + EL_BDX_BUBBLE_4, -1, -1 }, { O_BLADDER_4, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_5, TRUE, - EL_BDX_BLADDER_5, -1, -1 + EL_BDX_BUBBLE_5, -1, -1 }, { O_BLADDER_5, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_6, TRUE, - EL_BDX_BLADDER_6, -1, -1 + EL_BDX_BUBBLE_6, -1, -1 }, { O_BLADDER_6, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_7, TRUE, - EL_BDX_BLADDER_7, -1, -1 + EL_BDX_BUBBLE_7, -1, -1 }, { O_BLADDER_7, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_BLADDER_8, TRUE, - EL_BDX_BLADDER_8, -1, -1 + EL_BDX_BUBBLE_8, -1, -1 }, { O_BLADDER_8, FALSE, - EL_BDX_BLADDER, -1, -1 + EL_BDX_BUBBLE, -1, -1 }, { O_WAITING_STONE, TRUE, @@ -7686,6 +7686,9 @@ int map_element_BD_to_RND_cave(int element_bd) mapping_initialized = TRUE; } + // always map (scanned) run-time elements to normal elements + element_bd &= O_MASK; + if (element_bd < 0 || element_bd >= O_MAX_ALL) { Warn("invalid BD element %d", element_bd); @@ -7716,6 +7719,9 @@ int map_element_BD_to_RND_game(int element_bd) mapping_initialized = TRUE; } + // always map (scanned) run-time elements to normal elements + element_bd &= O_MASK; + if (element_bd < 0 || element_bd >= O_MAX_ALL) { Warn("invalid BD element %d", element_bd); @@ -11215,6 +11221,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 +11298,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,