mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Add weapon awakening functionality
This commit is contained in:
@@ -94,6 +94,27 @@ type WeaponAbilityEnhanceMaterialRow struct {
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type WeaponAwakenRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
WeaponAwakenEffectGroupId int32 `json:"WeaponAwakenEffectGroupId"`
|
||||
WeaponAwakenMaterialGroupId int32 `json:"WeaponAwakenMaterialGroupId"`
|
||||
ConsumeGold int32 `json:"ConsumeGold"`
|
||||
LevelLimitUp int32 `json:"LevelLimitUp"`
|
||||
}
|
||||
|
||||
type WeaponAwakenEffectGroupRow struct {
|
||||
WeaponAwakenEffectGroupId int32 `json:"WeaponAwakenEffectGroupId"`
|
||||
WeaponAwakenEffectType int32 `json:"WeaponAwakenEffectType"`
|
||||
WeaponAwakenEffectId int32 `json:"WeaponAwakenEffectId"`
|
||||
}
|
||||
|
||||
type WeaponAwakenMaterialGroupRow struct {
|
||||
WeaponAwakenMaterialGroupId int32 `json:"WeaponAwakenMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type weaponRarityEnhanceRow struct {
|
||||
RarityType int32 `json:"RarityType"`
|
||||
BaseEnhancementObtainedExp int32 `json:"BaseEnhancementObtainedExp"`
|
||||
@@ -137,6 +158,9 @@ type WeaponCatalog struct {
|
||||
LimitBreakCostByMaterialByEnhanceId map[int32]NumericalFunc
|
||||
BaseExpByEnhanceId map[int32]int32
|
||||
ReleaseConditionsByGroupId map[int32][]WeaponStoryReleaseConditionRow
|
||||
|
||||
AwakenByWeaponId map[int32]WeaponAwakenRow
|
||||
AwakenMaterialsByGroupId map[int32][]WeaponAwakenMaterialGroupRow
|
||||
}
|
||||
|
||||
func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
@@ -199,6 +223,15 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
return nil, fmt.Errorf("load weapon story release condition table: %w", err)
|
||||
}
|
||||
|
||||
awakenRows, err := utils.ReadJSON[WeaponAwakenRow]("EntityMWeaponAwakenTable.json")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon awaken table: %w", err)
|
||||
}
|
||||
awakenMatRows, err := utils.ReadJSON[WeaponAwakenMaterialGroupRow]("EntityMWeaponAwakenMaterialGroupTable.json")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon awaken material group table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &WeaponCatalog{
|
||||
Weapons: make(map[int32]WeaponMasterRow, len(weapons)),
|
||||
Materials: matCatalog.ByType[model.MaterialTypeWeaponEnhancement],
|
||||
@@ -225,6 +258,9 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
LimitBreakCostByMaterialByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
BaseExpByEnhanceId: make(map[int32]int32, len(enhanceRows)),
|
||||
ReleaseConditionsByGroupId: make(map[int32][]WeaponStoryReleaseConditionRow),
|
||||
|
||||
AwakenByWeaponId: make(map[int32]WeaponAwakenRow, len(awakenRows)),
|
||||
AwakenMaterialsByGroupId: make(map[int32][]WeaponAwakenMaterialGroupRow),
|
||||
}
|
||||
|
||||
for _, w := range weapons {
|
||||
@@ -353,6 +389,14 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
catalog.ReleaseConditionsByGroupId[c.WeaponStoryReleaseConditionGroupId], c)
|
||||
}
|
||||
|
||||
for _, row := range awakenRows {
|
||||
catalog.AwakenByWeaponId[row.WeaponId] = row
|
||||
}
|
||||
for _, row := range awakenMatRows {
|
||||
catalog.AwakenMaterialsByGroupId[row.WeaponAwakenMaterialGroupId] = append(
|
||||
catalog.AwakenMaterialsByGroupId[row.WeaponAwakenMaterialGroupId], row)
|
||||
}
|
||||
|
||||
// Rarity-based enhancement fallback: for weapons with WeaponSpecificEnhanceId == 0,
|
||||
// use EntityMWeaponRarityTable curves via synthetic enhance IDs (-RarityType).
|
||||
rarityByType := make(map[int32]weaponRarityEnhanceRow, len(rarityEnhanceRows))
|
||||
|
||||
@@ -21,3 +21,11 @@ const (
|
||||
CostumeAwakenEffectTypeAbility CostumeAwakenEffectType = 2
|
||||
CostumeAwakenEffectTypeItemAcquire CostumeAwakenEffectType = 3
|
||||
)
|
||||
|
||||
type WeaponAwakenEffectType int32
|
||||
|
||||
const (
|
||||
WeaponAwakenEffectTypeUnknown WeaponAwakenEffectType = 0
|
||||
WeaponAwakenEffectTypeStatusUp WeaponAwakenEffectType = 1
|
||||
WeaponAwakenEffectTypeAbility WeaponAwakenEffectType = 2
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ var weaponDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
@@ -26,11 +27,19 @@ var limitBreakDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
"IUserWeaponNote",
|
||||
}
|
||||
|
||||
var weaponAwakenDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type WeaponServiceServer struct {
|
||||
pb.UnimplementedWeaponServiceServer
|
||||
users store.UserRepository
|
||||
@@ -176,7 +185,8 @@ func (s *WeaponServiceServer) Sell(ctx context.Context, req *pb.SellRequest) (*p
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"})
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
@@ -206,6 +216,7 @@ func (s *WeaponServiceServer) Sell(ctx context.Context, req *pb.SellRequest) (*p
|
||||
delete(user.Weapons, uuid)
|
||||
delete(user.WeaponSkills, uuid)
|
||||
delete(user.WeaponAbilities, uuid)
|
||||
delete(user.WeaponAwakens, uuid)
|
||||
}
|
||||
|
||||
if totalGold > 0 {
|
||||
@@ -217,7 +228,7 @@ func (s *WeaponServiceServer) Sell(ctx context.Context, req *pb.SellRequest) (*p
|
||||
return nil, fmt.Errorf("weapon sell: %w", err)
|
||||
}
|
||||
|
||||
sellDiffTables := []string{"IUserWeapon", "IUserWeaponSkill", "IUserWeaponAbility", "IUserConsumableItem"}
|
||||
sellDiffTables := []string{"IUserWeapon", "IUserWeaponSkill", "IUserWeaponAbility", "IUserWeaponAwaken", "IUserConsumableItem"}
|
||||
tables := userdata.SelectTables(userdata.FullClientTableMap(snapshot), sellDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
@@ -583,7 +594,8 @@ func (s *WeaponServiceServer) LimitBreakByWeapon(ctx context.Context, req *pb.Li
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"})
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
@@ -626,6 +638,7 @@ func (s *WeaponServiceServer) LimitBreakByWeapon(ctx context.Context, req *pb.Li
|
||||
delete(user.Weapons, uuid)
|
||||
delete(user.WeaponSkills, uuid)
|
||||
delete(user.WeaponAbilities, uuid)
|
||||
delete(user.WeaponAwakens, uuid)
|
||||
consumedCount++
|
||||
}
|
||||
|
||||
@@ -668,7 +681,8 @@ func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.Enhan
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"})
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
var changedStoryIds []int32
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
@@ -714,6 +728,7 @@ func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.Enhan
|
||||
delete(user.Weapons, uuid)
|
||||
delete(user.WeaponSkills, uuid)
|
||||
delete(user.WeaponAbilities, uuid)
|
||||
delete(user.WeaponAwakens, uuid)
|
||||
consumedCount++
|
||||
}
|
||||
|
||||
@@ -795,3 +810,62 @@ func (s *WeaponServiceServer) checkWeaponStoryUnlocks(user *store.UserState, wea
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) Awaken(ctx context.Context, req *pb.WeaponAwakenRequest) (*pb.WeaponAwakenResponse, error) {
|
||||
log.Printf("[WeaponService] Awaken: uuid=%s", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] Awaken: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
return
|
||||
}
|
||||
|
||||
awakenRow, ok := s.catalog.AwakenByWeaponId[weapon.WeaponId]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] Awaken: no awaken data for weaponId=%d", weapon.WeaponId)
|
||||
return
|
||||
}
|
||||
|
||||
if _, already := user.WeaponAwakens[req.UserWeaponUuid]; already {
|
||||
log.Printf("[WeaponService] Awaken: weapon uuid=%s already awakened", req.UserWeaponUuid)
|
||||
return
|
||||
}
|
||||
|
||||
mats := s.catalog.AwakenMaterialsByGroupId[awakenRow.WeaponAwakenMaterialGroupId]
|
||||
for _, mat := range mats {
|
||||
cur := user.Materials[mat.MaterialId]
|
||||
cost := mat.Count
|
||||
if cur < cost {
|
||||
log.Printf("[WeaponService] Awaken: insufficient material id=%d have=%d need=%d", mat.MaterialId, cur, cost)
|
||||
cost = cur
|
||||
}
|
||||
user.Materials[mat.MaterialId] = cur - cost
|
||||
}
|
||||
|
||||
if awakenRow.ConsumeGold > 0 {
|
||||
user.ConsumableItems[s.config.ConsumableItemIdForGold] -= awakenRow.ConsumeGold
|
||||
log.Printf("[WeaponService] Awaken: gold cost=%d", awakenRow.ConsumeGold)
|
||||
}
|
||||
|
||||
user.WeaponAwakens[req.UserWeaponUuid] = store.WeaponAwakenState{
|
||||
UserWeaponUuid: req.UserWeaponUuid,
|
||||
LatestVersion: nowMillis,
|
||||
}
|
||||
|
||||
weapon.LatestVersion = nowMillis
|
||||
user.Weapons[req.UserWeaponUuid] = weapon
|
||||
log.Printf("[WeaponService] Awaken: weaponId=%d awakened", weapon.WeaponId)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("weapon awaken: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.FullClientTableMap(snapshot)
|
||||
diff := userdata.BuildDiffFromTables(userdata.SelectTables(tables, weaponAwakenDiffTables))
|
||||
|
||||
return &pb.WeaponAwakenResponse{DiffUserData: diff}, nil
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ type UserState struct {
|
||||
CostumeActiveSkills map[string]CostumeActiveSkillState
|
||||
WeaponSkills map[string][]WeaponSkillState // key: userWeaponUuid
|
||||
WeaponAbilities map[string][]WeaponAbilityState // key: userWeaponUuid
|
||||
WeaponAwakens map[string]WeaponAwakenState // key: userWeaponUuid
|
||||
DeckTypeNotes map[model.DeckType]DeckTypeNoteState
|
||||
WeaponNotes map[int32]WeaponNoteState
|
||||
DeckSubWeapons map[string][]string
|
||||
@@ -205,6 +206,9 @@ func (u *UserState) EnsureMaps() {
|
||||
if u.WeaponAbilities == nil {
|
||||
u.WeaponAbilities = make(map[string][]WeaponAbilityState)
|
||||
}
|
||||
if u.WeaponAwakens == nil {
|
||||
u.WeaponAwakens = make(map[string]WeaponAwakenState)
|
||||
}
|
||||
if u.DeckTypeNotes == nil {
|
||||
u.DeckTypeNotes = make(map[model.DeckType]DeckTypeNoteState)
|
||||
}
|
||||
@@ -462,6 +466,11 @@ type WeaponAbilityState struct {
|
||||
Level int32
|
||||
}
|
||||
|
||||
type WeaponAwakenState struct {
|
||||
UserWeaponUuid string
|
||||
LatestVersion int64
|
||||
}
|
||||
|
||||
type DeckTypeNoteState struct {
|
||||
DeckType model.DeckType
|
||||
MaxDeckPower int32
|
||||
|
||||
@@ -101,13 +101,16 @@ func init() {
|
||||
s, _ := encodeJSONMaps(sortedCageOrnamentRewardRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponAwaken", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponAwakenRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
"IUserCostumeLevelBonusReleaseStatus",
|
||||
"IUserCostumeLotteryEffect",
|
||||
"IUserCostumeLotteryEffectAbility",
|
||||
"IUserCostumeLotteryEffectStatusUp",
|
||||
"IUserCostumeLotteryEffectPending",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserPartsPresetTag",
|
||||
"IUserPartsStatusSub",
|
||||
)
|
||||
@@ -532,6 +535,20 @@ func SortedWeaponAbilityRecords(user store.UserState) []map[string]any {
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedWeaponAwakenRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.WeaponAwakens)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.WeaponAwakens[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userWeaponUuid": row.UserWeaponUuid,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func exploreRecord(user store.UserState) map[string]any {
|
||||
return map[string]any{
|
||||
"userId": user.UserId,
|
||||
|
||||
Reference in New Issue
Block a user