491 lines
13 KiB
Go
491 lines
13 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 (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) calcBattle(rounds int, me, he *_BattleRole) (succeed bool, roundList []*pb.BattleRound) {
|
||
|
var roles = [2]*_BattleRole{me, he}
|
||
|
|
||
|
var ri = 0
|
||
|
if he.capacity > me.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(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 (this *_BattleCalculator) calcSkill(unit *_BattleUnit, a, b *_BattleRole) (caster *pb.BattleTarget, skillId uint32, targetList []*pb.BattleTarget) {
|
||
|
for _, skill := range unit.attackSkills {
|
||
|
if unit.rage < skill.rage {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
switch skill.tipe {
|
||
|
case skillTypeAttack:
|
||
|
skillId = skill.id
|
||
|
caster, targetList = this.calcBuff(unit, skill, a, b)
|
||
|
unit.rage += 2
|
||
|
case skillTypeActive:
|
||
|
skillId = skill.id
|
||
|
unit.rage -= skill.rage
|
||
|
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{}
|
||
|
for _, buff := range skill.buffs {
|
||
|
if buff.buffType == buffTypeNone || len(buff.buffArgs) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
//switch buff.targetType {
|
||
|
//case targetTypeSelf:
|
||
|
// targets = []*_BattleUnit{unit}
|
||
|
//case targetTypeTeammate:
|
||
|
//case targetTypeEnemy:
|
||
|
//case targetTypeTeammateGender:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, teammate := range a.unitList {
|
||
|
// if teammate != nil {
|
||
|
// if slices.Contains(buff.targetArgs, teammate.gender) {
|
||
|
// targets = append(targets, teammate)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeEnemyGender:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, enemy := range b.unitList {
|
||
|
// if enemy != nil {
|
||
|
// if slices.Contains(buff.targetArgs, enemy.gender) {
|
||
|
// targets = append(targets, enemy)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeTeammateCountry:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, teammate := range a.unitList {
|
||
|
// if teammate != nil {
|
||
|
// if slices.Contains(buff.targetArgs, teammate.country) {
|
||
|
// targets = append(targets, teammate)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeEnemyCountry:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, enemy := range b.unitList {
|
||
|
// if enemy != nil {
|
||
|
// if slices.Contains(buff.targetArgs, enemy.country) {
|
||
|
// targets = append(targets, enemy)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeTeammateStatus:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, teammate := range a.unitList {
|
||
|
// if teammate != nil {
|
||
|
// if slices.ContainsFunc(buff.targetArgs, func(targetArg uint32) bool {
|
||
|
// return slices.ContainsFunc(teammate.deBuffs, func(buff *_BattleBuff) bool {
|
||
|
// return buff.id == targetArg
|
||
|
// })
|
||
|
// }) {
|
||
|
// targets = append(targets, teammate)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeEnemyStatus:
|
||
|
// if len(buff.targetArgs) == 0 {
|
||
|
// continue
|
||
|
// }
|
||
|
// for _, enemy := range b.unitList {
|
||
|
// if enemy != nil {
|
||
|
// if slices.ContainsFunc(buff.targetArgs, func(targetArg uint32) bool {
|
||
|
// return slices.ContainsFunc(enemy.deBuffs, func(buff *_BattleBuff) bool {
|
||
|
// return buff.id == targetArg
|
||
|
// })
|
||
|
// }) {
|
||
|
// targets = append(targets, enemy)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//case targetTypeAttackRate:
|
||
|
//}
|
||
|
|
||
|
var targets []*_BattleUnit
|
||
|
switch buff.buffType {
|
||
|
case buffTypePhysicalDamage:
|
||
|
if buff.targetType != targetTypeEnemy {
|
||
|
continue
|
||
|
}
|
||
|
targets = this.calcAttackTarget(b.unitList, buff.priorityType, buff.priorityArgs, buff.rangeType, buff.rangeNum)
|
||
|
for _, target := range targets {
|
||
|
var damage = this.calcDamage(buff, unit, target)
|
||
|
this.calcHp(target, damage, targetMap)
|
||
|
}
|
||
|
case buffTypeMagicDamage:
|
||
|
if buff.targetType != targetTypeEnemy {
|
||
|
continue
|
||
|
}
|
||
|
targets = this.calcAttackTarget(b.unitList, buff.priorityType, buff.priorityArgs, buff.rangeType, buff.rangeNum)
|
||
|
for _, target := range targets {
|
||
|
var damage = this.calcDamage(buff, unit, target)
|
||
|
this.calcHp(target, damage, targetMap)
|
||
|
}
|
||
|
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) 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(unitList []*_BattleUnit, priorityType, priorityArgs []uint32, rangeType, rangeNum uint32) []*_BattleUnit {
|
||
|
var targets []*_BattleUnit
|
||
|
if len(priorityType) == 0 {
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[ui].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
return util.RandomSelect(this.rd, targets, util.If(rangeType == rangeTypeSingle, 1, util.If(rangeNum > 0, int(rangeNum), len(targets))))
|
||
|
}
|
||
|
|
||
|
for i := range priorityType {
|
||
|
if len(priorityArgs) <= i {
|
||
|
continue
|
||
|
}
|
||
|
switch priorityType[i] {
|
||
|
case priorityTypeNone:
|
||
|
if len(priorityType) == 0 {
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[ui].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
return util.RandomSelect(this.rd, targets, util.If(rangeType == rangeTypeSingle, 1, util.If(rangeNum > 0, int(rangeNum), len(targets))))
|
||
|
}
|
||
|
case priorityTypePosition:
|
||
|
switch priorityArgs[i] {
|
||
|
case priorityArgFront:
|
||
|
for ui := range []int{0, 1, 2} {
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
if len(targets) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
case priorityArgBack:
|
||
|
for ui := range []int{3, 4, 5} {
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
if len(targets) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
case priorityArgColumn:
|
||
|
var columns [][]*_BattleUnit
|
||
|
for _, ui := range []int{0, 1, 2} {
|
||
|
var column []*_BattleUnit
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
column = append(column, unitList[ui])
|
||
|
}
|
||
|
if unitList[ui+3] != nil && unitList[i].hp > 0 {
|
||
|
column = append(column, unitList[ui+3])
|
||
|
}
|
||
|
if len(column) > 0 {
|
||
|
columns = append(columns, column)
|
||
|
}
|
||
|
}
|
||
|
for _, column := range util.RandomSelect(this.rd, columns, int(rangeNum)) {
|
||
|
for ui := range column {
|
||
|
targets = append(targets, column[ui])
|
||
|
}
|
||
|
}
|
||
|
return targets
|
||
|
}
|
||
|
case priorityTypeHp:
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
sort.Slice(targets, func(i, j int) bool {
|
||
|
if priorityArgs[i] == priorityArgMin {
|
||
|
return targets[i].hp < targets[j].hp
|
||
|
} else {
|
||
|
return targets[i].hp > targets[j].hp
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case priorityTypeGender:
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] == nil || unitList[ui].hp == 0 || unitList[ui].gender != priorityArgs[i] {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
if len(targets) == 0 {
|
||
|
continue
|
||
|
}
|
||
|
case priorityTypeRage:
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
sort.Slice(targets, func(i, j int) bool {
|
||
|
if priorityArgs[i] == priorityArgMin {
|
||
|
return targets[i].rage < targets[j].rage
|
||
|
} else {
|
||
|
return targets[i].rage > targets[j].rage
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
case priorityTypeAttack:
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[i].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
sort.Slice(targets, func(i, j int) bool {
|
||
|
if priorityArgs[i] == priorityArgMin {
|
||
|
return float64(unitList[i].attrs[pb.AttrType_Attack])*(1.0+float64(unitList[i].attrs[pb.AttrType_AttackRatio])*ratioIn) < float64(unitList[j].attrs[pb.AttrType_Attack])*(1.0+float64(unitList[j].attrs[pb.AttrType_AttackRatio])*ratioIn)
|
||
|
} else {
|
||
|
return float64(unitList[i].attrs[pb.AttrType_Attack])*(1.0+float64(unitList[i].attrs[pb.AttrType_AttackRatio])*ratioIn) > float64(unitList[j].attrs[pb.AttrType_Attack])*(1.0+float64(unitList[j].attrs[pb.AttrType_AttackRatio])*ratioIn)
|
||
|
}
|
||
|
})
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(targets) == 0 {
|
||
|
for ui := range unitList {
|
||
|
if unitList[ui] != nil && unitList[ui].hp > 0 {
|
||
|
targets = append(targets, unitList[ui])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if rangeType == rangeTypeSingle {
|
||
|
return targets[:1]
|
||
|
} else {
|
||
|
if uint32(len(targets)) <= rangeNum {
|
||
|
return targets
|
||
|
} else {
|
||
|
return targets[:rangeNum]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (this *_BattleCalculator) calcDamage(buff *_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
|
||
|
}
|