From eb61c0f7469a683ffaa70bf8aae18d828834e7aa Mon Sep 17 00:00:00 2001 From: Jonas Volman Date: Sat, 12 Sep 2026 17:23:46 -0700 Subject: [PATCH] Reconcile seventh 10K exact source batch --- config/parked.json | 342 ++++- config/semantic_data_matches.json | 2 +- config/symbols.json | 39 +- ...lee_teleport_model_node_packet_20260912.md | 82 ++ ...eds_obj_batch7_exact_and_fuzzy_20260912.md | 168 +++ ...face_obj_weapon_hud_best_fuzzy_20260912.md | 163 +++ ...ane_reconciliation_10k_batch_7_20260912.md | 125 ++ ...game_statistics_render_cameras_20260912.md | 49 + ..._obj_scope_table_owner_refresh_20260908.md | 5 +- docs/reconciliation_queue_20260907.md | 21 + .../flag_render_proper_20260912.md | 345 +++++ .../player_aim_projectile_20260912.md | 189 +++ source/ai/action_flee.c | 74 + source/ai/action_vehicle.c | 13 + source/ai/actions.c | 2 - source/ai/actions.h | 2 + source/ai/ai.c | 269 ++++ source/ai/ai.h | 4 + source/ai/ai_script.c | 101 ++ source/ai/ai_script.h | 4 + source/ai/path_structure_bsp.c | 226 +++ source/bungie_net/common/key_agreement.c | 120 +- .../network/transport_endpoint_set_winsock.c | 22 + .../network/transport_endpoint_winsock.h | 6 + source/effects/decals.c | 111 +- source/game/game.c | 2 +- source/game/game_statistics.c | 1 + source/game/game_statistics_internal.h | 24 + source/hs/hs.c | 5 +- source/interface/hud_draw.c | 410 +++++- source/interface/hud_draw.h | 9 + source/interface/interface.c | 102 +- source/interface/interface.h | 2 + .../ui_widget_event_handler_functions.c | 1 + source/main/main.c | 253 +++- source/main/main.h | 10 + source/math/real_math.h | 6 + source/models/model_animations.c | 82 ++ source/objects/damage.c | 356 ++++- source/rasterizer/rasterizer_debug_options.h | 1 + source/render/render.h | 7 + source/render/render_cameras.c | 309 +++++ source/render/render_cameras_internal.h | 13 + source/render/render_sprite.c | 9 +- source/scenario/scenario.c | 5 +- source/structures/structures.c | 107 +- source/units/biped_definitions.h | 2 +- source/units/bipeds.c | 1235 ++++++++++++++++- source/units/bipeds.h | 9 + source/units/units.c | 8 +- source/units/units.h | 9 + source/units/vehicles.h | 3 + 52 files changed, 5239 insertions(+), 225 deletions(-) create mode 100644 docs/object_matching_logs/ai_flee_teleport_model_node_packet_20260912.md create mode 100644 docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md create mode 100644 docs/object_matching_logs/interface_obj_weapon_hud_best_fuzzy_20260912.md create mode 100644 docs/object_matching_logs/lane_reconciliation_10k_batch_7_20260912.md create mode 100644 docs/object_matching_logs/prototype_owner_split_game_statistics_render_cameras_20260912.md create mode 100644 research/reconstructions/flag_render_proper_20260912.md create mode 100644 research/reconstructions/player_aim_projectile_20260912.md create mode 100644 source/game/game_statistics_internal.h diff --git a/config/parked.json b/config/parked.json index dfc58f47..6b18e3f6 100644 --- a/config/parked.json +++ b/config/parked.json @@ -1,6 +1,253 @@ { "version": 1, "entries": [ + { + "unit": "source/ai/ai", + "function": "_ai_disconnect_from_structure_bsp", + "class": "unclassified", + "evidence": "Best complete house-rule-reviewed January control-flow reconstruction retained from the Claude large-AI packet. It owns its public prototype in ai.h, scenario.c now includes that owner, and the body uses typed actor/prop/swarm accessors and named constants without compiler steering. Target and candidate have the same 1,280-byte padded envelope and all 62 relocation identities; remaining differences are distributed register/schedule choices. This is a zero-credit fuzzy park. Reopen only with authoritative January source/symbol context or a natural same-compiler donor that explains the residual. See docs/object_matching_logs/ai_obj_482743d8a_reconciliation_audit_20260909.md.", + "measurements": { + "target": { + "size": 1280, + "relocation_count": 62, + "normalized_sha256": "50551a92f7dfebd3f86a133f6737f4184152acadbde7c7958b057bb73e9276a9" + }, + "base": { + "size": 1280, + "relocation_count": 62, + "normalized_sha256": "19404fc01508e0ef27258ed54581626941ad7aab6d4372dc8a692870c739208b" + }, + "objdiff_percent": 95.6066 + } + }, + { + "unit": "source/ai/path_structure_bsp", + "function": "_structure_test_line2d", + "class": "instruction-scheduling", + "evidence": "Best complete ordinary-C reconstruction from the dedicated path-structure BSP audit. It preserves the January recursive fallback and target-proven boundary behavior, uses the owning header and named path/collision flag bits, and contains no volatile, barrier, pragma, assembly, or fake dependency. Candidate and target both occupy 1,104 padded bytes with all 29 relocation identities; the remaining difference is the documented legal-C schedule fixed point. This is a zero-credit fuzzy park. Reopen only with authoritative January source or a natural same-compiler donor. See docs/object_matching_logs/path_structure_bsp_obj.md.", + "measurements": { + "target": { + "size": 1104, + "relocation_count": 29, + "normalized_sha256": "664912aede956fbb3bc49b4517a90d315d445777054a1823c0eafadd2a1f2bc0" + }, + "base": { + "size": 1104, + "relocation_count": 29, + "normalized_sha256": "55ccd1c4c2cab82eb867a354392b848b9a1851bc29a4c0dab3f4e64aadaece83" + }, + "objdiff_percent": 95.24156 + } + }, + { + "unit": "source/interface/hud_draw", + "function": "_get_flash_color", + "class": "unclassified", + "evidence": "Best complete house-rule-reviewed HUD flash reconstruction. It uses named flash flags, project real/color types, natural fmod/cos/square-root interpolation, and no source-pressure steering. Candidate and target both occupy 608 padded bytes with all 32 relocation identities and score 99.944725% objdiff. The genuine call also causes the January-inline square_root body to appear as a target-absent candidate section; that helper receives no exact credit and keeps the whole object from object-exact status. Reopen with authoritative January inline provenance or a natural same-compiler donor explaining the residual/helper schedule.", + "measurements": { + "target": { + "size": 608, + "relocation_count": 32, + "normalized_sha256": "0717c2b85934e185e6cfb38a82a90f3160bc2e1b3372c2eb80d27af1f4f1818b" + }, + "base": { + "size": 608, + "relocation_count": 32, + "normalized_sha256": "35adc125fc6d7a1f0bc5a94ba0ee65fc6cad065651ef7c216c9a747a6732452b" + }, + "objdiff_percent": 99.944725 + } + }, + { + "unit": "source/interface/hud_draw", + "function": "_hud_draw_bitmap_direct", + "class": "unclassified", + "evidence": "Best complete readable bitmap-direct reconstruction retained at zero exact credit. It follows the owning HUD interface, uses project geometry/bitmap types and the shared draw path, and has no artificial compiler controls. The candidate has all 15 target relocation identities but is 336 versus 320 padded bytes; the remaining difference is not assigned a stronger cause without evidence. Reopen only with authoritative January source or a natural same-compiler donor.", + "measurements": { + "target": { + "size": 320, + "relocation_count": 15, + "normalized_sha256": "6ec7215284a2c2248b4524f4229982fa6855489a4033c233b7c9ba5505a9a8c8" + }, + "base": { + "size": 336, + "relocation_count": 15, + "normalized_sha256": "e13047e3464fe9dc0fc3e4382ddacf2415f83322c0edc8c06750a348eb0597a9" + }, + "objdiff_percent": 93.88 + } + }, + { + "unit": "source/main/main", + "function": "_main_game_render", + "class": "unclassified", + "evidence": "Best complete natural render-dispatch reconstruction retained after removing the inadmissible _ReadWriteBarrier experiment. It is readable ordinary C with authentic subsystem calls and no barrier, volatile, pragma, assembly, or fake dependency. The candidate is 464 versus 448 target padded bytes with 29 versus 28 relocations and scores 94.666664% objdiff. This is zero-credit fuzzy source. Reopen only with authoritative January source or a natural same-compiler donor explaining the extra call/relocation and schedule. See docs/object_matching_logs/main_obj_natural_state_and_render_packet_20260909.md.", + "measurements": { + "target": { + "size": 448, + "relocation_count": 28, + "normalized_sha256": "049052197c4b50b38b734ed982558cd27d4c999e6ee05897e02c99804c696a2b" + }, + "base": { + "size": 464, + "relocation_count": 29, + "normalized_sha256": "311d0f3787d9da53e6141d95ea4a0f60d8c0efd8bc82b65d8ee1b16b7f5a1952" + }, + "objdiff_percent": 94.666664 + } + }, + { + "unit": "source/main/main", + "function": "_set_window_camera_values", + "class": "unclassified", + "evidence": "Best complete ordinary-C window/camera state reconstruction. It uses named engine globals and project types, keeps its prototype with the Main owner, and contains no compiler-steering constructs. Candidate and target retain all 19 relocation identities but differ by one 16-byte padded slot (416 candidate versus 432 target), scoring 88.32374% objdiff. This is zero-credit fuzzy source. Reopen only with authoritative January source or a natural same-compiler donor. See docs/object_matching_logs/main_obj_natural_state_and_render_packet_20260909.md.", + "measurements": { + "target": { + "size": 432, + "relocation_count": 19, + "normalized_sha256": "b6b77e1325d664686277c8ab3678b163f92e428ce003a51526ef628c20a2e771" + }, + "base": { + "size": 416, + "relocation_count": 19, + "normalized_sha256": "9b3998256a96c5a2a09fe7c4c99b586627059b2c97d1628cfcf088ccb7b33eff" + }, + "objdiff_percent": 88.32374 + } + }, + { + "unit": "source/render/render_cameras", + "function": "_render_frustum_cube_view_fraction", + "class": "unclassified", + "evidence": "Best complete ordinary-C frustum projection reconstruction. It uses the render-camera owner header, named project math types/constants, and direct geometric operations without compiler steering. Candidate and target share the 1,200-byte padded envelope and all 78 relocation identities, scoring 96.128494% objdiff. This is zero-credit fuzzy source. Reopen only with authoritative January source or a natural same-compiler donor that explains the residual schedule.", + "measurements": { + "target": { + "size": 1200, + "relocation_count": 78, + "normalized_sha256": "bcc496e4a2fc55aa9fc78d14a886b2c29828c43c27a40aea5949da21f19543be" + }, + "base": { + "size": 1200, + "relocation_count": 78, + "normalized_sha256": "b3c2fd0e3984e2cdf2beeb4c3c3886e228cb40e87a0bd85f9b0e24736b2789d7" + }, + "objdiff_percent": 96.128494 + } + }, + { + "unit": "source/render/render_cameras", + "function": "_render_camera_build_frustum_bounds", + "class": "unclassified", + "evidence": "Best complete ordinary-C full frustum-bounds reconstruction. It uses the render-camera owner header and named project math types, with no source-pressure steering. Candidate and target share the 288-byte padded envelope and all nine relocation identities, scoring 99.41237% objdiff; the residual is an unresolved natural instruction-schedule difference. The clipped-frustum companion is strict exact at 352 padded bytes and is therefore not parked. The same TU's exact front-facing reconstruction naturally emits cross/vector helper sections absent from the target object; those helpers receive no credit and the whole object remains rejected for object-exact accounting. Reopen only with authoritative January inline provenance or a natural same-compiler donor.", + "measurements": { + "target": { + "size": 288, + "relocation_count": 9, + "normalized_sha256": "99e8fde76b974d967c9e83ee16177ec0eae87683db8631e38c5517d886c246e2" + }, + "base": { + "size": 288, + "relocation_count": 9, + "normalized_sha256": "752b1da78d5e94de589db2db1e9ae2e40b6d624200f87339d87b300130137349" + }, + "objdiff_percent": 99.41237 + } + }, + { + "unit": "source/interface/interface", + "function": "_interface_get_weapon_hud_index", + "class": "unclassified", + "evidence": "Best complete typed ordinary-C weapon-HUD resolver reconstructed from the HCEA semantic donor and January disassembly/relocation authority. It owns its public prototype in interface.h, uses player_get, unit_get, weapon_get and typed definition accessors, preserves the complete gunner-seat/default-HUD/flashlight behavior, and contains no representation punning, assembly or source-pressure steering. Target and candidate share the 448-byte padded envelope and all 20 relocation identities, scoring 94.96429% objdiff. January performs the final real out-parameter copies through integer registers while natural VC7 C uses x87 loads/stores; reproducing that choice through a wrong ABI, memcpy facade, volatile access or inline assembly is rejected. This is zero-credit fuzzy source. Reopen only with authoritative January source or independently attested source that naturally selects the integer copy. See docs/object_matching_logs/interface_obj_weapon_hud_best_fuzzy_20260912.md.", + "measurements": { + "target": { + "size": 448, + "relocation_count": 20, + "normalized_sha256": "0673f867b219cdbd399685310736ae3de8263479cb2104edd5c56f48930c6549" + }, + "base": { + "size": 448, + "relocation_count": 20, + "normalized_sha256": "57151d28c46aeee1fc2bb715ebde2fbc994b52447a4ba273d624aa56e7749e72" + }, + "objdiff_percent": 94.96429 + } + }, + { + "unit": "source/units/bipeds", + "function": "_biped_check_discard", + "class": "instruction-scheduling", + "evidence": "Best complete typed ordinary-C discard check retained from January control flow, relocation topology, diagnostic strings and HCEA semantic corroboration. Target and candidate share the 176-byte padded envelope and all 10 relocation identities; the remaining normalized-code difference is the formatted diagnostic argument evaluation order. No barrier, volatile/register forcing, pragma, assembly, fake dependency or nonsensical branch is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural same-compiler donor explaining that evaluation order. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", + "measurements": { + "target": { + "size": 176, + "relocation_count": 10, + "normalized_sha256": "5f7c9efc91592d80df55608dcd341f924ee6367af8b3e572a43212cfc6633250" + }, + "base": { + "size": 176, + "relocation_count": 10, + "normalized_sha256": "096f8aa672e716e4677d36521767e8dd355325c553bb753fed2d1341e9200103" + }, + "objdiff_percent": 65.9434 + } + }, + { + "unit": "source/units/bipeds", + "function": "_biped_fix_position", + "class": "instruction-scheduling", + "evidence": "Best complete typed ordinary-C position correction retained from January disassembly/relocations and HCEA semantic corroboration. Target and candidate share the 1,232-byte padded envelope and all 47 relocation identities; the remaining difference is a narrow-load/sign-extension schedule. The discarded pill-base result remains a genuine real_point3d and is not representation-punned to steer code generation. No barrier, volatile/register forcing, pragma, assembly, fake dependency or nonsensical logic is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural same-compiler donor explaining the remaining schedule. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", + "measurements": { + "target": { + "size": 1232, + "relocation_count": 47, + "normalized_sha256": "8932705148ab7aa1b778d8854ae1ae69e3eee275111372c0df4594eeab02ab33" + }, + "base": { + "size": 1232, + "relocation_count": 47, + "normalized_sha256": "efc83d31ae1a834a59c4d9e8e134c4acebdfb7b753ca5b30ec61f7ff4eac3b1b" + }, + "objdiff_percent": 95.90932 + } + }, + { + "unit": "source/units/bipeds", + "function": "_biped_update_turning", + "class": "unclassified", + "evidence": "Best complete typed ordinary-C turning update reconstructed from January target authority and donor-corroborated gameplay semantics. Target and candidate share the 1,712-byte padded envelope and all 49 relocation identities, scoring 95.2675% objdiff. The retained natural two-dimensional math path emits documented helper COMDAT debt, which receives no credit; no fake dependency, forced inline, barrier, volatile/register steering, pragma, assembly or nonsensical logic is present. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural compatible-compiler donor explaining the normalized-code residual. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", + "measurements": { + "target": { + "size": 1712, + "relocation_count": 49, + "normalized_sha256": "9e0e4d2d24a79a17e7407f07fe39e397f22ac43bf1553349b39ccf70675f8102" + }, + "base": { + "size": 1712, + "relocation_count": 49, + "normalized_sha256": "5ab254c5d0965680ebcc89ce3847468061469f642646d6332e7e6140159a9c48" + }, + "objdiff_percent": 95.2675 + } + }, + { + "unit": "source/units/bipeds", + "function": "_biped_update", + "class": "unclassified", + "evidence": "Best complete typed natural-C Biped update body retained from January control-flow/relocation authority and HCEA semantic corroboration. January occupies 1,120 padded bytes with 61 relocations; the candidate occupies 1,104 padded bytes with the same 61 relocation identities and scores 93.09064% objdiff. The broad residual is not assigned a speculative compiler mechanism. No fake dependency, barrier, volatile/register steering, pragma, assembly or nonsensical branch is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural compatible-compiler donor. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", + "measurements": { + "target": { + "size": 1120, + "relocation_count": 61, + "normalized_sha256": "fe3b81e079c33a132e6aa3403c6148ecbba692047ca2368e46dabe1884348f7e" + }, + "base": { + "size": 1104, + "relocation_count": 61, + "normalized_sha256": "95e78b0eb93d7d95433b3f66c4cda1e70557d9b7f6b50a9efa48dc69e56e27fd" + }, + "objdiff_percent": 93.09064 + } + }, { "unit": "source/models/model_animations", "function": "_animation_get_keyframe_rotation", @@ -1490,44 +1737,6 @@ "objdiff_percent": 98.809525 } }, - { - "unit": "source/bungie_net/common/key_agreement", - "function": "_key_agreement_get_packet_type", - "class": "instruction-scheduling", - "evidence": "Fuzzy-parked after a bounded ordinary-C type and expression audit. The retained private helper has the exact four relocation identities and the authentic January assertion/file strings; it reads the key-agreement packet type from the final encoded message byte after requiring `_message_type_packet`. The compiler strength-reduces `GET_MESSAGE_TYPE` to `and al, 0x0C; cmp al, 0x0C`, while January emits `shr al, 2; and al, 3; cmp al, 3`, leaving a 64-byte candidate against the 80-byte target. `key_agreement_get_packet_type` is a descriptive inferred private name: Stian/Pastudan's research-only `key_agreement_peek_packet_type` refers instead to the separate public `_is_message_encryption_key_message` predicate and was deliberately not misapplied here. No volatile/register forcing, barrier, pragma, assembly, or artificial dependency was admitted.", - "measurements": { - "target": { - "size": 80, - "relocation_count": 4, - "normalized_sha256": "17992eecb37a652d03f0088ce5123c158674116e7b2c88fe56083c3627f03278" - }, - "base": { - "size": 64, - "relocation_count": 4, - "normalized_sha256": "c85c2f19f63571e94b0334e0868eb18f2f2ec585b54cdf229b114e38c0c79eb3" - }, - "objdiff_percent": 95.75 - } - }, - { - "unit": "source/bungie_net/common/key_agreement", - "function": "_initiate_key_exchange", - "class": "register-allocation", - "evidence": "Fuzzy-parked with the simplest coherent Diffie-Hellman initiation flow: generate parameters and public key, build into the same-TU 0x200-byte buffer, byte-swap the header, write the complete message, and return the write result. Target and retained source have equal 128-byte padded size and the same six relocation identities. January preserves a preinitialized byte-sized TRUE result and a larger local frame; retaining `boolean result = TRUE; return result;` solely to influence code generation lacked independent source provenance and was rejected under the source-credibility rule. Direct `return TRUE`/`FALSE` is retained even though it scores lower. No exact candidate was found or rejected, and no compiler-steering construct is present.", - "measurements": { - "target": { - "size": 128, - "relocation_count": 6, - "normalized_sha256": "dd522968df4407b121545dc0bc712ea05f9e82a71fb21fcb1c46474c55defb9d" - }, - "base": { - "size": 128, - "relocation_count": 6, - "normalized_sha256": "193bbbb3b54c0db91978f2077f02bde2b3be2f86ebdb0cdef3489da18be109fc" - }, - "objdiff_percent": 69.57895 - } - }, { "unit": "source/bungie_net/common/key_agreement", "function": "_complete_key_exchange", @@ -1542,9 +1751,9 @@ "base": { "size": 416, "relocation_count": 17, - "normalized_sha256": "b1abf287400c1b3843364d05327512ff963fce5fb159f46f73100852027a411a" + "normalized_sha256": "fb0142cb223b631a4753a951718e0cfb7b4946eeafe6bdc99913cb95431a9d97" }, - "objdiff_percent": 84.85987 + "objdiff_percent": 97.42038 } }, { @@ -2710,7 +2919,7 @@ "unit": "source/units/bipeds", "function": "_biped_find_nearby_support_surface", "class": "unclassified", - "evidence": "Fuzzy-parked under the current house rule after restoring the complete typed support-surface search from January disassembly and relocation topology, the later HCEA semantic implementation, and the earlier e02971dfd campaign reconstruction. The same-build Halo CE symbol atlas independently recovers the private name biped_find_nearby_support_surface. The retained ordinary C obtains the biped physics pill, brackets the collision query with the semantic collision-user stack, tests the structure BSP with the breakable-surface flags, corrects back-facing planes through named fields and LONG_MAX, selects the closest support plane, and updates the biped's support surface, ground plane, and up vector. Target and candidate have the same 544-byte padded envelope, all 23 relocation identities, and 175 decoded instructions; the report records 99.976746 percent similarity. Their sole machine-code difference is the order of the first two independent x87 multiply/add terms inside the inlined plane3d_distance_to_point expression. The source-authentic project helper is retained rather than expanding and reordering its dot-product terms solely to steer code generation. The fail-closed classifier returns UNKNOWN. No forced inline/noinline, barrier, volatile access, pragma, assembly, fake dependency, raw offset, undefined behavior, manual bitwise plane mask, or nonsensical branch is retained. Reopen only for authoritative January source/local records, a proven January inline-schedule correction, or a natural compatible-compiler donor that explains the x87 evaluation order.", + "evidence": "Fuzzy-parked under the current house rule after restoring the complete typed support-surface search from January disassembly and relocation topology, the later HCEA semantic implementation, and the earlier e02971dfd campaign reconstruction. The same-build Halo CE symbol atlas independently recovers the private name biped_find_nearby_support_surface. The retained ordinary C obtains the biped physics pill, brackets the collision query with the semantic collision-user stack, tests the structure BSP with the breakable-surface flags, corrects back-facing planes through named fields and LONG_MAX, selects the closest support plane, and updates the biped's support surface, ground plane, and up vector. Target and candidate have the same 544-byte padded envelope, all 23 relocation identities, and 175 decoded instructions; the current report records 99.98837 percent similarity. Their sole machine-code difference is the order of the first two independent x87 multiply/add terms inside the inlined plane3d_distance_to_point expression. The source-authentic project helper is retained rather than expanding and reordering its dot-product terms solely to steer code generation. No forced inline/noinline, barrier, volatile access, pragma, assembly, fake dependency, raw offset, undefined behavior, manual bitwise plane mask, or nonsensical branch is retained. This is zero-credit fuzzy source. Reopen only for authoritative January source/local records, a proven January inline-schedule correction, or a natural compatible-compiler donor that explains the x87 evaluation order. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", "measurements": { "target": { "size": 544, @@ -2720,9 +2929,9 @@ "base": { "size": 544, "relocation_count": 23, - "normalized_sha256": "7264f6ba522f1b4dab0f59e9a1e725bfed9363c869e1727e0b69f94b1e1212b0" + "normalized_sha256": "a0ef780b38e8c2aa3674296ff845cfc30ae8c759adfd18d2ed816b4bc3536c02" }, - "objdiff_percent": 99.976746 + "objdiff_percent": 99.98837 } }, { @@ -3195,9 +3404,9 @@ "base": { "size": 672, "relocation_count": 43, - "normalized_sha256": "7db4861a5adf519e0457a0ffe785ff4d47e878fda47cc0a06206a529776ac91c" + "normalized_sha256": "885fdb81966284acf5ba7138a4bbbd2221e7c61c89114fdd6f53b96ba89b81a0" }, - "objdiff_percent": 97.76382 + "objdiff_percent": 97.67839 } }, { @@ -5066,7 +5275,7 @@ "unit": "source/objects/damage", "function": "_object_cause_damage", "class": "unclassified", - "evidence": "Fuzzy-parked under the current house rule after reconstructing the complete master object-damage dispatcher in ordinary typed C and naturally activating its private player-owner helper as a strict exact same-TU function. January COFF remains the sole byte, ABI, control-flow, constant, and relocation authority; independent HCEA source and Stian's PC lift corroborate the parent walk, damage randomization and modifiers, vehicle-child recursion, player feedback, collision material selection, shield/body dispatch, material-effect reporting, aftermath, and deletion behavior. This refreshed context writes object_damage_body as a credible typed fuzzy body and adds the independently authenticated same-TU private helpers damage_effect_new_at_location and object_destroy_region as strict exact functions. Those legitimate additions change this candidate's normalized hash but not its envelope or similarity: the target occupies 1984 padded / 1971 meaningful bytes; the candidate occupies 1952 padded / 1950 meaningful bytes; both carry the same 75 ordered relocation identities; objdiff remains 88.5429 percent. object_damage_shield and object_damage_aftermath remain unwritten and preserve a material private-call-boundary context gap. Reopen when those authentic callee bodies are reconstructed together, or when authoritative January source/local records explain the remaining register allocation, stack frame, and x87 schedule. No raw offset, false type, representation pun, volatile/register forcing, barrier, pragma, assembly, fake dependency, forced inline/noinline, undefined behavior, nonsensical branch, or fake context is retained.", + "evidence": "Fuzzy-parked under the current house rule after reconstructing the complete master object-damage dispatcher in ordinary typed C. January COFF remains the sole byte, ABI, control-flow, constant and relocation authority; independent HCEA source and Stian's PC lift corroborate the parent walk, damage randomization and modifiers, vehicle-child recursion, player feedback, collision material selection, shield/body dispatch, material-effect reporting, aftermath and deletion behavior. The current same-TU context includes strict-exact object_damage_shield, object_damage_aftermath, damage_effect_new_at_location and object_destroy_region bodies, plus the separately parked typed object_damage_body residual. The target occupies 1,984 padded / 1,971 meaningful bytes; the candidate occupies 1,968 padded bytes, carries the same 75 relocation identities and scores 95.71607% objdiff. The remaining broad register, frame and x87 schedule differences have no proved source-level mechanism. No raw offset, false type, representation pun, volatile/register forcing, barrier, pragma, assembly, fake dependency, forced inline/noinline, undefined behavior, nonsensical branch or fake context is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural same-compiler donor explaining the residual.", "measurements": { "target": { "size": 1984, @@ -5074,11 +5283,11 @@ "normalized_sha256": "68f55b08e71a95b90b258c6d1a1d50f7a637eca0af4fd3ea7388c0f7884015c0" }, "base": { - "size": 1952, + "size": 1968, "relocation_count": 75, - "normalized_sha256": "cfd8204015222ec9bd4dc25d3d1d245a4bcb17b29f273c39fb79dca037f9aa3b" + "normalized_sha256": "899b76c53282a5218dee64258c603b9fbaf8b11a078e3c0ab97d2638d9f713cd" }, - "objdiff_percent": 88.5429 + "objdiff_percent": 95.71607 } }, { @@ -6069,22 +6278,22 @@ "unit": "source/units/bipeds", "function": "_biped_get_sight_position", "class": "unclassified", - "evidence": "Best complete typed sight-position reconstruction retained using biped/tag access macros, project real types, named estimate modes, and existing vector helpers. January is 416 padded bytes and canonical is 400; both have 19 relocations. The residual has no proved compiler mechanism and receives zero exact credit. The unrooted private biped update cluster is separately excluded from production credit. See docs/object_matching_logs/claude_fable_opus_reconciliation_checkpoint_20260908.md.", + "evidence": "Best complete typed sight-position reconstruction retained using biped/tag access macros, project real types, named estimate modes, and existing vector helpers. Target and candidate now share the 416-byte padded envelope and all 19 relocation identities, scoring 97.74286% objdiff; the remaining difference is an x87/local-spill choice with no proved source-level compiler mechanism. No fake dependency, barrier, volatile/register steering, pragma, assembly or nonsensical logic is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural compatible-compiler donor explaining the spill schedule. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", "measurements": { "target": { "size": 416, "relocation_count": 19, "normalized_sha256": "59e27e5e5b6f7a7ca3ec56c3ff8f347e70de4848ad52d8c8dfe257e7ef3dffc8" }, - "base": { "size": 400, "relocation_count": 19, "normalized_sha256": "be9c30a4eee928c3876b49286bdf08856672e9b91fbf5c3d88def1451d3093dc" }, - "objdiff_percent": 92.6 + "base": { "size": 416, "relocation_count": 19, "normalized_sha256": "46b593827ea2de5386183bd8c60fdaab2c986990dbde4e7ec9e6fa33edc9e951" }, + "objdiff_percent": 97.74286 } }, { "unit": "source/units/bipeds", "function": "_biped_get_autoaim_pill", "class": "instruction-scheduling", - "evidence": "Best complete typed autoaim-pill reconstruction retained at 336 padded bytes and nine relocations on both sides. It uses biped definition accessors, named flags, project real types, and genuine vector helpers; the remaining normalized-code difference is scheduling. No raw casts, fake dependency, or forced helper emission is retained. Zero exact credit. See docs/object_matching_logs/claude_fable_opus_reconciliation_checkpoint_20260908.md.", + "evidence": "Best complete typed autoaim-pill reconstruction retained at 336 padded bytes and all nine relocation identities on both sides. It uses biped definition accessors, named flags, project real types and genuine vector helpers; the remaining spherical-branch difference is a pointer/value register schedule, with 98.08264% objdiff. No raw casts, fake dependency, barrier, volatile/register steering, pragma, assembly or nonsensical logic is retained. This is zero-credit fuzzy source. Reopen only with authoritative January source/local records or a natural compatible-compiler donor explaining the register schedule. See docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md.", "measurements": { "target": { "size": 336, "relocation_count": 9, "normalized_sha256": "c232d22b9b72f5355e3496cafe1fbfb7985a69515dfa2d0df1f2f9052a116600" }, - "base": { "size": 336, "relocation_count": 9, "normalized_sha256": "4568d5e227f2d6b948161aacf012add8dc170a551e3ef166146af82a6b90a7e7" }, - "objdiff_percent": 98.04132 + "base": { "size": 336, "relocation_count": 9, "normalized_sha256": "6b5e592092e374eea419480375447ba0dc54ddf8f01b5c37f07a5a18b9eb99f2" }, + "objdiff_percent": 98.08264 } }, { @@ -6443,11 +6652,11 @@ "unit": "source/rasterizer/xbox/rasterizer_xbox_models", "function": "__rasterizer_model_draw", "class": "tu-context-optimization", - "evidence": "Best complete typed model draw reconstruction retained from the Opus rasterizer lane after house-rule reconciliation. January and the candidate are both 5,168 padded bytes; January has 350 relocations and the candidate has 348 at 95.331955 percent similarity. The established REAL_MATH_EXTERNAL_POINT_FROM_LINE3D include schedule remains in force and this TU emits no point_from_line3d COMDAT. The shader-plasma access now uses its authentic enclosing definition instead of a raw byte offset, texture-animation outputs use true real_vector4d storage, self-illumination colors are interpolated component by component without incompatible point/vector/color casts, and the atmosphere-dominant test uses the named flag. These defined semantic choices account for part of the measured residual and are retained rather than restoring the closer prohibited representation-cast form. The correctly owned environment-fog backend declaration changes only compiler scheduling in this already-fuzzy owner and adds no exact credit. Semantic model-effect enums and render_lighting fields replace misleading donor placeholders. No forced inline, fake dependency, raw offset, representation pun, undefined behavior, or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/rasterizer_xbox_models_obj_opus_reconciliation_20260909.md.", + "evidence": "Best complete typed model draw reconstruction retained from the Opus rasterizer lane after house-rule reconciliation. January and the candidate are both 5,168 padded bytes; January has 350 relocations and the candidate has 348 at 95.32371 percent similarity. The established REAL_MATH_EXTERNAL_POINT_FROM_LINE3D include schedule remains in force and this TU emits no point_from_line3d COMDAT. The shader-plasma access now uses its authentic enclosing definition instead of a raw byte offset, texture-animation outputs use true real_vector4d storage, self-illumination colors are interpolated component by component without incompatible point/vector/color casts, and the atmosphere-dominant test uses the named flag. These defined semantic choices account for part of the measured residual and are retained rather than restoring the closer prohibited representation-cast form. The correctly owned environment-fog backend declaration changes only compiler scheduling in this already-fuzzy owner and adds no exact credit. Semantic model-effect enums and render_lighting fields replace misleading donor placeholders. No forced inline, fake dependency, raw offset, representation pun, undefined behavior, or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/rasterizer_xbox_models_obj_opus_reconciliation_20260909.md.", "measurements": { "target": { "size": 5168, "relocation_count": 350, "normalized_sha256": "99d8b95f1ce011781cedc3a123887065a0f8af4a54c842194fcd90c22fa2cc4e" }, - "base": { "size": 5168, "relocation_count": 348, "normalized_sha256": "112b4321bee9992ef063e93af69c91faba64a7ecf830fa5b7ff4e657832c26ee" }, - "objdiff_percent": 95.331955 + "base": { "size": 5168, "relocation_count": 348, "normalized_sha256": "23ca80471861b904938ae52617a1931ea48279e746341e540d1874a7701dbd35" }, + "objdiff_percent": 95.32371 } }, { @@ -6912,7 +7121,7 @@ "unit": "source/interface/hud_draw", "function": "_hud_draw_bitmap_with_meter", "class": "unclassified", - "evidence": "Best reviewed house-rule-compliant natural reconstruction retained from the 2026-09-09 reconciliation. January directly proves the apparently nonsensical animation operation: fld [definition+4] followed by a relocation-backed fmul of __real@00000000. Independent HCEA and Halo CE PC reconstructions preserve the same literal-zero multiply, while Marathon has no corresponding planar-fog animation implementation and January's stripped structures.obj PDB module contains no local-variable records from which an original phase name could be recovered. The source therefore labels this target-proven disabled feature as an original bug instead of inventing a time-varying expression. Current canonical target/base COFF measurements remain non-identical; the candidate is 32 padded bytes short and lacks one January tag-block lookup. Objdiff similarity is diagnostic only. This is a fuzzy research waypoint with zero exact-function and zero exact-byte credit. See docs/object_matching_logs/lane_reconciliation_batch5_fuzzy_ui_world_20260909.md.", + "evidence": "Best reviewed house-rule-compliant natural reconstruction retained from the 2026-09-09 reconciliation. January directly proves the apparently nonsensical animation operation: fld [definition+4] followed by a relocation-backed fmul of __real@00000000. Independent HCEA and Halo CE PC reconstructions preserve the same literal-zero multiply, while Marathon has no corresponding planar-fog animation implementation and January's stripped structures.obj PDB module contains no local-variable records from which an original phase name could be recovered. The source therefore labels this target-proven disabled feature as an original bug instead of inventing a time-varying expression. Current canonical target/base COFF measurements remain non-identical; the candidate is 16 padded bytes longer with the same relocation count, and the residual remains an instruction-selection/scheduling difference. Objdiff similarity is diagnostic only. This is a fuzzy research waypoint with zero exact-function and zero exact-byte credit. See docs/object_matching_logs/lane_reconciliation_batch5_fuzzy_ui_world_20260909.md.", "measurements": { "target": { "size": 384, @@ -6922,9 +7131,9 @@ "base": { "size": 400, "relocation_count": 17, - "normalized_sha256": "3cc01a61674255de737e68476c3052952b668dafb47a0c135c1ecc92a4c54c8b" + "normalized_sha256": "db685d4d000e7a31c14cacedc43115c1c7a8f8643a25880ef470cc16433dd942" }, - "objdiff_percent": 92.69841 + "objdiff_percent": 93.35714 } }, { @@ -7511,6 +7720,17 @@ "base": { "size": 784, "relocation_count": 18, "normalized_sha256": "a72cd2b9285abc4fe3ed3a8f4de6c0606fe474c9e01a1049e16b3285f1497f01" }, "objdiff_percent": 81.60833 } + }, + { + "unit": "source/ai/ai_script", + "function": "_ai_scripting_teleport_starting_location_private", + "class": "instruction-scheduling", + "evidence": "Complete typed HCEA-backed teleport helper retained under its authenticated semantic private name. Target and candidate both occupy 288 padded bytes and have the same 13 relocation destinations; the remaining one-byte schedule displacement begins after the January biped_approximate_surface_index call. Widening that shared function's return type can close this helper but perturbs accepted consumers, so that ABI change is rejected. This natural body receives zero credit. See docs/object_matching_logs/ai_flee_teleport_model_node_packet_20260912.md.", + "measurements": { + "target": { "size": 288, "relocation_count": 13, "normalized_sha256": "755b7a6e2990f977b15fb61eb09a0106990c68fcd7d69128922e69cc12e52c5e" }, + "base": { "size": 288, "relocation_count": 13, "normalized_sha256": "9c0415c2243227d13b1b96ca944e476f75acf40417113dab2fc3edcaa501b22e" }, + "objdiff_percent": 99.9375 + } } ] } diff --git a/config/semantic_data_matches.json b/config/semantic_data_matches.json index b765f5fd..0057e848 100644 --- a/config/semantic_data_matches.json +++ b/config/semantic_data_matches.json @@ -356,7 +356,7 @@ }, { "symbol": "_rdata_002a0a88", - "base_symbol": "$T18274", + "base_symbol": "$T18284", "source_function": "_main", "measurements": { "target": { diff --git a/config/symbols.json b/config/symbols.json index bcf1ea6e..9f8449e1 100644 --- a/config/symbols.json +++ b/config/symbols.json @@ -741,7 +741,7 @@ { "file_offset": 282768, "flags": 32, "name": "_ai_scripting_braindead_by_unit" }, { "file_offset": 283024, "flags": 32, "name": "_ai_scripting_ignore" }, { "file_offset": 283184, "flags": 32, "name": "_ai_scripting_prefer_target" }, -{ "file_offset": 283344, "flags": 32, "name": "_code_000452d0" }, +{ "file_offset": 283344, "flags": 32, "name": "_ai_scripting_teleport_starting_location_private", "static": true }, { "file_offset": 283632, "flags": 32, "name": "_ai_scripting_teleport_starting_location_if_unsupported" }, { "file_offset": 283728, "flags": 32, "name": "_ai_scripting_teleport_starting_location" }, { "file_offset": 283824, "flags": 32, "name": "_ai_scripting_try_to_fight_nothing" }, @@ -4181,7 +4181,7 @@ { "file_offset": 990096, "flags": 32, "name": "_main_present_frame" }, { "file_offset": 990240, "flags": 32, "name": "_main_setup_connection" }, { "file_offset": 990432, "flags": 32, "name": "_main_initialize_time" }, -{ "file_offset": 990512, "flags": 32, "name": "_screenshot_render" }, +{ "file_offset": 990512, "flags": 32, "name": "_screenshot_render", "static": true }, { "file_offset": 991008, "flags": 32, "name": "_main_framerate_render" }, { "file_offset": 991680, "flags": 32, "name": "_halt_and_catch_fire" }, { "file_offset": 992352, "flags": 32, "name": "_main_loop_of_death" }, @@ -5116,8 +5116,8 @@ { "file_offset": 1204944, "flags": 32, "name": "_object_get_actual_body_vitality" }, { "file_offset": 1205040, "flags": 32, "name": "_object_get_actual_shield_vitality" }, { "file_offset": 1205136, "flags": 32, "name": "_object_deplete_shield" }, -{ "file_offset": 1205264, "flags": 32, "name": "_object_damage_shield" }, -{ "file_offset": 1206160, "flags": 32, "name": "_object_damage_aftermath" }, +{ "file_offset": 1205264, "flags": 32, "name": "_object_damage_shield", "static": true }, +{ "file_offset": 1206160, "flags": 32, "name": "_object_damage_aftermath", "static": true }, { "file_offset": 1206720, "flags": 32, "name": "_damage_effect_new_at_location", "static": true }, { "file_offset": 1207232, "flags": 32, "name": "_render_debug_object_damage" }, { "file_offset": 1207696, "flags": 32, "name": "_object_deplete_body" }, @@ -6782,11 +6782,11 @@ { "file_offset": 1638880, "flags": 32, "name": "_biped_start_limp_body_physics" }, { "file_offset": 1639008, "flags": 32, "name": "_biped_stop_limp_body_physics" }, { "file_offset": 1639088, "flags": 32, "name": "_code_001902b0" }, -{ "file_offset": 1639328, "flags": 32, "name": "_code_001903a0" }, +{ "file_offset": 1639328, "flags": 32, "name": "_biped_check_discard", "static": true }, { "file_offset": 1639504, "flags": 32, "name": "_code_00190450" }, { "file_offset": 1639968, "flags": 32, "name": "_biped_flying_through_air" }, { "file_offset": 1640048, "flags": 32, "name": "_code_00190670" }, -{ "file_offset": 1640320, "flags": 32, "name": "_code_00190780" }, +{ "file_offset": 1640320, "flags": 32, "name": "_biped_make_footstep", "static": true }, { "file_offset": 1640592, "flags": 32, "name": "_biped_adjust_placement" }, { "file_offset": 1640688, "flags": 32, "name": "_biped_export_function_values" }, { "file_offset": 1640880, "flags": 32, "name": "_biped_get_sight_position" }, @@ -6797,16 +6797,16 @@ { "file_offset": 1643520, "flags": 32, "name": "_biped_approximate_surface_index" }, { "file_offset": 1643568, "flags": 32, "name": "_biped_find_pathfinding_surface_index" }, { "file_offset": 1644016, "flags": 32, "name": "_biped_exit_seat_end" }, -{ "file_offset": 1644256, "flags": 32, "name": "_code_001916e0" }, -{ "file_offset": 1644576, "flags": 32, "name": "_code_00191820" }, -{ "file_offset": 1645008, "flags": 32, "name": "_code_001919d0" }, -{ "file_offset": 1645312, "flags": 32, "name": "_code_00191b00" }, -{ "file_offset": 1645744, "flags": 32, "name": "_code_00191cb0" }, +{ "file_offset": 1644256, "flags": 32, "name": "_biped_falling_danger" }, +{ "file_offset": 1644576, "flags": 32, "name": "_biped_vehicle_speech", "static": true }, +{ "file_offset": 1645008, "flags": 32, "name": "_biped_apply_rotation", "static": true }, +{ "file_offset": 1645312, "flags": 32, "name": "_biped_jump", "static": true }, +{ "file_offset": 1645744, "flags": 32, "name": "_biped_try_to_make_footsteps", "static": true }, { "file_offset": 1646160, "flags": 32, "name": "_biped_find_nearby_support_surface", "static": true }, { "file_offset": 1646704, "flags": 32, "name": "_biped_verify_object_vectors", "static": true }, -{ "file_offset": 1646960, "flags": 32, "name": "_code_00192170" }, -{ "file_offset": 1647312, "flags": 32, "name": "_code_001922d0" }, -{ "file_offset": 1647488, "flags": 32, "name": "_code_00192380" }, +{ "file_offset": 1646960, "flags": 32, "name": "_biped_update_airborne" }, +{ "file_offset": 1647312, "flags": 32, "name": "_biped_update_landing", "static": true }, +{ "file_offset": 1647488, "flags": 32, "name": "_biped_update_slipping", "static": true }, { "file_offset": 1647616, "flags": 32, "name": "_code_00192400" }, { "file_offset": 1648128, "flags": 32, "name": "_biped_build_flying_axes" }, { "file_offset": 1648560, "flags": 32, "name": "_code_001927b0" }, @@ -6814,9 +6814,9 @@ { "file_offset": 1655296, "flags": 32, "name": "_biped_new" }, { "file_offset": 1655488, "flags": 32, "name": "_biped_preprocess_node_orientations" }, { "file_offset": 1655520, "flags": 32, "name": "_biped_accelerate" }, -{ "file_offset": 1656000, "flags": 32, "name": "_code_001944c0" }, -{ "file_offset": 1657712, "flags": 32, "name": "_code_00194b70" }, -{ "file_offset": 1661792, "flags": 32, "name": "_code_00195b60" }, +{ "file_offset": 1656000, "flags": 32, "name": "_biped_update_turning", "static": true }, +{ "file_offset": 1657712, "flags": 32, "name": "_biped_update_moving" }, +{ "file_offset": 1661792, "flags": 32, "name": "_biped_update_dead", "static": true }, { "file_offset": 1662000, "flags": 32, "name": "_biped_update" }, { "file_offset": 1663120, "flags": 32, "name": "_dialogue_get_vocalization_name" }, { "file_offset": 1663168, "flags": 32, "name": "_dialogue_get_vocalization_type_by_name" }, @@ -20385,7 +20385,7 @@ { "file_offset": 2764964, "flags": 0, "name": "__real@3d8f5c29" }, { "file_offset": 2764968, "flags": 0, "name": "__real@3f83d70a" }, { "file_offset": 2764972, "flags": 0, "name": "__real@bd036d41" }, -{ "file_offset": 2764976, "flags": 0, "name": "_rdata_002a30b0" }, +{ "file_offset": 2764976, "flags": 0, "name": "_fudge_vectors" }, { "file_offset": 2765300, "flags": 0, "name": "??_C@_0N@HOJCMADM@biped_update?$AA@" }, { "file_offset": 2765320, "flags": 0, "name": "??_C@_0ED@IAGIKAMA@WARNING?3?5biped?5?$CFs?5?$CI?$CFs?$CJ?5is?5in?5a?5b@" }, { "file_offset": 2765388, "flags": 0, "name": "__real@c4fa0000" }, @@ -22468,6 +22468,7 @@ { "file_offset": 4414832, "flags": 0, "name": "_decal_globals", "static": true }, { "file_offset": 4414840, "flags": 0, "name": "_decal_geometry", "static": true }, { "file_offset": 4445564, "flags": 0, "name": "_decal_locked_count_reported", "static": true }, +{ "file_offset": 4445565, "flags": 0, "name": "_decal_insert_locked_count_reported", "static": true }, { "file_offset": 4445566, "flags": 0, "name": "_decals_unlock_locked_count_reported", "static": true }, { "file_offset": 4445567, "flags": 0, "name": "_decals_unlock_permanent_count_reported", "static": true }, { "file_offset": 4445568, "flags": 0, "name": "_decal_delete_locked_reported", "static": true }, @@ -22705,7 +22706,7 @@ { "file_offset": 4984040, "flags": 0, "name": "_structure_decals_globals" }, { "file_offset": 4984048, "flags": 0, "name": "_warned_about_missing_subclusters", "static": true }, { "file_offset": 4984056, "flags": 0, "name": "_structure_visibility_globals", "static": true }, -{ "file_offset": 4985088, "flags": 0, "name": "_bss_004c1100" }, +{ "file_offset": 4985088, "flags": 0, "name": "_structure_globals", "static": true }, { "file_offset": 4987144, "flags": 0, "name": "_draw_string_globals" }, { "file_offset": 4987392, "flags": 0, "name": "_global_language_code" }, { "file_offset": 4987400, "flags": 0, "name": "_bss_004c1a08" }, diff --git a/docs/object_matching_logs/ai_flee_teleport_model_node_packet_20260912.md b/docs/object_matching_logs/ai_flee_teleport_model_node_packet_20260912.md new file mode 100644 index 00000000..117f4872 --- /dev/null +++ b/docs/object_matching_logs/ai_flee_teleport_model_node_packet_20260912.md @@ -0,0 +1,82 @@ +# AI flee, AI teleport, and model-node packet (2026-09-12) + +## Result + +This focused reconciliation admits four strict-exact owners for **920 +meaningful / 944 padded code bytes**. Meaningful sizes come from the canonical +objdiff report and exclude trailing alignment bytes. + +| unit | exact owner | meaningful | padded | relocations | +|---|---|---:|---:|---:| +| `source/ai/action_flee` | `_action_flee_control` | 395 | 400 | 8 | +| `source/ai/ai_script` | `_ai_scripting_teleport_starting_location_if_unsupported` | 94 | 96 | 7 | +| `source/ai/ai_script` | `_ai_scripting_teleport_starting_location` | 94 | 96 | 7 | +| `source/models/model_animations` | `_animation_graph_node_matrices_from_orientations` | 337 | 352 | 13 | + +The focused gates preserve every inherited exact owner: action flee is 10/13 +exact, AI Script is 100/116 exact after the target's stale address name is +aliased, model animations is 25/32 exact, and HS remains 446/448 exact after +the teleport declarations move to their owner header. Every focused compile +also rejects an emitted `_point_from_line3d` owner. + +## Provenance and house-rule audit + +The January split objects provide the controlling ABI, section, relocation, +and instruction evidence. HCEA's reconstructed symbol database independently +supplies the semantic names and complete field layouts in: + +- `src/blam/ai/action_flee_control.c`; +- `src/blam/ai/ai_scripting_teleport_starting_location_private.c` and the two + public wrapper files; +- `src/blam/models/animation_graph_node_matrices_from_orientations.c`; and +- `src/headers/animation_graph_node.h` (DB-verified 0x40-byte layout). + +The model-node layout is deliberately TU-private. Earlier audits rejected a +duplicate *guessed* layout; the later HCEA DB evidence now authenticates every +field, offset, and total size used here. Moving the layout into a broadly +included header is still unsafe under VC7 definition-position sensitivity. +This evidence update supersedes only the old guessed-layout objection; the +previous owner-scheduling warning remains in force. + +Action flee uses the January TU's 3/6/7 priority values with semantic local +names. HCEA authenticates the meanings, but its later-build shared enum has a +different numeric layout and is not transplanted. Object and prop access use +their typed subsystem macros, movement receives `NULL` for its path pointer, +and the function ends with an explicit `return;`. + +The teleport declarations live in `source/ai/ai_script.h`, and HS includes +that owner rather than redeclaring the public functions locally. The private +helper has the authenticated name +`_ai_scripting_teleport_starting_location_private`; the target symbol map no +longer publishes `_code_000452d0` as its identity. + +## Honest fuzzy frontier (zero credit) + +The private teleport helper is retained as the best complete, typed HCEA- +backed reconstruction, but it remains residual and receives **zero** exact +credit. Target and candidate are both 288 padded bytes with the same 13 +relocation destinations. Their normalized hashes are respectively +`755b7a6e2990f977b15fb61eb09a0106990c68fcd7d69128922e69cc12e52c5e` +and +`9c0415c2243227d13b1b96ca944e476f75acf40417113dab2fc3edcaa501b22e`; +objdiff reports 99.9375%. The remaining one-byte schedule displacement begins +after the January `biped_approximate_surface_index` call. Widening that shared +function's return type can close this helper but perturbs accepted consumers, +so the ABI change is rejected. + +`animation_update_internal` was also tested from HCEA evidence. Its natural +body requires a 32-bit owner contract that conflicts with the accepted shared +header and exact callers; the experiment was fully removed and the function +remains unwritten. The previously rejected AI Script allegiance-broken, +follow-target-AI, and command-list-status paths likewise remain unwritten. + +## Focused verification + +```text +python tools/campaign/gate.py source/ai/action_flee --all --forbid-emitted-symbol _point_from_line3d +python tools/campaign/gate.py source/ai/ai_script --all --alias ai_scripting_teleport_starting_location_private=code_000452d0 --forbid-emitted-symbol _point_from_line3d +python tools/campaign/gate.py source/hs/hs --all --forbid-emitted-symbol _point_from_line3d +python tools/campaign/gate.py source/models/model_animations --all --forbid-emitted-symbol _point_from_line3d +``` + +`git diff --check` passes for every source and header touched by this packet. diff --git a/docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md b/docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md new file mode 100644 index 00000000..b37efae8 --- /dev/null +++ b/docs/object_matching_logs/bipeds_obj_batch7_exact_and_fuzzy_20260912.md @@ -0,0 +1,168 @@ +# `bipeds.obj` batch-7 exact and fuzzy reconciliation (2026-09-12) + +## Result + +Against the batch-7 starting `build/report.json` state, `bipeds.obj` advances +from **26 exact functions** to **36 exact / 9 residual / 6 unwritten**. The ten +new strict owners contribute **2,981 meaningful exact code bytes** and **3,040 +padded target bytes**. The meaningful figures come from the target function +`size` fields in `build/report.json`; section alignment is not credited as +meaningful code. + +| January offset | Semantic owner | Linkage | Meaningful | Padded | Relocations | Normalized SHA-256 | +| ---: | --- | --- | ---: | ---: | ---: | --- | +| `0x190780` | `_biped_make_footstep` | private | 266 | 272 | 20 | `9ca826c266028d2ebf096c8194080858416f7bf4ee4bce1c56ebb25c38f0501a` | +| `0x1916e0` | `_biped_falling_danger` | public | 310 | 320 | 11 | `11b3e4ee1dca0cec06c0850c805f79d9e6a1cbbdecda4318472df09054821f0b` | +| `0x191820` | `_biped_vehicle_speech` | private | 427 | 432 | 21 | `bfe88cc6fa8b4ef188d940377a7c56455b1ac6f60f815a7011d0cd0bf68d7d22` | +| `0x1919d0` | `_biped_apply_rotation` | private | 298 | 304 | 9 | `35d6dd1953a903200ac1aad0076ffc4a92b8ae0a68e3971a1b2d29c9a82ccbe0` | +| `0x191b00` | `_biped_jump` | private | 429 | 432 | 10 | `8b12b750e880a57b0e96c576cf3790219d1f63737eae4b19255c99407f8bb99d` | +| `0x191cb0` | `_biped_try_to_make_footsteps` | private | 408 | 416 | 17 | `cc0fdad80586237445476a1b9607f6f8e3c89a0dd9d444ab67a355dad6c942e6` | +| `0x192170` | `_biped_update_airborne` | public | 345 | 352 | 20 | `c52eb65ddcbb139477ad361a3f1c248a489d4c49462edd250f1e50e20b64d95a` | +| `0x1922d0` | `_biped_update_landing` | private | 169 | 176 | 7 | `0d674d499d2be17aa09451a701a2cca9d665d6e2ee18082744dc9945da7acddc` | +| `0x192380` | `_biped_update_slipping` | private | 126 | 128 | 7 | `d4de48606368472bd8efac12d0c8fa2dfab4361fcd5c053560e7273b9a897f60` | +| `0x195b60` | `_biped_update_dead` | private | 203 | 208 | 12 | `e43c121f3fb32dc716ad097739235aff13d81fd3296624d883b17a9406e6243c` | +| **Total** | **10 owners** | | **2,981** | **3,040** | | | + +The semantic names are present in `config/symbols.json`; no definition uses a +`code` plus address identifier. `biped_falling_danger` and +`biped_update_airborne` are declared in the owning `bipeds.h`. The seven +private exact definitions and `biped_make_footstep` remain translation-unit +private because that is the natural linkage that reproduces January's bodies. + +## Source and symbol evidence + +The same-build January atlas at +`research/halo-symbol-atlas/symbols/halo_ce/4cc87b45f721270392a96f1674ed2b5cd4a7bb4355faeab4531d1cf1884d9520.jsonl` +maps the relevant offsets to the semantic Biped names. The HCEA PDB atlas and +the reconstruction files under `_research_cache/halocea/src/blam/units/` +independently corroborate the names, gameplay semantics, field use and control +flow. In particular, the following donor files were consulted as semantic +evidence rather than treated as interchangeable January code: + +- `biped_make_footstep.c`; +- `biped_falling_danger.c`; +- `biped_vehicle_speech.c`; +- `biped_apply_rotation.c`; +- `biped_jump.c`; +- `biped_try_to_make_footsteps.c`; +- `biped_update_airborne.c`; +- `biped_update_landing.c`; +- `biped_update_slipping.c`; and +- `biped_update_dead.c`. + +January disassembly, target relocations, target string literals and target +COFF sections remain the exactness oracle. The reconstruction uses project +`real` and typed Biped/tag accessors, named flag and enum constants, ordinary +multiline prototypes, and explicit terminal returns. It retains no inline +depth pragma, volatile/register steering, compiler barrier, raw byte emission, +address-derived identifier or implausible dead dependency. + +## `_biped_definition` layout proof + +The HCEA type-database reconstruction in +`_research_cache/halocea/src/headers/_biped_definition.h` (SHA-256 +`24f12c92b7c10dadf05242a2082549cd51f379e53570e293d23a276e918daecb`) +records the complete Biped-specific definition as **0x204 bytes**, with these +post-collision offsets: + +| Field | Offset within `_biped_definition` | +| --- | ---: | +| `collision_unused[10]` | `0x140` | +| `autoaim_width` | `0x168` | +| `runtime_cosine_stationary_turning_threshold` | `0x1d8` | +| `runtime_uphill_k1` | `0x1f0` | +| `runtime_pelvis_node_index` | `0x1f4` | +| `runtime_head_node_index` | `0x1f6` | +| `contact_points` | `0x1f8` | + +The January outer `biped_definition` places this substructure at `0x2f0`. +Target instructions independently access `autoaim_width` at `0x458` and the +pelvis/head indices at `0x4e4`/`0x4e6`, exactly the sums implied by the HCEA +layout. A compensating tail would incorrectly move the later runtime fields +and contradict the database's 0x204 total. The natural HCEA-shaped +`biped_make_footstep` also becomes a strict 272-byte match only with this +complete layout, corroborating the `contact_points` location. + +`fudge_vectors` is likewise restored as the authenticated 27-element +`real_vector3d const` data owner. Target and candidate have the same 324-byte +`.rdata` section, zero relocations and external COFF storage. It earns no code +credit. + +## Header blast-radius gate + +`biped_definitions.h` has seven direct C consumers and no intervening header +consumer. Every one was compiled with the focused gate after the complete +layout correction. The exact-function sets are supersets of the batch +starting report: **zero inherited exact functions were lost**. + +| Consumer | Current gate result | Lost starting exacts | +| --- | ---: | ---: | +| `source/units/bipeds` | 36 exact / 9 residual / 6 unwritten | 0 | +| `source/units/units` | 189 exact / 0 residual / 0 unwritten | 0 | +| `source/units/biped_limp_noodle` | 3 exact / 3 residual / 0 unwritten | 0 | +| `source/ai/actor_perception` | 32 exact / 6 residual / 6 unwritten | 0 | +| `source/game/aim_assist` | 9 exact / 6 residual / 1 unwritten | 0 | +| `source/ai/ai_debug` | 56 exact / 4 residual / 0 unwritten | 0 | +| `source/game/players` | 54 exact / 15 residual / 1 unwritten | 0 | + +Some non-Biped rows include unrelated concurrent gains relative to the older +`build/report.json`; none of those gains is claimed by this packet. The +important blast-radius result is the absence of any lost starting exact +owner, with Units still **189/189**. + +## Honest fuzzy boundary + +The complete, semantically credible residual bodies are retained for future +research and receive **zero exact code credit**: + +| Owner | Target padded bytes | Current boundary | +| --- | ---: | --- | +| `_biped_accelerate` | 480 | Pre-existing residual; unchanged by this packet. | +| `_biped_adjust_placement` | 96 | Same 39-instruction envelope; one independent pointer-register allocation differs. | +| `_biped_check_discard` | 176 | Same padded envelope; formatted diagnostic argument evaluation order differs. | +| `_biped_find_nearby_support_surface` | 544 | Same 23 relocations and 175 instructions; one commutative x87 operand-load pair differs. The retained grouping follows the HCEA semantic expression and was not permuted for codegen. | +| `_biped_fix_position` | 1,232 | Same padded and relocation envelope with a narrow load/sign-extension schedule delta. Its discarded pill-base output is a genuine `real_point3d`, not a representation-punned vector. | +| `_biped_get_autoaim_pill` | 336 | Correct target field offsets and identical 131-instruction envelope; only the spherical-branch pointer/value register schedule differs. | +| `_biped_get_sight_position` | 416 | Correct target assertions and same padded envelope; the remaining delta is an x87/local spill choice. | +| `_biped_update_turning` | 1,712 | Complete donor-corroborated gameplay body, but still a normalized-code residual. | +| `_biped_update` | 1,120 | Complete natural body; candidate is 1,104 padded bytes and remains residual. | + +No residual is counted as exact merely because it has the correct size, +relocation count or semantics. The 512-byte `_biped_update_jumping` stays +unwritten because the available HCEA donor only reconstructs the short jump +counter prefix and omits January's substantial cheat/jetpack and rumble logic. +The 5,376-byte `_biped_update_physics` is a large, fuzzy owner rather than the +requested small leaf and was not synthesized from incomplete evidence. + +## Emitted-helper ownership boundary + +The final candidate contains 25 helper code owners absent from the target +object. Seventeen were already present in the built starting candidate and +remain inherited, uncredited helper debt. This packet removes the former +candidate-only `_plane3d_distance_to_point` owner and introduces eight natural +helper COMDATs solely through the retained zero-credit fuzzy bodies: + +- `_collision_test_line` from `_biped_fix_position`; +- `_cross_product2d`; +- `_dot_product2d`; +- `_magnitude2d`; +- `_magnitude_squared2d`; +- `_normalize2d`; +- `_rotate_vector2d`; and +- `_scale_vector2d` from the two-dimensional turning path. + +These helpers are recorded explicitly so they cannot be mistaken for target +ownership or credited code. The exact-function credit above is based only on +target-owned, strict-equal sections. + +## Focused verification + +The final alias gate reports `36 exact / 9 residual / 6 unwritten` and passes +the emitted-symbol guard for `_point_from_line3d`. The TU uses the established +external-helper schedule for `point_from_line3d`, so no candidate definition +or COMDAT with that name is emitted. The seven-consumer layout sweep reports +zero inherited exact losses, and `git diff --check` passes. + +This is a focused packet only. The integration owner still owns configure, +the full Ninja build, the whole-canonical accepted-ledger comparison, park +validation, test suite and publication verdict. diff --git a/docs/object_matching_logs/interface_obj_weapon_hud_best_fuzzy_20260912.md b/docs/object_matching_logs/interface_obj_weapon_hud_best_fuzzy_20260912.md new file mode 100644 index 00000000..a56e2f63 --- /dev/null +++ b/docs/object_matching_logs/interface_obj_weapon_hud_best_fuzzy_20260912.md @@ -0,0 +1,163 @@ +# `interface.obj` weapon-HUD resolver best fuzzy (2026-09-12) + +## Disposition + +`interface_get_weapon_hud_index` is now present as a complete, typed, natural-C +reconstruction. It is the best evidenced candidate presently available, but +it is **not byte-exact** and receives **zero exact-function and zero exact-byte +credit**. + +| function | meaningful | target/candidate padded | target/candidate relocations | result | +| --- | ---: | ---: | ---: | --- | +| `_interface_get_weapon_hud_index` | 444 | 448 / 448 | 20 / 20 | residual, zero credit | + +The target normalized SHA-256 is +`0673f867b219cdbd399685310736ae3de8263479cb2104edd5c56f48930c6549`; +the candidate is +`57151d28c46aeee1fc2bb715ebde2fbc994b52447a4ba273d624aa56e7749e72`. +The normalized-byte comparison reports 69 differing bytes, largely branch +displacements after the two-byte instruction-stream drift. Relocation target +kind and destination sequence are identical. The target has 144 instructions +and the candidate 145. + +The ordinary isolated unit gate is: + +```text +residual 448 _interface_get_weapon_hud_index [sha] +== exact 15 residual 1 unwritten 2 (of 18 listed) +``` + +All fifteen inherited exact functions remain exact, and the emitted-symbol +guard confirms that `_point_from_line3d` is absent. + +## Source and January evidence + +The semantic donor is the immutable HCEA reconstruction: + +`halocea-review/src/blam/interface/interface_get_weapon_hud_index.c` + +Its SHA-256 is +`3895e1c2a1f2a4570d99a7218f7d5762c6badad9efb7e40e10eb194333b8a97f`. +The donor supplies the complete operation and the later-build identity, while +January's `interface.obj` instruction stream and relocations remain the +acceptance authority. The January symbol atlas and HCEA identify the public +owner and signature as: + +```c +long interface_get_weapon_hud_index( + real *flashlight_power); +``` + +The declaration now lives in `source/interface/interface.h`; the temporary +same-C-file declaration was removed. The body uses `player_get`, `unit_get`, +`weapon_get`, `unit_definition_get`, `weapon_definition_get`, and +`TAG_BLOCK_GET_ELEMENT`, not raw datum/tag accesses. It uses the established +`director_perspective` enum values and `_unit_seat_gunner_bit`, project `real`, +and an explicit returned value. TU-local semantic HUD views expose +`show_hud` and `defaults.default_weapon_hud`, with a compile-time check that +the tag-reference index is at January offset `0x2CC`; no raw-offset expression +is used by executable code. + +The complete behavior is: + +1. map the rendered local-player slot to its player datum; +2. reject hidden HUD, neutral/scripted cameras, or a missing player unit; +3. resolve the unit's current inventory weapon; +4. if absent, resolve the parent unit's weapon only from a gunner seat; +5. preserve the player's integrated-light power when its own weapon is used; +6. return the weapon definition's HUD tag, or the global default HUD for an + otherwise empty inventory; and +7. write the flashlight power on every exit. + +## Exact residual boundary + +The natural body reproduces every January instruction through the final +out-parameter copies. January copies the four-byte `real` representation +through general-purpose registers at both duplicated exits: + +```text +mov edx,[ebp-4] +mov [eax],edx +``` + +VC7 lowers the ordinary C assignment `*flashlight_power = flashlight;` through +the x87 stack: + +```text +fld dword ptr [ebp-4] +fstp dword ptr [edx] +``` + +That also changes the neighboring epilogue schedule and shifts the final two +relocation addresses by two bytes. The earlier investigation tested `/Op`, +declaration ordering, split initialization, and an explicit early return; none +closed the residual. Type punning, an aggregate-copy facade, `memcpy`, a +wrong integer-pointer ABI, volatile/register pressure, or inline assembly +would exist only to steer these bytes and is therefore rejected by the house +rules. Reopen only with original January source or an independently attested +source construct that naturally causes VC7 to use the integer copy. + +## Header blast radius + +Every direct `interface.h` C consumer was independently compiled with the +isolated campaign gate and the forbidden-point guard: + +- `source/interface/interface` +- `source/bink/bink_playback` +- `source/cache/xbox_texture_cache` +- `source/hs/hs_compile` +- `source/hs/hs` +- `source/game/game` +- `source/ai/ai_profile` +- `source/main/main` +- `source/text/draw_string` +- `source/game/game_engine` +- `source/interface/hud_messaging` +- `source/interface/ui_widget` +- `source/interface/hud` +- `source/interface/terminal` +- `source/rasterizer/xbox/rasterizer_xbox_motion_sensor` +- `source/render/render_debug` +- `source/physics/collision_usage` + +No baseline exact function was lost in sixteen consumers. `hs_compile.obj` +currently has three baseline-exact functions reported as relocation-identity +residuals (`hs_parse_tag_reference`, `hs_parse_enum`, and +`hs_parse_object_name`); an A/B gate with the new prototype temporarily +removed produced the same three residuals, proving they are pre-existing +shared-tree state and not caused by this owner declaration. + +## Adjacent 400--800-byte leaf review + +The remaining interface-family candidates in the requested size band were +reviewed without landing speculative code: + +- `hud_weapon.obj::_hud_render_weapon_interface` (496 padded) calls several + still-unwritten same-TU helpers through January's custom private register + conventions; reconstructing it alone cannot reproduce the call boundary. +- `hud_draw.obj::_code_000c0e10` (480 padded) is HCEA's + `hud_draw_multitexture_overlay_get_current_weapon_definition`, but January + passes its player parameter in EAX through a private convention selected + with the still-unwritten 2,272-byte caller. +- `hud_draw.obj::_hud_draw_static_element` (640 padded) likewise calls that + same 2,272-byte private renderer through a custom same-TU convention. +- The 320/192-byte color packers have prior independent rejection records: + their proposed exact bodies require hand-steered x87 assembly and one + natural staged form has a proven rounding counterexample. + +These are dependency packets, not honest standalone leaves. No exact credit +or source change is claimed for them here. + +## Verification artifacts + +- candidate object: `scratch/interface_weapon_hud_fuzzy.obj`, SHA-256 + `48ebb56d1b7179408baa44ac5a0d4311744716356c892f85bd946ffa2f7476ae` +- January split object SHA-256: + `2d89bac5a4216dc0bdb3c0144acde11606198c14e8e83154eda722795f1b91b6` +- `tools/fake_match_scan.py source/interface/interface.c`: zero review leads +- `git diff --check` on the two source-owner paths: pass + +The parked-function manifest must be updated only after the orchestrator's +full build refreshes `build/base` and `build/report.json`; adding the current +scratch measurement before that refresh would create a knowingly stale park. +No configure, Ninja build, commit, or push was performed in this worker. diff --git a/docs/object_matching_logs/lane_reconciliation_10k_batch_7_20260912.md b/docs/object_matching_logs/lane_reconciliation_10k_batch_7_20260912.md new file mode 100644 index 00000000..460eeffd --- /dev/null +++ b/docs/object_matching_logs/lane_reconciliation_10k_batch_7_20260912.md @@ -0,0 +1,125 @@ +# Seventh 10K lane-reconciliation batch (2026-09-12) + +## Canonical result + +The frozen published base is +`d6250fa7943e77031522a37414769af7f67301d1`, with 6,888 accepted functions +and 1,113,556 meaningful accepted exact code bytes. The reconciled build has +**6,918 accepted functions and 1,123,718 meaningful bytes**: **+30 functions +and +10,162 bytes**. + +The independent fail-closed target-section census records **29 new strict +functions, +9,470 meaningful bytes, +9,664 padded bytes, and zero +regressions**. One additional function, `decal_insert`, is accepted through +the semantic structural gate described below. No fuzzy or whole-object credit +is included. + +| accepted packet | functions | meaningful | padded | +|---|---:|---:|---:| +| AI flee, vehicle, and teleport wrappers | 4 | 659 | 672 | +| Bungie.net key agreement and transport | 3 | 378 | 400 | +| game safe-save predicate | 1 | 315 | 320 | +| HUD projection and weapon overlays | 2 | 1,470 | 1,488 | +| screenshot renderer | 1 | 482 | 496 | +| model-animation node matrices | 1 | 337 | 352 | +| object shield and aftermath damage | 2 | 1,454 | 1,456 | +| render-camera projection quartet | 4 | 792 | 832 | +| structure vector test | 1 | 602 | 608 | +| biped update family | 10 | 2,981 | 3,040 | +| decal insertion | 1 | 692 | 704 | +| **total** | **30** | **10,162** | **10,368** | + +## Exact-accounting reconciliation + +The strict census accounts for 29 functions and 9,470 meaningful / 9,664 +padded bytes. `source/effects/decals::_decal_insert` adds 692 meaningful / 704 +padded bytes only in the semantic-exact tier. Its complete code and +instruction streams, relocation offsets, types, counts, and resolved final +addresses agree. The strict symbolic comparator remains fail-closed because +the authentic expression is represented through a distinct same-address BSS +symbol. No alias facade or source-pressure trick is retained to manufacture a +strict verdict. + +The accepted delta is therefore exactly `9,470 + 692 = 10,162` meaningful +bytes and `29 + 1 = 30` functions. Residual and parked functions remain zero +credit even when their source is the best reconstruction presently known. + +## Ownership and house-rule reconciliation + +- `game_statistics_record_damage` is owned by the narrow internal header + `source/game/game_statistics_internal.h`. Existing public statistics APIs + remain in the January-compatible `game.h` position: moving all declarations + together reproducibly changed two unrelated Race functions by 496 padded + bytes because of C2 definition-position sensitivity. +- Render Sprite now consumes the two private camera interfaces from + `render_cameras_internal.h`; its consumer-local duplicate prototypes were + removed. +- `biped_update_airborne` and `biped_update_moving` declarations are in the + Biped owner header at positions that preserve both Units and Biped exact + code. The latter's tail position is intentionally compiler-sensitive. +- HUD's retained fuzzy color/vector path uses explicit project geometry types + and layout casts rather than incorrect pointer types. The exact HUD pair is + unchanged and the fuzzy body receives no credit. +- The AI Script encounter handle extraction uses + `DATUM_INDEX_TO_ABSOLUTE_INDEX`; its complete helper remains fuzzy and + receives no credit. The private `biped_state_unknown` name is explicitly + provisional and appears only in a zero-credit parked body pending original + symbol recovery. +- `screenshot_render` has the authentic mutable render-window parameter. All + new public/private declarations live with their subsystem owner rather than + in incidental consumers. + +The changed-source fake-match scan finds only inherited, target-evidenced low +level constructs: the existing AI optimization pragma pair, Decals' x87 color +helper, and HUD's existing return-address helper. No newly added volatile +steering, forced inline/noinline, raw-byte emission, address-derived private +names, or nonsensical exact source is admitted. + +## Retained fuzzy and rejected research + +All retained non-exact bodies are represented in `config/parked.json` and +receive zero exact credit. The batch-specific ledgers cover the AI, Biped, +Interface/HUD, and ownership packets. Two close but unsafe donor +reconstructions are preserved under `research/reconstructions/` for future +work: + +- `flag_render_proper_20260912.md` records a 98.77677% candidate whose access + facades and lock constant are not yet source-authentic. +- `player_aim_projectile_20260912.md` records a 99.24812% candidate whose + nested representation casts and helper schedule are not safe to publish as + source. + +Neither candidate contributes to the totals above. + +## Units whole-object boundary + +`units.obj` retains all 189 target functions exact, but the candidate still +emits the inherited 48-byte, zero-relocation `_point_from_line3d` COMDAT. Its +SHA-256 is +`9b763841f8519177ac2d3fd471226972b9fa92cf1e9061e9f92359077dc82741`, +identical in the batch-6 audit object and this batch's candidate. Natural +external-declaration experiments removed the helper only by regressing +`_unit_adjust_projectile_ray` and `_unit_throw_grenade_release`; those +experiments were reverted. This batch neither introduces nor credits that +debt, and makes no whole-object Matching claim for Units. Every other changed +translation unit passes the forbidden-helper gate. + +## Publication gate + +- Full configure/Ninja source build and semantic/progress reports: pass. +- Stable target-section sweep: 29 gains / 9,470 meaningful / 9,664 padded / + zero regressions. +- Semantic accepted ledger: +30 functions / +10,162 meaningful bytes. +- Final accepted total: 6,918 functions / 1,123,718 meaningful bytes. +- Units focused gate: 189 exact / 0 residual / 0 unwritten; inherited surplus + helper remains explicitly rejected at whole-object scope. +- Object-admission audit: zero candidates, contradictions, or revocations; + six known whole-object rejections remain visible. +- Park-manifest validation: 459 active entries, zero stale or invalid entries. +- Tool tests: 1,144 passed, 5 skipped, plus 26 subtests passed. +- `git diff --check`: pass. +- Stable snapshot: `scratch/batch7-final.json`, SHA-256 + `51bd88bd5383ee89b59591beec80191b0d148d80a14d02f4f2438d102ceece79`. + +The canonical treemap is refreshed at `http://127.0.0.1:8765/`. The next +10,000-byte cadence threshold is **1,133,718 meaningful accepted exact bytes**. diff --git a/docs/object_matching_logs/prototype_owner_split_game_statistics_render_cameras_20260912.md b/docs/object_matching_logs/prototype_owner_split_game_statistics_render_cameras_20260912.md new file mode 100644 index 00000000..e1231ce5 --- /dev/null +++ b/docs/object_matching_logs/prototype_owner_split_game_statistics_render_cameras_20260912.md @@ -0,0 +1,49 @@ +# Prototype-owner split: game statistics and render cameras (2026-09-12) + +## Game-statistics interface boundary + +`game_statistics_record_damage` is the sole statistics API consumed outside +the public `game.h` umbrella. It is now declared by the narrow owning header +`source/game/game_statistics_internal.h`, included by its defining +`game_statistics.c` translation unit and its only consumer, `damage.c`. + +`game_statistics_start` and `game_statistics_record_kill` remain in `game.h`. +That file is the closest associated public header and preserves the declaration +topology seen by the January build. `game_statistics_stop` has no consumer, so +it remains definition-only; its target external linkage is unchanged. + +A broad `game_statistics.h` consolidation was tested and rejected. Merely +removing the two existing declarations from `game.h` and exposing all four +functions through the broad header reproducibly reopened two previously exact +Race functions: + +- `_race_touch_flag`: 336 padded bytes, relocation count 28 instead of 30. +- `_race_engine_player_update`: 160 padded bytes, normalized-byte mismatch. + +Reverting only that broad declaration topology restored Race to 35 exact, one +pre-existing residual. The retained narrow split therefore resolves the real +cross-TU damage bridge without trading away 496 exact padded bytes or using a +conditional header/code-generation trick. + +Focused retained-state gates: + +- `game_statistics.obj`: 3 exact, 1 pre-existing residual. +- `damage.obj`: 31 exact, 3 pre-existing residuals. +- `game_engine.obj`: 169 exact, 11 pre-existing residuals. +- `game_engine_race.obj`: 35 exact, 1 pre-existing residual. + +## Render-cameras interface boundary + +`render_sprite.c` now includes the existing narrow owner +`render_cameras_internal.h`. Its consumer-local duplicates for +`render_frustum_cube_view_fraction` and `render_camera_screen_to_view` were +removed, and both declarations are owned by that internal header. + +All nine consumers of `render_cameras_internal.h` were enumerated and gated. +Their exact/residual sets remain unchanged. In particular, +`render_sprite.obj` remains 6 exact / 4 pre-existing residual and +`render_cameras.obj` remains 11 exact / 4 pre-existing residual / 6 unwritten. +Both focused gates also pass the forbidden `_point_from_line3d` emitted-symbol +guard. + +No new exact-match credit is claimed by this ownership-only cleanup. diff --git a/docs/object_matching_logs/shell_xbox_obj_scope_table_owner_refresh_20260908.md b/docs/object_matching_logs/shell_xbox_obj_scope_table_owner_refresh_20260908.md index 39c1f486..e3f102d0 100644 --- a/docs/object_matching_logs/shell_xbox_obj_scope_table_owner_refresh_20260908.md +++ b/docs/object_matching_logs/shell_xbox_obj_scope_table_owner_refresh_20260908.md @@ -2,9 +2,10 @@ The full rebuilt batch preserves the authenticated `_main` SEH scope table byte-for-byte, but the C2 compiler-local owner serial advances from `$T18273` -to `$T18274` after the accepted shared declaration updates in this batch. +to `$T18284` after the accepted shared declaration updates through the seventh +reconciliation batch. -`config/semantic_data_matches.json` now names `$T18274`. The verifier still +`config/semantic_data_matches.json` now names `$T18284`. The verifier still requires the same unique 12-byte `.rdata` section, `0x40401040` flags, two relocations, normalized SHA-256 `3414421b183188342209566fad4e0c0723070b4030630dde685ba70281e92725`, diff --git a/docs/reconciliation_queue_20260907.md b/docs/reconciliation_queue_20260907.md index 60b51a46..bd4a3a37 100644 --- a/docs/reconciliation_queue_20260907.md +++ b/docs/reconciliation_queue_20260907.md @@ -457,3 +457,24 @@ The full source build, stable regression sweep, 447-entry park validation, object-admission audit, and complete 1,144-test plus 26-subtest suite pass. The canonical treemap is refreshed for this boundary. The next required refresh threshold is **1,123,556 meaningful accepted exact code bytes**. + +## 2026-09-12 seventh 10K reconciliation packet + +Relative to published commit `d6250fa794`, the canonical semantic ledger +advances from 1,113,556 meaningful bytes / 6,888 accepted functions to +**1,123,718 meaningful bytes / 6,918 accepted functions**: a net gain of +**10,162 bytes / 30 functions**. The independent fail-closed target-section +census records 29 strict gains / 9,470 meaningful / 9,664 padded bytes with +zero regressions. + +`decal_insert` contributes the remaining 692 meaningful / 704 padded semantic +exact bytes only after its code, instruction stream, relocation shape, and +resolved final addresses agree. The strict symbolic comparator remains +fail-closed for its distinct same-address BSS representation; no alias facade +or fuzzy source receives credit. All other retained residuals are parked at +zero exact credit. See [the seventh 10K ledger](object_matching_logs/lane_reconciliation_10k_batch_7_20260912.md). + +The full source build, stable regression sweep, 459-entry park validation, +object-admission audit, and complete 1,144-test plus 26-subtest suite pass. +The canonical treemap is refreshed for this boundary. The next required +refresh threshold is **1,133,718 meaningful accepted exact code bytes**. diff --git a/research/reconstructions/flag_render_proper_20260912.md b/research/reconstructions/flag_render_proper_20260912.md new file mode 100644 index 00000000..8a54995b --- /dev/null +++ b/research/reconstructions/flag_render_proper_20260912.md @@ -0,0 +1,345 @@ +# flag_render_proper retained research candidate (2026-09-12) + +Status: archived research only; not admitted to production source and receives zero exact credit. + +The Claude candidate reconstructs the complete cloth mesh and render flow and reaches 98.77677% objdiff with the exact 2,112-byte padded envelope and all 84 relocation identities. It is not publication-safe: it introduces TU-local facades for shared rasterizer model types, an unauthenticated numeric lock constant, repeated raw object_get access, an apparently unused object_definition_get call, and expression/layout choices consistent with compiler steering. The pre-existing object ledger independently rejected this owner class pending authoritative declarations. Reopen only with original January/retail symbol or source evidence for the shared types and lock enum, or a natural same-compiler donor that accounts for the remaining instruction selection. + +Target: size 2112, relocations 84, normalized SHA-256 d0507f5d0637539037a0bda0c9c0e1a1c0a0753e04913603fcdd66e3f46f6416. + +Candidate: size 2112, relocations 84, normalized SHA-256 a5b00839955717b8a04a2127124eb2bed43603450324a386284f1379931100df. + +Archived patch: + +```diff +diff --git a/source/objects/widgets/flags.c b/source/objects/widgets/flags.c +index 01007d625..417769cb2 100644 +--- a/source/objects/widgets/flags.c ++++ b/source/objects/widgets/flags.c +@@ -64,11 +64,18 @@ symbols in this file: + + #include "cseries/cseries.h" + #include "cseries/errors.h" ++#include "game/game.h" + #include "memory/data.h" ++#include "objects/object_definitions.h" + #include "objects/objects.h" + #include "physics/point_physics.h" ++#include "rasterizer/rasterizer.h" ++#include "rasterizer/rasterizer_geometry.h" ++#include "render/render.h" + #include "saved games/game_state.h" + #include "scenario/scenario.h" ++#include "shaders/shader_definitions.h" ++#include "shaders/shaders.h" + #include "tag_files/tag_groups.h" + + /* ---------- constants */ +@@ -92,6 +99,11 @@ enum trailing_edge_shape + _trailing_edge_shape_trapezoid_short_bottom + }; + ++enum ++{ ++ _rasterizer_lock_flag = 11, ++}; ++ + enum attached_edge_shape + { + _attached_edge_shape_flat, +@@ -119,6 +131,56 @@ enum tesselate + + /* ---------- structures */ + ++struct model_vertex_uncompressed ++{ ++ real_point3d position; ++ real_vector3d normal; ++ real_vector3d binormal; ++ real_vector3d tangent; ++ real_point2d texcoord; ++ short nodes[2]; ++ real node_weights[2]; ++}; ++ ++struct model_vertex_compressed ++{ ++ real_point3d position; ++ unsigned long normal; ++ unsigned long binormal; ++ unsigned long tangent; ++ point2d texcoord; ++ byte nodes[2]; ++ short node_weight; ++}; ++ ++struct rasterizer_model_skinning ++{ ++ real_matrix4x3 const *node_matrices; ++ short node_matrix_count; ++ word pad; ++}; ++ ++struct render_model_effect ++{ ++ short type; ++ word pad; ++ real intensity; ++ byte reserved[0x20]; ++}; ++ ++struct rasterizer_model_begin_parameters ++{ ++ unsigned long geometry_flags; ++ long unique_identifier; ++ struct rasterizer_model_skinning skinning; ++ struct render_lighting lighting; ++ struct render_animation animation; ++ struct render_model_effect effect; ++ real_point3d centroid; ++ real radius; ++ real_vector2d base_map_scale; ++}; ++ + struct flag_datum_prefix + { + struct datum_header header; +@@ -669,6 +731,237 @@ struct flag_cell_datum *flag_datum_get_cell( + return &flag->cells[x * (definition->height - 1) + y]; + } + ++void flag_render_proper( ++ struct flag_datum_prefix *flag, ++ struct flag_definition *definition, ++ struct render_lighting const *lighting, ++ struct render_animation const *animation) ++{ ++ long shader_index; ++ short maximum_triangles; ++ long triangle_buffer_index; ++ long vertex_buffer_index; ++ ++ if (object_get(flag->object_index)->object.owner_team_index != _team_red) ++ shader_index = definition->shader_blue.index; ++ else ++ shader_index = definition->shader_red.index; ++ if (shader_index == NONE) ++ shader_index = definition->shader_blue.index; ++ ++ rasterizer_globals.current_lock_operation = _rasterizer_lock_flag; ++ maximum_triangles = (2*definition->height - 2)*(definition->width - 1); ++ triangle_buffer_index = rasterizer_dynamic_triangles_new(maximum_triangles); ++ vertex_buffer_index = rasterizer_dynamic_vertices_new( ++ _rasterizer_vertex_type_model_compressed, ++ definition->width*definition->height); ++ if (triangle_buffer_index != NONE && vertex_buffer_index != NONE) ++ { ++ struct model_vertex_compressed *vertices; ++ short *triangles; ++ real one_over_width; ++ real one_over_height; ++ struct model_vertex_uncompressed vertex; ++ struct rasterizer_model_begin_parameters parameters; ++ struct shader *shader; ++ struct flag_vertex_datum *corner00; ++ struct flag_vertex_datum *corner10; ++ struct flag_vertex_datum *corner01; ++ struct flag_vertex_datum *corner11; ++ short vertex_index = 0; ++ short i; ++ short x; ++ short y; ++ ++ object_definition_get(object_get(flag->object_index)->definition_index); ++ vertices = rasterizer_dynamic_vertices_lock(vertex_buffer_index); ++ triangles = rasterizer_dynamic_triangles_lock(triangle_buffer_index); ++ one_over_width = 1.0f/(definition->width - 1); ++ one_over_height = 1.0f/(definition->height - 1); ++ csmemset(&vertex, 0, sizeof(vertex)); ++ ++ for (x = 0; x < definition->width; x++) ++ { ++ for (y = 0; y < definition->height; y++, vertex_index++) ++ { ++ struct flag_vertex_datum *point = flag_datum_get_vertex(flag, definition, x, y); ++ short x0 = x < definition->width - 1 ? x : x - 1; ++ short y0 = y < definition->height - 1 ? y : y - 1; ++ struct flag_vertex_datum *origin = flag_datum_get_vertex(flag, definition, x0, y0); ++ struct flag_vertex_datum *across = flag_datum_get_vertex(flag, definition, x0 + 1, y0); ++ struct flag_vertex_datum *down = flag_datum_get_vertex(flag, definition, x0, y0 + 1); ++ real_vector3d horizontal; ++ real_vector3d vertical; ++ ++ vector_from_points3d(&origin->position, &across->position, &horizontal); ++ vector_from_points3d(&origin->position, &down->position, &vertical); ++ cross_product3d(&horizontal, &vertical, &vertex.normal); ++ normalize3d(&vertex.normal); ++ ++ vertex.position = point->position; ++ vertex.texcoord.x = x*one_over_width; ++ vertex.texcoord.y = y*one_over_height; ++ rasterizer_geometry_compress_vertices( ++ _rasterizer_vertex_type_model_uncompressed, ++ 1, ++ &vertices[vertex_index], ++ sizeof(struct model_vertex_compressed), ++ &vertex, ++ sizeof(struct model_vertex_uncompressed)); ++ } ++ } ++ ++ i = 0; ++ for (x = 0; x < definition->width - 1; x++) ++ { ++ for (y = 0; y < definition->height - 1; y++) ++ { ++ switch (flag_datum_get_cell(flag, definition, x, y)->tesselation) ++ { ++ case _tesselate_both: ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 742, ++ iheight*x + y; ++ triangles[3*i + 1] = definition->height*(x + 1) + y; ++ triangles[3*i + 2] = definition->height*x + y + 1; ++ i++; ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 747, ++ iheight*x + y + 1; ++ triangles[3*i + 1] = definition->height*(x + 1) + y; ++ triangles[3*i + 2] = definition->height*(x + 1) + y + 1; ++ i++; ++ break; ++ ++ case _tesselate_none: ++ break; ++ ++ case _tesselate_top_left: ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 754, ++ iheight*x + y; ++ triangles[3*i + 1] = definition->height*x + y + 1; ++ triangles[3*i + 2] = definition->height*(x + 1) + y; ++ i++; ++ break; ++ ++ case _tesselate_bottom_left: ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 761, ++ iheight*x + y; ++ triangles[3*i + 1] = definition->height*x + y + 1; ++ triangles[3*i + 2] = definition->height*(x + 1) + y + 1; ++ i++; ++ break; ++ ++ case _tesselate_top_right: ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 768, ++ iheight*x + y; ++ triangles[3*i + 1] = definition->height*(x + 1) + y + 1; ++ triangles[3*i + 2] = definition->height*(x + 1) + y; ++ i++; ++ break; ++ ++ case _tesselate_bottom_right: ++ match_assert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 775, ++ iheight*x + y + 1; ++ triangles[3*i + 1] = definition->height*(x + 1) + y + 1; ++ triangles[3*i + 2] = definition->height*(x + 1) + y; ++ i++; ++ break; ++ ++ default: ++ match_vassert( ++ "c:\\halo\\SOURCE\\objects\\widgets\\flags.c", ++ 784, ++ FALSE, ++ NULL); ++ break; ++ } ++ } ++ } ++ ++ rasterizer_dynamic_triangles_unlock(triangle_buffer_index); ++ rasterizer_dynamic_vertices_unlock(vertex_buffer_index); ++ shader = shader_definition_get(shader_index); ++ ++ { ++ real_point3d centroid; ++ ++ corner00 = flag_datum_get_vertex(flag, definition, 0, 0); ++ corner10 = flag_datum_get_vertex(flag, definition, definition->width - 1, 0); ++ corner01 = flag_datum_get_vertex(flag, definition, 0, definition->height - 1); ++ corner11 = flag_datum_get_vertex(flag, definition, definition->width - 1, definition->height - 1); ++ centroid.x = (corner11->position.x + corner01->position.x + corner10->position.x + ++ corner00->position.x)*0.25f; ++ centroid.y = (corner11->position.y + corner01->position.y + corner10->position.y + ++ corner00->position.y)*0.25f; ++ centroid.z = (corner11->position.z + corner01->position.z + corner10->position.z + ++ corner00->position.z)*0.25f; ++ ++ csmemset(¶meters, 0, sizeof(parameters)); ++ parameters.unique_identifier = 1; ++ parameters.skinning.node_matrices = global_identity4x3; ++ parameters.skinning.node_matrix_count = 1; ++ parameters.lighting = *lighting; ++ parameters.animation = *animation; ++ parameters.centroid = centroid; ++ parameters.base_map_scale.i = parameters.base_map_scale.j = 1.0f; ++ ++ rasterizer_profile_enable(FALSE); ++ rasterizer_models_begin(FALSE); ++ rasterizer_model_begin(¶meters, FALSE); ++ if (shader_type_is_transparent(shader->base.type)) ++ { ++ rasterizer_model_transparent_geometry_submit( ++ shader, ++ 0, ++ NULL, ++ triangle_buffer_index, ++ i, ++ NULL, ++ vertex_buffer_index, ++ ¢roid, ++ NULL); ++ } ++ else ++ { ++ rasterizer_model_draw( ++ shader, ++ 0, ++ NULL, ++ triangle_buffer_index, ++ i, ++ NULL, ++ vertex_buffer_index); ++ } ++ rasterizer_model_end(); ++ rasterizer_models_end(); ++ rasterizer_profile_enable(TRUE); ++ } ++ } ++ ++ rasterizer_dynamic_triangles_delete(triangle_buffer_index); ++ rasterizer_dynamic_vertices_delete(vertex_buffer_index); ++ rasterizer_globals.current_lock_operation = _rasterizer_lock_unlocked; ++ ++ return; ++} ++ + void flag_tesselate_region( + struct flag_definition *definition, + struct flag_datum_prefix *flag, + +``` diff --git a/research/reconstructions/player_aim_projectile_20260912.md b/research/reconstructions/player_aim_projectile_20260912.md new file mode 100644 index 00000000..e071c534 --- /dev/null +++ b/research/reconstructions/player_aim_projectile_20260912.md @@ -0,0 +1,189 @@ +# player_aim_projectile retained research candidate (2026-09-12) + +Status: archived research only; not admitted to production source and receives zero exact credit. + +The Claude candidate reconstructs the complete aim-magnetism and projectile trace flow and reaches 99.24812% objdiff with the exact 832-byte padded envelope and all 42 relocation identities. It is not publication-safe in its present form: the source has unsupported block nesting, point/vector representation casts used for in-place point addition, and a newly exposed helper owner whose January inline/out-of-line schedule has not been authenticated. The semantic candidate is preserved here without granting matching credit. Reopen only with authoritative source/symbol context for player_aim_projectile and pin_normal_to_cone3d, or a natural same-compiler donor that explains the point update and helper schedule. + +Target: size 832, relocations 42, normalized SHA-256 79f83e1a1b6ec5a6a83191382cd17fdb82c6da8f216f050b4c4379a4be525ea7. + +Candidate: size 832, relocations 42, normalized SHA-256 68607485f783d5000d94b7e70c70f1b33142754bdb5e67c904c3a128d47e5f02. + +Archived patch (the unrelated REAL_MATH_EXTERNAL_REAL_RANDOM_RANGE hunk belongs to another lane and must not be interpreted as part of this candidate): + +```diff +diff --git a/source/game/aim_assist.c b/source/game/aim_assist.c +index b5e833a93..cda42c6d7 100644 +--- a/source/game/aim_assist.c ++++ b/source/game/aim_assist.c +@@ -547,6 +547,118 @@ boolean aim_assist( + return FALSE; + } + ++long player_aim_projectile( ++ long player_index, ++ real_point3d const *position, ++ real_vector3d *direction) ++{ ++ long hit_object_index = NONE; ++ struct player_datum *player = player_get(player_index); ++ long aiming_unit_index = unit_get_aiming_unit_index(player->unit_index); ++ ++ match_assert_valid_real_normal3d( ++ "c:\\halo\\SOURCE\\game\\aim_assist.c", ++ 77, ++ direction); ++ match_assert( ++ "c:\\halo\\SOURCE\\game\\aim_assist.c", ++ 79, ++ global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH); ++ global_current_collision_users[global_current_collision_user_depth++] = _collision_user_aim_assist; ++ ++ { ++ short zoom_level = unit_get_zoom_level(aiming_unit_index); ++ struct aim_assist_parameters parameters; ++ ++ if (unit_get_aim_assist_parameters(aiming_unit_index, zoom_level, ¶meters)) ++ { ++ real_point3d camera_position; ++ real_vector3d camera_forward; ++ ++ director_camera_deterministic(player->unit_index, &camera_position, &camera_forward); ++ ++ { ++ real_vector3d aim_direction = *direction; ++ real magnetism_level = 0.f; ++ ++ { ++ struct aim_assist_target target; ++ ++ if (aim_assist( ++ ¶meters, ++ &camera_position, ++ &camera_forward, ++ player->unit_index, ++ (short)player->team_index, ++ &target)) ++ { ++ vector_from_points3d(position, &target.position, &aim_direction); ++ if (normalize3d(&aim_direction) == 0.f) ++ { ++ aim_direction = *direction; ++ } ++ ++ magnetism_level = target.autoaim_level; ++ hit_object_index = target.object_index; ++ } ++ } ++ ++ { ++ struct unit_datum *aiming_unit = unit_get(aiming_unit_index); ++ real camera_to_unit_distance = distance3d(&aiming_unit->object.position, &camera_position); ++ real_vector3d camera_direction = camera_forward; ++ real_vector3d vector; ++ struct collision_result collision; ++ real_vector3d impact_direction; ++ ++ normalize3d(&camera_direction); ++ scale_vector3d(&camera_direction, camera_to_unit_distance, &camera_direction); ++ add_vectors3d( ++ &camera_direction, ++ (real_vector3d const *)&camera_position, ++ (real_vector3d *)&camera_position); ++ scale_vector3d(&camera_forward, 128.f, &vector); ++ collision_test_vector( ++ _collision_test_for_projectiles_flags, ++ &camera_position, ++ &vector, ++ player->unit_index, ++ &collision); ++ ++ vector_from_points3d(position, &collision.point, &impact_direction); ++ { ++ real impact_magnitude = normalize3d(&impact_direction); ++ ++ if (impact_magnitude == 0.f) ++ { ++ impact_direction = *direction; ++ } ++ } ++ ++ fast_normals_interpolate(&impact_direction, &aim_direction, magnetism_level, &vector); ++ pin_normal_to_cone3d( ++ &vector, ++ direction, ++ sine(parameters.deviation_angle), ++ cosine(parameters.deviation_angle), ++ direction); ++ } ++ } ++ } ++ } ++ ++ match_assert( ++ "c:\\halo\\SOURCE\\game\\aim_assist.c", ++ 140, ++ global_current_collision_user_depth > 1); ++ --global_current_collision_user_depth; ++ ++ player->aim_assist_unit_index = hit_object_index; ++ player->aim_assist_timestamp = game_time_get(); ++ ++ return hit_object_index; ++} ++ + long local_player_aim_assist( + short local_player_index, + real *autoaim_level, +diff --git a/source/game/aim_assist.h b/source/game/aim_assist.h +index 0981f4d52..f5570893c 100644 +--- a/source/game/aim_assist.h ++++ b/source/game/aim_assist.h +@@ -59,6 +59,10 @@ boolean aim_assist( + long ignore_object_index, + short ignore_team_index, + struct aim_assist_target *target); ++long player_aim_projectile( ++ long player_index, ++ real_point3d const *position, ++ real_vector3d *direction); + long local_player_aim_assist( + short local_player_index, + real *autoaim_level, +diff --git a/source/math/real_math.h b/source/math/real_math.h +index 68f7673fc..3f467ccce 100644 +--- a/source/math/real_math.h ++++ b/source/math/real_math.h +@@ -560,6 +560,12 @@ void pitch_vectors(real_vector3d *forward, real_vector3d *up, real sine, real co + + void fast_normals_interpolate(real_vector3d const *a, real_vector3d const *b, real t, real_vector3d *result); + void normals_interpolate(real_vector3d const *a, real_vector3d const *b, real t, real_vector3d *result); ++boolean pin_normal_to_cone3d( ++ real_vector3d const *normal, ++ real_vector3d const *direction, ++ real sine, ++ real cosine, ++ real_vector3d *result); + real_vector3d *reflect_vector3d(real_vector3d const *incident, real_vector3d const *normal, real_vector3d *reflection); + real_vector3d *refract_vector3d(real_vector3d const *incident, real_vector3d const *normal, real coefficient_of_refraction, real_vector3d *refraction); + +@@ -1446,12 +1452,18 @@ __inline real real_random( + return real_seed_random(get_global_random_seed_address()); + } + ++#ifdef REAL_MATH_EXTERNAL_REAL_RANDOM_RANGE ++real real_random_range( ++ real lower_bound, ++ real upper_bound); ++#else + __inline real real_random_range( + real lower_bound, + real upper_bound) + { + return real_seed_random_range(get_global_random_seed_address(), lower_bound, upper_bound); + } ++#endif + + __inline real_vector3d *random_direction3d( + real_vector3d *direction) + +``` diff --git a/source/ai/action_flee.c b/source/ai/action_flee.c index 79a33f99..963adc28 100644 --- a/source/ai/action_flee.c +++ b/source/ai/action_flee.c @@ -75,6 +75,15 @@ enum _ai_information_flee = 4, }; +enum +{ + /* Later-build symbols authenticate these meanings; January uses this TU's 3/6/7 layout. */ + _action_flee_primary_priority_facing = 3, + _action_flee_primary_priority_locked_facing = 6, + _action_flee_primary_priority_locked_aiming = 7, + _action_flee_idle_look_type = 4, +}; + /* ---------- macros */ /* ---------- structures */ @@ -436,6 +445,71 @@ void action_flee_update( return; } +void action_flee_control( + long actor_index) +{ + struct actor_datum *actor = actor_get(actor_index); + struct flee_state_data *state_data = &actor->state.action_data.flee; + + if (state_data->panic_type > _actor_panic_none) + { + actor->orders.look.primary_priority = _action_flee_primary_priority_locked_facing; + actor->orders.look.primary_direction.type = _direction_specification_movement; + actor->orders.combat.abort_burst = TRUE; + } + else if (actor->target.target_prop_index != NONE && + prop_get(actor->target.target_prop_index)->visibility > 0) + { + actor->orders.look.primary_priority = _action_flee_primary_priority_locked_aiming; + actor->orders.look.primary_direction.type = _direction_specification_target; + actor->orders.combat.shoot_at_target = TRUE; + } + else if (state_data->flee_prop_index != NONE) + { + actor->orders.look.primary_priority = _action_flee_primary_priority_facing; + actor->orders.look.primary_direction.type = _direction_specification_prop; + actor->orders.look.primary_direction.prop_index = state_data->flee_prop_index; + } + else + { + actor->orders.look.primary_priority = _primary_priority_none; + } + actor->orders.look.idle_look_type = _action_flee_idle_look_type; + + actor->orders.move.panicked = state_data->panic_type > _actor_panic_none; + actor->orders.move.flaming = action_flee_blind_panic(state_data->panic_type); + actor->orders.move.stationary_crouch = TRUE; + actor->orders.move.moving_crouch = FALSE; + actor->orders.move.dive_into_cover = TRUE; + actor->orders.move.emerge_from_cover = FALSE; + + if (state_data->flee_firing_position_index == NONE) + { + actor_move_halt(actor_index); + } + else if (actor->meta.timeslice) + { + if (actor_move_to_firing_position(actor_index, state_data->flee_firing_position_index, NULL)) + { + actor->firing_positions.current_position_index = state_data->flee_firing_position_index; + actor->firing_positions.current_position_found_outside_range = state_data->flee_firing_position_found_randomly; + } + else + { + if (actor->firing_positions.current_position_index != NONE) + { + actor_discard_firing_position(actor_index, actor->firing_positions.current_position_index, FALSE); + actor_move_halt(actor_index); + actor->firing_positions.current_position_index = NONE; + } + state_data->flee_firing_position_index = NONE; + state_data->find_new_flee_position = TRUE; + } + } + + return; +} + static boolean action_flee_at_flee_position( long actor_index) { diff --git a/source/ai/action_vehicle.c b/source/ai/action_vehicle.c index 1b2e88f2..0e89db5e 100644 --- a/source/ai/action_vehicle.c +++ b/source/ai/action_vehicle.c @@ -156,6 +156,19 @@ real_vector2d *negate_vector2d( return result; } +void action_vehicle_begin( + long actor_index) +{ + struct actor_datum *actor = actor_get(actor_index); + struct vehicle_state_data *state_data = &actor->state.action_data.vehicle; + + state_data->stuck_detection_counter = 0; + state_data->stuck_detection_time = game_time_get(); + state_data->stuck_detection_point = actor->input.position.body_position; + + return; +} + void action_vehicle_update( long actor_index) { diff --git a/source/ai/actions.c b/source/ai/actions.c index 6b06aa76..37a43c7f 100644 --- a/source/ai/actions.c +++ b/source/ai/actions.c @@ -659,8 +659,6 @@ void action_wait_update( void action_wait_control( long actor_index); -void action_vehicle_begin( - long actor_index); boolean action_vehicle_perform( long actor_index); void action_vehicle_update( diff --git a/source/ai/actions.h b/source/ai/actions.h index ef8bad63..8841615c 100644 --- a/source/ai/actions.h +++ b/source/ai/actions.h @@ -610,6 +610,8 @@ void actor_conversation_control( /* ---------- prototypes/ACTION_VEHICLE.C */ +void action_vehicle_begin( + long actor_index); void action_vehicle_control( long actor_index); diff --git a/source/ai/ai.c b/source/ai/ai.c index 07340be5..c0eeb870 100644 --- a/source/ai/ai.c +++ b/source/ai/ai.c @@ -226,6 +226,7 @@ symbols in this file: #include "game/game.h" #include "game/game_allegiance.h" #include "game/game_globals.h" +#include "game/players.h" #include "memory/data.h" #include "objects/damage.h" @@ -262,6 +263,26 @@ enum MINIMUM_ACTOR_KILLING_SPREE = 3, MINIMUM_PLAYER_KILLING_SPREE = 5, AI_DESCRIPTION_BUFFER_SIZE = 512, + AI_BSP_TRANSITION_DESCRIPTION_SIZE = 256, + MAXIMUM_NUMBER_OF_UNITS_PER_SWARM = MAXIMUM_UNIT_INDICES_PER_SWARM, +}; + +/* The shared scenario-AI header does not yet own this source-attested enum. */ +enum actor_default_state +{ + actor_default_state_none = 0, + actor_default_state_asleep, + actor_default_state_alert, + actor_default_state_moving_repeat_position, + actor_default_state_moving_loop, + actor_default_state_moving_loop_back_and_forth, + actor_default_state_moving_loop_randomly, + actor_default_state_moving_randomly, + actor_default_state_guarding, + actor_default_state_guarding_at_guard_point, + actor_default_state_searching, + actor_default_state_fleeing, + number_of_actor_default_states, }; enum @@ -1554,6 +1575,254 @@ void ai_flush_spatial_effects( return; } +void ai_disconnect_from_structure_bsp( + void) +{ + struct scenario *scenario = global_scenario_get(); + short encounter_index; + + for (encounter_index = 0; + encounter_index < scenario->ai_encounters.count; + encounter_index++) + { + struct encounter_datum *encounter = encounter_get(encounter_index); + + if (encounter->active && encounter->current_count > 0) + { + struct encounter_actor_iterator iterator; + struct actor_datum *actor; + + encounter_actor_iterator_new(&iterator, encounter_index); + actor = encounter_actor_iterator_next(&iterator); + + while (actor) + { + char actor_description[AI_BSP_TRANSITION_DESCRIPTION_SIZE]; + boolean disconnect = FALSE; + + ai_debug_describe_actor( + iterator.index, + NONE, + TRUE, + actor_description, + sizeof(actor_description)); + + if (actor->target.target_prop_index == NONE || + actor->target.target_type < _actor_target_uninspected_orphan) + { + if (!game_team_is_enemy(actor->meta.team_index, _game_team_player)) + { + struct prop_iterator prop_iterator; + struct prop_datum *prop; + + prop_iterator_new(&prop_iterator, iterator.index); + prop = prop_iterator_next(&prop_iterator); + + while (prop) + { + if (prop->player && + (prop->visibility >= 2 || prop->distance < 3.0f)) + { + disconnect = TRUE; + } + + prop = prop_iterator_next(&prop_iterator); + } + } + } + else + { + struct prop_datum *target_prop = prop_get(actor->target.target_prop_index); + + if (target_prop->state >= _prop_state_uninspected_orphan && + target_prop->state <= _prop_state_inspected_orphan) + { + match_assert( + "c:\\halo\\SOURCE\\ai\\ai.c", + 0x8B9, + target_prop->parent_prop_index != NONE); + target_prop = prop_get(target_prop->parent_prop_index); + } + + if (target_prop->player && + actor->state.uncertain_combat_timer != NONE && + actor->state.uncertain_combat_timer < 90 && + target_prop->distance < 10.0f) + { + disconnect = TRUE; + } + } + + if (disconnect && actor->meta.swarm) + { + if (actor->meta.swarm_cache_index == NONE) + { + disconnect = FALSE; + } + else + { + struct swarm_datum *swarm = swarm_get(actor->meta.swarm_cache_index); + unsigned long const *combined_pvs = players_get_combined_pvs(); + long components_outside_pvs[MAXIMUM_NUMBER_OF_UNITS_PER_SWARM]; + short components_outside_pvs_count = 0; + short component_index; + + for (component_index = 0; + component_index < swarm->unit_count; + component_index++) + { + long unit_index = swarm->unit_indices[component_index]; + long ultimate_parent_index = object_get_ultimate_parent(unit_index); + short cluster_index = object_get(ultimate_parent_index)->object.location.cluster_index; + + if (cluster_index == NONE || + !BIT_VECTOR_TEST_FLAG(combined_pvs, cluster_index)) + { + match_assert( + "c:\\halo\\SOURCE\\ai\\ai.c", + 0x8EB, + components_outside_pvs_count < MAXIMUM_NUMBER_OF_UNITS_PER_SWARM); + components_outside_pvs[components_outside_pvs_count++] = unit_index; + } + } + + if (components_outside_pvs_count == 0) + { + if (ai_debug.print_bsp_transition) + { + error( + _error_silent, + "%s: all swarm inside PVS, transition unchanged", + actor_description); + } + } + else + { + short unit_count = swarm->unit_count; + + if (components_outside_pvs_count == unit_count) + { + if (ai_debug.print_bsp_transition) + { + error( + _error_silent, + "%s: no units inside PVS, do not transition", + actor_description); + } + + disconnect = FALSE; + } + else + { + short components_reattached_count = 0; + short outside_component_index; + + for (outside_component_index = 0; + outside_component_index < components_outside_pvs_count; + outside_component_index++) + { + long unit_index = components_outside_pvs[outside_component_index]; + + actor_swarm_detach_from_unit(iterator.index, unit_index); + if (actor_create_for_unit( + TRUE, + unit_index, + actor->meta.variant_definition_index, + actor->meta.encounter_index, + actor->meta.squad_index, + FALSE, + iterator.index, + FALSE, + actor_default_state_alert, + actor_default_state_none, + NONE, + 0) == NONE) + { + object_delete(unit_index); + } + else + { + components_reattached_count++; + } + } + + if (ai_debug.print_bsp_transition) + { + error( + _error_silent, + "%s: %d of %d units outside PVS, reattached %d%s", + actor_description, + components_outside_pvs_count, + unit_count, + components_reattached_count, + components_reattached_count < components_outside_pvs_count + ? " (deleted some)" + : ""); + } + } + } + } + } + + if (disconnect) + { + if (ai_debug.print_bsp_transition) + { + error( + _error_silent, + "%s: disconnect and transition to new bsp", + actor_description); + } + + actor->meta.disconnected_encounter_index = encounter_index; + actor->meta.disconnected_squad_index = actor->meta.squad_index; + actor_flush_position_indices(iterator.index); + encounter_detach_actor(iterator.index, FALSE); + encounterless_attach_actor(iterator.index); + } + + actor = encounter_actor_iterator_next(&iterator); + } + } + + encounter_force_deactivate(encounter_index); + } + + { + long actor_index = ai_globals->first_encounterless_actor_index; + + while (actor_index != NONE) + { + struct actor_datum *actor = actor_get(actor_index); + long next_actor_index = actor->meta.next_actor_index; + struct prop_iterator prop_iterator; + struct prop_datum *prop; + + match_assert( + "c:\\halo\\SOURCE\\ai\\ai.c", + 0x94A, + actor->meta.encounterless); + actor_flush_structure_indices(actor_index); + + prop_iterator_new(&prop_iterator, actor_index); + prop = prop_iterator_next(&prop_iterator); + + while (prop) + { + prop->body_location.cluster_index = NONE; + prop->body_location.leaf_index = NONE; + prop->pathfinding_surface_index = NONE; + + prop = prop_iterator_next(&prop_iterator); + } + + actor_index = next_actor_index; + } + } + + return; +} + void ai_reconnect_to_structure_bsp( void) { diff --git a/source/ai/ai.h b/source/ai/ai.h index a05b96cd..619ce378 100644 --- a/source/ai/ai.h +++ b/source/ai/ai.h @@ -103,6 +103,10 @@ void ai_place( void); void ai_update( void); +void ai_disconnect_from_structure_bsp( + void); +void ai_reconnect_to_structure_bsp( + void); void ai_globals_dialogue_triggers_enabled( boolean enabled); void ai_update_team_status( diff --git a/source/ai/ai_script.c b/source/ai/ai_script.c index 872168fa..f7581672 100644 --- a/source/ai/ai_script.c +++ b/source/ai/ai_script.c @@ -465,8 +465,10 @@ symbols in this file: #include "hs/hs.h" #include "hs/object_lists.h" #include "memory/data.h" +#include "objects/objects.h" #include "scenario/scenario.h" #include "scenario/scenario_definitions.h" +#include "units/bipeds.h" #include "units/units.h" #include "units/vehicle_scripting.h" @@ -645,6 +647,9 @@ typedef char ai_script_vehicle_candidate_state_offset_assert[ static long ai_scripting_assess_status( long actor_index); +static void ai_scripting_teleport_starting_location_private( + long ai_reference, + boolean only_if_unsupported); static void ai_scripting_kill_internal( long ai_reference, boolean silent); @@ -3397,6 +3402,102 @@ void ai_scripting_prefer_target( return; } +static void ai_scripting_teleport_starting_location_private( + long ai_reference, + boolean only_if_unsupported) +{ + struct ai_script_actor_reference_iterator iterator; + struct actor_datum *actor; + + ai_index_actor_iterator_new(ai_reference, &iterator); + for (actor = ai_index_actor_iterator_next(&iterator); + actor; + actor = ai_index_actor_iterator_next(&iterator)) + { + if (actor->meta.unit_index == NONE) + continue; + if (only_if_unsupported && + (actor->input.vehicle_index != NONE || + biped_approximate_surface_index(actor->meta.unit_index, NULL) != NONE)) + { + continue; + } + if (actor->meta.encounter_index == NONE) + continue; + + { + struct encounter_definition *encounter = TAG_BLOCK_GET_ELEMENT( + &global_scenario_get()->ai_encounters, + DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index), + struct encounter_definition); + struct squad_definition *squad = TAG_BLOCK_GET_ELEMENT( + &encounter->squads, + actor->meta.squad_index, + struct squad_definition); + short starting_location_index = encounter_get_actor_starting_location( + actor->meta.encounter_index, + actor->meta.squad_index, + TRUE); + + if (starting_location_index != NONE) + { + struct actor_starting_location const *starting_location = TAG_BLOCK_GET_ELEMENT( + &squad->starting_locations, + starting_location_index, + struct actor_starting_location); + real_vector3d forward; + + vector3d_from_angle(&forward, starting_location->facing); + object_set_position(actor->meta.unit_index, &starting_location->position, &forward, NULL); + object_reset(actor->meta.unit_index); + actor_move_halt(iterator.actor_index); + } + } + } + + return; +} + +void ai_scripting_teleport_starting_location_if_unsupported( + long ai_reference) +{ + if (ai_debug.print_scripting) + { + char ai_name[256]; + + ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name)); + error( + _error_silent, + "%s: ai_teleport_starting_location_if_unsupported %s", + hs_runtime_get_executing_thread_name(), + ai_name); + } + + ai_scripting_teleport_starting_location_private(ai_reference, TRUE); + + return; +} + +void ai_scripting_teleport_starting_location( + long ai_reference) +{ + if (ai_debug.print_scripting) + { + char ai_name[256]; + + ai_index_to_string(ai_reference, global_scenario_get(), ai_name, sizeof(ai_name)); + error( + _error_silent, + "%s: ai_teleport_starting_location %s", + hs_runtime_get_executing_thread_name(), + ai_name); + } + + ai_scripting_teleport_starting_location_private(ai_reference, FALSE); + + return; +} + void ai_scripting_command_list_advance( long ai_reference) { diff --git a/source/ai/ai_script.h b/source/ai/ai_script.h index 61f901da..3be1702e 100644 --- a/source/ai/ai_script.h +++ b/source/ai/ai_script.h @@ -220,6 +220,10 @@ void ai_scripting_ignore( void ai_scripting_prefer_target( long object_list_index, boolean prefer); +void ai_scripting_teleport_starting_location( + long ai_reference); +void ai_scripting_teleport_starting_location_if_unsupported( + long ai_reference); void ai_scripting_command_list_advance( long ai_reference); void ai_scripting_erase( diff --git a/source/ai/path_structure_bsp.c b/source/ai/path_structure_bsp.c index 66199200..86c450a0 100644 --- a/source/ai/path_structure_bsp.c +++ b/source/ai/path_structure_bsp.c @@ -231,6 +231,232 @@ boolean structure_surfaces_are_equivalent( return result; } +boolean structure_test_line2d( + struct structure_bsp const *structure, + boolean ignore_broken_surfaces, + real_point2d const *p0, + long p0_surface_index, + real_point2d const *p1, + long p1_surface_index, + struct path_collision_result *result) +{ + struct collision_bsp const *bsp; + byte const *pathfinding_surfaces; + long const *breakable_surface_flags; + boolean recursed; + boolean reached_target; + long surface_index; + real_vector2d delta; + + bsp = TAG_BLOCK_GET_ELEMENT(&structure->collision_bsp, 0, struct collision_bsp); + pathfinding_surfaces = structure->pathfinding_surfaces.address; + breakable_surface_flags = (long const *)breakable_surface_flags_get(); + surface_index = p0_surface_index; + recursed = FALSE; + match_assert("c:\\halo\\SOURCE\\ai\\path_structure_bsp.c", 217, result); + + delta.i = p1->x - p0->x; + delta.j = p1->y - p0->y; + + for (;;) + { + struct collision_surface const *surface; + long edge_index; + short edge_count; + boolean crossed_any; + real_point3d point_in_surface; + + surface = TAG_BLOCK_GET_ELEMENT(&bsp->surfaces, surface_index, struct collision_surface); + edge_index = surface->first_edge_index; + point_in_surface = *global_origin3d; + edge_count = 0; + crossed_any = FALSE; + reached_target = FALSE; + + do + { + struct collision_edge const *edge; + boolean on_right_side; + struct collision_vertex const *vertex_a; + struct collision_vertex const *vertex_b; + real_vector2d a_to_p0; + real_vector2d edge_vector; + real_vector2d b_to_p1; + real_vector2d b_to_p0; + + edge = TAG_BLOCK_GET_ELEMENT(&bsp->edges, edge_index, struct collision_edge); + on_right_side = surface_index == edge->surface_indices[1]; + vertex_a = TAG_BLOCK_GET_ELEMENT( + &bsp->vertices, + edge->vertex_indices[!on_right_side], + struct collision_vertex); + vertex_b = TAG_BLOCK_GET_ELEMENT( + &bsp->vertices, + edge->vertex_indices[on_right_side], + struct collision_vertex); + + edge_vector.i = vertex_b->point.x - vertex_a->point.x; + edge_vector.j = vertex_b->point.y - vertex_a->point.y; + b_to_p1.i = p1->x - vertex_a->point.x; + b_to_p1.j = p1->y - vertex_a->point.y; + b_to_p0.i = vertex_a->point.x - p0->x; + b_to_p0.j = vertex_a->point.y - p0->y; + a_to_p0.i = vertex_b->point.x - p0->x; + a_to_p0.j = vertex_b->point.y - p0->y; + + if (edge->surface_indices[!on_right_side] == p1_surface_index) + reached_target = TRUE; + + point_in_surface.x += vertex_a->point.x; + point_in_surface.y += vertex_a->point.y; + point_in_surface.z += vertex_a->point.z; + edge_count++; + + if (cross_product2d(&edge_vector, &b_to_p1) > 0.0f) + { + crossed_any = TRUE; + if (cross_product2d(&delta, &b_to_p0) > 0.0f && + cross_product2d(&a_to_p0, &delta) > 0.0f) + { + long neighbor_surface_index; + byte pathfinding_surface_flags; + boolean passable; + + neighbor_surface_index = edge->surface_indices[!on_right_side]; + pathfinding_surface_flags = pathfinding_surfaces[neighbor_surface_index]; + passable = TEST_FLAG( + pathfinding_surface_flags, + _pathfinding_surface_walkable_bit); + if (!ignore_broken_surfaces && + passable && + TEST_FLAG( + pathfinding_surface_flags, + _pathfinding_surface_breakable_bit)) + { + struct collision_surface const *collision_surface; + + collision_surface = TAG_BLOCK_GET_ELEMENT( + &bsp->surfaces, + neighbor_surface_index, + struct collision_surface); + match_assert( + "c:\\halo\\SOURCE\\ai\\path_structure_bsp.c", + 274, + TEST_FLAG(collision_surface->flags, _collision_surface_breakable_bit)); + passable = BIT_VECTOR_TEST_FLAG( + breakable_surface_flags, + collision_surface->breakable_surface_index); + } + + if (passable) + { + surface_index = neighbor_surface_index; + goto continue_outer; + } + + { + real edge_length; + real t; + real_point2d hit_point; + + edge_length = magnitude2d(&edge_vector); + t = (cross_product2d(&edge_vector, &b_to_p0) - + edge_length * (1.0f / 128.0f)) / + cross_product2d(&edge_vector, &delta); + hit_point.x = delta.i * t + p0->x; + hit_point.y = delta.j * t + p0->y; + collision_surface_project_point2d( + bsp, + surface_index, + _z, + TRUE, + &hit_point, + &result->point); + result->surface_index = surface_index; + result->edge_index = edge_index; + result->collision = TRUE; + result->t = t; + return TRUE; + } + } + } + + edge_index = edge->edge_indices[on_right_side]; + } + while (edge_index != surface->first_edge_index); + + if (crossed_any) + { + real scale; + + match_assert( + "c:\\halo\\SOURCE\\ai\\path_structure_bsp.c", + 316, + surface_index >= 0 && surface_index < structure->pathfinding_surfaces.count); + scale = 1.0f / edge_count; + point_in_surface.x *= scale; + point_in_surface.y *= scale; + + if (!recursed && pathfinding_surfaces[surface_index]) + { + struct path_collision_result p0_result; + + if (!structure_test_line2d( + structure, + ignore_broken_surfaces, + (real_point2d const *)&point_in_surface, + surface_index, + p0, + NONE, + &p0_result)) + { + recursed = TRUE; + surface_index = p0_result.surface_index; + continue; + } + } + + goto blocked_at_start; + } + + break; + +continue_outer: + ; + } + + if (surface_index == p1_surface_index || reached_target || p1_surface_index == NONE) + { + collision_surface_project_point2d( + bsp, + surface_index, + _z, + TRUE, + p1, + &result->point); + result->surface_index = surface_index; + result->edge_index = NONE; + result->collision = FALSE; + result->t = 1.0f; + return FALSE; + } + +blocked_at_start: + collision_surface_project_point2d( + bsp, + p0_surface_index, + _z, + TRUE, + p0, + &result->point); + result->surface_index = NONE; + result->edge_index = NONE; + result->collision = TRUE; + result->t = 0.0f; + + return TRUE; +} + boolean clip_empty_interval_by_solid_interval( real *empty_t0, real *empty_t1, diff --git a/source/bungie_net/common/key_agreement.c b/source/bungie_net/common/key_agreement.c index 595b6a29..c73865d6 100644 --- a/source/bungie_net/common/key_agreement.c +++ b/source/bungie_net/common/key_agreement.c @@ -207,6 +207,7 @@ boolean initiate_key_exchange( struct public_key *prime, struct public_key *secret) { + boolean success = TRUE; struct public_key generator; word *message; short message_size; @@ -220,14 +221,18 @@ boolean initiate_key_exchange( key_agreement_message_buffer, sizeof(key_agreement_message_buffer)); if (!message) - return FALSE; + { + success = FALSE; + } + else + { + message_size = GET_MESSAGE_SIZE(*message); + byte_swap_message_header(message, _byte_order_network); + if (write_endpoint(endpoint, message, message_size) != message_size) + success = FALSE; + } - message_size = GET_MESSAGE_SIZE(*message); - byte_swap_message_header(message, _byte_order_network); - if (write_endpoint(endpoint, message, message_size) != message_size) - return FALSE; - - return TRUE; + return success; } boolean complete_key_exchange( @@ -244,68 +249,69 @@ boolean complete_key_exchange( union key_agreement_packet_value packet_type; union key_agreement_packet_value packet_version; word *message; - word message_size; - enum message_type message_type; + short message_size; + byte message_type; packet_version.value = KEY_AGREEMENT_PACKET_VERSION; match_assert(KEY_AGREEMENT_FILE, 0x105, msgptr && prime && secret && private_key); message_size = GET_MESSAGE_SIZE(*msgptr); + encoded_packet_size.value = (word)message_size - sizeof(word); message_type = GET_MESSAGE_TYPE(*msgptr); - encoded_packet_size.value = message_size - sizeof(word); - if (message_type != _message_type_packet) - return FALSE; - packet_type.encoded = key_agreement_get_packet_type(msgptr); - - switch ((enum key_agreement_packet_type)packet_type.encoded) + if (message_type == _message_type_packet) { - case _key_agreement_packet_type_initiate: + packet_type.value = key_agreement_get_packet_type(msgptr); + + switch ((enum key_agreement_packet_type)packet_type.encoded) { - if (!key_agreement_decode_packet( - &initiate_packet, - msgptr + 1, - &encoded_packet_size.encoded, - &packet_type.encoded, - &packet_version.encoded, - 0)) + case _key_agreement_packet_type_initiate: { - return FALSE; + if (!key_agreement_decode_packet( + &initiate_packet, + msgptr + 1, + &encoded_packet_size.encoded, + &packet_type.encoded, + &packet_version.encoded, + 0)) + { + return FALSE; + } + + secret->dwords[0] = randomrange(0xFF, initiate_packet.prime.dwords[0] - 2); + secret->dwords[1] = randomrange(0xFF, initiate_packet.prime.dwords[1] - 2); + generate_public_key(&initiate_packet.prime, secret, &initiate_packet.generator, &key); + message = build_finalize_key_agreement_message( + &key, + key_agreement_message_buffer, + sizeof(key_agreement_message_buffer)); + if (!message) + return FALSE; + + message_size = GET_MESSAGE_SIZE(*message); + byte_swap_message_header(message, _byte_order_network); + if (write_endpoint(endpoint, message, message_size) != message_size) + return FALSE; + + generate_private_key(&initiate_packet.key, &initiate_packet.prime, secret, private_key); + return TRUE; } - secret->dwords[0] = randomrange(0xFF, initiate_packet.prime.dwords[0] - 2); - secret->dwords[1] = randomrange(0xFF, initiate_packet.prime.dwords[1] - 2); - generate_public_key(&initiate_packet.prime, secret, &initiate_packet.generator, &key); - message = build_finalize_key_agreement_message( - &key, - key_agreement_message_buffer, - sizeof(key_agreement_message_buffer)); - if (!message) - return FALSE; - - message_size = GET_MESSAGE_SIZE(*message); - byte_swap_message_header(message, _byte_order_network); - if (write_endpoint(endpoint, message, message_size) != message_size) - return FALSE; - - generate_private_key(&initiate_packet.key, &initiate_packet.prime, secret, private_key); - return TRUE; - } - - case _key_agreement_packet_type_finalize: - { - if (!key_agreement_decode_packet( - &finalize_packet, - msgptr + 1, - &encoded_packet_size.encoded, - &packet_type.encoded, - &packet_version.encoded, - 0)) + case _key_agreement_packet_type_finalize: { - return FALSE; - } + if (!key_agreement_decode_packet( + &finalize_packet, + msgptr + 1, + &encoded_packet_size.encoded, + &packet_type.encoded, + &packet_version.encoded, + 0)) + { + return FALSE; + } - generate_private_key(&finalize_packet.key, prime, secret, private_key); - return TRUE; + generate_private_key(&finalize_packet.key, prime, secret, private_key); + return TRUE; + } } } @@ -325,7 +331,7 @@ void initialize_key_agreement_packets( static char key_agreement_get_packet_type( word const *msgptr) { - enum message_type message_type; + byte message_type; word message_size = GET_MESSAGE_SIZE(*msgptr); match_assert( diff --git a/source/bungie_net/network/transport_endpoint_set_winsock.c b/source/bungie_net/network/transport_endpoint_set_winsock.c index d3cd5cf2..fd44f685 100644 --- a/source/bungie_net/network/transport_endpoint_set_winsock.c +++ b/source/bungie_net/network/transport_endpoint_set_winsock.c @@ -107,6 +107,7 @@ symbols in this file: #include "cseries/errors.h" #include "bungie_net/network/transport.h" #include "bungie_net/network/transport_endpoint_winsock.h" +#include "memory/byte_swapping.h" /* ---------- constants */ @@ -712,4 +713,25 @@ boolean transport_is_nonce( return transport_nonce_is_equal(src, global_nonce); } +void transport_client_start( + XNADDR const *xnaddr, + XNKEY const *key, + XNKID const *key_id, + word port, + struct transport_address *address) +{ + IN_ADDR in_addr; + + transport_client_stop(); + transport_push_key(key, key_id); + XNetXnAddrToInAddr(xnaddr, key_id, &in_addr); + address->address.long_words[0] = SWAP4(in_addr.s_addr); + address->address_length = IPV4_ADDRESS_LENGTH; + address->port = port; + address->address_type = 0; + global_client_active = TRUE; + + return; +} + /* ---------- private code */ diff --git a/source/bungie_net/network/transport_endpoint_winsock.h b/source/bungie_net/network/transport_endpoint_winsock.h index c6680c47..a16ab12a 100644 --- a/source/bungie_net/network/transport_endpoint_winsock.h +++ b/source/bungie_net/network/transport_endpoint_winsock.h @@ -23,6 +23,12 @@ header included in hcex build. void transport_client_stop( void); +void transport_client_start( + XNADDR const *xnaddr, + XNKEY const *key, + XNKID const *key_id, + word port, + struct transport_address *address); short transport_server_initialize( void); short transport_server_terminate( diff --git a/source/effects/decals.c b/source/effects/decals.c index 60401ca8..4136ae70 100644 --- a/source/effects/decals.c +++ b/source/effects/decals.c @@ -460,7 +460,7 @@ void decal_clip_to_surface( short *surface_queue_write_index, long *deviant_surface_list, short *deviant_surface_count); -long decal_insert( +static long decal_insert( long cache_index, short cluster_index, short layer, @@ -473,6 +473,7 @@ extern struct data_array *global_decal_data; boolean decals_enabled= TRUE; static boolean decal_locked_count_reported; +static boolean decal_insert_locked_count_reported; static struct decal_globals *decal_globals; static struct decal_geometry decal_geometry; static boolean decals_unlock_locked_count_reported; @@ -2256,7 +2257,115 @@ static void decal_reinsert( return; } +static long decal_insert( + long cache_index, + short cluster_index, + short layer, + long next_decal_index, + boolean permanent) +{ + struct data_iterator iterator; + long decal_index = datum_new_at_index(global_decal_data, cache_index); + match_assert("c:\\halo\\SOURCE\\effects\\decals.c", 479, + cluster_index >= 0 && cluster_index < MAXIMUM_CLUSTERS_PER_STRUCTURE); + match_assert("c:\\halo\\SOURCE\\effects\\decals.c", 480, + layer >= 0 && layer < NUMBER_OF_DECAL_LAYERS); + + if (decal_index != NONE) + { + struct decal_datum *new_decal = DECAL_GET(decal_index); + + if (permanent) + { + new_decal->flags = FLAG(_decal_permanent_bit); + decal_globals->permanent_count++; + } + else if (100 * seed_random(get_global_local_random_seed_address()) < 655350) + { + new_decal->flags = FLAG(_decal_locked_bit); + decal_globals->locked_count++; + + if (decal_globals->locked_count > 512) + { + short restart_count = 0; + + data_iterator_new(&iterator, global_decal_data); + while (decal_globals->locked_count > 256) + { + struct decal_datum *decal = data_iterator_next(&iterator); + + if (decal) + { + if (TEST_FLAG(decal->flags, _decal_locked_bit) && + (100 * seed_random(get_global_local_random_seed_address()) < 2686935 || + decal->cluster_index == NONE)) + { + SET_FLAG(decal->flags, _decal_locked_bit, FALSE); + decal_globals->locked_count--; + } + } + else + { + data_iterator_new(&iterator, global_decal_data); + if (++restart_count >= 100) + { + error( + _error_silent, + "### ERROR decals: failed to unlock decals during insert -- tell Bernie!!"); + return NONE; + } + } + } + + if (decal_globals->locked_count < 0 && !decal_insert_locked_count_reported) + { + error( + _error_silent, + "### ERROR decals: locked count is invalid (#%d) -- tell Bernie!!", + decal_globals->locked_count); + decal_insert_locked_count_reported = TRUE; + } + } + } + else + { + new_decal->flags = 0; + } + if (next_decal_index != NONE) + { + struct decal_datum *next_decal = DECAL_GET(next_decal_index); + + match_assert("c:\\halo\\SOURCE\\effects\\decals.c", 556, + next_decal->cluster_index == cluster_index); + + if (next_decal->previous_decal_index != NONE) + { + DECAL_GET(next_decal->previous_decal_index)->next_decal_index = decal_index; + } + else + { + decal_set_first_decal_index(cluster_index, layer, decal_index); + } + + next_decal->previous_decal_index = decal_index; + new_decal->previous_decal_index = decal_index; + new_decal->next_decal_index = next_decal_index; + new_decal->cluster_index = cluster_index; + new_decal->layer = layer; + } + else + { + decal_reinsert(decal_index, cluster_index, layer); + } + } + else + { + error(_error_silent, "### ERROR failed to insert decal"); + } + + return decal_index; +} static void decal_set_first_decal_index( short cluster_index, diff --git a/source/game/game.c b/source/game/game.c index dc1c10aa..96e1c3dc 100644 --- a/source/game/game.c +++ b/source/game/game.c @@ -637,7 +637,7 @@ boolean game_safe_to_save( { if (debug_game_save) console_warning("not safe to save: vehicle_moving_near_any_player"); - return FALSE; + safe = FALSE; } return safe; diff --git a/source/game/game_statistics.c b/source/game/game_statistics.c index 09bc1c41..970628e6 100644 --- a/source/game/game_statistics.c +++ b/source/game/game_statistics.c @@ -20,6 +20,7 @@ symbols in this file: #include "real_math.h" #include "game/game_allegiance.h" #include "game/game_engine.h" +#include "game/game_statistics_internal.h" #include "game/players.h" #include "memory/data.h" #include "units/units.h" diff --git a/source/game/game_statistics_internal.h b/source/game/game_statistics_internal.h new file mode 100644 index 00000000..ca04d30c --- /dev/null +++ b/source/game/game_statistics_internal.h @@ -0,0 +1,24 @@ +/* +GAME_STATISTICS_INTERNAL.H + +Narrow cross-translation-unit interface owned by GAME_STATISTICS.C. +*/ + +#ifndef __GAME_STATISTICS_INTERNAL_H +#define __GAME_STATISTICS_INTERNAL_H +#pragma once + +/* ---------- headers */ + +#include "cseries/cseries.h" + +/* ---------- prototypes/GAME_STATISTICS.C */ + +void game_statistics_record_damage( + long object_index, + real damage, + long owner_player_index, + long owner_object_index, + short owner_team_index); + +#endif // __GAME_STATISTICS_INTERNAL_H diff --git a/source/hs/hs.c b/source/hs/hs.c index ba0af690..23c46adc 100644 --- a/source/hs/hs.c +++ b/source/hs/hs.c @@ -2787,6 +2787,7 @@ symbols in this file: #include "scenario/scenario_definitions.h" #include "ai/ai_scenario_definitions.h" #include "ai/ai_profile.h" +#include "ai/ai_script.h" #include "cutscene/recorded_animation_definitions.h" #include "cutscene/recorded_animations.h" #include "devices/devices.h" @@ -4189,10 +4190,6 @@ void ai_scripting_ignore( void ai_scripting_prefer_target( long ai_index, boolean prefer); -void ai_scripting_teleport_starting_location( - long ai_index); -void ai_scripting_teleport_starting_location_if_unsupported( - long ai_index); void ai_scripting_renew( long ai_index); void ai_scripting_try_to_fight_nothing( diff --git a/source/interface/hud_draw.c b/source/interface/hud_draw.c index 09c1b050..168b2060 100644 --- a/source/interface/hud_draw.c +++ b/source/interface/hud_draw.c @@ -84,7 +84,11 @@ symbols in this file: #include "cseries/cseries.h" #include "math/real_math.h" +#include "bitmaps/bitmaps.h" #include "bitmaps/bitmap_group.h" +#include "cache/texture_cache.h" +#include "effects/particles.h" +#include "game/game.h" #include "game/players.h" #include "interface/hud_definitions.h" #include "interface/hud_draw.h" @@ -105,6 +109,17 @@ enum _hud_dont_scale_offset_bit = 0, }; +enum hud_flash_flags +{ + _hud_flash_reverse_colors_bit = 0, +}; + +enum hud_weapon_overlay_flags +{ + _hud_overlay_flashes_bit = 0, + _hud_overlay_runtime_invalid_bit, +}; + enum { _shader_framebuffer_blend_function_alpha_multiply_add = 7, @@ -119,8 +134,29 @@ enum match_vassert("c:\\halo\\SOURCE\\interface\\hud_draw.c", line, corrupt_index==NONE, csprintf(temporary, "corrupt stack at %d!", corrupt_index)); \ } +#define _hud_anchor_right_bit 0 +#define _hud_anchor_bottom_bit 1 + /* ---------- structures */ +struct weapon_hud_overlay_item +{ + struct hud_placement_definition placement; + struct hud_color_definition colors; + short frame_rate; + short pad; + short sequence_index; + short type; + long flags; + long unused[14]; +}; + +struct weapon_hud_overlay_definition +{ + struct tag_reference bitmap; + struct tag_block items; +}; + /* ---------- prototypes */ static real_rectangle2d const *get_sprite_clip_rect( @@ -269,12 +305,319 @@ pixel32 real_alpha_intensity_to_pixel32( return real_argb_color_to_pixel32(&color); } +void hud_calculate_point( + short local_player_index, + struct hud_absolute_placement_definition const *absolute_placement, + struct hud_placement_definition const *placement, + struct bitmap_data const *bitmap_data, + boolean in_multiplayer, + real override_scale, + point2d *result) +{ + long return_eip = get_return_eip(); + long stack_buffer[STACK_BUFFER_LENGTH]; + real_point2d point; + real scale; + short corner; + + csmemset(stack_buffer, 0x62, sizeof(stack_buffer)); + + if (in_multiplayer && override_scale != 0.0f) + { + scale = override_scale; + } + else + { + scale = hud_globals_get_scale(in_multiplayer); + } + + match_assert( + "c:\\halo\\SOURCE\\interface\\hud_draw.c", + 125, + render.local_player_index==local_player_index); + match_assert( + "c:\\halo\\SOURCE\\interface\\hud_draw.c", + 126, + absolute_placement); + match_assert( + "c:\\halo\\SOURCE\\interface\\hud_draw.c", + 127, + placement); + + corner = absolute_placement->corner; + if (corner < _hud_anchor_center) + { + point.x = placement->offset.x * + (TEST_FLAG(corner, _hud_anchor_right_bit) ? -1 : 1) * scale + + render.camera.window_bounds.v[ + ((corner & FLAG(_hud_anchor_right_bit)) << 1) | + FLAG(_hud_anchor_right_bit)] - + render.camera.viewport_bounds.x0; + point.y = placement->offset.y * + (TEST_FLAG(corner, _hud_anchor_bottom_bit) ? -1 : 1) * scale + + render.camera.window_bounds.v[corner & FLAG(_hud_anchor_bottom_bit)] - + render.camera.viewport_bounds.y0; + } + else + { + point2d window_center; + + window_center.x = (short)( + (render.camera.window_bounds.x1 + render.camera.window_bounds.x0) / 2); + window_center.y = (short)( + (render.camera.window_bounds.y1 + render.camera.window_bounds.y0) / 2); + + point.x = window_center.x - render.camera.viewport_bounds.x0 + + placement->offset.x * scale; + point.y = window_center.y - render.camera.viewport_bounds.y0 + + placement->offset.y * scale; + } + + if (bitmap_data) + { + switch (corner) + { + case _hud_anchor_top_left: + point.x += bitmap_data->registration_point_x * scale; + point.y += bitmap_data->registration_point_y * scale; + break; + + case _hud_anchor_top_right: + point.x += (bitmap_data->registration_point_x - bitmap_data->width) * scale; + point.y += bitmap_data->registration_point_y * scale; + break; + + case _hud_anchor_bottom_left: + point.x += bitmap_data->registration_point_x * scale; + point.y += (bitmap_data->registration_point_y - bitmap_data->height) * scale; + break; + + case _hud_anchor_bottom_right: + point.x += (bitmap_data->registration_point_x - bitmap_data->width) * scale; + point.y += (bitmap_data->registration_point_y - bitmap_data->height) * scale; + break; + + case _hud_anchor_center: + point.x += (bitmap_data->registration_point_x + bitmap_data->width / 2) * scale; + point.y += (bitmap_data->registration_point_y + bitmap_data->width / 2) * scale; + break; + + default: + match_assert( + "c:\\halo\\SOURCE\\interface\\hud_draw.c", + 174, + !"unreachable"); + break; + } + } + + result->x = (short)fast_ftol(point.x); + result->y = (short)fast_ftol(point.y); + + hud_draw_stack_buffer_check(181); + + return; +} + long get_flash_duration( struct hud_color_definition const *hud_color) { return fast_ftol(hud_color->flash_period * 30.0f); } +pixel32 get_flash_color( + struct hud_color_definition const *hud_color, + long reference_value) +{ + long return_eip = get_return_eip(); + long stack_buffer[STACK_BUFFER_LENGTH]; + real_argb_color result; + real_argb_color base_color; + real_argb_color flash_color; + real flash_phase; + + csmemset(stack_buffer, 0x62, sizeof(stack_buffer)); + + flash_phase = (real)fmod( + (game_time_get() - reference_value) * (1.0f / TICKS_PER_SECOND), + hud_color->flash_period); + pixel32_to_real_argb_color(hud_color->color, &base_color); + pixel32_to_real_argb_color(hud_color->flash_color, &flash_color); + + if (flash_phase < hud_color->number_of_flashes * + (hud_color->flash_delay + hud_color->flash_length)) + { + flash_phase = (real)fmod( + flash_phase, + hud_color->flash_delay + hud_color->flash_length); + + if (!reference_value) + { + if (TEST_FLAG( + hud_color->flash_flags, + _hud_flash_reverse_colors_bit)) + { + result = base_color; + } + else + { + result = flash_color; + } + } + else if (flash_phase < hud_color->flash_length) + { + real fraction = square_root( + PIN( + (real)(1.0 - + (cos(flash_phase / hud_color->flash_length * 6.283f) + 1.0) * 0.5), + 0.0f, + 1.0f)); + + if (TEST_FLAG( + hud_color->flash_flags, + _hud_flash_reverse_colors_bit)) + { + vectors_interpolate( + (real_vector3d const *)&flash_color.rgb, + (real_vector3d const *)&base_color.rgb, + fraction, + (real_vector3d *)&result.rgb); + scalars_interpolate( + flash_color.alpha, + base_color.alpha, + fraction, + &result.alpha); + } + else + { + vectors_interpolate( + (real_vector3d const *)&base_color.rgb, + (real_vector3d const *)&flash_color.rgb, + fraction, + (real_vector3d *)&result.rgb); + scalars_interpolate( + base_color.alpha, + flash_color.alpha, + fraction, + &result.alpha); + } + } + else + { + if (TEST_FLAG( + hud_color->flash_flags, + _hud_flash_reverse_colors_bit)) + { + result = flash_color; + } + else + { + result = base_color; + } + } + } + else + { + result = base_color; + } + + hud_draw_stack_buffer_check(311); + + return real_argb_color_to_pixel32(&result); +} + +void hud_draw_weapon_overlays( + short local_player_index, + struct hud_absolute_placement_definition const *absolute_placement, + struct weapon_hud_overlay_definition const *overlays, + long type_flags, + long reference_time, + short draw_flags, + boolean in_multiplayer) +{ + long return_eip = get_return_eip(); + long stack_buffer[STACK_BUFFER_LENGTH]; + long item_index; + + csmemset(stack_buffer, 0x62, sizeof(stack_buffer)); + + for (item_index = 0; item_index < overlays->items.count; item_index++) + { + struct weapon_hud_overlay_item const *item = TAG_BLOCK_GET_ELEMENT( + &overlays->items, + item_index, + struct weapon_hud_overlay_item); + + if (!TEST_FLAG(item->flags, _hud_overlay_runtime_invalid_bit) && + (item->type & type_flags)) + { + struct bitmap_group_sequence *sequence = TAG_BLOCK_GET_ELEMENT( + &bitmap_group_get(overlays->bitmap.index)->sequences, + item->sequence_index, + struct bitmap_group_sequence); + pixel32 color; + short frame_index; + + if (TEST_FLAG(item->flags, _hud_overlay_flashes_bit) && + TEST_FLAG(draw_flags, _hud_draw_flashing_bit)) + { + color = get_flash_color(&item->colors, reference_time); + } + else + { + color = item->colors.color; + } + + if (TEST_FLAG(item->flags, _hud_overlay_flashes_bit) && + TEST_FLAG(draw_flags, _hud_draw_flashing_bit) && + item->frame_rate > 0) + { + frame_index = (short)(((game_time_get() - reference_time) / + item->frame_rate / TICKS_PER_SECOND) % sequence->sprites.count); + } + else + { + frame_index = 0; + } + + { + struct bitmap_data const *bitmap = NULL; + real_rectangle2d const *clip = NULL; + + hud_retrieve_bitmap_and_bounding_rect( + overlays->bitmap.index, + item->sequence_index, + frame_index, + &bitmap, + &clip); + + if (bitmap && _texture_cache_bitmap_get_hardware_format( + (struct bitmap_data *)bitmap, + FALSE, + TRUE)) + { + hud_draw_bitmap_with_meter( + NULL, + bitmap, + absolute_placement, + &item->placement, + clip, + 1.0f, + 0.0f, + color, + in_multiplayer, + FALSE, + FALSE); + } + } + } + } + + hud_draw_stack_buffer_check(748); + + return; +} + void hud_draw_bitmap( struct bitmap_data const *bitmap, struct hud_absolute_placement_definition const *absolute_placement, @@ -380,7 +723,6 @@ static void hud_draw_bitmap_with_meter( real_vector2d xy_scale; point2d point; real_rectangle2d bounds; - boolean use_multiplayer_scaling; csmemset(stack_buffer, 0x62, sizeof(stack_buffer)); @@ -402,17 +744,15 @@ static void hud_draw_bitmap_with_meter( xy_scale.i = placement->scale.i*scale; xy_scale.j = placement->scale.j*scale; - use_multiplayer_scaling = - in_multiplayer && !TEST_FLAG( - placement->multiplayer_scaling_flags, - _hud_dont_scale_offset_bit); hud_calculate_point( render.local_player_index, absolute_placement, placement, NULL, - use_multiplayer_scaling, + in_multiplayer && !TEST_FLAG( + placement->multiplayer_scaling_flags, + _hud_dont_scale_offset_bit), 0.0f, &point); hud_calculate_bitmap_bounds( @@ -537,3 +877,61 @@ static void hud_calculate_bitmap_bounds( return; } + +void hud_draw_bitmap_direct( + struct bitmap_data const *bitmap, + short placement, + point2d const *point, + real_rectangle2d const *clip, + real scale, + real theta, + pixel32 color, + boolean is_interface_bitmap) +{ + long return_eip = get_return_eip(); + long stack_buffer[STACK_BUFFER_LENGTH]; + real_rectangle2d default_clip; + real_vector2d xy_scale; + real_rectangle2d bounds; + + csmemset(stack_buffer, 0x62, sizeof(stack_buffer)); + + default_clip.x0 = 0.0f; + default_clip.x1 = 1.0f; + default_clip.y0 = 0.0f; + default_clip.y1 = 1.0f; + + if (is_interface_bitmap) + { + default_clip.x1 = (real)bitmap->width; + default_clip.y1 = (real)bitmap->height; + } + + if (!clip) + { + clip = &default_clip; + } + + xy_scale.i = scale; + xy_scale.j = scale; + + hud_calculate_bitmap_bounds( + bitmap, + placement, + clip, + &bounds, + is_interface_bitmap); + hud_draw_bitmap_internal( + NULL, + bitmap, + point, + clip, + &bounds, + &xy_scale, + theta, + color); + + hud_draw_stack_buffer_check(856); + + return; +} diff --git a/source/interface/hud_draw.h b/source/interface/hud_draw.h index 3b549ff0..8c7f8b02 100644 --- a/source/interface/hud_draw.h +++ b/source/interface/hud_draw.h @@ -27,6 +27,7 @@ struct hud_placement_definition; struct meter_hud_element_definition; struct number_hud_element_definition; struct static_hud_element_definition; +struct weapon_hud_overlay_definition; long get_return_eip( void); @@ -60,6 +61,14 @@ long get_flash_duration( pixel32 get_flash_color( struct hud_color_definition const *hud_color, long reference_value); +void hud_draw_weapon_overlays( + short local_player_index, + struct hud_absolute_placement_definition const *absolute_placement, + struct weapon_hud_overlay_definition const *overlays, + long type_flags, + long reference_time, + short draw_flags, + boolean in_multiplayer); void hud_draw_bitmap( struct bitmap_data const *bitmap, struct hud_absolute_placement_definition const *absolute_placement, diff --git a/source/interface/interface.c b/source/interface/interface.c index 305e8a7b..81b1f098 100644 --- a/source/interface/interface.c +++ b/source/interface/interface.c @@ -72,6 +72,7 @@ symbols in this file: #include "cseries/cseries.h" #include "bitmaps/bitmap_group.h" #include "bitmaps/color_table_group.h" +#include "camera/director.h" #include "cutscene/cinematics.h" #include "game/game_globals.h" #include "game/game_engine.h" @@ -85,11 +86,15 @@ symbols in this file: #include "main/main.h" #include "main/main_runtime.h" #include "math/real_math.h" +#include "items/weapon_definitions.h" +#include "items/weapons.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_cinematics.h" #include "render/render.h" #include "scenario/scenario.h" #include "text/draw_string.h" +#include "units/unit_definitions.h" +#include "units/units.h" /* ---------- constants */ @@ -271,16 +276,38 @@ struct profile_value typedef char profile_value_size_assert[ sizeof(struct profile_value) == 0x20C ? 1 : -1]; +struct interface_hud_scripted_globals +{ + boolean show_hud; + boolean show_hud_help_text; + byte unused[2]; +}; + +struct interface_hud_defaults_definition +{ + struct tag_reference default_weapon_hud; +}; + +struct interface_hud_globals_definition +{ + byte unused[0x2C0]; + struct interface_hud_defaults_definition defaults; +}; + +typedef char interface_hud_globals_default_weapon_hud_index_offset_assert[ + offsetof(struct interface_hud_globals_definition, defaults.default_weapon_hud.index) == 0x2CC ? 1 : -1]; + /* ---------- prototypes */ void interface_splitscreen_render( void); void code_000cea10( void); -long interface_get_weapon_hud_index( - real *flashlight_power); /* ---------- globals */ +extern struct interface_hud_globals_definition *hud_globals; +extern struct interface_hud_scripted_globals *hud_scripted_globals; + static short profile_game_value_count = NUMBER_OF_PROFILE_GAME_VALUES; static struct profile_value profile_game_values[MAXIMUM_PROFILE_VALUES] = { @@ -501,6 +528,77 @@ void interface_draw_bitmap( return; } +long interface_get_weapon_hud_index( + real *flashlight_power) +{ + long player_index = local_player_get_player_index(render.local_player_index); + long weapon_hud_index = NONE; + real flashlight = 0.0f; + + if (player_index != NONE) + { + struct player_datum *player = player_get(player_index); + director_perspective perspective = director_get_perspective(render.local_player_index); + + if (hud_scripted_globals && + hud_scripted_globals->show_hud && + perspective != _director_perspective_neutral && + perspective != _director_perspective_scripted && + player->unit_index != NONE) + { + long weapon_index = unit_inventory_get_weapon( + player->unit_index, + unit_get(player->unit_index)->unit.current_weapon_index); + + if (weapon_index == NONE) + { + struct unit_datum *unit = unit_get(player->unit_index); + + if (unit->object.parent_object_index != NONE && + unit->unit.parent_seat_index != NONE) + { + struct unit_datum *parent = unit_get(unit->object.parent_object_index); + struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT( + &unit_definition_get(parent->definition_index)->unit.seats, + unit->unit.parent_seat_index, + struct unit_seat); + + if (TEST_FLAG(seat->flags, _unit_seat_gunner_bit)) + { + weapon_index = unit_inventory_get_weapon( + unit->object.parent_object_index, + unit_get(unit->object.parent_object_index)->unit.current_weapon_index); + } + } + } + else + { + flashlight = unit_get(player->unit_index)->unit.integrated_night_vision_power; + } + + if (weapon_index != NONE) + { + long hud_index = weapon_definition_get( + weapon_get(weapon_index)->definition_index)-> + weapon.interface_definition.hud_interface.index; + + if (hud_index != NONE) + { + weapon_hud_index = hud_index; + } + else if (!unit_get_weapon_count(player->unit_index)) + { + weapon_hud_index = hud_globals->defaults.default_weapon_hud.index; + } + } + } + } + + *flashlight_power = flashlight; + + return weapon_hud_index; +} + void interface_draw_bitmap_modulated( struct bitmap_data const *bitmap, point2d const *point, diff --git a/source/interface/interface.h b/source/interface/interface.h index 79324def..5c3f242a 100644 --- a/source/interface/interface.h +++ b/source/interface/interface.h @@ -51,6 +51,8 @@ real_argb_color *interface_get_real_argb_color( short interface_color_table_index, short color_index, real_argb_color *color); +long interface_get_weapon_hud_index( + real *flashlight_power); void interface_set_bitmap_text_draw_mode( short interface_font_index, short style, diff --git a/source/interface/ui_widget_event_handler_functions.c b/source/interface/ui_widget_event_handler_functions.c index c2b6638c..7762e395 100644 --- a/source/interface/ui_widget_event_handler_functions.c +++ b/source/interface/ui_widget_event_handler_functions.c @@ -909,6 +909,7 @@ symbols in this file: #include "cseries.h" #include "bungie_net/network/transport.h" +#include "bungie_net/network/transport_endpoint_winsock.h" #include "interface/player_ui.h" #include "saved games/player_profile.h" #include "interface/ui_widget_definitions.h" diff --git a/source/main/main.c b/source/main/main.c index e0c12da3..f89c76b6 100644 --- a/source/main/main.c +++ b/source/main/main.c @@ -359,6 +359,7 @@ symbols in this file: #include "scenario/scenario_definitions.h" #include "cache/cache_files.h" #include "cache/predicted_resources.h" +#include "bitmaps/bitmap_group.h" #include "bitmaps/bitmaps_internal.h" #include "interface/hud.h" #include "interface/interface.h" @@ -368,6 +369,7 @@ symbols in this file: #include "sound/sound_manager.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_debug.h" +#include "rasterizer/rasterizer_debug_options.h" #include "bink/bink_playback.h" #include "main/d3d_intimacy.h" #include "networking/network_game_globals.h" @@ -384,6 +386,11 @@ symbols in this file: /* ---------- constants */ +enum +{ + _bitmap_format_x8r8g8b8 = 10, +}; + /* ---------- macros */ /* ---------- structures */ @@ -651,8 +658,6 @@ extern void main_initialize_time(void); extern void main_skip_private(void); extern void main_update_time( void); -extern void main_game_render( - double time_delta_since_tick_sec); static void main_frame_rate_debug( void); @@ -1107,6 +1112,69 @@ static void screenshot_record( return; } +void set_window_camera_values( + struct render_window *window, + struct observer_result const *observer) +{ + struct render_camera *camera = &window->rasterizer_camera; + + if (observer) + { + camera->position = observer->position; + camera->forward = observer->forward; + camera->up = observer->up; + camera->vertical_field_of_view = + 2.0f * arctangent( + 0.75f * render_camera_get_adjusted_field_of_view_tangent( + observer->field_of_view), + 1.0f); + + if (window->local_player_index != NONE && + !console_is_active() && + !game_time_get_paused() && + director_get_perspective(window->local_player_index) != + _director_perspective_neutral) + { + real_matrix4x3 effect_matrix; + real_matrix4x3 view_matrix; + + player_effect_get_camera_effect_matrix( + window->local_player_index, + &effect_matrix); + matrix4x3_from_point_and_vectors( + &view_matrix, + &observer->position, + &observer->forward, + &observer->up); + matrix4x3_multiply(&view_matrix, &effect_matrix, &view_matrix); + matrix4x3_to_point_and_vectors( + &view_matrix, + &camera->position, + &camera->forward, + &camera->up); + } + } + else + { + camera->position = *global_origin3d; + camera->forward = *global_forward3d; + camera->up = *global_up3d; + camera->vertical_field_of_view = + 2.0f * arctangent( + 0.75f * render_camera_get_adjusted_field_of_view_tangent( + DEGREES_TO_RADIANS(80.0f)), + 1.0f); + } + + camera->mirrored = FALSE; + camera->z_near = rasterizer_globals.near_clip_distance; + camera->z_far = rasterizer_globals.far_clip_distance; + if (!debug_render_freeze) + window->render_camera = *camera; + + return; +} + void main_present_frame( void) { @@ -2475,6 +2543,90 @@ static void main_reset_map_private( return; } +static void screenshot_render( + struct render_window *windows) +{ + char path[512]; + struct file_reference reference; + point2d screenshot_page_index; + point2d screenshot_index; + struct bitmap_data *bitmap; + long screenshot_width; + long screenshot_height; + + global_screenshot_size = PIN(global_screenshot_size, 1, 3); + screenshot_width = + rasterizer_globals.reserved04.screen_bounds.x1 - + rasterizer_globals.reserved04.screen_bounds.x0; + screenshot_height = + rasterizer_globals.reserved04.screen_bounds.y1 - + rasterizer_globals.reserved04.screen_bounds.y0; + bitmap = bitmap_2d_new( + global_screenshot_size * screenshot_width, + global_screenshot_size * screenshot_height, + 0, + _bitmap_format_x8r8g8b8); + + if (bitmap && bitmap->base_address) + { + console_printf(TRUE, ""); + console_close(); + + for (screenshot_page_index.y = 0; + screenshot_page_index.y < global_screenshot_count.count; + screenshot_page_index.y++) + { + for (screenshot_page_index.x = 0; + screenshot_page_index.x < global_screenshot_count.count; + screenshot_page_index.x++) + { + for (screenshot_index.y = 0; + screenshot_index.y < global_screenshot_size; + screenshot_index.y++) + { + for (screenshot_index.x = 0; + screenshot_index.x < global_screenshot_size; + screenshot_index.x++) + { + if (global_screenshot_count.count > 1 || + global_screenshot_size > 1) + { + render_frame( + windows, + 1, + &screenshot_page_index, + &screenshot_index, + bitmap, + 0.0f); + render_frame_present(&screenshot_index, bitmap); + } + else + { + render_frame(windows, 1, NULL, NULL, bitmap, 0.0f); + render_frame_present(NULL, bitmap); + } + } + } + + sprintf( + path, + "%dscreenshot%d%d.tif", + main_globals.screenshot_identifier, + screenshot_page_index.y, + screenshot_page_index.x); + file_reference_create_from_path(&reference, path, FALSE); + screenshot_record(bitmap, &reference); + } + } + + main_globals.screenshot_identifier++; + bitmap_delete(bitmap); + } + + global_screenshot_count.count = 0; + return; +} + void halt_and_catch_fire( void) { @@ -2609,6 +2761,103 @@ void main_loop_of_death( return; } +void main_game_render( + double time_delta_since_tick_sec) +{ + boolean force_single_screen; + long window_index; + struct render_window *window; + struct observer_result const *observer; + long player_window_count; + long window_count; + long last_local_player_index; + + lock_global_random_seed(); + collision_log_continue_period(TRUE); + sound_render(); + force_single_screen = game_engine_force_single_screen(); + last_local_player_index = NONE; + + if (local_player_count() < 1) + window_count = 1; + else if (local_player_count() > MAXIMUM_LOCAL_PLAYERS) + window_count = MAXIMUM_LOCAL_PLAYERS; + else + window_count = local_player_count(); + player_window_count = window_count; + if (force_single_screen || cinematic_in_progress()) + { + window_count = 1; + player_window_count = 1; + } + + for (window_index = 0; window_index < player_window_count; window_index++) + { + window = &global_screenshot_count.windows[window_index]; + observer = NULL; + + compute_window_bounds( + window_index, + player_window_count, + &window->rasterizer_camera.viewport_bounds, + &window->rasterizer_camera.window_bounds); + if (force_single_screen || window_index >= window_count) + { + window->local_player_index = NONE; + } + else + { + if (rasterizer_debug_options.force_all_player_views_to_default_player && + (short)last_local_player_index != NONE) + { + goto assign_player_camera; + } + if (main_globals.connection == _game_connection_film_playback) + { + last_local_player_index = 0; + goto assign_player_camera; + } + last_local_player_index = local_player_get_next(last_local_player_index); + + assign_player_camera: + window->local_player_index = (short)last_local_player_index; + observer = observer_get_camera(last_local_player_index); + } + + set_window_camera_values(window, observer); + window->console_window = FALSE; + } + + window = &global_screenshot_count.windows[player_window_count]; + compute_window_bounds( + 0, + 1, + &window->rasterizer_camera.viewport_bounds, + &window->rasterizer_camera.window_bounds); + window->local_player_index = NONE; + window->console_window = TRUE; + set_window_camera_values(window, NULL); + + if (global_screenshot_count.count <= 0) + { + render_frame( + global_screenshot_count.windows, + player_window_count + 1, + NULL, + NULL, + main_globals.movie, + (real)time_delta_since_tick_sec); + } + else + { + screenshot_render(global_screenshot_count.windows); + } + + collision_log_end_period(); + unlock_global_random_seed(); + return; +} + void main_loop( void) { diff --git a/source/main/main.h b/source/main/main.h index f0b0eecf..15732752 100644 --- a/source/main/main.h +++ b/source/main/main.h @@ -12,11 +12,21 @@ MAIN.H /* ---------- structures */ +struct observer_result; +struct render_window; + /* ---------- prototypes/MAIN.C */ float main_get_seconds_elapsed( void); +void set_window_camera_values( + struct render_window *window, + struct observer_result const *observer); + +void main_game_render( + double time_delta_since_tick_sec); + void game_connection_set( short connection); diff --git a/source/math/real_math.h b/source/math/real_math.h index 68f7673f..6d8ed69e 100644 --- a/source/math/real_math.h +++ b/source/math/real_math.h @@ -1446,12 +1446,18 @@ __inline real real_random( return real_seed_random(get_global_random_seed_address()); } +#ifdef REAL_MATH_EXTERNAL_REAL_RANDOM_RANGE +real real_random_range( + real lower_bound, + real upper_bound); +#else __inline real real_random_range( real lower_bound, real upper_bound) { return real_seed_random_range(get_global_random_seed_address(), lower_bound, upper_bound); } +#endif __inline real_vector3d *random_direction3d( real_vector3d *direction) diff --git a/source/models/model_animations.c b/source/models/model_animations.c index 9207e573..83c9279e 100644 --- a/source/models/model_animations.c +++ b/source/models/model_animations.c @@ -239,6 +239,23 @@ struct compressed_animation_header unsigned long rotation_node_headers[1]; }; +/* Recovered animation-graph block layouts kept TU-private to preserve VC7 header scheduling. */ +struct animation_graph_node +{ + char name[TAG_STRING_LENGTH+1]; + short next_sibling_node_index; + short first_child_node_index; + short parent_node_index; + word pad; + unsigned long flags; + real_vector3d base_vector; + real range; + long pad1; +}; + +typedef char verify_animation_graph_node_size[ + sizeof(struct animation_graph_node) == 0x40 ? 1 : -1]; + typedef char verify_compressed_animation_header_rotation_node_headers_offset[ offsetof(struct compressed_animation_header, rotation_node_headers) == 0x2C ? 1 : -1]; @@ -395,6 +412,71 @@ void animation_set_frame_size( return; } +void animation_graph_node_matrices_from_orientations( + long animation_graph_index, + real_matrix4x3 *node_matrices, + struct real_orientation const *node_orientations, + real_point3d const *origin, + real_vector3d const *forward, + real_vector3d const *up) +{ + struct animation_graph const *animation_graph = animation_graph_definition_get( + animation_graph_index); + real_matrix4x3 root_matrix; + short node_indices[MAXIMUM_NODES_PER_MODEL]; + short read_index; + short write_index; + + matrix4x3_from_point_and_vectors(&root_matrix, origin, forward, up); + + if (animation_graph->nodes.count > 0) + { + read_index = 0; + write_index = 1; + node_indices[0] = 0; + + do + { + short node_index = node_indices[read_index++]; + struct animation_graph_node *node = TAG_BLOCK_GET_ELEMENT( + &animation_graph->nodes, + node_index, + struct animation_graph_node); + real_matrix4x3 const *parent_matrix; + real_matrix4x3 local_matrix; + + if (!node_index) + parent_matrix = &root_matrix; + else + parent_matrix = &node_matrices[node->parent_node_index]; + + matrix4x3_from_orientation(&local_matrix, &node_orientations[node_index]); + matrix4x3_multiply(parent_matrix, &local_matrix, &node_matrices[node_index]); + + if (node->next_sibling_node_index != NONE) + { + match_assert( + "c:\\halo\\SOURCE\\models\\model_animations.c", + 1250, + write_indexnext_sibling_node_index; + } + + if (node->first_child_node_index != NONE) + { + match_assert( + "c:\\halo\\SOURCE\\models\\model_animations.c", + 1256, + write_indexfirst_child_node_index; + } + } + while (read_index != write_index); + } + + return; +} + short animation_graph_get_animation_by_name( long animation_graph_index, char const *animation_name) diff --git a/source/objects/damage.c b/source/objects/damage.c index b670a8c0..7b9fdd8b 100644 --- a/source/objects/damage.c +++ b/source/objects/damage.c @@ -110,11 +110,15 @@ symbols in this file: #include "game/game_allegiance.h" #include "game/game_engine.h" #include "game/game_globals.h" +#include "game/game_statistics_internal.h" #include "game/players.h" #include "hs/object_lists.h" #include "input/input.h" #include "interface/hud_unit.h" +#include "items/items.h" +#include "items/projectiles.h" #include "main/console.h" +#include "math/periodic_functions.h" #include "damage_effect_definitions.h" #include "object_definitions.h" #include "object_types.h" @@ -129,7 +133,9 @@ symbols in this file: #include "tag_files/tag_files.h" #include "text/draw_string.h" #include "units/unit_definitions.h" +#include "units/bipeds.h" #include "units/units.h" +#include "units/vehicles.h" /* ---------- constants */ @@ -216,7 +222,10 @@ enum enum { + _damage_side_effect_none = 0, + _damage_side_effect_harmless, _damage_side_effect_lethal_to_the_unsuspecting = 2, + _damage_side_effect_emp, }; enum @@ -324,39 +333,6 @@ void render_debug_object_damage( static long get_player_index_from_object_or_parents( long object_index); -void object_damage_shield( - long object_index, - struct damage_resistance const *damage_resistance, - struct damage_resistance_material const *damage_material, - struct damage_definition const *damage_definition, - struct damage_data *damage, - unsigned long *being_damaged_flags, - real *shield_damage, - real *total_damage); - -void object_damage_body( - long object_index, - short region_index, - short node_index, - real_vector3d const *object_normal, - struct damage_resistance const *damage_resistance, - struct damage_resistance_material const *damage_material, - struct damage_definition const *damage_definition, - struct damage_data *damage, - unsigned long *being_damaged_flags, - real *body_damage, - real *body_damage_multiplier, - real total_damage); - -void object_damage_aftermath( - long object_index, - struct damage_data *damage, - unsigned long being_damaged_flags, - real shield_damage, - real body_damage, - real body_damage_multiplier, - short body_part); - boolean unit_unsuspecting( long unit_index, real_point3d const *point); @@ -821,6 +797,320 @@ void object_set_melee_attack_inhibited( return; } +static void object_damage_shield( + long object_index, + struct damage_resistance const *damage_resistance, + struct damage_resistance_material const *damage_material, + struct damage_definition const *damage_definition, + struct damage_data *damage, + unsigned long *being_damaged_flags, + real *shield_damage_reference, + real *total_damage_reference) +{ + struct object_datum *object = object_get(object_index); + real total_damage = *total_damage_reference; + boolean negligible_damage = FALSE; + real shield_damage = total_damage; + boolean ignore_difficulty = FALSE; + real maximum_shield_vitality; + real inverse_maximum_shield_vitality; + + (void)damage; + + if (!game_engine_running() && + damage_definition->category == _damage_category_falling && + object->object.owner_team_index == _game_team_player) + { + ignore_difficulty = TRUE; + } + + if (object->object.shield_vitality > 0.f) + { + maximum_shield_vitality = + object_get_maximum_shield_vitality(object_index, ignore_difficulty); + inverse_maximum_shield_vitality = maximum_shield_vitality > 0.f + ? 1.f / maximum_shield_vitality + : 0.f; + + if (!TEST_FLAG( + *being_damaged_flags, + _object_being_damaged_by_friendly_bit) || + !TEST_FLAG( + damage_resistance->flags, + _damage_resistance_always_shields_friendly_damage_bit)) + { + shield_damage = + (1.f - damage_material->shield_leak_fraction) * total_damage; + if (object->object.shield_vitality <= + damage_resistance->shield_failure_threshold && + damage_resistance->shield_failure_threshold > 0.f) + { + real shield_failure = transition_function_evaluate( + damage_resistance->shield_failure_function, + object->object.shield_vitality / + damage_resistance->shield_failure_threshold); + + shield_damage *= + (1.f - damage_resistance->maximum_shield_failure) * + shield_failure + + damage_resistance->maximum_shield_failure; + } + } + + if (TEST_FLAG( + object->object.damage_flags, + _object_shield_over_charging_bit)) + { + shield_damage = total_damage; + total_damage = 0.f; + } + else + { + real actual_shield_damage; + real normalized_shield_damage; + + if (shield_damage < 0.f) + shield_damage = 0.f; + total_damage -= shield_damage; + + if (TEST_FLAG( + *being_damaged_flags, + _object_being_damaged_by_friendly_bit) && + TEST_FLAG( + *being_damaged_flags, + _object_being_damaged_multiplied_by_difficulty_bit)) + { + real difficulty = + game_difficulty_get_value(_game_difficulty_value_enemy_damage); + + if (difficulty > 0.f) + shield_damage /= difficulty; + } + + actual_shield_damage = + damage_material->shield_damage_multiplier * shield_damage; + match_vassert( + "c:\\halo\\SOURCE\\objects\\damage.c", + 1550, + damage_resistance->shield_material_type >= 0 && + damage_resistance->shield_material_type < NUMBER_OF_MATERIAL_TYPES, + "damage_resistance->shield_material_type>=0 && damage_resistance->shield_material_typematerial_modifiers[ + damage_resistance->shield_material_type]; + if (actual_shield_damage < _real_epsilon) + negligible_damage = TRUE; + + normalized_shield_damage = + actual_shield_damage * inverse_maximum_shield_vitality; + if (normalized_shield_damage > object->object.shield_vitality || + damage_definition->side_effect == _damage_side_effect_emp) + { + real excess_damage = + actual_shield_damage - + maximum_shield_vitality * object->object.shield_vitality; + + if (excess_damage > 0.f) + total_damage += excess_damage; + object->object.shield_vitality = 0.f; + if (!TEST_FLAG( + object->object.damage_flags, + _object_shield_depleted_bit)) + { + object_deplete_shield(object_index); + SET_FLAG( + *being_damaged_flags, + _object_being_damaged_shield_depleted_bit, + TRUE); + } + } + else + { + if (!TEST_FLAG( + object->object.damage_flags, + _object_cannot_take_damage_bit)) + { + object->object.shield_vitality -= normalized_shield_damage; + } + + if (!TEST_FLAG( + object->object.damage_flags, + _object_passed_shield_damage_threshold_bit) && + object->object.shield_vitality < + damage_resistance->shield_damaged_effect_threshold) + { + damage_effect_new_on_object( + damage_resistance->shield_damaged_effect.index, + object_index); + SET_FLAG( + object->object.damage_flags, + _object_passed_shield_damage_threshold_bit, + TRUE); + } + } + } + + if (!negligible_damage) + { + real normalized_damage = + (*total_damage_reference - total_damage) * + inverse_maximum_shield_vitality; + + object->object.shield_damage_decay_timer = 0; + if (!TEST_FLAG( + object->object.damage_flags, + _object_shield_depleted_bit)) + { + object->object.current_shield_damage = 1.f; + } + object->object.recent_shield_damage += normalized_damage; + if (object->object.current_shield_damage > 1.f) + object->object.current_shield_damage = 1.f; + if (object->object.recent_shield_damage > 1.f) + object->object.recent_shield_damage = 1.f; + } + } + else + { + shield_damage = 0.f; + object->object.shield_vitality = 0.f; + } + + if (shield_damage >= damage_resistance->minimum_shield_stun_damage || + object->object.shield_vitality == 0.f) + { + object->object.shield_stun_ticks = + (short)(damage_resistance->shield_stun_time * TICKS_PER_SECOND); + } + + *shield_damage_reference = shield_damage; + *total_damage_reference = total_damage; + return; +} + +static void object_damage_aftermath( + long object_index, + struct damage_data *damage, + unsigned long being_damaged_flags, + real shield_damage, + real body_damage, + real body_damage_multiplier, + short body_part) +{ + struct object_datum *object = object_get(object_index); + struct object_definition *object_definition = + object_definition_get(object->definition_index); + struct damage_effect_definition *damage_effect = + damage_effect_definition_get(damage->definition_index); + + if (object_definition->object.acceleration_scale > _real_epsilon) + { + real_vector3d direction = damage->direction; + real_vector3d acceleration; + real acceleration_scale; + + direction.k += 0.45f; + normalize3d(&direction); + acceleration_scale = + damage_effect->damage.instantaneous_acceleration * + object_definition->object.acceleration_scale * + 0.033333335f; + scale_vector3d(&direction, acceleration_scale, &acceleration); + + switch (object->object.type) + { + case _object_type_projectile: + projectile_accelerate(object_index, &acceleration); + break; + + case _object_type_weapon: + case _object_type_equipment: + case _object_type_garbage: + item_accelerate( + object_index, + &acceleration, + damage->scale > 0.5f && + TEST_FLAG( + damage_effect->damage.flags, + _damage_detonates_explosives_bit)); + break; + + case _object_type_biped: + case _object_type_vehicle: + { + struct unit_datum const *unit = (struct unit_datum const *)object; + + if (damage_effect->damage.instantaneous_acceleration > _real_epsilon && + !TEST_FLAG(unit->unit.flags, _unit_impervious_bit)) + { + if (object->object.type == _object_type_biped) + { + biped_accelerate(object_index, &acceleration); + } + else if (object->object.type == _object_type_vehicle) + { + if (TEST_FLAG( + damage_effect->damage.flags, + _damage_detonates_explosives_bit)) + { + scale_vector3d(&acceleration, 2.f, &acceleration); + } + vehicle_accelerate(object_index, &acceleration); + } + } + } + break; + + default: + break; + } + } + + if (game_engine_can_score() && + !TEST_FLAG(damage->flags, _damage_no_statistics_bit)) + { + game_statistics_record_damage( + object_index, + shield_damage + body_damage, + damage->owner_player_index, + damage->owner_object_index, + damage->owner_team_index); + if (TEST_FLAG( + being_damaged_flags, + _object_being_damaged_body_depleted_bit)) + { + game_statistics_record_kill( + object_index, + damage->owner_player_index, + damage->owner_object_index, + damage->owner_team_index); + } + } + else if (game_engine_can_score()) + { + long player_index = player_index_from_unit_index(object_index); + + game_engine_player_killed( + player_index, + object_index, + player_index, + TRUE); + } + + if (TEST_FLAG(_object_mask_unit, object->object.type)) + { + unit_damage_aftermath( + object_index, + damage, + being_damaged_flags, + shield_damage, + body_damage, + body_damage_multiplier, + body_part); + } + + return; +} + void object_damage_body( long object_index, short region_index, diff --git a/source/rasterizer/rasterizer_debug_options.h b/source/rasterizer/rasterizer_debug_options.h index 98223bad..b75dc754 100644 --- a/source/rasterizer/rasterizer_debug_options.h +++ b/source/rasterizer/rasterizer_debug_options.h @@ -100,5 +100,6 @@ struct rasterizer_debug_options_definition }; extern struct rasterizer_debug_options_definition rasterizer_debug_options; +extern boolean debug_render_freeze; #endif /* __RASTERIZER_DEBUG_OPTIONS_H */ diff --git a/source/render/render.h b/source/render/render.h index ddfe6e8a..6cfd139d 100644 --- a/source/render/render.h +++ b/source/render/render.h @@ -94,6 +94,13 @@ void render_initialize_for_new_map(void); void render_dispose_from_old_map(void); void render_dispose(void); +void render_frame( + struct render_window *windoze, + short window_count, + point2d const *screenshot_page_index, + point2d const *screenshot_index, + struct bitmap_data *screenshot_bitmap, + real time_delta_since_tick_sec); void render_frame_pregame( struct render_window const *window, struct bitmap_data *bitmap); diff --git a/source/render/render_cameras.c b/source/render/render_cameras.c index fefd59f4..9474d962 100644 --- a/source/render/render_cameras.c +++ b/source/render/render_cameras.c @@ -141,6 +141,11 @@ enum render_frustum_point_flag_bits _render_frustum_point_flags_right_bit, _render_frustum_point_flags_top_bit, _render_frustum_point_flags_bottom_bit, + _render_frustum_point_flags_near_bit, + _render_frustum_point_flags_far_bit, + NUMBER_OF_RENDER_FRUSTUM_POINT_FLAGS, + + RENDER_FRUSTUM_POINT_FLAGS_PLANE_MASK = FLAG(NUMBER_OF_RENDER_FRUSTUM_POINT_FLAGS) - 1, }; /* ---------- macros */ @@ -149,6 +154,8 @@ enum render_frustum_point_flag_bits /* ---------- globals */ +boolean debug_no_frustum_clip; + /* ---------- public code */ void render_camera_new( @@ -253,6 +260,133 @@ real render_frustum_sphere_diameter_in_pixels( return (frustum->projection_world_to_screen.j / clamped_depth) * radius * 2.0f; } +real render_frustum_cube_view_fraction( + struct render_frustum const *frustum, + real_rectangle3d const *bounds) +{ + real fraction; + + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 876, + frustum); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 877, + bounds); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 878, + bounds->x0<=bounds->x1); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 879, + bounds->y0<=bounds->y1); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 880, + bounds->z0<=bounds->z1); + + if (bounds->z0 >= 0.0f) + { + fraction = 0.0f; + } + else if (bounds->z1 >= 0.0f) + { + fraction = 1.0f; + } + else + { + real inverse_z0 = 1.0f / bounds->z0; + real projection_x = frustum->projection_matrix[0][0]; + real projection_offset_x = frustum->projection_matrix[2][0]; + real projection_y = frustum->projection_matrix[1][1]; + real projection_offset_y = frustum->projection_matrix[2][1]; + real inverse_z1 = 1.0f / bounds->z1; + real positive_inverse_z0; + real positive_inverse_z1; + real left; + real bottom; + real right; + real top; + + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 900, + frustum->projection_valid); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 903, + frustum->projection_matrix[1][0]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 904, + frustum->projection_matrix[3][0]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 905, + frustum->projection_matrix[0][1]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 906, + frustum->projection_matrix[3][1]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 907, + frustum->projection_matrix[0][3]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 908, + frustum->projection_matrix[1][3]==0.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 909, + frustum->projection_matrix[2][3]==-1.0f); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 910, + frustum->projection_matrix[3][3]==0.0f); + + positive_inverse_z0 = -inverse_z0; + positive_inverse_z1 = -inverse_z1; + left = MAX( + MIN( + (bounds->z0 * projection_offset_x + bounds->x0 * projection_x) * + positive_inverse_z0, + (bounds->z1 * projection_offset_x + bounds->x0 * projection_x) * + positive_inverse_z1), + -1.0f); + bottom = MAX( + MIN( + (bounds->z0 * projection_offset_y + bounds->y0 * projection_y) * + positive_inverse_z0, + (bounds->z1 * projection_offset_y + bounds->y0 * projection_y) * + positive_inverse_z1), + -1.0f); + right = MIN( + MAX( + (bounds->z0 * projection_offset_x + bounds->x1 * projection_x) * + positive_inverse_z0, + (bounds->z1 * projection_offset_x + bounds->x1 * projection_x) * + positive_inverse_z1), + 1.0f); + top = MIN( + MAX( + (bounds->z0 * projection_offset_y + bounds->y1 * projection_y) * + positive_inverse_z0, + (bounds->z1 * projection_offset_y + bounds->y1 * projection_y) * + positive_inverse_z1), + 1.0f); + fraction = (right - left) * (top - bottom) * 0.25f; + if (!(fraction > 0.0f)) + { + fraction = 0.0f; + } + } + + return fraction; +} + word render_frustum_build_point_flags( const struct render_frustum *frustum, const real_point3d *point) @@ -270,6 +404,80 @@ word render_frustum_build_point_flags( return flags; } +boolean render_frustum_triangle_visible( + struct render_frustum const *frustum, + real_point3d const *point0, + real_point3d const *point1, + real_point3d const *point2) +{ + word flags = RENDER_FRUSTUM_POINT_FLAGS_PLANE_MASK; + word point_flags; + + point_flags = render_frustum_build_point_flags(frustum, point0); + if (!point_flags) + return TRUE; + flags &= point_flags; + point_flags = render_frustum_build_point_flags(frustum, point1); + if (!point_flags) + return TRUE; + flags &= point_flags; + point_flags = render_frustum_build_point_flags(frustum, point2); + if (!point_flags) + return TRUE; + flags &= point_flags; + + return flags == 0; +} + +boolean render_camera_triangle_frontfacing( + struct render_camera const *camera, + real_point3d const *point0, + real_point3d const *point1, + real_point3d const *point2) +{ + real_vector3d camera_to_point; + real_vector3d edge0; + real_vector3d edge1; + real_vector3d normal; + + vector_from_points3d(&camera->position, point0, &camera_to_point); + vector_from_points3d(point0, point1, &edge0); + vector_from_points3d(point1, point2, &edge1); + cross_product3d(&edge0, &edge1, &normal); + + return dot_product3d(&normal, &camera_to_point) > -_real_epsilon; +} + +void render_frustum_get_projection_bounds( + struct render_frustum const *frustum, + real_rectangle2d *bounds) +{ + real x; + real y; + real inverse_x_scale; + real inverse_y_scale; + + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 935, + frustum && frustum->projection_valid); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 936, + bounds); + + x = -frustum->projection_matrix[2][0]; + y = -frustum->projection_matrix[2][1]; + inverse_x_scale = -1.0f / frustum->projection_matrix[0][0]; + inverse_y_scale = -1.0f / frustum->projection_matrix[1][1]; + + bounds->x0 = (x - 1.0f) * inverse_x_scale; + bounds->x1 = (x + 1.0f) * inverse_x_scale; + bounds->y0 = (y - 1.0f) * inverse_y_scale; + bounds->y1 = (y + 1.0f) * inverse_y_scale; + return; +} + void render_camera_screen_to_world( const struct render_camera *camera, const struct render_frustum *frustum, @@ -311,6 +519,107 @@ void render_camera_screen_to_world( return; } +void render_camera_build_frustum_bounds( + struct render_camera const *camera, + real_rectangle2d *frustum_bounds) +{ + real aspect_ratio; + real inverse_window_height; + + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 305, + camera); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 306, + frustum_bounds); + + aspect_ratio = (real)(camera->viewport_bounds.y1 - camera->viewport_bounds.y0) / + (camera->viewport_bounds.x1 - camera->viewport_bounds.x0); + inverse_window_height = 1.0f / + (camera->window_bounds.y1 - camera->window_bounds.y0); + + frustum_bounds->x0 = + (2 * camera->viewport_bounds.x0 - + camera->window_bounds.x0 - camera->window_bounds.x1) * + inverse_window_height; + frustum_bounds->x1 = + (2 * camera->viewport_bounds.x1 - + camera->window_bounds.x0 - camera->window_bounds.x1) * + inverse_window_height; + frustum_bounds->y0 = + (2 * camera->viewport_bounds.y0 - + camera->window_bounds.y0 - camera->window_bounds.y1) * + inverse_window_height; + frustum_bounds->y1 = + (2 * camera->viewport_bounds.y1 - + camera->window_bounds.y0 - camera->window_bounds.y1) * + inverse_window_height; + + frustum_bounds->x0 *= aspect_ratio; + frustum_bounds->x1 *= aspect_ratio; + + { + real temporary_y0 = frustum_bounds->y0; + + frustum_bounds->y0 = -frustum_bounds->y1; + frustum_bounds->y1 = -temporary_y0; + } + return; +} + +boolean render_camera_build_clipped_frustum_bounds( + struct render_camera const *camera, + real_rectangle2d const *clip, + real_rectangle2d *frustum_bounds) +{ + boolean use_full_bounds = TRUE; + + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 342, + camera); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 343, + clip); + match_assert( + "c:\\halo\\SOURCE\\render\\render_cameras.c", + 344, + frustum_bounds); + + if (!debug_no_frustum_clip && + clip->x0 < clip->x1 && + clip->y0 < clip->y1) + { + long viewport_height = camera->viewport_bounds.y1 - camera->viewport_bounds.y0; + long viewport_width = camera->viewport_bounds.x1 - camera->viewport_bounds.x0; + real aspect_ratio = (real)viewport_height / (real)viewport_width; + real inverse_tangent = 1.0f / + tangent(camera->vertical_field_of_view * 0.5f); + real horizontal_scale = inverse_tangent * aspect_ratio; + + frustum_bounds->x0 = clip->x0 * horizontal_scale; + frustum_bounds->x1 = clip->x1 * horizontal_scale; + frustum_bounds->y0 = inverse_tangent * clip->y0; + frustum_bounds->y1 = clip->y1 * inverse_tangent; + use_full_bounds = + frustum_bounds->x0 >= frustum_bounds->x1 || + frustum_bounds->y0 >= frustum_bounds->y1; + } + + if (use_full_bounds) + { + frustum_bounds->y1 = 1.0f; + frustum_bounds->x1 = 1.0f; + frustum_bounds->y0 = -1.0f; + frustum_bounds->x0 = -1.0f; + } + + return !use_full_bounds; +} + boolean render_camera_view_to_screen( struct render_camera const *camera, struct render_frustum const *frustum, diff --git a/source/render/render_cameras_internal.h b/source/render/render_cameras_internal.h index b30bbaf9..7cb48a7c 100644 --- a/source/render/render_cameras_internal.h +++ b/source/render/render_cameras_internal.h @@ -19,6 +19,9 @@ real render_frustum_sphere_diameter_in_pixels( struct render_frustum const *frustum, real_point3d const *point, real radius); +real render_frustum_cube_view_fraction( + struct render_frustum const *frustum, + real_rectangle3d const *bounds); void render_frustum_get_projection_bounds( struct render_frustum const *frustum, real_rectangle2d *bounds); @@ -35,6 +38,11 @@ boolean render_frustum_triangle_visible( real_point3d const *point0, real_point3d const *point1, real_point3d const *point2); +boolean render_camera_triangle_frontfacing( + struct render_camera const *camera, + real_point3d const *point0, + real_point3d const *point1, + real_point3d const *point2); boolean render_camera_build_clipped_frustum_bounds( struct render_camera const *camera, real_rectangle2d const *clip, @@ -51,6 +59,11 @@ boolean render_camera_view_to_screen( struct render_frustum const *frustum, real_point3d const *view_point, real_point2d *screen_point); +void render_camera_screen_to_view( + struct render_camera const *camera, + struct render_frustum const *frustum, + real_point2d const *screen_point, + real_vector3d *view_vector); boolean render_camera_world_to_screen( struct render_camera const *camera, struct render_frustum const *frustum, diff --git a/source/render/render_sprite.c b/source/render/render_sprite.c index f6a3aee5..72684b40 100644 --- a/source/render/render_sprite.c +++ b/source/render/render_sprite.c @@ -82,6 +82,7 @@ symbols in this file: #include "math/real_math.h" #include "rasterizer/rasterizer.h" #include "render/render.h" +#include "render/render_cameras_internal.h" #include "render/render_debug.h" #include "render/render_sprite.h" #include "tag_files/tag_files.h" @@ -159,14 +160,6 @@ void *_texture_cache_bitmap_get_hardware_format( struct bitmap_data *bitmap, boolean block, boolean load); -real render_frustum_cube_view_fraction( - struct render_frustum const *frustum, - real_rectangle3d const *bounds); -void render_camera_screen_to_view( - struct render_camera const *camera, - struct render_frustum const *frustum, - real_point2d const *screen_point, - real_vector3d *view_vector); static void build_sprite_transform_origin_and_direction( struct build_sprite_data const *data, diff --git a/source/scenario/scenario.c b/source/scenario/scenario.c index ec168a7a..386ddc2b 100644 --- a/source/scenario/scenario.c +++ b/source/scenario/scenario.c @@ -164,6 +164,7 @@ symbols in this file: #include "cseries.h" #include "scenario.h" +#include "ai/ai.h" #include "bink/bink_playback.h" #include "cache/cache_files.h" #include "cseries/errors.h" @@ -220,8 +221,6 @@ void objects_reconnect_to_structure_bsp( void); void lights_reconnect_to_structure_bsp( void); -void ai_reconnect_to_structure_bsp( - void); void particles_reconnect_to_structure_bsp( void); void contrails_reconnect_to_structure_bsp( @@ -243,8 +242,6 @@ void objects_disconnect_from_structure_bsp( void); void lights_disconnect_from_structure_bsp( void); -void ai_disconnect_from_structure_bsp( - void); void particles_disconnect_from_structure_bsp( void); void contrails_disconnect_from_structure_bsp( diff --git a/source/structures/structures.c b/source/structures/structures.c index ba635b79..3d1a66ad 100644 --- a/source/structures/structures.c +++ b/source/structures/structures.c @@ -61,6 +61,8 @@ symbols in this file: #include "cseries/cseries.h" #include "math/real_math.h" #include "physics/collision_bsp_definitions.h" +#include "physics/collisions.h" +#include "physics/collision_usage.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_debug.h" #include "rasterizer/rasterizer_geometry_environment.h" @@ -69,6 +71,7 @@ symbols in this file: #include "scenario/fog_definitions.h" #include "scenario/sky_definitions.h" #include "structure_bsp_definitions.h" +#include "structure_vector_tests.h" #include "structures.h" /* ---------- constants */ @@ -79,6 +82,7 @@ enum _render_planar_fog_mode_normal = 1, _render_planar_fog_mode_fully_fogged, _render_fog_runtime_screen_use_sky_interpolator_bit = 0, + _collision_surface_two_sided_bit = 0, }; /* ---------- macros */ @@ -221,12 +225,10 @@ typedef char verify_structure_runtime_globals_size[ /* ---------- globals */ -struct structure_runtime_globals bss_004c1100; +static struct structure_runtime_globals structure_globals; extern boolean debug_fog_planes; -#define structure_globals bss_004c1100 - /* ---------- public code */ void structures_initialize( @@ -574,6 +576,105 @@ short structure_clusters_in_cone( return cluster_count; } +boolean structure_test_vector( + real_point3d const *p, + real_vector3d const *v, + real_point3d *collision_point, + short *lightmap_index, + short *material_index, + long *surface_index, + real *s, + real *t) +{ + boolean found = FALSE; + boolean done; + struct collision_result collision; + + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x188, + p); + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x189, + v); + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x18A, + material_index); + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x18B, + surface_index); + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x18C, + s); + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x18D, + t); + + *collision_point = *p; + do + { + done = TRUE; + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x196, + global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH); + global_current_collision_users[global_current_collision_user_depth++] = + _collision_user_structure_lighting; + + if (collision_test_vector( + FLAG(_collision_test_front_facing_surfaces_bit) | + FLAG(_collision_test_structure_bit), + collision_point, + v, + NONE, + &collision)) + { + struct structure_bsp *structure = global_structure_bsp_get(); + + *collision_point = collision.point; + if (structure_render_surface_from_point_and_leaf( + collision_point, + collision.location.leaf_index, + collision.plane_designator & LONG_MAX, + lightmap_index, + material_index, + surface_index, + s, + t) && + TAG_BLOCK_GET_ELEMENT( + &structure->lightmaps, + *lightmap_index, + struct structure_lightmap)->bitmap_index != NONE) + { + found = TRUE; + } + else if (TEST_FLAG( + collision.flags, + _collision_surface_two_sided_bit)) + { + done = FALSE; + collision_point->x += v->i * (1.0f / 4096.0f); + collision_point->y += v->j * (1.0f / 4096.0f); + collision_point->z += v->k * (1.0f / 4096.0f); + } + } + + match_assert( + "c:\\halo\\SOURCE\\structures\\structures.c", + 0x1AA, + global_current_collision_user_depth > 1); + --global_current_collision_user_depth; + } + while (!done); + + return found; +} + long structure_get_planar_fog_definition_index( struct structure_bsp const *structure, short cluster_index, diff --git a/source/units/biped_definitions.h b/source/units/biped_definitions.h index f40b5d94..d6e013f1 100644 --- a/source/units/biped_definitions.h +++ b/source/units/biped_definitions.h @@ -96,7 +96,7 @@ struct _biped_definition real collision_height_standing; real collision_height_crouching; real collision_radius; - real collision_unused[12]; + real collision_unused[10]; real autoaim_width; real unused[27]; real runtime_cosine_stationary_turning_threshold; diff --git a/source/units/bipeds.c b/source/units/bipeds.c index 267bf2f7..b8157a9a 100644 --- a/source/units/bipeds.c +++ b/source/units/bipeds.c @@ -233,27 +233,38 @@ symbols in this file: /* ---------- headers */ +#define REAL_MATH_EXTERNAL_POINT_FROM_LINE3D +#define REAL_MATH_EXTERNAL_REAL_RANDOM_RANGE #include "cseries.h" +#include "ai/ai_communication.h" +#include "ai/ai_debug.h" +#include "ai/actors.h" #include "cseries/errors.h" #include "cseries/profile.h" #include "cutscene/cinematics.h" #include "effects/material_effects.h" +#include "game/cheats.h" #include "game/game.h" #include "game/game_engine.h" +#include "game/game_globals.h" #include "interface/first_person_weapons.h" #include "items/weapons.h" #include "models/model_animation_definitions.h" #include "units/biped_limp_noodle.h" #include "units/biped_definitions.h" #include "units/bipeds.h" +#include "units/vehicle_definitions.h" +#include "units/vehicles.h" #include "physics/breakable_surfaces.h" #include "physics/collision_bsp.h" #include "physics/collision_bsp_definitions.h" +#include "physics/collision_models.h" #include "physics/collisions.h" #include "physics/collision_usage.h" #include "physics/physics.h" #include "render/render_debug.h" #include "scenario/scenario.h" +#include "structures/structure_bsp_definitions.h" /* ---------- constants */ @@ -267,6 +278,19 @@ enum _material_effect_biped_jump_land, }; +enum +{ + _unit_animation_action_melee = 7, +}; + +enum +{ + biped_state_idle = 0, + biped_state_moving, + biped_state_unknown, + NUMBER_OF_BIPED_STATES, +}; + /* ---------- macros */ #define BIPED_CLIMBING_SNAP_ANGLE ((real)(10.0*M_PI/180.0)) @@ -280,6 +304,27 @@ struct biped_contact_point char marker_name[32]; }; +struct game_globals_falling_damage +{ + byte unused0[0x8C]; + real runtime_maximum_falling_velocity; + real runtime_minimum_damage_velocity; + real runtime_maximum_damage_velocity; +}; + +struct vehicle_runtime_datum +{ + long definition_index; + struct _object_datum object; + struct _unit_datum unit; + struct + { + word flags; + short reserved; + byte airborne_ticks; + } vehicle; +}; + struct unit_animation_update_data { char state_desired; @@ -319,6 +364,13 @@ struct scenario_biped_datum struct scenario_unit_datum unit; }; +/* ---------- prototypes */ + +static void biped_make_footstep( + long biped_index, + short event_index, + short contact_point_index); + /* ---------- globals */ boolean debug_biped_physics; @@ -332,6 +384,37 @@ static struct profile_section biped_update_section = {"biped_update", NONE, TRUE extern boolean debug_objects_biped_autoaim_pills; extern boolean debug_objects_biped_physics_pills; +real_vector3d const fudge_vectors[27] = +{ + { { 0.f, 0.f, 0.f } }, + { { 1.f, 0.f, 0.f } }, + { { 0.f, 0.f, 1.f } }, + { { 0.70710677f, 0.f, 0.70710677f } }, + { { 0.57735026f, 0.57735026f, 0.57735026f } }, + { { 0.57735026f, -0.57735026f, 0.57735026f } }, + { { 0.70710677f, 0.70710677f, 0.f } }, + { { 0.70710677f, -0.70710677f, 0.f } }, + { { 0.f, 0.70710677f, 0.70710677f } }, + { { 0.f, -0.70710677f, 0.70710677f } }, + { { -1.f, 0.f, 0.f } }, + { { 0.f, 1.f, 0.f } }, + { { 0.f, -1.f, 0.f } }, + { { -0.70710677f, -0.70710677f, 0.f } }, + { { -0.70710677f, 0.70710677f, 0.f } }, + { { -0.70710677f, 0.f, 0.70710677f } }, + { { -0.57735026f, -0.57735026f, 0.57735026f } }, + { { -0.57735026f, 0.57735026f, 0.57735026f } }, + { { 0.f, 0.f, -1.f } }, + { { 0.70710677f, 0.f, -0.70710677f } }, + { { -0.70710677f, 0.f, -0.70710677f } }, + { { 0.f, 0.70710677f, -0.70710677f } }, + { { 0.f, -0.70710677f, -0.70710677f } }, + { { 0.57735026f, 0.57735026f, -0.57735026f } }, + { { 0.57735026f, -0.57735026f, -0.57735026f } }, + { { -0.57735026f, -0.57735026f, -0.57735026f } }, + { { -0.57735026f, 0.57735026f, -0.57735026f } }, +}; + /* ---------- public code */ char animation_left_foot_frame_index( @@ -516,15 +599,16 @@ void biped_adjust_placement( { struct biped_datum *biped = biped_get(biped_index); struct biped_definition *definition = biped_definition_get(biped->definition_index); + unsigned long flags = definition->biped.flags; - if (TEST_FLAG(definition->biped.flags, _biped_pill_centered_at_origin_bit) && - !TEST_FLAG(definition->biped.flags, _biped_flying_bit)) + if (TEST_FLAG(flags, _biped_pill_centered_at_origin_bit) && + !TEST_FLAG(flags, _biped_flying_bit)) { real height_offset = definition->biped.collision_radius; - data->position.x += data->up.i * height_offset; - data->position.y += data->up.j * height_offset; - data->position.z += data->up.k * height_offset; + data->position.x += data->up.i*height_offset; + data->position.y += data->up.j*height_offset; + data->position.z += data->up.k*height_offset; } return; @@ -572,9 +656,11 @@ void biped_get_sight_position( struct biped_definition *definition = biped_definition_get(biped->definition_index); match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 759, - (estimate_mode==_unit_estimate_none) || estimated_body_position); + (estimate_mode == _unit_estimate_none) || + (estimated_body_position != NULL)); match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 760, - (estimate_mode!=_unit_estimate_gun_position) || desired_facing); + (estimate_mode != _unit_estimate_gun_position) || + (desired_facing != NULL)); if (estimate_mode==_unit_estimate_none) object_get_origin(biped_index, sight_position); @@ -591,12 +677,20 @@ void biped_get_sight_position( left.j = desired_facing->i; left.k = 0.f; - sight_position->x += desired_facing->i*desired_gun_offset->i; - sight_position->y += desired_facing->j*desired_gun_offset->i; - sight_position->z += desired_facing->k*desired_gun_offset->i; - sight_position->x += left.i*desired_gun_offset->j; - sight_position->y += left.j*desired_gun_offset->j; - sight_position->z += left.k*desired_gun_offset->j; + { + real forward_distance = desired_gun_offset->i; + + sight_position->x += desired_facing->i*forward_distance; + sight_position->y += desired_facing->j*forward_distance; + sight_position->z += desired_facing->k*forward_distance; + { + real sideways_distance = desired_gun_offset->j; + + sight_position->x += left.i*sideways_distance; + sight_position->y += left.j*sideways_distance; + sight_position->z += left.k*sideways_distance; + } + } sight_position->z += desired_gun_offset->k; } else @@ -669,6 +763,238 @@ void biped_get_autoaim_pill( return; } +boolean biped_fix_position( + long biped_index, + long line_of_sight_object_index, + real_point3d const *new_position, + real_point3d *final_position, + real maximum_radius_fudge_factor, + boolean fix_below_new_position, + boolean dont_teleport, + boolean use_radius_as_multiplier) +{ + boolean fixed = FALSE; + real_point3d line_of_sight_position; + struct biped_datum *biped; + unsigned long collision_flags; + real_point3d position; + real pill_height; + real pill_width; + struct collision_model_instance line_of_sight_instance; + real_vector3d left; + real_vector3d pill_vector; + short maximum_fudge_vector_count; + short fudge_vector_index; + struct collision_result pill_collision; + struct collision_result line_collision; + struct collision_model_test_pill_result model_pill_collision; + + match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 893, + final_position || !dont_teleport); + match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 895, + global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH); + global_current_collision_users[global_current_collision_user_depth++] = + _collision_user_bipeds; + + if (biped_index == NONE && line_of_sight_object_index == NONE) + goto collision_user_end; + + { + boolean line_of_sight_only; + + if (line_of_sight_object_index != NONE) + { + real unused_radius; + + object_get_bounding_sphere( + line_of_sight_object_index, + &line_of_sight_position, + &unused_radius); + } + + line_of_sight_only = FALSE; + if (biped_index == NONE) + { + biped_index = line_of_sight_object_index; + line_of_sight_only = TRUE; + } + + biped = biped_get(biped_index); + { + struct biped_definition *definition = + biped_definition_get(biped->definition_index); + + collision_flags = TEST_FLAG( + definition->biped.flags, + _biped_passes_through_bipeds_bit) ? + _collision_test_for_bipeds_passthrough_living_flags : + _collision_test_for_bipeds_living_flags; + } + + if (new_position) + { + real_point3d unused_pill_base; + + position = *new_position; + biped_get_physics_pill( + biped_index, + &unused_pill_base, + &pill_height, + &pill_width); + } + else + { + biped_get_physics_pill( + biped_index, + &position, + &pill_height, + &pill_width); + } + + if (line_of_sight_only) + biped_index = NONE; + + maximum_fudge_vector_count = NUMBEROF(fudge_vectors) - + (fix_below_new_position ? 0 : 9); + if (line_of_sight_object_index != NONE) + { + collision_model_instance_new( + &line_of_sight_instance, + line_of_sight_object_index); + } + + cross_product3d(&biped->object.forward, &biped->object.up, &left); + normalize3d(&left); + scale_vector3d(global_up3d, pill_height, &pill_vector); + if (use_radius_as_multiplier) + maximum_radius_fudge_factor *= pill_width; + + fudge_vector_index = 0; + do + { + real_point3d fixed_position; + + if (fudge_vector_index >= maximum_fudge_vector_count) + break; + + if (fix_below_new_position) + { + real distance = + maximum_radius_fudge_factor*fudge_vectors[fudge_vector_index].i; + + fixed_position.x = biped->object.forward.i*distance + position.x; + fixed_position.y = biped->object.forward.j*distance + position.y; + fixed_position.z = biped->object.forward.k*distance + position.z; + distance = maximum_radius_fudge_factor*fudge_vectors[fudge_vector_index].j; + fixed_position.x += left.i*distance; + fixed_position.y += left.j*distance; + fixed_position.z += left.k*distance; + distance = maximum_radius_fudge_factor*fudge_vectors[fudge_vector_index].k; + fixed_position.x += biped->object.up.i*distance; + fixed_position.y += biped->object.up.j*distance; + fixed_position.z += biped->object.up.k*distance; + } + else + { + real_vector3d const *fudge_vector = + &fudge_vectors[fudge_vector_index]; + + fixed_position.x = + maximum_radius_fudge_factor*fudge_vector->i + position.x; + fixed_position.y = + maximum_radius_fudge_factor*fudge_vector->j + position.y; + fixed_position.z = + maximum_radius_fudge_factor*fudge_vector->k + position.z; + } + + if (scenario_leaf_index_from_point(&fixed_position) != NONE) + { + long cluster_index = TAG_BLOCK_GET_ELEMENT( + &global_structure_bsp_get()->leaves, + scenario_leaf_index_from_point(&fixed_position) & LONG_MAX, + struct structure_leaf)->cluster_index; + + if (cluster_index != NONE && + collision_fix_pill( + collision_flags, + &fixed_position, + pill_width*2.f, + pill_height, + pill_width, + biped_index, + &fixed_position) && + !collision_test_pill( + collision_flags, + &fixed_position, + &pill_vector, + pill_width, + biped_index, + &pill_collision) && + (line_of_sight_object_index == NONE || + (!collision_model_test_pill( + &line_of_sight_instance, + &fixed_position, + &pill_vector, + pill_width, + &model_pill_collision) && + (!collision_test_line( + collision_flags, + &fixed_position, + &line_of_sight_position, + biped_index, + &line_collision) || + line_collision.object_index == line_of_sight_object_index) && + (!collision_test_line( + collision_flags, + &line_of_sight_position, + &fixed_position, + line_of_sight_object_index, + &line_collision) || + line_collision.object_index == biped_index)))) + { + struct biped_definition *definition = + biped_definition_get(biped->definition_index); + struct location fixed_location; + + scenario_location_from_point(&fixed_location, &fixed_position); + match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 1054, + fixed_location.cluster_index!=NONE); + if (!TEST_FLAG( + definition->biped.flags, + _biped_pill_centered_at_origin_bit)) + { + fixed_position.z -= definition->biped.collision_radius; + } + + if (biped_index != NONE && !dont_teleport) + { + biped->object.position = fixed_position; + object_compute_node_matrices_recursive(biped_index); + object_translate( + biped_index, + &fixed_position, + &fixed_location); + } + + if (final_position) + *final_position = fixed_position; + fixed = TRUE; + } + } + + ++fudge_vector_index; + } + while (!fixed); + } + +collision_user_end: + match_assert("c:\\halo\\SOURCE\\units\\bipeds.c", 1080, + global_current_collision_user_depth > 1); + --global_current_collision_user_depth; + + return fixed; +} + void biped_exit_seat_end( long biped_index, long seat_object_index) @@ -1036,6 +1362,233 @@ void biped_accelerate( return; } +void biped_falling_danger( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + struct biped_definition *definition = biped_definition_get(biped->definition_index); + + if (!TEST_FLAG(biped->object.damage_flags, _object_dead_bit) && + !(definition->biped.flags & + (FLAG(_biped_flying_bit) | FLAG(_biped_immune_to_falling_damage_bit))) && + !TEST_FLAG(biped->unit.flags, _unit_no_falling_damage_bit) && + biped->unit.actor_index!=NONE && + biped->unit.animation.state!=_unit_state_ai_impulse) + { + long game_time = game_time_get(); + + if (biped->biped.airborne_ticks>30 && + (biped->biped.last_falling_communication_time==NONE || + biped->biped.last_falling_communication_time+15falling_damage, + 0, + struct game_globals_falling_damage); + boolean dangerous = TRUE; + real_point3d ground_point; + + biped->biped.last_falling_communication_time = game_time; + if (biped_find_ground_surface( + biped_index, + global_down3d, + 6.f, + &ground_point, + NULL)!=NONE) + { + real_point3d origin; + + object_get_origin(biped_index, &origin); + if (biped->object.translational_velocity.k>0.f || + (origin.z-ground_point.z)*global_gravity*2.f + + biped->object.translational_velocity.k* + biped->object.translational_velocity.k < + falling_damage->runtime_maximum_damage_velocity* + falling_damage->runtime_maximum_damage_velocity) + { + dangerous = FALSE; + } + } + + if (dangerous) + unit_scream(biped_index, _unit_scream_falling); + } + } + + return; +} + +static void biped_vehicle_speech( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + struct vehicle_runtime_datum *vehicle = vehicle_runtime_get( + biped->object.parent_object_index); + struct unit_definition *vehicle_definition = vehicle_definition_get( + vehicle->definition_index); + + if (TEST_FLAG(vehicle_definition->unit.flags, _unit_causes_passenger_dialogue_bit) && + biped->unit.actor_index!=NONE && + biped->unit.animation.state!=_unit_state_ai_impulse && + biped->unit.gunner_inactive_ticks>120) + { + long game_time = game_time_get(); + + if (vehicle->vehicle.airborne_ticks>30 && + (biped->biped.last_falling_communication_time==NONE || + biped->biped.last_falling_communication_time+15biped.last_falling_communication_time = game_time; + if (biped_find_ground_surface( + biped_index, + global_down3d, + 8.f, + NULL, + NULL)==NONE) + { + real_vector3d predicted_direction; + + falling = TRUE; + predicted_direction.i = vehicle->object.translational_velocity.i*60.f; + predicted_direction.j = vehicle->object.translational_velocity.j*60.f; + predicted_direction.k = vehicle->object.translational_velocity.k*60.f - + global_gravity*1800.f; + if (normalize3d(&predicted_direction)>0.f) + { + real_vector3d landing_normal; + + if (biped_find_ground_surface( + biped_index, + &predicted_direction, + 8.f, + NULL, + &landing_normal)!=NONE) + { + if (landing_normal.k>0.3f) + falling = FALSE; + } + } + } + + if (falling) + { + ai_communication_event( + _ai_communication_vehicle_falling, + biped_index, + NONE, + NONE, + NONE, + NONE, + NULL); + } + else if (vehicle->object.up.k>0.6f && + magnitude3d(&vehicle->object.angular_velocity)<0.052359879f) + { + ai_communication_event( + _ai_communication_vehicle_woohoo, + biped_index, + NONE, + NONE, + NONE, + NONE, + NULL); + } + else + { + ai_communication_event( + _ai_communication_vehicle_scared, + biped_index, + NONE, + NONE, + NONE, + NONE, + NULL); + } + } + } + + return; +} + +static boolean biped_jump( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + struct biped_definition *definition = biped_definition_get(biped->definition_index); + boolean jumped = FALSE; + + if (!TEST_FLAG(biped->biped.flags, _biped_airborne_bit) && + biped->biped.landing!=1) + { + long actor_index; + real_vector3d jump_velocity; + real jump_magnitude; + + jumped = TRUE; + jump_magnitude = definition->biped.jump_velocity; + if (biped->unit.player_index!=NONE) + { + struct game_globals_player_information *player_information = + TAG_BLOCK_GET_ELEMENT( + &scenario_get_game_globals()->player_information, + 0, + struct game_globals_player_information); + + jump_magnitude *= + 1.f - biped->unit.body_stun*player_information->stun_turning_penalty; + } + if (cheat.super_jump && biped->unit.player_index!=NONE) + jump_magnitude *= 4.f; + + jump_velocity = biped->object.translational_velocity; + { + real upward_velocity = + jump_velocity.i*biped->object.up.i + + (jump_velocity.k*biped->object.up.k + + jump_velocity.j*biped->object.up.j); + + if (upward_velocityobject.up.i*velocity_delta; + jump_velocity.j += biped->object.up.j*velocity_delta; + jump_velocity.k += biped->object.up.k*velocity_delta; + } + } + + actor_index = biped->unit.swarm_actor_index; + if (actor_index==NONE) + actor_index = biped->unit.actor_index; + if (actor_index!=NONE) + { + boolean leap = biped->unit.animation.state==_unit_state_leap_start || + biped->unit.animation.state==_unit_state_leap_airborne; + + jumped = actor_aim_jump( + actor_index, + biped_index, + leap, + jump_magnitude, + &jump_velocity); + } + + if (jumped) + { + biped->object.translational_velocity = jump_velocity; + SET_FLAG(biped->biped.flags, _biped_airborne_bit, TRUE); + biped->biped.jump_recovery_timer = 0; + biped->biped.support_surface_index = NONE; + biped_make_footstep(biped_index, _material_effect_biped_jump, 0); + biped_make_footstep(biped_index, _material_effect_biped_jump, 1); + } + } + + return jumped; +} + static void biped_update_dead( long biped_index, struct unit_animation_update_data *animation) @@ -1116,6 +1669,163 @@ static void biped_make_footstep( return; } +static void biped_try_to_make_footsteps( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + boolean air_contact; + boolean moving; + + biped_definition_get(biped->definition_index); + moving = FALSE; + air_contact = FALSE; + + switch (biped->unit.animation.state) + { + case _unit_state_turn_left: + case _unit_state_turn_right: + air_contact = TRUE; + break; + + case _unit_state_move_front: + case _unit_state_move_back: + case _unit_state_move_left: + case _unit_state_move_right: + if (magnitude_squared3d(&biped->unit.throttle)>0.25f) + moving = TRUE; + break; + } + + if (biped->object.animation.state.index!=NONE) + { + struct animation_graph *animation_graph = animation_graph_definition_get( + biped->object.animation.animation_graph_index); + struct animation *animation = TAG_BLOCK_GET_ELEMENT( + &animation_graph->animations, + biped->object.animation.state.index, + struct animation); + + if (air_contact) + { + if (biped->object.animation.state.frame_index==0) + { + biped_make_footstep(biped_index, _material_effect_biped_shuffle, 0); + biped_make_footstep(biped_index, _material_effect_biped_shuffle, 1); + } + } + else if (moving && + (animation->private_left_foot_frame_index || + animation->private_right_foot_frame_index)) + { + short frame_index = biped->object.animation.state.frame_index; + boolean right_foot; + + if (frame_index==animation->private_left_foot_frame_index) + right_foot = FALSE; + else if (frame_index==animation->private_right_foot_frame_index) + right_foot = TRUE; + else + goto no_footstep; + + biped_make_footstep( + biped_index, + biped->unit.animation.base_seat_index==_unit_base_seat_stand, + right_foot!=FALSE); + } + } + +no_footstep: + switch (biped->biped.state) + { + case biped_state_idle: + if (biped->biped.stop_ticks>0) + { + if (++biped->biped.stop_ticks>3) + { + biped_make_footstep(biped_index, _material_effect_biped_shuffle, 0); + biped_make_footstep(biped_index, _material_effect_biped_shuffle, 1); + biped->biped.stop_ticks = 0; + } + } + break; + + case biped_state_moving: + biped->biped.stop_ticks = 1; + break; + + default: + biped->biped.stop_ticks = 0; + break; + } + + return; +} + +void biped_update_airborne( + long biped_index, + struct unit_animation_update_data *animation) +{ + struct biped_datum *biped = biped_get(biped_index); + struct biped_definition *definition = biped_definition_get(biped->definition_index); + boolean flying = FALSE; + + if (biped_flying_through_air(biped_index)) + { + flying = TRUE; + if (TEST_FLAG(definition->biped.flags, _biped_rotate_while_airborne_bit)) + { + if (biped->unit.animation.state!=_unit_state_melee_airborne && + biped->unit.animation.state!=_unit_state_leap_melee) + { + real impulse = real_seed_random_range( + get_global_random_seed_address(), + 0.052359879f, + 0.08726646f); + real_vector3d axis; + + if (biped->object.up.k<0.8f) + { + cross_product3d(&biped->object.up, global_up3d, &axis); + if (normalize3d(&axis)>0.f) + goto axis_ready; + } + + { + vector3d_from_angle(&axis, real_random_range(0.f, 2.f*_pi)); + } + + axis_ready: + point_from_line3d( + (real_point3d *)&biped->object.angular_velocity, + &axis, + impulse, + (real_point3d *)&biped->object.angular_velocity); + } + + biped_apply_rotation(biped_index); + } + } + + match_assert( + "c:\\halo\\SOURCE\\units\\bipeds.c", + 2687, + TEST_FLAG(biped->biped.flags, _biped_airborne_bit)); + + if (biped->unit.animation.state==_unit_state_leap_start || + biped->unit.animation.state==_unit_state_leap_airborne) + { + animation->state_desired = _unit_state_leap_airborne; + } + else if (biped->unit.animation.state==_unit_state_airborne || flying) + { + animation->state_desired = _unit_state_airborne; + } + + biped_verify_object_vectors(biped_index, "post-airborne"); + + return; +} + static void biped_update_landing( long biped_index, struct unit_animation_update_data *animation) @@ -1218,7 +1928,10 @@ static void biped_find_nearby_support_surface( surface_plane = *plane; } - distance = plane3d_distance_to_point(&surface_plane, &base); + distance = + base.y*surface_plane.n.j + + (surface_plane.n.i*base.x + base.z*surface_plane.n.k) - + surface_plane.d; if (distancedefinition_index); + unsigned long flags = definition->biped.flags; + + if (TEST_FLAG(flags, _biped_flying_bit) && + !TEST_FLAG(biped->object.damage_flags, _object_dead_bit)) + { + boolean nearly_stationary = FALSE; + real_vector3d target_facing; + real_vector3d turn_axis; + real turn_rate; + real target_bank; + real bank_blend; + real bank_time; + real_rectangle2d aiming_bounds; + real angular_velocity_limit; + real angular_acceleration_limit; + + if (magnitude_squared3d(&biped->object.translational_velocity)<0.00027777778f && + magnitude_squared3d(&biped->object.angular_velocity)<0.0000013538552f && + magnitude_squared3d(&biped->unit.throttle)<0.010000001f) + { + real stationary_turning_threshold = + TEST_FLAG(biped->unit.control_flags, _unit_control_exact_facing_bit) ? + 0.99000001f : definition->biped.runtime_cosine_stationary_turning_threshold; + + if (dot_product3d( + &biped->unit.desired_facing_vector, + &biped->object.forward)>stationary_turning_threshold) + { + nearly_stationary = TRUE; + } + } + + if (nearly_stationary) + { + target_facing = biped->object.forward; + } + else + { + real pitch = definition->biped.flying_pitch_amount*biped->unit.throttle.k; + + target_facing = biped->unit.desired_facing_vector; + if (pitch!=0.f) + { + target_facing.k += pitch; + if (normalize3d(&target_facing)==0.f) + target_facing = biped->unit.desired_facing_vector; + } + } + + cross_product3d(&biped->object.forward, &biped->object.up, &turn_axis); + turn_rate = dot_product3d(&turn_axis, &biped->unit.desired_facing_vector)* + (10.f/3.f)*biped->unit.throttle.i - biped->unit.throttle.j; + if (turn_rate>1.5f) + turn_rate = 1.5f; + + target_bank = definition->biped.flying_bank_amount*turn_rate; + if (biped->biped.bank*target_bank>0.f) + { + real bank_ratio = biped->biped.bank/target_bank; + + if (bank_ratio>1.f) + bank_ratio = 1.f; + bank_blend = 1.f-bank_ratio; + } + else + { + bank_blend = 1.f; + } + + bank_time = definition->biped.flying_bank_apply_time*bank_blend + + definition->biped.flying_bank_decay_time*(1.f-bank_blend); + if (bank_time>0.f) + { + biped->biped.bank += + (target_bank-biped->biped.bank)/(bank_time*TICKS_PER_SECOND); + } + else + { + biped->biped.bank = target_bank; + } + + aiming_bounds.x0 = -_pi; + aiming_bounds.x1 = _pi; + aiming_bounds.y0 = -_pi/2.f; + aiming_bounds.y1 = _pi/2.f; + angular_velocity_limit = + definition->biped.flying_angular_velocity/TICKS_PER_SECOND; + angular_acceleration_limit = + definition->biped.flying_angular_acceleration/ + (TICKS_PER_SECOND*TICKS_PER_SECOND); + if (angular_acceleration_limit==0.f) + { + biped->object.forward = target_facing; + } + else + { + unit_euler_aiming_update( + NULL, + &biped->object.forward, + &target_facing, + &biped->object.angular_velocity, + &aiming_bounds, + angular_velocity_limit, + angular_acceleration_limit); + } + + biped_snap_facing(biped_index); + biped_verify_object_vectors(biped_index, "post-fly-turn"); + } + else if (biped->unit.animation.base_seat_index!=_unit_base_seat_asleep) + { + boolean flaming = + biped->unit.animation.base_seat_index==_unit_base_seat_flaming; + real_vector3d turn_axis; + real turn_error; + real facing_alignment; + boolean turn_right; + + if (TEST_FLAG(flags, _biped_climbs_anything_bit)) + { + real_vector3d cross; + real_vector3d error; + + cross_product3d( + &biped->object.up, + &biped->unit.desired_facing_vector, + &cross); + cross_product3d(&cross, &biped->object.up, &turn_axis); + if (normalize3d(&turn_axis)==0.f) + turn_axis = biped->object.forward; + + cross_product3d(&turn_axis, &biped->object.forward, &error); + turn_error = dot_product3d(&error, &biped->object.up); + facing_alignment = dot_product3d(&turn_axis, &biped->object.forward); + } + else + { + turn_axis = biped->unit.desired_facing_vector; + turn_axis.k = 0.f; + if (normalize2d((real_vector2d *)&turn_axis)==0.f) + turn_axis = biped->object.forward; + + turn_error = cross_product2d( + (real_vector2d *)&turn_axis, + (real_vector2d *)&biped->object.forward); + facing_alignment = dot_product2d( + (real_vector2d *)&turn_axis, + (real_vector2d *)&biped->object.forward); + } + + turn_right = turn_error>0.f; + if (facing_alignment<-0.89999998f) + { + if (biped->unit.animation.state==_unit_state_turn_right) + turn_right = TRUE; + else if (biped->unit.animation.state==_unit_state_turn_left) + turn_right = FALSE; + } + + if (biped->biped.state==biped_state_moving || + TEST_FLAG(flags, _biped_turns_without_animating_bit)) + { + if (!TEST_FLAG(biped->unit.control_flags, _unit_control_look_dont_turn_bit)) + { + real turn_step = + definition->biped.moving_turning_speed/TICKS_PER_SECOND; + real cosine = (real)cos(turn_step); + real sine = (real)sin(turn_step); + real progress; + + if (turn_right) + sine = -sine; + + if (TEST_FLAG(flags, _biped_climbs_anything_bit)) + { + real_vector3d cross; + + rotate_vector_about_axis( + &biped->object.forward, + &biped->object.up, + sine, + cosine); + cross_product3d( + &turn_axis, + &biped->object.forward, + &cross); + progress = dot_product3d(&cross, &biped->object.up); + } + else + { + rotate_vector2d( + (real_vector2d *)&biped->object.forward, + sine, + cosine, + (real_vector2d *)&biped->object.forward); + progress = cross_product2d( + (real_vector2d *)&turn_axis, + (real_vector2d *)&biped->object.forward); + } + + if ((!turn_right || progress<0.f) && + (turn_right || progress>0.f)) + { + if (TEST_FLAG(flags, _biped_climbs_anything_bit)) + { + real_vector3d rotation_axis; + + cross_product3d( + &biped->object.up, + &turn_axis, + &rotation_axis); + if (normalize3d(&rotation_axis)>0.f) + { + cross_product3d( + &rotation_axis, + &biped->object.up, + &biped->object.forward); + } + } + else + { + biped->object.forward = turn_axis; + biped->object.forward.k = 0.f; + biped->object.up = *global_up3d; + } + + normalize3d(&biped->object.forward); + } + } + + biped_verify_object_vectors(biped_index, "post-moving-turn"); + } + else if (biped->biped.state==biped_state_idle && + !flaming && + !TEST_FLAG(biped->unit.flags, _unit_aim_without_turning_bit) && + !TEST_FLAG(biped->unit.control_flags, _unit_control_look_dont_turn_bit)) + { + real stationary_turning_threshold = + TEST_FLAG(biped->unit.control_flags, _unit_control_exact_facing_bit) ? + 0.99000001f : definition->biped.runtime_cosine_stationary_turning_threshold; + + if (facing_alignmentunit.flags, _unit_is_special_bit)) + { + animation->state_desired = turn_right ? + _unit_state_turn_right : _unit_state_turn_left; + } + + biped_verify_object_vectors(biped_index, "post-standing-turn"); + } + } + + return; +} + boolean biped_new( long biped_index) { @@ -1403,3 +2377,236 @@ boolean biped_new( return TRUE; } + +static boolean biped_check_discard( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + + if (!game_engine_running() && + (TEST_FLAG(biped->object.flags, _object_outside_of_map_bit) || + biped->object.location.cluster_index==NONE) && + biped->object.position.z<-2000.f) + { + long actor_index = biped->unit.swarm_actor_index; + + if (actor_index==NONE) + actor_index = biped->unit.actor_index; + + error( + _error_silent, + "WARNING: biped %s (%s) is in a bad place (%.1f %.1f %.1f), erasing", + tag_name_strip_path(tag_get_name(biped->definition_index)), + ai_debug_describe_actor( + actor_index, + biped_index, + TRUE, + temporary, + 256), + biped->object.position.x, + biped->object.position.y, + biped->object.position.z); + object_delete(biped_index); + } + + return FALSE; +} + +boolean biped_update( + long biped_index) +{ + struct biped_datum *biped = biped_get(biped_index); + struct biped_definition *definition = biped_definition_get(biped->definition_index); + struct unit_animation_update_data animation; + + if (debug_biped_skip_update) + return TRUE; + + profile_enter(biped_update_section); + biped_verify_object_vectors(biped_index, "pre-update"); + + animation.state_desired = _unit_state_idle; + animation.crouching = FALSE; + + if (biped->object.parent_object_index!=NONE) + { + struct object_datum *parent = object_get(biped->object.parent_object_index); + + if (parent->object.type==_object_type_vehicle) + { + struct unit_datum *vehicle = vehicle_get(biped->object.parent_object_index); + + biped_vehicle_speech(biped_index); + if (TEST_FLAG(biped->unit.control_flags, _unit_control_action_bit)) + unit_try_and_exit_seat(biped_index); + + if (rider_ejection && + vehicle->object.up.k<0.f && + TEST_FLAG(vehicle->object.flags, _object_on_ground_bit)) + { + unit_exit_seat_end(biped_index); + } + } + else if (parent->object.type==_object_type_biped) + { + animation.state_desired = + (char)((parent->object.damage_flags & FLAG(_object_dead_bit)) | 0x20); + } + } + else + { + biped_snap_facing(biped_index); + if (TEST_FLAG(biped->object.damage_flags, _object_dead_bit) || + !(definition->biped.flags & + (FLAG(_biped_flying_bit) | FLAG(_biped_climbs_anything_bit)))) + { + biped->unit.desired_facing_vector.k = 0.f; + if (normalize3d(&biped->unit.desired_facing_vector)==0.f) + biped->unit.desired_facing_vector = *global_forward3d; + } + + switch (biped->unit.animation.state) + { + case _unit_state_idle: + case _unit_state_turn_left: + case _unit_state_turn_right: + biped->biped.state = biped_state_idle; + break; + + case _unit_state_move_front: + case _unit_state_move_back: + case _unit_state_move_left: + case _unit_state_move_right: + biped->biped.state = biped_state_moving; + break; + + default: + biped->biped.state = biped_state_unknown; + break; + } + + if (magnitude_squared3d(&biped->unit.throttle)<0.01f) + biped->unit.throttle = *global_zero_vector3d; + + if (TEST_FLAG(biped->biped.flags, _biped_airborne_bit)) + { + if (biped->biped.airborne_ticksbiped.airborne_ticks; + } + else + { + biped->biped.airborne_ticks = 0; + } + + if (TEST_FLAG(biped->biped.flags, _biped_slipping_bit)) + { + if (biped->biped.slipping_ticksbiped.slipping_ticks; + } + else + { + biped->biped.slipping_ticks = 0; + } + + animation.state_desired = _unit_state_idle; + animation.crouching = TEST_FLAG( + biped->unit.control_flags, + _unit_control_crouch_modifier_bit); + + biped_verify_object_vectors(biped_index, "pre-turning"); + if (!TEST_FLAG(biped->object.damage_flags, _object_dead_bit)) + { + biped_update_turning(biped_index, &animation); + biped_verify_object_vectors(biped_index, "post-turning"); + } + + biped_update_moving(biped_index, &animation); + biped_verify_object_vectors(biped_index, "post-moving"); + + if (TEST_FLAG(biped->object.damage_flags, _object_dead_bit)) + { + biped_update_dead(biped_index, &animation); + } + else if (TEST_FLAG(biped->biped.flags, _biped_airborne_bit)) + { + biped_update_airborne(biped_index, &animation); + } + else if (biped->biped.landing!=NONE) + { + biped_update_landing(biped_index, &animation); + } + else if (TEST_FLAG(biped->biped.flags, _biped_slipping_bit)) + { + biped_update_slipping(biped_index, &animation); + } + + biped_verify_object_vectors(biped_index, "post-dead/air/land/slip"); + + if (!biped->biped.player_melee_ticks) + { + if (biped->unit.player_index!=NONE && + TEST_FLAG(biped->unit.control_flags, _unit_control_use_equipment_bit)) + { + long weapon_index = unit_inventory_get_weapon( + biped_index, + unit_get(biped_index)->unit.current_weapon_index); + + if (!weapon_prevents_melee_attack(weapon_index) && + biped->unit.current_zoom_level==NONE) + { + char total_time; + char damage_offset; + + unit_animation_start_action(biped_index, _unit_animation_action_melee); + weapon_stop_reload(weapon_index); + first_person_weapon_message_from_unit( + biped_index, + _first_person_weapon_message_melee); + total_time = (char)weapon_get_first_person_animation_time( + weapon_index, + _weapon_first_person_animation_time_frame_count, + _first_person_weapon_animation_melee, + NONE); + biped->biped.player_melee_ticks = total_time; + damage_offset = (char)weapon_get_first_person_animation_time( + weapon_index, + _weapon_first_person_animation_time_private_key_frame, + _first_person_weapon_animation_melee, + NONE); + biped->biped.player_melee_attack_tick = total_time - damage_offset; + biped->biped.player_melee_ticks = total_time - (total_time >> 2); + biped->biped.player_melee_attack_tick = + total_time - damage_offset - (total_time >> 2); + } + } + } + else + { + if (biped->biped.player_melee_ticks==biped->biped.player_melee_attack_tick) + unit_cause_player_melee_damage(biped_index); + --biped->biped.player_melee_ticks; + } + + biped_try_to_make_footsteps(biped_index); + biped_falling_danger(biped_index); + biped_check_discard(biped_index); + } + + if (unit_update_animation(biped_index, &animation)==1) + biped_jump(biped_index); + + if (TEST_FLAG(biped->object.damage_flags, _object_dead_bit) && + TEST_FLAG(biped->object.flags, _object_at_rest_bit)) + { + ++biped->object.idle_ticks; + } + else + { + biped->object.idle_ticks = 0; + } + + biped_verify_object_vectors(biped_index, "post-update"); + profile_exit(biped_update_section); + + return TRUE; +} diff --git a/source/units/bipeds.h b/source/units/bipeds.h index 42a32c96..3f69158c 100644 --- a/source/units/bipeds.h +++ b/source/units/bipeds.h @@ -35,6 +35,7 @@ struct animation; struct object_placement_data; struct real_orientation; struct scenario_object_datum; +struct unit_animation_update_data; struct _biped_datum { @@ -153,6 +154,8 @@ void biped_build_flying_axes( void biped_exit_seat_end( long biped_index, long seat_object_index); +void biped_falling_danger( + long biped_index); void biped_render_debug( long biped_index); short biped_approximate_surface_index( @@ -165,9 +168,15 @@ boolean biped_new( long biped_index); boolean biped_update( long biped_index); +void biped_update_airborne( + long biped_index, + struct unit_animation_update_data *animation); void biped_preprocess_node_orientations( long biped_index, struct real_orientation *node_orientations); +void biped_update_moving( + long biped_index, + struct unit_animation_update_data *animation); /* ---------- globals */ diff --git a/source/units/units.c b/source/units/units.c index d90a942b..f193083a 100644 --- a/source/units/units.c +++ b/source/units/units.c @@ -6736,8 +6736,8 @@ void unit_damage_aftermath( unsigned long damage_flags, real shield_damage, real body_damage, - long unused, - long animation_index) + real body_damage_multiplier, + short body_part) { struct unit_datum *unit; struct unit_definition *unit_definition; @@ -6757,7 +6757,7 @@ void unit_damage_aftermath( _object_being_damaged_body_depleted_bit); feigned = FALSE; - (void)unused; + (void)body_damage_multiplier; if (debug_damage_taken && unit->unit.player_index != NONE) { @@ -6929,7 +6929,7 @@ void unit_damage_aftermath( resists_pings, animation_flag, angle, - (short)animation_index, + body_part, direction_valid ? &damage_direction : NULL); } diff --git a/source/units/units.h b/source/units/units.h index 7c00bcb9..a9140869 100644 --- a/source/units/units.h +++ b/source/units/units.h @@ -840,6 +840,15 @@ boolean unit_drop_current_weapon(long unit_index, boolean immediate); boolean unit_throw_grenade_begin(long unit_index, real_vector2d const *alignment_vector); +void unit_damage_aftermath( + long unit_index, + struct damage_data *damage_data, + unsigned long damage_flags, + real shield_damage, + real body_damage, + real body_damage_multiplier, + short body_part); + void unit_place( long unit_index, struct scenario_unit_datum const *scenario_unit); diff --git a/source/units/vehicles.h b/source/units/vehicles.h index a987891c..b8ba2841 100644 --- a/source/units/vehicles.h +++ b/source/units/vehicles.h @@ -55,6 +55,9 @@ void vehicle_delete( long vehicle_find_pathfinding_surface_index( long vehicle_index, real_point3d *position); +void vehicle_accelerate( + long vehicle_index, + real_vector3d const *acceleration); /* ---------- globals */