diff --git a/config/parked.json b/config/parked.json index 7f2b5137..1cf66e19 100644 --- a/config/parked.json +++ b/config/parked.json @@ -3109,9 +3109,9 @@ "base": { "size": 672, "relocation_count": 43, - "normalized_sha256": "885fdb81966284acf5ba7138a4bbbd2221e7c61c89114fdd6f53b96ba89b81a0" + "normalized_sha256": "7db4861a5adf519e0457a0ffe785ff4d47e878fda47cc0a06206a529776ac91c" }, - "objdiff_percent": 97.67839 + "objdiff_percent": 97.76382 } }, { @@ -6215,6 +6215,94 @@ "base": { "size": 608, "relocation_count": 26, "normalized_sha256": "bcf4cefdb82e1d1eb34388f794ca0cdcc98c1d01b635c44e8796e744e9e3e920" }, "objdiff_percent": 95.505615 } + }, + { + "unit": "source/interface/hud_messaging", + "function": "_hud_messaging_update", + "class": "unclassified", + "evidence": "Complete typed HUD renderer reconstructed from January control flow, strings, data and relocations, with the independent HCEA implementation as a semantic map. It covers objective/help/custom state messages, text and bitmap elements, button remapping, split-screen placement, timed item messages, fading and expiration. January is 2,928 padded bytes with 137 relocations; canonical is 2,880 with 136. The remaining delta is a VC7 load/register schedule around split-screen line placement, not known missing behavior. No compiler steering, raw offsets, fake dependency, undefined behavior, representation pun, forced inline or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/hud_messaging_obj_renderer_caller_closure_20260908.md.", + "measurements": { + "target": { "size": 2928, "relocation_count": 137, "normalized_sha256": "26a1376422e3742f9d65a8d83241919e1e370769a936ade9c9b859829d7d1b7b" }, + "base": { "size": 2880, "relocation_count": 136, "normalized_sha256": "d5d32a3ce4d44e6e87ed3daa1d1ce28cf1a746bd53ae0b678bf7ec99a955d815" }, + "objdiff_percent": 66.71034 + } + }, + { + "unit": "source/math/geometry", + "function": "_convex_hull3d_begin", + "class": "unclassified", + "evidence": "Complete typed tetrahedron seed reconstructed from January assertions, layout and disassembly with the independent HCEA convex-hull source as a semantic map. January is 1,488 padded bytes and canonical is 1,472, both with 28 relocations and the same ordered destinations. The remaining 16-byte aggregate-home and topology schedule difference has no proved source-level mechanism. No compiler steering, raw layout, fake dependency, extra helper owner, forced inline or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md.", + "measurements": { + "target": { "size": 1488, "relocation_count": 28, "normalized_sha256": "d3e0ba2e2077ce1e167e22398d1ce4b18329c8a3301ef606b46f56456ee282bc" }, + "base": { "size": 1472, "relocation_count": 28, "normalized_sha256": "1b5824819ddffa0f161d260eec28283d4b1e3fc5a7422dee6ec8e5f5653f8804" }, + "objdiff_percent": 93.49269 + } + }, + { + "unit": "source/math/geometry", + "function": "_convex_hull3d_expand", + "class": "unclassified", + "evidence": "Complete typed convex-hull expansion reconstructed from January assertions, layout and disassembly with independent HCEA source as a semantic map. Target and canonical are both 1,808 padded bytes with 67 relocations and the same ordered destinations. Final audit moved indexed surface and edge pointer formation behind the corresponding bounds validation, removing abstract-C undefined behavior from the earlier, superficially closer candidate. That valid source perturbs VC7 allocation and relocation addresses across the horizon walk, reducing objdiff to 84.64924 percent; it is retained because semantic validity outranks similarity. No compiler steering, raw layout, fake dependency, extra helper owner, forced inline, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md.", + "measurements": { + "target": { "size": 1808, "relocation_count": 67, "normalized_sha256": "bad17e2158737f40d52656451e9f2ce826a8b27d395f84032536dceab223fbb0" }, + "base": { "size": 1808, "relocation_count": 67, "normalized_sha256": "3b42a1bae5ecb4b878280d95cedf580f83abd8c332c193aae825d59a7ae3c5f1" }, + "objdiff_percent": 84.64924 + } + }, + { + "unit": "source/sound/sound_manager", + "function": "_update_channel_for_looping_sound", + "class": "unclassified", + "evidence": "Complete typed looping-channel update reconstructed from January code/relocations and HCEA semantics. It covers properties, cache transitions, linked permutations, definition changes and crossfades. January is 1,360 padded bytes with 61 relocations; canonical is 1,376 with 61. No unique source-level mechanism explains the residual, so it receives zero exact credit. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.", + "measurements": { + "target": { "size": 1360, "relocation_count": 61, "normalized_sha256": "457d122269e7e06c8b4a557f80eef434e3eb925aa8aaaa73d9e69bce635a6d78" }, + "base": { "size": 1376, "relocation_count": 61, "normalized_sha256": "5fa29769faf68bcaa178d1a65f979d8641b10b3727a404d00849b345f42a0ff7" }, + "objdiff_percent": 96.782814 + } + }, + { + "unit": "source/sound/sound_manager", + "function": "_update_channel_for_impulse_sound", + "class": "unclassified", + "evidence": "Complete typed impulse-channel property/queue/update body retained under its corrected semantic target name. January and canonical are both 528 padded bytes with all 29 relocations; normalized code still differs through ordinary scheduling. January bodies, callers and HCEA names independently prove the target symbol rotation. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.", + "measurements": { + "target": { "size": 528, "relocation_count": 29, "normalized_sha256": "b700f26596f466e5771d4f875b0d3a9354600ee13b4dd1553cde0c48d45d8202" }, + "base": { "size": 528, "relocation_count": 29, "normalized_sha256": "2d81b0f86a90351a964320970505552d0b323a95a871e75b9d8a4e50cf29eeb4" }, + "objdiff_percent": 94.03593 + } + }, + { + "unit": "source/sound/sound_manager", + "function": "_limit_pitch", + "class": "unclassified", + "evidence": "Complete ordinary-C pitch-bend limiter reconstructed from January and HCEA behavior. January is 96 padded bytes and canonical is 112, both with one relocation. The residual is not assigned a speculative compiler mechanism and receives zero exact credit. No compiler steering, fake dependency, undefined behavior or nonsensical branch is retained. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.", + "measurements": { + "target": { "size": 96, "relocation_count": 1, "normalized_sha256": "fa465e8daf708a83bb16686d2c254a68937b1ad2c600be40817358dbdd31c6f3" }, + "base": { "size": 112, "relocation_count": 1, "normalized_sha256": "c296946da525c8cb2c0e7c9ddced82e27a98fbfb51f9eb7e032bcecd8c880375" }, + "objdiff_percent": 91.57895 + } + }, + { + "unit": "source/sound/sound_manager", + "function": "_sound_set_definition_end", + "class": "unclassified", + "evidence": "Complete queued-end-definition transition retained under its corrected semantic target name. January and canonical are both 208 padded bytes with all nine relocations; the code hash remains different. It uses the now-exact bounded channel-summary builder and the typed instance-limit flow. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.", + "measurements": { + "target": { "size": 208, "relocation_count": 9, "normalized_sha256": "06f507e27039b2a39192271221e6c7b703aee4d5ae45becdac84f3543095ec13" }, + "base": { "size": 208, "relocation_count": 9, "normalized_sha256": "4f5a03ed0532981c792521f1ce7cdcc47eef5a3ee2d038caf65f57b1987675d9" }, + "objdiff_percent": 91.884056 + } + }, + { + "unit": "source/sound/sound_manager", + "function": "_sound_find_like_channel", + "class": "unclassified", + "evidence": "Complete typed best-channel selector for a supplied channel list. Target and canonical are both 272 padded bytes with all 16 relocations and 97.15909 percent similarity; the remaining normalized difference is not assigned a speculative mechanism. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.", + "measurements": { + "target": { "size": 272, "relocation_count": 16, "normalized_sha256": "240a72742e0bdcb19f5bbf684bcffaefcf37a483a9fc59393cf4a6d0c36984b3" }, + "base": { "size": 272, "relocation_count": 16, "normalized_sha256": "a695dfa283a53aeabb3923c2186b6690ff2b319205588ba7cebefce9b7c91c56" }, + "objdiff_percent": 97.15909 + } } ] } diff --git a/config/symbols.json b/config/symbols.json index 28dc187c..9a5db995 100644 --- a/config/symbols.json +++ b/config/symbols.json @@ -7546,22 +7546,22 @@ { "file_offset": 1820560, "flags": 32, "name": "_source_distance" }, { "file_offset": 1820768, "flags": 32, "name": "_sound_find_like_channel" }, { "file_offset": 1821040, "flags": 32, "name": "_sound_preempts_sound" }, -{ "file_offset": 1821296, "flags": 32, "name": "_sound_find_best_channel" }, +{ "file_offset": 1821296, "flags": 32, "name": "_update_channel_for_impulse_sound" }, { "file_offset": 1821824, "flags": 32, "name": "_looping_sound_new" }, -{ "file_offset": 1822128, "flags": 32, "name": "_looping_sound_new_sound" }, +{ "file_offset": 1822128, "flags": 32, "name": "_sound_set_definition_end" }, { "file_offset": 1822336, "flags": 32, "name": "_detail_sound_random_offset" }, { "file_offset": 1822528, "flags": 32, "name": "_sound_stop_impulse" }, { "file_offset": 1822656, "flags": 32, "name": "_sound_stop_impulse_by_source_and_definition" }, { "file_offset": 1822768, "flags": 32, "name": "_sound_stop_all" }, { "file_offset": 1822864, "flags": 32, "name": "_source_audible" }, { "file_offset": 1823104, "flags": 32, "name": "_refresh_sounds" }, -{ "file_offset": 1823648, "flags": 32, "name": "_sound_find_channel" }, +{ "file_offset": 1823648, "flags": 32, "name": "_sound_find_best_channel" }, { "file_offset": 1824064, "flags": 32, "name": "_update_potentially_audible_looping_sound" }, { "file_offset": 1824544, "flags": 32, "name": "_update_channel_for_looping_sound" }, { "file_offset": 1825904, "flags": 32, "name": "_sound_new_impulse" }, { "file_offset": 1826880, "flags": 32, "name": "_sound_refresh_looping" }, { "file_offset": 1828016, "flags": 32, "name": "_refresh_listener" }, -{ "file_offset": 1828512, "flags": 32, "name": "_update_channel_for_impulse_sound" }, +{ "file_offset": 1828512, "flags": 32, "name": "_sound_find_channel" }, { "file_offset": 1829008, "flags": 32, "name": "_update_channels" }, { "file_offset": 1829872, "flags": 32, "name": "_process_looping_sounds" }, { "file_offset": 1830368, "flags": 32, "name": "_sound_idle" }, @@ -22064,6 +22064,7 @@ { "file_offset": 3034188, "flags": 0, "name": "_button_mappings", "static": true }, { "file_offset": 3034204, "flags": 0, "name": "_time_code_time" }, { "file_offset": 3034208, "flags": 0, "name": "_time_code_stop_time" }, +{ "file_offset": 3034212, "flags": 0, "name": "_split_screen_hud_message_offset", "static": true }, { "file_offset": 3034216, "flags": 0, "name": "?overcharge_count@?1??hud_render_unit_interface@@9@9", "static": true }, { "file_offset": 3034220, "flags": 0, "name": "_scale" }, { "file_offset": 3034244, "flags": 0, "name": "_data_002e4c84" }, diff --git a/docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md b/docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md new file mode 100644 index 00000000..fb3a09c2 --- /dev/null +++ b/docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md @@ -0,0 +1,110 @@ +# Geometry: convex-hull seed and expansion reconciliation (2026-09-08) + +## Result and scope + +This packet restores the two public implementations required by the already +exact `convex_hull3d` caller: + +- `convex_hull3d_begin` selects four affinely independent points, initializes + the tetrahedron topology, constructs its four outward-facing planes, and + clears the unused vertex, edge, and surface tails. +- `convex_hull3d_expand` classifies a new point, removes visible faces, finds + and walks the horizon, splices surviving vertex fans, and constructs the new + face fan with bounded caller-provided storage. + +Both functions are complete, typed, public C implementations. They are honest +fuzzy residuals and receive zero exact credit. Geometry moves from 18 exact / 6 +residual / 6 unwritten to **18 exact / 8 residual / 4 unwritten**. The packet +converts **3,296 target padded bytes** from unwritten to reviewed residual code +without changing any inherited exact function. + +`source/math/geometry.h` already contained the release-PDB-authenticated +`vertex3d`, `edge3d`, and `surface3d` layouts and the correct public prototypes, +so it was not edited. The two optional polygon clipping functions remain +outside this packet. No configuration file was edited in this lane. + +## Provenance and reconstruction + +The January split object is authoritative for code, assertion lines, public +linkage, padded extents, and relocation ownership. Semantic source evidence is +the read-only HCEA material at: + +- `scratch/_halocea-reference-20260907/src/blam/structures/convex_hull3d_begin.c` +- `scratch/_halocea-reference-20260907/src/blam/math/convex_hull3d_expand.c` + +Fable commit `f9dd42218b3325e1b3a63d7860e5309f69fd3d8e` +authenticates the surrounding public caller, point test, owner types, and +prototypes, but did not contain bodies for this pair. The implementation keeps +project `real` and `boolean` types, one parameter per line, C89 block-scoped +declarations, explicit returns, and the existing public owner names. + +The January assertion evidence is retained verbatim: the four pointer checks +at source lines 1710-1713 and 1973-1976, the boundary assertion at 2076, the +vertex-fan assertions at 2098-2099, and the horizon/vertex assertions at +2167-2171 and 2206-2210. The point, vertex, edge, surface, and plane accesses +agree with the owner layouts and the January offsets. + +No helper was exported or force-retained. In particular, Geometry emits no +`_point_from_line3d` symbol. January has no Geometry definition or reference +for that helper; its 48-byte `IMAGE_COMDAT_SELECT_NODUPLICATES` owner belongs +elsewhere. The seed's line-distance math therefore remains naturally inlined +with typed vectors instead of creating a conflicting helper owner. + +## Measured object evidence + +The final isolated candidate is `scratch/geometry-safe-20260908.obj`. + +| Function | Target size / relocs | Candidate size / relocs | Target normalized SHA-256 | Candidate normalized SHA-256 | +| --- | ---: | ---: | --- | --- | +| `_convex_hull3d_begin` | 1488 / 28 | 1472 / 28 | `d3e0ba2e2077ce1e167e22398d1ce4b18329c8a3301ef606b46f56456ee282bc` | `1b5824819ddffa0f161d260eec28283d4b1e3fc5a7422dee6ec8e5f5653f8804` | +| `_convex_hull3d_expand` | 1808 / 67 | 1808 / 67 | `bad17e2158737f40d52656451e9f2ce826a8b27d395f84032536dceab223fbb0` | `3b42a1bae5ecb4b878280d95cedf580f83abd8c332c193aae825d59a7ae3c5f1` | + +For begin, the ordered relocation-destination sequence is equal, although +later relocation addresses move with the 16-byte extent difference. Target and +candidate contain 482 and 484 decoded instructions. The prefix and the entire +line-search body are instruction-aligned; the dominant unresolved mechanism is +a 12-byte aggregate-home difference (`0x4c` versus `0x40` stack frames), plus +the resulting plane-expression and topology scheduling. A projected-point +aggregate experiment was rejected because VC7 scalarized it more aggressively, +regressing the candidate to 1456 padded bytes and a `0x34` frame. + +For expand, padded size, relocation count, and the ordered relocation- +destination sequence agree. Final root audit found that the earlier +98.264755-percent candidate formed indexed pointers before checking their +indices. That abstract-C undefined behavior was rejected. Canonical now +validates each surface or edge index before forming its pointer; the resulting +valid source is **84.64924 percent** similar because VC7 changes allocation and +relocation addresses across the horizon walk. Semantic validity takes priority +over the older superficial similarity, and no allocator-only source steering +was attempted. + +## Gates and integration metadata + +The required isolated gate passes compilation and the emitted-symbol guard: + +```text +python -B tools/campaign/gate.py source/math/geometry --all \ + --out scratch/geometry-safe-20260908.obj \ + --forbid-emitted-symbol _point_from_line3d + +exact 18, residual 8, unwritten 4 (30 listed) +``` + +The source-policy scan reports zero review leads: + +```text +python -B tools/fake_match_scan.py --fail-on-findings source/math/geometry.c +Scanned 1 file; 0 review leads +``` + +The read-only parked-function audit reports 351 active, zero stale, and zero +invalid entries. No configure or Ninja command was run, as required by the lane +scope. + +Root integration should retain both functions as zero-credit fuzzy parks with +class `unclassified`, using the target/candidate measurements above and the +ordinary post-integration objdiff percentages. Do not add or change symbol +ownership: both public target symbols already exist in `config/symbols.json`. +The park evidence should point to this ledger and state that begin is a +16-byte/aggregate-home scheduling residual, while expand has exact size and +relocation layout with only the measured instruction/register schedule left. diff --git a/docs/object_matching_logs/hud_geometry_sound_caller_packet_reconciliation_20260908.md b/docs/object_matching_logs/hud_geometry_sound_caller_packet_reconciliation_20260908.md new file mode 100644 index 00000000..eabef058 --- /dev/null +++ b/docs/object_matching_logs/hud_geometry_sound_caller_packet_reconciliation_20260908.md @@ -0,0 +1,69 @@ +# HUD, Geometry, and Sound caller-packet reconciliation (2026-09-08) + +## Canonical result + +This packet advances the semantic exact ledger from **1,035,078 meaningful +code bytes / 6,600 accepted exact functions** to **1,037,674 meaningful code +bytes / 6,611 accepted exact functions**. The net admitted result is **+2,596 +meaningful / +2,688 padded code bytes across 11 functions**, with zero losses +in the 8,245-function rename-stable census. + +| Family | Exact functions | Meaningful bytes | Padded bytes | +| --- | ---: | ---: | ---: | +| HUD messaging | 3 | 562 | 576 | +| Sound manager | 8 | 2,034 | 2,112 | +| Geometry | 0 | 0 | 0 | +| **Total** | **11** | **2,596** | **2,688** | + +The exact functions are `_compare_messages`, `_render_state_text`, +`_render_state_bitmap`, `_sound_idle`, `_sound_update_time`, +`_update_channels`, `_channel_set_properties_hardware`, +`_channel_queue_sound`, `_channel_get_state`, +`_sound_manager_master_gain`, and `_sound_channel_summary_build`. + +## Honest residual accounting + +The packet also replaces unwritten stubs with **8,688 padded bytes** of +complete, typed, reviewed source. These functions remain fuzzy and receive +**zero exact credit**: + +| Family | Complete fuzzy target scope | +| --- | ---: | +| HUD messaging renderer | 2,928 padded bytes | +| Geometry convex-hull seed and expansion | 3,296 padded bytes | +| Sound channel update/select graph | 2,464 padded bytes | +| **Total** | **8,688 padded bytes** | + +Together, the exact and fuzzy work reconstructs 11,376 padded target bytes. +The individual ledgers retain target/candidate hashes, relocation counts, +provenance, residual classifications, and reopen conditions. No fuzzy body is +counted in the canonical exact-byte total. + +The final review keeps valid source ahead of superficial similarity. Bounds +are validated before Geometry forms indexed pointers; HUD's icon helper is +const-correct; and Sound definition flags, instance-limit constants, and +cross-TU prototypes live with their genuine owners. The four corrected +Sound-manager private names are supported by January caller relocations and +the release-PDB/HCEA semantic map. Renaming them earns no byte credit. + +## Validation + +The canonical integration was configured and rebuilt as a whole. Focused +gates report HUD **33 exact / 3 residual / 0 unwritten**, Geometry **18 / 8 / +4**, and Sound **40 / 13 / 12**. All three pass the forbidden +`_point_from_line3d` emission guard. The stable before/after census reports +**11 gains / 2,688 padded bytes and zero regressions**. The source-policy scan +reports zero fake-match review leads in the changed C files, the object +admission audit adds no unsupported whole-object candidate, and the test suite +reports **1,147 passed, 2 skipped, and 26 subtests passed**. + +All retained residuals are registered in `config/parked.json`; the park audit +reports **359 active, zero stale, and zero invalid**. The packet does not cross +the **1,041,486 meaningful-byte** treemap cadence threshold, so the canonical +treemap is intentionally not refreshed by this commit. + +## Detailed ledgers + +- `hud_messaging_obj_renderer_caller_closure_20260908.md` +- `geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md` +- `sound_manager_obj_idle_channel_spine_20260908.md` diff --git a/docs/object_matching_logs/hud_messaging_obj_renderer_caller_closure_20260908.md b/docs/object_matching_logs/hud_messaging_obj_renderer_caller_closure_20260908.md new file mode 100644 index 00000000..818404dc --- /dev/null +++ b/docs/object_matching_logs/hud_messaging_obj_renderer_caller_closure_20260908.md @@ -0,0 +1,71 @@ +# `hud_messaging.obj` renderer caller closure (2026-09-08) + +## Result + +This packet reconstructs the complete `hud_messaging_update` graph rather than +tuning another isolated residual. The unit moves from 30 exact / 2 residual / +4 unwritten January owners to **33 exact / 3 residual / 0 unwritten** owners. +Three ordinary C helpers become strict exact: + +| function | meaningful bytes | padded bytes | +| --- | ---: | ---: | +| `_compare_messages` | 47 | 48 | +| `_render_state_text` | 128 | 128 | +| `_render_state_bitmap` | 387 | 400 | +| **total** | **562** | **576** | + +`_hud_messaging_update` is a complete, typed **fuzzy** reconstruction and +receives zero exact credit. January is 2,928 padded bytes with 137 +relocations and normalized SHA-256 +`26a1376422e3742f9d65a8d83241919e1e370769a936ade9c9b859829d7d1b7b`; +the retained candidate is 2,880 padded bytes with 136 relocations and +normalized SHA-256 +`d5d32a3ce4d44e6e87ed3daa1d1ce28cf1a746bd53ae0b678bf7ec99a955d815`. +The production objdiff percentage is recorded in `config/parked.json` after +the aggregate full build. + +## Evidence and reconstruction + +January target code, assertion strings, relocations, data placement and +function boundaries are authoritative. The independent HCEA reconstruction +at `scratch/_halocea-reference-20260907/src/blam/interface/` supplies the +semantic control-flow map. Every target dependency was checked against the +January relocation stream; later-build behavior was not assumed when January +disagreed. + +The retained renderer covers objective/help/custom state messages, text and +bitmap elements, input-button remapping, split-screen placement, timed item +messages, fading and expiration. It uses named HUD enums, `real` and the +existing typed tag macros. Exact-coordinate assertion wrappers preserve the +January file/line payloads. Target evidence requires two calls to +`global_scenario_get` on the scenario-name path and distinct raw-text and +item-text draw call sites; both are represented directly instead of being +compiler-steered away. + +The target data directly identifies `button_mappings`, `time_code_time`, and +`time_code_stop_time`. The adjacent four-byte value 17 is named +`split_screen_hud_message_offset`, consistent with both its two January uses +and the cross-build source. It is target-static in `config/symbols.json`. + +The remaining renderer delta is not missing high-level behavior. Size and +relocation comparison leaves a 48-byte / one-relocation difference dominated +by VC7's duplicate `hud_msg_def` load and stack/register schedule around the +split-screen line-top calculation. Further expression perturbation would be +low-leverage compiler steering, so the body is parked for future source/local +evidence. + +## House-rule and gate checks + +- No private `code + address` or global `bss + address` names were introduced. +- Typed tag access, named enum constants and project scalar/color/geometry + types are used throughout. +- `get_flash_color` is declared in its genuine `hud_draw.h` owner. +- No forced inline, volatile/register steering, raw offset, fake dependency, + undefined behavior, pointer representation pun, inline assembly or + nonsensical branch is retained. +- The isolated full-unit gate reports 33 exact / 3 residual / 0 unwritten. +- The `_point_from_line3d` emitted-symbol guard and changed-source fake-match + scan pass. + +Final production build, rename-stable sweep, park audit, semantic totals and +tool-suite results are recorded in the aggregate reconciliation checkpoint. diff --git a/docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md b/docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md new file mode 100644 index 00000000..d096a425 --- /dev/null +++ b/docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md @@ -0,0 +1,162 @@ +# `sound_manager.obj` idle/channel spine reconstruction (2026-09-08) + +## Result + +This packet advances `source/sound/sound_manager` from **32 strict-exact, +8 residual, and 25 unwritten** January function owners to **40 strict-exact, +13 residual, and 12 unwritten** owners. It adds eight genuine exact functions, +**2,034 meaningful / 2,112 padded exact code bytes**, and retains five newly emitted, complete +source reconstructions as measured fuzzy results with zero exact credit. No +inherited residual was tuned. + +The requested caller-connected spine covers 3,792 January padded bytes. With +its two direct private dependencies, this packet reconstructs 4,576 January +padded bytes in total. + +| semantic source function | January owner used for the gate | January padded bytes | result | +|---|---|---:|---| +| `sound_idle` | `_sound_idle` | 112 | **exact** | +| `sound_update_time` | `_sound_update_time` | 48 | **exact** | +| `update_channels` | `_update_channels` | 864 | **exact** | +| `update_channel_for_looping_sound` | `_update_channel_for_looping_sound` | 1,360 | fuzzy: candidate 1,376, same relocation count, SHA differs; **zero credit** | +| `update_channel_for_impulse_sound` | `_update_channel_for_impulse_sound` after reviewed rotation | 528 | fuzzy: same size and relocation count, SHA differs; **zero credit** | +| `channel_set_properties_hardware` | `_channel_set_properties_hardware` | 160 | **exact** | +| `channel_queue_sound` | `_channel_queue_sound` | 128 | **exact** | +| `channel_get_state` | `_channel_get_state` | 240 | **exact** | +| `limit_pitch` | `_limit_pitch` | 96 | fuzzy: candidate 112, SHA differs; **zero credit** | +| `sound_manager_master_gain` | `_sound_manager_master_gain` | 48 | **exact** | +| `sound_set_definition_end` | `_sound_set_definition_end` after reviewed rename | 208 | fuzzy: same size and relocation count, SHA differs; **zero credit** | +| `sound_channel_summary_build` | `_sound_channel_summary_build` | 512 | **exact** | +| `sound_find_like_channel` | `_sound_find_like_channel` | 272 | fuzzy: same size and relocation count, SHA differs; **zero credit** | + +The exact rows have the following January normalized hashes: + +| owner | padded bytes | relocations | normalized SHA-256 prefix | +|---|---:|---:|---| +| `_sound_idle` | 112 | 14 | `085dc0186fa101c9` | +| `_sound_update_time` | 48 | 5 | `33deb9bf16ce2e64` | +| `_update_channels` | 864 | 51 | `a51542452d0006ed` | +| `_channel_set_properties_hardware` | 160 | 12 | `e647272b731fcf12` | +| `_channel_queue_sound` | 128 | 9 | `fa9faabe6c779a25` | +| `_channel_get_state` | 240 | 15 | `e9fb1d4c81e46eae` | +| `_sound_manager_master_gain` | 48 | 2 | `6a2cc8c25eac16c9` | +| `_sound_channel_summary_build` | 512 | 32 | `f4ae27c691076057` | + +## Reconstruction evidence + +January target disassembly, relocation order, object layout, and assertion +strings are authoritative. The HCEA reconstruction beneath +`scratch/_halocea-reference-20260907/src/blam/{cache,sound,game}` supplies the +semantic control-flow and field-name map, while donor commit +`e4c48546384d1c11c7c1463db0c6676d283aff7e` supplies an independent repository +source checkpoint. Later-build behavior was discarded wherever the January +object disproved it. In particular, this packet uses the January 0x3C sound +platform vtable shape and the older two-argument channel queue / one-argument +channel update calls rather than the later HCEA interfaces. + +The exact `_sound_idle` body naturally inlines the exact +`sound_update_time` arithmetic. The source-level cast precedes the subtraction, +which produces January's `fild`/`fisub` sequence both in the standalone helper +and at the expected inline site. `_update_channels` closes without compiler +steering: the January expression order for the relative-position magnitude and +the nested mouth-aperture call reproduce all 864 bytes and all 51 relocation +sites. No `point_from_line3d` helper or COMDAT is emitted. + +The newly exact channel helpers use project datum/tag accessors and the +January platform interface. The channel state machine releases finished +cache permutations, promotes queued permutations, and advances the estimated +tick count. The master-gain helper preserves January's dialog-class exception +and global non-dialog multiplier. A root audit restored the four target-proven +capacity assertions missing from the initial donor lift and the two genuine +`sound_class_get` calls. That closes the complete 512-byte channel-summary +builder exactly; it is a semantic bounds repair, not allocator tuning. + +## Required symbol corrections + +The current symbol labels cannot be used as semantic source names. These are +target-symbol corrections for the owning symbol map, not aliases to publish in +C: + +1. January address `0x001BCA70`, previously `_sound_find_best_channel`, is + semantic `update_channel_for_impulse_sound`. Its 528-byte body constructs + platform properties, asserts the permutation is cached, queues it, records + the playing channel, and updates the hardware channel. The exact + `_update_channels` relocation at function offset `0x02E1` targets this owner. + An initial isolated diagnostic used an alias; production now carries the + corrected target label directly. +2. January address `0x001BD3A0`, previously `_sound_find_channel`, is semantic + `sound_find_best_channel`. Its 416-byte body scans compatible hardware + channels, immediately accepts a free one, and otherwise uses + `sound_preempts_sound` and distance to select the best victim. +3. January address `0x001BE6A0`, previously + `_update_channel_for_impulse_sound`, is semantic `sound_find_channel`. Its + 496-byte body performs speech/source reuse and instance-limit selection. Its + calls at offsets `0x0167` and `0x01D3` target the 416-byte owner currently + labeled `_sound_find_channel`, confirming the semantic best-channel helper + in item 2. It does not update impulse hardware properties. Rotate all + three labels atomically only after checking every call relocation; no credit + is claimed for the 416- or 496-byte owners in this packet. +4. January address `0x001BCDB0`, previously `_looping_sound_new_sound`, is + semantic `sound_set_definition_end`. It swaps to the queued end definition, + reselects pitch range/permutation, applies instance limits, and stops the + selected sound. `_update_channel_for_looping_sound` relocates to it at + function offset `0x03D7`. Production now carries the corrected target label + directly. This is distinct + from January's exact `_update_potentially_audible_looping_sound`, which is the + allocator called `looping_sound_new_sound` by the later HCEA reconstruction. + +These mappings are supported jointly by the HCEA release-PDB semantic names, +the January bodies, and the January caller relocations. The source retains the +semantic names and deliberately does not claim an exact or fuzzy result under +a knowingly wrong semantic label. + +## Cross-TU ownership + +The three temporary gate declarations were removed from `sound_manager.c`. +Their genuine owners and exact signatures are now published in +`sound_definitions.h` and `game_sound.h`: + +```c +/* sound_definitions.h; definition is sound_definitions.c:167 */ +real sound_permutation_get_real_mouth_aperture( + struct sound_permutation *permutation, + long tick_index); + +/* game_sound.h; definition is game_sound.c:911 */ +void game_sound_set_mouth_aperture( + long object_index, + real mouth_aperture); + +/* game_sound.h; declaration game_sound.c:224, definition game_sound.c:1009 */ +boolean track_object_impulse_sound( + long object_index, + void const *attachment_data, + struct sound_source *source); +``` + +January `game_sound.obj` defines `_track_object_impulse_sound` with +COFF storage class 2 (external), and January `sound_manager.obj` carries an +undefined class-2 external for it. The production definition is therefore +external and the old duplicate static declaration is gone. The shared +16-entry instance-limit constants likewise moved from `sound_classes.c` to +their genuine `sound_classes.h` owner; the full consumer rebuild verifies the +migration. + +## Validation + +The final isolated production command was: + +```text +python -B tools/campaign/gate.py source/sound/sound_manager --all --forbid-emitted-symbol _point_from_line3d --out %TEMP%\sound_packet_final.obj +``` + +It reported **40 exact / 13 residual / 12 unwritten of 65**, and the forbidden +emitted-symbol guard passed. `relocdiff` independently reports that exact +`_update_channels` has 864/864 bytes, 51/51 relocations, an equal normalized +hash, and zero differing relocation rows. The summary builder is also exact +at 512/512 bytes and 32/32 relocations. The source fake-match scan reports +zero review leads, and `git diff --check -- source/sound/sound_manager.c` +passes apart from Git's existing LF-to-CRLF advisory. + +The aggregate root checkpoint records the shared symbol map, park entries, +full build, rename-stable sweep, tests, commit and publication results. diff --git a/docs/reconciliation_queue_20260907.md b/docs/reconciliation_queue_20260907.md index 56eb4e6b..df556c0b 100644 --- a/docs/reconciliation_queue_20260907.md +++ b/docs/reconciliation_queue_20260907.md @@ -385,3 +385,19 @@ and [the Physics research ledger](object_matching_logs/physics_obj_update_new_re This gain does not yet cross the **1,041,486-byte** treemap cadence threshold, so the next verified packet should be accumulated before refreshing the map. + +## 2026-09-08 HUD, Geometry, and Sound caller packet + +The canonical semantic ledger now reports **1,037,674 meaningful exact code +bytes / 6,611 accepted exact functions**. HUD and Sound add **11 exact +functions / 2,596 meaningful / 2,688 padded bytes**, with zero losses in the +8,245-function stable census. Geometry adds no exact credit in this packet. + +An additional **8,688 padded bytes** of complete HUD, Geometry, and Sound +source are retained as explicitly documented zero-credit fuzzies. The batch +uses genuine declaration owners and semantic private names, passes the full +build, park, fake-match, admission, and test gates, and does not emit the +forbidden `_point_from_line3d` helper. See +[the aggregate ledger](object_matching_logs/hud_geometry_sound_caller_packet_reconciliation_20260908.md). + +The **1,041,486-byte** treemap refresh threshold has not yet been crossed. diff --git a/source/interface/hud_draw.h b/source/interface/hud_draw.h index c9d8437e..4e41fe44 100644 --- a/source/interface/hud_draw.h +++ b/source/interface/hud_draw.h @@ -57,6 +57,9 @@ void hud_calculate_point( point2d *result); long get_flash_duration( struct hud_color_definition const *hud_color); +pixel32 get_flash_color( + struct hud_color_definition const *hud_color, + long reference_value); void hud_draw_meter( short local_player_index, struct hud_absolute_placement_definition const *placement, diff --git a/source/interface/hud_messaging.c b/source/interface/hud_messaging.c index 312da3a2..e3c22c81 100644 --- a/source/interface/hud_messaging.c +++ b/source/interface/hud_messaging.c @@ -117,18 +117,30 @@ symbols in this file: #include "cseries/cseries.h" #include "cseries/errors.h" +#include "bitmaps/bitmap_group.h" +#include "bitmaps/bitmaps.h" +#include "cache/texture_cache.h" +#include "cutscene/cinematics.h" #include "game/game.h" #include "game/game_engine.h" #include "game/players.h" +#include "input/input_abstraction.h" #include "interface/hud_draw.h" +#include "interface/hud.h" #include "interface/hud_messaging.h" #include "interface/interface.h" +#include "interface/ui_widget.h" +#include "items/item_definitions.h" +#include "rasterizer/rasterizer.h" #include "render/render.h" #include "render/render_debug.h" #include "saved games/game_state.h" #include "scenario/scenario.h" #include "scenario/scenario_definitions.h" #include "tag_files/tag_groups.h" +#include "text/draw_string.h" +#include "text/font_group.h" +#include "text/text_group.h" #include "text/unicode.h" #include @@ -162,6 +174,78 @@ enum hud_number_show_flags NUMBER_OF_HUD_NUMBER_SHOW_FLAGS }; +enum hud_icon_flags +{ + _hud_icon_use_text_bit, + _hud_icon_use_color_bit, + _hud_icon_absolute_width_bit, + NUMBER_OF_HUD_ICON_FLAGS +}; + +enum hud_flash_flags +{ + _hud_flash_reverse_colors_bit, + NUMBER_OF_HUD_FLASH_FLAGS +}; + +enum text_justification +{ + _text_justification_left, + _text_justification_right, + _text_justification_center, + NUMBER_OF_TEXT_JUSTIFICATIONS +}; + +enum hud_icon_type +{ + _icon_a_button, + _icon_b_button, + _icon_x_button, + _icon_y_button, + _icon_black_button, + _icon_white_button, + _icon_left_trigger, + _icon_right_trigger, + _icon_dpad_up, + _icon_dpad_down, + _icon_dpad_left, + _icon_dpad_right, + _icon_start_button, + _icon_back_button, + _icon_left_thumb, + _icon_right_thumb, + _icon_left_stick, + _icon_right_stick, + _icon_action, + _icon_throw_grenade, + _icon_primary_trigger, + _icon_integrated_light, + _icon_jump, + _icon_use_equipment, + _icon_rotate_weapons, + _icon_rotate_grenades, + _icon_crouch, + _icon_zoom, + _icon_accept, + _icon_back, + _icon_move, + _icon_look, + _icon_custom_1, + _icon_custom_2, + _icon_custom_3, + _icon_custom_4, + _icon_custom_5, + _icon_custom_6, + _icon_custom_7, + _icon_custom_8, + NUMBER_OF_ICON_TYPES +}; + +enum +{ + NUMBER_OF_HUD_CUSTOM_ICONS = _icon_custom_8 - _icon_custom_1 + 1 +}; + /* ---------- macros */ #define HUD_MESSAGE_TEXT_DEFINITION_GET(index) \ @@ -206,7 +290,16 @@ struct hud_state_message_element byte data; }; -struct icon_hud_element_definition; +struct icon_hud_element_definition +{ + short sequence_index; + short width_offset; + point2d offset; + pixel32 color; + char frame_rate; + byte flags; + short text_index; +}; struct hud_state_message_text_info_definition { @@ -356,7 +449,8 @@ struct hud_timer_definition struct hud_messaging_parameters_definition { - byte reserved000[0x48]; + struct hud_absolute_placement_definition absolute_placement; + struct hud_placement_definition placement; struct tag_reference single_player_font; struct tag_reference multi_player_font; real up_time; @@ -451,6 +545,16 @@ static struct hud_message_definition *find_free_hud_message( struct hud_messaging_datum_definition *datum, long item_definition_index, char message_offset); +static void render_state_text( + rectangle2d *bounds, + rectangle2d *cursor_bounds, + wchar_t const *text, + boolean custom); +static void render_state_bitmap( + rectangle2d *bounds, + rectangle2d *cursor_bounds, + pixel32 color, + struct icon_hud_element_definition const *icon); /* ---------- globals */ @@ -458,11 +562,102 @@ static struct hud_messaging_globals_definition *hud_messaging_globals; extern struct hud_globals_definition *hud_globals; extern struct hud_messaging_parameters_definition *hud_msg_def; extern struct hud_scripted_globals_definition *hud_scripted_globals; -extern long time_code_time; -extern long time_code_stop_time; +static char button_mappings[_icon_custom_1 - _icon_action] = +{ + 2, + 6, + 7, + 5, + 0, + 4, + 3, + 1, + 11, + 10, + _icon_a_button, + _icon_b_button, + _icon_left_stick, + _icon_right_stick +}; +long time_code_time = NONE; +long time_code_stop_time = NONE; +static long split_screen_hud_message_offset = 17; /* ---------- public code */ +static void render_state_text( + rectangle2d *bounds, + rectangle2d *cursor_bounds, + wchar_t const *text, + boolean custom) +{ + rectangle2d text_bounds; + + draw_string_set_indents(cursor_bounds->x0 - bounds->x0, 0); + draw_unicode_string_compute_bounds(bounds, text, &text_bounds, cursor_bounds); + cursor_bounds->x0 -= 3; + text_bounds.x0 = bounds->x0; + if (custom && game_engine_running()) + draw_string_and_hack_in_icons(&text_bounds, NULL, NULL, 0, text, TRUE); + else + rasterizer_draw_unicode_string(&text_bounds, NULL, NULL, 0, text); + bounds->y0 = cursor_bounds->y0; + + return; +} + +static void render_state_bitmap( + rectangle2d *bounds, + rectangle2d *cursor_bounds, + pixel32 color, + struct icon_hud_element_definition const *icon) +{ + long bitmap_group_index = hud_globals->messaging.messaging_icons.index; + long frame_index = 0; + struct bitmap_data *bitmap = NULL; + real_rectangle2d const *clip = NULL; + real scale; + point2d point; + + if (icon->frame_rate) + frame_index = game_time_get() / icon->frame_rate; + hud_retrieve_bitmap_and_bounding_rect( + bitmap_group_index, + icon->sequence_index, + frame_index, + &bitmap, + &clip); + if (bitmap && _texture_cache_bitmap_get_hardware_format(bitmap, FALSE, TRUE)) + { + scale = local_player_count() > 1 ? 0.75f : 1.0f; + point.x = (short)(icon->offset.x * scale + cursor_bounds->x0); + point.y = (short)(cursor_bounds->y1 - icon->offset.y * scale); + hud_draw_bitmap_direct( + bitmap, + _hud_corner_bottom_left, + &point, + clip, + scale, + 0.0f, + TEST_FLAG(icon->flags, _hud_icon_use_color_bit) ? icon->color : color, + FALSE); + if (TEST_FLAG(icon->flags, _hud_icon_absolute_width_bit)) + cursor_bounds->x0 = (short)(icon->width_offset * scale + point.x); + else if (clip) + { + cursor_bounds->x0 = (short)( + ((clip->x1 - clip->x0) * bitmap->width + icon->width_offset) * scale + point.x); + } + else + { + cursor_bounds->x0 = (short)( + (bitmap->width + icon->width_offset) * scale + point.x); + } + } + + return; +} + void hud_messaging_initialize( void) { @@ -1205,3 +1400,528 @@ static long compare_messages( return difference; } + +void hud_messaging_update( + short local_player_index) +{ + if (!cinematic_in_progress() && + local_player_index != NONE && + game_engine_hud_draw_messages(local_player_get_player_index(local_player_index))) + { + long font_index = local_player_count() > 1 && + hud_msg_def->multi_player_font.index != NONE + ? hud_msg_def->multi_player_font.index + : hud_msg_def->single_player_font.index; + boolean split_screen = local_player_count() > 1; + point2d screen_point; + struct font_header *font; + short line_top; + short line_height; + short first_line_height; + struct hud_messaging_datum_definition *datum; + short maximum_message_count; + boolean objective_active; + boolean help_active; + boolean state_active; + rectangle2d line_bounds; + rectangle2d line_cursor; + real_argb_color state_color; + real_argb_color text_color; + pixel32 pixel_color = 0; + + hud_calculate_point( + local_player_index, + &hud_msg_def->absolute_placement, + &hud_msg_def->placement, + NULL, + local_player_count() > 1, + 0.0f, + &screen_point); + font = font_definition_get(font_index); + line_top = screen_point.y; + if (split_screen) + { + line_height = font->leading_height + font->ascending_height; + line_top -= (short)split_screen_hud_message_offset; + } + else + { + line_height = font->leading_height + + font->descending_height + + font->ascending_height; + } + first_line_height = line_height; + datum = &hud_messaging_globals->message_data[render.local_player_index]; + maximum_message_count = 4 - (local_player_count() > 1); + objective_active = hud_messaging_globals->objective.message && + hud_messaging_globals->objective.uptime; + help_active = hud_scripted_globals->show_hud_help_text && + hud_messaging_globals->help_message; + state_active = datum->state_message.valid && + (datum->state_message.state_message || datum->state_message.message_buffer[0]); + + if (objective_active || help_active || state_active) + { + struct game_input_preferences preferences; + struct hud_message_text_definition *hud_messages; + struct hud_state_message_definition *message; + + input_abstraction_get_local_player_preferences( + local_player_index, + &preferences); + if (objective_active) + { + struct hud_color_definition *objective_color = + &hud_globals->messaging.objective_color; + pixel32 flash_color = get_flash_color( + objective_color, + game_time_get() + hud_messaging_globals->objective.uptime - + objective_color->custom.objective.up_ticks - + objective_color->custom.objective.fade_ticks); + + pixel32_to_real_argb_color(flash_color, &state_color); + state_color.alpha *= MIN( + (real)hud_messaging_globals->objective.uptime / + objective_color->custom.objective.fade_ticks, + 1.0f); + pixel_color = real_argb_color_to_pixel32(&state_color); + } + else if (help_active) + { + if (hud_messaging_globals->use_flash) + { + pixel_color = get_flash_color( + &hud_globals->messaging.color, + hud_messaging_globals->flash_start_time); + } + else if (TEST_FLAG( + hud_globals->messaging.color.flash_flags, + _hud_flash_reverse_colors_bit)) + { + pixel_color = hud_globals->messaging.color.flash_color; + } + else + { + pixel_color = hud_globals->messaging.color.color; + } + pixel32_to_real_argb_color(pixel_color, &state_color); + } + else + { + state_color = hud_msg_def->state_color; + pixel_color = real_argb_color_to_pixel32(&state_color); + } + + line_bounds.y0 = line_top; + line_bounds.x0 = screen_point.x; + line_bounds.y1 = 5 * line_height + line_top; + line_bounds.x1 = render.camera.window_bounds.x1 - + render.camera.viewport_bounds.x0; + line_cursor = line_bounds; + draw_string_set_draw_mode( + font_index, + NONE, + _text_justification_left, + 0, + &state_color); + + if (!objective_active && !help_active && + !datum->state_message.state_message) + { + if (datum->state_message.message_buffer[0]) + { + render_state_text( + &line_bounds, + &line_cursor, + datum->state_message.message_buffer, + TRUE); + } + } + else + { + word text_position; + short element_index; + + if (objective_active) + { + struct scenario *scenario; + + match_vassert( + "c:\\halo\\SOURCE\\interface\\hud_messaging.c", + 0x420, + hud_messaging_globals->objective.message && + hud_messaging_globals->objective.uptime, + "hud_messaging_globals->objective.message && hud_messaging_globals->objective.uptime"); + hud_messaging_globals->objective.uptime = MAX( + hud_messaging_globals->objective.uptime - game_time_get_elapsed(), + 0); + scenario = global_scenario_get(); + hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET( + scenario->hud_messages.index); + message = hud_messaging_globals->objective.message; + } + else if (help_active) + { + struct scenario *scenario = global_scenario_get(); + + hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET( + scenario->hud_messages.index); + message = hud_messaging_globals->help_message; + } + else + { + match_vassert( + "c:\\halo\\SOURCE\\interface\\hud_messaging.c", + 0x42C, + state_active, + "show_state_message"); + hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET( + hud_globals->messaging.hud_messages.index); + message = datum->state_message.state_message; + } + + text_position = message->text_start_index; + for (element_index = 0; + element_index < message->element_count; + element_index++) + { + struct hud_state_message_element *element = TAG_BLOCK_GET_ELEMENT( + &hud_messages->elements, + message->element_start_index + element_index, + struct hud_state_message_element); + + switch (element->type) + { + case _hud_message_type_text: + { + wchar_t const *text = tag_data_get_pointer( + &hud_messages->text_data, + text_position * sizeof(wchar_t), + element->data * sizeof(wchar_t)); + rectangle2d text_bounds; + + draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0); + draw_unicode_string_compute_bounds( + &line_bounds, + text, + &text_bounds, + &line_cursor); + line_cursor.x0 -= 3; + text_bounds.x0 = line_bounds.x0; + rasterizer_draw_unicode_string( + &text_bounds, + NULL, + NULL, + 0, + text); + line_bounds.y0 = line_cursor.y0; + text_position += element->data; + } + break; + + case _hud_message_type_icon: + { + short icon_index = NONE; + + if (element->data <= _icon_right_stick) + { + icon_index = element->data; + } + else if (element->data <= _icon_look) + { + if (element->data <= _icon_accept) + { + icon_index = preferences.game_control_to_xbox_buttons[ + button_mappings[element->data - _icon_action]]; + } + else + { + icon_index = button_mappings[element->data - _icon_action]; + } + } + else if (hud_scripted_globals->show_hud_help_text) + { + error(_error_silent, "help text cannot use custom icons"); + } + else + { + short custom_index = element->data - _icon_custom_1; + + match_vassert( + "c:\\halo\\SOURCE\\interface\\hud_messaging.c", + 0x457, + custom_index < NUMBER_OF_HUD_CUSTOM_ICONS, + "custom_indexstate_message.is_text_flags, custom_index)) + { + short string_index = datum->state_message.info[custom_index].text.string_index; + + if (string_index == NONE) + { + render_state_text( + &line_bounds, + &line_cursor, + L"", + FALSE); + } + else if (datum->state_message.info[custom_index].text.uses_scenario_names) + { + match_vassert( + "c:\\halo\\SOURCE\\interface\\hud_messaging.c", + 0x460, + global_scenario_get()->custom_object_names.index != NONE, + "global_scenario_get()->custom_object_names.index!=NONE"); + render_state_text( + &line_bounds, + &line_cursor, + unicode_string_list_get_string( + global_scenario_get()->custom_object_names.index, + string_index), + FALSE); + } + else + { + render_state_text( + &line_bounds, + &line_cursor, + unicode_string_list_get_string( + hud_globals->messaging.alternate_icon_text.index, + string_index), + FALSE); + } + } + else + { + struct icon_hud_element_definition const *icon = + datum->state_message.info[custom_index].icon; + + if (icon) + { + render_state_bitmap( + &line_bounds, + &line_cursor, + pixel_color, + icon); + } + else + { + error( + _error_silent, + "help message using old code. get latest code and tags."); + } + } + break; + } + + if (icon_index < hud_globals->messaging.button_icons.count) + { + struct icon_hud_element_definition const *icon; + + icon_index = remap_sticks_for_local_player( + icon_index, + local_player_index); + icon = TAG_BLOCK_GET_ELEMENT( + &hud_globals->messaging.button_icons, + icon_index, + struct icon_hud_element_definition); + if (TEST_FLAG(icon->flags, _hud_icon_use_text_bit)) + { + wchar_t const *icon_text; + rectangle2d text_bounds; + + if (TEST_FLAG(icon->flags, _hud_icon_use_color_bit)) + { + pixel32_to_real_argb_color(pixel_color, &text_color); + draw_string_set_draw_mode( + font_index, + NONE, + _text_justification_left, + 0, + &text_color); + } + icon_text = unicode_string_list_get_string( + hud_globals->messaging.alternate_icon_text.index, + icon->text_index); + draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0); + draw_unicode_string_compute_bounds( + &line_bounds, + icon_text, + &text_bounds, + &line_cursor); + line_cursor.x0 -= 3; + text_bounds.x0 = line_bounds.x0; + rasterizer_draw_unicode_string( + &text_bounds, + NULL, + NULL, + 0, + icon_text); + line_bounds.y0 = line_cursor.y0; + draw_string_set_draw_mode( + font_index, + NONE, + _text_justification_left, + 0, + &state_color); + } + else + { + render_state_bitmap( + &line_bounds, + &line_cursor, + pixel_color, + icon); + } + } + else + { + rectangle2d text_bounds; + + draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0); + draw_unicode_string_compute_bounds( + &line_bounds, + L"", + &text_bounds, + &line_cursor); + line_cursor.x0 -= 3; + text_bounds.x0 = line_bounds.x0; + rasterizer_draw_unicode_string( + &text_bounds, + NULL, + NULL, + 0, + L""); + line_bounds.y0 = line_cursor.y0; + } + } + break; + + default: + match_assert( + "c:\\halo\\SOURCE\\interface\\hud_messaging.c", + 0x4A7, + !"unreachable"); + break; + } + } + } + + draw_string_set_indents(0, 0); + line_height = first_line_height; + line_top = line_cursor.y1; + } + + if (!objective_active && !help_active && + (datum->state_message.valid || datum->leave_first_line_blank)) + { + if (split_screen) + { + line_top = (short)( + hud_msg_def->spacing * line_height + + screen_point.y - split_screen_hud_message_offset); + } + else + { + line_top = (short)( + hud_msg_def->spacing * line_height + screen_point.y); + } + maximum_message_count--; + } + + qsort( + datum->messages, + NUMBEROF(datum->messages), + sizeof(datum->messages[0]), + (int (__cdecl *)(void const *, void const *))compare_messages); + if (maximum_message_count > 0) + { + wchar_t formatted[276]; + short message_index; + + for (message_index = 0; + message_index < maximum_message_count; + message_index++) + { + struct hud_message_definition *message = &datum->messages[message_index]; + long now; + long elapsed; + rectangle2d message_bounds; + wchar_t const *item_text; + + if (!message->valid) + break; + now = game_time_get(); + elapsed = now - message->time; + text_color = hud_msg_def->text_color; + if ((real)elapsed > hud_msg_def->up_time * TICKS_PER_SECOND) + { + real fade = PIN( + 1.0f - + ((real)elapsed - hud_msg_def->up_time * TICKS_PER_SECOND) / + (hud_msg_def->fade_time * TICKS_PER_SECOND), + 0.0f, + 1.0f); + + text_color.alpha *= (real)pow((double)fade, 1.9f); + } + + message_bounds = render.camera.window_bounds; + message_bounds.y0 = line_top; + message_bounds.x0 = screen_point.x; + message_bounds.y1 = line_height + line_top; + message_bounds.x1 = render.camera.window_bounds.x1 - + render.camera.viewport_bounds.x0; + line_top = (short)(hud_msg_def->spacing * line_height + line_top); + draw_string_set_draw_mode( + font_index, + NONE, + _text_justification_left, + 0, + &text_color); + + if (message->item_definition_index == NONE) + { + rasterizer_draw_unicode_string( + &message_bounds, + NULL, + NULL, + 0, + message->text); + } + else + { + char message_offset = message->message_offset; + struct item_definition *item = item_definition_get( + message->item_definition_index); + + if (message_offset == NONE) + message_offset = message->quantity > 1; + item_text = hud_get_item_string( + item->item.hud_message_index + message_offset); + if ((message->message_offset == NONE && message_offset) || + message->quantity) + { + usprintf( + formatted, + item_text, + message->quantity / + MAX(item->item.hud_message_value_scale, 1)); + item_text = formatted; + } + rasterizer_draw_unicode_string( + &message_bounds, + NULL, + NULL, + 0, + item_text); + } + message->valid = (real)(now - message->time) < + (hud_msg_def->fade_time + hud_msg_def->up_time) * TICKS_PER_SECOND; + if (!message->valid) + message->time = NONE; + } + } + } + + return; +} diff --git a/source/math/geometry.c b/source/math/geometry.c index 9d4bdd58..7d67f1fe 100644 --- a/source/math/geometry.c +++ b/source/math/geometry.c @@ -725,6 +725,472 @@ boolean convex_polygon3d_verify( return TRUE; } +boolean convex_hull3d_begin( + short point_count, + real_point3d const *points, + short vertex_count, + struct vertex3d *vertices, + short edge_count, + struct edge3d *edges, + short surface_count, + struct surface3d *surfaces) +{ + short minimum_x_point_index = NONE; + short farthest_point_index = NONE; + short farthest_line_point_index = NONE; + short farthest_plane_point_index = NONE; + real minimum_x; + real maximum_distance_squared; + real maximum_line_distance_squared; + real maximum_plane_distance; + real_vector3d line_direction; + real_plane3d plane; + short point_index; + + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1710, points); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1711, vertices); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1712, edges); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1713, surfaces); + + if (vertex_count < 4 || edge_count < 6 || surface_count < 4) + { + return FALSE; + } + + minimum_x = REAL_MAX; + for (point_index = 0; point_index < point_count; point_index++) + { + if (points[point_index].x < minimum_x) + { + minimum_x = points[point_index].x; + minimum_x_point_index = point_index; + } + } + if (minimum_x_point_index == NONE) + { + return FALSE; + } + + maximum_distance_squared = 0.f; + for (point_index = 0; point_index < point_count; point_index++) + { + if (distance_squared3d(points + point_index, points + minimum_x_point_index) > maximum_distance_squared) + { + farthest_point_index = point_index; + maximum_distance_squared = distance_squared3d(points + point_index, points + minimum_x_point_index); + } + } + if (farthest_point_index == NONE || maximum_distance_squared < global_convex_hull3d_delta) + { + return FALSE; + } + + maximum_line_distance_squared = 0.f; + vector_from_points3d(points + minimum_x_point_index, points + farthest_point_index, &line_direction); + for (point_index = 0; point_index < point_count; point_index++) + { + real_vector3d offset; + real_vector3d projection = line_direction; + real t; + real distance_squared; + + vector_from_points3d(points + minimum_x_point_index, points + point_index, &offset); + t = dot_product3d(&offset, &line_direction) / magnitude_squared3d(&line_direction); + scale_vector3d(&projection, t, &projection); + subtract_vectors3d(&offset, &projection, &offset); + distance_squared = magnitude_squared3d(&offset); + + if (distance_squared > maximum_line_distance_squared) + { + maximum_line_distance_squared = distance_squared; + farthest_line_point_index = point_index; + } + } + if (farthest_line_point_index == NONE || maximum_line_distance_squared < global_convex_hull3d_delta) + { + return FALSE; + } + + maximum_plane_distance = 0.f; + plane3d_from_points(&plane, + points + minimum_x_point_index, + points + farthest_point_index, + points + farthest_line_point_index); + for (point_index = 0; point_index < point_count; point_index++) + { + real distance = points[point_index].x*plane.n.i + + points[point_index].y*plane.n.j + points[point_index].z*plane.n.k - plane.d; + + if (fabs(distance) > fabs(maximum_plane_distance)) + { + maximum_plane_distance = distance; + farthest_plane_point_index = point_index; + } + } + if (farthest_plane_point_index == NONE || fabs(maximum_plane_distance) < global_convex_hull3d_delta) + { + return FALSE; + } + + if (maximum_plane_distance > 0.f) + { + short swap = farthest_point_index; + farthest_point_index = farthest_line_point_index; + farthest_line_point_index = swap; + } + + vertices[1].point_index = farthest_point_index; + vertices[2].point_index = farthest_line_point_index; + vertices[0].point_index = minimum_x_point_index; + vertices[3].edge_index = 3; + vertices[0].extant = TRUE; + vertices[0].edge_index = 0; + vertices[1].extant = TRUE; + vertices[1].edge_index = 0; + vertices[2].extant = TRUE; + vertices[2].edge_index = 1; + vertices[3].extant = TRUE; + vertices[3].point_index = farthest_plane_point_index; + + edges[0].edge_indices[1] = 3; + edges[2].edge_indices[1] = 5; + edges[2].surface_indices[1] = 3; + edges[3].vertex_indices[1] = 3; + edges[3].surface_indices[1] = 3; + edges[4].vertex_indices[0] = 3; + edges[4].edge_indices[1] = 5; + edges[5].vertex_indices[0] = 3; + edges[5].edge_indices[1] = 3; + edges[5].surface_indices[1] = 3; + edges[0].extant = TRUE; + edges[0].vertex_indices[0] = 0; + edges[0].vertex_indices[1] = 1; + edges[0].edge_indices[0] = 1; + edges[0].surface_indices[0] = 0; + edges[0].surface_indices[1] = 1; + edges[1].extant = TRUE; + edges[1].vertex_indices[0] = 1; + edges[1].vertex_indices[1] = 2; + edges[1].edge_indices[0] = 2; + edges[1].edge_indices[1] = 4; + edges[1].surface_indices[0] = 0; + edges[1].surface_indices[1] = 2; + edges[2].extant = TRUE; + edges[2].vertex_indices[0] = 2; + edges[2].vertex_indices[1] = 0; + edges[2].edge_indices[0] = 0; + edges[2].surface_indices[0] = 0; + edges[3].extant = TRUE; + edges[3].vertex_indices[0] = 0; + edges[3].edge_indices[0] = 4; + edges[3].edge_indices[1] = 2; + edges[3].surface_indices[0] = 1; + edges[4].extant = TRUE; + edges[4].vertex_indices[1] = 1; + edges[4].edge_indices[0] = 0; + edges[4].surface_indices[0] = 1; + edges[4].surface_indices[1] = 2; + edges[5].extant = TRUE; + edges[5].vertex_indices[1] = 2; + edges[5].edge_indices[0] = 1; + edges[5].surface_indices[0] = 2; + + surfaces[0].extant = TRUE; + plane3d_from_points(&surfaces[0].plane, + points + minimum_x_point_index, + points + farthest_point_index, + points + farthest_line_point_index); + surfaces[0].edge_index = 0; + surfaces[1].extant = TRUE; + plane3d_from_points(&surfaces[1].plane, + points + minimum_x_point_index, + points + farthest_plane_point_index, + points + farthest_point_index); + surfaces[1].edge_index = 0; + surfaces[2].extant = TRUE; + plane3d_from_points(&surfaces[2].plane, + points + farthest_point_index, + points + farthest_plane_point_index, + points + farthest_line_point_index); + surfaces[2].edge_index = 1; + surfaces[3].extant = TRUE; + plane3d_from_points(&surfaces[3].plane, + points + minimum_x_point_index, + points + farthest_line_point_index, + points + farthest_plane_point_index); + surfaces[3].edge_index = 2; + + for (point_index = 4; point_index < vertex_count; point_index++) + { + vertices[point_index].extant = FALSE; + } + for (point_index = 6; point_index < edge_count; point_index++) + { + edges[point_index].extant = FALSE; + } + for (point_index = 4; point_index < surface_count; point_index++) + { + surfaces[point_index].extant = FALSE; + } + + return TRUE; +} + +boolean convex_hull3d_expand( + short point_count, + real_point3d const *points, + short vertex_count, + struct vertex3d *vertices, + short edge_count, + struct edge3d *edges, + short surface_count, + struct surface3d *surfaces, + short point_index) +{ + real_point3d const *new_point; + boolean already_contained = TRUE; + short first_boundary_edge_index; + short surface_index; + short edge_index; + short vertex_index; + short new_vertex_index; + short previous_new_edge_index; + short first_new_edge_index; + + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1973, points); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1974, vertices); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1975, edges); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1976, surfaces); + + if (point_index < 0 || point_index >= point_count) + { + return FALSE; + } + + new_point = points + point_index; + first_boundary_edge_index = NONE; + for (surface_index = 0; surface_index < surface_count; surface_index++) + { + struct surface3d const *surface = surfaces + surface_index; + + if (surface->extant && + surface->plane.n.i*new_point->x + surface->plane.n.j*new_point->y + + surface->plane.n.k*new_point->z - surface->plane.d > global_convex_hull3d_delta) + { + already_contained = FALSE; + break; + } + } + if (already_contained) + { + return TRUE; + } + + for (surface_index = 0; surface_index < surface_count; surface_index++) + { + struct surface3d *surface = surfaces + surface_index; + + if (surface->extant) + { + real distance = surface->plane.n.i*new_point->x + + surface->plane.n.j*new_point->y + surface->plane.n.k*new_point->z - surface->plane.d; + surface->extant = !(distance > -global_convex_hull3d_epsilon); + } + } + + for (edge_index = 0; edge_index < edge_count; edge_index++) + { + struct edge3d *edge = edges + edge_index; + + if (edge->extant) + { + struct surface3d *surface0; + struct surface3d *surface1; + + if (edge->surface_indices[0] < 0 || edge->surface_indices[0] >= surface_count || + edge->surface_indices[1] < 0 || edge->surface_indices[1] >= surface_count) + { + return FALSE; + } + surface0 = surfaces + edge->surface_indices[0]; + surface1 = surfaces + edge->surface_indices[1]; + + edge->extant = surface0->extant || surface1->extant; + if (edge->extant && surface0->extant != surface1->extant) + { + if (!surface0->extant) + { + edge->surface_indices[0] = NONE; + } + else + { + edge->surface_indices[1] = NONE; + } + if (first_boundary_edge_index == NONE) + { + first_boundary_edge_index = edge_index; + } + } + } + } + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2076, first_boundary_edge_index!=NONE); + + for (vertex_index = 0; vertex_index < vertex_count; vertex_index++) + { + struct vertex3d *vertex = vertices + vertex_index; + + if (vertex->extant) + { + short first_edge_index = NONE; + short previous_edge_index = NONE; + short edge_index = (short)vertex->edge_index; + + do + { + struct edge3d *edge; + + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2098, edge_index>=0 && edge_indexvertex_indices[0]==vertex_index || edge->vertex_indices[1]==vertex_index); + + if (edge->extant) + { + if (previous_edge_index == NONE) + { + first_edge_index = edge_index; + } + else + { + struct edge3d *previous_edge = edges + previous_edge_index; + previous_edge->edge_indices[previous_edge->vertex_indices[0]==vertex_index] = edge_index; + } + previous_edge_index = edge_index; + } + edge_index = (short)edge->edge_indices[edge->vertex_indices[0]==vertex_index]; + } + while (edge_index != vertex->edge_index); + + if (previous_edge_index == NONE) + { + vertex->extant = FALSE; + } + else + { + struct edge3d *previous_edge = edges + previous_edge_index; + vertex->edge_index = first_edge_index; + previous_edge->edge_indices[previous_edge->vertex_indices[0]==vertex_index] = first_edge_index; + } + } + } + + new_vertex_index = 0; + first_new_edge_index = NONE; + previous_new_edge_index = NONE; + edge_index = first_boundary_edge_index; + while (new_vertex_index < vertex_count && vertices[new_vertex_index].extant) + { + new_vertex_index++; + } + if (new_vertex_index >= vertex_count) + { + return FALSE; + } + + for (;;) + { + struct edge3d *edge; + struct vertex3d *vertex1; + struct vertex3d *vertex2; + struct surface3d *new_surface; + struct edge3d *new_edge; + short new_surface_index; + short new_edge_index; + + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2167, edge_index>=0 && edge_indexextant); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2169, + edge->vertex_indices[0]>=0 && edge->vertex_indices[0]vertex_indices[1]>=0 && edge->vertex_indices[1]surface_indices[0]==NONE)!=(edge->surface_indices[1]==NONE)); + + for (new_surface_index = 0; + new_surface_index < surface_count && surfaces[new_surface_index].extant; + new_surface_index++) + { + } + if (new_surface_index >= surface_count) + { + break; + } + + for (new_edge_index = 0; + new_edge_index < edge_count && edges[new_edge_index].extant; + new_edge_index++) + { + } + if (new_edge_index >= edge_count) + { + break; + } + + new_surface = surfaces + new_surface_index; + vertex1 = vertices + edge->vertex_indices[edge->surface_indices[0]!=NONE]; + vertex2 = vertices + edge->vertex_indices[edge->surface_indices[1]!=NONE]; + + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2206, vertex1->extant); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2207, + vertex1->point_index>=0 && vertex1->point_indexextant); + match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2210, + vertex2->point_index>=0 && vertex2->point_indexextant = TRUE; + plane3d_from_points(&new_surface->plane, + new_point, + points + vertex1->point_index, + points + vertex2->point_index); + new_surface->edge_index = edge_index; + + new_edge = edges + new_edge_index; + new_edge->extant = TRUE; + new_edge->vertex_indices[0] = edge->vertex_indices[edge->surface_indices[1]!=NONE]; + new_edge->vertex_indices[1] = new_vertex_index; + new_edge->edge_indices[0] = previous_new_edge_index; + new_edge->edge_indices[1] = edge->edge_indices[edge->surface_indices[0]!=NONE]; + new_edge->surface_indices[0] = new_surface_index; + new_edge->surface_indices[1] = NONE; + + edge->edge_indices[edge->surface_indices[0]!=NONE] = new_edge_index; + edge->surface_indices[edge->surface_indices[0]!=NONE] = new_surface_index; + if (previous_new_edge_index == NONE) + { + first_new_edge_index = new_edge_index; + } + else + { + edges[previous_new_edge_index].surface_indices[1] = new_surface_index; + } + previous_new_edge_index = new_edge_index; + + edge_index = (short)new_edge->edge_indices[1]; + if (edge_index == first_boundary_edge_index) + { + vertices[new_vertex_index].extant = TRUE; + vertices[new_vertex_index].point_index = point_index; + vertices[new_vertex_index].edge_index = first_new_edge_index; + edges[first_new_edge_index].edge_indices[0] = new_edge_index; + new_edge->surface_indices[1] = edges[first_new_edge_index].surface_indices[0]; + return TRUE; + } + } + + return FALSE; +} + boolean convex_hull3d( short point_count, real_point3d const *points, diff --git a/source/render/render_debug.h b/source/render/render_debug.h index 4e8b86d0..07717fa2 100644 --- a/source/render/render_debug.h +++ b/source/render/render_debug.h @@ -18,7 +18,11 @@ header included in hcex build. void render_debug_string(boolean immediate, const char *string); -void render_debug_string_at_point(boolean immediate, real_point3d *point, const char *string, real_argb_color const *color); +void render_debug_string_at_point( + boolean immediate, + real_point3d const *point, + const char *string, + real_argb_color const *color); void render_debug_point(boolean immediate, real_point3d const *point, real size, real_argb_color const *color); void render_debug_line(boolean immediate, real_point3d const *point0, real_point3d const *point1, real_argb_color const *color); diff --git a/source/sound/game_sound.c b/source/sound/game_sound.c index 7b47a05e..b8b57970 100644 --- a/source/sound/game_sound.c +++ b/source/sound/game_sound.c @@ -221,11 +221,6 @@ static void scripted_music_stop_all( static void scripted_looping_sound_stop_internal( long sound_index, boolean fixed_fadeout); -static boolean track_object_impulse_sound( - long object_index, - void const *attachment_data, - struct sound_source *source); - /* ---------- globals */ struct game_sound_globals *game_sound_globals; @@ -1006,7 +1001,7 @@ static boolean looping_sound_definition_is_music( return FALSE; } -static boolean track_object_impulse_sound( +boolean track_object_impulse_sound( long object_index, void const *attachment_data, struct sound_source *source) diff --git a/source/sound/game_sound.h b/source/sound/game_sound.h index dba54ab3..36dda194 100644 --- a/source/sound/game_sound.h +++ b/source/sound/game_sound.h @@ -97,6 +97,13 @@ void compute_sound_obstruction( short local_player_index, struct sound_source *source, real distance); +void game_sound_set_mouth_aperture( + long object_index, + real mouth_aperture); +boolean track_object_impulse_sound( + long object_index, + void const *attachment_data, + struct sound_source *source); /* ---------- globals */ diff --git a/source/sound/sound_classes.c b/source/sound/sound_classes.c index 66e48903..4a431a0d 100644 --- a/source/sound/sound_classes.c +++ b/source/sound/sound_classes.c @@ -115,9 +115,7 @@ symbols in this file: enum { - NUMBER_OF_SOUND_CLASSES = 51, - MAXIMUM_SOUND_INSTANCES_PER_DEFINITION = 16, - MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION = 16 + NUMBER_OF_SOUND_CLASSES = 51 }; /* ---------- macros */ diff --git a/source/sound/sound_classes.h b/source/sound/sound_classes.h index a52c6cf4..ee690265 100644 --- a/source/sound/sound_classes.h +++ b/source/sound/sound_classes.h @@ -13,6 +13,12 @@ file has inline function assertions. /* ---------- constants */ +enum +{ + MAXIMUM_SOUND_INSTANCES_PER_DEFINITION = 16, + MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION = 16 +}; + enum sound_class { _sound_class_unit_dialog = 19, diff --git a/source/sound/sound_definitions.h b/source/sound/sound_definitions.h index f80070cf..f90b9a76 100644 --- a/source/sound/sound_definitions.h +++ b/source/sound/sound_definitions.h @@ -39,6 +39,13 @@ enum MAXIMUM_DETAIL_SOUNDS_PER_LOOPING_SOUND = 32, }; +enum sound_definition_flags +{ + _sound_definition_fit_to_compression_block_size_bit, + _sound_definition_linked_permutations_bit, + NUMBER_OF_SOUND_DEFINITION_FLAGS, +}; + enum looping_sound_definition_flags { _looping_sound_deafening_bit, @@ -194,6 +201,9 @@ real sound_definition_get_maximum_distance( long sound_index); real sound_definition_get_minimum_distance( long sound_index); +real sound_permutation_get_real_mouth_aperture( + struct sound_permutation *permutation, + long tick_index); short sound_definition_find_pitch_range_by_pitch( struct sound_definition *definition, diff --git a/source/sound/sound_manager.c b/source/sound/sound_manager.c index d5abcdf6..bcfe86b9 100644 --- a/source/sound/sound_manager.c +++ b/source/sound/sound_manager.c @@ -312,6 +312,14 @@ enum sound_channel_flags _sound_channel_compressed_bit, }; +enum sound_channel_state +{ + _sound_channel_idle, + _sound_channel_playing, + _sound_channel_queued, + NUMBER_OF_SOUND_CHANNEL_STATES, +}; + enum looping_sound_track_flags { _fade_in_at_start_bit, @@ -333,10 +341,11 @@ enum looping_sound_track_flags struct sound_pitch_range)->permutations, \ permutation_index, \ struct sound_permutation) +#define sound_cache_sound_loaded(sound) \ + _sound_cache_sound_request((sound), FALSE, FALSE, FALSE) /* ---------- structures */ -struct platform_sound_channel_properties; struct platform_sound_listener_properties; struct sound_location; struct sound_permutation; @@ -373,6 +382,28 @@ struct sound_channel_datum struct sound_permutation *queued_permutation; }; +struct sound_channel_summary +{ + short like_definition_count; + short like_definition_channels[MAXIMUM_SOUND_INSTANCES_PER_DEFINITION]; + short maximum_instance_count; + short like_source_count; + short like_source_channels[MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION]; + short maximum_source_instance_count; +}; + +struct platform_sound_channel_properties +{ + real minimum_distance; + real maximum_distance; + real pitch; + real gain; + real cone_inside_angle; + real cone_outside_angle; + real cone_outside_gain; + real reverb_attenuation; +}; + struct sound_datum { short identifier; @@ -438,15 +469,9 @@ struct sound_platform_definition void); void (*queue_sound_to_channel)( short channel_index, - struct sound_permutation const *permutation, - long hardware_format, - boolean looping, - short playback_rate, - boolean cache_miss); + struct sound_permutation *permutation); void (*channel_update)( - short channel_index, - boolean force, - short playback_rate); + short channel_index); void (*stop_channel)( short channel_index); short (*get_channel_state)( @@ -457,24 +482,16 @@ struct sound_platform_definition void); void (*set_channel_location)( short channel_index, - boolean valid, + boolean spatialized, struct sound_location const *location, - real minimum_distance, - real maximum_distance, - boolean force, - short playback_rate); + real obstruction, + real occlusion, + boolean attenuate_direct_path); void (*set_channel_properties)( short channel_index, struct platform_sound_channel_properties const *properties, - boolean gain_only, - boolean force, - short playback_rate); - void (*handle_audio_profile_changes)( - boolean surround_sound, - boolean headphones, - long speaker_configuration); - boolean (*hardware_reverb_enabled)( - void); + boolean gain_only); + real direct_path_gain; }; struct sound_manager_globals @@ -504,6 +521,10 @@ typedef char verify_sound_listener_size[ sizeof(struct sound_listener) == 0x44 ? 1 : -1]; typedef char verify_sound_channel_datum_size[ sizeof(struct sound_channel_datum) == 0x18 ? 1 : -1]; +typedef char verify_sound_channel_summary_size[ + sizeof(struct sound_channel_summary) == 0x48 ? 1 : -1]; +typedef char verify_platform_sound_channel_properties_size[ + sizeof(struct platform_sound_channel_properties) == 0x20 ? 1 : -1]; typedef char verify_sound_datum_size[ sizeof(struct sound_datum) == 0xAC ? 1 : -1]; typedef char verify_looping_sound_datum_size[ @@ -512,7 +533,7 @@ typedef char verify_looping_sound_datum_size[ typedef char verify_sound_manager_globals_size[ sizeof(struct sound_manager_globals) == 0x178 ? 1 : -1]; typedef char verify_sound_platform_definition_size[ - sizeof(struct sound_platform_definition) == 0x40 ? 1 : -1]; + sizeof(struct sound_platform_definition) == 0x3C ? 1 : -1]; typedef char verify_sound_platform_dispose_offset[ offsetof(struct sound_platform_definition, dispose) == 0x8 ? 1 : -1]; typedef char verify_sound_platform_pause_offset[ @@ -560,6 +581,16 @@ static long sound_travel_milliseconds( real distance); static boolean refresh_sound( long sound_index); +static void sound_channel_summary_build( + struct sound_channel_summary *summary, + long sound_index); +static void channel_queue_sound( + short channel_index, + struct sound_permutation *permutation); +static void channel_set_properties_hardware( + short channel_index, + struct platform_sound_channel_properties *properties, + boolean gain_only); static short channel_get_state( short channel_index); static boolean track_loop_track_sound( @@ -575,6 +606,8 @@ static long looping_sound_new( long definition_index, long identifier, struct sound_source const *source); +static void sound_set_definition_end( + long sound_index); static long update_potentially_audible_looping_sound( long definition_index, long looping_sound_index, @@ -584,6 +617,18 @@ static real limit_pitch( real desired_pitch, real old_pitch, real maximum_bend); +static short sound_find_like_channel( + long sound_index, + short const *channel_indices, + short channel_count); +static void update_channel_for_impulse_sound( + short channel_index, + real fade); +static void update_channel_for_looping_sound( + short channel_index, + real fade); +static void update_channels( + void); static void render_debug_sound( long sound_index); static void render_debug_looping_sound( @@ -944,7 +989,7 @@ static void sound_update_time( long render_time = system_milliseconds(); sound_manager_globals.ticks_elapsed = - (real)(render_time - sound_manager_globals.render_time) * 0.029999999f; + ((real)render_time - sound_manager_globals.render_time) * 0.029999999f; sound_manager_globals.render_time = render_time; return; @@ -1110,6 +1155,125 @@ static boolean refresh_sound( return FALSE; } +static void sound_channel_summary_build( + struct sound_channel_summary *summary, + long sound_index) +{ + struct sound_datum *sound = sound_get(sound_index); + struct sound_definition *definition = + sound_definition_get(sound->definition_index); + short channel_index; + + summary->like_definition_count = 0; + summary->like_source_count = 0; + summary->maximum_instance_count = + sound_class_get(definition->sound_class)->maximum_number_per_definition; + summary->maximum_source_instance_count = + sound_class_get(definition->sound_class)->maximum_number_per_object; + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x6A4, + summary->maximum_source_instance_count<=MAXIMUM_SOUND_INSTANCES_PER_DEFINITION); + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x6A5, + summary->maximum_instance_count<=MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION); + + for ( + channel_index = 0; + channel_index < sound_manager_globals.channel_count; + channel_index++) + { + struct sound_channel_datum *channel = channel_get(channel_index); + + if (channel->sound_index != NONE && channel->sound_index != sound_index) + { + struct sound_datum *other_sound = sound_get(channel->sound_index); + + if (sound_valid_for_channel( + definition->compression, + definition->encoding, + definition->sample_rate, + sound->source.spatialization_mode, + channel->type_flags) && + sound->definition_index == other_sound->definition_index) + { + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x6B5, + summary->like_definition_countmaximum_instance_count); + summary->like_definition_channels[ + summary->like_definition_count++] = channel_index; + + if (sound->source_identifier != NONE && + sound->source_identifier == other_sound->source_identifier) + { + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x6BB, + summary->like_source_countmaximum_source_instance_count); + summary->like_source_channels[ + summary->like_source_count++] = channel_index; + } + } + } + } + + return; +} + +static void channel_queue_sound( + short channel_index, + struct sound_permutation *permutation) +{ + struct sound_channel_datum *channel = channel_get(channel_index); + + if (channel->queued_permutation) + { + sound_cache_sound_finished(channel->queued_permutation); + } + + sound_manager_globals.platform_definition->queue_sound_to_channel( + channel_index, + permutation); + + if (channel->playing_permutation) + { + channel->queued_permutation = permutation; + } + else + { + channel->playing_permutation = permutation; + channel->estimated_tick_time = 0.f; + } + + return; +} + +static void channel_set_properties_hardware( + short channel_index, + struct platform_sound_channel_properties *properties, + boolean gain_only) +{ + struct sound_channel_datum *channel = channel_get(channel_index); + + if (!gain_only) + { + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x848, + properties->pitch>0.f); + channel->pitch = properties->pitch; + } + + sound_manager_globals.platform_definition->set_channel_properties( + channel_index, + properties, + gain_only); + + return; +} + static boolean track_loop_track_sound( long looping_sound_index, void const *track_data, @@ -1231,6 +1395,61 @@ static long looping_sound_new( return looping_sound_index; } +static void sound_set_definition_end( + long sound_index) +{ + struct sound_datum *sound = sound_get(sound_index); + struct sound_definition *definition = + sound_definition_get(sound->next_definition_index); + + SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, TRUE); + sound->definition_index = sound->next_definition_index; + sound->next_definition_index = NONE; + sound->pitch_range_index = sound_definition_find_pitch_range_by_pitch( + definition, + sound->pitch, + (word)sound->pitch_range_index); + sound->permutation_index = sound_definition_next_permutation( + definition, + sound->pitch_range_index, + NONE); + + if (sound->playing_channel_index != NONE) + { + struct sound_channel_summary summary; + short channel_index = NONE; + + sound_channel_summary_build(&summary, sound_index); + if (summary.like_source_count >= summary.maximum_source_instance_count) + { + channel_index = sound_find_like_channel( + sound_index, + summary.like_source_channels, + summary.like_source_count); + } + else if (summary.like_definition_count >= summary.maximum_instance_count) + { + channel_index = sound_find_like_channel( + sound_index, + summary.like_definition_channels, + summary.like_definition_count); + } + else + { + return; + } + + if (channel_index != NONE) + { + sound_index = channel_get(channel_index)->sound_index; + } + + sound_stop(sound_index); + } + + return; +} + static long update_potentially_audible_looping_sound( long definition_index, long looping_sound_index, @@ -1773,6 +1992,104 @@ static real source_distance_squared( return distance_squared; } +static short sound_find_like_channel( + long sound_index, + short const *channel_indices, + short channel_count) +{ + struct sound_datum *sound = sound_get(sound_index); + struct sound_definition *definition = + sound_definition_get(sound->definition_index); + real sound_distance_squared = source_distance_squared( + sound->listener_index, + &sound->source); + short index; + + for (index = 0; index < channel_count; index++) + { + short channel_index = channel_indices[index]; + struct sound_datum *other_sound = + sound_get(channel_get(channel_index)->sound_index); + struct sound_class_definition *sound_class = + sound_class_get(definition->sound_class); + + if (sound_manager_globals.render_time - other_sound->start_time >= + sound_class->preemption_time) + { + real other_distance_squared = source_distance_squared( + other_sound->listener_index, + &other_sound->source); + + if (sound_distance_squared - other_distance_squared < 1.f) + { + return channel_index; + } + } + } + + return NONE; +} + +static void update_channel_for_impulse_sound( + short channel_index, + real fade) +{ + struct sound_channel_datum *channel = channel_get(channel_index); + struct sound_datum *sound = sound_get(channel->sound_index); + struct sound_definition *definition = + sound_definition_get(sound->definition_index); + real scale = sound->source.scale; + real master_gain = sound_manager_master_gain(definition->sound_class); + real scale_upper_gain = definition->one_gain_modifier; + real scale_lower_gain = definition->zero_gain_modifier; + real distance_gain = + (scale_upper_gain - scale_lower_gain) * scale + scale_lower_gain; + real gain = master_gain * sound->source.gain * fade * distance_gain; + struct platform_sound_channel_properties properties; + + if (sound->playing_channel_index == NONE) + { + struct sound_pitch_range *pitch_range = TAG_BLOCK_GET_ELEMENT( + &definition->pitch_ranges, + sound->pitch_range_index, + struct sound_pitch_range); + struct sound_permutation *permutation = TAG_BLOCK_GET_ELEMENT( + &pitch_range->permutations, + sound->permutation_index, + struct sound_permutation); + + properties.gain = permutation->gain * definition->gain_modifier * gain; + properties.pitch = sound->pitch * pitch_range->playback_rate; + properties.minimum_distance = sound_definition_get_minimum_distance( + sound->definition_index); + properties.cone_inside_angle = definition->inner_cone_angle; + properties.cone_outside_angle = definition->outer_cone_angle; + properties.cone_outside_gain = definition->outer_cone_gain; + properties.maximum_distance = FLT_MAX; + properties.reverb_attenuation = + sound_class_get(definition->sound_class)->wet_gain; + + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x79A, + sound_cache_sound_loaded(permutation)); + channel_set_properties_hardware(channel_index, &properties, FALSE); + channel_queue_sound(channel_index, permutation); + sound->playing_channel_index = channel_index; + } + else + { + properties.gain = + channel->playing_permutation->gain * definition->gain_modifier * gain; + + channel_set_properties_hardware(channel_index, &properties, TRUE); + } + + sound_manager_globals.platform_definition->channel_update(channel_index); + + return; +} + void sound_stop_all( void) { @@ -2256,6 +2573,223 @@ boolean sound_refresh_looping( return result; } +static void update_channel_for_looping_sound( + short channel_index, + real fade) +{ + struct sound_channel_datum *channel = channel_get(channel_index); + struct sound_datum *sound = sound_get(channel->sound_index); + struct sound_definition *definition = + sound_definition_get(sound->definition_index); + struct looping_sound_datum *looping_sound = + looping_sound_get(sound->source_identifier); + long *primary_sound_index = + &looping_sound->tracks[sound->loop_track_index].primary_sound_index; + struct looping_sound_definition *looping_definition = + looping_sound_definition_get(looping_sound->definition_index); + struct looping_sound_track *track = TAG_BLOCK_GET_ELEMENT( + &looping_definition->tracks, + sound->loop_track_index, + struct looping_sound_track); + real scale = sound->source.scale; + real pitch = sound_scale_value( + sound->pitch, + definition->zero_pitch_modifier, + definition->one_pitch_modifier, + scale); + struct platform_sound_channel_properties properties; + struct sound_pitch_range *pitch_range; + + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x9C4, + sound->type!=_sound_impulse); + + properties.minimum_distance = sound_definition_get_minimum_distance( + sound->definition_index); + properties.maximum_distance = FLT_MAX; + properties.cone_inside_angle = definition->inner_cone_angle; + properties.cone_outside_angle = definition->outer_cone_angle; + properties.cone_outside_gain = definition->outer_cone_gain; + properties.reverb_attenuation = + sound_class_get(definition->sound_class)->wet_gain; + properties.gain = sound_manager_master_gain(definition->sound_class) * + track->gain * + sound_scale_value( + definition->gain_modifier, + definition->zero_gain_modifier, + definition->one_gain_modifier, + scale) * + sound->source.gain * fade; + + if (sound->playing_channel_index == NONE) + { + struct sound_permutation *permutation; + + pitch_range = TAG_BLOCK_GET_ELEMENT( + &definition->pitch_ranges, + sound->pitch_range_index, + struct sound_pitch_range); + permutation = TAG_BLOCK_GET_ELEMENT( + &pitch_range->permutations, + sound->permutation_index, + struct sound_permutation); + properties.gain *= permutation->gain; + properties.pitch = pitch * pitch_range->playback_rate; + + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x9DA, + sound_cache_sound_loaded(permutation)); + channel_set_properties_hardware(channel_index, &properties, FALSE); + channel_queue_sound(channel_index, permutation); + sound->playing_channel_index = channel_index; + } + else + { + real limited_pitch; + + pitch_range = TAG_BLOCK_GET_ELEMENT( + &definition->pitch_ranges, + sound->pitch_range_index, + struct sound_pitch_range); + limited_pitch = limit_pitch( + pitch, + channel_get(sound->playing_channel_index)->pitch * + pitch_range->natural_pitch, + definition->maximum_bend_per_second); + properties.pitch = limited_pitch * pitch_range->playback_rate; + + if (sound->type == _sound_loop_track && + (sound->fade_start_time == sound->fade_stop_time || + sound->fade_interpolation_end != 0.f) && + sound_definition_find_pitch_range_by_pitch( + definition, + limited_pitch, + (word)sound->pitch_range_index) != sound->pitch_range_index && + channel->sound_index == *primary_sound_index && + !sound_manager_globals.idling) + { + long new_sound_index = update_potentially_audible_looping_sound( + sound->definition_index, + sound->source_identifier, + sound->loop_track_index, + _sound_loop_track); + + if (new_sound_index != NONE) + { + sound_start_fade( + _sound_fade_mode_crossfade, + sound_pitch_range_fade_time, + new_sound_index, + channel->sound_index); + *primary_sound_index = new_sound_index; + } + } + + if (sound->type != _sound_stop_track && + (sound->type != _sound_start_track || + !TEST_FLAG(track->flags, _fade_in_at_start_bit)) && + (channel_get_state(sound->playing_channel_index) != + _sound_channel_queued || + TEST_FLAG(sound->flags, _sound_waiting_for_cache_bit) || + (channel->playing_permutation->next_permutation_index == NONE && + sound->next_definition_index != NONE))) + { + if (sound->next_definition_index != NONE) + { + if (!channel->playing_permutation || + channel->playing_permutation->next_permutation_index == NONE) + { + sound_set_definition_end(channel->sound_index); + definition = sound_definition_get(sound->definition_index); + pitch_range = TAG_BLOCK_GET_ELEMENT( + &definition->pitch_ranges, + sound->pitch_range_index, + struct sound_pitch_range); + } + } + else if (!TEST_FLAG(sound->flags, _sound_waiting_for_cache_bit)) + { + short permutation_index = sound_definition_next_permutation( + definition, + sound->pitch_range_index, + sound->permutation_index); + + if (permutation_index == NONE) + { + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0xA1C, + TEST_FLAG(definition->flags, _sound_definition_linked_permutations_bit)); + + if (TEST_FLAG( + looping_definition->flags, + _looping_sound_fake_impulse_sound_bit)) + { + sound->type = _sound_stop_track; + looping_sound->ordered_sounds_finished = TRUE; + } + else + { + permutation_index = sound_definition_next_permutation( + definition, + sound->pitch_range_index, + NONE); + } + } + + if (permutation_index != NONE) + { + sound->permutation_index = permutation_index; + SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, TRUE); + } + } + + { + struct sound_permutation *permutation = TAG_BLOCK_GET_ELEMENT( + &pitch_range->permutations, + sound->permutation_index, + struct sound_permutation); + + if (sound->type != _sound_stop_track && + _sound_cache_sound_request( + permutation, + FALSE, + TRUE, + TRUE)) + { + SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, FALSE); + channel_queue_sound(channel_index, permutation); + + if (sound->next_definition_index == NONE && + permutation->next_permutation_index == NONE) + { + if (sound->type == _sound_start_track) + { + sound->type = _sound_loop_track; + } + else if (sound->type == _sound_stopping_track) + { + sound->type = _sound_stop_track; + } + } + } + } + } + + properties.gain *= TAG_BLOCK_GET_ELEMENT( + &pitch_range->permutations, + sound->permutation_index, + struct sound_permutation)->gain; + channel_set_properties_hardware(channel_index, &properties, FALSE); + } + + sound_manager_globals.platform_definition->channel_update(channel_index); + + return; +} + void sound_dispose_from_old_map( void) { @@ -2491,3 +3025,204 @@ static void refresh_sounds( return; } + +static void update_channels( + void) +{ + short channel_index; + + for ( + channel_index = 0; + channel_index < sound_manager_globals.channel_count; + channel_index++) + { + struct sound_channel_datum *channel = channel_get(channel_index); + + if (channel->sound_index != NONE) + { + struct sound_datum *sound = sound_get(channel->sound_index); + struct sound_definition *definition = + sound_definition_get(sound->definition_index); + real fade = sound_calculate_fade(channel->sound_index); + + if (fade == 0.f && sound->fade_interpolation_end == 0.f) + { + sound_stop(channel->sound_index); + channel->sound_index = NONE; + continue; + } + + if (TEST_FLAG(channel->type_flags, _sound_channel_3d_bit)) + { + switch (sound->source.spatialization_mode) + { + case _sound_spatialization_mode_none: + match_vassert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x7D4, + FALSE, + NULL); + break; + + case _sound_spatialization_mode_absolute: + { + struct sound_listener *listener = + listener_get(sound->listener_index); + struct sound_location location; + + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x7DB, + listener->valid); + matrix4x3_inverse_transform_point( + &listener->matrix, + &sound->source.location.position, + &location.position); + matrix4x3_inverse_transform_normal( + &listener->matrix, + &sound->source.location.forward, + &location.forward); + matrix4x3_inverse_transform_vector( + &listener->matrix, + &sound->source.location.translational_velocity, + &location.translational_velocity); + location.translational_velocity.i = + TICKS_PER_SECOND * location.translational_velocity.i - + listener->velocity.i; + location.translational_velocity.j = + TICKS_PER_SECOND * location.translational_velocity.j - + listener->velocity.j; + location.translational_velocity.k = + TICKS_PER_SECOND * location.translational_velocity.k - + listener->velocity.k; + sound_manager_globals.platform_definition->set_channel_location( + channel_index, + TRUE, + &location, + sound->source.obstruction, + sound->source.occlusion, + listener->underwater); + } + break; + + case _sound_spatialization_mode_relative: + sound_manager_globals.platform_definition->set_channel_location( + channel_index, + TRUE, + &sound->source.location, + 0.f, + 0.f, + FALSE); + break; + + default: + match_vassert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x7EC, + FALSE, + NULL); + break; + } + } + else + { + real_point3d relative_position = sound->source.location.position; + + switch (sound->source.spatialization_mode) + { + case _sound_spatialization_mode_none: + break; + + case _sound_spatialization_mode_absolute: + { + struct sound_listener *listener = + listener_get(sound->listener_index); + + match_assert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x7FA, + listener->valid); + matrix4x3_inverse_transform_point( + &listener->matrix, + &sound->source.location.position, + &relative_position); + } + /* fall through */ + + case _sound_spatialization_mode_relative: + { + real minimum_distance = + sound_definition_get_minimum_distance( + sound->definition_index); + real maximum_distance = + sound_definition_get_maximum_distance( + sound->definition_index); + real distance = square_root( + relative_position.x * relative_position.x + + (relative_position.y * relative_position.y + + relative_position.z * relative_position.z)); + real attenuation = 1.f - + (distance - minimum_distance) / + (maximum_distance - minimum_distance); + + fade *= PIN(attenuation, 0.f, 1.f); + } + break; + + default: + match_vassert( + "c:\\halo\\SOURCE\\sound\\sound_manager.c", + 0x80A, + FALSE, + NULL); + break; + } + } + + if (sound->type == _sound_impulse) + { + update_channel_for_impulse_sound(channel_index, fade); + } + else + { + update_channel_for_looping_sound(channel_index, fade); + } + + if (sound_class_get(definition->sound_class)->speech && + sound->track_proc == track_object_impulse_sound) + { + game_sound_set_mouth_aperture( + sound->source_identifier, + sound_permutation_get_real_mouth_aperture( + channel->playing_permutation, + (long)channel->estimated_tick_time)); + } + } + } + + return; +} + +void sound_idle( + void) +{ + sound_manager_globals.idling = TRUE; + + if (sound_manager_globals.initialized && sound_manager_globals.enabled) + { + sound_manager_globals.platform_definition->begin_scene(); + + if (!sound_manager_globals.paused) + { + sound_update_time(); + update_channels(); + } + + sound_manager_globals.platform_definition->end_scene(); + } + + sound_cache_idle(); + sound_manager_globals.idling = FALSE; + + return; +}