Implement memoir sub-status system with level-based unlocks

This commit is contained in:
Ilya Groshev
2026-04-22 14:03:26 +03:00
parent 7828de031c
commit 4dc722c5d3
12 changed files with 228 additions and 1 deletions
@@ -39,6 +39,8 @@ func (f NumericalFunc) Evaluate(value int32) int32 {
p[1]*value*value/1000 +
p[2]*value/1000 +
p[3]
case model.NumericalFunctionTypePartsMainOption:
return p[0]*value/1000 + p[1]
default:
return 0
}
+68
View File
@@ -7,6 +7,13 @@ import (
"lunar-tear/server/internal/utils"
)
type PartsStatusMainDef struct {
StatusKindType int32
StatusCalculationType int32
StatusChangeInitialValue int32
StatusNumericalFunctionId int32
}
type PartsCatalog struct {
PartsById map[int32]EntityMParts
DefaultPartsStatusMainByLotteryGroup map[int32]int32
@@ -14,6 +21,11 @@ type PartsCatalog struct {
RateByGroupAndLevel map[int32]map[int32]int32
PriceByGroupAndLevel map[int32]map[int32]int32
SellPriceByRarity map[model.RarityType]NumericalFunc
PartsStatusMainById map[int32]PartsStatusMainDef
SubStatusPool map[int32][]int32 // lotteryGroupId -> eligible PartsStatusMainIds
SubStatusUnlockLvls map[model.RarityType][]int32 // rarity -> levels where sub-slots unlock
FuncResolver *FunctionResolver
}
func LoadPartsCatalog() (*PartsCatalog, error) {
@@ -83,6 +95,16 @@ func LoadPartsCatalog() (*PartsCatalog, error) {
priceByGroupAndLevel[p.PartsLevelUpPriceGroupId][p.LevelLowerLimit] = p.Gold
}
partsStatusMainById, subStatusPool := buildPartsStatusMain()
unlockLvls := []int32{3, 6, 9, 12}
subStatusUnlockLvls := map[model.RarityType][]int32{
model.RarityNormal: unlockLvls,
model.RarityRare: unlockLvls,
model.RaritySRare: unlockLvls,
model.RaritySSRare: unlockLvls,
}
return &PartsCatalog{
PartsById: partsById,
DefaultPartsStatusMainByLotteryGroup: defaultPartsStatusMainByLotteryGroup,
@@ -90,5 +112,51 @@ func LoadPartsCatalog() (*PartsCatalog, error) {
RateByGroupAndLevel: rateByGroupAndLevel,
PriceByGroupAndLevel: priceByGroupAndLevel,
SellPriceByRarity: sellPriceByRarity,
PartsStatusMainById: partsStatusMainById,
SubStatusPool: subStatusPool,
SubStatusUnlockLvls: subStatusUnlockLvls,
FuncResolver: funcResolver,
}, nil
}
// buildPartsStatusMain constructs the 36 PartsStatusMain definitions and
// groups them into sub-status lottery pools by tier (1-4).
// The data mirrors EntityMPartsStatusMainTable.json which is structured as
// 9 stat categories x 4 tiers. Tier within each category maps to the
// PartsStatusSubLotteryGroupId on the part definition.
func buildPartsStatusMain() (map[int32]PartsStatusMainDef, map[int32][]int32) {
type statCat struct {
kindType int32
calcType int32
initVals [4]int32
funcStart int32
}
cats := []statCat{
{2, 1, [4]int32{50, 100, 150, 250}, 101}, // Attack flat
{7, 1, [4]int32{50, 100, 150, 250}, 101}, // Vitality flat
{2, 2, [4]int32{10, 30, 70, 120}, 105}, // Attack %
{7, 2, [4]int32{10, 30, 70, 120}, 105}, // Vitality %
{6, 2, [4]int32{10, 30, 70, 120}, 105}, // HP %
{6, 1, [4]int32{600, 1200, 1800, 3000}, 109}, // HP flat
{4, 1, [4]int32{10, 30, 70, 120}, 113}, // CritRatio
{3, 1, [4]int32{20, 50, 80, 100}, 117}, // CritAttack
{1, 1, [4]int32{10, 20, 30, 40}, 121}, // Agility
}
defs := make(map[int32]PartsStatusMainDef, 36)
pool := map[int32][]int32{1: {}, 2: {}, 3: {}, 4: {}}
id := int32(1)
for _, c := range cats {
for tier := 0; tier < 4; tier++ {
defs[id] = PartsStatusMainDef{
StatusKindType: c.kindType,
StatusCalculationType: c.calcType,
StatusChangeInitialValue: c.initVals[tier],
StatusNumericalFunctionId: c.funcStart + int32(tier),
}
pool[int32(tier+1)] = append(pool[int32(tier+1)], id)
id++
}
}
return defs, pool
}