Files
bl/logic/service/fight/info/BattleSkillEntity.go

282 lines
8.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package info
import (
element "blazing/common/data/Element"
"blazing/common/data/xml/skill"
"blazing/common/utils/random"
"context"
"fmt"
"strconv"
"strings"
"github.com/gogf/gf/v2/os/glog"
"github.com/shopspring/decimal"
"github.com/tnnmigga/enum"
)
const BattleSkillEntityCtx = "skill"
// EnumSkillType 技能类型枚举基础类型
type EnumCategory int
// SkillType 技能类型枚举实例使用enum包创建
// 与原Java枚举保持相同的数值映射PHYSICAL=1, SPECIAL=2, STATUS=4
var Category = enum.New[struct {
PHYSICAL EnumCategory `enum:"1"` // 物理攻击
SPECIAL EnumCategory `enum:"2"` // 特殊攻击
STATUS EnumCategory `enum:"4"` // 状态技能
}]()
// BattleSkillEntity 战斗技能实体
// 实现了战斗中技能的所有属性和行为包括PP管理、技能使用、属性获取等
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
type BattleSkillEntity struct {
skill.Move
ctx context.Context
SideEffects []int
SideEffectArgs []int
PP int
InfinityPP bool
DamageMultiplierZone map[DamageMultiplierZone]float64
isCritical bool //技能是否暴击
// 技能类型属性
//SkillType EnumCategory // 技能类型(物理/特殊/状态)
}
// CreateBattleSkillWithInfinity 创建战斗技能实例可指定是否无限PP
func CreateBattleSkillWithInfinity(id int, pp int) *BattleSkillEntity {
//如果PP是-1 ,那就是无限PP
// ID小于10001的视为无效技能
if id < 10001 {
return nil
}
// 从资源仓库获取技能数据
move, ok := skill.MovesConfig.Moves[id]
if !ok {
glog.Error(context.Background(), "技能ID无效", "id", id)
}
var ret BattleSkillEntity
// 解析副作用参数
sideEffectArgs := parseSideEffectArgs(move.SideEffectArg)
tt := strings.Split(move.SideEffect, " ")
rf, err := strSliceToIntSlice(tt)
if err == nil {
ret.SideEffects = rf
}
ret.SideEffectArgs = sideEffectArgs
ret.DamageMultiplierZone = make(map[DamageMultiplierZone]float64)
return &ret
}
// 将字符串切片转换为整数切片
func strSliceToIntSlice(strs []string) ([]int, error) {
intSlice := make([]int, 0, len(strs)) // 预分配容量,提高性能
for _, s := range strs {
// 将字符串转换为整数
num, err := strconv.Atoi(s)
if err != nil {
// 转换失败时返回错误(包含具体的错误信息和失败的字符串)
return nil, fmt.Errorf("无法将字符串 '%s' 转换为整数: %v", s, err)
}
intSlice = append(intSlice, num)
}
return intSlice, nil
}
// CanUse 检查技能是否可以使用PP是否充足
func (s *BattleSkillEntity) CanUse() bool {
return s.PP > 0
}
// 获取技能类型
func (s *BattleSkillEntity) Category() EnumCategory {
return EnumCategory(s.Move.Category)
}
// 获取技能属性
func (s *BattleSkillEntity) Type() element.ElementType {
return element.ElementType(s.Move.Type)
}
func (u *BattleSkillEntity) NewBattleAction(ctx context.Context) {
ret := BattleAction{}
ctx = context.WithValue(ctx, BattleSkillEntityCtx, &ret) //添加用户到上下文
ret.ctx = ctx
}
// 解析副作用参数字符串为整数列表
func parseSideEffectArgs(argsStr string) []int {
if argsStr == "" {
return []int{}
}
parts := strings.Fields(argsStr)
args := make([]int, 0, len(parts))
for _, part := range parts {
if num, err := strconv.Atoi(part); err == nil {
args = append(args, num)
}
}
return args
}
// 获取技能名称为空时使用ID
func getSkillName(move *BattleSkillEntity) string {
if move.Name == "" {
return strconv.FormatInt(int64(move.ID), 10)
}
return move.Name
}
// 获取副作用列表,处理空值情况
func getSideEffects(move *BattleSkillEntity) []int {
if move.SideEffect == "" {
return []int{}
}
return move.SideEffects
}
// DamageMultiplierZone 伤害乘算区枚举使用enum包定义
type DamageMultiplierZone int
var DamageMultiplierZoneEnum = enum.New[struct {
POWER_ADD DamageMultiplierZone // 威力加算区,直接加成威力值
POWER_MUL DamageMultiplierZone // 威力乘算区,倍率调整(如威力倍数)
SPECIAL_EFFECT_MUL DamageMultiplierZone //殊效果乘算区, 例如Boss伤害减免或哈莫雷特的非对应顺序技能攻击为0, 此系数默认为1
ATK_RESISTANCE DamageMultiplierZone //攻击伤害减免系数乘区, 默认为1, 减伤50%等效于将这个系数设置为0.5, 同种类取最高
SP_ATK_RESISTANCE DamageMultiplierZone //特殊攻击伤害减免系数乘区, 默认为1, 减伤50%等效于将这个系数设置为0.5, 同种类取最高
ATTACK_COUNT_ZONE DamageMultiplierZone //攻击次数
DEFENSE_ZONE DamageMultiplierZone //固定伤害减免
SP_DEFENSE_ZONE DamageMultiplierZone //物伤减免
ATK_DEFENSE_ZONE DamageMultiplierZone //特伤减免
Critical_ZONE DamageMultiplierZone //暴击乘区
}]()
func (s *BattleSkillEntity) Random() *random.RandomXS128 {
battle, _ := s.ctx.Value(BattleContainerCtx).(*BattleContainer1V1)
return battle.Rand
}
func (s *BattleSkillEntity) Pet() (*BattlePetEntity, bool) {
pet, ok := s.ctx.Value(BattlePetEntityCtx).(*BattlePetEntity)
return pet, ok
}
// 暴击伤害 返回暴击率和是否暴击
func (s *BattleSkillEntity) CriticalRate() decimal.Decimal {
return decimal.NewFromFloat(2)
}
// 计算技能威力
func (s *BattleSkillEntity) CalculatePower(p *BattlePetEntity) int64 {
//这里应该算上威力区
// 初始化随机值范围217~255
randomnum := s.Random().NextLongN(39) + 217
pet, _ := s.Pet()
// 1. 计算等级因子 (level * 0.4 + 2)
levelFactor := decimal.NewFromInt(int64(pet.Level)).
Mul(decimal.NewFromFloat(0.4)).Add(decimal.NewFromInt(2))
// 2. 计算威力因子 (基础威力 + 加算) * 乘算
powerAdd := decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.POWER_ADD]) //威力加算区
powerMul := decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.POWER_MUL]) //威力乘算区
powerZone := decimal.NewFromInt(int64(s.Power)).Add(powerAdd).Mul(powerMul)
var (
attackDec decimal.Decimal //攻击值
defenseDec decimal.Decimal //防御值
damageReduction decimal.Decimal //伤害百分比减免
)
switch s.Category() { //判断技能类型
case Category.PHYSICAL:
attackDec = decimal.NewFromInt(int64(pet.UnitAttributes[AttrType.Attack].Value()))
defenseDec = decimal.NewFromInt(int64(p.UnitAttributes[AttrType.Defense].Value()))
damageReduction = decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.ATK_RESISTANCE])
case Category.SPECIAL:
attackDec = decimal.NewFromInt(int64(pet.UnitAttributes[AttrType.Attack].Value()))
defenseDec = decimal.NewFromInt(int64(p.UnitAttributes[AttrType.Speed].Value()))
damageReduction = decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.SP_ATK_RESISTANCE])
}
//攻击次数结算
attackCount := decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.ATTACK_COUNT_ZONE])
// 5. 基础伤害公式:等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2然后乘以攻击次数
baseDamage := levelFactor.
Mul(powerZone).
Mul(attackDec).
Div(defenseDec).
Div(decimal.NewFromInt(50)).
Add(decimal.NewFromInt(2)).
Mul(attackCount)
// 6. 同系加成属性相同则乘以同系加成倍率否则1
sameTypeBonus := decimal.NewFromFloat(1.0)
if s.Type() == pet.Type() {
sameTypeBonus = decimal.NewFromFloat(1.5)
}
t, _ := element.NewElementCalculator().GetOffensiveMultiplier(int(pet.Type()), int(pet.Type()))
typeRate := decimal.NewFromFloat(t)
// 8. 暴击倍率暴击时使用暴击倍率否则1
criticalRate := decimal.NewFromFloat(1.0)
if s.isCritical {
criticalRate = s.CriticalRate()
}
// 9. 技能特殊效果倍率
specialEffect := decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.SPECIAL_EFFECT_MUL])
// 10. 随机倍率随机值除以255
randomFactor := decimal.NewFromInt(int64(randomnum)).Div(decimal.NewFromInt(255))
// 11. 计算总伤害
damage := baseDamage.
Mul(sameTypeBonus). // 同属性加成
Mul(typeRate). // 克制系数
Mul(criticalRate). //暴击系数
Mul(damageReduction). //减伤计算
Mul(specialEffect). //特殊效果
Mul(randomFactor) //随机波动
// 12. 存储真实伤害到额外倍率,方便后续查询
//context.PutExtraRate("REAL_DAMAGE", damage)
// 13. 应用固定伤害减免伤害不能低于0
fixReduction := decimal.NewFromFloat(s.DamageMultiplierZone[DamageMultiplierZoneEnum.DEFENSE_ZONE])
if fixReduction.GreaterThan(decimal.Zero) {
damage = damage.Sub(fixReduction)
if damage.LessThan(decimal.Zero) {
damage = decimal.Zero
}
}
// 返回最终伤害(整数部分)
return damage.IntPart()
}