fix(physics): #265 landing-bounce family - retail check_contact seed + velocity-free landing commit
Retail jump landings BOUNCE: the floor touch records both a contact plane (grounding) AND a collision normal (collided_with_environment), and handle_all_collisions reflects the unmodified impact velocity off it at 5% elasticity (v += -(v.n)(elasticity+1).n, DEFAULT_ELASTICITY 0.05 @0x007c6a7c). Our transition already recorded both facts; the bounce was suppressed by the AD-25 adaptation stack in the per-tick commit: a Velocity.Z<=0 landing gate (needed because the resolver glued ascending movers to the ground) plus a landing Velocity.Z=0 hand-zero whose stated purpose was making the reflect a no-op. Downhill glided instead of bouncing, flat-ground landings had no pop, and uphill jumps flapped between grounded/airborne against the animation machine. Three retail mechanisms replace the stack: - check_contact (0x0050f5b0) seeding in ResolveWithTransition: a body in CONTACT seeds the transition's contact only while v.contactPlane.N <= 0.0002; moving away seeds the last-known plane alone (get_object_info 0x00511cc0). Ascending jumps therefore run contact-free (ballistic, no glue) - the gate's reason-for-being is gone. The plane requirement is strict: Contact-without-plane is unrepresentable in retail. - SetPositionInternal-shaped commit (0x00515330, byte-read end-to-end, velocity-sign-FREE): contact purely from the transition's contact plane, HitGround on the airborne->walkable edge, HandleAllCollisions with unmodified impact velocity. Whole commit gated on Ok && candidateMoved (retail pc:283657 skips SetPositionInternal entirely when the candidate did not move) - a standing body's contact state is never re-derived, which is what keeps rest bit-stable (AD-41 updated). - Byte decodes: gate override state&0x800000=Sledding, zero branch state&0x20000=Inelastic, reflect strictly dot<0 - our port already had all three correct. Settle: real landings (>=0.25 m/s) bounce and decay geometrically; smaller impacts are consumed by retail's unconditional small-velocity zero, so standing never micro-bounces. Re-baselines documented in place: landing-survival pin measures decay post-settle; LiveCompare_Tick0/376 pin the new IsOnGround=false on zero-move ticks (captured true was the retired seed echo; tick 376's captured body carries an 11.8 m/s grounded velocity from the deleted get_state_velocity-overwrite era); de-overlap fixture now carries the plane real grounded bodies always have. New pins: LandingBounceSeedingTests (ascent no-seed, rest keeps contact, strict plane, slope 5% reversal + tangential preservation, Sledding override). Investigation + implementation record: docs/research/2026-07-30-landing-bounce-family.md. Complete Release suite: 10,031 passed / 5 skips / 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7fcc7db1d1
commit
2d611b2b01
10 changed files with 620 additions and 84 deletions
77
tests/AcDream.App.Tests/UI/Layout/VitaeColorDumpProbe.cs
Normal file
77
tests/AcDream.App.Tests/UI/Layout/VitaeColorDumpProbe.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using AcDream.App.UI.Layout;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
// THROWAWAY probe (#268): dump the authored 0x1B font-color arrays for the
|
||||
// character-panel footer labels so we can read retail's vitae parenthetical
|
||||
// color (AppendTextWithFont color index 3, gmSkillUI 0x0049b972). Delete
|
||||
// after the color constant is captured.
|
||||
public sealed class VitaeColorDumpProbe
|
||||
{
|
||||
[Fact]
|
||||
public void Dump_footer_font_color_arrays()
|
||||
{
|
||||
var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"Documents", "Asheron's Call");
|
||||
if (!Directory.Exists(datDir)) return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
foreach (uint layoutId in new[] { 0x2100002Eu, 0x2100002Cu })
|
||||
{
|
||||
var root = LayoutImporter.ImportInfos(dats, layoutId);
|
||||
if (root is null) { sb.AppendLine($"layout {layoutId:X8}: NOT FOUND"); continue; }
|
||||
sb.AppendLine($"=== layout {layoutId:X8} ===");
|
||||
Walk(root, sb);
|
||||
}
|
||||
|
||||
var outPath = Path.Combine(AppContext.BaseDirectory, "vitae-color-dump.txt");
|
||||
File.WriteAllText(outPath, sb.ToString());
|
||||
// Also drop a copy next to the repo artifacts when resolvable.
|
||||
var repoCopy = Environment.GetEnvironmentVariable("ACDREAM_PROBE_OUT");
|
||||
if (!string.IsNullOrEmpty(repoCopy))
|
||||
File.WriteAllText(repoCopy, sb.ToString());
|
||||
}
|
||||
|
||||
private static void Walk(ElementInfo e, StringBuilder sb)
|
||||
{
|
||||
if (e.TryGetEffectiveProperty(0x1Bu, out var color))
|
||||
{
|
||||
if (color.Kind == UiPropertyKind.Array && color.ArrayValue.Count > 1)
|
||||
{
|
||||
sb.Append($"element {e.Id:X8} 0x1B array[{color.ArrayValue.Count}]:");
|
||||
for (int i = 0; i < color.ArrayValue.Count; i++)
|
||||
{
|
||||
var v = color.ArrayValue[i];
|
||||
if (v.Kind == UiPropertyKind.Color)
|
||||
{
|
||||
var c = v.ColorValue;
|
||||
sb.Append($" [{i}]=A{c.Alpha:D3},R{c.Red:D3},G{c.Green:D3},B{c.Blue:D3}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append($" [{i}]=kind:{v.Kind}");
|
||||
}
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
else if (color.Kind == UiPropertyKind.Color)
|
||||
{
|
||||
var c = color.ColorValue;
|
||||
sb.AppendLine(
|
||||
$"element {e.Id:X8} 0x1B single: A{c.Alpha:D3},R{c.Red:D3},G{c.Green:D3},B{c.Blue:D3}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in e.Children)
|
||||
Walk(child, sb);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue