ecs/servers/game/logic/player_battle_calculator.go
2025-07-16 17:34:36 +08:00

519 lines
14 KiB
Go

package logic
import (
"ecs/proto/pb"
"github.com/oylshe1314/framework/util"
"math/rand/v2"
"sort"
)
type _BattleCalculator struct {
rd *rand.Rand
me *_BattleRole
he *_BattleRole
}
// 战斗
func (this *_BattleCalculator) calcBattle(rounds int) (succeed bool, roundList []*pb.BattleRound) {
var roles = [2]*_BattleRole{this.me, this.he}
var ri = 0
if roles[1].capacity > roles[0].capacity {
ri = 1
}
var dead bool
var index = [2]int{-1, -1}
var caster, aCaster, bCaster *_BattleUnit
for i, unit := range roles[ri].unitList {
if unit != nil {
unit.speed = unit.baseSpeed + unit.posSpeed + unit.amuletSpeed + unit.harnessSpeed
if aCaster == nil || unit.speed > aCaster.speed {
aCaster = unit
index[ri] = i
}
}
}
caster = aCaster
for round := range rounds {
var battleRound = &pb.BattleRound{BattleRound: uint32(round) + 1}
dead = false
for {
var ai, bi = ri, (ri + 1) % 2
var battleAction = &pb.BattleAction{}
battleRound.ActionList = append(battleRound.ActionList, battleAction)
battleAction.Caster, battleAction.SkillId, battleAction.TargetList = this.calcSkill(battleRound.BattleRound, caster, roles[ai], roles[bi])
dead, index[bi], bCaster = this.calcNextCaster(index[bi], roles[bi].unitList)
if dead {
succeed = bi != 0
break
}
dead, index[ai], aCaster = this.calcNextCaster(index[ai], roles[ai].unitList)
if dead {
succeed = ai != 0
break
}
if bCaster == nil {
if aCaster == nil {
index[ai] = -1
index[bi] = -1
break
} else {
caster = aCaster
}
} else {
if aCaster == nil {
caster = bCaster
ri = bi
} else {
if aCaster.speed > bCaster.speed {
caster = aCaster
} else {
caster = bCaster
ri = bi
}
}
}
}
roundList = append(roundList, battleRound)
if dead {
break
}
}
return succeed, roundList
}
// 出手
func (tnis *_BattleCalculator) calcNextCaster(lastIndex int, unitList []*_BattleUnit) (dead bool, newIndex int, caster *_BattleUnit) {
dead = true
var posIndex = 0
for i, unit := range unitList {
if i == lastIndex || unit == nil {
continue
}
if unit.hp > 0 {
dead = false
posIndex += 1
unit.posSpeed = uint32(20 - (4 * posIndex))
unit.speed = unit.baseSpeed + unit.posSpeed + unit.amuletSpeed + unit.harnessSpeed
if lastIndex < 0 || unit.speed <= unitList[lastIndex].speed {
if caster == nil || unit.speed > caster.speed {
caster = unit
newIndex = i
}
}
}
}
return
}
// 技能
func (this *_BattleCalculator) calcSkill(battleRound uint32, unit *_BattleUnit, a, b *_BattleRole) (caster *pb.BattleTarget, skillId uint32, targetList []*pb.BattleTarget) {
for _, skill := range unit.attackSkills {
if skill.cooldown > 1 {
if battleRound-skill.releaseRound < skill.cooldown {
continue
}
skill.releaseRound = battleRound
}
if unit.rage < skill.consumeRage {
continue
}
switch skill.skillType {
case skillTypeAttack:
skillId = skill.id
caster, targetList = this.calcBuff(unit, skill, a, b)
unit.rage += 2
case skillTypeActive:
skillId = skill.id
unit.rage -= skill.consumeRage
caster, targetList = this.calcBuff(unit, skill, a, b)
default:
continue
}
break
}
return
}
// 效果
func (this *_BattleCalculator) calcBuff(unit *_BattleUnit, skill *_BattleSkill, a, b *_BattleRole) (caster *pb.BattleTarget, targetList []*pb.BattleTarget) {
caster = &pb.BattleTarget{Type: uint32(util.If(unit.role.attacker, 1, 2)), Position: unit.position}
caster.HpMax = unit.attrs[pb.AttrType_Hp]
caster.Rage = unit.rage
var targetMap = map[uint32]*pb.BattleTarget{}
var buffGroup int32 = -1
for bi := 0; bi < len(skill.buffs); {
var buff = skill.buffs[bi]
bi += 1
if buff.buffType == buffTypeNone || len(buff.buffArgs) == 0 {
continue
}
if buffGroup < 0 {
buffGroup = buff.group
} else {
if buff.group != 0 && buff.group != buffGroup {
break
}
}
var buffedTargetsMap map[uint32][]*_BattleUnit
switch buff.buffType {
case buffTypePhysicalDamage:
if buff.targetType != targetTypeEnemy {
continue
}
var targets = this.calcAttackTarget(unit, buff, a, b, nil, buffedTargetsMap)
var gainBuffs = this.calcGainBuff(&bi, skill.buffs, targets, buffedTargetsMap)
for _, target := range targets {
var damage = this.calcDamage(buff, gainBuffs[target.id], unit, target)
this.calcHp(target, damage, targetMap)
}
buffedTargetsMap[buff.id] = targets
case buffTypeMagicDamage:
if buff.targetType != targetTypeEnemy {
continue
}
var targets = this.calcAttackTarget(unit, buff, a, b, nil, buffedTargetsMap)
var gainBuffs = this.calcGainBuff(&bi, skill.buffs, targets, buffedTargetsMap)
for _, target := range targets {
var damage = this.calcDamage(buff, gainBuffs[target.id], unit, target)
this.calcHp(target, damage, targetMap)
}
buffedTargetsMap[buff.id] = targets
case buffTypeStatusAbnormal:
case buffTypeChangeAttr:
case buffTypeRevive:
case buffTypeDamageReturn:
case buffTypeBisect:
case buffTypeShield:
case buffTypeChangeHp:
case buffTypeSteal:
case buffTypeAddRage:
case buffTypeImmune:
case buffTypeUndead:
case buffTypeDamageShare:
case buffTypeEffectAbnormal:
}
}
for _, target := range targetMap {
targetList = append(targetList, target)
}
return
}
func (this *_BattleCalculator) calcGainBuff(bi *int, buffs []*_BattleBuff, selectedTargets []*_BattleUnit, buffedTargetsMap map[uint32][]*_BattleUnit) map[uint64][]*_BattleBuff {
for *bi < len(buffs) {
var buff = buffs[*bi]
switch buff.buffType {
case buffTypeChangeAttr:
//*bi += 1
default:
break
}
}
return nil
}
// 血量
func (this *_BattleCalculator) calcHp(unit *_BattleUnit, damage int64, targetMap map[uint32]*pb.BattleTarget) {
var newHp = int64(unit.hp) + damage
if newHp < 0 {
newHp = 0
}
unit.hp = uint64(newHp)
var target = targetMap[unit.position]
if target == nil {
target = &pb.BattleTarget{
Type: uint32(util.If(unit.role.attacker, 1, 2)),
Position: unit.position,
HpMax: unit.attrs[pb.AttrType_Hp],
Rage: unit.rage,
BuffList: nil,
ValueList: nil,
}
targetMap[target.Position] = target
}
target.ValueList = append(target.ValueList, &pb.BattleValue{
Value: damage,
Hp: unit.hp,
})
}
// 目标
func (this *_BattleCalculator) calcAttackTarget(unit *_BattleUnit, buff *_BattleBuff, a, b *_BattleRole, selectedTargets []*_BattleUnit, buffedTargetsMap map[uint32][]*_BattleUnit) []*_BattleUnit {
var targets []*_BattleUnit
switch buff.targetType {
case targetTypeSelf:
targets = []*_BattleUnit{unit}
case targetTypeTeammate:
targets = a.unitList
case targetTypeEnemy:
targets = b.unitList
case targetTypeTeammateGender:
for _, targetArg := range buff.targetArgs {
for ui := range a.unitList {
if a.unitList[ui] != nil && a.unitList[ui].hp > 0 {
if a.unitList[ui].gender == uint32(targetArg) {
targets = append(targets, a.unitList[ui])
}
}
}
}
case targetTypeEnemyGender:
for _, targetArg := range buff.targetArgs {
for ui := range b.unitList {
if a.unitList[ui] != nil && a.unitList[ui].hp > 0 {
if b.unitList[ui].gender == uint32(targetArg) {
targets = append(targets, b.unitList[ui])
}
}
}
}
case targetTypeTeammateCountry:
for _, targetArg := range buff.targetArgs {
for ui := range a.unitList {
if a.unitList[ui] != nil && a.unitList[ui].hp > 0 {
if a.unitList[ui].country == uint32(targetArg) {
targets = append(targets, a.unitList[ui])
}
}
}
}
case targetTypeEnemyCountry:
for _, targetArg := range buff.targetArgs {
for ui := range b.unitList {
if a.unitList[ui] != nil && a.unitList[ui].hp > 0 {
if b.unitList[ui].country == uint32(targetArg) {
targets = append(targets, b.unitList[ui])
}
}
}
}
}
var buffedTargets []*_BattleUnit
for i, conditionType := range buff.conditionType {
switch conditionType {
case conditionTypeBuffId:
buffedTargets = buffedTargetsMap[uint32(buff.conditionArgs[i])]
if len(buffedTargets) == 0 {
return nil
}
case conditionTypeBuff:
if len(buffedTargets) == 0 {
return nil
}
for _, buffedTarget := range buffedTargets {
var idx = util.SliceFindIndex(buffedTarget.buffs, func(bi int) bool {
return buffedTarget.buffs[bi].id == uint32(buff.conditionArgs[i])
})
if idx == -1 {
return nil
}
}
case conditionTypeDeBuff:
if len(buffedTargets) == 0 {
return nil
}
for _, buffedTarget := range buffedTargets {
var idx = util.SliceFindIndex(buffedTarget.deBuffs, func(bi int) bool {
return buffedTarget.buffs[bi].id == uint32(buff.conditionArgs[i])
})
if idx == -1 {
return nil
}
}
case conditionTypeTargetLess:
if int32((float64(len(selectedTargets))/float64(len(buffedTargets)))*ratioOut) >= buff.conditionArgs[i] {
return nil
}
case conditionTypeTargetGreater:
if int32((float64(len(selectedTargets))/float64(len(buffedTargets)))*ratioOut) <= buff.conditionArgs[i] {
return nil
}
}
}
if len(buff.priorityType) == 0 {
if buff.rangeType == rangeTypeSingle {
return util.RandomSelect(this.rd, targets, 1)
} else {
if buff.rangeNum == 0 {
return targets
} else {
return util.RandomSelect(this.rd, targets, int(buff.rangeNum))
}
}
} else {
for i := range buff.priorityType {
if len(buff.priorityArgs) <= i {
continue
}
switch buff.priorityType[i] {
case priorityTypeNone:
continue
case priorityTypePosition:
switch buff.priorityArgs[i] {
case priorityArgFront:
var priorityTargets []*_BattleUnit
for _, target := range targets {
if target.position < 3 {
priorityTargets = append(priorityTargets, target)
}
}
if len(priorityTargets) > 0 {
targets = priorityTargets
}
case priorityArgBack:
var priorityTargets []*_BattleUnit
for _, target := range targets {
if target.position > 2 {
priorityTargets = append(priorityTargets, target)
}
}
if len(priorityTargets) > 0 {
targets = priorityTargets
}
case priorityArgColumn:
if buff.rangeNum < 1 || buff.rangeNum > 2 {
continue
}
var columns [3][2]*_BattleUnit
for _, target := range targets {
columns[target.position%3][target.position/3] = target
}
targets = targets[:0]
for _, column := range util.RandomSelect(this.rd, columns[:], int(buff.rangeNum)) {
for ci := range column {
targets = append(targets, column[ci])
}
}
return targets
}
case priorityTypeHp:
sort.Slice(targets, func(i, j int) bool {
if buff.priorityArgs[i] == priorityArgMin {
return targets[i].hp < targets[j].hp
} else {
return targets[i].hp > targets[j].hp
}
})
break
case priorityTypeGender:
var priorityTargets []*_BattleUnit
for _, target := range targets {
if target.gender > uint32(buff.priorityArgs[i]) {
priorityTargets = append(priorityTargets, target)
}
}
if len(priorityTargets) > 0 {
targets = priorityTargets
}
case priorityTypeRage:
sort.Slice(targets, func(i, j int) bool {
if buff.priorityArgs[i] == priorityArgMin {
return targets[i].rage < targets[j].rage
} else {
return targets[i].rage > targets[j].rage
}
})
break
case priorityTypeAttack:
sort.Slice(targets, func(i, j int) bool {
if buff.priorityArgs[i] == priorityArgMin {
return float64(targets[i].attrs[pb.AttrType_Attack])*(1.0+float64(targets[i].attrs[pb.AttrType_AttackRatio])*ratioIn) < float64(targets[j].attrs[pb.AttrType_Attack])*(1.0+float64(targets[j].attrs[pb.AttrType_AttackRatio])*ratioIn)
} else {
return float64(targets[i].attrs[pb.AttrType_Attack])*(1.0+float64(targets[i].attrs[pb.AttrType_AttackRatio])*ratioIn) > float64(targets[j].attrs[pb.AttrType_Attack])*(1.0+float64(targets[j].attrs[pb.AttrType_AttackRatio])*ratioIn)
}
})
break
}
}
if buff.rangeType == rangeTypeSingle {
return targets[:1]
} else {
if uint32(len(targets)) <= buff.rangeNum {
return targets
} else {
return targets[:buff.rangeNum]
}
}
}
}
// 伤害
func (this *_BattleCalculator) calcDamage(buff *_BattleBuff, addBuffs []*_BattleBuff, unit *_BattleUnit, target *_BattleUnit) int64 {
var defense float64
switch buff.buffType {
case buffTypePhysicalDamage:
defense = float64(target.attrs[pb.AttrType_PhysicalDefense]) * (1.0 + float64(target.attrs[pb.AttrType_PhysicalDefenseRatio])*ratioIn)
case buffTypeMagicDamage:
defense = float64(target.attrs[pb.AttrType_MagicDefense]) * (1.0 + float64(target.attrs[pb.AttrType_MagicDefenseRatio])*ratioIn)
default:
return 0
}
if len(buff.buffArgs) < 2 {
return 0
}
var attack = float64(unit.attrs[pb.AttrType_Attack]) * (1.0 + float64(unit.attrs[pb.AttrType_AttackRatio])*ratioIn)
var damage float64
if buff.buffArgs[0] == 1 {
damage = float64(buff.buffArgs[1])
} else {
damage = attack * (float64(buff.buffArgs[1]) * ratioIn)
}
var damageRatio = 1.0 + float64(unit.attrs[pb.AttrType_DamageRatio]-target.attrs[pb.AttrType_DamageRelief])*ratioIn
var finalDamageRatio = 1.0 + float64(unit.attrs[pb.AttrType_FinalDamageRatio])*ratioIn
var finalDamageRelief = 1.0 - float64(target.attrs[pb.AttrType_FinalDamageRelief])*ratioIn
var criticalRate = (float64(unit.attrs[pb.AttrType_CriticalRate]) - float64(target.attrs[pb.AttrType_CriticalResistance])) * ratioIn
var factor = this.rd.Float64()
if factor <= criticalRate {
var criticalDamage = float64(unit.attrs[pb.AttrType_CriticalDamage]) * ratioIn
var criticalDamageRelief = float64(unit.attrs[pb.AttrType_CriticalDamageRelief]) * ratioIn
damage = ((damage - defense) * damageRatio * 2) * (criticalDamage - criticalDamageRelief) * finalDamageRatio * finalDamageRelief
} else {
damage = (damage - defense) * damageRatio * finalDamageRatio * finalDamageRelief
if damage <= 0 {
damage = 1
}
}
return int64(-damage)
}
// 治疗
func (this *_BattleCalculator) calcTreat(buff _BattleBuff, unit *_BattleUnit, target *_BattleUnit) int64 {
return 0
}