mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
565 lines
18 KiB
Go
565 lines
18 KiB
Go
package store
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"sort"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"lunar-tear/server/internal/gametime"
|
|
"lunar-tear/server/internal/model"
|
|
)
|
|
|
|
func DeductPrice(user *UserState, priceType, priceId, amount int32) error {
|
|
switch priceType {
|
|
case model.PriceTypeConsumableItem:
|
|
cur := user.ConsumableItems[priceId]
|
|
if cur < amount {
|
|
return fmt.Errorf("insufficient consumable %d: have %d, need %d", priceId, cur, amount)
|
|
}
|
|
user.ConsumableItems[priceId] = cur - amount
|
|
case model.PriceTypeGem:
|
|
total := user.Gem.FreeGem + user.Gem.PaidGem
|
|
if total < amount {
|
|
return fmt.Errorf("insufficient gems: have %d, need %d", total, amount)
|
|
}
|
|
if user.Gem.FreeGem >= amount {
|
|
user.Gem.FreeGem -= amount
|
|
} else {
|
|
amount -= user.Gem.FreeGem
|
|
user.Gem.FreeGem = 0
|
|
user.Gem.PaidGem -= amount
|
|
}
|
|
case model.PriceTypePaidGem:
|
|
if user.Gem.PaidGem < amount {
|
|
return fmt.Errorf("insufficient paid gems: have %d, need %d", user.Gem.PaidGem, amount)
|
|
}
|
|
user.Gem.PaidGem -= amount
|
|
case model.PriceTypePlatformPayment:
|
|
// real-money purchase -- treat as free on private server
|
|
default:
|
|
log.Printf("[DeductPrice] unhandled priceType=%d priceId=%d amount=%d", priceType, priceId, amount)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DeductPossession(user *UserState, possessionType model.PossessionType, possessionId, count int32) {
|
|
switch possessionType {
|
|
case model.PossessionTypeMaterial:
|
|
user.Materials[possessionId] -= count
|
|
if user.Materials[possessionId] <= 0 {
|
|
delete(user.Materials, possessionId)
|
|
}
|
|
case model.PossessionTypeConsumableItem:
|
|
user.ConsumableItems[possessionId] -= count
|
|
if user.ConsumableItems[possessionId] <= 0 {
|
|
delete(user.ConsumableItems, possessionId)
|
|
}
|
|
case model.PossessionTypePaidGem:
|
|
user.Gem.PaidGem -= count
|
|
case model.PossessionTypeFreeGem:
|
|
user.Gem.FreeGem -= count
|
|
default:
|
|
log.Printf("[DeductPossession] unhandled type=%d id=%d count=%d", possessionType, possessionId, count)
|
|
}
|
|
}
|
|
|
|
func GrantPossession(user *UserState, possessionType model.PossessionType, possessionId, count int32) {
|
|
switch possessionType {
|
|
case model.PossessionTypeMaterial:
|
|
user.Materials[possessionId] += count
|
|
case model.PossessionTypeConsumableItem:
|
|
user.ConsumableItems[possessionId] += count
|
|
case model.PossessionTypePaidGem:
|
|
user.Gem.PaidGem += count
|
|
case model.PossessionTypeFreeGem:
|
|
user.Gem.FreeGem += count
|
|
case model.PossessionTypeImportantItem:
|
|
user.ImportantItems[possessionId] += count
|
|
case model.PossessionTypePremiumItem:
|
|
user.PremiumItems[possessionId] = gametime.NowMillis()
|
|
default:
|
|
log.Printf("[GrantPossession] unhandled type=%d id=%d count=%d", possessionType, possessionId, count)
|
|
}
|
|
}
|
|
|
|
type CostumeRef struct {
|
|
CharacterId int32
|
|
}
|
|
|
|
type WeaponRef struct {
|
|
WeaponSkillGroupId int32
|
|
WeaponAbilityGroupId int32
|
|
WeaponStoryReleaseConditionGroupId int32
|
|
}
|
|
|
|
type WeaponStoryReleaseCond struct {
|
|
StoryIndex int32
|
|
WeaponStoryReleaseConditionType model.WeaponStoryReleaseConditionType
|
|
ConditionValue int32
|
|
}
|
|
|
|
type PartsRef struct {
|
|
PartsGroupId int32
|
|
RarityType int32
|
|
PartsInitialLotteryId int32
|
|
PartsStatusMainLotteryGroupId int32
|
|
PartsStatusSubLotteryGroupId int32
|
|
}
|
|
|
|
// PartsStatusSubDef carries the per-lottery-id sub-status shape needed at
|
|
// grant time. Held here so the store package does not import masterdata.
|
|
type PartsStatusSubDef struct {
|
|
StatusKindType int32
|
|
StatusCalculationType int32
|
|
StatusChangeInitialValue int32
|
|
StatusFunc func(level int32) int32
|
|
}
|
|
|
|
type PossessionGranter struct {
|
|
CostumeById map[int32]CostumeRef
|
|
WeaponById map[int32]WeaponRef
|
|
WeaponSkillSlots map[int32][]int32
|
|
WeaponAbilitySlots map[int32][]int32
|
|
ReleaseConditions map[int32][]WeaponStoryReleaseCond
|
|
|
|
PartsById map[int32]PartsRef
|
|
DefaultPartsStatusMainByLotteryGroup map[int32]int32
|
|
PartsVariantsByGroupRarity map[int32]map[int32][]int32
|
|
PartsSubStatusPool map[int32][]int32
|
|
PartsSubStatusDefs map[int32]PartsStatusSubDef
|
|
|
|
PartsSellPriceL1ByRarity map[int32]int32
|
|
GoldConsumableItemId int32
|
|
|
|
LastChangedStoryWeaponIds []int32
|
|
}
|
|
|
|
func (g *PossessionGranter) DrainChangedStoryWeaponIds() []int32 {
|
|
ids := g.LastChangedStoryWeaponIds
|
|
g.LastChangedStoryWeaponIds = nil
|
|
return ids
|
|
}
|
|
|
|
func (g *PossessionGranter) GrantFull(user *UserState, possessionType model.PossessionType, possessionId, count int32, nowMillis int64) {
|
|
switch possessionType {
|
|
case model.PossessionTypeCostume, model.PossessionTypeCostumeEnhanced:
|
|
g.GrantCostume(user, possessionId, nowMillis)
|
|
case model.PossessionTypeWeapon, model.PossessionTypeWeaponEnhanced:
|
|
g.GrantWeapon(user, possessionId, nowMillis)
|
|
case model.PossessionTypeCompanion, model.PossessionTypeCompanionEnhanced:
|
|
g.GrantCompanion(user, possessionId, nowMillis)
|
|
case model.PossessionTypeParts, model.PossessionTypePartsEnhanced:
|
|
g.GrantParts(user, possessionId, nowMillis)
|
|
default:
|
|
GrantPossession(user, possessionType, possessionId, count)
|
|
}
|
|
}
|
|
|
|
func (g *PossessionGranter) GrantCostume(user *UserState, costumeId int32, nowMillis int64) {
|
|
for _, row := range user.Costumes {
|
|
if row.CostumeId == costumeId {
|
|
return
|
|
}
|
|
}
|
|
if cm, ok := g.CostumeById[costumeId]; ok {
|
|
if _, exists := user.Characters[cm.CharacterId]; !exists {
|
|
user.Characters[cm.CharacterId] = CharacterState{
|
|
CharacterId: cm.CharacterId,
|
|
Level: 1,
|
|
}
|
|
}
|
|
}
|
|
key := uuid.New().String()
|
|
user.Costumes[key] = CostumeState{
|
|
UserCostumeUuid: key,
|
|
CostumeId: costumeId,
|
|
Level: 1,
|
|
HeadupDisplayViewId: 1,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
user.CostumeActiveSkills[key] = CostumeActiveSkillState{
|
|
UserCostumeUuid: key,
|
|
Level: 1,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
}
|
|
|
|
func (g *PossessionGranter) GrantCompanion(user *UserState, companionId int32, nowMillis int64) {
|
|
for _, row := range user.Companions {
|
|
if row.CompanionId == companionId {
|
|
return
|
|
}
|
|
}
|
|
key := uuid.New().String()
|
|
user.Companions[key] = CompanionState{
|
|
UserCompanionUuid: key,
|
|
CompanionId: companionId,
|
|
Level: 1,
|
|
HeadupDisplayViewId: 1,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
}
|
|
|
|
func (g *PossessionGranter) GrantParts(user *UserState, requestedPartsId int32, nowMillis int64) {
|
|
chosenPartsId, chosenRef, ok := g.rollPartsVariant(requestedPartsId)
|
|
if !ok {
|
|
g.grantBareParts(user, requestedPartsId, nowMillis)
|
|
return
|
|
}
|
|
g.createParts(user, chosenPartsId, chosenRef, nowMillis)
|
|
}
|
|
|
|
// The rolled variant sets both rarity and rank, so the auto-sale decision can
|
|
// only happen after the roll. Returns the rolled variant id and whether it sold.
|
|
func (g *PossessionGranter) GrantOrSellPartsDrop(user *UserState, requestedPartsId int32, raritySet, rankSet map[int32]bool, nowMillis int64) (int32, bool) {
|
|
chosenPartsId, chosenRef, ok := g.rollPartsVariant(requestedPartsId)
|
|
if !ok {
|
|
g.grantBareParts(user, requestedPartsId, nowMillis)
|
|
return requestedPartsId, false
|
|
}
|
|
rarity := chosenRef.RarityType
|
|
rank := chosenRef.PartsInitialLotteryId
|
|
if price, ok := g.PartsSellPriceL1ByRarity[rarity]; ok && raritySet[rarity] && rankSet[rank] {
|
|
user.ConsumableItems[g.GoldConsumableItemId] += price
|
|
log.Printf("[GrantParts] auto-sold chosen=%d rarity=%d rank=%d -> %d gold", chosenPartsId, rarity, rank, price)
|
|
return chosenPartsId, true
|
|
}
|
|
g.createParts(user, chosenPartsId, chosenRef, nowMillis)
|
|
return chosenPartsId, false
|
|
}
|
|
|
|
func (g *PossessionGranter) grantBareParts(user *UserState, partsId int32, nowMillis int64) {
|
|
key := uuid.New().String()
|
|
user.Parts[key] = PartsState{
|
|
UserPartsUuid: key,
|
|
PartsId: partsId,
|
|
Level: 1,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
log.Printf("[GrantParts] unknown partsId=%d, granted as-is with no variant roll", partsId)
|
|
}
|
|
|
|
// rollPartsVariant picks one of a parts group's 5 variants at random; the five
|
|
// carry distinct PartsInitialLotteryId 1..5, which is the part's rank.
|
|
func (g *PossessionGranter) rollPartsVariant(requestedPartsId int32) (int32, PartsRef, bool) {
|
|
ref, refOk := g.PartsById[requestedPartsId]
|
|
if !refOk {
|
|
return requestedPartsId, PartsRef{}, false
|
|
}
|
|
chosenPartsId := requestedPartsId
|
|
chosenRef := ref
|
|
if variants := g.PartsVariantsByGroupRarity[ref.PartsGroupId][ref.RarityType]; len(variants) == 5 {
|
|
chosenPartsId = variants[rand.Intn(len(variants))]
|
|
chosenRef = g.PartsById[chosenPartsId]
|
|
} else {
|
|
log.Printf("[GrantParts] no 5-variant set for group=%d rarity=%d (have %d), granting requested=%d", ref.PartsGroupId, ref.RarityType, len(variants), requestedPartsId)
|
|
}
|
|
return chosenPartsId, chosenRef, true
|
|
}
|
|
|
|
func (g *PossessionGranter) createParts(user *UserState, chosenPartsId int32, chosenRef PartsRef, nowMillis int64) {
|
|
mainStatId := g.DefaultPartsStatusMainByLotteryGroup[chosenRef.PartsStatusMainLotteryGroupId]
|
|
if _, exists := user.PartsGroupNotes[chosenRef.PartsGroupId]; !exists {
|
|
user.PartsGroupNotes[chosenRef.PartsGroupId] = PartsGroupNoteState{
|
|
PartsGroupId: chosenRef.PartsGroupId,
|
|
FirstAcquisitionDatetime: nowMillis,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
}
|
|
key := uuid.New().String()
|
|
user.Parts[key] = PartsState{
|
|
UserPartsUuid: key,
|
|
PartsId: chosenPartsId,
|
|
Level: 1,
|
|
PartsStatusMainId: mainStatId,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
|
|
initialCount := chosenRef.PartsInitialLotteryId
|
|
pool := g.PartsSubStatusPool[chosenRef.PartsStatusSubLotteryGroupId]
|
|
if initialCount > 1 && len(pool) > 0 {
|
|
for i := int32(0); i < initialCount-1; i++ {
|
|
pickId := pool[rand.Intn(len(pool))]
|
|
def, ok := g.PartsSubStatusDefs[pickId]
|
|
if !ok {
|
|
continue
|
|
}
|
|
val := def.StatusChangeInitialValue
|
|
if def.StatusFunc != nil {
|
|
val = def.StatusFunc(1)
|
|
}
|
|
user.PartsStatusSubs[PartsStatusSubKey{UserPartsUuid: key, StatusIndex: i + 1}] = PartsStatusSubState{
|
|
UserPartsUuid: key,
|
|
StatusIndex: i + 1,
|
|
PartsStatusSubLotteryId: pickId,
|
|
Level: 1,
|
|
StatusKindType: def.StatusKindType,
|
|
StatusCalculationType: def.StatusCalculationType,
|
|
StatusChangeValue: val,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Printf("[GrantParts] chosen=%d group=%d rarity=%d preUnlockedSubs=%d", chosenPartsId, chosenRef.PartsGroupId, chosenRef.RarityType, initialCount-1)
|
|
}
|
|
|
|
func (g *PossessionGranter) GrantWeapon(user *UserState, weaponId int32, nowMillis int64) {
|
|
key := uuid.New().String()
|
|
user.Weapons[key] = WeaponState{
|
|
UserWeaponUuid: key,
|
|
WeaponId: weaponId,
|
|
Level: 1,
|
|
AcquisitionDatetime: nowMillis,
|
|
}
|
|
if _, exists := user.WeaponNotes[weaponId]; !exists {
|
|
user.WeaponNotes[weaponId] = WeaponNoteState{
|
|
WeaponId: weaponId,
|
|
MaxLevel: 1,
|
|
MaxLimitBreakCount: 0,
|
|
FirstAcquisitionDatetime: nowMillis,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
}
|
|
weapon, ok := g.WeaponById[weaponId]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
g.populateWeaponSkillsAbilities(user, key, weapon)
|
|
if weapon.WeaponStoryReleaseConditionGroupId != 0 {
|
|
changed := false
|
|
for _, cond := range g.ReleaseConditions[weapon.WeaponStoryReleaseConditionGroupId] {
|
|
switch cond.WeaponStoryReleaseConditionType {
|
|
case model.WeaponStoryReleaseConditionTypeAcquisition:
|
|
if grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis) {
|
|
changed = true
|
|
}
|
|
case model.WeaponStoryReleaseConditionTypeQuestClear:
|
|
if qs, ok := user.Quests[cond.ConditionValue]; ok && qs.QuestStateType == model.UserQuestStateTypeCleared {
|
|
if grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis) {
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if changed {
|
|
g.LastChangedStoryWeaponIds = append(g.LastChangedStoryWeaponIds, weaponId)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *PossessionGranter) populateWeaponSkillsAbilities(user *UserState, weaponUuid string, weapon WeaponRef) {
|
|
if slots, ok := g.WeaponSkillSlots[weapon.WeaponSkillGroupId]; ok {
|
|
skills := make([]WeaponSkillState, len(slots))
|
|
for i, slot := range slots {
|
|
skills[i] = WeaponSkillState{
|
|
UserWeaponUuid: weaponUuid,
|
|
SlotNumber: slot,
|
|
Level: 1,
|
|
}
|
|
}
|
|
user.WeaponSkills[weaponUuid] = skills
|
|
}
|
|
if slots, ok := g.WeaponAbilitySlots[weapon.WeaponAbilityGroupId]; ok {
|
|
abilities := make([]WeaponAbilityState, len(slots))
|
|
for i, slot := range slots {
|
|
abilities[i] = WeaponAbilityState{
|
|
UserWeaponUuid: weaponUuid,
|
|
SlotNumber: slot,
|
|
Level: 1,
|
|
}
|
|
}
|
|
user.WeaponAbilities[weaponUuid] = abilities
|
|
}
|
|
}
|
|
|
|
func GrantWeaponStoryUnlock(user *UserState, weaponId, storyIndex int32, nowMillis int64) bool {
|
|
return grantWeaponStoryUnlock(user, weaponId, storyIndex, nowMillis)
|
|
}
|
|
|
|
func grantWeaponStoryUnlock(user *UserState, weaponId, storyIndex int32, nowMillis int64) bool {
|
|
hasWeapon := false
|
|
for _, row := range user.Weapons {
|
|
if row.WeaponId == weaponId {
|
|
hasWeapon = true
|
|
break
|
|
}
|
|
}
|
|
if !hasWeapon {
|
|
log.Printf("[grantWeaponStoryUnlock] skipping weaponId=%d (weapon not in user.Weapons)", weaponId)
|
|
return false
|
|
}
|
|
if user.WeaponStories == nil {
|
|
user.WeaponStories = make(map[int32]WeaponStoryState)
|
|
}
|
|
cur := user.WeaponStories[weaponId]
|
|
if storyIndex <= cur.ReleasedMaxStoryIndex {
|
|
return false
|
|
}
|
|
user.WeaponStories[weaponId] = WeaponStoryState{
|
|
WeaponId: weaponId,
|
|
ReleasedMaxStoryIndex: storyIndex,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
return true
|
|
}
|
|
|
|
func EnsureDefaultDeck(user *UserState, nowMillis int64) {
|
|
if len(user.Costumes) == 0 || len(user.Decks) > 0 {
|
|
return
|
|
}
|
|
|
|
const rionCostumeId = int32(10100)
|
|
const rionWeaponId = int32(101001)
|
|
|
|
var costumeUuid, weaponUuid string
|
|
for k, v := range user.Costumes {
|
|
if v.CostumeId == rionCostumeId {
|
|
costumeUuid = k
|
|
break
|
|
}
|
|
}
|
|
for k, v := range user.Weapons {
|
|
if v.WeaponId == rionWeaponId {
|
|
weaponUuid = k
|
|
break
|
|
}
|
|
}
|
|
|
|
dcUuid := uuid.New().String()
|
|
user.DeckCharacters[dcUuid] = DeckCharacterState{
|
|
UserDeckCharacterUuid: dcUuid,
|
|
UserCompanionUuid: "",
|
|
UserCostumeUuid: costumeUuid,
|
|
MainUserWeaponUuid: weaponUuid,
|
|
Power: 100,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
user.Decks[DeckKey{DeckType: model.DeckTypeQuest, UserDeckNumber: 1}] = DeckState{
|
|
DeckType: model.DeckTypeQuest,
|
|
UserDeckNumber: 1,
|
|
UserDeckCharacterUuid01: dcUuid,
|
|
Name: "Deck 1",
|
|
Power: 100,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
|
|
if _, exists := user.DeckTypeNotes[model.DeckTypeQuest]; !exists {
|
|
user.DeckTypeNotes[model.DeckTypeQuest] = DeckTypeNoteState{
|
|
DeckType: model.DeckTypeQuest,
|
|
MaxDeckPower: 100,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
}
|
|
}
|
|
|
|
func FirstSortedKey[V any](m map[string]V) string {
|
|
if len(m) == 0 {
|
|
return ""
|
|
}
|
|
keys := make([]string, 0, len(m))
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return keys[0]
|
|
}
|
|
|
|
func ApplyDeckReplacement(user *UserState, deckType model.DeckType, userDeckNumber int32, slots []DeckCharacterInput, nowMillis int64) {
|
|
deckKey := DeckKey{DeckType: deckType, UserDeckNumber: userDeckNumber}
|
|
deck := user.Decks[deckKey]
|
|
deck.DeckType = deckType
|
|
deck.UserDeckNumber = userDeckNumber
|
|
if deck.Name == "" {
|
|
deck.Name = fmt.Sprintf("Deck %d", userDeckNumber)
|
|
}
|
|
if deck.Power == 0 {
|
|
deck.Power = 100
|
|
}
|
|
|
|
for _, oldUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} {
|
|
if oldUuid == "" {
|
|
continue
|
|
}
|
|
delete(user.DeckCharacters, oldUuid)
|
|
delete(user.DeckSubWeapons, oldUuid)
|
|
delete(user.DeckParts, oldUuid)
|
|
}
|
|
|
|
var newUuids [3]string
|
|
for i := range 3 {
|
|
if i >= len(slots) || slots[i].UserCostumeUuid == "" {
|
|
continue
|
|
}
|
|
slot := slots[i]
|
|
dcUuid := uuid.New().String()
|
|
user.DeckCharacters[dcUuid] = DeckCharacterState{
|
|
UserDeckCharacterUuid: dcUuid,
|
|
UserCostumeUuid: slot.UserCostumeUuid,
|
|
MainUserWeaponUuid: slot.MainUserWeaponUuid,
|
|
UserCompanionUuid: slot.UserCompanionUuid,
|
|
UserThoughtUuid: slot.UserThoughtUuid,
|
|
DressupCostumeId: slot.DressupCostumeId,
|
|
LatestVersion: nowMillis,
|
|
}
|
|
user.DeckSubWeapons[dcUuid] = slot.SubWeaponUuids
|
|
user.DeckParts[dcUuid] = slot.PartsUuids
|
|
newUuids[i] = dcUuid
|
|
}
|
|
|
|
deck.UserDeckCharacterUuid01 = newUuids[0]
|
|
deck.UserDeckCharacterUuid02 = newUuids[1]
|
|
deck.UserDeckCharacterUuid03 = newUuids[2]
|
|
deck.LatestVersion = nowMillis
|
|
user.Decks[deckKey] = deck
|
|
}
|
|
|
|
func RemoveDeckData(user *UserState, deckType model.DeckType, userDeckNumber int32) {
|
|
deckKey := DeckKey{DeckType: deckType, UserDeckNumber: userDeckNumber}
|
|
deck, ok := user.Decks[deckKey]
|
|
if !ok {
|
|
return
|
|
}
|
|
for _, dcUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} {
|
|
if dcUuid == "" {
|
|
continue
|
|
}
|
|
delete(user.DeckCharacters, dcUuid)
|
|
delete(user.DeckSubWeapons, dcUuid)
|
|
delete(user.DeckParts, dcUuid)
|
|
}
|
|
delete(user.Decks, deckKey)
|
|
}
|
|
|
|
func ReadDeckSlots(user *UserState, deckType model.DeckType, userDeckNumber int32) []DeckCharacterInput {
|
|
deckKey := DeckKey{DeckType: deckType, UserDeckNumber: userDeckNumber}
|
|
deck, ok := user.Decks[deckKey]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
slots := make([]DeckCharacterInput, 3)
|
|
for i, dcUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} {
|
|
if dcUuid == "" {
|
|
continue
|
|
}
|
|
dc, ok := user.DeckCharacters[dcUuid]
|
|
if !ok {
|
|
continue
|
|
}
|
|
slots[i] = DeckCharacterInput{
|
|
UserCostumeUuid: dc.UserCostumeUuid,
|
|
MainUserWeaponUuid: dc.MainUserWeaponUuid,
|
|
SubWeaponUuids: user.DeckSubWeapons[dcUuid],
|
|
PartsUuids: user.DeckParts[dcUuid],
|
|
UserCompanionUuid: dc.UserCompanionUuid,
|
|
UserThoughtUuid: dc.UserThoughtUuid,
|
|
DressupCostumeId: dc.DressupCostumeId,
|
|
}
|
|
}
|
|
return slots
|
|
}
|