mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Implement memoir sub-status system with level-based unlocks
This commit is contained in:
@@ -39,6 +39,8 @@ func (f NumericalFunc) Evaluate(value int32) int32 {
|
|||||||
p[1]*value*value/1000 +
|
p[1]*value*value/1000 +
|
||||||
p[2]*value/1000 +
|
p[2]*value/1000 +
|
||||||
p[3]
|
p[3]
|
||||||
|
case model.NumericalFunctionTypePartsMainOption:
|
||||||
|
return p[0]*value/1000 + p[1]
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ import (
|
|||||||
"lunar-tear/server/internal/utils"
|
"lunar-tear/server/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PartsStatusMainDef struct {
|
||||||
|
StatusKindType int32
|
||||||
|
StatusCalculationType int32
|
||||||
|
StatusChangeInitialValue int32
|
||||||
|
StatusNumericalFunctionId int32
|
||||||
|
}
|
||||||
|
|
||||||
type PartsCatalog struct {
|
type PartsCatalog struct {
|
||||||
PartsById map[int32]EntityMParts
|
PartsById map[int32]EntityMParts
|
||||||
DefaultPartsStatusMainByLotteryGroup map[int32]int32
|
DefaultPartsStatusMainByLotteryGroup map[int32]int32
|
||||||
@@ -14,6 +21,11 @@ type PartsCatalog struct {
|
|||||||
RateByGroupAndLevel map[int32]map[int32]int32
|
RateByGroupAndLevel map[int32]map[int32]int32
|
||||||
PriceByGroupAndLevel map[int32]map[int32]int32
|
PriceByGroupAndLevel map[int32]map[int32]int32
|
||||||
SellPriceByRarity map[model.RarityType]NumericalFunc
|
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) {
|
func LoadPartsCatalog() (*PartsCatalog, error) {
|
||||||
@@ -83,6 +95,16 @@ func LoadPartsCatalog() (*PartsCatalog, error) {
|
|||||||
priceByGroupAndLevel[p.PartsLevelUpPriceGroupId][p.LevelLowerLimit] = p.Gold
|
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{
|
return &PartsCatalog{
|
||||||
PartsById: partsById,
|
PartsById: partsById,
|
||||||
DefaultPartsStatusMainByLotteryGroup: defaultPartsStatusMainByLotteryGroup,
|
DefaultPartsStatusMainByLotteryGroup: defaultPartsStatusMainByLotteryGroup,
|
||||||
@@ -90,5 +112,51 @@ func LoadPartsCatalog() (*PartsCatalog, error) {
|
|||||||
RateByGroupAndLevel: rateByGroupAndLevel,
|
RateByGroupAndLevel: rateByGroupAndLevel,
|
||||||
PriceByGroupAndLevel: priceByGroupAndLevel,
|
PriceByGroupAndLevel: priceByGroupAndLevel,
|
||||||
SellPriceByRarity: sellPriceByRarity,
|
SellPriceByRarity: sellPriceByRarity,
|
||||||
|
PartsStatusMainById: partsStatusMainById,
|
||||||
|
SubStatusPool: subStatusPool,
|
||||||
|
SubStatusUnlockLvls: subStatusUnlockLvls,
|
||||||
|
FuncResolver: funcResolver,
|
||||||
}, nil
|
}, 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ func (s *PartsServiceServer) Sell(ctx context.Context, req *pb.PartsSellRequest)
|
|||||||
gold := sellFunc.Evaluate(part.Level)
|
gold := sellFunc.Evaluate(part.Level)
|
||||||
totalGold += gold
|
totalGold += gold
|
||||||
delete(user.Parts, uuid)
|
delete(user.Parts, uuid)
|
||||||
|
for k := range user.PartsStatusSubs {
|
||||||
|
if k.UserPartsUuid == uuid {
|
||||||
|
delete(user.PartsStatusSubs, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
log.Printf("[PartsService] Sell: uuid=%s partsId=%d level=%d -> %d gold", uuid, part.PartsId, part.Level, gold)
|
log.Printf("[PartsService] Sell: uuid=%s partsId=%d level=%d -> %d gold", uuid, part.PartsId, part.Level, gold)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +136,8 @@ func (s *PartsServiceServer) Enhance(ctx context.Context, req *pb.PartsEnhanceRe
|
|||||||
isSuccess = true
|
isSuccess = true
|
||||||
log.Printf("[PartsService] Enhance: SUCCESS partsId=%d level %d -> %d (rate=%d‰, cost=%d gold)",
|
log.Printf("[PartsService] Enhance: SUCCESS partsId=%d level %d -> %d (rate=%d‰, cost=%d gold)",
|
||||||
part.PartsId, part.Level-1, part.Level, successRate, goldCost)
|
part.PartsId, part.Level-1, part.Level, successRate, goldCost)
|
||||||
|
|
||||||
|
s.grantSubStatuses(user, req.UserPartsUuid, part, partDef, nowMillis)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[PartsService] Enhance: FAIL partsId=%d stays level %d (rate=%d‰, cost=%d gold)",
|
log.Printf("[PartsService] Enhance: FAIL partsId=%d stays level %d (rate=%d‰, cost=%d gold)",
|
||||||
part.PartsId, part.Level, successRate, goldCost)
|
part.PartsId, part.Level, successRate, goldCost)
|
||||||
@@ -148,6 +155,49 @@ func (s *PartsServiceServer) Enhance(ctx context.Context, req *pb.PartsEnhanceRe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PartsServiceServer) grantSubStatuses(user *store.UserState, uuid string, part store.PartsState, partDef masterdata.EntityMParts, nowMillis int64) {
|
||||||
|
unlockLevels := s.catalog.SubStatusUnlockLvls[partDef.RarityType]
|
||||||
|
pool := s.catalog.SubStatusPool[partDef.PartsStatusSubLotteryGroupId]
|
||||||
|
if len(pool) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for slotIdx, lvl := range unlockLevels {
|
||||||
|
if part.Level != lvl {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
statusIndex := int32(slotIdx + 1)
|
||||||
|
key := store.PartsStatusSubKey{UserPartsUuid: uuid, StatusIndex: statusIndex}
|
||||||
|
if _, exists := user.PartsStatusSubs[key]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pick := pool[rand.Intn(len(pool))]
|
||||||
|
def, ok := s.catalog.PartsStatusMainById[pick]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
statusValue := def.StatusChangeInitialValue
|
||||||
|
if f, ok := s.catalog.FuncResolver.Resolve(def.StatusNumericalFunctionId); ok {
|
||||||
|
statusValue = f.Evaluate(part.Level)
|
||||||
|
}
|
||||||
|
|
||||||
|
user.PartsStatusSubs[key] = store.PartsStatusSubState{
|
||||||
|
UserPartsUuid: uuid,
|
||||||
|
StatusIndex: statusIndex,
|
||||||
|
PartsStatusSubLotteryId: pick,
|
||||||
|
Level: part.Level,
|
||||||
|
StatusKindType: def.StatusKindType,
|
||||||
|
StatusCalculationType: def.StatusCalculationType,
|
||||||
|
StatusChangeValue: statusValue,
|
||||||
|
LatestVersion: nowMillis,
|
||||||
|
}
|
||||||
|
log.Printf("[PartsService] Enhance: granted sub-status slot=%d lotteryId=%d kind=%d calc=%d val=%d",
|
||||||
|
statusIndex, pick, def.StatusKindType, def.StatusCalculationType, statusValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PartsServiceServer) ReplacePreset(ctx context.Context, req *pb.PartsReplacePresetRequest) (*pb.PartsReplacePresetResponse, error) {
|
func (s *PartsServiceServer) ReplacePreset(ctx context.Context, req *pb.PartsReplacePresetRequest) (*pb.PartsReplacePresetResponse, error) {
|
||||||
log.Printf("[PartsService] ReplacePreset: preset=%d uuids=[%s, %s, %s]",
|
log.Printf("[PartsService] ReplacePreset: preset=%d uuids=[%s, %s, %s]",
|
||||||
req.UserPartsPresetNumber, req.UserPartsUuid01, req.UserPartsUuid02, req.UserPartsUuid03)
|
req.UserPartsPresetNumber, req.UserPartsUuid01, req.UserPartsUuid02, req.UserPartsUuid03)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func CloneUserState(u UserState) UserState {
|
|||||||
out.Parts = maps.Clone(u.Parts)
|
out.Parts = maps.Clone(u.Parts)
|
||||||
out.PartsGroupNotes = maps.Clone(u.PartsGroupNotes)
|
out.PartsGroupNotes = maps.Clone(u.PartsGroupNotes)
|
||||||
out.PartsPresets = maps.Clone(u.PartsPresets)
|
out.PartsPresets = maps.Clone(u.PartsPresets)
|
||||||
|
out.PartsStatusSubs = maps.Clone(u.PartsStatusSubs)
|
||||||
out.ImportantItems = maps.Clone(u.ImportantItems)
|
out.ImportantItems = maps.Clone(u.ImportantItems)
|
||||||
out.CostumeActiveSkills = maps.Clone(u.CostumeActiveSkills)
|
out.CostumeActiveSkills = maps.Clone(u.CostumeActiveSkills)
|
||||||
out.WeaponSkills = cloneSliceMap(u.WeaponSkills)
|
out.WeaponSkills = cloneSliceMap(u.WeaponSkills)
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ func SeedUserState(userId int64, uuid string, nowMillis int64, platform model.Cl
|
|||||||
Parts: make(map[string]PartsState),
|
Parts: make(map[string]PartsState),
|
||||||
PartsGroupNotes: make(map[int32]PartsGroupNoteState),
|
PartsGroupNotes: make(map[int32]PartsGroupNoteState),
|
||||||
PartsPresets: make(map[int32]PartsPresetState),
|
PartsPresets: make(map[int32]PartsPresetState),
|
||||||
|
PartsStatusSubs: make(map[PartsStatusSubKey]PartsStatusSubState),
|
||||||
ImportantItems: make(map[int32]int32),
|
ImportantItems: make(map[int32]int32),
|
||||||
CostumeActiveSkills: make(map[string]CostumeActiveSkillState),
|
CostumeActiveSkills: make(map[string]CostumeActiveSkillState),
|
||||||
WeaponSkills: make(map[string][]WeaponSkillState),
|
WeaponSkills: make(map[string][]WeaponSkillState),
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ func initMaps(u *store.UserState) {
|
|||||||
u.Parts = make(map[string]store.PartsState)
|
u.Parts = make(map[string]store.PartsState)
|
||||||
u.PartsGroupNotes = make(map[int32]store.PartsGroupNoteState)
|
u.PartsGroupNotes = make(map[int32]store.PartsGroupNoteState)
|
||||||
u.PartsPresets = make(map[int32]store.PartsPresetState)
|
u.PartsPresets = make(map[int32]store.PartsPresetState)
|
||||||
|
u.PartsStatusSubs = make(map[store.PartsStatusSubKey]store.PartsStatusSubState)
|
||||||
u.DeckTypeNotes = make(map[model.DeckType]store.DeckTypeNoteState)
|
u.DeckTypeNotes = make(map[model.DeckType]store.DeckTypeNoteState)
|
||||||
u.ConsumableItems = make(map[int32]int32)
|
u.ConsumableItems = make(map[int32]int32)
|
||||||
u.Materials = make(map[int32]int32)
|
u.Materials = make(map[int32]int32)
|
||||||
@@ -453,6 +454,16 @@ func loadMapTables(db *sql.DB, uid int64, u *store.UserState) {
|
|||||||
u.PartsPresets[v.UserPartsPresetNumber] = v
|
u.PartsPresets[v.UserPartsPresetNumber] = v
|
||||||
})
|
})
|
||||||
|
|
||||||
|
queryRows(db, `SELECT user_parts_uuid, status_index, parts_status_sub_lottery_id, level,
|
||||||
|
status_kind_type, status_calculation_type, status_change_value, latest_version
|
||||||
|
FROM user_parts_status_subs WHERE user_id=?`, uid,
|
||||||
|
func(rows *sql.Rows) {
|
||||||
|
var v store.PartsStatusSubState
|
||||||
|
rows.Scan(&v.UserPartsUuid, &v.StatusIndex, &v.PartsStatusSubLotteryId, &v.Level,
|
||||||
|
&v.StatusKindType, &v.StatusCalculationType, &v.StatusChangeValue, &v.LatestVersion)
|
||||||
|
u.PartsStatusSubs[store.PartsStatusSubKey{UserPartsUuid: v.UserPartsUuid, StatusIndex: v.StatusIndex}] = v
|
||||||
|
})
|
||||||
|
|
||||||
queryRows(db, `SELECT deck_type, max_deck_power, latest_version FROM user_deck_type_notes WHERE user_id=?`, uid,
|
queryRows(db, `SELECT deck_type, max_deck_power, latest_version FROM user_deck_type_notes WHERE user_id=?`, uid,
|
||||||
func(rows *sql.Rows) {
|
func(rows *sql.Rows) {
|
||||||
var dt int32
|
var dt int32
|
||||||
|
|||||||
@@ -290,6 +290,12 @@ func writeUserState(tx *sql.Tx, uid int64, u *store.UserState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, v := range u.PartsStatusSubs {
|
||||||
|
if err := exec(`INSERT INTO user_parts_status_subs (user_id, user_parts_uuid, status_index, parts_status_sub_lottery_id, level, status_kind_type, status_calculation_type, status_change_value, latest_version) VALUES (?,?,?,?,?,?,?,?,?)`,
|
||||||
|
uid, v.UserPartsUuid, v.StatusIndex, v.PartsStatusSubLotteryId, v.Level, v.StatusKindType, v.StatusCalculationType, v.StatusChangeValue, v.LatestVersion); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, v := range u.DeckTypeNotes {
|
for _, v := range u.DeckTypeNotes {
|
||||||
if err := exec(`INSERT INTO user_deck_type_notes (user_id, deck_type, max_deck_power, latest_version) VALUES (?,?,?,?)`,
|
if err := exec(`INSERT INTO user_deck_type_notes (user_id, deck_type, max_deck_power, latest_version) VALUES (?,?,?,?)`,
|
||||||
uid, int32(v.DeckType), v.MaxDeckPower, v.LatestVersion); err != nil {
|
uid, int32(v.DeckType), v.MaxDeckPower, v.LatestVersion); err != nil {
|
||||||
@@ -808,6 +814,18 @@ func diffAndSave(tx *sql.Tx, uid int64, before, after *store.UserState) error {
|
|||||||
return []any{v.UserPartsPresetNumber, v.UserPartsUuid01, v.UserPartsUuid02, v.UserPartsUuid03, v.Name, v.UserPartsPresetTagNumber, v.LatestVersion}
|
return []any{v.UserPartsPresetNumber, v.UserPartsUuid01, v.UserPartsUuid02, v.UserPartsUuid03, v.Name, v.UserPartsPresetTagNumber, v.LatestVersion}
|
||||||
}, "user_parts_preset_number, user_parts_uuid01, user_parts_uuid02, user_parts_uuid03, name, user_parts_preset_tag_number, latest_version")
|
}, "user_parts_preset_number, user_parts_uuid01, user_parts_uuid02, user_parts_uuid03, name, user_parts_preset_tag_number, latest_version")
|
||||||
|
|
||||||
|
for k, v := range after.PartsStatusSubs {
|
||||||
|
if old, ok := before.PartsStatusSubs[k]; !ok || old != v {
|
||||||
|
exec(`INSERT OR REPLACE INTO user_parts_status_subs (user_id, user_parts_uuid, status_index, parts_status_sub_lottery_id, level, status_kind_type, status_calculation_type, status_change_value, latest_version) VALUES (?,?,?,?,?,?,?,?,?)`,
|
||||||
|
uid, k.UserPartsUuid, k.StatusIndex, v.PartsStatusSubLotteryId, v.Level, v.StatusKindType, v.StatusCalculationType, v.StatusChangeValue, v.LatestVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range before.PartsStatusSubs {
|
||||||
|
if _, ok := after.PartsStatusSubs[k]; !ok {
|
||||||
|
exec(`DELETE FROM user_parts_status_subs WHERE user_id=? AND user_parts_uuid=? AND status_index=?`, uid, k.UserPartsUuid, k.StatusIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Deck type notes (key is model.DeckType which is int32-based)
|
// Deck type notes (key is model.DeckType which is int32-based)
|
||||||
for k, v := range after.DeckTypeNotes {
|
for k, v := range after.DeckTypeNotes {
|
||||||
if old, ok := before.DeckTypeNotes[k]; !ok || old != v {
|
if old, ok := before.DeckTypeNotes[k]; !ok || old != v {
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ func (s *SQLiteStore) ImportUser(u *store.UserState) error {
|
|||||||
"user_deck_sub_weapons",
|
"user_deck_sub_weapons",
|
||||||
"user_decks",
|
"user_decks",
|
||||||
"user_deck_characters",
|
"user_deck_characters",
|
||||||
|
"user_parts_status_subs",
|
||||||
"user_parts_presets",
|
"user_parts_presets",
|
||||||
"user_parts_group_notes",
|
"user_parts_group_notes",
|
||||||
"user_parts",
|
"user_parts",
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ type UserState struct {
|
|||||||
Parts map[string]PartsState
|
Parts map[string]PartsState
|
||||||
PartsGroupNotes map[int32]PartsGroupNoteState
|
PartsGroupNotes map[int32]PartsGroupNoteState
|
||||||
PartsPresets map[int32]PartsPresetState
|
PartsPresets map[int32]PartsPresetState
|
||||||
|
PartsStatusSubs map[PartsStatusSubKey]PartsStatusSubState
|
||||||
ImportantItems map[int32]int32
|
ImportantItems map[int32]int32
|
||||||
CostumeActiveSkills map[string]CostumeActiveSkillState
|
CostumeActiveSkills map[string]CostumeActiveSkillState
|
||||||
WeaponSkills map[string][]WeaponSkillState // key: userWeaponUuid
|
WeaponSkills map[string][]WeaponSkillState // key: userWeaponUuid
|
||||||
@@ -197,6 +198,9 @@ func (u *UserState) EnsureMaps() {
|
|||||||
if u.PartsPresets == nil {
|
if u.PartsPresets == nil {
|
||||||
u.PartsPresets = make(map[int32]PartsPresetState)
|
u.PartsPresets = make(map[int32]PartsPresetState)
|
||||||
}
|
}
|
||||||
|
if u.PartsStatusSubs == nil {
|
||||||
|
u.PartsStatusSubs = make(map[PartsStatusSubKey]PartsStatusSubState)
|
||||||
|
}
|
||||||
if u.ImportantItems == nil {
|
if u.ImportantItems == nil {
|
||||||
u.ImportantItems = make(map[int32]int32)
|
u.ImportantItems = make(map[int32]int32)
|
||||||
}
|
}
|
||||||
@@ -833,6 +837,22 @@ type PartsPresetState struct {
|
|||||||
LatestVersion int64
|
LatestVersion int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PartsStatusSubKey struct {
|
||||||
|
UserPartsUuid string
|
||||||
|
StatusIndex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartsStatusSubState struct {
|
||||||
|
UserPartsUuid string
|
||||||
|
StatusIndex int32
|
||||||
|
PartsStatusSubLotteryId int32
|
||||||
|
Level int32
|
||||||
|
StatusKindType int32
|
||||||
|
StatusCalculationType int32
|
||||||
|
StatusChangeValue int32
|
||||||
|
LatestVersion int64
|
||||||
|
}
|
||||||
|
|
||||||
type NotificationState struct {
|
type NotificationState struct {
|
||||||
GiftNotReceiveCount int32
|
GiftNotReceiveCount int32
|
||||||
FriendRequestReceiveCount int32
|
FriendRequestReceiveCount int32
|
||||||
|
|||||||
@@ -161,6 +161,9 @@ func ChangedTables(before, after *store.UserState) []string {
|
|||||||
if !mapsEqualStruct(before.PartsPresets, after.PartsPresets) {
|
if !mapsEqualStruct(before.PartsPresets, after.PartsPresets) {
|
||||||
add("IUserPartsPreset")
|
add("IUserPartsPreset")
|
||||||
}
|
}
|
||||||
|
if !mapsEqualStruct(before.PartsStatusSubs, after.PartsStatusSubs) {
|
||||||
|
add("IUserPartsStatusSub")
|
||||||
|
}
|
||||||
if !mapsEqualStruct(before.CostumeActiveSkills, after.CostumeActiveSkills) {
|
if !mapsEqualStruct(before.CostumeActiveSkills, after.CostumeActiveSkills) {
|
||||||
add("IUserCostumeActiveSkill")
|
add("IUserCostumeActiveSkill")
|
||||||
}
|
}
|
||||||
@@ -348,6 +351,8 @@ func keyFieldsForTable(table string) []string {
|
|||||||
return []string{"userId", "userThoughtUuid"}
|
return []string{"userId", "userThoughtUuid"}
|
||||||
case "IUserParts":
|
case "IUserParts":
|
||||||
return []string{"userId", "userPartsUuid"}
|
return []string{"userId", "userPartsUuid"}
|
||||||
|
case "IUserPartsStatusSub":
|
||||||
|
return []string{"userId", "userPartsUuid", "statusIndex"}
|
||||||
case "IUserDeckCharacter":
|
case "IUserDeckCharacter":
|
||||||
return []string{"userId", "userDeckCharacterUuid"}
|
return []string{"userId", "userDeckCharacterUuid"}
|
||||||
case "IUserDeck":
|
case "IUserDeck":
|
||||||
|
|||||||
@@ -114,12 +114,15 @@ func init() {
|
|||||||
s, _ := utils.EncodeJSONMaps(SortedCostumeLotteryEffectPendingRecords(user)...)
|
s, _ := utils.EncodeJSONMaps(SortedCostumeLotteryEffectPendingRecords(user)...)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
register("IUserPartsStatusSub", func(user store.UserState) string {
|
||||||
|
s, _ := utils.EncodeJSONMaps(sortedPartsStatusSubRecords(user)...)
|
||||||
|
return s
|
||||||
|
})
|
||||||
registerStatic(
|
registerStatic(
|
||||||
"IUserCostumeLevelBonusReleaseStatus",
|
"IUserCostumeLevelBonusReleaseStatus",
|
||||||
"IUserCostumeLotteryEffectAbility",
|
"IUserCostumeLotteryEffectAbility",
|
||||||
"IUserCostumeLotteryEffectStatusUp",
|
"IUserCostumeLotteryEffectStatusUp",
|
||||||
"IUserPartsPresetTag",
|
"IUserPartsPresetTag",
|
||||||
"IUserPartsStatusSub",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,6 +496,35 @@ func sortedPartsPresetRecords(user store.UserState) []map[string]any {
|
|||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sortedPartsStatusSubRecords(user store.UserState) []map[string]any {
|
||||||
|
keys := make([]store.PartsStatusSubKey, 0, len(user.PartsStatusSubs))
|
||||||
|
for k := range user.PartsStatusSubs {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
if keys[i].UserPartsUuid != keys[j].UserPartsUuid {
|
||||||
|
return keys[i].UserPartsUuid < keys[j].UserPartsUuid
|
||||||
|
}
|
||||||
|
return keys[i].StatusIndex < keys[j].StatusIndex
|
||||||
|
})
|
||||||
|
records := make([]map[string]any, 0, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
row := user.PartsStatusSubs[k]
|
||||||
|
records = append(records, map[string]any{
|
||||||
|
"userId": user.UserId,
|
||||||
|
"userPartsUuid": row.UserPartsUuid,
|
||||||
|
"statusIndex": row.StatusIndex,
|
||||||
|
"partsStatusSubLotteryId": row.PartsStatusSubLotteryId,
|
||||||
|
"level": row.Level,
|
||||||
|
"statusKindType": row.StatusKindType,
|
||||||
|
"statusCalculationType": row.StatusCalculationType,
|
||||||
|
"statusChangeValue": row.StatusChangeValue,
|
||||||
|
"latestVersion": row.LatestVersion,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
func sortedCostumeActiveSkillRecords(user store.UserState) []map[string]any {
|
func sortedCostumeActiveSkillRecords(user store.UserState) []map[string]any {
|
||||||
keys := sortedStringKeys(user.CostumeActiveSkills)
|
keys := sortedStringKeys(user.CostumeActiveSkills)
|
||||||
records := make([]map[string]any, 0, len(keys))
|
records := make([]map[string]any, 0, len(keys))
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- +goose Up
|
||||||
|
CREATE TABLE user_parts_status_subs (
|
||||||
|
user_id INTEGER NOT NULL REFERENCES users(user_id),
|
||||||
|
user_parts_uuid TEXT NOT NULL,
|
||||||
|
status_index INTEGER NOT NULL,
|
||||||
|
parts_status_sub_lottery_id INTEGER NOT NULL DEFAULT 0,
|
||||||
|
level INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status_kind_type INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status_calculation_type INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status_change_value INTEGER NOT NULL DEFAULT 0,
|
||||||
|
latest_version INTEGER NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (user_id, user_parts_uuid, status_index)
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE user_parts SET level = 1;
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
DROP TABLE IF EXISTS user_parts_status_subs;
|
||||||
Reference in New Issue
Block a user