Fix FIELD movement sprite ghosting
This commit is contained in:
@@ -66,6 +66,41 @@ public class RenderObjectBlendTests
|
||||
Assert.Equal(BlendKind.Alpha, ro2.Blend);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FieldMovement_StaticAlphaSuppressesOnlyIdleLoopingClone()
|
||||
{
|
||||
const long prototype = 0x9c40;
|
||||
const long idle = prototype + 7;
|
||||
const long moving = prototype + 0x76c;
|
||||
|
||||
var g = new GfxState();
|
||||
g.SetSurface(0x8e, 0x1234, 0x00ff00);
|
||||
|
||||
// FIELD@0x9230: the shared zero-area prototype receives mode-0 color before its loop is armed.
|
||||
g.BindDraw(prototype, 0x3e, 0, 0, 0, 0, 0, 0);
|
||||
g.SetStaticObjectColorResolved(prototype, mode: 0, alpha: 0, rgb: 0);
|
||||
g.SetSrcRect(prototype, frameCount: 4, columns: 2, cell: 0, period: 200);
|
||||
|
||||
// DRAWCH clones the prototype, then draw-texture makes the concrete unit visible.
|
||||
Assert.True(g.CloneObject(prototype, idle));
|
||||
g.BindDraw(idle, 0x8e, 0, 0, 32, 48, 100, 100);
|
||||
Assert.Equal(255, g.SnapshotVisibleObjects().Single(x => x.Handle == idle).Alpha);
|
||||
|
||||
// FIELD@0x45c9 clones the idle unit for motion and @0x45dc suppresses the retained original.
|
||||
Assert.True(g.CloneObject(idle, moving));
|
||||
g.SetStaticObjectColorResolved(idle, mode: 0, alpha: 0, rgb: -1);
|
||||
var movingFrame = g.SnapshotVisibleObjects();
|
||||
Assert.Equal(0, movingFrame.Single(x => x.Handle == idle).Alpha);
|
||||
Assert.Equal(255, movingFrame.Single(x => x.Handle == moving).Alpha);
|
||||
|
||||
// DRAWCH at the next tile republishes the idle object; the following static write can suppress
|
||||
// it for another route segment or restore it at alpha 255 when movement finishes.
|
||||
g.BindDraw(idle, 0x8e, 0, 0, 32, 48, 200, 100);
|
||||
Assert.Equal(255, g.SnapshotVisibleObjects().Single(x => x.Handle == idle).Alpha);
|
||||
g.SetStaticObjectColorResolved(idle, mode: 0, alpha: 255, rgb: 0xffffff);
|
||||
Assert.Equal(255, g.SnapshotVisibleObjects().Single(x => x.Handle == idle).Alpha);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TransitionCleanup_Mode2ToMode0PreservedWhite_RemainsIdentity()
|
||||
{
|
||||
|
||||
@@ -127,6 +127,9 @@ public sealed class GfxState
|
||||
// opacity + multiplicative modulation even after the target commits. This distinguishes ADV chrome
|
||||
// fades from a static mode-0 0x203 such as a CG initialized with 0x00ffffff (opaque identity).
|
||||
public bool OneShotColorBlend;
|
||||
// FIELD applies static mode-0 alpha after binding a looping unit sprite to suppress the retained
|
||||
// idle object while a separate clone moves between tiles. A later draw-texture rebind clears it.
|
||||
public bool StaticLoopColorBlend;
|
||||
|
||||
// ---- src-rect / spritesheet-cell channel (ops 0x239 one-shot cell, 0x231 looping animation).
|
||||
// Native obj+0x238 is the TOTAL FRAME COUNT and +0x23c is the COLUMN COUNT. Each frame keeps
|
||||
@@ -483,7 +486,7 @@ public sealed class GfxState
|
||||
Color = s.Color, HasColor = s.HasColor, StaticColorMode = s.StaticColorMode,
|
||||
OneShotColorTarget = s.OneShotColorTarget, ColorDelayMs = s.ColorDelayMs,
|
||||
ColorDurationMs = s.ColorDurationMs, OneShotColorEnabled = s.OneShotColorEnabled,
|
||||
OneShotColorBlend = s.OneShotColorBlend,
|
||||
OneShotColorBlend = s.OneShotColorBlend, StaticLoopColorBlend = s.StaticLoopColorBlend,
|
||||
SrcFrameCount = s.SrcFrameCount, SrcColumns = s.SrcColumns, SrcCell = s.SrcCell,
|
||||
SrcPeriod = s.SrcPeriod, SrcStart = s.SrcStart, SrcAnim = s.SrcAnim,
|
||||
ColorPeriod = s.ColorPeriod, ColorStart = s.ColorStart, ColorTarget = s.ColorTarget,
|
||||
@@ -630,7 +633,12 @@ public sealed class GfxState
|
||||
public void SetStaticObjectColorResolved(long handle, long mode, long alpha, long rgb)
|
||||
{
|
||||
SetObjectColorResolved(handle, alpha, rgb);
|
||||
lock (_lock) GetOrCreate(handle).StaticColorMode = mode;
|
||||
lock (_lock)
|
||||
{
|
||||
var o = GetOrCreate(handle);
|
||||
o.StaticColorMode = mode;
|
||||
o.StaticLoopColorBlend = mode == 0 && o.Visible && o.SrcAnim;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Ops 0x239 (one-shot cell endpoint, period=0 in this retained model) / 0x231 (looping):
|
||||
@@ -903,6 +911,7 @@ public sealed class GfxState
|
||||
{
|
||||
var o = GetOrCreate(handle);
|
||||
o.SourceSlot = slot; o.SrcRect = (sx, sy, w, h); o.V24 = (dstX, dstY, 0); o.Visible = true;
|
||||
o.StaticLoopColorBlend = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1258,6 +1267,13 @@ public sealed class GfxState
|
||||
{
|
||||
alpha = a; strength = 0; blend = BlendKind.Alpha; multiplyTint = true;
|
||||
}
|
||||
else if (o.StaticColorMode == 0 && o.StaticLoopColorBlend)
|
||||
{
|
||||
// FIELD clones the visible idle unit to its moving handle, then writes alpha zero
|
||||
// to the still-bound looping original. Consume that post-bind write as opacity;
|
||||
// ADV's ordinary static alpha-zero CG initialization remains the opaque mode-0 path.
|
||||
alpha = a; strength = 0; blend = BlendKind.Alpha; multiplyTint = true;
|
||||
}
|
||||
else if (o.StaticColorMode == 2)
|
||||
{
|
||||
// Native transition-source mode: 0xffffffff is opaque identity modulation,
|
||||
|
||||
Reference in New Issue
Block a user