fix: complete retail parity stability pass
This commit is contained in:
parent
d3df4cb20a
commit
f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions
|
|
@ -1,18 +1,16 @@
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Properties;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// House-tab conformance (Batch C, 2026-08-17; two-line correction at the
|
||||
/// same-day morning gate round): the houseless case renders retail's exact
|
||||
/// TWO lines in builder order — <c>gmHouseUI::DisplayBuyPayment
|
||||
/// @0x004a2b30</c>'s houseless branch ("You do not currently own a
|
||||
/// house.", byte-decoded <c>data_7ab688</c> — the m_pHouseData gate only
|
||||
/// selects WHICH text; the emit is unconditional) followed by
|
||||
/// <c>DisplayPurchaseTimeText @0x004a3110</c>'s wait-period line.
|
||||
/// House-tab conformance for retail's complete
|
||||
/// <c>gmHouseUI::DisplayHouseData @0x004a3380</c> builder chain: houseless
|
||||
/// and owned rows, payment grammar, time/location math, warning colors,
|
||||
/// incremental rent notices, and session reset.
|
||||
/// </summary>
|
||||
public sealed class RuntimeHouseStateTests
|
||||
{
|
||||
|
|
@ -73,17 +71,146 @@ public sealed class RuntimeHouseStateTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void HouseData_OwnedHouseWithExpiredWaitPeriod_ShowsAbandonFirstLine()
|
||||
public void HouseData_OwnedUnpaidCottage_ComposesEveryRetailBuilderInOrder()
|
||||
{
|
||||
var clock = new ManualTimeProvider();
|
||||
var objects = new ClientObjectTable();
|
||||
objects.AddOrUpdate(new ClientObject { ObjectId = Self, Type = ItemType.Creature });
|
||||
var house = new RuntimeHouseState(objects);
|
||||
var house = new RuntimeHouseState(objects, clock);
|
||||
const uint buyTime = 1_700_000_000u;
|
||||
const uint rentTime = 1_700_086_400u;
|
||||
uint location = LandDefs.LcoordToGid(1200, 800);
|
||||
var data = new GameEvents.HouseData(
|
||||
BuyTime: buyTime,
|
||||
RentTime: rentTime,
|
||||
Type: 1u,
|
||||
MaintenanceFree: false,
|
||||
Buy:
|
||||
[
|
||||
new GameEvents.HousePayment(1, 0, 273u, "Pyreal", "Pyreals"),
|
||||
new GameEvents.HousePayment(2, 0, 274u, "Trade Note", string.Empty),
|
||||
],
|
||||
Rent:
|
||||
[
|
||||
new GameEvents.HousePayment(10, 4, 273u, "Pyreal", "Pyreals"),
|
||||
new GameEvents.HousePayment(2, 2, 274u, "Box", string.Empty),
|
||||
],
|
||||
Position: Position(location));
|
||||
|
||||
house.ApplyHouseData(SampleHouseData(), Self);
|
||||
house.ApplyHouseData(data, Self);
|
||||
|
||||
Assert.Equal(
|
||||
["You may buy another house immediately after you abandon this one."],
|
||||
[
|
||||
"The purchase price for this dwelling is:\n1 Pyreal, 2 Trade Notes",
|
||||
"Rent:\n4/10 Pyreals, 2/2 Boxes",
|
||||
"Bought: " + RetailTime(buyTime),
|
||||
"This maintenance period ends: " + RetailTime(rentTime + 2_592_000L),
|
||||
"Maintenance is next due: " + RetailTime(rentTime + 2_592_000L),
|
||||
"Location: 21.9S, 18.1E",
|
||||
"Warning! You have not paid your maintenance costs for the last "
|
||||
+ "30 day maintenance period. Please pay these costs by this deadline"
|
||||
+ " or you will lose your house, and all your items within it.",
|
||||
"You may buy another house immediately after you abandon this one.",
|
||||
],
|
||||
house.Lines);
|
||||
|
||||
Assert.All(house.PanelLines.Take(6),
|
||||
line => Assert.Equal(HousePanelTextColor.Normal, line.Color));
|
||||
Assert.Equal(HousePanelTextColor.RentNotPaid, house.PanelLines[6].Color);
|
||||
Assert.Equal(HousePanelTextColor.Normal, house.PanelLines[7].Color);
|
||||
Assert.Equal(data.Position, house.Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HouseData_PaidRent_UsesSecondPeriodDueDateAndPaidColor()
|
||||
{
|
||||
const uint rentTime = 1_700_086_400u;
|
||||
var house = new RuntimeHouseState(timeProvider: new ManualTimeProvider());
|
||||
GameEvents.HouseData data = SampleHouseData() with
|
||||
{
|
||||
RentTime = rentTime,
|
||||
Type = 1u,
|
||||
Rent = [new GameEvents.HousePayment(5, 5, 1u, "Token", "Tokens")],
|
||||
};
|
||||
|
||||
house.ApplyHouseData(data, Self);
|
||||
|
||||
Assert.Equal(
|
||||
"Maintenance is next due: " + RetailTime(rentTime + 5_184_000L),
|
||||
house.Lines[4]);
|
||||
Assert.Equal(
|
||||
"The maintenance has already been paid for this period. "
|
||||
+ "You may not prepay next period's maintenance.",
|
||||
house.Lines[^2]);
|
||||
Assert.Equal(HousePanelTextColor.RentPaid, house.PanelLines[^2].Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HouseData_Apartment_UsesNinetyDayPeriodAndOmitsLocation()
|
||||
{
|
||||
const uint rentTime = 1_700_086_400u;
|
||||
var house = new RuntimeHouseState(timeProvider: new ManualTimeProvider());
|
||||
GameEvents.HouseData data = SampleHouseData() with
|
||||
{
|
||||
RentTime = rentTime,
|
||||
Type = 4u,
|
||||
MaintenanceFree = true,
|
||||
Position = Position(LandDefs.LcoordToGid(1200, 800)),
|
||||
};
|
||||
|
||||
house.ApplyHouseData(data, Self);
|
||||
|
||||
Assert.Equal(
|
||||
"This maintenance period ends: " + RetailTime(rentTime + 7_776_000L),
|
||||
house.Lines[3]);
|
||||
Assert.Equal(
|
||||
"Maintenance is next due: " + RetailTime(rentTime + 15_552_000L),
|
||||
house.Lines[4]);
|
||||
Assert.DoesNotContain(house.Lines, line => line.StartsWith("Location: "));
|
||||
Assert.Null(house.Position);
|
||||
Assert.Equal(HousePanelTextColor.RentPaid, house.PanelLines[^2].Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RentNotices_UpdateRetainedSnapshotAndRefreshPanel()
|
||||
{
|
||||
var house = new RuntimeHouseState(timeProvider: new ManualTimeProvider());
|
||||
GameEvents.HouseData data = SampleHouseData() with
|
||||
{
|
||||
RentTime = 1_700_000_000u,
|
||||
Rent = [new GameEvents.HousePayment(10, 10, 1u, "Pyreal", "Pyreals")],
|
||||
};
|
||||
house.ApplyHouseData(data, Self);
|
||||
Assert.Equal(HousePanelTextColor.RentPaid, house.PanelLines[^2].Color);
|
||||
|
||||
house.ApplyRentTime(1_710_000_000u, Self);
|
||||
|
||||
Assert.Equal("Rent:\n0/10 Pyreals", house.Lines[1]);
|
||||
Assert.Equal(HousePanelTextColor.RentNotPaid, house.PanelLines[^2].Color);
|
||||
Assert.Equal(
|
||||
"This maintenance period ends: " + RetailTime(1_712_592_000L),
|
||||
house.Lines[3]);
|
||||
|
||||
house.ApplyRentPayment(
|
||||
[new GameEvents.HousePayment(10, 10, 1u, "Pyreal", "Pyreals")], Self);
|
||||
|
||||
Assert.Equal("Rent:\n10/10 Pyreals", house.Lines[1]);
|
||||
Assert.Equal(HousePanelTextColor.RentPaid, house.PanelLines[^2].Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HouseData_DefensivelyCopiesPaymentLists()
|
||||
{
|
||||
var buy = new List<GameEvents.HousePayment>
|
||||
{
|
||||
new(1, 0, 1u, "Token", "Tokens"),
|
||||
};
|
||||
var house = new RuntimeHouseState();
|
||||
house.ApplyHouseData(SampleHouseData() with { Buy = buy }, Self);
|
||||
|
||||
buy[0] = new GameEvents.HousePayment(99, 0, 1u, "Changed", "Changed");
|
||||
|
||||
Assert.Equal("The purchase price for this dwelling is:\n1 Token", house.Lines[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -181,6 +308,8 @@ public sealed class RuntimeHouseStateTests
|
|||
house.ResetSession();
|
||||
|
||||
Assert.Empty(house.Lines);
|
||||
Assert.Empty(house.PanelLines);
|
||||
Assert.Null(house.Position);
|
||||
Assert.False(house.HasReceivedNotice);
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +339,13 @@ public sealed class RuntimeHouseStateTests
|
|||
Rent: Array.Empty<GameEvents.HousePayment>(),
|
||||
Position: new CreateObject.ServerPosition(0u, 0f, 0f, 0f, 1f, 0f, 0f, 0f));
|
||||
|
||||
private static CreateObject.ServerPosition Position(uint cellId) =>
|
||||
new(cellId, 0f, 0f, 0f, 1f, 0f, 0f, 0f);
|
||||
|
||||
private static string RetailTime(long epochSeconds) =>
|
||||
DateTimeOffset.FromUnixTimeSeconds(epochSeconds).UtcDateTime
|
||||
.ToString(System.Globalization.CultureInfo.CurrentCulture);
|
||||
|
||||
private sealed class ManualTimeProvider : TimeProvider
|
||||
{
|
||||
private DateTimeOffset _now = new(2026, 8, 17, 0, 0, 0, TimeSpan.Zero);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue