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

431 lines
12 KiB
Go

package logic
import (
"ecs/proto"
"ecs/proto/pb"
"ecs/servers/game/data"
"github.com/oylshe1314/framework/util"
"sort"
)
func (this *Player) EnterBattle(opponent *Player, sceneTable *data.Scene) {
this.Temp.Fighting = true
this.Temp.RandSeed = util.DefaultRandom.Uint64()
this.enterScene(uint32(sceneTable.Id))
this.copyBattle(opponent, sceneTable)
}
func (this *Player) EndBattle() {
this.Temp.Fighting = false
this.Temp.RandSeed = 0
this.enterScene(0)
}
func (this *Player) copyBattle(opponent *Player, sceneTable *data.Scene) {
var ack, err = this.virtualBattle(opponent, sceneTable)
if err != nil {
_ = this.TipNotice(err)
return
}
if ack.Succeed {
this.PassedCopy(sceneTable, ack.Score)
}
_ = this.Send(pb.ModId_ModuleBattle, pb.MsgId_ModBattleEnter, ack)
}
type _BattleBuff struct {
id uint32
group int32
targetType uint32
targetArgs []int32
conditionType []uint32
conditionArgs []int32
rangeType uint32
rangeNum uint32
priorityType []uint32
priorityArgs []int32
buffType uint32
buffArgs []int64
duration uint32
canMerge bool
canCover bool
canStack bool
canClean bool
}
type _BattleSkill struct {
id uint32
skillType uint32
cooldown uint32
consumeRage uint32
releaseRound uint32
buffs []*_BattleBuff
}
type _BattleUnit struct {
id uint64
position uint32
gender uint32
country uint32
hp uint64
rage uint32
speed uint32
baseSpeed uint32
posSpeed uint32
amuletSpeed uint32
harnessSpeed uint32
attrs Attrs
role *_BattleRole
buffs []*_BattleBuff
deBuffs []*_BattleBuff
attackSkills []*_BattleSkill
passiveSkills []*_BattleSkill
}
type _BattleRole struct {
attacker bool
addSpeed uint32
capacity uint64
unitList []*_BattleUnit
}
func (this *Player) virtualBattle(opponent *Player, sceneTable *data.Scene) (ack *pb.BattleEnterAck, tip proto.TipError) {
var lineup = this.CurrentLineup()
if lineup == nil {
return nil, proto.TipLineupNotFound
}
var me = &_BattleRole{attacker: true}
me.capacity, me.unitList, tip = this.loadHeroes(me, lineup)
if tip != nil {
return nil, tip
}
var he = &_BattleRole{attacker: false}
if pb.CopyType(sceneTable.CopyType) == pb.CopyType_CopyArena {
if opponent == nil {
return nil, proto.TipOpponentNotFound
}
var heLineup = opponent.CurrentLineup()
if heLineup == nil {
return nil, proto.TipLineupNotFound
}
he.capacity, he.unitList, tip = this.loadHeroes(he, heLineup)
if tip != nil {
return nil, tip
}
} else {
he.capacity, he.unitList, tip = this.loadMonsters(he, []int{sceneTable.Monster1, sceneTable.Monster2, sceneTable.Monster3, sceneTable.Monster4, sceneTable.Monster5, sceneTable.Monster6})
if tip != nil {
return nil, tip
}
}
ack = &pb.BattleEnterAck{
SceneId: uint32(sceneTable.Id),
}
ack.Heroes = make([]*pb.BattleUnit, 6)
for i := range me.unitList {
if me.unitList[i] != nil {
ack.Heroes[i] = &pb.BattleUnit{
UnitId: me.unitList[i].id,
}
}
}
ack.Enemies = make([]*pb.BattleUnit, 6)
for i := range he.unitList {
if he.unitList[i] != nil {
ack.Enemies[i] = &pb.BattleUnit{
UnitId: he.unitList[i].id,
}
}
}
if len(me.unitList) == 0 {
ack.Succeed = false
ack.Score = 0
} else if len(he.unitList) == 0 {
ack.Succeed = true
ack.Score = 3
} else {
var calculator = &_BattleCalculator{
rd: util.NewRand(),
me: me,
he: he,
}
ack.Succeed, ack.RoundList = calculator.calcBattle(sceneTable.BattleRound)
ack.BattleRounds = uint32(len(ack.RoundList))
var rewardItems = map[uint32]uint32{}
if ack.Succeed {
ack.Score = 2
if me.unitList[0].hp > 0 {
ack.Score = 3
}
if sceneTable.PassExpMultiple > 0 {
this.addExp(this.RoleLevel*uint32(sceneTable.PassExpMultiple), LogTypeItemObtainByCopy)
}
if sceneTable.PassCoinMultiple > 0 {
this.addMoney(pb.MoneyType_Coin, this.RoleLevel*uint32(sceneTable.PassCoinMultiple), LogTypeItemObtainByCopy)
}
if sceneTable.FirstPassDrop > 0 {
if this.GetCopyPassed(pb.CopyType(sceneTable.CopyType), uint32(sceneTable.CopyId)) == 0 {
var dropResult = this.manager.tables.Drop.Drop(sceneTable.FirstPassDrop, 1)
for _, items := range dropResult {
rewardItems[uint32(items[0])] += uint32(items[1])
ack.RewardList = append(ack.RewardList, &pb.Item{
ItemId: uint32(items[0]),
ItemNum: uint32(items[1]),
})
}
}
}
if sceneTable.PassDrop > 0 {
var dropResult = this.manager.tables.Drop.Drop(sceneTable.PassDrop, 1)
for _, items := range dropResult {
rewardItems[uint32(items[0])] += uint32(items[1])
ack.RewardList = append(ack.RewardList, &pb.Item{
ItemId: uint32(items[0]),
ItemNum: uint32(items[1]),
})
}
}
}
if sceneTable.MustDrop > 0 {
var dropResult = this.manager.tables.Drop.Drop(sceneTable.MustDrop, 1)
for _, items := range dropResult {
rewardItems[uint32(items[0])] += uint32(items[1])
ack.RewardList = append(ack.RewardList, &pb.Item{
ItemId: uint32(items[0]),
ItemNum: uint32(items[1]),
})
}
}
this.AddItems(rewardItems, LogTypeItemObtainByCopy)
}
return ack, nil
}
func (this *Player) calcAttrsCapacity(attrs Attrs) uint64 {
return uint64((float64(attrs[pb.AttrType_Attack]) * 2) +
(float64(attrs[pb.AttrType_Hp]) * 0.2) +
(float64(attrs[pb.AttrType_PhysicalDefense]) * 6) +
(float64(attrs[pb.AttrType_MagicDefense]) * 6) +
(float64(attrs[pb.AttrType_DamageRatio]) * 12) +
(float64(attrs[pb.AttrType_DamageRelief]) * 12) +
(float64(attrs[pb.AttrType_CriticalRate]) * 12) +
(float64(attrs[pb.AttrType_CriticalResistance]) * 12) +
(float64(attrs[pb.AttrType_CriticalDamage]) * 12) +
(float64(attrs[pb.AttrType_CriticalDamageRelief]) * 12) +
(float64(attrs[pb.AttrType_HitRate]) * 12) +
(float64(attrs[pb.AttrType_DodgeRate]) * 12) +
(float64(attrs[pb.AttrType_TreatRatio]) * 12) +
(float64(attrs[pb.AttrType_ByTreatedRate]) * 12) +
(float64(attrs[pb.AttrType_FinalDamageRatio]) * 12))
}
func (this *Player) loadHeroes(role *_BattleRole, lineup *PlayerLineup) (capacity uint64, unitList []*_BattleUnit, tip proto.TipError) {
unitList = make([]*_BattleUnit, 6)
var heroIndex = 0
for i := range 6 {
if lineup.Heroes[i] != nil {
if hero := this.Hero[lineup.Heroes[i].HeroUid]; hero != nil {
var unit = &_BattleUnit{
id: lineup.Heroes[i].HeroUid,
position: lineup.Heroes[i].Position,
attrs: hero.Attrs(),
role: role,
}
var heroTable = this.manager.tables.Hero.Find1(int(hero.Id))
if heroTable == nil {
return 0, nil, proto.TipDataTablesError
}
unit.gender = uint32(heroTable.Sex)
unit.country = uint32(heroTable.Country)
unit.attackSkills, tip = this.loadSkills(heroTable.Skill)
if tip != nil {
return
}
sort.Slice(unit.attackSkills, func(i, j int) bool {
return unit.attackSkills[i].skillType > unit.attackSkills[j].skillType
})
unit.hp = unit.attrs[pb.AttrType_Hp]
unit.rage = 4
unit.baseSpeed = 100
unit.posSpeed = uint32(20 - 4*heroIndex)
unit.amuletSpeed = 0 //TODO calc speed
unit.harnessSpeed = 0 //TODO calc speed
//TODO load hero passive skills, from equip, break, soul
capacity += this.calcAttrsCapacity(unit.attrs)
unitList[lineup.Heroes[i].Position-1] = unit
}
}
}
return
}
func (this *Player) loadMonsters(role *_BattleRole, monsterIds []int) (capacity uint64, unitList []*_BattleUnit, tip proto.TipError) {
unitList = make([]*_BattleUnit, 6)
var monsterIndex uint64 = 0
for i, monsterId := range monsterIds {
if monsterId == 0 {
continue
}
var monsterTable = this.manager.tables.Monster.Find(int(monsterId))
if monsterTable == nil {
return 0, nil, proto.TipDataTablesError
}
var unit = &_BattleUnit{
id: uint64(monsterTable.Id),
position: uint32(i + 1),
gender: uint32(monsterTable.Gender),
country: uint32(monsterTable.Country),
role: role,
}
unit.attackSkills, tip = this.loadSkills(monsterTable.AttackSkills)
if tip != nil {
return
}
sort.Slice(unit.attackSkills, func(i, j int) bool {
return unit.attackSkills[i].skillType > unit.attackSkills[j].skillType
})
unit.passiveSkills, tip = this.loadSkills(monsterTable.PassiveSkills)
if tip != nil {
return
}
unit.attrs[pb.AttrType_Attack] = uint64(monsterTable.AttrValue1)
unit.attrs[pb.AttrType_Hp] = uint64(monsterTable.AttrValue2)
unit.attrs[pb.AttrType_PhysicalDefense] = uint64(monsterTable.AttrValue3)
unit.attrs[pb.AttrType_MagicDefense] = uint64(monsterTable.AttrValue4)
unit.attrs[pb.AttrType_AttackRatio] = uint64(monsterTable.AttrValue5)
unit.attrs[pb.AttrType_HpRatio] = uint64(monsterTable.AttrValue6)
unit.attrs[pb.AttrType_PhysicalDefenseRatio] = uint64(monsterTable.AttrValue7)
unit.attrs[pb.AttrType_MagicDefenseRatio] = uint64(monsterTable.AttrValue8)
unit.attrs[pb.AttrType_DamageRatio] = uint64(monsterTable.AttrValue9)
unit.attrs[pb.AttrType_DamageRelief] = uint64(monsterTable.AttrValue10)
unit.attrs[pb.AttrType_CriticalRate] = uint64(monsterTable.AttrValue11)
unit.attrs[pb.AttrType_CriticalResistance] = uint64(monsterTable.AttrValue12)
unit.attrs[pb.AttrType_CriticalDamage] = uint64(monsterTable.AttrValue13)
unit.attrs[pb.AttrType_CriticalDamageRelief] = uint64(monsterTable.AttrValue14)
unit.attrs[pb.AttrType_HitRate] = uint64(monsterTable.AttrValue15)
unit.attrs[pb.AttrType_DodgeRate] = uint64(monsterTable.AttrValue16)
unit.attrs[pb.AttrType_TreatRatio] = uint64(monsterTable.AttrValue17)
unit.attrs[pb.AttrType_ByTreatedRate] = uint64(monsterTable.AttrValue18)
unit.attrs[pb.AttrType_FinalDamageRatio] = uint64(monsterTable.AttrValue19)
unit.attrs[pb.AttrType_FinalDamageRelief] = uint64(monsterTable.AttrValue20)
monsterIndex += 1
unit.hp = unit.attrs[pb.AttrType_Hp]
unit.rage = 4
unit.baseSpeed = 0
unit.posSpeed = uint32(20 - 4*monsterIndex)
unit.amuletSpeed = 0 //TODO calc speed
unit.harnessSpeed = 0 //TODO calc speed
capacity += this.calcAttrsCapacity(unit.attrs)
unitList[i] = unit
}
return
}
func (this *Player) loadSkills(skillIds []int) (skillList []*_BattleSkill, tip proto.TipError) {
skillList = make([]*_BattleSkill, 0, len(skillIds))
for _, skillId := range skillIds {
var skillTable = this.manager.tables.Skill.Find(skillId)
if skillTable == nil {
return nil, proto.TipDataTablesError
}
var skill = &_BattleSkill{
id: uint32(skillId),
skillType: uint32(skillTable.SkillType),
consumeRage: uint32(skillTable.ConsumeRage),
}
skill.buffs, tip = this.loadSkillsBuff(skillTable.SkillBuff)
if tip != nil {
return
}
skillList = append(skillList, skill)
}
return
}
func (this *Player) loadSkillsBuff(buffIds []int) (buffList []*_BattleBuff, tip proto.TipError) {
buffList = make([]*_BattleBuff, 0, len(buffIds))
for _, buffId := range buffIds {
var buffTable = this.manager.tables.SkillBuff.Find(buffId)
if buffTable == nil {
return nil, proto.TipDataTablesError
}
var buff = &_BattleBuff{
id: uint32(buffId),
targetType: uint32(buffTable.TargetType),
targetArgs: util.NumbersConvert2(buffTable.TargetArgs, int32(0)),
rangeType: uint32(buffTable.RangeType),
rangeNum: uint32(buffTable.RangeNum),
priorityType: util.NumbersConvert2(buffTable.PriorityType, uint32(0)),
priorityArgs: util.NumbersConvert2(buffTable.PriorityArgs, int32(0)),
buffType: uint32(buffTable.BuffType),
buffArgs: util.NumbersConvert2(buffTable.BuffArgs, int64(0)),
duration: uint32(buffTable.Duration),
canMerge: buffTable.CanMerge,
canCover: buffTable.CanCover,
canStack: buffTable.CanStack,
canClean: buffTable.CanClean,
}
buffList = append(buffList, buff)
}
sort.Slice(buffList, func(i, j int) bool {
if buffList[i].group == buffList[j].group {
return buffList[i].id < buffList[j].id
} else {
if buffList[i].group == 0 {
return false
}
if buffList[j].group == 0 {
return true
}
return buffList[i].group < buffList[j].group
}
})
return buffList, nil
}