feat: add src-rect (spritesheet) + animated-color channel fields/mutators

Adapted to Task-1 RE: position is a direct V24 set (not ping-pong); the
oscillating channels are src-rect scroll (0x231/0x239) and color glow (0x232).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-08 22:02:46 -04:00
parent 130eadee3d
commit 7ca51bfdf3
2 changed files with 80 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
using Age.Engine.Model;
using Xunit;
public class AnimChannelTests
{
private static GfxState VisibleObj(long handle)
{
var g = new GfxState();
g.SetSurface(1, resId: 5, colorKey: -1);
g.BindDraw(handle, 1, 0, 0, 256, 64, 100, 100); // 256x64 sheet at base pos (100,100)
return g;
}
[Fact]
public void SetSrcRect_StoresGridCellPeriod()
{
var g = VisibleObj(0x100);
g.SetSrcRect(0x100, gridW: 4, gridH: 1, cell: 2, period: 800);
var o = g.TryGet(0x100)!;
Assert.Equal(4, o.SrcGridW);
Assert.Equal(2, o.SrcCell);
Assert.Equal(800, o.SrcPeriod);
Assert.True(o.SrcAnim);
Assert.Equal(-1, o.SrcStart); // uninitialized until first interpolated frame
}
[Fact]
public void SetSrcRect_ClampsGridToAtLeastOne()
{
var g = VisibleObj(0x100);
g.SetSrcRect(0x100, gridW: 0, gridH: 0, cell: 0, period: 0);
var o = g.TryGet(0x100)!;
Assert.Equal(1, o.SrcGridW);
Assert.Equal(1, o.SrcGridH);
}
[Fact]
public void SetColorAnim_StoresPeriodAndTarget()
{
var g = VisibleObj(0x100);
long target = GfxState.PackColor(0x80, 0xFF0000); // half-alpha red glow
g.SetColorAnim(0x100, period: 1000, target: target);
var o = g.TryGet(0x100)!;
Assert.Equal(1000, o.ColorPeriod);
Assert.Equal(target, o.ColorTarget);
Assert.True(o.ColorAnim);
}
}

View File

@@ -32,6 +32,15 @@ public sealed class GfxState
public long Field64, Field68, Field6c; public long Field64, Field68, Field6c;
public long Color; public long Color;
public bool HasColor; // true once op 0x202/0x203 set a color/alpha modulation on this object public bool HasColor; // true once op 0x202/0x203 set a color/alpha modulation on this object
// ---- src-rect / spritesheet-cell channel (ops 0x239 static cell, 0x231 animate). Interpolator
// SRC-RECT SCROLL channel: period obj+0x230, start obj+0x21c, grid obj+0x238/0x23c. ----
public long SrcGridW = 1, SrcGridH = 1, SrcCell, SrcPeriod, SrcStart = -1;
public bool SrcAnim;
// ---- animated color/glow channel (op 0x232). Interpolator COLOR channel: period obj+0x220,
// start obj+0x20c, target obj+0x240 — PING-PONG (distinct from static 0x202/0x203). ----
public long ColorPeriod, ColorStart = -1, ColorTarget;
public bool ColorAnim;
// draw-texture bind (gfx_object_bind_draw): the surface to draw + its source rect + the visible flag. // draw-texture bind (gfx_object_bind_draw): the surface to draw + its source rect + the visible flag.
public int SourceSlot = -1; public int SourceSlot = -1;
public (int X, int Y, int W, int H) SrcRect; public (int X, int Y, int W, int H) SrcRect;
@@ -137,6 +146,29 @@ public sealed class GfxState
{ {
lock (_lock) { var o = GetOrCreate(handle); o.Color = packed; o.HasColor = true; } lock (_lock) { var o = GetOrCreate(handle); o.Color = packed; o.HasColor = true; }
} }
/// <summary>Ops 0x239 (static cell, period=0) / 0x231 (animate, period&gt;0): set the spritesheet grid +
/// the visible cell. When animated, the interpolator ping-pongs the cell across the grid over the period.</summary>
public void SetSrcRect(long handle, long gridW, long gridH, long cell, long period)
{
lock (_lock)
{
var o = GetOrCreate(handle);
o.SrcGridW = gridW < 1 ? 1 : gridW; o.SrcGridH = gridH < 1 ? 1 : gridH;
o.SrcCell = cell; o.SrcPeriod = period; o.SrcStart = -1; o.SrcAnim = true;
}
}
/// <summary>Op 0x232 (anim-color): ping-pong the object's color/alpha toward <paramref name="target"/>
/// (packed 0xAARRGGBB) over <paramref name="period"/> ms — the pulsing glow. Distinct from static 0x202/0x203.</summary>
public void SetColorAnim(long handle, long period, long target)
{
lock (_lock)
{
var o = GetOrCreate(handle);
o.ColorPeriod = period; o.ColorTarget = target; o.ColorStart = -1; o.ColorAnim = true;
}
}
public void ClearSurface(int slot) { lock (_lock) { _surfaces[slot] = (0, 0); } } // create-texture (blank) public void ClearSurface(int slot) { lock (_lock) { _surfaces[slot] = (0, 0); } } // create-texture (blank)
/// <summary>draw-texture bind (gfx_object_bind_draw): object <paramref name="handle"/> draws surface /// <summary>draw-texture bind (gfx_object_bind_draw): object <paramref name="handle"/> draws surface