Files
bd2/go/internal/server/gamedata/attributes.go
T

73 lines
2.3 KiB
Go

package gamedata
import "math"
// Stat is the small, protocol-independent subset of character statistics
// needed by the local server. The client uses the same two kinds of values
// for every stat: a flat contribution and a percentage contribution.
type Stat int
const (
StatHealth Stat = iota + 1
StatAttack
StatMagic
StatDefencePercent
StatMagicResistancePercent
StatCriticalChance
StatCriticalDamage
StatElementDamage
StatElementResistance
)
// StatContribution is one already-resolved contribution from an equipment
// option, costume, collection, awakening, or another account system. Percent
// is expressed as a fraction (0.08 means 8%), matching the client formula.
type StatContribution struct {
Stat Stat
Flat float64
Percent float64
// Option preserves the exact Define_CharStatOption for systems such as
// awakening whose element-specific distinctions exceed BaseStats.
Option uint64
}
// BaseStats contains the design value after the character level curve has
// been applied. It is deliberately separate from CharacterStats so callers
// cannot accidentally persist a derived value as design data.
type BaseStats struct {
Health float64
Attack float64
Magic float64
}
// AggregateStats applies the final client-side aggregation order:
//
// final = base + round(((base + flat) * (1 + percent)) - base)
//
// The game truncates the design value before applying account contributions;
// the final bonus is truncated toward zero as well. Keeping this operation
// in one place prevents revival, CharInfo, and battle code from developing
// subtly different HP calculations.
func AggregateStats(base BaseStats, contributions []StatContribution) BaseStats {
flat := map[Stat]float64{}
percent := map[Stat]float64{}
for _, contribution := range contributions {
if contribution.Stat == 0 {
continue
}
flat[contribution.Stat] += contribution.Flat
percent[contribution.Stat] += contribution.Percent
}
return BaseStats{
Health: aggregateOne(base.Health, flat[StatHealth], percent[StatHealth]),
Attack: aggregateOne(base.Attack, flat[StatAttack], percent[StatAttack]),
Magic: aggregateOne(base.Magic, flat[StatMagic], percent[StatMagic]),
}
}
func aggregateOne(base, flat, percent float64) float64 {
base = math.Trunc(base)
bonus := ((base + flat) * (1 + percent)) - base
return base + math.Trunc(bonus)
}