mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Fix stale story-unlock and post-evolve weapon state
This commit is contained in:
@@ -42,6 +42,8 @@ type WeaponCatalog struct {
|
|||||||
BaseExpByEnhanceId map[int32]int32
|
BaseExpByEnhanceId map[int32]int32
|
||||||
ReleaseConditionsByGroupId map[int32][]EntityMWeaponStoryReleaseConditionGroup
|
ReleaseConditionsByGroupId map[int32][]EntityMWeaponStoryReleaseConditionGroup
|
||||||
|
|
||||||
|
LevelingEnhanceIdByWeaponId map[int32]int32
|
||||||
|
|
||||||
AwakenByWeaponId map[int32]EntityMWeaponAwaken
|
AwakenByWeaponId map[int32]EntityMWeaponAwaken
|
||||||
AwakenMaterialsByGroupId map[int32][]EntityMWeaponAwakenMaterialGroup
|
AwakenMaterialsByGroupId map[int32][]EntityMWeaponAwakenMaterialGroup
|
||||||
}
|
}
|
||||||
@@ -142,6 +144,8 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
|||||||
BaseExpByEnhanceId: make(map[int32]int32, len(enhanceRows)),
|
BaseExpByEnhanceId: make(map[int32]int32, len(enhanceRows)),
|
||||||
ReleaseConditionsByGroupId: make(map[int32][]EntityMWeaponStoryReleaseConditionGroup),
|
ReleaseConditionsByGroupId: make(map[int32][]EntityMWeaponStoryReleaseConditionGroup),
|
||||||
|
|
||||||
|
LevelingEnhanceIdByWeaponId: make(map[int32]int32, len(weapons)),
|
||||||
|
|
||||||
AwakenByWeaponId: make(map[int32]EntityMWeaponAwaken, len(awakenRows)),
|
AwakenByWeaponId: make(map[int32]EntityMWeaponAwaken, len(awakenRows)),
|
||||||
AwakenMaterialsByGroupId: make(map[int32][]EntityMWeaponAwakenMaterialGroup),
|
AwakenMaterialsByGroupId: make(map[int32][]EntityMWeaponAwakenMaterialGroup),
|
||||||
}
|
}
|
||||||
@@ -342,5 +346,28 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
|||||||
}
|
}
|
||||||
log.Printf("[WeaponCatalog] rarity fallback: assigned synthetic enhance IDs to %d weapons", fallbackCount)
|
log.Printf("[WeaponCatalog] rarity fallback: assigned synthetic enhance IDs to %d weapons", fallbackCount)
|
||||||
|
|
||||||
|
// Build LevelingEnhanceIdByWeaponId: every member of an evolution chain
|
||||||
|
// inherits the chain root's (post-rarity-fallback) WeaponSpecificEnhanceId
|
||||||
|
// so cumulative exp stays consistent across Evolve. Weapons not in any
|
||||||
|
// chain map to their own enhance id.
|
||||||
|
for _, rows := range grouped {
|
||||||
|
if len(rows) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rootMaster, ok := catalog.Weapons[rows[0].WeaponId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rootEnhanceId := rootMaster.WeaponSpecificEnhanceId
|
||||||
|
for _, row := range rows {
|
||||||
|
catalog.LevelingEnhanceIdByWeaponId[row.WeaponId] = rootEnhanceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for wid, w := range catalog.Weapons {
|
||||||
|
if _, exists := catalog.LevelingEnhanceIdByWeaponId[wid]; !exists {
|
||||||
|
catalog.LevelingEnhanceIdByWeaponId[wid] = w.WeaponSpecificEnhanceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return catalog, nil
|
return catalog, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,8 +122,26 @@ func (s *WeaponServiceServer) EnhanceByMaterial(ctx context.Context, req *pb.Enh
|
|||||||
}
|
}
|
||||||
|
|
||||||
weapon.Exp += totalExp
|
weapon.Exp += totalExp
|
||||||
if thresholds, ok := catalog.ExpByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
levelingEnhanceId := catalog.LevelingEnhanceIdByWeaponId[weapon.WeaponId]
|
||||||
|
if thresholds, ok := catalog.ExpByEnhanceId[levelingEnhanceId]; ok {
|
||||||
weapon.Level, weapon.Exp = gameutil.LevelAndCap(weapon.Exp, thresholds)
|
weapon.Level, weapon.Exp = gameutil.LevelAndCap(weapon.Exp, thresholds)
|
||||||
|
if maxFunc, ok := catalog.MaxLevelByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
||||||
|
cap := maxFunc.Evaluate(weapon.LimitBreakCount)
|
||||||
|
if weapon.Level > cap {
|
||||||
|
weapon.Level = cap
|
||||||
|
if int(cap) >= 0 && int(cap) < len(thresholds) {
|
||||||
|
weapon.Exp = thresholds[cap]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
note := user.WeaponNotes[weapon.WeaponId]
|
||||||
|
if note.MaxLevel < weapon.Level {
|
||||||
|
note.WeaponId = weapon.WeaponId
|
||||||
|
note.MaxLevel = weapon.Level
|
||||||
|
note.LatestVersion = nowMillis
|
||||||
|
user.WeaponNotes[weapon.WeaponId] = note
|
||||||
}
|
}
|
||||||
|
|
||||||
weapon.LatestVersion = nowMillis
|
weapon.LatestVersion = nowMillis
|
||||||
@@ -240,10 +258,52 @@ func (s *WeaponServiceServer) Evolve(ctx context.Context, req *pb.EvolveRequest)
|
|||||||
log.Printf("[WeaponService] Evolve: gold cost=%d", goldCost)
|
log.Printf("[WeaponService] Evolve: gold cost=%d", goldCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldWeaponId := wm.WeaponId
|
||||||
weapon.WeaponId = evolvedId
|
weapon.WeaponId = evolvedId
|
||||||
weapon.LatestVersion = nowMillis
|
weapon.LatestVersion = nowMillis
|
||||||
user.Weapons[req.UserWeaponUuid] = weapon
|
user.Weapons[req.UserWeaponUuid] = weapon
|
||||||
|
|
||||||
|
note, hasNote := user.WeaponNotes[evolvedId]
|
||||||
|
if !hasNote {
|
||||||
|
note = store.WeaponNoteState{
|
||||||
|
WeaponId: evolvedId,
|
||||||
|
MaxLevel: weapon.Level,
|
||||||
|
MaxLimitBreakCount: weapon.LimitBreakCount,
|
||||||
|
FirstAcquisitionDatetime: nowMillis,
|
||||||
|
LatestVersion: nowMillis,
|
||||||
|
}
|
||||||
|
user.WeaponNotes[evolvedId] = note
|
||||||
|
} else {
|
||||||
|
changed := false
|
||||||
|
if note.MaxLevel < weapon.Level {
|
||||||
|
note.MaxLevel = weapon.Level
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if note.MaxLimitBreakCount < weapon.LimitBreakCount {
|
||||||
|
note.MaxLimitBreakCount = weapon.LimitBreakCount
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
note.WeaponId = evolvedId
|
||||||
|
note.LatestVersion = nowMillis
|
||||||
|
user.WeaponNotes[evolvedId] = note
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldStory, hasOldStory := user.WeaponStories[oldWeaponId]; hasOldStory {
|
||||||
|
newStory, hasNewStory := user.WeaponStories[evolvedId]
|
||||||
|
if !hasNewStory || newStory.ReleasedMaxStoryIndex < oldStory.ReleasedMaxStoryIndex {
|
||||||
|
if user.WeaponStories == nil {
|
||||||
|
user.WeaponStories = make(map[int32]store.WeaponStoryState)
|
||||||
|
}
|
||||||
|
user.WeaponStories[evolvedId] = store.WeaponStoryState{
|
||||||
|
WeaponId: evolvedId,
|
||||||
|
ReleasedMaxStoryIndex: oldStory.ReleasedMaxStoryIndex,
|
||||||
|
LatestVersion: nowMillis,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
evolvedMaster, ok := catalog.Weapons[evolvedId]
|
evolvedMaster, ok := catalog.Weapons[evolvedId]
|
||||||
if ok {
|
if ok {
|
||||||
if slots, ok := catalog.AbilitySlots[evolvedMaster.WeaponAbilityGroupId]; ok {
|
if slots, ok := catalog.AbilitySlots[evolvedMaster.WeaponAbilityGroupId]; ok {
|
||||||
@@ -259,7 +319,7 @@ func (s *WeaponServiceServer) Evolve(ctx context.Context, req *pb.EvolveRequest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[WeaponService] Evolve: weaponId %d -> %d", wm.WeaponId, evolvedId)
|
log.Printf("[WeaponService] Evolve: weaponId %d -> %d", oldWeaponId, evolvedId)
|
||||||
|
|
||||||
checkWeaponStoryUnlocks(catalog, user, evolvedId, weapon.Level, nowMillis)
|
checkWeaponStoryUnlocks(catalog, user, evolvedId, weapon.Level, nowMillis)
|
||||||
})
|
})
|
||||||
@@ -657,7 +717,8 @@ func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.Enhan
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
baseExp := catalog.BaseExpByEnhanceId[matMaster.WeaponSpecificEnhanceId]
|
matLevelingEnhanceId := catalog.LevelingEnhanceIdByWeaponId[matWeapon.WeaponId]
|
||||||
|
baseExp := catalog.BaseExpByEnhanceId[matLevelingEnhanceId]
|
||||||
if matMaster.WeaponType != 0 && matMaster.WeaponType == wm.WeaponType {
|
if matMaster.WeaponType != 0 && matMaster.WeaponType == wm.WeaponType {
|
||||||
baseExp = baseExp * config.MaterialSameWeaponExpCoefficientPermil / 1000
|
baseExp = baseExp * config.MaterialSameWeaponExpCoefficientPermil / 1000
|
||||||
}
|
}
|
||||||
@@ -683,8 +744,26 @@ func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.Enhan
|
|||||||
}
|
}
|
||||||
|
|
||||||
weapon.Exp += totalExp
|
weapon.Exp += totalExp
|
||||||
if thresholds, ok := catalog.ExpByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
levelingEnhanceId := catalog.LevelingEnhanceIdByWeaponId[weapon.WeaponId]
|
||||||
|
if thresholds, ok := catalog.ExpByEnhanceId[levelingEnhanceId]; ok {
|
||||||
weapon.Level, weapon.Exp = gameutil.LevelAndCap(weapon.Exp, thresholds)
|
weapon.Level, weapon.Exp = gameutil.LevelAndCap(weapon.Exp, thresholds)
|
||||||
|
if maxFunc, ok := catalog.MaxLevelByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
||||||
|
cap := maxFunc.Evaluate(weapon.LimitBreakCount)
|
||||||
|
if weapon.Level > cap {
|
||||||
|
weapon.Level = cap
|
||||||
|
if int(cap) >= 0 && int(cap) < len(thresholds) {
|
||||||
|
weapon.Exp = thresholds[cap]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
note := user.WeaponNotes[weapon.WeaponId]
|
||||||
|
if note.MaxLevel < weapon.Level {
|
||||||
|
note.WeaponId = weapon.WeaponId
|
||||||
|
note.MaxLevel = weapon.Level
|
||||||
|
note.LatestVersion = nowMillis
|
||||||
|
user.WeaponNotes[weapon.WeaponId] = note
|
||||||
}
|
}
|
||||||
|
|
||||||
weapon.LatestVersion = nowMillis
|
weapon.LatestVersion = nowMillis
|
||||||
|
|||||||
@@ -294,17 +294,16 @@ func ComputeDelta(before, after *store.UserState, changedTables []string) map[st
|
|||||||
diff := make(map[string]*pb.DiffData, len(changedTables))
|
diff := make(map[string]*pb.DiffData, len(changedTables))
|
||||||
for _, table := range changedTables {
|
for _, table := range changedTables {
|
||||||
afterJSON := projectTable(table, *after)
|
afterJSON := projectTable(table, *after)
|
||||||
|
updates := afterJSON
|
||||||
deleteKeys := "[]"
|
deleteKeys := "[]"
|
||||||
if kf := keyFieldsForTable(table); len(kf) > 0 {
|
if kf := keyFieldsForTable(table); len(kf) > 0 {
|
||||||
beforeJSON := projectTable(table, *before)
|
beforeRecs := parseJSONRecords(projectTable(table, *before))
|
||||||
deleteKeys = ComputeDeleteKeys(
|
afterRecs := parseJSONRecords(afterJSON)
|
||||||
parseJSONRecords(beforeJSON),
|
updates = ComputeUpdateRecords(beforeRecs, afterRecs, kf)
|
||||||
parseJSONRecords(afterJSON),
|
deleteKeys = ComputeDeleteKeys(beforeRecs, afterRecs, kf)
|
||||||
kf,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
diff[table] = &pb.DiffData{
|
diff[table] = &pb.DiffData{
|
||||||
UpdateRecordsJson: afterJSON,
|
UpdateRecordsJson: updates,
|
||||||
DeleteKeysJson: deleteKeys,
|
DeleteKeysJson: deleteKeys,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package userdata
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
pb "lunar-tear/server/gen/proto"
|
pb "lunar-tear/server/gen/proto"
|
||||||
@@ -120,6 +121,34 @@ func ComputeDeleteKeys(oldRecords, newRecords []map[string]any, keyFields []stri
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ComputeUpdateRecords(oldRecords, newRecords []map[string]any, keyFields []string) string {
|
||||||
|
if len(newRecords) == 0 {
|
||||||
|
return "[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
oldByKey := make(map[string]map[string]any, len(oldRecords))
|
||||||
|
for _, r := range oldRecords {
|
||||||
|
oldByKey[compositeKey(r, keyFields)] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed []map[string]any
|
||||||
|
for _, r := range newRecords {
|
||||||
|
prev, exists := oldByKey[compositeKey(r, keyFields)]
|
||||||
|
if !exists || !reflect.DeepEqual(prev, r) {
|
||||||
|
changed = append(changed, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changed) == 0 {
|
||||||
|
return "[]"
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(changed)
|
||||||
|
if err != nil {
|
||||||
|
return "[]"
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
func compositeKey(record map[string]any, fields []string) string {
|
func compositeKey(record map[string]any, fields []string) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for i, f := range fields {
|
for i, f := range fields {
|
||||||
|
|||||||
Reference in New Issue
Block a user