Files
lunar-tear/server/internal/masterdata/weapon.go
T

347 lines
16 KiB
Go

package masterdata
import (
"fmt"
"log"
"sort"
"lunar-tear/server/internal/model"
"lunar-tear/server/internal/utils"
)
type WeaponAwakenEffectGroupRow struct {
WeaponAwakenEffectGroupId int32 `json:"WeaponAwakenEffectGroupId"`
WeaponAwakenEffectType int32 `json:"WeaponAwakenEffectType"`
WeaponAwakenEffectId int32 `json:"WeaponAwakenEffectId"`
}
type WeaponCatalog struct {
Weapons map[int32]EntityMWeapon
Materials map[int32]EntityMMaterial
ExpByEnhanceId map[int32][]int32
GoldCostByEnhanceId map[int32]NumericalFunc
MaxLevelByEnhanceId map[int32]NumericalFunc
SellPriceByEnhanceId map[int32]NumericalFunc
MedalsByWeaponId map[int32]map[int32]int32 // WeaponId -> ConsumableItemId -> Count
EvolutionNextWeaponId map[int32]int32
EvolutionOrder map[int32]int32 // WeaponId -> 0-based position in evolution chain
EvolutionMaterials map[int32][]EntityMWeaponEvolutionMaterialGroup // WeaponEvolutionMaterialGroupId -> materials
EvolutionCostByEnhanceId map[int32]NumericalFunc
AbilitySlots map[int32][]int32 // WeaponAbilityGroupId -> slot numbers
SkillGroupsByGroupId map[int32][]EntityMWeaponSkillGroup
SkillEnhanceMats map[[2]int32][]EntityMWeaponSkillEnhancementMaterial // key: [enhancementMaterialId, skillLevel]
SkillMaxLevelByEnhanceId map[int32]NumericalFunc
SkillCostByEnhanceId map[int32]NumericalFunc
AbilityGroupsByGroupId map[int32][]EntityMWeaponAbilityGroup
AbilityEnhanceMats map[[2]int32][]EntityMWeaponAbilityEnhancementMaterial // key: [enhancementMaterialId, abilityLevel]
AbilityMaxLevelByEnhanceId map[int32]NumericalFunc
AbilityCostByEnhanceId map[int32]NumericalFunc
EnhanceCostByWeaponByEnhanceId map[int32]NumericalFunc
LimitBreakCostByWeaponByEnhanceId map[int32]NumericalFunc
LimitBreakCostByMaterialByEnhanceId map[int32]NumericalFunc
BaseExpByEnhanceId map[int32]int32
ReleaseConditionsByGroupId map[int32][]EntityMWeaponStoryReleaseConditionGroup
AwakenByWeaponId map[int32]EntityMWeaponAwaken
AwakenMaterialsByGroupId map[int32][]EntityMWeaponAwakenMaterialGroup
}
func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
weapons, err := utils.ReadTable[EntityMWeapon]("m_weapon")
if err != nil {
return nil, fmt.Errorf("load weapon table: %w", err)
}
enhanceRows, err := utils.ReadTable[EntityMWeaponSpecificEnhance]("m_weapon_specific_enhance")
if err != nil {
return nil, fmt.Errorf("load weapon specific enhance table: %w", err)
}
rarityEnhanceRows, err := utils.ReadTable[EntityMWeaponRarity]("m_weapon_rarity")
if err != nil {
return nil, fmt.Errorf("load weapon rarity table: %w", err)
}
paramMapRows, err := LoadParameterMap()
if err != nil {
return nil, err
}
funcResolver, err := LoadFunctionResolver()
if err != nil {
return nil, fmt.Errorf("load function resolver: %w", err)
}
exchangeRows, err := utils.ReadTable[EntityMWeaponConsumeExchangeConsumableItemGroup]("m_weapon_consume_exchange_consumable_item_group")
if err != nil {
return nil, fmt.Errorf("load weapon consume exchange table: %w", err)
}
evoGroupRows, err := utils.ReadTable[EntityMWeaponEvolutionGroup]("m_weapon_evolution_group")
if err != nil {
return nil, fmt.Errorf("load weapon evolution group table: %w", err)
}
evoMatRows, err := utils.ReadTable[EntityMWeaponEvolutionMaterialGroup]("m_weapon_evolution_material_group")
if err != nil {
return nil, fmt.Errorf("load weapon evolution material group table: %w", err)
}
abilityGroupRows, err := utils.ReadTable[EntityMWeaponAbilityGroup]("m_weapon_ability_group")
if err != nil {
return nil, fmt.Errorf("load weapon ability group table: %w", err)
}
skillGroupRows, err := utils.ReadTable[EntityMWeaponSkillGroup]("m_weapon_skill_group")
if err != nil {
return nil, fmt.Errorf("load weapon skill group table: %w", err)
}
skillMatRows, err := utils.ReadTable[EntityMWeaponSkillEnhancementMaterial]("m_weapon_skill_enhancement_material")
if err != nil {
return nil, fmt.Errorf("load weapon skill enhancement material table: %w", err)
}
abilityMatRows, err := utils.ReadTable[EntityMWeaponAbilityEnhancementMaterial]("m_weapon_ability_enhancement_material")
if err != nil {
return nil, fmt.Errorf("load weapon ability enhancement material table: %w", err)
}
releaseConditions, err := utils.ReadTable[EntityMWeaponStoryReleaseConditionGroup]("m_weapon_story_release_condition_group")
if err != nil {
return nil, fmt.Errorf("load weapon story release condition table: %w", err)
}
awakenRows, err := utils.ReadTable[EntityMWeaponAwaken]("m_weapon_awaken")
if err != nil {
return nil, fmt.Errorf("load weapon awaken table: %w", err)
}
awakenMatRows, err := utils.ReadTable[EntityMWeaponAwakenMaterialGroup]("m_weapon_awaken_material_group")
if err != nil {
return nil, fmt.Errorf("load weapon awaken material group table: %w", err)
}
catalog := &WeaponCatalog{
Weapons: make(map[int32]EntityMWeapon, len(weapons)),
Materials: matCatalog.ByType[model.MaterialTypeWeaponEnhancement],
ExpByEnhanceId: make(map[int32][]int32, len(enhanceRows)),
GoldCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
MaxLevelByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
SellPriceByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
MedalsByWeaponId: make(map[int32]map[int32]int32),
EvolutionNextWeaponId: make(map[int32]int32),
EvolutionOrder: make(map[int32]int32),
EvolutionMaterials: make(map[int32][]EntityMWeaponEvolutionMaterialGroup),
EvolutionCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
AbilitySlots: make(map[int32][]int32),
SkillGroupsByGroupId: make(map[int32][]EntityMWeaponSkillGroup),
SkillEnhanceMats: make(map[[2]int32][]EntityMWeaponSkillEnhancementMaterial),
SkillMaxLevelByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
SkillCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
AbilityGroupsByGroupId: make(map[int32][]EntityMWeaponAbilityGroup),
AbilityEnhanceMats: make(map[[2]int32][]EntityMWeaponAbilityEnhancementMaterial),
AbilityMaxLevelByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
AbilityCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
EnhanceCostByWeaponByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
LimitBreakCostByWeaponByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
LimitBreakCostByMaterialByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
BaseExpByEnhanceId: make(map[int32]int32, len(enhanceRows)),
ReleaseConditionsByGroupId: make(map[int32][]EntityMWeaponStoryReleaseConditionGroup),
AwakenByWeaponId: make(map[int32]EntityMWeaponAwaken, len(awakenRows)),
AwakenMaterialsByGroupId: make(map[int32][]EntityMWeaponAwakenMaterialGroup),
}
for _, w := range weapons {
catalog.Weapons[w.WeaponId] = w
}
for _, r := range enhanceRows {
if _, ok := catalog.ExpByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
catalog.ExpByEnhanceId[r.WeaponSpecificEnhanceId] = BuildExpThresholds(paramMapRows, r.RequiredExpForLevelUpNumericalParameterMapId)
}
if _, ok := catalog.GoldCostByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.EnhancementCostByMaterialNumericalFunctionId); found {
catalog.GoldCostByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.MaxLevelByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.MaxLevelNumericalFunctionId); found {
catalog.MaxLevelByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.SellPriceByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.SellPriceNumericalFunctionId); found {
catalog.SellPriceByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.EvolutionCostByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.EvolutionCostNumericalFunctionId); found {
catalog.EvolutionCostByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.SkillMaxLevelByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.MaxSkillLevelNumericalFunctionId); found {
catalog.SkillMaxLevelByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.SkillCostByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.SkillEnhancementCostNumericalFunctionId); found {
catalog.SkillCostByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.AbilityMaxLevelByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.MaxAbilityLevelNumericalFunctionId); found {
catalog.AbilityMaxLevelByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.AbilityCostByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.AbilityEnhancementCostNumericalFunctionId); found {
catalog.AbilityCostByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.EnhanceCostByWeaponByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.EnhancementCostByWeaponNumericalFunctionId); found {
catalog.EnhanceCostByWeaponByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.LimitBreakCostByWeaponByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.LimitBreakCostByWeaponNumericalFunctionId); found {
catalog.LimitBreakCostByWeaponByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.LimitBreakCostByMaterialByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
if f, found := funcResolver.Resolve(r.LimitBreakCostByMaterialNumericalFunctionId); found {
catalog.LimitBreakCostByMaterialByEnhanceId[r.WeaponSpecificEnhanceId] = f
}
}
if _, ok := catalog.BaseExpByEnhanceId[r.WeaponSpecificEnhanceId]; !ok {
catalog.BaseExpByEnhanceId[r.WeaponSpecificEnhanceId] = r.BaseEnhancementObtainedExp
}
}
for _, ex := range exchangeRows {
if catalog.MedalsByWeaponId[ex.WeaponId] == nil {
catalog.MedalsByWeaponId[ex.WeaponId] = make(map[int32]int32)
}
catalog.MedalsByWeaponId[ex.WeaponId][ex.ConsumableItemId] = ex.Count
}
grouped := make(map[int32][]EntityMWeaponEvolutionGroup)
for _, row := range evoGroupRows {
grouped[row.WeaponEvolutionGroupId] = append(grouped[row.WeaponEvolutionGroupId], row)
}
for _, rows := range grouped {
sort.Slice(rows, func(i, j int) bool {
return rows[i].EvolutionOrder < rows[j].EvolutionOrder
})
for i, row := range rows {
catalog.EvolutionOrder[row.WeaponId] = int32(i)
if i < len(rows)-1 {
catalog.EvolutionNextWeaponId[row.WeaponId] = rows[i+1].WeaponId
}
}
}
for _, row := range evoMatRows {
catalog.EvolutionMaterials[row.WeaponEvolutionMaterialGroupId] = append(
catalog.EvolutionMaterials[row.WeaponEvolutionMaterialGroupId], row)
}
for _, row := range abilityGroupRows {
catalog.AbilitySlots[row.WeaponAbilityGroupId] = append(
catalog.AbilitySlots[row.WeaponAbilityGroupId], row.SlotNumber)
}
for _, row := range skillGroupRows {
catalog.SkillGroupsByGroupId[row.WeaponSkillGroupId] = append(
catalog.SkillGroupsByGroupId[row.WeaponSkillGroupId], row)
}
for _, row := range skillMatRows {
key := [2]int32{row.WeaponSkillEnhancementMaterialId, row.SkillLevel}
catalog.SkillEnhanceMats[key] = append(catalog.SkillEnhanceMats[key], row)
}
for _, row := range abilityGroupRows {
catalog.AbilityGroupsByGroupId[row.WeaponAbilityGroupId] = append(
catalog.AbilityGroupsByGroupId[row.WeaponAbilityGroupId], row)
}
for _, row := range abilityMatRows {
key := [2]int32{row.WeaponAbilityEnhancementMaterialId, row.AbilityLevel}
catalog.AbilityEnhanceMats[key] = append(catalog.AbilityEnhanceMats[key], row)
}
for _, c := range releaseConditions {
catalog.ReleaseConditionsByGroupId[c.WeaponStoryReleaseConditionGroupId] = append(
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]EntityMWeaponRarity, len(rarityEnhanceRows))
for _, r := range rarityEnhanceRows {
rarityByType[r.RarityType] = r
}
registeredRarity := make(map[int32]bool, len(rarityEnhanceRows))
fallbackCount := 0
for wid, w := range catalog.Weapons {
if w.WeaponSpecificEnhanceId != 0 {
continue
}
syntheticId := -w.RarityType
if !registeredRarity[w.RarityType] {
r, ok := rarityByType[w.RarityType]
if !ok {
continue
}
catalog.ExpByEnhanceId[syntheticId] = BuildExpThresholds(paramMapRows, r.RequiredExpForLevelUpNumericalParameterMapId)
if f, found := funcResolver.Resolve(r.EnhancementCostByMaterialNumericalFunctionId); found {
catalog.GoldCostByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.MaxLevelNumericalFunctionId); found {
catalog.MaxLevelByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.SellPriceNumericalFunctionId); found {
catalog.SellPriceByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.EvolutionCostNumericalFunctionId); found {
catalog.EvolutionCostByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.MaxSkillLevelNumericalFunctionId); found {
catalog.SkillMaxLevelByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.SkillEnhancementCostNumericalFunctionId); found {
catalog.SkillCostByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.MaxAbilityLevelNumericalFunctionId); found {
catalog.AbilityMaxLevelByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.AbilityEnhancementCostNumericalFunctionId); found {
catalog.AbilityCostByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.EnhancementCostByWeaponNumericalFunctionId); found {
catalog.EnhanceCostByWeaponByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.LimitBreakCostByWeaponNumericalFunctionId); found {
catalog.LimitBreakCostByWeaponByEnhanceId[syntheticId] = f
}
if f, found := funcResolver.Resolve(r.LimitBreakCostByMaterialNumericalFunctionId); found {
catalog.LimitBreakCostByMaterialByEnhanceId[syntheticId] = f
}
catalog.BaseExpByEnhanceId[syntheticId] = r.BaseEnhancementObtainedExp
registeredRarity[w.RarityType] = true
}
w.WeaponSpecificEnhanceId = syntheticId
catalog.Weapons[wid] = w
fallbackCount++
}
log.Printf("[WeaponCatalog] rarity fallback: assigned synthetic enhance IDs to %d weapons", fallbackCount)
return catalog, nil
}