# Retail skill-formula calculation ## Oracle - Named retail: `SkillFormula::Calculate @ 0x00591960` in `named-retail/acclient_2013_pseudo_c.txt`. - Retail layout: `SkillFormula` in `named-retail/acclient.h:40199` stores six unsigned 32-bit fields in wire order: `W`, `X`, `Y`, `Z`, `Attr1`, `Attr2`. The signed-looking branches in the recovered x87 code are not signed formula semantics. They are the compiler's unsigned-32-bit-to-floating-point fix-up: when the high bit is set, it adds `2^32` after the initial signed conversion. ## Pseudocode ```text bool SkillFormula.Calculate(uint attribute1, uint attribute2, out uint result) { uint divisor = Z; if (divisor == 0) return false; // Every operation on this line is uint32 and wraps modulo 2^32. uint numerator = X * attribute1 + Y * attribute2 + W; // Formula values are non-negative. Retail rounds to nearest integer, // with exact halves rounding upward. result = uint(floor(double(numerator) / double(divisor) + 0.5)); return true; } ``` Consequences pinned by the port: - `Z == 0` is the only failure gate. `X == 0` remains a valid formula. - `W` participates in the numerator before division; it is not added after division. - multiplication and addition wrap as unsigned 32-bit arithmetic before the floating-point division. - the division rounds half-up; it does not truncate. ## Reference cross-check ACE's [`SkillFormula`](https://github.com/ACEmulator/ACE/blob/master/Source/ACE.DatLoader/Entity/SkillFormula.cs) confirms the unsigned DAT order `W, X, Y, Z, Attr1, Attr2`. ACE's [`AttributeFormula.GetFormula`](https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Server/Entity/AttributeFormula.cs) is only an interpretation aid: it rejects `X == 0`, assumes unit multipliers, omits `W`, and rounds a summed-attribute quotient. That is sufficient for ordinary stock ACE data but is not the general retail algorithm. Chorizite DatReaderWriter's generated [`SkillFormula`](https://github.com/Chorizite/DatReaderWriter/blob/master/DatReaderWriter/Generated/Types/SkillFormula.generated.cs) independently confirms the same six-field order and maps its public names to `W`, `X`, `Y`, and `Z`. It exposes the four arithmetic words as signed `int`, so the retail port must reinterpret their bits as `uint`. Its summary places the additive value after division; the named retail function proves that the actual client adds `W` to the numerator instead. ## Port decision `RetailSkillFormula` is the sole App-layer calculation seam. It reinterprets the DAT words as unsigned, performs the exact unchecked 32-bit numerator, and uses the retail half-up division. `LiveSkillCreditResolver` performs only the skill-table lookup and treats a missing attribute as zero, matching the prior live-property projection behavior.