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 }