feat(fight): 新增战斗伤害计算模块,实现精确的伤害乘区计算

This commit is contained in:
575560454
2025-07-15 14:46:35 +00:00
parent 8f67c7f55b
commit 38ec291275
5 changed files with 1603 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
package damage
import (
"errors"
"math/rand"
"github.com/shopspring/decimal"
)
// Calculate 计算最终伤害使用decimal确保浮点精度
func Calculate(random *rand.Rand, context *DamageContext, isCritical bool) (int64, error) {
// 参数校验
if err := validateParams(random, context); err != nil {
return 0, err
}
// 应用随机浮动值217-255
applyRandomValue(random, context)
logger.Trace("伤害计算上下文: {}", context.Dump())
// 1. 计算基础因子(等级相关)
levelFactor := calculateLevelFactor(context)
// 2. 计算技能威力因子(基础威力+加算+乘算)
powerFactor := calculatePowerFactor(context)
// 3. 计算减伤因子(物理/特殊伤害区分)
damageReduction := calculateDamageReduction(context)
// 4. 计算攻击次数因子
attackCount := decimal.NewFromFloat(context.GetOtherRate(fight.ATTACK_COUNT_ZONE))
// 5. 计算基础伤害部分: (等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2)
baseDamage := calculateBaseDamage(levelFactor, powerFactor, context, attackCount)
// 6. 计算各类加成系数(同系、克制、暴击等)
bonusFactors := calculateBonusFactors(context, isCritical, damageReduction)
// 7. 计算总伤害(基础伤害 * 所有加成系数)
totalDamage := calculateTotalDamage(baseDamage, bonusFactors, context)
// 8. 应用固定伤害减免
finalDamage := applyFixDamageReduction(totalDamage, context)
return finalDamage.Round(0).IntPart(), nil
}
// validateParams 校验输入参数合法性
func validateParams(random *rand.Rand, context *DamageContext) error {
if random == nil {
return errors.New("随机数生成器不可为nil")
}
if context == nil {
return errors.New("伤害计算上下文不可为nil")
}
if context.AttackerPet() == nil || context.DefenderPet() == nil {
return errors.New("攻击方或防御方精灵不可为nil")
}
if context.Defense <= 0 {
return errors.New("防御值不可为负数或零")
}
return nil
}
// applyRandomValue 生成并设置217-255之间的随机浮动值若未设置
func applyRandomValue(random *rand.Rand, context *DamageContext) {
if context.Random != -1 {
return
}
// 生成[217, 255]范围随机数0-38 + 217
context.Random = random.Intn(39) + 217
}
// calculateLevelFactor 计算等级相关因子level * 0.4 + 2
func calculateLevelFactor(context *DamageContext) decimal.Decimal {
level := decimal.NewFromInt(int64(context.AttackerPet().Level()))
return level.Mul(decimal.NewFromFloat(0.4)).Add(decimal.NewFromInt(2))
}
// calculatePowerFactor 计算技能威力因子:(基础威力 + 威力加算区) * 威力乘算区
func calculatePowerFactor(context *DamageContext) decimal.Decimal {
basePower := decimal.NewFromInt64(context.BasePower)
powerAdd := decimal.NewFromFloat(context.GetOtherRate(fight.POWER_ADDITION_ZONE))
powerMul := decimal.NewFromFloat(context.GetOtherRate(fight.POWER_MULTIPLIER_ZONE))
return basePower.Add(powerAdd).Mul(powerMul)
}
// calculateDamageReduction 计算减伤因子(区分物理/特殊伤害)
func calculateDamageReduction(context *DamageContext) decimal.Decimal {
switch context.Skill.SkillType() {
case structs.PHYSICAL:
// 物理伤害:使用物理抗性区
resist := decimal.NewFromFloat(context.GetOtherRate(fight.ATK_RESISTANCE_ZONE))
logger.Debug("{}物理减伤系数: {}", context.AttackerPet().Pet().Name(), resist)
return resist
case structs.SPECIAL:
// 特殊伤害:使用特殊抗性区
resist := decimal.NewFromFloat(context.GetOtherRate(fight.SP_ATK_RESISTANCE_ZONE))
logger.Debug("{}特攻减伤系数: {}", context.AttackerPet().Pet().Name(), resist)
return resist
default:
return decimal.NewFromFloat(1.0) // 未知类型默认无减伤
}
}
// calculateBaseDamage 计算基础伤害部分
func calculateBaseDamage(levelFactor, powerFactor decimal.Decimal, context *DamageContext, attackCount decimal.Decimal) decimal.Decimal {
attack := decimal.NewFromInt64(context.Attack)
defense := decimal.NewFromInt64(context.Defense)
// (等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2)
return levelFactor.
Mul(powerFactor).
Mul(attack).
Div(decimal.NewFromInt(50)).
Add(decimal.NewFromInt(2))
}
// calculateBonusFactors 计算所有加成系数(同系、克制、暴击等)
func calculateBonusFactors(context *DamageContext, isCritical bool, damageReduction decimal.Decimal) struct {
sameType decimal.Decimal
typeRate decimal.Decimal
critical decimal.Decimal
specialEffect decimal.Decimal
damageReduction decimal.Decimal
} {
return struct {
sameType decimal.Decimal
typeRate decimal.Decimal
critical decimal.Decimal
specialEffect decimal.Decimal
damageReduction decimal.Decimal
}{
sameType: decimal.NewFromFloat(calculateSameTypeBonus(context)),
typeRate: decimal.NewFromFloat(calculateType克制Bonus(context)),
critical: decimal.NewFromFloat(calculateCriticalBonus(context, isCritical)),
specialEffect: decimal.NewFromFloat(context.GetOtherRate(fight.SPECIAL_EFFECT_MULTIPLIER_ZONE)),
damageReduction: damageReduction,
}
}
// calculateTotalDamage 计算总伤害(基础伤害 * 所有加成系数)
func calculateTotalDamage(baseDamage decimal.Decimal, factors struct {
sameType decimal.Decimal
typeRate decimal.Decimal
critical decimal.Decimal
specialEffect decimal.Decimal
damageReduction decimal.Decimal
}, context *DamageContext) decimal.Decimal {
// 浮动系数random / 255
randomFactor := decimal.NewFromInt(int64(context.Random)).Div(decimal.NewFromInt(255), 10)
// 总伤害 = 基础伤害 * 同系加成 * 克制系数 * 暴击系数 * 减伤系数 * 特殊效果 * 浮动系数
return baseDamage.
Mul(factors.sameType).
Mul(factors.typeRate).
Mul(factors.critical).
Mul(factors.damageReduction).
Mul(factors.specialEffect).
Mul(randomFactor)
}
// applyFixDamageReduction 应用固定伤害减免
func applyFixDamageReduction(totalDamage decimal.Decimal, context *DamageContext) decimal.Decimal {
fixReduction := decimal.NewFromFloat(context.GetOtherRate(fight.FIX_DAMAGE_RESISTANCE_ZONE))
if fixReduction.LessThanOrEqual(decimal.Zero) {
return totalDamage
}
logger.Debug("{}固定伤害减免: {}", context.AttackerPet().Pet().Name(), fixReduction)
if fixReduction.GreaterThanOrEqual(totalDamage) {
return decimal.Zero // 减免后伤害为0
}
return totalDamage.Sub(fixReduction) // 减去固定减免值
}
// calculateSameTypeBonus 计算同系加成系数(技能类型与精灵类型相同时生效)
func calculateSameTypeBonus(context *DamageContext) float64 {
if context.Type() == context.AttackerPet().Type() {
bonus := context.SameTypeRate()
logger.Trace("{}触发同系加成,系数: {}", context.AttackerPet().Pet().Name(), bonus)
return bonus
}
return 1.0 // 非同系无加成
}
// calculateType克制Bonus 计算属性克制系数
func calculateType克制Bonus(context *DamageContext) float64 {
skillType := context.Skill.Type()
defenderType := context.DefenderPet().Type()
rate := newBattle.GetGameResourceRepo().GetPetAttributeCounter(skillType, defenderType)
logger.Trace("{}({}) 攻击 {}({}),克制系数: {}",
context.AttackerPet().Pet().Name(), skillType,
context.DefenderPet().Pet().Name(), defenderType, rate)
return rate
}
// calculateCriticalBonus 计算暴击加成系数
func calculateCriticalBonus(context *DamageContext, isCritical bool) float64 {
if isCritical {
criticalRate := context.CriticalRate()
logger.Trace("触发暴击,暴击系数: {}", criticalRate)
return criticalRate
}
return 1.0 // 未暴击无加成
}

View File

@@ -0,0 +1,234 @@
package damage
import (
"fmt"
"strings"
)
// DamageContext 伤害计算上下文与Java版本字段和逻辑完全对应
type DamageContext struct {
// 不可变字段对应Java的final
attacker *entity.BattleInputSourceEntity // 攻击者
defender *entity.BattleInputSourceEntity // 防御者
originalAttackerPet *entity.BattlePetEntity // 原始攻击宠物
originalDefenderPet *entity.BattlePetEntity // 原始防御宠物
skill *entity.BattleSkillEntity // 技能
// 可变字段
Type int64 // 技能属性类型
BasePower int64 // 基础威力
Attack int64 // 攻击值
Defense int64 // 防御值
CriticalRate float64 // 暴击倍率默认2.0
SameTypeRate float64 // 同系加成倍率默认1.5
Random int // 随机值(默认-1
otherRates map[fight.DamageMultiplierZone]float64 // 伤害乘区倍率映射
extraRates map[string]float64 // 额外倍率映射
}
// NewDamageContext 创建DamageContext实例对应Java构造函数
func NewDamageContext(attacker, defender *entity.BattleInputSourceEntity, skill *entity.BattleSkillEntity) *DamageContext {
ctx := &DamageContext{
attacker: attacker,
defender: defender,
originalAttackerPet: attacker.MainPet(),
originalDefenderPet: defender.MainPet(),
skill: skill,
// 初始化默认值对应Java的字段默认值
CriticalRate: 2.0,
SameTypeRate: 1.5,
Random: -1,
otherRates: make(map[fight.DamageMultiplierZone]float64),
extraRates: make(map[string]float64),
}
ctx.initOtherRates() // 初始化伤害乘区默认值
return ctx
}
// initOtherRates 初始化otherRates默认值与Java完全一致
func (c *DamageContext) initOtherRates() {
c.otherRates[fight.DamageMultiplierZoneEnum.POWER_ADDITION_ZONE] = 0.0
c.otherRates[fight.DamageMultiplierZoneEnum.POWER_MULTIPLIER_ZONE] = 1.0
c.otherRates[fight.DamageMultiplierZoneEnum.ATK_RESISTANCE_ZONE] = 1.0
c.otherRates[fight.DamageMultiplierZoneEnum.SP_ATK_RESISTANCE_ZONE] = 1.0
c.otherRates[fight.DamageMultiplierZoneEnum.SPECIAL_EFFECT_MULTIPLIER_ZONE] = 1.0
c.otherRates[fight.DamageMultiplierZoneEnum.ATTACK_COUNT_ZONE] = 1.0
c.otherRates[fight.DamageMultiplierZoneEnum.FIX_DAMAGE_RESISTANCE_ZONE] = 0.0
}
// PutOtherRate 设置伤害乘区倍率支持链式调用对应Java的putOtherRate
func (c *DamageContext) PutOtherRate(key fight.DamageMultiplierZone, value float64) *DamageContext {
switch key {
case fight.DamageMultiplierZoneEnum.FIX_DAMAGE_RESISTANCE_ZONE:
// 固定伤害减免区:累加
c.otherRates[key] = c.GetOtherRate(key) + value
case fight.DamageMultiplierZoneEnum.ATTACK_COUNT_ZONE:
// 攻击次数区:直接覆盖
c.otherRates[key] = value
case fight.DamageMultiplierZoneEnum.ATK_RESISTANCE_ZONE, fight.DamageMultiplierZoneEnum.SP_ATK_RESISTANCE_ZONE:
// 抗性区乘法叠加仅当value>0
if value > 0 {
c.otherRates[key] = c.GetOtherRate(key) * value
}
default:
// 其他区加法叠加且不小于0.1
c.otherRates[key] = c.GetOtherRate(key) + value
if c.otherRates[key] < 0 {
c.otherRates[key] = 0.1
}
}
return c // 链式调用支持
}
// RemoveOtherRate 重置伤害乘区倍率为1.0支持链式调用对应Java的removeOtherRate
func (c *DamageContext) RemoveOtherRate(key fight.DamageMultiplierZone) *DamageContext {
c.otherRates[key] = 1.0
return c // 链式调用支持
}
// PutExtraRate 设置额外倍率对应Java的putExtraRate
func (c *DamageContext) PutExtraRate(key string, value float64) {
c.extraRates[key] = value
}
// GetExtraRate 获取额外倍率对应Java的getExtraRate
// 返回值:(倍率值, 是否存在)
func (c *DamageContext) GetExtraRate(key string) (float64, bool) {
val, ok := c.extraRates[key]
return val, ok
}
// GetOtherRate 获取伤害乘区倍率对应Java的getOtherRate
func (c *DamageContext) GetOtherRate(key fight.DamageMultiplierZone) float64 {
if val, ok := c.otherRates[key]; ok {
return val
}
return 1.0 // 默认值1.0
}
// AttackerPet 返回当前攻击宠物对应Java的attackerPet()
func (c *DamageContext) AttackerPet() *entity.BattlePetEntity {
return c.attacker.MainPet()
}
// DefenderPet 返回当前防御宠物对应Java的defenderPet()
func (c *DamageContext) DefenderPet() *entity.BattlePetEntity {
return c.defender.MainPet()
}
// Dump 生成调试字符串对应Java的dump()
func (c *DamageContext) Dump() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("DamageContext(Type=%d, ", c.Type))
sb.WriteString(fmt.Sprintf("basePower=%d, ", c.BasePower))
sb.WriteString(fmt.Sprintf("attack=%d, ", c.Attack))
sb.WriteString(fmt.Sprintf("defense=%d, ", c.Defense))
sb.WriteString(fmt.Sprintf("criticalRate=%.2f, ", c.CriticalRate))
sb.WriteString(fmt.Sprintf("sameTypeRate=%.2f, ", c.SameTypeRate))
sb.WriteString(fmt.Sprintf("random=%d)", c.Random))
return sb.String()
}
// 以下为 getter/setter 方法对应Java的fluent风格访问器
// Attacker 返回攻击者
func (c *DamageContext) Attacker() *entity.BattleInputSourceEntity {
return c.attacker
}
// Defender 返回防御者
func (c *DamageContext) Defender() *entity.BattleInputSourceEntity {
return c.defender
}
// OriginalAttackerPet 返回原始攻击宠物
func (c *DamageContext) OriginalAttackerPet() *entity.BattlePetEntity {
return c.originalAttackerPet
}
// OriginalDefenderPet 返回原始防御宠物
func (c *DamageContext) OriginalDefenderPet() *entity.BattlePetEntity {
return c.originalDefenderPet
}
// Skill 返回技能
func (c *DamageContext) Skill() *entity.BattleSkillEntity {
return c.skill
}
// Type 返回技能属性类型
func (c *DamageContext) GetType() int64 {
return c.Type
}
// SetType 设置技能属性类型(支持链式调用)
func (c *DamageContext) SetType(t int64) *DamageContext {
c.Type = t
return c
}
// BasePower 返回基础威力
func (c *DamageContext) GetBasePower() int64 {
return c.BasePower
}
// SetBasePower 设置基础威力(支持链式调用)
func (c *DamageContext) SetBasePower(p int64) *DamageContext {
c.BasePower = p
return c
}
// Attack 返回攻击值
func (c *DamageContext) GetAttack() int64 {
return c.Attack
}
// SetAttack 设置攻击值(支持链式调用)
func (c *DamageContext) SetAttack(a int64) *DamageContext {
c.Attack = a
return c
}
// Defense 返回防御值
func (c *DamageContext) GetDefense() int64 {
return c.Defense
}
// SetDefense 设置防御值(支持链式调用)
func (c *DamageContext) SetDefense(d int64) *DamageContext {
c.Defense = d
return c
}
// CriticalRate 返回暴击倍率
func (c *DamageContext) GetCriticalRate() float64 {
return c.CriticalRate
}
// SetCriticalRate 设置暴击倍率(支持链式调用)
func (c *DamageContext) SetCriticalRate(r float64) *DamageContext {
c.CriticalRate = r
return c
}
// SameTypeRate 返回同系加成倍率
func (c *DamageContext) GetSameTypeRate() float64 {
return c.SameTypeRate
}
// SetSameTypeRate 设置同系加成倍率(支持链式调用)
func (c *DamageContext) SetSameTypeRate(r float64) *DamageContext {
c.SameTypeRate = r
return c
}
// Random 返回随机值
func (c *DamageContext) GetRandom() int {
return c.Random
}
// SetRandom 设置随机值(支持链式调用)
func (c *DamageContext) SetRandom(r int) *DamageContext {
c.Random = r
return c
}

View File

@@ -0,0 +1,77 @@
package damage
import (
"fmt"
"github.com/tnnmigga/enum"
)
// DamageMultiplierZone 伤害乘算区枚举
type DamageMultiplierZone int
// 定义伤害乘算区常量
var DamageMultiplierZoneEnum = enum.New[struct {
// 威力加算区:单纯的威力增加(如"威力+20"
POWER_ADDITION_ZONE DamageMultiplierZone
// 威力乘算区:威力倍率调整(如"威力翻倍"
POWER_MULTIPLIER_ZONE DamageMultiplierZone
// 物理伤害减免乘区默认1减伤50%对应0.5
ATK_RESISTANCE_ZONE DamageMultiplierZone
// 特殊伤害减免乘区默认1减伤50%对应0.5
SP_ATK_RESISTANCE_ZONE DamageMultiplierZone
// 特殊效果乘算区如Boss伤害减免、技能特殊效果
SPECIAL_EFFECT_MULTIPLIER_ZONE DamageMultiplierZone
// 攻击次数默认1如"攻击2次"对应2
ATTACK_COUNT_ZONE DamageMultiplierZone
// 固定伤害减免默认0直接扣除固定值
FIX_DAMAGE_RESISTANCE_ZONE DamageMultiplierZone
}]()
// Code 返回枚举对应的整数值
func (z DamageMultiplierZone) Code() int {
return int(z)
}
// String 实现Stringer接口
func (z DamageMultiplierZone) String() string {
switch z {
case DamageMultiplierZoneEnum.POWER_ADDITION_ZONE:
return "POWER_ADDITION_ZONE"
case DamageMultiplierZoneEnum.POWER_MULTIPLIER_ZONE:
return "POWER_MULTIPLIER_ZONE"
case DamageMultiplierZoneEnum.ATK_RESISTANCE_ZONE:
return "ATK_RESISTANCE_ZONE"
case DamageMultiplierZoneEnum.SP_ATK_RESISTANCE_ZONE:
return "SP_ATK_RESISTANCE_ZONE"
case DamageMultiplierZoneEnum.SPECIAL_EFFECT_MULTIPLIER_ZONE:
return "SPECIAL_EFFECT_MULTIPLIER_ZONE"
case DamageMultiplierZoneEnum.ATTACK_COUNT_ZONE:
return "ATTACK_COUNT_ZONE"
case DamageMultiplierZoneEnum.FIX_DAMAGE_RESISTANCE_ZONE:
return "FIX_DAMAGE_RESISTANCE_ZONE"
default:
return fmt.Sprintf("Unknown(DamageMultiplierZone=%d)", z)
}
}
// FromCode 根据整数值获取枚举值
func FromCode(code int) (DamageMultiplierZone, error) {
switch code {
case 1:
return DamageMultiplierZoneEnum.POWER_ADDITION_ZONE, nil
case 2:
return DamageMultiplierZoneEnum.POWER_MULTIPLIER_ZONE, nil
case 3:
return DamageMultiplierZoneEnum.ATK_RESISTANCE_ZONE, nil
case 4:
return DamageMultiplierZoneEnum.SP_ATK_RESISTANCE_ZONE, nil
case 5:
return DamageMultiplierZoneEnum.SPECIAL_EFFECT_MULTIPLIER_ZONE, nil
case 6:
return DamageMultiplierZoneEnum.ATTACK_COUNT_ZONE, nil
case 7:
return DamageMultiplierZoneEnum.FIX_DAMAGE_RESISTANCE_ZONE, nil
default:
return 0, fmt.Errorf("未知的DamageMultiplierZone代码: %d", code)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
package fight
import "github.com/tnnmigga/enum"
// 战斗模式
type EnumBattleMode int
var BattleMode = enum.New[struct {
PVE EnumBattleMode // 玩家 vs AI
PVP EnumBattleMode // 玩家 vs 玩家
}]()