refactor(fight): 重构战斗系统并添加技能限制效果
- 重构了战斗单位、技能、Effect等基础数据结构 - 新增技能类型和技能限制Effect - 优化了战斗流程和状态机逻辑 - 调整了伤害计算方式和Buff处理机制
This commit is contained in:
@@ -7,19 +7,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ===== 战斗单位 =====
|
||||
type BattleUnit struct {
|
||||
Name string
|
||||
Level int
|
||||
Atk int
|
||||
Def int
|
||||
MaxHP int
|
||||
HP int
|
||||
Buffs map[string]Effect
|
||||
IsCritical bool
|
||||
// ==== 基础数据结构 ====
|
||||
|
||||
// 阻止增益buff标记
|
||||
PreventBuff bool
|
||||
type BattleUnit struct {
|
||||
Name string
|
||||
Level int
|
||||
Atk int
|
||||
Def int
|
||||
MaxHP int
|
||||
HP int
|
||||
Buffs map[string]Effect
|
||||
|
||||
IsCritical bool
|
||||
}
|
||||
|
||||
func NewBattleUnit(name string, level, atk, def, maxHP int) *BattleUnit {
|
||||
@@ -34,158 +33,46 @@ func NewBattleUnit(name string, level, atk, def, maxHP int) *BattleUnit {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 事件接口及类型 =====
|
||||
// ==== 技能类型 ====
|
||||
|
||||
type BattleEvent interface {
|
||||
Type() string
|
||||
}
|
||||
const (
|
||||
SkillTypePhysical SkillType = iota + 1
|
||||
SkillTypeSpecial
|
||||
SkillTypeGrass
|
||||
SkillTypeWater
|
||||
SkillTypeFire
|
||||
)
|
||||
|
||||
type SkillUseEvent struct {
|
||||
Source *BattleUnit
|
||||
Target *BattleUnit
|
||||
IsAttack bool // 是否攻击技能
|
||||
}
|
||||
// ==== 战斗上下文 ====
|
||||
|
||||
func (e SkillUseEvent) Type() string { return "SkillUse" }
|
||||
|
||||
// ===== 战斗上下文 =====
|
||||
type BattleContext struct {
|
||||
Rand *rand.Rand
|
||||
Events []BattleEvent
|
||||
Rand *rand.Rand
|
||||
}
|
||||
|
||||
// ===== Effect 接口 =====
|
||||
// ==== 技能 ====
|
||||
|
||||
type Skill struct {
|
||||
Name string
|
||||
Type SkillType
|
||||
Power int
|
||||
IsAttack bool
|
||||
Attacker *BattleUnit
|
||||
Defender *BattleUnit
|
||||
Effects []Effect
|
||||
}
|
||||
|
||||
// ==== Effect接口 ====
|
||||
|
||||
type Effect interface {
|
||||
ID() string
|
||||
Apply(attacker, defender *BattleUnit, ctx *BattleContext)
|
||||
|
||||
BeforeApplyBuff(attacker, defender *BattleUnit, skill *Skill, damageBuff *int, ctx *BattleContext)
|
||||
AfterApplyBuff(skill *Skill, owner, target *BattleUnit, ownerDamage *int, targetDamage *int) error
|
||||
Apply(owner, target *BattleUnit, ctx *BattleContext)
|
||||
Next() bool
|
||||
}
|
||||
|
||||
// ===== Effect实现 =====
|
||||
|
||||
// 1. n回合内每回合恢复自身已损失体力的50%
|
||||
type RecoverLostHPEffect struct {
|
||||
id string
|
||||
Duration int
|
||||
}
|
||||
|
||||
func (e *RecoverLostHPEffect) ID() string { return e.id }
|
||||
|
||||
func (e *RecoverLostHPEffect) Apply(attacker, defender *BattleUnit, ctx *BattleContext) {
|
||||
if e.Duration <= 0 {
|
||||
return
|
||||
}
|
||||
lost := attacker.MaxHP - attacker.HP
|
||||
heal := lost / 2
|
||||
if heal < 1 {
|
||||
heal = 1
|
||||
}
|
||||
attacker.HP += heal
|
||||
if attacker.HP > attacker.MaxHP {
|
||||
attacker.HP = attacker.MaxHP
|
||||
}
|
||||
fmt.Printf("[%s] 回合回复 %d 点已损失体力 (HP=%d)\n", attacker.Name, heal, attacker.HP)
|
||||
}
|
||||
|
||||
func (e *RecoverLostHPEffect) Next() bool {
|
||||
e.Duration--
|
||||
return e.Duration > 0
|
||||
}
|
||||
|
||||
// 2. 自身不处于能力强化状态时则YY能力变化(示例:攻击力+50)
|
||||
type ConditionalBuffEffect struct {
|
||||
id string
|
||||
Duration int
|
||||
BuffState string // 需要检测的强化状态ID
|
||||
Applied bool
|
||||
ChangeFunc func(unit *BattleUnit)
|
||||
UndoFunc func(unit *BattleUnit)
|
||||
}
|
||||
|
||||
func (e *ConditionalBuffEffect) ID() string { return e.id }
|
||||
|
||||
func (e *ConditionalBuffEffect) Apply(attacker, defender *BattleUnit, ctx *BattleContext) {
|
||||
hasBuff := false
|
||||
if _, ok := attacker.Buffs[e.BuffState]; ok {
|
||||
hasBuff = true
|
||||
}
|
||||
if !hasBuff && !e.Applied {
|
||||
if e.ChangeFunc != nil {
|
||||
e.ChangeFunc(attacker)
|
||||
}
|
||||
e.Applied = true
|
||||
fmt.Printf("[%s] 未处于强化状态,触发能力变化\n", attacker.Name)
|
||||
} else if hasBuff && e.Applied {
|
||||
if e.UndoFunc != nil {
|
||||
e.UndoFunc(attacker)
|
||||
}
|
||||
e.Applied = false
|
||||
fmt.Printf("[%s] 处于强化状态,撤销能力变化\n", attacker.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ConditionalBuffEffect) Next() bool {
|
||||
e.Duration--
|
||||
if e.Duration <= 0 && e.Applied && e.UndoFunc != nil {
|
||||
e.UndoFunc(nil) // 这里传nil简化逻辑,正常应传unit
|
||||
}
|
||||
return e.Duration > 0
|
||||
}
|
||||
|
||||
// 3. n回合内若对手使用攻击技能降低对手最大体力的1/m
|
||||
type ReduceMaxHPOnAttackEffect struct {
|
||||
id string
|
||||
Duration int
|
||||
M int
|
||||
}
|
||||
|
||||
func (e *ReduceMaxHPOnAttackEffect) ID() string { return e.id }
|
||||
|
||||
func (e *ReduceMaxHPOnAttackEffect) Apply(attacker, defender *BattleUnit, ctx *BattleContext) {
|
||||
if e.Duration <= 0 {
|
||||
return
|
||||
}
|
||||
for _, ev := range ctx.Events {
|
||||
if ev.Type() == "SkillUse" {
|
||||
su := ev.(SkillUseEvent)
|
||||
if su.Source == defender && su.IsAttack {
|
||||
reduce := defender.MaxHP / e.M
|
||||
defender.MaxHP -= reduce
|
||||
if defender.HP > defender.MaxHP {
|
||||
defender.HP = defender.MaxHP
|
||||
}
|
||||
fmt.Printf("[%s] 使用攻击技能,被降低最大体力 %d (MaxHP=%d)\n", defender.Name, reduce, defender.MaxHP)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ReduceMaxHPOnAttackEffect) Next() bool {
|
||||
e.Duration--
|
||||
return e.Duration > 0
|
||||
}
|
||||
|
||||
// 4. n回合对手无法使自身能力出现提升状态
|
||||
type PreventEnemyBuffEffect struct {
|
||||
id string
|
||||
Duration int
|
||||
}
|
||||
|
||||
func (e *PreventEnemyBuffEffect) ID() string { return e.id }
|
||||
|
||||
func (e *PreventEnemyBuffEffect) Apply(attacker, defender *BattleUnit, ctx *BattleContext) {
|
||||
// 标记对手PreventBuff
|
||||
defender.PreventBuff = true
|
||||
fmt.Printf("[%s] 使对手 [%s] 无法获得能力提升状态\n", attacker.Name, defender.Name)
|
||||
}
|
||||
|
||||
func (e *PreventEnemyBuffEffect) Next() bool {
|
||||
e.Duration--
|
||||
return e.Duration > 0
|
||||
}
|
||||
|
||||
// ===== 伤害计算函数 =====
|
||||
// ==== 伤害计算 ====
|
||||
|
||||
func CalculateDamage(attacker, defender *BattleUnit, power int, isCritical bool, r *rand.Rand) int {
|
||||
randomFactor := float64(r.Intn(39)+217) / 255.0
|
||||
@@ -204,33 +91,143 @@ func CalculateDamage(attacker, defender *BattleUnit, power int, isCritical bool,
|
||||
return damage
|
||||
}
|
||||
|
||||
// ===== 技能 =====
|
||||
// ==== 具体 Effect 实现 ====
|
||||
|
||||
type Skill struct {
|
||||
Name string
|
||||
Effects []Effect
|
||||
IsAttack bool // 标记是否攻击技能
|
||||
// 1. 普通伤害Effect(一次性或持续)
|
||||
|
||||
type DamageEffect struct {
|
||||
id string
|
||||
power int
|
||||
duration int
|
||||
}
|
||||
|
||||
func (s *Skill) Cast(attacker, defender *BattleUnit, ctx *BattleContext) {
|
||||
fmt.Printf("\n>>> %s 使用技能 [%s] <<<\n", attacker.Name, s.Name)
|
||||
func (d *DamageEffect) ID() string { return d.id }
|
||||
|
||||
// 记录技能使用事件,供Effect消费
|
||||
ctx.Events = append(ctx.Events, SkillUseEvent{
|
||||
Source: attacker,
|
||||
Target: defender,
|
||||
IsAttack: s.IsAttack,
|
||||
})
|
||||
func (d *DamageEffect) BeforeApplyBuff(attacker, defender *BattleUnit, skill *Skill, damageBuff *int, ctx *BattleContext) {
|
||||
// 不做修改,示例留空
|
||||
}
|
||||
|
||||
for _, effect := range s.Effects {
|
||||
effect.Apply(attacker, defender, ctx)
|
||||
if defender.HP <= 0 {
|
||||
break
|
||||
func (d *DamageEffect) AfterApplyBuff(skill *Skill, owner, target *BattleUnit, ownerDamage *int, targetDamage *int) error {
|
||||
// 持续回合伤害:每回合额外加伤害
|
||||
if d.duration > 0 {
|
||||
*targetDamage += d.power
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DamageEffect) Apply(owner, target *BattleUnit, ctx *BattleContext) {
|
||||
if d.duration > 0 {
|
||||
target.HP -= d.power
|
||||
if target.HP < 0 {
|
||||
target.HP = 0
|
||||
}
|
||||
fmt.Printf("[%s] 持续伤害 %d 点,目标 %s 剩余HP %d\n", owner.Name, d.power, target.Name, target.HP)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DamageEffect) Next() bool {
|
||||
d.duration--
|
||||
return d.duration > 0
|
||||
}
|
||||
|
||||
// 2. Boss限制技能类型Effect (参考Map60Boss0)
|
||||
|
||||
type SkillLimitEffect struct {
|
||||
id string
|
||||
duration int
|
||||
allowedSkillType SkillType
|
||||
}
|
||||
|
||||
func NewSkillLimitEffect(duration int) *SkillLimitEffect {
|
||||
return &SkillLimitEffect{
|
||||
id: "SkillLimitEffect",
|
||||
duration: duration,
|
||||
allowedSkillType: SkillTypeGrass, // 从草技能开始循环
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SkillLimitEffect) ID() string { return s.id }
|
||||
|
||||
func (s *SkillLimitEffect) BeforeApplyBuff(attacker, defender *BattleUnit, skill *Skill, damageBuff *int, ctx *BattleContext) {
|
||||
// 不处理
|
||||
}
|
||||
|
||||
func (s *SkillLimitEffect) AfterApplyBuff(skill *Skill, owner, target *BattleUnit, ownerDamage *int, targetDamage *int) error {
|
||||
if !skill.IsAttack {
|
||||
return nil
|
||||
}
|
||||
|
||||
if skill.Type != s.allowedSkillType {
|
||||
*targetDamage = 0
|
||||
fmt.Printf("[SkillLimitEffect] 技能类型不符,伤害清零。允许类型: %d,本次类型: %d\n", s.allowedSkillType, skill.Type)
|
||||
} else {
|
||||
// 循环切换允许技能类型
|
||||
s.allowedSkillType++
|
||||
if s.allowedSkillType > SkillTypeFire {
|
||||
s.allowedSkillType = SkillTypeGrass
|
||||
}
|
||||
fmt.Printf("[SkillLimitEffect] 技能类型符合,允许类型切换为 %d\n", s.allowedSkillType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SkillLimitEffect) Apply(owner, target *BattleUnit, ctx *BattleContext) {
|
||||
// 可做每回合特效,示例空实现
|
||||
}
|
||||
|
||||
func (s *SkillLimitEffect) Next() bool {
|
||||
s.duration--
|
||||
return s.duration > 0
|
||||
}
|
||||
|
||||
// ==== Buff管理器 ====
|
||||
|
||||
func ProcessBuffs(unit *BattleUnit, target *BattleUnit, ctx *BattleContext) {
|
||||
for id, buff := range unit.Buffs {
|
||||
buff.Apply(unit, target, ctx)
|
||||
if !buff.Next() {
|
||||
fmt.Printf("[%s] 的 Buff [%s] 已结束\n", unit.Name, id)
|
||||
delete(unit.Buffs, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 战斗状态机 =====
|
||||
// ==== 技能释放 ====
|
||||
|
||||
func (s *Skill) Cast(ctx *BattleContext) {
|
||||
fmt.Printf("\n%s 使用技能 [%s]\n", s.Attacker.Name, s.Name)
|
||||
|
||||
ownerDamage := CalculateDamage(s.Attacker, s.Defender, s.Power, s.Attacker.IsCritical, ctx.Rand)
|
||||
targetDamage := ownerDamage
|
||||
|
||||
// 技能中各效果触发BeforeApplyBuff(影响伤害前)
|
||||
for _, e := range s.Effects {
|
||||
e.BeforeApplyBuff(s.Attacker, s.Defender, s, &ownerDamage, ctx)
|
||||
}
|
||||
|
||||
// Buff触发AfterApplyBuff,允许调整伤害
|
||||
for _, buff := range s.Defender.Buffs {
|
||||
buff.AfterApplyBuff(s, s.Defender, s.Attacker, &targetDamage, &targetDamage)
|
||||
}
|
||||
|
||||
// 应用伤害
|
||||
if targetDamage > 0 {
|
||||
s.Defender.HP -= targetDamage
|
||||
if s.Defender.HP < 0 {
|
||||
s.Defender.HP = 0
|
||||
}
|
||||
fmt.Printf("%s 对 %s 造成 %d 伤害,剩余HP %d\n", s.Attacker.Name, s.Defender.Name, targetDamage, s.Defender.HP)
|
||||
} else {
|
||||
fmt.Printf("%s 的攻击被完全抵消!\n", s.Attacker.Name)
|
||||
}
|
||||
|
||||
// 技能自身Effects持续效果Apply
|
||||
for _, e := range s.Effects {
|
||||
e.Apply(s.Attacker, s.Defender, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== 战斗状态机 ====
|
||||
|
||||
type BattleState int
|
||||
|
||||
@@ -256,113 +253,98 @@ func (fsm *BattleStateMachine) Next() {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Buff处理 =====
|
||||
// ==== 战斗主流程示例 ====
|
||||
|
||||
func AddBuff(unit *BattleUnit, buff Effect) {
|
||||
// 检查PreventBuff标志,阻止增益buff
|
||||
if unit.PreventBuff {
|
||||
fmt.Printf("[%s] 被阻止获得buff [%s]\n", unit.Name, buff.ID())
|
||||
return
|
||||
}
|
||||
unit.Buffs[buff.ID()] = buff
|
||||
fmt.Printf("[%s] 获得buff [%s]\n", unit.Name, buff.ID())
|
||||
}
|
||||
|
||||
func ProcessBuffs(unit *BattleUnit, target *BattleUnit, ctx *BattleContext) {
|
||||
for id, buff := range unit.Buffs {
|
||||
buff.Apply(unit, target, ctx)
|
||||
if !buff.Next() {
|
||||
fmt.Printf("[%s] 的buff [%s] 结束\n", unit.Name, id)
|
||||
delete(unit.Buffs, id)
|
||||
}
|
||||
}
|
||||
// 重置阻止buff标记,每回合重新计算
|
||||
unit.PreventBuff = false
|
||||
}
|
||||
|
||||
// ===== 测试战斗流程 =====
|
||||
|
||||
func TestBattleSystem(t *testing.T) {
|
||||
func Test_main(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
player := NewBattleUnit("雷伊", 100, 300, 200, 300)
|
||||
enemy := NewBattleUnit("盖亚", 100, 280, 220, 300)
|
||||
player := NewBattleUnit("雷伊", 100, 300, 200, 5000)
|
||||
enemy := NewBattleUnit("Map60Boss", 100, 280, 220, 4800)
|
||||
|
||||
ctx := &BattleContext{Rand: r}
|
||||
return
|
||||
// 玩家技能:普通攻击 + 附加恢复buff(持续3回合)
|
||||
playerSkill := &Skill{
|
||||
Name: "雷光闪",
|
||||
Effects: []Effect{
|
||||
&RecoverLostHPEffect{id: "recover1", Duration: 3},
|
||||
},
|
||||
|
||||
// 给Boss添加技能限制buff(持续5回合)
|
||||
enemy.Buffs["SkillLimit"] = NewSkillLimitEffect(5)
|
||||
|
||||
// 定义技能
|
||||
grassSkill := &Skill{
|
||||
Name: "草之击",
|
||||
Type: SkillTypeGrass,
|
||||
Power: 100,
|
||||
IsAttack: true,
|
||||
}
|
||||
|
||||
// 敌人技能:攻击并附加降低最大HP效果(持续2回合)
|
||||
enemySkill := &Skill{
|
||||
Name: "岩石猛击",
|
||||
Attacker: player,
|
||||
Defender: enemy,
|
||||
Effects: []Effect{
|
||||
&ReduceMaxHPOnAttackEffect{id: "reduce_maxhp1", Duration: 2, M: 5},
|
||||
&DamageEffect{id: "damage1", power: 200, duration: 0},
|
||||
},
|
||||
}
|
||||
|
||||
waterSkill := &Skill{
|
||||
Name: "水之击",
|
||||
Type: SkillTypeWater,
|
||||
Power: 120,
|
||||
IsAttack: true,
|
||||
}
|
||||
|
||||
// 玩家添加阻止敌人增益buffbuff(持续2回合)
|
||||
preventBuff := &PreventEnemyBuffEffect{id: "prevent_buff1", Duration: 2}
|
||||
AddBuff(player, preventBuff)
|
||||
|
||||
// 玩家添加条件能力变化buff (攻击力+50),不处于强化状态时生效
|
||||
condBuff := &ConditionalBuffEffect{
|
||||
id: "cond_buff1",
|
||||
Duration: 5,
|
||||
BuffState: "attack_up",
|
||||
ChangeFunc: func(unit *BattleUnit) {
|
||||
if unit != nil {
|
||||
unit.Atk += 50
|
||||
}
|
||||
},
|
||||
UndoFunc: func(unit *BattleUnit) {
|
||||
if unit != nil {
|
||||
unit.Atk -= 50
|
||||
}
|
||||
Attacker: player,
|
||||
Defender: enemy,
|
||||
Effects: []Effect{
|
||||
&DamageEffect{id: "damage2", power: 100, duration: 0},
|
||||
},
|
||||
}
|
||||
AddBuff(player, condBuff)
|
||||
|
||||
fsm := &BattleStateMachine{State: StateStart}
|
||||
round := 1
|
||||
|
||||
for player.HP > 0 && enemy.HP > 0 {
|
||||
for player.HP > 0 && enemy.HP > 0 && round <= 10 {
|
||||
fmt.Printf("\n===== 回合 %d =====\n", round)
|
||||
ctx.Events = nil // 清空事件队列,准备本回合事件
|
||||
|
||||
switch fsm.State {
|
||||
case StateStart:
|
||||
fmt.Println("战斗开始!")
|
||||
fsm.Next()
|
||||
|
||||
case StatePlayerTurn:
|
||||
playerSkill.Cast(player, enemy, ctx)
|
||||
// 玩家随机选择草或水技能攻击Boss
|
||||
if round%2 == 1 {
|
||||
grassSkill.Attacker = player
|
||||
grassSkill.Defender = enemy
|
||||
grassSkill.Cast(ctx)
|
||||
} else {
|
||||
waterSkill.Attacker = player
|
||||
waterSkill.Defender = enemy
|
||||
waterSkill.Cast(ctx)
|
||||
}
|
||||
|
||||
ProcessBuffs(player, enemy, ctx)
|
||||
ProcessBuffs(enemy, player, ctx)
|
||||
|
||||
fsm.Next()
|
||||
|
||||
case StateEnemyTurn:
|
||||
enemySkill.Cast(enemy, player, ctx)
|
||||
// Boss普通攻击玩家
|
||||
bossSkill := &Skill{
|
||||
Name: "Boss普通攻击",
|
||||
Type: SkillTypePhysical,
|
||||
Power: 90,
|
||||
IsAttack: true,
|
||||
Attacker: enemy,
|
||||
Defender: player,
|
||||
Effects: []Effect{&DamageEffect{id: "bossHit", power: 150, duration: 0}},
|
||||
}
|
||||
bossSkill.Cast(ctx)
|
||||
|
||||
ProcessBuffs(player, enemy, ctx)
|
||||
ProcessBuffs(enemy, player, ctx)
|
||||
|
||||
fsm.Next()
|
||||
}
|
||||
|
||||
if player.HP <= 0 || enemy.HP <= 0 {
|
||||
if player.HP == 0 {
|
||||
fmt.Println("Boss胜利!")
|
||||
break
|
||||
} else if enemy.HP == 0 {
|
||||
fmt.Println("玩家胜利!")
|
||||
break
|
||||
}
|
||||
|
||||
round++
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 战斗结束 ===")
|
||||
if player.HP > 0 {
|
||||
fmt.Println("玩家胜利!")
|
||||
} else {
|
||||
fmt.Println("敌人胜利!")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user