package element import ( "fmt" "math" ) // 元素类型枚举(覆盖所有单属性,ID与原配置完全对齐) type ElementType int const ( ElementTypeGrass ElementType = 1 // 草 ElementTypeWater ElementType = 2 // 水 ElementTypeFire ElementType = 3 // 火 ElementTypeFlying ElementType = 4 // 飞行 ElementTypeElectric ElementType = 5 // 电 ElementTypeSteel ElementType = 6 // 机械 ElementTypeGround ElementType = 7 // 地面 ElementTypeNormal ElementType = 8 // 普通 ElementTypeIce ElementType = 9 // 冰 ElementTypePsychic ElementType = 10 // 超能 ElementTypeFighting ElementType = 11 // 战斗 ElementTypeLight ElementType = 12 // 光 ElementTypeDark ElementType = 13 // 暗影 ElementTypeMythic ElementType = 14 // 神秘 ElementTypeDragon ElementType = 15 // 龙 ElementTypeSaint ElementType = 16 // 圣灵 ElementTypeDimension ElementType = 17 // 次元 ElementTypeAncient ElementType = 18 // 远古 ElementTypeDemon ElementType = 19 // 邪灵 ElementTypeNature ElementType = 20 // 自然 ElementTypeKing ElementType = 221 // 王 ElementTypeChaos ElementType = 222 // 混沌 ElementTypeDeity ElementType = 223 // 神灵 ElementTypeSamsara ElementType = 224 // 轮回 ElementTypeInsect ElementType = 225 // 虫 ElementTypeVoid ElementType = 226 // 虚空 ) // 全局常量定义(统一管理合法范围) const ( maxMatrixSize = 227 // 矩阵维度(覆盖最大属性ID 226) ) // 属性配置由 data/skillTypes.json 初始化,数据来自 seer-types-relation。 var validSingleElementIDs [maxMatrixSize]bool // 元素名称映射(按ID直接索引,便于日志输出) var elementNameMap [maxMatrixSize]string // 双属性映射(key=双属性ID,value=组成的两个单属性ID) var dualElementMap = map[int][2]int{} // 元素组合结构体 type ElementCombination struct { Primary ElementType // 主属性(按ID升序排序) Secondary *ElementType // 副属性(双属性非空) ID int // 组合唯一ID } // 全局预加载资源(程序启动时初始化,运行时只读) var ( validCombinationIDs [maxMatrixSize]bool elementCombinationPool [maxMatrixSize]ElementCombination dualElementSecondaryPool [maxMatrixSize]ElementType matrix [maxMatrixSize][maxMatrixSize]float64 Calculator *ElementCalculator ) // init 预加载所有资源(程序启动时执行一次,无并发问题) func init() { initFullTableMatrix() initElementCombinationPool() Calculator = NewElementCalculator() } func initElementCombinationPool() { for id, valid := range validSingleElementIDs { if !valid { continue } validCombinationIDs[id] = true elementCombinationPool[id] = ElementCombination{ Primary: ElementType(id), ID: id, } } for dualID, atts := range dualElementMap { primaryID, secondaryID := atts[0], atts[1] primary, secondary := ElementType(primaryID), ElementType(secondaryID) if primary > secondary { primary, secondary = secondary, primary } dualElementSecondaryPool[dualID] = secondary validCombinationIDs[dualID] = true elementCombinationPool[dualID] = ElementCombination{ Primary: primary, Secondary: &dualElementSecondaryPool[dualID], ID: dualID, } } } func isValidCombinationID(id int) bool { return id > 0 && id < maxMatrixSize && validCombinationIDs[id] } // IsDual 判断是否为双属性 func (ec *ElementCombination) IsDual() bool { return ec.Secondary != nil } // Elements 获取所有属性列表 func (ec *ElementCombination) Elements() []ElementType { if secondary := ec.Secondary; secondary != nil { return []ElementType{ec.Primary, *secondary} } return []ElementType{ec.Primary} } // String 友好格式化输出 func (ec *ElementCombination) String() string { if secondary := ec.Secondary; secondary != nil { return fmt.Sprintf("(%s, %s)", elementNameMap[ec.Primary], elementNameMap[*secondary]) } return fmt.Sprintf("(%s)", elementNameMap[ec.Primary]) } // ElementCalculator 无锁元素克制计算器(所有倍数在初始化阶段预计算) type ElementCalculator struct { offensiveTable [maxMatrixSize][maxMatrixSize]float64 } // NewElementCalculator 创建计算器实例(构建只读查表缓存) func NewElementCalculator() *ElementCalculator { c := &ElementCalculator{} c.initOffensiveTable() return c } func (c *ElementCalculator) initOffensiveTable() { for attackerID, valid := range validCombinationIDs { if !valid { continue } attacker := &elementCombinationPool[attackerID] for defenderID, valid := range validCombinationIDs { if !valid { continue } defender := &elementCombinationPool[defenderID] c.offensiveTable[attackerID][defenderID] = c.calculateMultiplier(attacker, defender) } } } // getMatrixValue 直接返回矩阵值(修复核心问题:不再将0转换为1) func (c *ElementCalculator) getMatrixValue(attacker, defender ElementType) float64 { return matrix[attacker][defender] } // GetCombination 获取元素组合(直接按ID索引) func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) { if !isValidCombinationID(id) { return nil, fmt.Errorf("invalid element combination ID: %d", id) } return &elementCombinationPool[id], nil } // GetOffensiveMultiplier 计算攻击方→防御方的克制倍数(只读查表) func (c *ElementCalculator) GetOffensiveMultiplier(attackerID, defenderID int) (float64, error) { if !isValidCombinationID(attackerID) { return 0, fmt.Errorf("attacker invalid: invalid element combination ID: %d", attackerID) } if !isValidCombinationID(defenderID) { return 0, fmt.Errorf("defender invalid: invalid element combination ID: %d", defenderID) } return c.offensiveTable[attackerID][defenderID], nil } // calculateMultiplier 核心克制计算逻辑 func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombination) float64 { if !attacker.IsDual() && !defender.IsDual() { return c.getMatrixValue(attacker.Primary, defender.Primary) } if !attacker.IsDual() { y1, y2 := defender.Primary, *defender.Secondary m1 := c.getMatrixValue(attacker.Primary, y1) m2 := c.getMatrixValue(attacker.Primary, y2) switch { case m1 == 2 && m2 == 2: return 4.0 case m1 == 0 || m2 == 0: return (m1 + m2) / 4.0 default: return (m1 + m2) / 2.0 } } if !defender.IsDual() { return c.calculateDualToSingle(attacker.Primary, *attacker.Secondary, defender.Primary) } x1, x2 := attacker.Primary, *attacker.Secondary y1, y2 := defender.Primary, *defender.Secondary coeffY1 := c.calculateDualToSingle(x1, x2, y1) coeffY2 := c.calculateDualToSingle(x1, x2, y2) return (coeffY1 + coeffY2) / 2.0 } // calculateDualToSingle 辅助函数:双→单计算 func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender ElementType) float64 { k1 := c.getMatrixValue(attacker1, defender) k2 := c.getMatrixValue(attacker2, defender) switch { case k1 == 2 && k2 == 2: return 4.0 case k1 == 0 || k2 == 0: return (k1 + k2) / 4.0 default: return (k1 + k2) / 2.0 } } // TestAllScenarios 全场景测试(验证预加载和计算逻辑) func TestAllScenarios() { m1, _ := Calculator.GetOffensiveMultiplier(1, 2) fmt.Println("草→水: %.2f(预期2.0)", m1) if math.Abs(m1-2.0) > 0.001 { fmt.Println("测试1失败:实际%.2f", m1) } m2, _ := Calculator.GetOffensiveMultiplier(222, 226) fmt.Println("混沌→虚空: %.2f(预期0.0)", m2) if math.Abs(m2-0.0) > 0.001 { fmt.Println("测试2失败:实际%.2f", m2) } m3, _ := Calculator.GetOffensiveMultiplier(3, 43) fmt.Println("火→冰龙: %.2f(预期1.5)", m3) if math.Abs(m3-1.5) > 0.001 { fmt.Println("测试3失败:实际%.2f", m3) } m4, _ := Calculator.GetOffensiveMultiplier(92, 223) fmt.Println("混沌暗影→神灵: %.2f(预期1.25)", m4) if math.Abs(m4-1.25) > 0.001 { fmt.Println("测试4失败:实际%.2f", m4) } m5, _ := Calculator.GetOffensiveMultiplier(113, 98) fmt.Println("虚空邪灵→混沌远古: %.2f(预期0.875", m5) if math.Abs(m5-0.875) > 0.001 { fmt.Println("测试5失败:实际%.2f", m5) } m6, _ := Calculator.GetOffensiveMultiplier(113, 98) if math.Abs(m6-m5) > 0.001 { fmt.Println("测试6失败:缓存未命中") } m7, _ := Calculator.GetOffensiveMultiplier(5, 7) fmt.Println("电→地面: %.2f(预期0.0)", m7) if math.Abs(m7-0.0) > 0.001 { fmt.Println("测试7失败:实际%.2f", m7) } m8, _ := Calculator.GetOffensiveMultiplier(35, 7) fmt.Println("电战斗→地面: %.2f(预期0.25)", m8) if math.Abs(m8-0.25) > 0.001 { fmt.Println("测试8失败:实际%.2f", m8) } }