diff --git a/logic/service/fight/damage/Calculator.go1 b/logic/service/fight/damage/Calculator.go1 new file mode 100644 index 000000000..a1d357d2f --- /dev/null +++ b/logic/service/fight/damage/Calculator.go1 @@ -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 // 未暴击无加成 +} diff --git a/logic/service/fight/damage/Context.go1 b/logic/service/fight/damage/Context.go1 new file mode 100644 index 000000000..6df418c37 --- /dev/null +++ b/logic/service/fight/damage/Context.go1 @@ -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 +} diff --git a/logic/service/fight/damage/MultiplierZone.go b/logic/service/fight/damage/MultiplierZone.go new file mode 100644 index 000000000..ad40e1541 --- /dev/null +++ b/logic/service/fight/damage/MultiplierZone.go @@ -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) + } +} diff --git a/logic/service/fight/fight.go1 b/logic/service/fight/fight.go1 new file mode 100644 index 000000000..ead258cfb --- /dev/null +++ b/logic/service/fight/fight.go1 @@ -0,0 +1,1075 @@ +package fight + +import ( + "bufio" + "fmt" + "math" + "math/rand" + "os" + "strconv" + "strings" + "sync" + "time" +) + +// 战斗阶段 +type BattlePhase int + +const ( + PreTurn BattlePhase = iota // 回合开始前 + PreSkill // 技能使用前 + SkillUse // 技能生效时(执行所有效果) + PostTurn // 回合结束后(处理持续状态) +) + +// 效果接口:所有技能效果都需实现此接口 +type Effect interface { + Execute(ctx *BattleCtx) // 执行效果,修改战斗上下文 +} + +// 技能定义:由多个效果组成 +type Skill struct { + Name string + Type PokemonType // 技能类型 + Effects []Effect // 效果列表(伤害、状态、增益等) + PP int // 技能使用次数 + MaxPP int // 最大使用次数 + Accuracy int // 命中率(0-100) +} + +// 精灵类型 +type PokemonType string + +const ( + TypeElectric PokemonType = "电" + TypeGrass PokemonType = "草" + TypeWater PokemonType = "水" + TypeFire PokemonType = "火" +) + +// 精灵属性 +type Pokemon struct { + ID string + Type PokemonType + HP int + MaxHP int + Attack int + Defense int + SpAttack int + SpDefense int + Speed int + Level int + Skills []*Skill + Statuses []*Status // 持续状态(中毒、烧伤等) + Buffs []*Buff // 临时增益(攻击提升、防御降低等) +} + +// 持续状态(如中毒) +type Status struct { + Name string // 状态名称 + Damage int // 每回合伤害 + Turns int // 剩余持续回合 + CanAct bool // 是否可以行动 + OnTurnEnd func(*Pokemon) // 回合结束时触发的效果 +} + +// 临时增益/减益 +type Buff struct { + Name string // 增益名称 + Stat string // 影响的属性(Attack, Defense等) + Modifier float64 // 修正比例(1.5表示提升50%) + Turns int // 持续回合 +} + +// 玩家接口(统一决策) +type Player interface { + GetID() string + GetPokemons() []*Pokemon + GetActivePokemon() *Pokemon + ChooseSkill(ctx *BattleCtx) *Skill // 选择技能 + ChooseTarget(availableTargets []*Pokemon) *Pokemon // 选择目标 + IsAI() bool +} + +// 战斗上下文:传递阶段数据 + 效果交互 +type BattleCtx struct { + Attacker Player // 进攻方 + Defender Player // 防守方 + User *Pokemon // 当前行动精灵 + Target *Pokemon // 目标精灵 + Damage int // 实时伤害(效果可修改) + Phase BattlePhase // 当前阶段 + Skill *Skill // 当前使用的技能 + IsCrit bool // 是否暴击 +} + +// 战斗管理器:调度阶段 + 效果执行 +type BattleManager struct { + hooks map[BattlePhase][]func(*BattleCtx) // 阶段事件钩子 + isOver bool // 战斗是否结束 + mu sync.Mutex // 线程安全锁 + mode EnumBattleMode // 战斗模式 +} + +func NewBattleManager(mode EnumBattleMode) *BattleManager { + return &BattleManager{ + hooks: make(map[BattlePhase][]func(*BattleCtx)), + mode: mode, + } +} + +// 注册阶段事件 +func (m *BattleManager) On(phase BattlePhase, hook func(*BattleCtx)) { + m.mu.Lock() + defer m.mu.Unlock() + m.hooks[phase] = append(m.hooks[phase], hook) +} + +// 触发阶段事件(支持中断) +func (m *BattleManager) trigger(ctx *BattleCtx) { + m.mu.Lock() + defer m.mu.Unlock() + for _, fn := range m.hooks[ctx.Phase] { + fn(ctx) + if m.isOver { + return // 战斗结束,提前退出 + } + } +} + +// 执行单回合(进攻方 → 防守方) +func (m *BattleManager) RunTurn(attacker, defender Player) { + // 1. 筛选存活精灵 + user := attacker.GetActivePokemon() + if user == nil { + m.isOver = true + fmt.Printf("[%s] 已无存活精灵,战斗结束!\n", attacker.GetID()) + return + } + + // 检查是否可以行动(状态影响) + if !canPokemonAct(user) { + fmt.Printf("[%s] %s 因状态无法行动!\n", attacker.GetID(), user.ID) + return + } + + // 2. 初始化上下文(伤害初始为0,由效果叠加) + ctx := &BattleCtx{ + Attacker: attacker, + Defender: defender, + User: user, + Damage: 0, // 效果将修改此值 + } + + // 3. 阶段1:回合开始前(检查状态、buff) + ctx.Phase = PreTurn + m.trigger(ctx) + if m.isOver { + return + } + + // 4. 决策:选择技能 + availableSkills := getAvailableSkills(user) + if len(availableSkills) == 0 { + fmt.Printf("[%s] %s 无可用技能,跳过本次行动!\n", attacker.GetID(), user.ID) + return + } + + chosenSkill := attacker.ChooseSkill(ctx) + if chosenSkill == nil { + fmt.Printf("[%s] 未选择技能,跳过本次行动!\n", attacker.GetID()) + return + } + ctx.Skill = chosenSkill + + // 5. 选择目标 + availableTargets := getAlivePokemons(defender.GetPokemons()) + if len(availableTargets) == 0 { + m.isOver = true + fmt.Printf("[%s] 已无存活精灵,战斗结束!\n", defender.GetID()) + return + } + + var target *Pokemon + if len(availableTargets) > 1 { + target = attacker.ChooseTarget(availableTargets) + } else { + target = availableTargets[0] + } + ctx.Target = target + + // 6. 阶段2:技能使用前(血量检查、前置条件) + ctx.Phase = PreSkill + m.trigger(ctx) + if m.isOver { + return + } + + // 7. 阶段3:技能生效时(执行所有效果) + ctx.Phase = SkillUse + fmt.Printf("[%s] %s 使用 %s(PP: %d/%d)\n", + attacker.GetID(), user.ID, chosenSkill.Name, chosenSkill.PP, chosenSkill.MaxPP) + + // 检查命中率 + if !checkAccuracy(chosenSkill) { + fmt.Printf(" → 技能 %s 未命中!\n", chosenSkill.Name) + return + } + + // 检查暴击 + ctx.IsCrit = checkCriticalHit() + if ctx.IsCrit { + fmt.Println(" → 暴击!伤害翻倍!") + } + + // 计算属性相克 + typeEffectiveness := calculateTypeEffectiveness(chosenSkill.Type, target.Type) + if typeEffectiveness > 1.0 { + fmt.Println(" → 效果拔群!") + } else if typeEffectiveness < 1.0 && typeEffectiveness > 0 { + fmt.Println(" → 效果一般...") + } else if typeEffectiveness == 0 { + fmt.Println(" → 完全无效!") + return + } + + // 应用属性修正 + modifier := getStatModifier(user, "Attack") / getStatModifier(target, "Defense") + + // 遍历技能的所有效果,依次执行 + for _, effect := range chosenSkill.Effects { + effect.Execute(ctx) + } + + // 应用属性相克和暴击修正 + ctx.Damage = int(float64(ctx.Damage) * typeEffectiveness) + if ctx.IsCrit { + ctx.Damage = int(float64(ctx.Damage) * 2.0) // 暴击伤害翻倍 + } + + // 应用伤害修正 + ctx.Damage = int(float64(ctx.Damage) * modifier) + + m.trigger(ctx) // 触发阶段钩子(可扩展额外逻辑) + if m.isOver { + return + } + + // 8. 应用最终伤害(如果有伤害) + if ctx.Damage > 0 { + target.HP -= ctx.Damage + fmt.Printf(" → 最终伤害:%d,%s 剩余HP: %d\n", + ctx.Damage, target.ID, target.HP) + } + + // 减少技能PP + chosenSkill.PP-- + + // 9. 阶段4:回合结束后(处理持续状态 + 死亡判定) + ctx.Phase = PostTurn + m.trigger(ctx) +} + +// 检查精灵是否可以行动 +func canPokemonAct(p *Pokemon) bool { + for _, status := range p.Statuses { + if !status.CanAct { + return false + } + } + return true +} + +// 获取可用技能 +func getAvailableSkills(p *Pokemon) []*Skill { + var available []*Skill + for _, skill := range p.Skills { + if skill.PP > 0 { + available = append(available, skill) + } + } + return available +} + +// 获取存活精灵 +func getAlivePokemons(pokemons []*Pokemon) []*Pokemon { + var alive []*Pokemon + for _, p := range pokemons { + if p.HP > 0 { + alive = append(alive, p) + } + } + return alive +} + +// 检查命中率 +func checkAccuracy(skill *Skill) bool { + return rand.Intn(100) < skill.Accuracy +} + +// 检查暴击 +func checkCriticalHit() bool { + return rand.Intn(20) == 0 // 5%暴击率 +} + +// 计算属性相克 +func calculateTypeEffectiveness(attackType, defenseType PokemonType) float64 { + // 简单的属性相克表 + effectiveness := map[PokemonType]map[PokemonType]float64{ + TypeElectric: { + TypeWater: 2.0, + TypeGrass: 0.5, + TypeElectric: 0.5, + }, + TypeGrass: { + TypeWater: 2.0, + TypeFire: 2.0, + TypeGrass: 0.5, + TypeElectric: 1.0, + }, + TypeWater: { + TypeFire: 2.0, + TypeGrass: 0.5, + TypeWater: 0.5, + }, + TypeFire: { + TypeGrass: 2.0, + TypeWater: 0.5, + TypeFire: 0.5, + }, + } + + // 默认1.0 + if _, ok := effectiveness[attackType]; !ok { + return 1.0 + } + + if value, ok := effectiveness[attackType][defenseType]; ok { + return value + } + + return 1.0 +} + +// 获取属性修正值 +func getStatModifier(p *Pokemon, stat string) float64 { + modifier := 1.0 + for _, buff := range p.Buffs { + if buff.Stat == stat { + modifier *= buff.Modifier + } + } + return modifier +} + +// -------------------- 效果实现 -------------------- // + +// 1. 基础伤害效果 +type DamageEffect struct { + BasePower int // 基础伤害值 +} + +func (e *DamageEffect) Execute(ctx *BattleCtx) { + // 基础伤害 + 攻击力修正 + attack := ctx.User.Attack + if ctx.Skill.Type == ctx.User.Type { + attack = int(float64(attack) * 1.5) // 本系加成50% + } + + // 简单的伤害计算公式 + baseDamage := int(math.Floor(0.85 * float64(e.BasePower) * float64(attack) / float64(ctx.Target.Defense))) + ctx.Damage += baseDamage +} + +// 2. 伤害修正效果(增伤/减伤) +type ModifyDamageEffect struct { + Percent float64 // 百分比修正(1.5=增伤50%,0.8=减伤20%) +} + +func (e *ModifyDamageEffect) Execute(ctx *BattleCtx) { + ctx.Damage = int(float64(ctx.Damage) * (1 + e.Percent)) +} + +// 3. 附加持续状态(如中毒) +type StatusEffect struct { + Name string // 状态名称 + Damage int // 每回合伤害 + Turns int // 持续回合数 + CanAct bool // 是否可以行动 +} + +func (e *StatusEffect) Execute(ctx *BattleCtx) { + // 检查是否已存在相同状态 + for _, status := range ctx.Target.Statuses { + if status.Name == e.Name { + // 刷新状态 + status.Turns = e.Turns + return + } + } + + ctx.Target.Statuses = append(ctx.Target.Statuses, &Status{ + Name: e.Name, + Damage: e.Damage, + Turns: e.Turns, + CanAct: e.CanAct, + OnTurnEnd: func(p *Pokemon) { + if e.Name == "中毒" { + p.HP -= e.Damage + fmt.Printf(" → %s 因中毒受到 %d 点伤害\n", p.ID, e.Damage) + } else if e.Name == "烧伤" { + p.HP -= e.Damage + fmt.Printf(" → %s 因烧伤受到 %d 点伤害\n", p.ID, e.Damage) + // 烧伤降低攻击力 + p.Attack = int(math.Max(1, float64(p.Attack)*0.75)) + } + }, + }) +} + +// 4. 治疗效果 +type HealEffect struct { + Amount int // 治疗量 +} + +func (e *HealEffect) Execute(ctx *BattleCtx) { + // 治疗量不超过最大HP + healAmount := e.Amount + if ctx.User.HP+healAmount > ctx.User.MaxHP { + healAmount = ctx.User.MaxHP - ctx.User.HP + } + + ctx.User.HP += healAmount + fmt.Printf(" → %s 回复了 %d HP,当前HP: %d\n", + ctx.User.ID, healAmount, ctx.User.HP) +} + +// 5. 属性增益效果 +type StatBoostEffect struct { + Stat string // 影响的属性 + Modifier float64 // 修正比例 + Turns int // 持续回合 +} + +func (e *StatBoostEffect) Execute(ctx *BattleCtx) { + ctx.User.Buffs = append(ctx.User.Buffs, &Buff{ + Name: fmt.Sprintf("%s提升", e.Stat), + Stat: e.Stat, + Modifier: e.Modifier, + Turns: e.Turns, + }) + + fmt.Printf(" → %s的%s提升了!\n", ctx.User.ID, e.Stat) +} + +// 6. 属性减益效果 +type StatDebuffEffect struct { + Stat string // 影响的属性 + Modifier float64 // 修正比例 + Turns int // 持续回合 +} + +func (e *StatDebuffEffect) Execute(ctx *BattleCtx) { + ctx.Target.Buffs = append(ctx.Target.Buffs, &Buff{ + Name: fmt.Sprintf("%s降低", e.Stat), + Stat: e.Stat, + Modifier: 1.0 / e.Modifier, // 减益是增益的倒数 + Turns: e.Turns, + }) + + fmt.Printf(" → %s的%s降低了!\n", ctx.Target.ID, e.Stat) +} + +// -------------------- 玩家实现 -------------------- // + +// 人类玩家 +type HumanPlayer struct { + id string + pokemons []*Pokemon + activeIndex int // 当前活跃精灵的索引 +} + +func NewHumanPlayer(id string, pokemons []*Pokemon) *HumanPlayer { + return &HumanPlayer{ + id: id, + pokemons: pokemons, + activeIndex: 0, // 默认第一只精灵 + } +} + +func (p *HumanPlayer) GetID() string { return p.id } +func (p *HumanPlayer) GetPokemons() []*Pokemon { return p.pokemons } +func (p *HumanPlayer) IsAI() bool { return false } + +func (p *HumanPlayer) GetActivePokemon() *Pokemon { + if p.activeIndex < 0 || p.activeIndex >= len(p.pokemons) { + return nil + } + return p.pokemons[p.activeIndex] +} + +func (p *HumanPlayer) ChooseSkill(ctx *BattleCtx) *Skill { + // 显示当前精灵状态 + fmt.Printf("\n[%s] %s 的回合(HP: %d/%d)\n", + p.id, ctx.User.ID, ctx.User.HP, ctx.User.MaxHP) + + // 显示可用技能 + availableSkills := getAvailableSkills(ctx.User) + if len(availableSkills) == 0 { + fmt.Println("无可用技能!") + return nil + } + + fmt.Println("可用技能:") + for i, skill := range availableSkills { + fmt.Printf("%d. %s (PP: %d/%d)\n", i+1, skill.Name, skill.PP, skill.MaxPP) + } + + // 获取用户选择 + reader := bufio.NewReader(os.Stdin) + var choice int + + for { + fmt.Print("请选择技能 (1-", len(availableSkills), "): ") + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("输入错误,请重试!") + continue + } + + input = strings.TrimSpace(input) + choice, err = strconv.Atoi(input) + if err != nil || choice < 1 || choice > len(availableSkills) { + fmt.Println("无效选择,请重试!") + continue + } + + break + } + + return availableSkills[choice-1] +} + +func (p *HumanPlayer) ChooseTarget(availableTargets []*Pokemon) *Pokemon { + if len(availableTargets) == 1 { + return availableTargets[0] + } + + fmt.Println("选择目标:") + for i, target := range availableTargets { + fmt.Printf("%d. %s (HP: %d/%d)\n", i+1, target.ID, target.HP, target.MaxHP) + } + + // 获取用户选择 + reader := bufio.NewReader(os.Stdin) + var choice int + + for { + fmt.Print("请选择目标 (1-", len(availableTargets), "): ") + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("输入错误,请重试!") + continue + } + + input = strings.TrimSpace(input) + choice, err = strconv.Atoi(input) + if err != nil || choice < 1 || choice > len(availableTargets) { + fmt.Println("无效选择,请重试!") + continue + } + + break + } + + return availableTargets[choice-1] +} + +// AI玩家 +type AIPlayer struct { + id string + pokemons []*Pokemon + activeIndex int +} + +func NewAIPlayer(id string, pokemons []*Pokemon) *AIPlayer { + return &AIPlayer{ + id: id, + pokemons: pokemons, + activeIndex: 0, + } +} + +func (p *AIPlayer) GetID() string { return p.id } +func (p *AIPlayer) GetPokemons() []*Pokemon { return p.pokemons } +func (p *AIPlayer) IsAI() bool { return true } + +func (p *AIPlayer) GetActivePokemon() *Pokemon { + if p.activeIndex < 0 || p.activeIndex >= len(p.pokemons) { + return nil + } + return p.pokemons[p.activeIndex] +} + +func (p *AIPlayer) ChooseSkill(ctx *BattleCtx) *Skill { + time.Sleep(1 * time.Second) // 模拟思考 + + availableSkills := getAvailableSkills(ctx.User) + if len(availableSkills) == 0 { + return nil + } + + // 简单AI逻辑:优先选择伤害最高的技能 + bestSkill := availableSkills[0] + highestDamage := 0 + + for _, skill := range availableSkills { + // 计算预估伤害 + estimatedDamage := 0 + for _, effect := range skill.Effects { + if damageEffect, ok := effect.(*DamageEffect); ok { + estimatedDamage += damageEffect.BasePower + } + } + + // 考虑属性相克 + typeEffectiveness := calculateTypeEffectiveness(skill.Type, ctx.Target.Type) + estimatedDamage = int(float64(estimatedDamage) * typeEffectiveness) + + if estimatedDamage > highestDamage { + highestDamage = estimatedDamage + bestSkill = skill + } + } + + fmt.Printf("[%s(AI)] %s 使用 %s\n", p.id, ctx.User.ID, bestSkill.Name) + return bestSkill +} + +func (p *AIPlayer) ChooseTarget(availableTargets []*Pokemon) *Pokemon { + time.Sleep(1 * time.Second) // 模拟思考 + + // 简单AI逻辑:优先选择HP最低的目标 + var target *Pokemon + lowestHP := math.MaxInt32 + + for _, p := range availableTargets { + if p.HP < lowestHP { + lowestHP = p.HP + target = p + } + } + + return target +} + +// -------------------- 示例:创建精灵和技能 -------------------- // + +// 创建皮卡丘 +func createPikachu() *Pokemon { + return &Pokemon{ + ID: "皮卡丘", + Type: TypeElectric, + HP: 100, + MaxHP: 100, + Attack: 55, + Defense: 40, + SpAttack: 50, + SpDefense: 50, + Speed: 90, + Level: 5, + Skills: []*Skill{ + { + Name: "十万伏特", + Type: TypeElectric, + Effects: []Effect{ + &DamageEffect{BasePower: 90}, + }, + PP: 15, + MaxPP: 15, + Accuracy: 100, + }, + { + Name: "伏特攻击", + Type: TypeElectric, + Effects: []Effect{ + &DamageEffect{BasePower: 50}, + &StatusEffect{Name: "麻痹", Damage: 0, Turns: 3, CanAct: false}, + }, + PP: 30, + MaxPP: 30, + Accuracy: 100, + }, + { + Name: "电网", + Type: TypeElectric, + Effects: []Effect{ + &DamageEffect{BasePower: 40}, + &StatDebuffEffect{Stat: "Speed", Modifier: 1.5, Turns: 3}, + }, + PP: 20, + MaxPP: 20, + Accuracy: 100, + }, + { + Name: "充电", + Type: TypeElectric, + Effects: []Effect{ + &HealEffect{Amount: 20}, + &StatBoostEffect{Stat: "SpAttack", Modifier: 1.5, Turns: 2}, + }, + PP: 10, + MaxPP: 10, + Accuracy: 100, + }, + }, + } +} + +// 创建妙蛙种子 +func createBulbasaur() *Pokemon { + return &Pokemon{ + ID: "妙蛙种子", + Type: TypeGrass, + HP: 120, + MaxHP: 120, + Attack: 49, + Defense: 49, + SpAttack: 65, + SpDefense: 65, + Speed: 45, + Level: 5, + Skills: []*Skill{ + { + Name: "飞叶快刀", + Type: TypeGrass, + Effects: []Effect{ + &DamageEffect{BasePower: 55}, + }, + PP: 25, + MaxPP: 25, + Accuracy: 95, + }, + { + Name: "藤鞭", + Type: TypeGrass, + Effects: []Effect{ + &DamageEffect{BasePower: 45}, + }, + PP: 30, + MaxPP: 30, + Accuracy: 100, + }, + { + Name: "寄生种子", + Type: TypeGrass, + Effects: []Effect{ + &StatusEffect{ + Name: "寄生", + Damage: 10, + Turns: 5, + CanAct: true, + }, + }, + PP: 10, + MaxPP: 10, + Accuracy: 90, + }, + { + Name: "生长", + Type: TypeGrass, + Effects: []Effect{ + &StatBoostEffect{Stat: "Attack", Modifier: 1.5, Turns: 2}, + &StatBoostEffect{Stat: "SpAttack", Modifier: 1.5, Turns: 2}, + }, + PP: 20, + MaxPP: 20, + Accuracy: 100, + }, + }, + } +} + +// 创建杰尼龟 +func createSquirtle() *Pokemon { + return &Pokemon{ + ID: "杰尼龟", + Type: TypeWater, + HP: 110, + MaxHP: 110, + Attack: 48, + Defense: 65, + SpAttack: 50, + SpDefense: 64, + Speed: 43, + Level: 5, + Skills: []*Skill{ + { + Name: "水枪", + Type: TypeWater, + Effects: []Effect{ + &DamageEffect{BasePower: 40}, + }, + PP: 25, + MaxPP: 25, + Accuracy: 100, + }, + { + Name: "水炮", + Type: TypeWater, + Effects: []Effect{ + &DamageEffect{BasePower: 110}, + }, + PP: 5, + MaxPP: 5, + Accuracy: 80, + }, + { + Name: "缩壳", + Type: TypeWater, + Effects: []Effect{ + &StatBoostEffect{Stat: "Defense", Modifier: 2.0, Turns: 4}, + }, + PP: 20, + MaxPP: 20, + Accuracy: 100, + }, + { + Name: "泡沫光线", + Type: TypeWater, + Effects: []Effect{ + &DamageEffect{BasePower: 65}, + &StatDebuffEffect{Stat: "Speed", Modifier: 1.5, Turns: 3}, + }, + PP: 20, + MaxPP: 20, + Accuracy: 100, + }, + }, + } +} + +// 创建小火龙 +func createCharmander() *Pokemon { + return &Pokemon{ + ID: "小火龙", + Type: TypeFire, + HP: 100, + MaxHP: 100, + Attack: 52, + Defense: 43, + SpAttack: 60, + SpDefense: 50, + Speed: 65, + Level: 5, + Skills: []*Skill{ + { + Name: "火苗", + Type: TypeFire, + Effects: []Effect{ + &DamageEffect{BasePower: 40}, + &StatusEffect{Name: "烧伤", Damage: 5, Turns: 3, CanAct: true}, + }, + PP: 25, + MaxPP: 25, + Accuracy: 100, + }, + { + Name: "火焰牙", + Type: TypeFire, + Effects: []Effect{ + &DamageEffect{BasePower: 65}, + }, + PP: 15, + MaxPP: 15, + Accuracy: 95, + }, + { + Name: "龙息", + Type: TypeFire, + Effects: []Effect{ + &DamageEffect{BasePower: 60}, + }, + PP: 20, + MaxPP: 20, + Accuracy: 100, + }, + { + Name: "瞪眼", + Type: TypeFire, + Effects: []Effect{ + &StatDebuffEffect{Stat: "Defense", Modifier: 1.5, Turns: 3}, + }, + PP: 30, + MaxPP: 30, + Accuracy: 100, + }, + }, + } +} + +// -------------------- 主函数 -------------------- // +func main() { + rand.Seed(time.Now().UnixNano()) + + // 初始化精灵 + pikachu := createPikachu() + bulbasaur := createBulbasaur() + //squirtle := createSquirtle() + charmander := createCharmander() + + // 选择战斗模式 + fmt.Println("请选择战斗模式:") + fmt.Println("1. PVE (玩家 vs AI)") + fmt.Println("2. PVP (玩家 vs 玩家)") + + reader := bufio.NewReader(os.Stdin) + var modeChoice int + + for { + fmt.Print("请输入选择 (1-2): ") + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("输入错误,请重试!") + continue + } + + input = strings.TrimSpace(input) + modeChoice, err = strconv.Atoi(input) + if err != nil || (modeChoice != 1 && modeChoice != 2) { + fmt.Println("无效选择,请重试!") + continue + } + + break + } + + var battle *BattleManager + var player1, player2 Player + + if modeChoice == 1 { // PVE + fmt.Println("\n===== PVE 战斗模式 =====") + battle = NewBattleManager(BattleMode.PVE) + + // 创建玩家和AI + player1 = NewHumanPlayer("玩家", []*Pokemon{pikachu}) + player2 = NewAIPlayer("野生精灵", []*Pokemon{bulbasaur}) + + fmt.Printf("\n你遇到了野生的 %s!\n", bulbasaur.ID) + } else { // PVP + fmt.Println("\n===== PVP 战斗模式 =====") + battle = NewBattleManager(BattleMode.PVP) + + // 创建两个玩家 + player1 = NewHumanPlayer("玩家1", []*Pokemon{pikachu}) + player2 = NewHumanPlayer("玩家2", []*Pokemon{charmander}) + + fmt.Println("\n玩家1 vs 玩家2") + } + + // 注册阶段钩子: + // 回合开始前:检查状态 + battle.On(PreTurn, func(ctx *BattleCtx) { + fmt.Printf("\n[回合开始前] %s (HP: %d/%d) 状态:\n", + ctx.User.ID, ctx.User.HP, ctx.User.MaxHP) + + // 显示状态 + if len(ctx.User.Statuses) == 0 { + fmt.Println(" 无异常状态") + } else { + for _, status := range ctx.User.Statuses { + fmt.Printf(" %s (剩余回合: %d)\n", status.Name, status.Turns) + } + } + + // 显示增益/减益 + if len(ctx.User.Buffs) > 0 { + fmt.Println(" 增益/减益:") + for _, buff := range ctx.User.Buffs { + fmt.Printf(" %s (剩余回合: %d)\n", buff.Name, buff.Turns) + } + } + }) + + // 回合结束后:处理持续状态 + 死亡判定 + battle.On(PostTurn, func(ctx *BattleCtx) { + // 1. 处理持续状态 + for i := len(ctx.User.Statuses) - 1; i >= 0; i-- { + status := ctx.User.Statuses[i] + + // 执行状态回合结束效果 + if status.OnTurnEnd != nil { + status.OnTurnEnd(ctx.User) + } + + // 减少持续回合 + status.Turns-- + if status.Turns <= 0 { + ctx.User.Statuses = append(ctx.User.Statuses[:i], ctx.User.Statuses[i+1:]...) + fmt.Printf(" → %s 的 %s 状态已解除!\n", ctx.User.ID, status.Name) + } + } + + // 2. 处理增益/减益回合 + for i := len(ctx.User.Buffs) - 1; i >= 0; i-- { + buff := ctx.User.Buffs[i] + buff.Turns-- + if buff.Turns <= 0 { + ctx.User.Buffs = append(ctx.User.Buffs[:i], ctx.User.Buffs[i+1:]...) + fmt.Printf(" → %s 的 %s 效果已结束!\n", ctx.User.ID, buff.Name) + } + } + + // 3. 死亡判定 + if ctx.User.HP <= 0 { + ctx.User.HP = 0 // 确保HP不小于0 + fmt.Printf(" → %s 被击败!\n", ctx.User.ID) + + // 检查是否所有精灵都已被击败 + if len(getAlivePokemons(ctx.Attacker.GetPokemons())) == 0 { + fmt.Printf("[%s] 所有精灵都已被击败,%s 获胜!\n", + ctx.Attacker.GetID(), ctx.Defender.GetID()) + battle.isOver = true + } else { + // 在实际游戏中,这里应该允许玩家更换精灵 + fmt.Println(" → 需要更换精灵(当前未实现此功能)") + } + } + }) + + // 启动战斗 + fmt.Println("\n===== 战斗开始 =====") + + // 决定先手顺序 + var first, second Player + if player1.GetActivePokemon().Speed >= player2.GetActivePokemon().Speed { + first = player1 + second = player2 + } else { + first = player2 + second = player1 + } + + fmt.Printf("\n%s 的 %s 速度更快,率先行动!\n", + first.GetID(), first.GetActivePokemon().ID) + + // 回合循环 + turn := 1 + for !battle.isOver { + fmt.Printf("\n===== 第 %d 回合 =====", turn) + fmt.Println("\n-------------------") + + // 先手行动 + battle.RunTurn(first, second) + if battle.isOver { + break + } + + // 后手行动 + battle.RunTurn(second, first) + + turn++ + } + + fmt.Println("\n===== 战斗结束 =====") +} diff --git a/logic/service/fight/mode.go b/logic/service/fight/mode.go new file mode 100644 index 000000000..bb95cc0b5 --- /dev/null +++ b/logic/service/fight/mode.go @@ -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 玩家 +}]()