Extract reset, selection, entered-world, and route construction behind LiveSessionHost while preserving the sole LiveSessionController authority. Retain partial route and subscription cleanup for retry, and replace the embedded ACE-only shortcut with the exact named-retail unsigned skill formula. Co-authored-by: Codex <codex@openai.com>
112 lines
3.2 KiB
C#
112 lines
3.2 KiB
C#
using AcDream.App.Net;
|
|
using DatReaderWriter.DBObjs;
|
|
using DatReaderWriter.Enums;
|
|
using DatReaderWriter.Types;
|
|
|
|
namespace AcDream.App.Tests.Net;
|
|
|
|
public sealed class RetailSkillFormulaTests
|
|
{
|
|
[Fact]
|
|
public void ZeroDivisorIsTheOnlyFormulaFailureGate()
|
|
{
|
|
SkillFormula invalid = Formula(w: 7, x: 1, y: 1, z: 0);
|
|
Assert.False(RetailSkillFormula.TryCalculate(invalid, 10u, 20u, out uint invalidResult));
|
|
Assert.Equal(0u, invalidResult);
|
|
|
|
SkillFormula zeroX = Formula(w: 7, x: 0, y: 2, z: 3);
|
|
Assert.True(RetailSkillFormula.TryCalculate(zeroX, 10u, 4u, out uint validResult));
|
|
Assert.Equal(5u, validResult);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(9u, 4u, 2u)]
|
|
[InlineData(10u, 4u, 3u)]
|
|
[InlineData(11u, 4u, 3u)]
|
|
[InlineData(12u, 4u, 3u)]
|
|
public void DivisionRoundsToNearestWithExactHalvesUp(
|
|
uint numerator,
|
|
uint divisor,
|
|
uint expected)
|
|
{
|
|
SkillFormula formula = Formula(w: 0, x: 1, y: 0, z: divisor);
|
|
|
|
Assert.True(RetailSkillFormula.TryCalculate(
|
|
formula,
|
|
numerator,
|
|
0u,
|
|
out uint result));
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void AdditiveWordIsInsideTheNumerator()
|
|
{
|
|
SkillFormula formula = Formula(w: 3, x: 1, y: 1, z: 4);
|
|
|
|
Assert.True(RetailSkillFormula.TryCalculate(formula, 4u, 2u, out uint result));
|
|
|
|
Assert.Equal(2u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void NumeratorUsesUnsignedThirtyTwoBitWrap()
|
|
{
|
|
SkillFormula formula = Formula(w: 1, x: 2, y: 0, z: 2);
|
|
|
|
Assert.True(RetailSkillFormula.TryCalculate(
|
|
formula,
|
|
uint.MaxValue,
|
|
0u,
|
|
out uint result));
|
|
|
|
Assert.Equal(0x80000000u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void SignedDatStorageIsReinterpretedAsRetailUnsignedWords()
|
|
{
|
|
SkillFormula formula = Formula(w: -1, x: 0, y: 0, z: 1);
|
|
|
|
Assert.True(RetailSkillFormula.TryCalculate(formula, 0u, 0u, out uint result));
|
|
|
|
Assert.Equal(uint.MaxValue, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void LiveResolverLooksUpTheDatFormulaAndTreatsMissingAttributesAsZero()
|
|
{
|
|
const uint skillId = 0x345u;
|
|
const uint firstAttributeId = 1u;
|
|
const uint secondAttributeId = 2u;
|
|
var skillTable = new SkillTable();
|
|
skillTable.Skills.Add((SkillId)skillId, new SkillBase
|
|
{
|
|
Formula = new SkillFormula
|
|
{
|
|
AdditiveBonus = 1,
|
|
Attribute1Multiplier = 1,
|
|
Attribute2Multiplier = 2,
|
|
Divisor = 3,
|
|
Attribute1 = (AttributeId)firstAttributeId,
|
|
Attribute2 = (AttributeId)secondAttributeId,
|
|
},
|
|
});
|
|
var resolver = new LiveSkillCreditResolver(skillTable);
|
|
|
|
uint result = resolver.Resolve(
|
|
skillId,
|
|
new Dictionary<uint, uint> { [firstAttributeId] = 8u });
|
|
|
|
Assert.Equal(3u, result);
|
|
Assert.Equal(0u, resolver.Resolve(0x999u, new Dictionary<uint, uint>()));
|
|
}
|
|
|
|
private static SkillFormula Formula(int w, int x, int y, uint z) => new()
|
|
{
|
|
AdditiveBonus = w,
|
|
Attribute1Multiplier = x,
|
|
Attribute2Multiplier = y,
|
|
Divisor = unchecked((int)z),
|
|
};
|
|
}
|