acdream/src/AcDream.App/Net/RetailSkillFormula.cs
Erik 557eb7ef6b refactor(net): own live session composition
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>
2026-07-22 10:36:06 +02:00

72 lines
2.2 KiB
C#

using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.App.Net;
/// <summary>
/// Exact unsigned formula used by retail <c>SkillFormula::Calculate @
/// 0x00591960</c>. DAT reader fields are signed storage views, so their bits
/// are deliberately reinterpreted as retail's unsigned W/X/Y/Z words.
/// </summary>
internal static class RetailSkillFormula
{
public static bool TryCalculate(
SkillFormula formula,
uint attribute1,
uint attribute2,
out uint result)
{
ArgumentNullException.ThrowIfNull(formula);
uint divisor = unchecked((uint)formula.Divisor);
if (divisor == 0u)
{
result = 0u;
return false;
}
uint x = unchecked((uint)formula.Attribute1Multiplier);
uint y = unchecked((uint)formula.Attribute2Multiplier);
uint w = unchecked((uint)formula.AdditiveBonus);
uint numerator = unchecked(x * attribute1 + y * attribute2 + w);
result = (uint)Math.Floor((double)numerator / divisor + 0.5d);
return true;
}
}
/// <summary>
/// Named live-session resolver for the attribute contribution to one skill.
/// Table lookup and missing-property policy stay separate from retail math.
/// </summary>
internal sealed class LiveSkillCreditResolver(SkillTable? skillTable)
{
public uint Resolve(
uint skillId,
IReadOnlyDictionary<uint, uint> attributeCurrents)
{
ArgumentNullException.ThrowIfNull(attributeCurrents);
if (skillTable?.Skills is null
|| !skillTable.Skills.TryGetValue(
(DatReaderWriter.Enums.SkillId)skillId,
out var skillBase))
{
return 0u;
}
SkillFormula formula = skillBase.Formula;
attributeCurrents.TryGetValue(
(uint)formula.Attribute1,
out uint attribute1);
attributeCurrents.TryGetValue(
(uint)formula.Attribute2,
out uint attribute2);
return RetailSkillFormula.TryCalculate(
formula,
attribute1,
attribute2,
out uint result)
? result
: 0u;
}
}