Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions architecture/sound.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ Model animation sounds are client presentation, not server simulation state. The

WC3 evaluates these event tracks before frustum culling so a client-visible model may still be audible while off-screen. The renderer owns event-key de-duplication per entity/model and resolves the animated event node to world space through the existing MDX node matrices. This local path currently passes authored volume; pitch variance and authored panner distances require a future generic mixer/API extension rather than WC3-specific client branches.

UI sprites (`DrawSprite`: glue panels, button particles) render with `RDF_UI_SPRITE`. They all carry entity number 0, so their event state is keyed by `renderEntity_t.instance_id` (the sprite's owner id), exactly like entity-camera views; only `SND` events are dispatched, and they are passed to `PlaySoundAt` with a NULL origin, which the client plays non-positionally. A sprite's first sighting counts as entering its sequence, so keys on the first frame of a glue `Birth`/`Morph` (e.g. `SNDXALPD`, `LeftGlueScreenPopDown`) still play.

## Named interface sounds

Menus play named kits through the imported `PlaySoundByName`. World of Warcraft fills the kit table from `DBFilesClient\SoundEntries.dbc`; when that DBC is absent (Warcraft III), `S_LoadSoundEntries` registers `UI\SoundInfo\UISounds.slk` rows as kits through a `slkField_t` schema (label, `FileNames` variants, `DirectoryBase`, `Volume` on the SLK's 0..127 scale). The WC3 glue menu plays `GlueScreenClick` on every completed button/checkbox click. Kit lookup is case-insensitive, and an unknown name is reported once on stderr. Each play picks one file variant (DBC `freq` weights when authored, otherwise uniform over the SLK's comma-separated `FileNames`) and loads it through the path-keyed `sfx_t` table, so kit variants share the normal registration/eviction lifetime.


## Assets

Expand Down
4 changes: 2 additions & 2 deletions client/cl_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -911,9 +911,9 @@ TEST(client_session, menu_rebuild_clears_world_scope_before_returning_to_menu) {
#endif


/* A NULL origin is a screen-space (UI) presentation sound and plays without distance attenuation. */
static void CL_RendererPlaySoundAt(cstring_t path, vec3_t const *origin, float volume) {
if (!origin) return;
S_PlaySoundPacket(path, origin, true, CHAN_AUTO, volume, 1.0f, 0.0f);
S_PlaySoundPacket(path, origin, origin != NULL, CHAN_AUTO, volume, 1.0f, 0.0f);
}

void CL_Init(void) {
Expand Down
3 changes: 1 addition & 2 deletions client/cl_movie.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,7 @@ void CL_MovieShutdown(void) {

bool CL_PlayMovie(cstring_t path) {
#ifndef BZ_FFMPEG
(void)path;
CON_printf("Movie playback is disabled in this build (rebuild with FFMPEG=1).");
CON_printf("Skipping movie %s: playback is disabled in this build (rebuild with FFMPEG=1).", path ? path : "");
return false;
#else
AVCodec const *video_decoder;
Expand Down
2 changes: 1 addition & 1 deletion client/tr_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ typedef struct {
void (*MemFree)(handle_t);
uint32_t (*LoadSlk)(cstring_t filename, slkField_t const *schema, void **dest, uint32_t row_stride);
cstring_t (*CvarString)(cstring_t name, cstring_t fallback);
void (*PlaySoundAt)(cstring_t path, vec3_t const *origin, float volume);
void (*PlaySoundAt)(cstring_t path, vec3_t const *origin, float volume); /* NULL origin: non-positional UI sound */
void (*error)(cstring_t fmt, ...);
} refImport_t;

Expand Down
1 change: 1 addition & 0 deletions common/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ enum {
FLAG(RDF_NOPARTICLES, 4),
FLAG(RDF_USE_ENTITY_CAMERA, 5),
FLAG(RDF_ISOLATED_PARTICLES, 6),
FLAG(RDF_UI_SPRITE, 7), /* screen-space UI model: events keyed by instance_id, sounds non-positional */
};

#define MAX_COMMANDS 12
Expand Down
10 changes: 10 additions & 0 deletions docs/games/warcraft-3/campaign-progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ hard-coded by race. Campaign and mission rows are rebuilt/reloaded when entering
the campaign selector, so progress written by the preceding map is visible after
the session returns to the frontend.

### Selector rows

Patched clients build both selector columns at runtime from `CampaignStrings` (Warsmash's `CampaignMenuUI` does the same); the static `TutorialFrame` … `NightElfFrame` and `Mission0Frame` … `Mission13Frame` rows authored by the 1.00 `CampaignMenu.fdf` are always hidden. Each row clones `CampaignArrowButtonTemplate` (maps, campaigns) or `CampaignCameraButtonTemplate` (movies), `StandardSmallTextTemplate` for the gold header and `StandardTitleTextTemplate` (grey 0.764) for the name, laid out like the 1.00 rows.

Rows occupy fixed 0.0315625 slots; ten slots (the height of TFT `CampaignListBox.fdf`'s scroll bar, 0.315625) make the box, whose top-left sits at `(-0.287, -0.1274)` from `CampaignMenu`'s top-right. Every campaign is followed by a blank slot, and an Introduction cinematic by one blank slot. A column that fits is centred in the box; a longer one starts at the top and scrolls with the mouse wheel (the screen's `scroll` hook). These constants were fitted to retail 1.2x captures: two campaigns centre at 0.236/0.301 below the top edge, and the Prologue mission column (Intro, blank, Opening, two chapters) centres at 0.285 with 0.032 spacing, both within about a pixel. The earlier static-row layout sat ~0.03 higher because the 1.00 FDF rows are not what patched clients draw. The `WarCraftIIILogo` sprite's FDF offset leaves it off-screen, so the menu loads `MainMenuLogo` and anchors it at `TOPRIGHT (-0.13, -0.08)`, mirroring the main menu (and Warsmash).

Choosing a campaign swaps the backdrop to that campaign's scene and plays its `Birth` camera move; Back from the mission column restores the selector's default-campaign scene with the same move.

The 1.00 `War3.mpq` `CampaignStrings.txt` also predates the later schema: it has no `[Index]`, `Background`, `Cursor`, or `DefaultOpen`, splits missions into `TitleN`/`MissionN`/`FileN`, and names cinematics only by title (`InCinematic`/`OpCinematic`/`EdCinematic`, headers in `[Label]`). The parser maps those to `<Key>In/Op/Ed` movies and the `<Key>Backdrop` skin, printing a console warning for each campaign that falls back. Without `DefaultOpen`, `unlocked` visibility shows nothing until progress exists; the default `all` shows every campaign.

The older `wc3_campaign_mission_visibility=played` and
`wc3_campaign_played_<campaign>_<mission>` frontend bridge is removed. Merely
launching a map is not campaign progression; authored JASS availability natives
Expand Down
1 change: 1 addition & 0 deletions games/warcraft-3/menu/menu_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ void UI_DrawFrame(frameDef_t const *frame);
void UI_DrawFrames(frameDef_t const *const *roots, uint32_t num_roots);
bool M_EditKey(int key);
bool M_MouseEvent(menuMouseEvent_t event, int x, int y, int32_t param);
bool UI_FrameContainsPoint(frameDef_t const *frame, float fdf_x, float fdf_y);
void M_TextInput(cstring_t text);
void UI_EditTextInput(cstring_t text);
bool UI_EditHasFocus(frameDef_t const *frame);
Expand Down
5 changes: 5 additions & 0 deletions games/warcraft-3/menu/menu_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,11 @@ bool M_MouseEvent(menuMouseEvent_t event, int x, int y, int32_t param) {
frameDef_t const *hit = UI_HitTest(fdf.x, fdf.y);
UI_UpdateMouseFrameFlags(hit, up && left);

if (wheel_y && UI_GetCurrentScreen() && UI_GetCurrentScreen()->scroll &&
UI_GetCurrentScreen()->scroll(fdf.x, fdf.y, wheel_y)) {
return true;
}

/* Dispatch to per-type event handler */
if (hit && hit->event_handler) {
hit->event_handler((frameDef_t *)hit, event, fdf.x, fdf.y, param);
Expand Down
14 changes: 14 additions & 0 deletions games/warcraft-3/menu/menu_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ static bool UI_PointInRect(float x, float y, rect_t const *rect) {
y >= rect->y && y < rect->y + rect->h;
}

/* Uses the rectangle from the last laid-out frame, the same space UI_HitTest reads. */
bool UI_FrameContainsPoint(frameDef_t const *frame, float fdf_x, float fdf_y) {
uint32_t index;
return frame && !frame->hidden && UI_FrameIndex(frame, &index) && runtimes[index].calculated &&
UI_PointInRect(fdf_x, fdf_y, &runtimes[index].rect);
}

static bool UI_FrameIsInteractive(frameDef_t const *frame) {
if (!frame || frame->hidden || frame->disabled || (frame->ui_flags & UIFLAG_PASSTHROUGH)) {
return false;
Expand Down Expand Up @@ -476,6 +483,11 @@ static void UI_DrawHighlightFrame(frameDef_t const *frame, rect_t const *rect);
* PER-TYPE EVENT HANDLERS — called from UI_MouseEventLocal
* ======================================================================== */

/* Glue controls share UISounds.slk's GlueScreenClick on a completed click, as retail/Warsmash do. */
static void UI_PlayGlueClick(void) {
if (mi.PlaySoundByName) mi.PlaySoundByName("GlueScreenClick");
}

static void UI_ButtonEventHandler(frameDef_t *frame, menuMouseEvent_t event, float fdf_x, float fdf_y, int32_t param) {
(void)fdf_x; (void)fdf_y;
if (param != 1) {
Expand All @@ -486,6 +498,7 @@ static void UI_ButtonEventHandler(frameDef_t *frame, menuMouseEvent_t event, flo
} else if (event == MENU_MOUSE_UP) {
frame->ui_flags &= ~UIFLAG_PRESSED;
if (frame->OnClick[0]) {
UI_PlayGlueClick();
UI_QueueCommand(frame->OnClick);
}
}
Expand All @@ -502,6 +515,7 @@ static void UI_CheckBoxEventHandler(frameDef_t *frame, menuMouseEvent_t event, f
frame->ui_flags &= ~UIFLAG_PRESSED;
frame->ui_flags ^= UIFLAG_CHECKED;
((frameDef_t *)frame)->CheckBox.Checked = (frame->ui_flags & UIFLAG_CHECKED) != 0;
UI_PlayGlueClick();
if (frame->OnClick[0]) {
UI_QueueCommand(frame->OnClick);
}
Expand Down
2 changes: 2 additions & 0 deletions games/warcraft-3/menu/menu_screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ typedef struct uiScreen_s {
void (*refresh)(int msec);
void (*draw)(void);
void (*key_event)(int key, bool down);
/* Mouse wheel over screen-owned content; return true when consumed so it does not reach the hit frame. */
bool (*scroll)(float fdf_x, float fdf_y, int delta);
} uiScreen_t;

/* Screen implementations */
Expand Down
Loading
Loading