• src/doors/syncconquer/PRO

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Thursday, July 09, 2026 01:55:00
    https://gitlab.synchro.net/main/sbbs/-/commit/96c85102da3a401c7ce1df48
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/door/door_io.c src/doors/syncconquer/vanilla/common/ini.h paths.cpp
    Log Message:
    syncalert: fix the startup abort on Ubuntu; -home is optional again

    Nightfox reported syncalert dying immediately with "*** buffer overflow detected ***: terminated" (SIGABRT) on his BBS, from a Release binary,
    and supplied a core. Reading the libc build-id out of the core's first
    mapped page and pulling the matching debuginfo named the frame:
    __realpath_chk -> __chk_fail -> __fortify_fail -> abort.

    door_realpath() resolved paths into a local `char buf[1024]`. glibc
    requires a caller-supplied realpath() buffer to hold at least PATH_MAX
    (4096) bytes, and _FORTIFY_SOURCE enforces that via __realpath_chk(),
    which abort()s whenever the destination's known object size is under
    PATH_MAX -- unconditionally, no matter how short the resolved path
    actually is. Ubuntu's gcc default-enables -D_FORTIFY_SOURCE at -O2 and
    Debian's does not, so identical source killed the door on his host and
    ran fine here. Pass NULL instead (POSIX.1-2008 has realpath() malloc the result) and free it, which sidesteps PATH_MAX entirely; Windows keeps _fullpath() with a _MAX_PATH buffer. Verified by rebuilding with -D_FORTIFY_SOURCE=2, which reproduces the abort exactly before the fix
    and runs clean after. The vendored engine was already correct here (paths_posix.cpp uses char[PATH_MAX]; Argv_Path() writes into a
    std::string), so this was the only affected call site.

    Two further startup bugs surfaced while reproducing it:

    Without -home, door_setup_engine_paths() returned early and wrote no REDALERT.INI at all, so the engine never learned DataPath and Bootstrap() aborted on LOCAL.MIX -- even with a valid assets dir beside the binary.
    A bare launch could not work. -home now defaults to the launch directory, matching SyncDuke and SyncDOOM.

    INIClass::MAX_LINE_LENGTH was 128, truncating every INI line at 128
    characters, and PathsClass::Init() read [Paths] into a matching
    char[128]. The DataPath=/UserPath= values the door writes are filesystem
    paths, so a deep-enough install silently lost its assets directory and
    died in Bootstrap() the same way. Raised to 4224 (a PATH_MAX value plus
    its key) and the paths.cpp buffer sized as char[PATH_MAX]. Both vendored
    edits are recorded in PROVENANCE.md as local patch 9.

    Verified against both toolchain configurations (fortified, as Ubuntu
    builds it, and plain as Debian does): a bare no-arg launch, Nightfox's
    exact `-home <dir>/` invocation, -home with -assets, and a 135-character DataPath all now reach the render loop instead of aborting.

    Reported-by: Nightfox (Digital Distortion BBS)

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 10, 2026 23:41:00
    https://gitlab.synchro.net/main/sbbs/-/commit/334b2006e09f9201ac4c2afc
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/redalert/function.h goptions.cpp menus.cpp version.cpp
    Log Message:
    syncalert: show the door version in the Red Alert menus

    Add SyncAlert_Version_Name() (version.cpp) -- "<ver> <date> synchro.net"
    from SYNCALERT_VERSION (a #define, bump per release) plus the build's git commit date (common/gitinfo.h). It is drawn next to the engine's
    Version_Name() in the main-menu and options footers, so the engine revision
    and edition/expansion markers (E/CS/AM/D) are preserved and the door version sits with them. The git short-hash is left to the engine line -- it is the identical value, so the door line omits it to avoid duplication.

    Placement adapts to the dialog width: the options box is wide enough for one combined line (scenario name, then engine + door version); the narrower main menu puts the door version on its own centered line below the engine version, both centered as two separate prints (a single "%s\r%s" TPF_CENTER print only centers one line).

    Vendored menu edits recorded in PROVENANCE.md (patch 12).

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Saturday, July 11, 2026 04:50:00
    https://gitlab.synchro.net/main/sbbs/-/commit/96140261b2f6c68e41f59442
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/door/door_input.c door_io.c soundio_termgfx.cpp src/doors/syncconquer/vanilla/redalert/options.cpp sounddlg.cpp
    Log Message:
    syncalert: make the Sound Controls usable over the terminal

    Three fixes to in-game music/sound control, all driven by SyncTERM play:

    Widen the volume sliders. The Options sound sliders were a 10px-tall band
    at 640x400 (5 base units x RESFACTOR 2), narrower than one text-cell row, so
    a cell-granular mouse (SyncTERM has no SGR-Pixels) could click between the
    band and miss it. Raised to 8 base = one full 16px cell row, so a click
    always lands.

    Add +/- music-volume hotkeys. SyncAlert_Music_Volume_Step() (options.cpp, extern "C") nudges Options.ScoreVolume ~10% and applies it via the same Set_Score_Volume() the slider uses, so the two agree and it persists. The
    door intercepts +/=/-/_ in all keyboard modes: door_io_hotkey()'s raw-byte branch (legacy/kitty) and door_input.c's evdev decoder (SyncTERM).

    Fix the "stop" button. The score loops CLIENT-SIDE (termgfx uploads it once
    and the terminal loops it), so SoundImp_Stop_Sample() resetting our tracker never silenced it -- only door exit called termgfx_audio_music_stop(). But stopping immediately would gap a theme CHANGE (which also stops the old
    tracker before starting the new). So arm a deferred stop on Stop_Sample of a music tracker, cancel it when a new score stream starts (a theme change),
    and otherwise fire it ~120ms later from a per-tick housekeep. A real Theme.Stop() (the stop button, THEME_QUIET) now goes quiet; song changes
    stay seamless.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Saturday, July 11, 2026 04:50:00
    https://gitlab.synchro.net/main/sbbs/-/commit/f7154ef1336296e8ec07c643
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/door/door_io.c src/doors/syncconquer/vanilla/redalert/egos.cpp init.cpp menus.cpp options.cpp scroll.cpp sounddlg.cpp startup.cpp
    Log Message:
    syncalert: live-test fixes -- UI polish, portable INI, volume persistence

    A batch of fixes from SyncTERM live testing, spanning the door's own code
    and small tracked patches to the vendored engine (PROVENANCE #15-20).

    UI polish:
    - Stats overlay (Ctrl-S) now erases to end of line before its SGR reset, so
    a shorter readout can't leave the previous longer line's tail on screen;
    the bottom row renders as a clean full-width bar (door_io.c).
    - Mouse edge-scroll: widened RA's literal 1-pixel scroll border to a small
    band (scroll.cpp). Over a terminal the pointer is cell-quantized and can't
    land on the exact edge pixel -- especially the top/left, which the door
    can't snap to 0 without breaking menu-button clicks -- so edge-scroll was
    nearly unusable. No click coordinate moves, so menus are unaffected.
    - Sound Controls music slider now tracks changes made from outside the
    dialog (the +/- hotkeys) without fighting the mouse: it re-syncs only when
    ScoreVolume itself changes, never overriding the gauge from its own value
    (sounddlg.cpp).

    Portable per-user INI (door_io.c):
    - The per-user REDALERT.INI is shared across hosts, so it must not carry
    host-specific absolute paths. UserPath is now dropped entirely (the argv[0]
    rewrite already makes the engine derive it), and DataPath is written
    relative to the per-user home via a new door_relpath() helper. Stale
    absolute keys a previous build wrote are actively removed.

    Music-volume persistence and behavior:
    - Root cause of "settings never persist": on exit RA re-saved the config from
    the INIClass it loaded at startup, and INIClass::Load() merges rather than
    replaces, so the reload kept the startup snapshot and clobbered whatever the
    game wrote during play. Fixed by using a fresh INIClass for the exit save
    (startup.cpp).
    - Save_Settings now always writes ScoreVolume (was gated to GAME_NORMAL under
    FIXIT_VERSION_3), and the +/- hotkey persists via Save_Settings, guarded at
    the rails (options.cpp).
    - Muting is respected everywhere: removed the force-to-0.4-when-muted override
    on the title screen and credits scroll (menus.cpp, egos.cpp), and stopped a
    Skirmish/MP game from overwriting the user's volume with the separate
    MultiScoreVolume -- the single music-volume setting is now authoritative
    (init.cpp).

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sunday, July 12, 2026 05:11:00
    https://gitlab.synchro.net/main/sbbs/-/commit/aba585c60b398ba1212259c7
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/redalert/visudlg.cpp src/doors/syncconquer/vanilla/tiberiandawn/gamedlg.cpp goptions.cpp menus.cpp visudlg.cpp
    Log Message:
    syncconquer: menu-version placement + wider option sliders

    Live-test polish for both titles' terminal menus.

    syncdawn version line (PROVENANCE #26 refined): main-menu footer now
    centers the engine version and the door version as two stacked rows,
    raised one row off the dialog's bottom edge (like Red Alert); the
    options-menu footer is shifted left off the box edge so it clears the
    buttons. menus.cpp / goptions.cpp.

    Wider option sliders for SyncTERM's cell-granular (~16px) mouse -- the
    same fix as the Sound Controls sliders (patches 13/25):

    - syncdawn Scroll Rate slider (tiberiandawn/gamedlg.cpp): 6->8 factor =
    16px = one cell row (PROVENANCE #27). Game Speed left as-is (hittable
    in practice).
    - Visual Controls sliders (Brightness/Color/Contrast/Tint) in BOTH
    redalert/visudlg.cpp and tiberiandawn/visudlg.cpp: the shared slider
    height 5->8 factor = 16px; the 22px inter-slider spacing already leaves
    room (PROVENANCE #28). This also clears a pre-existing visual artifact
    on the RA sliders that the door's software-fill fix had already fixed.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Monday, July 13, 2026 00:36:00
    https://gitlab.synchro.net/main/sbbs/-/commit/ddd71e8e444a1e1f4a5f1d45
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/tiberiandawn/startup.cpp
    Log Message:
    syncconquer: syncdawn -- no stray Win32 window, no hang on exit (Windows)

    Patch #11 fixed two things for Red Alert on the strength of one observation: the headless door has no Win32 window and no message pump. Neither premise was ever carried over to Tiberian Dawn, and on Windows both bit.

    The stray window. TD's main() called Create_Main_Window() under a bare
    #if defined(_WIN32) && !defined(SDL_BUILD). RA calls it too, but hands it command_show = 0 -- SW_HIDE -- so RA's window is created and never seen. TD instead derives command_show from GetStartupInfo() (SW_SHOWDEFAULT when the parent set no STARTF_USESHOWWINDOW) and follows it with ShowWindow() + UpdateWindow() + SetFocus() on a WS_EX_TOPMOST | WS_POPUP | WS_MAXIMIZE
    window. A door whose video goes to the CALLER's terminal was thus popping a fullscreen, always-on-top "Command & Conquer" window onto the BBS machine's
    own desktop and taking focus there. Gate the call on !NEW_VIDEO_BUILD: MainWindow stays NULL, which is what the rest of the door already assumes.

    That window's title rendered as CJK glyphs, and the cause is worth recording because it is upstream's, not ours: both winstubs register the class with RegisterClassA and pass an ANSI title to CreateWindowExA, then fall through to the DefWindowProc *macro* -- DefWindowProcW under this UNICODE build. The default WM_NCCREATE handler duly reads "Command & Conquer" as UTF-16. Upstream never sees it: its Windows build defines SDL_BUILD and compiles none of this.

    The exit hang. main()'s ReadyToQuit spin-wait -- PostMessage(WM_DESTROY), then do { Keyboard->Check(); } while (ReadyToQuit == 1) -- was likewise ungated. With no pump nothing can run Windows_Procedure()'s WM_DESTROY case to advance ReadyToQuit 1 -> 2, so "Exit Game" spun forever burning CPU: precisely the
    hang patch #11 cured for RA. Same guard. Corroborated by the door's own node log -- a Windows syncdawn session ends without the stats[exit] line that every syncalert session prints.

    A NULL MainWindow is safe here: what remains are MessageBoxA(NULL, ...) on fatal paths, the harmless PostMessage() above, multiplayer-only netdlg.cpp
    (the door is single-player), and common/vqaloader.cpp's VQA_OpenAudio(handle, MainWindow), whose hwnd door/vqaaudio_termgfx.cpp ignores. TD's GameInFocus is already unconditionally true -- RA needed patching to force it, TD does not -- so losing the window's WM_ACTIVATEAPP does not silence TD's audio.

    Vendored patch, recorded as PROVENANCE #33. Both binaries still link clean at Win32/Release.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Tuesday, July 14, 2026 21:04:00
    https://gitlab.synchro.net/main/sbbs/-/commit/d67fe28c6134d826fa6ba258
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/tiberiandawn/sounddlg.cpp
    Log Message:
    syncconquer: TD Sound Controls music slider tracks +/- volume hotkey

    The door's +/- music-volume hotkeys call Options.Set_Score_Volume()
    directly, but Tiberian Dawn's Sound Controls dialog set the music slider's value only once at open -- so pressing +/- with the dialog up changed the volume audibly while the thumb stayed put. Red Alert already re-syncs its slider (PROVENANCE patch #15); this ports the same fix to tiberiandawn/sounddlg.cpp: track last_score = Options.ScoreVolume and, each dialog loop, music.Set_Value() + redraw when ScoreVolume changed. Triggered
    on a change to ScoreVolume (not gauge != ScoreVolume) so a mouse drag isn't snapped back mid-drag. Recorded as PROVENANCE #34.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Tuesday, July 14, 2026 21:04:00
    https://gitlab.synchro.net/main/sbbs/-/commit/7830701b3722cbb378548619
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/redalert/display.cpp src/doors/syncconquer/vanilla/tiberiandawn/display.cpp
    Log Message:
    syncconquer: full tactical redraw on scroll (fix staircase/black tearing)

    DisplayClass::Draw_It() scrolls the map by reusing already-drawn pixels -- shifting the replicable block via an overlapping self-blit (HidPage.Blit(HidPage, ...)) or, for a DirectDraw hid page, a SeenBuff.Blit(HidPage, ...) -- then redraws only the newly exposed edge. Neither moves content correctly in the door's plain software framebuffer,
    so any scroll (most visibly the Home/End jump) left staircase remnants of shifted sprites plus black gaps in the 640x400 buffer. It showed the same
    under sixel and JXL -- the framebuffer itself was torn, not the transport.

    Force forced = true as soon as a scroll is detected, so the blit-shift
    branch is skipped and the engine's own full-redraw path (its existing else fallback, already used for full-size scrolls) runs instead. Same class as
    the AllowHardwareBlitFills FillRect no-op. Costs the door nothing -- it re-encodes the whole frame every present regardless. Applied to both
    engines; PROVENANCE patch #35.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:53:00
    https://gitlab.synchro.net/main/sbbs/-/commit/fc45ba2c317afdcd90e159db
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/door/door_io.c src/doors/syncconquer/vanilla/redalert/conquer.cpp src/doors/syncconquer/vanilla/tiberiandawn/conquer.cpp xtrn/syncalert/syncalert.example.ini xtrn/syncdawn/syncdawn.example.ini
    Log Message:
    syncconquer: auto-skip FMV cutscenes when the client has no audio

    With "[game] movies" left at its default, a cutscene now plays only when
    the client can actually play audio (termgfx_audio_tier == 1, i.e. SyncTERM
    with a working decoder); on a terminal with no usable audio it is skipped.
    A silent FMV is a poor experience and one of the heaviest things the door
    sends over a terminal, so playing it there is a bad trade.

    The setting is now tri-state:
    movies = true -> always play
    movies = false -> never play
    absent (default)-> auto: play only if the client has audio

    The decision is made at play time, from each engine's Play_Movie(), via a
    new extern "C" door_movies_suppressed() in door_io.c -- not at startup
    where -NOMOVIES is set. The audio capability only resolves a few client round-trips into the session (async), but a cutscene doesn't play until
    after asset loading, well after the tier is known.

    Side effect: a latent bug fixed. Tiberian Dawn never read bNoMovies (an
    RA-only global), so "[game] movies = false" was a no-op for syncdawn; the shared play-time gate now honors it for both titles.

    Plumbing: the <door>.ini path resolution is factored into door_ini_path(),
    and door_config_tristate() distinguishes an absent key from an explicit true/false (validated against xpdev iniReadBool). Documented in the sample syncalert.example.ini / syncdawn.example.ini; engine seam is PROVENANCE #36.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/d72fbfc27d9db84541dd38cb
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/tiberiandawn/conquer.cpp
    Log Message:
    syncdawn: cut mission-load time ~12s -> ~3s (skip per-mix CD search)

    A TD mission load took ~12s. Instrumenting the phases showed the whole
    cost is in Map.Load's Init_Theater, and within that in the MixFileClass constructor's first action -- Force_CD_Available(RequiredCD) -- which runs
    for every mix built during the load. Its Change_Local_Dir path re-runs the search-drive detection/refresh on a CD-number change: ~3s for the theater
    mix, ~5.9s for the (missing) hi-res icon mix. It is not I/O -- the mix read
    is a buffered fread and localhost SMB is fast.

    The door keeps all game data in one local assets dir that is already on the file search path, so there is never a CD to swap. Short-circuit the search
    and return true. But Change_Local_Dir also does the audio setup a CD-number change needs (stop the theme, reinit the score mixfiles, rescan themes), and skipping that left the sound system mis-initialized after a load (a buzzer where the theme should be), so we still do it -- once per actual CD change, gated by a static last-CD. The original body is kept under #if 0.

    Vendored edit (PROVENANCE #37); the read path itself was never the problem. Load drops from ~12s to ~3s, audio intact.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/80caf1a94951729bbdad1216
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/redalert/scroll.cpp src/doors/syncconquer/vanilla/tiberiandawn/scroll.cpp
    Log Message:
    syncconquer: fix mouse edge-scroll for a pixel-granular terminal mouse

    With SGR-Pixels (1016) mouse reporting, the map was very hard to edge-scroll and, once triggered, scrolled far too fast.

    Trigger: a pixel mouse can't reliably hit the exact edge pixel over a scaled terminal window, so the tight edge band was near-unreachable. Widen it in
    pixel mode (gated on the door's live door_io_mouse_pixels): RA's 8*RESFACTOR band -> 24*RESFACTOR. TD's scroll.cpp had only the stock literal 1-pixel
    border and never got RA's earlier band fix at all -- give it a 48/16-px band too (measured from where the pointer actually lands, ~gx 591-635 at the right edge, so it catches the hover zone).

    Speed: ScrollClass::AI runs at the door's fast present rate, so a full _rate[] step per tick made the map fly. Cut the auto/mouse edge-scroll step to _rate[rate] / 4 -- RA already had this (PROVENANCE #29); mirror it to TD. Keyboard/inertia scroll is untouched.

    Vendored edits, PROVENANCE #38 (extends the #16 RA band to the pixel-mouse
    case and to TD). Cell-quantized (non-pixel) clients keep the tighter band.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/463b95ce485c232db95835b1
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/door/door_io.c src/doors/syncconquer/vanilla/tiberiandawn/audio.cpp xtrn/syncdawn/syncdawn.example.ini
    Log Message:
    syncdawn: caption the EVA voice announcements on-screen (accessibility)

    The spoken EVA callouts -- "construction complete", "unit ready", "our base
    is under attack", "insufficient funds", and so on -- are audio-only in stock C&C, so a deaf player or anyone on a no-audio terminal gets nothing. They all funnel through a single Speak(VoxType, ...), and the game already has an on-screen message list, so captioning them is self-contained: a VoxCaption[VOX_COUNT] table (aligned to the existing Speech[] table) plus a Messages.Add_Message() in Speak(), deduped by voice.

    Gated by <door>.ini "[game] captions" (new door_captions_enabled() in door_io.c): true = always, false = never, absent/auto = on only when the
    client has no usable audio -- the accessibility complement to the movie audio-gate. Documented in syncdawn.example.ini; engine seam is PROVENANCE #39.

    TD only for now; RA's VoxType list is larger/different and gets its own table as a follow-up.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sunday, July 26, 2026 15:23:00
    https://gitlab.synchro.net/main/sbbs/-/commit/6ed5e2bf11ba41cf0fb56c35
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/tiberiandawn/intro.cpp
    Log Message:
    syncdawn: the side-pick briefings were the FMV the audio gate missed

    On a terminal that cannot play audio the door skips cutscenes: a silent
    FMV is a poor experience and a lot of bandwidth. Every movie obeyed that
    except one -- starting a new game and picking a side played a full-motion briefing anyway.

    The gate lives in Play_Movie(), which is the chokepoint for cutscenes but
    not for these two: Choose_Side() plays the scenario-1 briefings with
    VQA_Play() directly, so the gate never saw them. Reported against GDI,
    but both sides go through the same code and behave the same way; GDI is
    just the side usually picked.

    Suppression now happens ahead of the GOOD/BAD split, closing and freeing
    both handles without playing either. The movies are still opened above
    it deliberately -- the side-pick screen's interpolated palettes are read
    out of those same files, so skipping the open would break the screen that
    leads into the briefing.

    Red Alert has no equivalent: it plays every cutscene through Play_Movie()
    and was already covered.

    Verified both directions on a no-audio client: with the default gate the
    pick goes straight to the mission, and with [game] movies = true the
    briefing still plays.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Monday, July 27, 2026 20:26:00
    https://gitlab.synchro.net/main/sbbs/-/commit/35881f16ef0daf4dce26b628
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/redalert/audio.cpp xtrn/syncalert/README.md syncalert.example.ini
    Log Message:
    syncconquer: caption the EVA announcements in Red Alert too

    Tiberian Dawn has captioned its spoken EVA callouts since the vendored
    patch that added them; Red Alert did not, so the same door shipped an accessibility feature to one title and not the other -- a deaf player, or
    one on a terminal with no audio, simply lost "construction complete",
    "unit ready" and "our base is under attack" in SyncAlert.

    Same shape as the Tiberian Dawn patch: a caption table aligned to the
    engine's own Speech[] table, emitted from Speak() when the door's
    [game] captions gate is true. Red Alert's engine differs enough that
    the code could not be copied across. Its VoxType carries 116 entries
    rather than 40, including unused slots (captioned NULL, which the caption helper skips) and one that exists only in an English build, so the table reproduces that conditional to stay aligned elsewhere. Its message list
    hangs off Session and takes a different argument list, so captions follow
    Red Alert's own system-message call sites in house.cpp and taction.cpp
    for color and timeout rather than Tiberian Dawn's. And its Add_Message()
    does not dedup by id, so the "don't stack a repeat while it is still on
    screen" behavior the other title gets for free is done explicitly with a
    lookup first; caption ids are offset out of the TutorialText id space
    those same call sites use, so an unrelated message can never be mistaken
    for a caption already showing.

    The caption is emitted ahead of, and independently of, Red Alert's audio
    gate -- that gate gives up on volume, sample type or quiet mode, which is exactly the case captions exist to cover.

    syncalert.example.ini gains the [game] captions key so the setting is discoverable rather than merely implemented, documented as the same
    tri-state the sibling title uses: on, off, or auto (show them only when
    the client has no usable audio).

    Live-verified against a real session: the caption appears, and matches
    the line being spoken.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net