519 lines
14 KiB
Go
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
|
|
}
|