[Scummvm-git-logs] scummvm master -> 0bc6ab70f5213976ac0a4a3956cb22027778835b
dreammaster
noreply at scummvm.org
Fri Sep 18 07:03:14 UTC 2026
This automated email contains information about 9 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
0c0afadbe9 MADS: Fix AnimView transition timing
7f807fe843 MADS: Prevent AnimView fade catch-up bursts
5813395017 MADS: Align Rex AnimView reveal with frame deadline
0098b74914 MADS: Avoid duplicate presentation during paced fades
c4308ee348 MADS: Fix AnimView boundary line handling
302570b42f MADS: Add tunable AnimView minimum black interval
b232221e61 MADS: Set AnimView min extra black interval to 500ms
813622bbc9 MADS: NEBULAR: Keep AnimView boundary lines hidden
0bc6ab70f5 MADS: Fix uninitialized local warnings
Commit: 0c0afadbe994fc6be02c1fea53574f430a8b2b4f
https://github.com/scummvm/scummvm/commit/0c0afadbe994fc6be02c1fea53574f430a8b2b4f
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:22+10:00
Commit Message:
MADS: Fix AnimView transition timing
Match correct AnimView fade behavior across the three MADS games.
Rex and Phantom use the full 16-step fade in, while Dragonsphere
uses the quick variant.
AnimView palette steps now follow the DOS VGA retrace rate, or the
Macintosh TickCount rate for Mac Rex.
Changed paths:
engines/mads/animview/anim_timer.cpp
engines/mads/core/magic.cpp
engines/mads/core/magic.h
engines/mads/core/matte.cpp
engines/mads/core/matte.h
diff --git a/engines/mads/animview/anim_timer.cpp b/engines/mads/animview/anim_timer.cpp
index 066b937a8e4..7b62e15ed1b 100644
--- a/engines/mads/animview/anim_timer.cpp
+++ b/engines/mads/animview/anim_timer.cpp
@@ -37,6 +37,9 @@ static const byte FX_TIMES[16] = {
0, 110, 110, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0, 0, 0, 0
};
+static const int DOS_FADE_STEP_RATE = 70;
+static const int MACINTOSH_FADE_STEP_RATE = 60;
+
constexpr int MESSAGES_COUNT = 8;
static int messageHandle[MESSAGES_COUNT];
@@ -81,6 +84,8 @@ void anim_timer() {
Speech *speech;
Frame *frame;
int sound, count;
+ bool full_fade_in;
+ int fade_step_rate;
if (current_error_code || speechResourceId != -1)
goto done;
@@ -266,7 +271,15 @@ block2:
matte_refresh_work();
}
- matte_frame(runFx, 0);
+ full_fade_in = g_engine->getGameID() == GType_RexNebular ||
+ g_engine->getGameID() == GType_Phantom;
+ fade_step_rate = g_engine->hasMacintoshInterface() ?
+ MACINTOSH_FADE_STEP_RATE : DOS_FADE_STEP_RATE;
+
+ // Rex and Phantom AnimView use the full 16-step fade-in. The later
+ // Dragonsphere executable uses the quick fade. DOS palette updates are
+ // paced by VGA retrace, while Macintosh fades use the 60 Hz TickCount.
+ matte_frame(runFx, 0, full_fade_in, fade_step_rate);
mouse_hide();
block3:
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index 4e32ac3e86c..e3e575b4fe6 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -58,6 +58,14 @@ int magic_special_center_y = -1;
int magic_low_fade_bound = 1;
int magic_high_fade_bound = 252;
+static void magic_wait_for_fade_step(uint32 fade_base_time, int step,
+ int fade_step_rate) {
+ const uint32 deadline = fade_base_time +
+ ((step + 1) * 1000 + fade_step_rate - 1) / fade_step_rate;
+ while (g_system->getMillis() < deadline)
+ g_engine->hasPendingKey();
+}
+
void magic_get_grey_values(Palette *pal, byte *grey_value,
int base_color, int num_colors) {
@@ -200,7 +208,7 @@ void magic_grey_ramp_palette(Palette &pal, int num_greys) {
void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int base_color, int num_colors,
int base_grey, int num_greys,
- int tick_delay, int steps) {
+ int tick_delay, int steps, int fade_step_rate) {
int count;
int color;
int index;
@@ -210,6 +218,7 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int shift_sign;
long base_timing, now_timing;
long memory_needed;
+ uint32 fade_base_time = 0;
byte *work_memory = NULL;
Heap magic_heap;
byte *pal_index;
@@ -256,7 +265,10 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
}
}
- base_timing = timer_read_600();
+ if (fade_step_rate > 0)
+ fade_base_time = g_system->getMillis();
+ else
+ base_timing = timer_read_600();
for (step = 0; step < steps; step++) {
for (count = base_color; count < (base_color + num_colors); count++) {
@@ -275,12 +287,18 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
if (g_engine->hasMacintoshInterface())
g_system->updateScreen();
- do {
+ if (fade_step_rate > 0) {
if (!g_engine->hasMacintoshInterface())
g_engine->getScreen()->update();
- now_timing = timer_read_600();
- } while (now_timing < (base_timing + tick_delay));
- base_timing = now_timing;
+ magic_wait_for_fade_step(fade_base_time, step, fade_step_rate);
+ } else {
+ do {
+ if (!g_engine->hasMacintoshInterface())
+ g_engine->getScreen()->update();
+ now_timing = timer_read_600();
+ } while (now_timing < (base_timing + tick_delay));
+ base_timing = now_timing;
+ }
}
if (map_pointer != NULL) {
@@ -299,7 +317,7 @@ done:
void magic_fade_from_grey(RGBcolor *pal, Palette target,
int base_color, int num_colors,
int base_grey, int num_greys,
- int tick_delay, int steps) {
+ int tick_delay, int steps, int fade_step_rate) {
int count;
int color;
int index;
@@ -309,6 +327,7 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
int shift_sign;
long base_timing, now_timing;
long memory_needed;
+ uint32 fade_base_time = 0;
byte *work_memory = NULL;
Heap magic_heap;
byte *pal_index;
@@ -358,7 +377,10 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
}
}
- base_timing = timer_read_600();
+ if (fade_step_rate > 0)
+ fade_base_time = g_system->getMillis();
+ else
+ base_timing = timer_read_600();
for (step = 0; step < steps; step++) {
for (count = base_color; count < (base_color + num_colors); count++) {
@@ -377,12 +399,18 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
if (g_engine->hasMacintoshInterface())
g_system->updateScreen();
- do {
+ if (fade_step_rate > 0) {
if (!g_engine->hasMacintoshInterface())
g_engine->getScreen()->update();
- now_timing = timer_read_600();
- } while (now_timing < (base_timing + tick_delay));
- base_timing = now_timing;
+ magic_wait_for_fade_step(fade_base_time, step, fade_step_rate);
+ } else {
+ do {
+ if (!g_engine->hasMacintoshInterface())
+ g_engine->getScreen()->update();
+ now_timing = timer_read_600();
+ } while (now_timing < (base_timing + tick_delay));
+ base_timing = now_timing;
+ }
}
done:
diff --git a/engines/mads/core/magic.h b/engines/mads/core/magic.h
index 5d5ddf93726..70b158e93b8 100644
--- a/engines/mads/core/magic.h
+++ b/engines/mads/core/magic.h
@@ -78,9 +78,10 @@ extern void magic_map_to_grey_ramp(Palette *pal, int base_color, int num_colors,
extern void magic_grey_ramp_palette(Palette &pal, int num_greys);
extern void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int base_color, int num_colors, int base_grey, int num_greys,
- int tick_delay, int steps);
+ int tick_delay, int steps, int fade_step_rate = 0);
extern void magic_fade_from_grey(RGBcolor *pal, Palette target, int base_color,
- int num_colors, int base_grey, int num_greys, int tick_delay, int steps);
+ int num_colors, int base_grey, int num_greys, int tick_delay, int steps,
+ int fade_step_rate = 0);
/**
* Picture-to-picture transition in which one of the screen
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index e0ec847692d..5aba6319d14 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/system.h"
#include "mads/core/general.h"
#include "mads/core/sprite.h"
#include "mads/core/buffer.h"
@@ -456,14 +457,18 @@ void filter_matte_list(MattePtr matte, int size, int base_index) {
}
}
-static void matte_quick_from_black(byte *special_pal, int ticks) {
+static void matte_quick_from_black(byte *special_pal, int ticks,
+ int fade_step_rate) {
int going;
+ int step = 0;
byte *source;
byte *dest;
byte *special;
byte increments[768];
long fade_clock;
long now_clock;
+ uint32 fade_base_time = 0;
+ uint32 fade_deadline = 0;
source = &master_palette[0].r;
special = increments;
@@ -477,9 +482,18 @@ static void matte_quick_from_black(byte *special_pal, int ticks) {
special[i] = inc;
}
+ if (fade_step_rate > 0)
+ fade_base_time = g_system->getMillis();
+
do {
going = false;
- fade_clock = timer_read_600() + ticks;
+ if (fade_step_rate > 0) {
+ fade_deadline = fade_base_time +
+ ((step + 1) * 1000 + fade_step_rate - 1) /
+ fade_step_rate;
+ } else {
+ fade_clock = timer_read_600() + ticks;
+ }
for (int i = 0; i < 768; i++) {
byte current = dest[i]; // current fading value (starts at black)
@@ -499,14 +513,27 @@ static void matte_quick_from_black(byte *special_pal, int ticks) {
mcga_setpal((Palette *)special_pal);
- do {
- now_clock = timer_read_600();
- } while (now_clock < fade_clock);
+ if (fade_step_rate > 0) {
+ if (g_engine->hasMacintoshInterface())
+ g_system->updateScreen();
+ else
+ g_engine->getScreen()->update();
+
+ while (g_system->getMillis() < fade_deadline)
+ g_engine->hasPendingKey();
+ } else {
+ do {
+ now_clock = timer_read_600();
+ } while (now_clock < fade_clock);
+ }
+
+ ++step;
} while (going);
}
-static void matte_special_effect(int special_effect, int full_screen) {
+static void matte_special_effect(int special_effect, int full_screen,
+ bool full_fade_in, int fade_step_rate) {
int count;
int pixel_rate;
byte *background_swap;
@@ -541,7 +568,8 @@ static void matte_special_effect(int special_effect, int full_screen) {
if (special_effect == MATTE_FX_FADE_THRU_BLACK) {
mcga_getpal(&special_pal);
- magic_fade_to_grey(special_pal, NULL, 0, 256, 0, 1, 1, 16);
+ magic_fade_to_grey(special_pal, NULL, 0, 256, 0, 1,
+ 1, 16, fade_step_rate);
buffer_fill(scr_live, 0);
}
@@ -552,7 +580,12 @@ static void matte_special_effect(int special_effect, int full_screen) {
viewing_at_x, viewing_at_y,
work_screen->x, work_screen->y);
- matte_quick_from_black(&special_pal[0].r, 1);
+ if (full_fade_in)
+ magic_fade_from_grey(&special_pal[0], master_palette,
+ 0, 256, 0, 1, 1, 16, fade_step_rate);
+ else
+ matte_quick_from_black(&special_pal[0].r, 1,
+ fade_step_rate);
break;
case MATTE_FX_CORNER_LOWER_LEFT:
@@ -616,7 +649,8 @@ static void matte_special_effect(int special_effect, int full_screen) {
}
}
-void matte_frame(int special_effect, int full_screen) {
+void matte_frame(int special_effect, int full_screen, bool full_fade_in,
+ int fade_step_rate) {
Matte *matte;
Image *image;
int id;
@@ -924,7 +958,8 @@ void matte_frame(int special_effect, int full_screen) {
}
} else {
- matte_special_effect(special_effect, full_screen);
+ matte_special_effect(special_effect, full_screen, full_fade_in,
+ fade_step_rate);
sound_queue_flush();
}
}
diff --git a/engines/mads/core/matte.h b/engines/mads/core/matte.h
index e1c8a56683e..6d8edb76401 100644
--- a/engines/mads/core/matte.h
+++ b/engines/mads/core/matte.h
@@ -126,7 +126,8 @@ extern void matte_init(int init_series);
extern int matte_map_work_screen();
-extern void matte_frame(int special_effect, int full_screen);
+extern void matte_frame(int special_effect, int full_screen,
+ bool full_fade_in = false, int fade_step_rate = 0);
extern int matte_load_series(const char *name, int load_flags, int bonus_series_number);
extern void matte_deallocate_series(int id, int free_memory);
Commit: 7f807fe8435b7750ff4925b05b9e015f2702bbd4
https://github.com/scummvm/scummvm/commit/7f807fe8435b7750ff4925b05b9e015f2702bbd4
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:22+10:00
Commit Message:
MADS: Prevent AnimView fade catch-up bursts
Rebase paced fades after a long host stall so subsequent palette
steps remain visible instead of being emitted in a burst.
Changed paths:
engines/mads/core/magic.cpp
engines/mads/core/magic.h
engines/mads/core/matte.cpp
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index e3e575b4fe6..563db119a2d 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -58,15 +58,35 @@ int magic_special_center_y = -1;
int magic_low_fade_bound = 1;
int magic_high_fade_bound = 252;
-static void magic_wait_for_fade_step(uint32 fade_base_time, int step,
+void magic_fade_pacer_init(MagicFadePacer &pacer) {
+ pacer.base_time = g_system->getMillis();
+ pacer.base_step = 0;
+}
+
+void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
int fade_step_rate) {
- const uint32 deadline = fade_base_time +
- ((step + 1) * 1000 + fade_step_rate - 1) / fade_step_rate;
- while (g_system->getMillis() < deadline)
+ int relative_step = step - pacer.base_step + 1;
+ uint32 deadline = pacer.base_time +
+ (relative_step * 1000 + fade_step_rate - 1) / fade_step_rate;
+ const uint32 next_deadline = pacer.base_time +
+ ((relative_step + 1) * 1000 + fade_step_rate - 1) /
+ fade_step_rate;
+ uint32 now = g_system->getMillis();
+
+ // Native DOS palette writes wait for the next retrace. If the host stalls
+ // past another scheduled step, rebase so the remaining steps do not burst.
+ if (now >= next_deadline) {
+ pacer.base_time = now;
+ pacer.base_step = step;
+ deadline = now + (1000 + fade_step_rate - 1) / fade_step_rate;
+ }
+
+ while (now < deadline) {
g_engine->hasPendingKey();
+ now = g_system->getMillis();
+ }
}
-
void magic_get_grey_values(Palette *pal, byte *grey_value,
int base_color, int num_colors) {
int i;
@@ -218,7 +238,7 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int shift_sign;
long base_timing, now_timing;
long memory_needed;
- uint32 fade_base_time = 0;
+ MagicFadePacer fade_pacer;
byte *work_memory = NULL;
Heap magic_heap;
byte *pal_index;
@@ -266,7 +286,7 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
}
if (fade_step_rate > 0)
- fade_base_time = g_system->getMillis();
+ magic_fade_pacer_init(fade_pacer);
else
base_timing = timer_read_600();
@@ -290,7 +310,7 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
if (fade_step_rate > 0) {
if (!g_engine->hasMacintoshInterface())
g_engine->getScreen()->update();
- magic_wait_for_fade_step(fade_base_time, step, fade_step_rate);
+ magic_fade_pacer_wait(fade_pacer, step, fade_step_rate);
} else {
do {
if (!g_engine->hasMacintoshInterface())
@@ -327,7 +347,7 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
int shift_sign;
long base_timing, now_timing;
long memory_needed;
- uint32 fade_base_time = 0;
+ MagicFadePacer fade_pacer;
byte *work_memory = NULL;
Heap magic_heap;
byte *pal_index;
@@ -378,7 +398,7 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
}
if (fade_step_rate > 0)
- fade_base_time = g_system->getMillis();
+ magic_fade_pacer_init(fade_pacer);
else
base_timing = timer_read_600();
@@ -402,7 +422,7 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
if (fade_step_rate > 0) {
if (!g_engine->hasMacintoshInterface())
g_engine->getScreen()->update();
- magic_wait_for_fade_step(fade_base_time, step, fade_step_rate);
+ magic_fade_pacer_wait(fade_pacer, step, fade_step_rate);
} else {
do {
if (!g_engine->hasMacintoshInterface())
diff --git a/engines/mads/core/magic.h b/engines/mads/core/magic.h
index 70b158e93b8..c445089fb10 100644
--- a/engines/mads/core/magic.h
+++ b/engines/mads/core/magic.h
@@ -49,6 +49,11 @@ struct MagicGrey {
typedef MagicGrey *MagicGreyPtr;
+struct MagicFadePacer {
+ uint32 base_time;
+ int base_step;
+};
+
extern byte magic_color_flags[3];
extern byte magic_color_values[3];
extern int magic_low_fade_bound;
@@ -76,6 +81,9 @@ extern void magic_set_color_values(byte r, byte g, byte b);
extern void magic_map_to_grey_ramp(Palette *pal, int base_color, int num_colors,
int base_grey, int num_greys, MagicGreyPtr magic_map);
extern void magic_grey_ramp_palette(Palette &pal, int num_greys);
+extern void magic_fade_pacer_init(MagicFadePacer &pacer);
+extern void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
+ int fade_step_rate);
extern void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int base_color, int num_colors, int base_grey, int num_greys,
int tick_delay, int steps, int fade_step_rate = 0);
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index 5aba6319d14..31d0431ea4c 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -467,8 +467,7 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
byte increments[768];
long fade_clock;
long now_clock;
- uint32 fade_base_time = 0;
- uint32 fade_deadline = 0;
+ MagicFadePacer fade_pacer;
source = &master_palette[0].r;
special = increments;
@@ -483,17 +482,12 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
}
if (fade_step_rate > 0)
- fade_base_time = g_system->getMillis();
+ magic_fade_pacer_init(fade_pacer);
do {
going = false;
- if (fade_step_rate > 0) {
- fade_deadline = fade_base_time +
- ((step + 1) * 1000 + fade_step_rate - 1) /
- fade_step_rate;
- } else {
+ if (fade_step_rate <= 0)
fade_clock = timer_read_600() + ticks;
- }
for (int i = 0; i < 768; i++) {
byte current = dest[i]; // current fading value (starts at black)
@@ -519,8 +513,7 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
else
g_engine->getScreen()->update();
- while (g_system->getMillis() < fade_deadline)
- g_engine->hasPendingKey();
+ magic_fade_pacer_wait(fade_pacer, step, fade_step_rate);
} else {
do {
now_clock = timer_read_600();
Commit: 5813395017368595a4f0999b9226f35c0cea7fc4
https://github.com/scummvm/scummvm/commit/5813395017368595a4f0999b9226f35c0cea7fc4
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:22+10:00
Commit Message:
MADS: Align Rex AnimView reveal with frame deadline
Use the existing Rex transition deadline to delay fade-in while the
screen is black. Keep the original post-effect deadline check so real
overruns still release late. Fast-host presentation adjustment.
Assisted-by: Codex:GPT-5.5
Changed paths:
engines/mads/animview/anim_timer.cpp
engines/mads/core/magic.cpp
engines/mads/core/magic.h
engines/mads/core/matte.cpp
engines/mads/core/matte.h
engines/mads/core/timer.cpp
engines/mads/core/timer.h
diff --git a/engines/mads/animview/anim_timer.cpp b/engines/mads/animview/anim_timer.cpp
index 7b62e15ed1b..7ea6f88cac5 100644
--- a/engines/mads/animview/anim_timer.cpp
+++ b/engines/mads/animview/anim_timer.cpp
@@ -86,6 +86,7 @@ void anim_timer() {
int sound, count;
bool full_fade_in;
int fade_step_rate;
+ long fade_end_time;
if (current_error_code || speechResourceId != -1)
goto done;
@@ -275,11 +276,18 @@ block2:
g_engine->getGameID() == GType_Phantom;
fade_step_rate = g_engine->hasMacintoshInterface() ?
MACINTOSH_FADE_STEP_RATE : DOS_FADE_STEP_RATE;
+ fade_end_time = -1;
+ if (g_engine->getGameID() == GType_RexNebular &&
+ (runFx == MATTE_FX_FADE_FROM_BLACK ||
+ runFx == MATTE_FX_FADE_THRU_BLACK))
+ fade_end_time = timer1;
// Rex and Phantom AnimView use the full 16-step fade-in. The later
// Dragonsphere executable uses the quick fade. DOS palette updates are
// paced by VGA retrace, while Macintosh fades use the 60 Hz TickCount.
- matte_frame(runFx, 0, full_fade_in, fade_step_rate);
+ // When Rex finishes fade preparation early, keep the remaining transition
+ // time black so the fade-in ends at the existing animation deadline.
+ matte_frame(runFx, 0, full_fade_in, fade_step_rate, fade_end_time);
mouse_hide();
block3:
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index 563db119a2d..5432e49568d 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -87,6 +87,24 @@ void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
}
}
+static void magic_wait_for_fade_start(long fade_end_time, int steps,
+ int fade_step_rate) {
+ if (fade_end_time < 0 || fade_step_rate <= 0)
+ return;
+
+ const long fade_ticks = (steps * TIMER_TICKS_PER_SECOND +
+ fade_step_rate - 1) /
+ fade_step_rate;
+ const long fade_start_time = fade_end_time - fade_ticks;
+ long now = timer_read();
+
+ // timer_read() services events while retaining the animation's 60 Hz
+ // clock domain.
+ while (now < fade_start_time) {
+ now = timer_read();
+ }
+}
+
void magic_get_grey_values(Palette *pal, byte *grey_value,
int base_color, int num_colors) {
int i;
@@ -337,7 +355,7 @@ done:
void magic_fade_from_grey(RGBcolor *pal, Palette target,
int base_color, int num_colors,
int base_grey, int num_greys,
- int tick_delay, int steps, int fade_step_rate) {
+ int tick_delay, int steps, int fade_step_rate, long fade_end_time) {
int count;
int color;
int index;
@@ -397,10 +415,12 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
}
}
- if (fade_step_rate > 0)
+ if (fade_step_rate > 0) {
+ magic_wait_for_fade_start(fade_end_time, steps, fade_step_rate);
magic_fade_pacer_init(fade_pacer);
- else
+ } else {
base_timing = timer_read_600();
+ }
for (step = 0; step < steps; step++) {
for (count = base_color; count < (base_color + num_colors); count++) {
diff --git a/engines/mads/core/magic.h b/engines/mads/core/magic.h
index c445089fb10..2753a99bb47 100644
--- a/engines/mads/core/magic.h
+++ b/engines/mads/core/magic.h
@@ -87,9 +87,10 @@ extern void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
extern void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int base_color, int num_colors, int base_grey, int num_greys,
int tick_delay, int steps, int fade_step_rate = 0);
+// fade_end_time is in timer_read() ticks; -1 leaves reveal timing unchanged.
extern void magic_fade_from_grey(RGBcolor *pal, Palette target, int base_color,
int num_colors, int base_grey, int num_greys, int tick_delay, int steps,
- int fade_step_rate = 0);
+ int fade_step_rate = 0, long fade_end_time = -1);
/**
* Picture-to-picture transition in which one of the screen
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index 31d0431ea4c..aebaedac5c6 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -526,7 +526,7 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
}
static void matte_special_effect(int special_effect, int full_screen,
- bool full_fade_in, int fade_step_rate) {
+ bool full_fade_in, int fade_step_rate, long fade_end_time) {
int count;
int pixel_rate;
byte *background_swap;
@@ -575,7 +575,8 @@ static void matte_special_effect(int special_effect, int full_screen,
if (full_fade_in)
magic_fade_from_grey(&special_pal[0], master_palette,
- 0, 256, 0, 1, 1, 16, fade_step_rate);
+ 0, 256, 0, 1, 1, 16, fade_step_rate,
+ fade_end_time);
else
matte_quick_from_black(&special_pal[0].r, 1,
fade_step_rate);
@@ -643,7 +644,7 @@ static void matte_special_effect(int special_effect, int full_screen,
}
void matte_frame(int special_effect, int full_screen, bool full_fade_in,
- int fade_step_rate) {
+ int fade_step_rate, long fade_end_time) {
Matte *matte;
Image *image;
int id;
@@ -952,7 +953,7 @@ void matte_frame(int special_effect, int full_screen, bool full_fade_in,
} else {
matte_special_effect(special_effect, full_screen, full_fade_in,
- fade_step_rate);
+ fade_step_rate, fade_end_time);
sound_queue_flush();
}
}
diff --git a/engines/mads/core/matte.h b/engines/mads/core/matte.h
index 6d8edb76401..de4fdb97913 100644
--- a/engines/mads/core/matte.h
+++ b/engines/mads/core/matte.h
@@ -126,8 +126,10 @@ extern void matte_init(int init_series);
extern int matte_map_work_screen();
+// fade_end_time is in timer_read() ticks; -1 leaves reveal timing unchanged.
extern void matte_frame(int special_effect, int full_screen,
- bool full_fade_in = false, int fade_step_rate = 0);
+ bool full_fade_in = false, int fade_step_rate = 0,
+ long fade_end_time = -1);
extern int matte_load_series(const char *name, int load_flags, int bonus_series_number);
extern void matte_deallocate_series(int id, int free_memory);
diff --git a/engines/mads/core/timer.cpp b/engines/mads/core/timer.cpp
index b9b01f771dd..9c120dc2f47 100644
--- a/engines/mads/core/timer.cpp
+++ b/engines/mads/core/timer.cpp
@@ -60,7 +60,7 @@ long timer_read() {
// by pointing timer_address at the interrupt handler's timer_60_low counter;
// here we derive the same rate directly from wall-clock milliseconds.
unsigned long ms = g_engine->getMillis();
- return (long)(ms * 60 / 1000);
+ return (long)(ms * TIMER_TICKS_PER_SECOND / 1000);
}
long timer_read_600() {
@@ -70,7 +70,7 @@ long timer_read_600() {
long timer_read_60() {
unsigned long ms = g_engine->getMillis();
- return ms * 60 / 1000;
+ return ms * TIMER_TICKS_PER_SECOND / 1000;
}
void timer_set_sound_flag(int sound_flag) {
diff --git a/engines/mads/core/timer.h b/engines/mads/core/timer.h
index 5ede9aba434..d51c123cc8c 100644
--- a/engines/mads/core/timer.h
+++ b/engines/mads/core/timer.h
@@ -26,6 +26,8 @@
namespace MADS {
+static const int TIMER_TICKS_PER_SECOND = 60;
+
extern long *timer_address; /* "Default" timer count address */
extern word timer_rate; /* Current timer ticks/sec */
extern int timer_service_active; /* Flag if timer service active */
@@ -45,7 +47,7 @@ extern long timer_dos_low;
/**
- * Reads system clock, returns number of ticks since startup (1 tick = 54.9ms)
+ * Reads the system clock in TIMER_TICKS_PER_SECOND ticks per second.
*/
extern long timer_read();
inline long timer_read_dos() {
Commit: 0098b749144c8cb1a4d1606a005ef1c391c3303d
https://github.com/scummvm/scummvm/commit/0098b749144c8cb1a4d1606a005ef1c391c3303d
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:22+10:00
Commit Message:
MADS: Avoid duplicate presentation during paced fades
Paced AnimView fades explicitly present every palette step. Continue to
service input, timers, Macintosh UI and the generic display schedule while
waiting, but suppress the redundant automatic screen update.
Assisted-by: Codex:GPT-5.5
Changed paths:
engines/mads/core/magic.cpp
engines/mads/mads.cpp
engines/mads/mads.h
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index 5432e49568d..3b274e7af80 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -82,7 +82,10 @@ void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
}
while (now < deadline) {
- g_engine->hasPendingKey();
+ // The palette step was presented explicitly above. Service input and
+ // timers without presenting the same step again on the nominal 50 Hz
+ // MADS display schedule.
+ g_engine->hasPendingKey(false);
now = g_system->getMillis();
}
}
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index 3e4b6fbb035..a0da8e8d368 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -248,11 +248,12 @@ void MADSEngine::syncGame(Common::Serializer &s) {
s.syncAsSint16LE(previous_room);
}
-void MADSEngine::pollEvents() {
+void MADSEngine::pollEvents(bool presentScreen) {
// Check for screen update time
uint32 time = g_system->getMillis();
if (time >= _nextFrameTime) {
- updateScreen();
+ if (presentScreen)
+ updateScreen();
_nextFrameTime = time + GAME_FRAME_TIME;
serviceMacintoshUI();
}
@@ -386,8 +387,8 @@ void MADSEngine::checkForTimerFunction() {
serviceMacintoshSound();
}
-bool MADSEngine::hasPendingKey() {
- pollEvents();
+bool MADSEngine::hasPendingKey(bool presentScreen) {
+ pollEvents(presentScreen);
return !_keyEvents.empty();
}
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index cfc5eb2a050..a1301426aaf 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -98,7 +98,7 @@ protected:
bool hasFeature(EngineFeature f) const override;
- void pollEvents();
+ void pollEvents(bool presentScreen = true);
void checkForTimerFunction();
public:
@@ -129,7 +129,7 @@ public:
return _screen;
}
- bool hasPendingKey();
+ bool hasPendingKey(bool presentScreen = true);
int getKey();
void flushKeys();
Commit: c4308ee348895d331a095047ddcb71ebade08816
https://github.com/scummvm/scummvm/commit/c4308ee348895d331a095047ddcb71ebade08816
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:22+10:00
Commit Message:
MADS: Fix AnimView boundary line handling
Match the DOS viewer's initially enabled boundary-line flag before
applying each playlist's -w toggle. Allow optional override.
Explicitly update boundary rows and restore them after AnimView clears
the screen while black. Handle fades.
Assisted-by: Codex:GPT-5.5
Changed paths:
engines/mads/animview/anim_timer.cpp
engines/mads/animview/animview.cpp
engines/mads/animview/animview.h
engines/mads/animview/functions.cpp
engines/mads/animview/functions.h
engines/mads/core/matte.cpp
engines/mads/core/matte.h
engines/mads/dragonsphere/main.cpp
engines/mads/nebular/bonus/bonus.cpp
engines/mads/nebular/mac_frontend.cpp
engines/mads/nebular/main.cpp
engines/mads/phantom/main.cpp
diff --git a/engines/mads/animview/anim_timer.cpp b/engines/mads/animview/anim_timer.cpp
index 7ea6f88cac5..77bf886e358 100644
--- a/engines/mads/animview/anim_timer.cpp
+++ b/engines/mads/animview/anim_timer.cpp
@@ -287,7 +287,8 @@ block2:
// paced by VGA retrace, while Macintosh fades use the 60 Hz TickCount.
// When Rex finishes fade preparation early, keep the remaining transition
// time black so the fade-in ends at the existing animation deadline.
- matte_frame(runFx, 0, full_fade_in, fade_step_rate, fade_end_time);
+ matte_frame(runFx, 0, full_fade_in, fade_step_rate, fade_end_time,
+ boundaryLineColor);
mouse_hide();
block3:
diff --git a/engines/mads/animview/animview.cpp b/engines/mads/animview/animview.cpp
index 3af8b2f9f99..8bcbc6ef649 100644
--- a/engines/mads/animview/animview.cpp
+++ b/engines/mads/animview/animview.cpp
@@ -67,6 +67,7 @@ int imageFrame;
CycleList anim_cycle_list;
bool has_cycles;
int currentViewX, currentViewY;
+int boundaryLineColor;
int concat_mode;
bool stop_music_at_end;
bool wait_for_music_at_end;
@@ -92,7 +93,7 @@ static int runVal1, runVal2, runVal3;
static int runVal12;
static int error_code;
static int presentationBufferHeight;
-static bool presentationDrawBoundaryLines;
+static BoundaryLineMode presentationBoundaryLines;
static bool presentationServiceFramesInline;
/**
@@ -100,7 +101,9 @@ static bool presentationServiceFramesInline;
*/
static void init_globals() {
anim_timer_init();
- functions_init();
+ // The DOS viewers initialize the resource-controlled boundary flag on;
+ // Macintosh Rex uses a separate full-height presentation without lines.
+ functions_init(!g_engine->hasMacintoshInterface());
concat_mode = 0;
has_sound_file = false;
*sound_file_name = '\0';
@@ -114,6 +117,7 @@ static void init_globals() {
current_anim = nullptr;
current_anim_inter = nullptr;
has_cycles = false;
+ boundaryLineColor = -1;
viewing_at_y2 = 0;
hasSpeechAudio = false;
speechResourceId = -1;
@@ -190,14 +194,16 @@ static void run_animation(int animIndex) {
mouse_set_view_port(0, 0);
}
- auto &screen = *g_engine->getScreen();
- if (presentationDrawBoundaryLines && viewing_at_y &&
- anim_list[animIndex].show_bars) {
- screen.hLine(0, viewing_at_y - 2, 319, 253);
- screen.hLine(0, viewing_at_y + scr_work.y + 1, 319, 253);
- } else if (presentationDrawBoundaryLines && viewing_at_y) {
- screen.hLine(0, viewing_at_y - 2, 319, 0);
- screen.hLine(0, viewing_at_y + scr_work.y + 1, 319, 0);
+ boundaryLineColor = -1;
+ if (viewing_at_y) {
+ bool showLines = presentationBoundaryLines == kBoundaryLinesShown ||
+ (presentationBoundaryLines == kBoundaryLinesFromResource &&
+ anim_list[animIndex].show_bars);
+ boundaryLineColor = showLines ? 253 : 0;
+ g_engine->getScreen()->hLine(0, viewing_at_y - 2, 319,
+ boundaryLineColor);
+ g_engine->getScreen()->hLine(0, viewing_at_y + scr_work.y + 1,
+ 319, boundaryLineColor);
}
buffer_fill(scr_work, 0);
@@ -635,18 +641,10 @@ done:
timer_remove();
}
-void animview_main(const char *resName) {
- Presentation presentation;
- presentation.bufferHeight = 0;
- presentation.drawBoundaryLines = true;
- presentation.serviceFramesInline = false;
- animview_main(resName, presentation);
-}
-
void animview_main(const char *resName, const Presentation &presentation) {
char name[16];
presentationBufferHeight = presentation.bufferHeight;
- presentationDrawBoundaryLines = presentation.drawBoundaryLines;
+ presentationBoundaryLines = presentation.boundaryLines;
presentationServiceFramesInline = presentation.serviceFramesInline;
init_globals();
diff --git a/engines/mads/animview/animview.h b/engines/mads/animview/animview.h
index 8e0e071fc6e..0986bd16f1f 100644
--- a/engines/mads/animview/animview.h
+++ b/engines/mads/animview/animview.h
@@ -29,9 +29,15 @@
namespace MADS {
namespace AnimView {
+enum BoundaryLineMode {
+ kBoundaryLinesFromResource,
+ kBoundaryLinesHidden,
+ kBoundaryLinesShown
+};
+
struct Presentation {
int bufferHeight;
- bool drawBoundaryLines;
+ BoundaryLineMode boundaryLines;
bool serviceFramesInline;
};
@@ -56,6 +62,7 @@ extern int imageFrame;
extern CycleList anim_cycle_list;
extern bool has_cycles;
extern int currentViewX, currentViewY;
+extern int boundaryLineColor;
extern int concat_mode;
extern bool wait_for_music_at_end;
extern bool stop_music_at_end;
@@ -63,7 +70,6 @@ extern bool hasSpeechAudio;
extern int speechResourceId;
// Main animview function
-extern void animview_main(const char *resName);
extern void animview_main(const char *resName, const Presentation &presentation);
} // namespace AnimView
diff --git a/engines/mads/animview/functions.cpp b/engines/mads/animview/functions.cpp
index eb1d89cb3e6..dd9aa92b3ad 100644
--- a/engines/mads/animview/functions.cpp
+++ b/engines/mads/animview/functions.cpp
@@ -43,11 +43,11 @@ bool do_not_clear_screen;
/**
* Initialize globals
*/
-void functions_init() {
+void functions_init(bool showWhiteBars) {
anim_count = 0;
background_load_status = true;
sound_interrupts_mode = 0;
- show_white_bars = false;
+ show_white_bars = showWhiteBars;
resync_timer1 = true;
resync_timer2 = false;
exit_immediately_at_end = false;
diff --git a/engines/mads/animview/functions.h b/engines/mads/animview/functions.h
index 1b7f32d0b4c..5f27ad0a85a 100644
--- a/engines/mads/animview/functions.h
+++ b/engines/mads/animview/functions.h
@@ -50,7 +50,7 @@ extern bool do_not_clear_screen;
/**
* Initialize globals
*/
-extern void functions_init();
+extern void functions_init(bool showWhiteBars);
/**
* Adds an animation to the list of .aa files to show in sequence
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index aebaedac5c6..e53393ad3d6 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -525,8 +525,20 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
} while (going);
}
+static void matte_restore_boundary_lines(Buffer *work_screen,
+ int boundary_line_color) {
+ if (boundary_line_color < 0 || !viewing_at_y)
+ return;
+
+ g_engine->getScreen()->hLine(0, viewing_at_y - 2, video_x - 1,
+ boundary_line_color);
+ g_engine->getScreen()->hLine(0, viewing_at_y + work_screen->y + 1,
+ video_x - 1, boundary_line_color);
+}
+
static void matte_special_effect(int special_effect, int full_screen,
- bool full_fade_in, int fade_step_rate, long fade_end_time) {
+ bool full_fade_in, int fade_step_rate, long fade_end_time,
+ int boundary_line_color) {
int count;
int pixel_rate;
byte *background_swap;
@@ -572,6 +584,8 @@ static void matte_special_effect(int special_effect, int full_screen,
video_update(work_screen, 0, 0,
viewing_at_x, viewing_at_y,
work_screen->x, work_screen->y);
+ if (special_effect == MATTE_FX_FADE_THRU_BLACK)
+ matte_restore_boundary_lines(work_screen, boundary_line_color);
if (full_fade_in)
magic_fade_from_grey(&special_pal[0], master_palette,
@@ -624,6 +638,9 @@ static void matte_special_effect(int special_effect, int full_screen,
buffer_fill(scr_live, 0);
video_update(work_screen, 0, 0, viewing_at_x, viewing_at_y,
work_screen->x, work_screen->y);
+ matte_restore_boundary_lines(work_screen, boundary_line_color);
+ if (boundary_line_color >= 0)
+ g_engine->getScreen()->update();
mcga_setpal(&master_palette);
break;
@@ -644,7 +661,7 @@ static void matte_special_effect(int special_effect, int full_screen,
}
void matte_frame(int special_effect, int full_screen, bool full_fade_in,
- int fade_step_rate, long fade_end_time) {
+ int fade_step_rate, long fade_end_time, int boundary_line_color) {
Matte *matte;
Image *image;
int id;
@@ -953,7 +970,7 @@ void matte_frame(int special_effect, int full_screen, bool full_fade_in,
} else {
matte_special_effect(special_effect, full_screen, full_fade_in,
- fade_step_rate, fade_end_time);
+ fade_step_rate, fade_end_time, boundary_line_color);
sound_queue_flush();
}
}
diff --git a/engines/mads/core/matte.h b/engines/mads/core/matte.h
index de4fdb97913..671703ad440 100644
--- a/engines/mads/core/matte.h
+++ b/engines/mads/core/matte.h
@@ -127,9 +127,11 @@ extern int matte_map_work_screen();
// fade_end_time is in timer_read() ticks; -1 leaves reveal timing unchanged.
+// boundary_line_color restores AnimView rows after a through-black clear;
+// -1 leaves boundary pixels unmanaged.
extern void matte_frame(int special_effect, int full_screen,
bool full_fade_in = false, int fade_step_rate = 0,
- long fade_end_time = -1);
+ long fade_end_time = -1, int boundary_line_color = -1);
extern int matte_load_series(const char *name, int load_flags, int bonus_series_number);
extern void matte_deallocate_series(int id, int free_memory);
diff --git a/engines/mads/dragonsphere/main.cpp b/engines/mads/dragonsphere/main.cpp
index 2f409bbfee6..068dca0cfcf 100644
--- a/engines/mads/dragonsphere/main.cpp
+++ b/engines/mads/dragonsphere/main.cpp
@@ -46,6 +46,17 @@ namespace Dragonsphere {
constexpr bool SHOW_LINES = true;
constexpr byte LINE_COLOR = 2;
+static void runAnimView(const char *resource) {
+ AnimView::Presentation presentation;
+ presentation.bufferHeight = 0;
+ presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.serviceFramesInline = false;
+ if (ConfMan.hasKey("animview_boundary_lines"))
+ presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
+ AnimView::kBoundaryLinesShown : AnimView::kBoundaryLinesHidden;
+ AnimView::animview_main(resource, presentation);
+}
+
static void main_menu_main() {
auto &screen = *g_engine->getScreen();
Palette palette;
@@ -253,7 +264,7 @@ void dragonsphere_main() {
return;
case 3:
- AnimView::animview_main("@dragon");
+ runAnimView("@dragon");
selected_item = g_engine->isDemo() ? 0 : -1;
break;
@@ -263,7 +274,7 @@ void dragonsphere_main() {
case 9:
// Demo
- AnimView::animview_main("@demodisk");
+ runAnimView("@demodisk");
selected_item = 0;
break;
diff --git a/engines/mads/nebular/bonus/bonus.cpp b/engines/mads/nebular/bonus/bonus.cpp
index 0564cdb8ee7..08872324e61 100644
--- a/engines/mads/nebular/bonus/bonus.cpp
+++ b/engines/mads/nebular/bonus/bonus.cpp
@@ -13,6 +13,7 @@
#include "mads/nebular/bonus/bonus.h"
#include "common/array.h"
+#include "common/config-manager.h"
#include "common/file.h"
#include "common/func.h"
#include "common/path.h"
@@ -141,6 +142,17 @@ static bool checkPresentationFiles(const char *resourceName,
return reportMissingFiles(action, missingFiles);
}
+static void runAnimView(const char *resource) {
+ AnimView::Presentation presentation;
+ presentation.bufferHeight = 0;
+ presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.serviceFramesInline = false;
+ if (ConfMan.hasKey("animview_boundary_lines"))
+ presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
+ AnimView::kBoundaryLinesShown : AnimView::kBoundaryLinesHidden;
+ AnimView::animview_main(resource, presentation);
+}
+
class BonusApplication {
public:
explicit BonusApplication(Sound::RexSoundManager &soundManager) :
@@ -160,15 +172,15 @@ public:
switch (_ui.runMainMenu(_mainSelection)) {
case BonusTextUI::kDeathScenes:
if (checkPresentationFiles("death.res", "show the death scenes"))
- AnimView::animview_main("@death");
+ runAnimView("@death");
break;
case BonusTextUI::kEvolution:
if (checkPresentationFiles("evolve.res", "show the evolution sequence"))
- AnimView::animview_main("@evolve");
+ runAnimView("@evolve");
break;
case BonusTextUI::kSets:
if (checkPresentationFiles("sets.res", "show the Rex Nebular sets"))
- AnimView::animview_main("@sets");
+ runAnimView("@sets");
break;
case BonusTextUI::kMusic:
runMusicMenu();
diff --git a/engines/mads/nebular/mac_frontend.cpp b/engines/mads/nebular/mac_frontend.cpp
index 5237b504fd3..667a8f12bd6 100644
--- a/engines/mads/nebular/mac_frontend.cpp
+++ b/engines/mads/nebular/mac_frontend.cpp
@@ -45,7 +45,7 @@ static void runAnimViewContent(RexNebularEngine &engine,
const char *resource) {
AnimView::Presentation presentation;
presentation.bufferHeight = 200;
- presentation.drawBoundaryLines = false;
+ presentation.boundaryLines = AnimView::kBoundaryLinesHidden;
presentation.serviceFramesInline = true;
engine.getScreen()->clear();
diff --git a/engines/mads/nebular/main.cpp b/engines/mads/nebular/main.cpp
index 5a34dbec18b..5c01014381f 100644
--- a/engines/mads/nebular/main.cpp
+++ b/engines/mads/nebular/main.cpp
@@ -304,8 +304,16 @@ static void run_full_frame_animview(RexNebularEngine *engine,
const char *resource) {
if (g_engine->getPlatform() == Common::kPlatformMacintosh)
MacFrontend::runAnimView(*engine, resource);
- else
- AnimView::animview_main(resource);
+ else {
+ AnimView::Presentation presentation;
+ presentation.bufferHeight = 0;
+ presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.serviceFramesInline = false;
+ if (ConfMan.hasKey("animview_boundary_lines"))
+ presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
+ AnimView::kBoundaryLinesShown : AnimView::kBoundaryLinesHidden;
+ AnimView::animview_main(resource, presentation);
+ }
}
static void run_full_frame_textview(RexNebularEngine *engine,
diff --git a/engines/mads/phantom/main.cpp b/engines/mads/phantom/main.cpp
index 6247ccb74d9..897e266b4cd 100644
--- a/engines/mads/phantom/main.cpp
+++ b/engines/mads/phantom/main.cpp
@@ -49,6 +49,17 @@ namespace Phantom {
constexpr bool SHOW_LINES = true;
constexpr byte LINE_COLOR = 2;
+static void runAnimView(const char *resource) {
+ AnimView::Presentation presentation;
+ presentation.bufferHeight = 0;
+ presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.serviceFramesInline = false;
+ if (ConfMan.hasKey("animview_boundary_lines"))
+ presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
+ AnimView::kBoundaryLinesShown : AnimView::kBoundaryLinesHidden;
+ AnimView::animview_main(resource, presentation);
+}
+
static void main_menu_main() {
auto &screen = *g_engine->getScreen();
Palette palette;
@@ -267,7 +278,7 @@ void phantom_main() {
return;
case 3:
- AnimView::animview_main("@phantom");
+ runAnimView("@phantom");
selected_item = -1;
break;
@@ -276,7 +287,7 @@ void phantom_main() {
return;
case 9:
- AnimView::animview_main("@demodisk");
+ runAnimView("@demodisk");
selected_item = 0;
break;
Commit: 302570b42f9e343a4f7366d9333b3b1afc6b437c
https://github.com/scummvm/scummvm/commit/302570b42f9e343a4f7366d9333b3b1afc6b437c
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:23+10:00
Commit Message:
MADS: Add tunable AnimView minimum black interval
The wait begins after fade-in palette preparation, and its adjusted
completion deadline is returned to AnimView so later frame and sound
timing cannot catch up around the deliberate black interval.
Assisted-by: Codex:GPT-6
Changed paths:
engines/mads/animview/anim_timer.cpp
engines/mads/core/config.cpp
engines/mads/core/config.h
engines/mads/core/magic.cpp
engines/mads/core/magic.h
engines/mads/core/matte.cpp
engines/mads/core/matte.h
diff --git a/engines/mads/animview/anim_timer.cpp b/engines/mads/animview/anim_timer.cpp
index 77bf886e358..0fcb52a69cc 100644
--- a/engines/mads/animview/anim_timer.cpp
+++ b/engines/mads/animview/anim_timer.cpp
@@ -22,6 +22,7 @@
#include "mads/animview/anim_timer.h"
#include "mads/animview/animview.h"
#include "mads/animview/functions.h"
+#include "mads/core/config.h"
#include "mads/core/cycle.h"
#include "mads/core/matte.h"
#include "mads/core/mcga.h"
@@ -86,7 +87,9 @@ void anim_timer() {
int sound, count;
bool full_fade_in;
int fade_step_rate;
- long fade_end_time;
+ long completion_deadline;
+ long *completion_deadline_ptr;
+ int minimum_black_ticks;
if (current_error_code || speechResourceId != -1)
goto done;
@@ -276,19 +279,26 @@ block2:
g_engine->getGameID() == GType_Phantom;
fade_step_rate = g_engine->hasMacintoshInterface() ?
MACINTOSH_FADE_STEP_RATE : DOS_FADE_STEP_RATE;
- fade_end_time = -1;
- if (g_engine->getGameID() == GType_RexNebular &&
+ minimum_black_ticks = config_file.animview_minimum_black_ticks;
+ completion_deadline = timer1;
+ completion_deadline_ptr = nullptr;
+ if ((g_engine->getGameID() == GType_RexNebular ||
+ minimum_black_ticks > 0) &&
(runFx == MATTE_FX_FADE_FROM_BLACK ||
runFx == MATTE_FX_FADE_THRU_BLACK))
- fade_end_time = timer1;
+ completion_deadline_ptr = &completion_deadline;
// Rex and Phantom AnimView use the full 16-step fade-in. The later
// Dragonsphere executable uses the quick fade. DOS palette updates are
// paced by VGA retrace, while Macintosh fades use the 60 Hz TickCount.
- // When Rex finishes fade preparation early, keep the remaining transition
- // time black so the fade-in ends at the existing animation deadline.
- matte_frame(runFx, 0, full_fade_in, fade_step_rate, fade_end_time,
- boundaryLineColor);
+ // Rex uses its existing transition deadline to keep an early fade-in black.
+ // The optional minimum starts after incoming-palette preparation and is a
+ // fast-host presentation policy, not a native delay. If it extends the
+ // reveal, move the completion deadline used by subsequent scheduling.
+ matte_frame(runFx, 0, full_fade_in, fade_step_rate,
+ completion_deadline_ptr, minimum_black_ticks, boundaryLineColor);
+ if (completion_deadline_ptr != nullptr)
+ timer1 = completion_deadline;
mouse_hide();
block3:
diff --git a/engines/mads/core/config.cpp b/engines/mads/core/config.cpp
index 8e0783531c6..53f38abf216 100644
--- a/engines/mads/core/config.cpp
+++ b/engines/mads/core/config.cpp
@@ -46,6 +46,7 @@ void init_config() {
config_file.screen_fade = SCREEN_FADE_SMOOTH;
config_file.panning_speed = PANNING_SMOOTH;
config_file.mouse_cursor_fix = MOUSE_MICROSOFT;
+ config_file.animview_minimum_black_ticks = 0;
}
void read_config_file() {
@@ -59,6 +60,7 @@ void read_config_file() {
ConfMan.registerDefault("show_speech_boxes", true);
ConfMan.registerDefault("naughtiness", true);
ConfMan.registerDefault("difficulty", -1);
+ ConfMan.registerDefault("animview_minimum_black_ticks", 0);
config_file.music_flag = !ConfMan.getBool("music_mute") && !ConfMan.getBool("mute");
config_file.sound_flag = !ConfMan.getBool("sfx_mute") && !ConfMan.getBool("mute");
@@ -72,6 +74,10 @@ void read_config_file() {
config_file.quotes_enabled = ConfMan.getBool("quotes_enabled");
config_file.screen_fade = ConfMan.getInt("screen_fade");
config_file.panning_speed = ConfMan.getInt("panning_speed");
+ config_file.animview_minimum_black_ticks =
+ ConfMan.getInt("animview_minimum_black_ticks");
+ if (config_file.animview_minimum_black_ticks < 0)
+ config_file.animview_minimum_black_ticks = 0;
config_file.show_speech_boxes = ConfMan.getBool("show_speech_boxes");
config_file.original_save_load = ConfMan.getBool("original_menus");
diff --git a/engines/mads/core/config.h b/engines/mads/core/config.h
index c03da9ffc63..af92acd6cf0 100644
--- a/engines/mads/core/config.h
+++ b/engines/mads/core/config.h
@@ -89,6 +89,7 @@ struct ConfigFile {
bool speech_version_installed; /* Version with speech installed */
bool show_speech_boxes; /* Show text during speech */
bool original_save_load; /* Original vs ScummVM dialogs */
+ int animview_minimum_black_ticks;
int misc2;
int misc3;
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index 3b274e7af80..b4d6f5c41e4 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -90,20 +90,26 @@ void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
}
}
-static void magic_wait_for_fade_start(long fade_end_time, int steps,
- int fade_step_rate) {
- if (fade_end_time < 0 || fade_step_rate <= 0)
+void magic_wait_for_fade_start(long *completion_deadline, int steps,
+ int fade_step_rate, int minimum_black_ticks) {
+ if (completion_deadline == nullptr || *completion_deadline < 0 ||
+ fade_step_rate <= 0)
return;
const long fade_ticks = (steps * TIMER_TICKS_PER_SECOND +
fade_step_rate - 1) /
fade_step_rate;
- const long fade_start_time = fade_end_time - fade_ticks;
+ const long deadline_start = *completion_deadline - fade_ticks;
long now = timer_read();
+ long fade_start = deadline_start;
+ if (minimum_black_ticks > 0) {
+ fade_start = MAX(deadline_start, now + minimum_black_ticks);
+ *completion_deadline = fade_start + fade_ticks;
+ }
// timer_read() services events while retaining the animation's 60 Hz
// clock domain.
- while (now < fade_start_time) {
+ while (now < fade_start) {
now = timer_read();
}
}
@@ -358,7 +364,8 @@ done:
void magic_fade_from_grey(RGBcolor *pal, Palette target,
int base_color, int num_colors,
int base_grey, int num_greys,
- int tick_delay, int steps, int fade_step_rate, long fade_end_time) {
+ int tick_delay, int steps, int fade_step_rate, long *completion_deadline,
+ int minimum_black_ticks) {
int count;
int color;
int index;
@@ -419,7 +426,8 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
}
if (fade_step_rate > 0) {
- magic_wait_for_fade_start(fade_end_time, steps, fade_step_rate);
+ magic_wait_for_fade_start(completion_deadline, steps, fade_step_rate,
+ minimum_black_ticks);
magic_fade_pacer_init(fade_pacer);
} else {
base_timing = timer_read_600();
diff --git a/engines/mads/core/magic.h b/engines/mads/core/magic.h
index 2753a99bb47..6539a3ace42 100644
--- a/engines/mads/core/magic.h
+++ b/engines/mads/core/magic.h
@@ -84,13 +84,18 @@ extern void magic_grey_ramp_palette(Palette &pal, int num_greys);
extern void magic_fade_pacer_init(MagicFadePacer &pacer);
extern void magic_fade_pacer_wait(MagicFadePacer &pacer, int step,
int fade_step_rate);
+extern void magic_wait_for_fade_start(long *completion_deadline, int steps,
+ int fade_step_rate, int minimum_black_ticks);
extern void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int base_color, int num_colors, int base_grey, int num_greys,
int tick_delay, int steps, int fade_step_rate = 0);
-// fade_end_time is in timer_read() ticks; -1 leaves reveal timing unchanged.
+// completion_deadline uses timer_read() ticks and is updated when a minimum
+// black interval after palette preparation moves the reveal. nullptr leaves
+// reveal timing unchanged.
extern void magic_fade_from_grey(RGBcolor *pal, Palette target, int base_color,
int num_colors, int base_grey, int num_greys, int tick_delay, int steps,
- int fade_step_rate = 0, long fade_end_time = -1);
+ int fade_step_rate = 0, long *completion_deadline = nullptr,
+ int minimum_black_ticks = 0);
/**
* Picture-to-picture transition in which one of the screen
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index e53393ad3d6..5c69f14a61d 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -458,9 +458,11 @@ void filter_matte_list(MattePtr matte, int size, int base_index) {
}
static void matte_quick_from_black(byte *special_pal, int ticks,
- int fade_step_rate) {
+ int fade_step_rate, long *completion_deadline,
+ int minimum_black_ticks) {
int going;
int step = 0;
+ int fade_steps = 1;
byte *source;
byte *dest;
byte *special;
@@ -479,10 +481,16 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
if (inc == 0)
inc = 1;
special[i] = inc;
+ if (source[i])
+ fade_steps = MAX(fade_steps,
+ ((int)source[i] + inc - 1) / inc);
}
- if (fade_step_rate > 0)
+ if (fade_step_rate > 0) {
+ magic_wait_for_fade_start(completion_deadline, fade_steps,
+ fade_step_rate, minimum_black_ticks);
magic_fade_pacer_init(fade_pacer);
+ }
do {
going = false;
@@ -537,8 +545,8 @@ static void matte_restore_boundary_lines(Buffer *work_screen,
}
static void matte_special_effect(int special_effect, int full_screen,
- bool full_fade_in, int fade_step_rate, long fade_end_time,
- int boundary_line_color) {
+ bool full_fade_in, int fade_step_rate, long *completion_deadline,
+ int minimum_black_ticks, int boundary_line_color) {
int count;
int pixel_rate;
byte *background_swap;
@@ -590,10 +598,11 @@ static void matte_special_effect(int special_effect, int full_screen,
if (full_fade_in)
magic_fade_from_grey(&special_pal[0], master_palette,
0, 256, 0, 1, 1, 16, fade_step_rate,
- fade_end_time);
+ completion_deadline, minimum_black_ticks);
else
matte_quick_from_black(&special_pal[0].r, 1,
- fade_step_rate);
+ fade_step_rate, completion_deadline,
+ minimum_black_ticks);
break;
case MATTE_FX_CORNER_LOWER_LEFT:
@@ -661,7 +670,8 @@ static void matte_special_effect(int special_effect, int full_screen,
}
void matte_frame(int special_effect, int full_screen, bool full_fade_in,
- int fade_step_rate, long fade_end_time, int boundary_line_color) {
+ int fade_step_rate, long *completion_deadline,
+ int minimum_black_ticks, int boundary_line_color) {
Matte *matte;
Image *image;
int id;
@@ -970,7 +980,8 @@ void matte_frame(int special_effect, int full_screen, bool full_fade_in,
} else {
matte_special_effect(special_effect, full_screen, full_fade_in,
- fade_step_rate, fade_end_time, boundary_line_color);
+ fade_step_rate, completion_deadline,
+ minimum_black_ticks, boundary_line_color);
sound_queue_flush();
}
}
diff --git a/engines/mads/core/matte.h b/engines/mads/core/matte.h
index 671703ad440..2a70c5fb7b7 100644
--- a/engines/mads/core/matte.h
+++ b/engines/mads/core/matte.h
@@ -125,13 +125,15 @@ extern TileResource picture_resource, depth_resource; /* Tile resources */
extern void matte_init(int init_series);
extern int matte_map_work_screen();
-
-// fade_end_time is in timer_read() ticks; -1 leaves reveal timing unchanged.
+// completion_deadline uses timer_read() ticks and is updated when a minimum
+// black interval after palette preparation moves the reveal. nullptr leaves
+// reveal timing unchanged.
// boundary_line_color restores AnimView rows after a through-black clear;
// -1 leaves boundary pixels unmanaged.
extern void matte_frame(int special_effect, int full_screen,
bool full_fade_in = false, int fade_step_rate = 0,
- long fade_end_time = -1, int boundary_line_color = -1);
+ long *completion_deadline = nullptr, int minimum_black_ticks = 0,
+ int boundary_line_color = -1);
extern int matte_load_series(const char *name, int load_flags, int bonus_series_number);
extern void matte_deallocate_series(int id, int free_memory);
Commit: b232221e6136aaaa64ea25947e90a4179fbfd564
https://github.com/scummvm/scummvm/commit/b232221e6136aaaa64ea25947e90a4179fbfd564
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:23+10:00
Commit Message:
MADS: Set AnimView min extra black interval to 500ms
Fast-host presentation adjustment, not recovered from executables.
Changed paths:
engines/mads/core/config.cpp
diff --git a/engines/mads/core/config.cpp b/engines/mads/core/config.cpp
index 53f38abf216..16849f14e31 100644
--- a/engines/mads/core/config.cpp
+++ b/engines/mads/core/config.cpp
@@ -28,6 +28,8 @@
namespace MADS {
+static const int DEFAULT_MINIMUM_BLACK_TICKS = 30;
+
ConfigFile config_file;
void init_config() {
@@ -46,7 +48,7 @@ void init_config() {
config_file.screen_fade = SCREEN_FADE_SMOOTH;
config_file.panning_speed = PANNING_SMOOTH;
config_file.mouse_cursor_fix = MOUSE_MICROSOFT;
- config_file.animview_minimum_black_ticks = 0;
+ config_file.animview_minimum_black_ticks = DEFAULT_MINIMUM_BLACK_TICKS;
}
void read_config_file() {
@@ -60,7 +62,7 @@ void read_config_file() {
ConfMan.registerDefault("show_speech_boxes", true);
ConfMan.registerDefault("naughtiness", true);
ConfMan.registerDefault("difficulty", -1);
- ConfMan.registerDefault("animview_minimum_black_ticks", 0);
+ ConfMan.registerDefault("animview_minimum_black_ticks", -1);
config_file.music_flag = !ConfMan.getBool("music_mute") && !ConfMan.getBool("mute");
config_file.sound_flag = !ConfMan.getBool("sfx_mute") && !ConfMan.getBool("mute");
@@ -77,7 +79,7 @@ void read_config_file() {
config_file.animview_minimum_black_ticks =
ConfMan.getInt("animview_minimum_black_ticks");
if (config_file.animview_minimum_black_ticks < 0)
- config_file.animview_minimum_black_ticks = 0;
+ config_file.animview_minimum_black_ticks = DEFAULT_MINIMUM_BLACK_TICKS;
config_file.show_speech_boxes = ConfMan.getBool("show_speech_boxes");
config_file.original_save_load = ConfMan.getBool("original_menus");
Commit: 813622bbc9be609b35c1dadcbf4de321e984bbe5
https://github.com/scummvm/scummvm/commit/813622bbc9be609b35c1dadcbf4de321e984bbe5
Author: fusefib (fibofuse at gmail.com)
Date: 2026-09-18T17:02:23+10:00
Commit Message:
MADS: NEBULAR: Keep AnimView boundary lines hidden
Changed paths:
engines/mads/nebular/bonus/bonus.cpp
engines/mads/nebular/main.cpp
diff --git a/engines/mads/nebular/bonus/bonus.cpp b/engines/mads/nebular/bonus/bonus.cpp
index 08872324e61..e00d722813a 100644
--- a/engines/mads/nebular/bonus/bonus.cpp
+++ b/engines/mads/nebular/bonus/bonus.cpp
@@ -145,7 +145,7 @@ static bool checkPresentationFiles(const char *resourceName,
static void runAnimView(const char *resource) {
AnimView::Presentation presentation;
presentation.bufferHeight = 0;
- presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.boundaryLines = AnimView::kBoundaryLinesHidden;
presentation.serviceFramesInline = false;
if (ConfMan.hasKey("animview_boundary_lines"))
presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
diff --git a/engines/mads/nebular/main.cpp b/engines/mads/nebular/main.cpp
index 5c01014381f..2cb427817a6 100644
--- a/engines/mads/nebular/main.cpp
+++ b/engines/mads/nebular/main.cpp
@@ -307,7 +307,7 @@ static void run_full_frame_animview(RexNebularEngine *engine,
else {
AnimView::Presentation presentation;
presentation.bufferHeight = 0;
- presentation.boundaryLines = AnimView::kBoundaryLinesFromResource;
+ presentation.boundaryLines = AnimView::kBoundaryLinesHidden;
presentation.serviceFramesInline = false;
if (ConfMan.hasKey("animview_boundary_lines"))
presentation.boundaryLines = ConfMan.getBool("animview_boundary_lines") ?
Commit: 0bc6ab70f5213976ac0a4a3956cb22027778835b
https://github.com/scummvm/scummvm/commit/0bc6ab70f5213976ac0a4a3956cb22027778835b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-09-18T17:02:43+10:00
Commit Message:
MADS: Fix uninitialized local warnings
Changed paths:
engines/mads/core/magic.cpp
engines/mads/core/matte.cpp
diff --git a/engines/mads/core/magic.cpp b/engines/mads/core/magic.cpp
index b4d6f5c41e4..09fbf336d15 100644
--- a/engines/mads/core/magic.cpp
+++ b/engines/mads/core/magic.cpp
@@ -263,7 +263,7 @@ void magic_fade_to_grey(Palette &pal, byte *map_pointer,
int step;
int intensity;
int shift_sign;
- long base_timing, now_timing;
+ long base_timing = 0, now_timing;
long memory_needed;
MagicFadePacer fade_pacer;
byte *work_memory = NULL;
@@ -373,7 +373,7 @@ void magic_fade_from_grey(RGBcolor *pal, Palette target,
int step;
int intensity;
int shift_sign;
- long base_timing, now_timing;
+ long base_timing = 0, now_timing;
long memory_needed;
MagicFadePacer fade_pacer;
byte *work_memory = NULL;
diff --git a/engines/mads/core/matte.cpp b/engines/mads/core/matte.cpp
index 5c69f14a61d..7506a46e0d5 100644
--- a/engines/mads/core/matte.cpp
+++ b/engines/mads/core/matte.cpp
@@ -467,7 +467,7 @@ static void matte_quick_from_black(byte *special_pal, int ticks,
byte *dest;
byte *special;
byte increments[768];
- long fade_clock;
+ long fade_clock = 0;
long now_clock;
MagicFadePacer fade_pacer;
More information about the Scummvm-git-logs
mailing list