package arena import ( "context" "fmt" "go_dreamfactory/comm" "go_dreamfactory/lego/core" "go_dreamfactory/lego/sys/log" "go_dreamfactory/lego/sys/mgo" "go_dreamfactory/modules" "go_dreamfactory/pb" "go_dreamfactory/sys/configure" cfg "go_dreamfactory/sys/configure/structs" "go_dreamfactory/sys/db" "math" "math/rand" "time" "go_dreamfactory/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/x/bsonx" ) // /竞技场 数据组件 type modelArena struct { modules.MCompModel module *Arena } // 组件初始化接口 func (this *modelArena) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) { this.TableName = comm.TableArena this.MCompModel.Init(service, module, comp, opt) this.module = module.(*Arena) //创建uid索引 if _, err = this.DB.CreateIndex(core.SqlTable(this.TableName), mongo.IndexModel{ Keys: bsonx.Doc{ {Key: "uid", Value: bsonx.Int32(1)}, }, }); err != nil { return } _, err = this.DB.CreateIndex(core.SqlTable(this.TableName), mongo.IndexModel{ Keys: bson.M{"loc": "2dsphere"}, }) return } //是否在维护中 func (this *modelArena) isInMaintenance() (ok bool) { var ( opentime time.Time totalduration time.Duration lefttime time.Duration ) opentime = this.module.service.GetOpentime() totalduration = configure.Now().Sub(opentime) lefttime = totalduration % (time.Hour * 24 * 7 * time.Duration(this.module.ModuleTools.GetGlobalConf().PvpEndtime)) if lefttime < time.Hour*time.Duration(this.module.ModuleTools.GetGlobalConf().PvpMaintenancetime) { return true } return false } //获取赛季结算时间 func (this *modelArena) settlementTime() (endtime time.Time) { var ( opentime time.Time totalduration time.Duration lefttime time.Duration ) opentime = this.module.service.GetOpentime() totalduration = configure.Now().Sub(opentime) lefttime = totalduration % (time.Hour * 24 * 7 * time.Duration(this.module.ModuleTools.GetGlobalConf().PvpEndtime)) endtime = configure.Now().Add(time.Hour*24*7*time.Duration(this.module.ModuleTools.GetGlobalConf().PvpEndtime) - lefttime) return } // 查询用户装备数据 func (this *modelArena) queryPlayerInfo(uId string) (result *pb.DBArenaUser, err error) { result = &pb.DBArenaUser{} if err = this.Get(uId, result); err != nil && err != mgo.MongodbNil { this.module.Errorln(err) return } return } // 查询用户装备数据 func (this *modelArena) queryPlayers(uIds []string) (result []*pb.DBArenaUser, err error) { result = make([]*pb.DBArenaUser, 0) if _, err = this.GetByUids(uIds, &result); err != nil && err != mgo.MongodbNil { this.module.Errorln(err) return } return } // 查询用户装备数据 func (this *modelArena) queryArenaPlayer(uId string) (result *pb.ArenaPlayer, err error) { temp := &pb.DBArenaUser{} if err = this.Get(uId, temp); err != nil { this.module.Errorln(err) return } result = &pb.ArenaPlayer{ Uinfo: temp.Uinfo, Dan: temp.Dan, Integral: temp.Integral, Defend: temp.Defend, Isai: false, } return } // 查询用户英雄数据 func (this *modelArena) queryUserHeros(uid string, heroids []string) (results []*pb.DBHero, err error) { var ( model *db.DBModel ids []string resultstemp []*pb.DBHero ) if model, err = this.module.GetDBModelByUid(uid, comm.TableHero); err != nil { this.module.Errorln(err) return } for _, v := range heroids { if v != "" { ids = append(ids, v) } } resultstemp = make([]*pb.DBHero, 0) if err = model.GetListObjs(uid, ids, &resultstemp); err != nil { this.module.Errorln(err) return } results = make([]*pb.DBHero, len(heroids)) for i1, v1 := range resultstemp { for i2, _ := range results { if i1 == i2 { results[i2] = v1 } } } return } // /保存用户竞技场信息 func (this *modelArena) updateArenaUserInfo(info *pb.DBArenaUser) (err error) { var ( dan int32 ) if info.Integral < 0 { info.Integral = 0 } if dan, err = this.computedan(info.Integral); err != nil { return } info.Dan = dan this.Change(info.Uinfo.Uid, map[string]interface{}{ "integral": info.Integral, "dan": dan, "rank": info.Rank, "buynum": info.Buynum, "lastrtickettime": info.Lastrtickettime, "attack": info.Attack, "defend": info.Defend, "streak": info.Streak, "record": info.Record, "attackwinuum": info.Attackwinuum, "attacktotaluum": info.Attacktotaluum, "defendwinuum": info.Defendwinuum, "defendtotaluum": info.Defendtotaluum, "loc": []float64{float64(dan), float64(rand.Int31n(100)) / 1000.0}, "isdef": info.Isdef, "uinfo": info.Uinfo, "dan2": info.Dan2, "lastweektime": info.Lastweektime, "settlementtime": info.Settlementtime, "tasks": info.Tasks, "danaward": info.Danaward, "maxdan": info.Maxdan, "weekaward": info.Weekaward, "prededuction": info.Prededuction, }) return } func (this *modelArena) modifyIntegral(uid string, integral int32) (err error) { var ( dan int32 player *pb.ArenaPlayer ) if dan, err = this.computedan(integral); err != nil { return } player = &pb.ArenaPlayer{Uinfo: &pb.BaseUserInfo{Uid: uid}, Integral: integral} if err = this.module.modelRank.updateArenaRank(player); err != nil { this.module.Errorln(err) return } this.Change(uid, map[string]interface{}{ "integral": integral, "dan": dan, "rank": player.Rank, }) return } func (this *modelArena) computedan(integral int32) (dan int32, err error) { var ( active *cfg.GameArenaActiveRewardData ) if active, err = this.module.configure.getActiveReward(integral); err != nil { this.module.Errorln(err) return } dan = active.LvId return } // 匹配机器人 func (this *modelArena) matcheAI(dan, num int32) (results []*pb.ArenaPlayer, err error) { var ( active *cfg.GameArenaActiveRewardData ais []*cfg.GameArenaRobotData formats []*cfg.GameMonsterFormatData robots []*cfg.GameRobotData rank []int32 targets []int32 ) if active, err = this.module.configure.getActiveRewardById(dan); err != nil { this.module.Errorln(err) return } if ais, err = this.module.configure.getArenaRobot(dan); err != nil { this.module.Errorln(err) return } rank = make([]int32, len(ais)) for i, v := range ais { rank[i] = v.Weight } targets = make([]int32, num) for i := 0; i < int(num); i++ { index := comm.GetRandW(rank) targets[i] = index } results = make([]*pb.ArenaPlayer, num) if robots, err = this.module.ModuleTools.RandRobotConfig(int32(len(targets))); err != nil { return } for i, v := range targets { aiconf := ais[v] if formats, err = this.module.configure.getMonsterFormat(aiconf.MonsterformatId); err != nil { this.module.Errorln(err) return } results[i] = &pb.ArenaPlayer{ Uinfo: comm.GetRobotBaseInfo(robots[i]), Dan: dan, Integral: int32(rand.Intn(int(active.ScoreUp)-int(active.ScoreLow))) + active.ScoreLow, Isai: true, Mformatid: aiconf.MonsterformatId, Defend: &pb.DBPlayerBattleFormt{ Leadpos: 0, Formt: make([]*pb.DBHero, len(formats)), }, } for i1, v1 := range formats { if v1 == nil { results[i].Defend.Formt[i1] = nil } else { if v1.CaptainId == 1 { results[i].Defend.Leadpos = int32(i1) } // if monst, err = this.module.configure.getMonster(v1.Monster); err != nil { // this.module.Errorln(err) // } hero := &pb.DBHero{} if hero = this.module.ModuleHero.CreateMonster(fmt.Sprintf("%d", v1.Heroid), v1.Star, v1.Lv); hero == nil { err = fmt.Errorf("CreateMonster 失败") return } hero.Property[cfg.GamePropertyType_Base_MaxHp_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_MaxHp_Base]) * v1.Hppro) hero.Property[cfg.GamePropertyType_Base_Atk_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_Atk_Base]) * v1.Atkpro) hero.Property[cfg.GamePropertyType_Base_Def_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_Def_Base]) * v1.Defpro) hero.Suits = make([]*pb.DB_EquipmentSuit, 0) for _, v := range v1.Equip { hero.Suits = append(hero.Suits, &pb.DB_EquipmentSuit{ Suitid: v, Effect: true, }) } results[i].Defend.Formt[i1] = hero } } } return } // 获取目标去陪数据 func (this *modelArena) matchePlayer(uid string, group, dan, num int32) (results []*pb.ArenaPlayer, err error) { var ( cursor *mongo.Cursor ) results = make([]*pb.ArenaPlayer, 0) port := []float64{float64(dan), float64(rand.Int31n(100)) / 1000.0} if cursor, err = this.DBModel.DB.Find(comm.TableArena, bson.M{ "isdef": true, "uid": bson.M{"$ne": uid}, "uinfo.group": group, "dan": dan, "loc": bson.M{ "$near": bson.M{ "$geometry": bson.M{"type": "Point", "coordinates": port}, "$maxDistance": 100000, }, }, }, options.Find().SetSkip(0).SetLimit(int64(num))); err != nil { this.module.Errorln(err) return } else { for cursor.Next(context.Background()) { temp := &pb.DBArenaUser{} if err = cursor.Decode(temp); err != nil { this.module.Errorln(err) return } results = append(results, &pb.ArenaPlayer{ Uinfo: temp.Uinfo, Dan: temp.Dan, Integral: temp.Integral, Defend: temp.Defend, }) } } return } func (this *modelArena) getAI(mformatId int32) (ai *pb.ArenaPlayer, err error) { var ( robots []*cfg.GameRobotData formats []*cfg.GameMonsterFormatData ) if robots, err = this.module.ModuleTools.RandRobotConfig(1); err != nil { return } if formats, err = this.module.configure.getMonsterFormat(mformatId); err != nil { this.module.Errorln(err) return } ai = &pb.ArenaPlayer{ Uinfo: comm.GetRobotBaseInfo(robots[0]), Isai: true, Mformatid: mformatId, Defend: &pb.DBPlayerBattleFormt{ Leadpos: 0, Formt: make([]*pb.DBHero, len(formats)), }, } for i1, v1 := range formats { if v1 == nil { ai.Defend.Formt[i1] = nil } else { hero := &pb.DBHero{} if hero = this.module.ModuleHero.CreateMonster(fmt.Sprintf("%d", v1.Heroid), v1.Star, v1.Lv); hero == nil { err = fmt.Errorf("CreateMonster 失败") return } hero.Property[cfg.GamePropertyType_Base_MaxHp_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_MaxHp_Base]) * v1.Hppro) hero.Property[cfg.GamePropertyType_Base_Atk_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_Atk_Base]) * v1.Atkpro) hero.Property[cfg.GamePropertyType_Base_Def_Base] = int32(float32(hero.Property[cfg.GamePropertyType_Base_Def_Base]) * v1.Defpro) hero.Suits = make([]*pb.DB_EquipmentSuit, 0) for _, v := range v1.Equip { hero.Suits = append(hero.Suits, &pb.DB_EquipmentSuit{ Suitid: v, Effect: true, }) } ai.Defend.Formt[i1] = hero } } return } // 积分计算 func (this *modelArena) integralCompute(red, bule *pb.ArenaPlayer, iswin bool) { var ( redactive *cfg.GameArenaActiveRewardData buleactive *cfg.GameArenaActiveRewardData err error ) if redactive, err = this.module.configure.getActiveRewardById(red.Dan); err != nil { this.module.Errorln(err) return } if buleactive, err = this.module.configure.getActiveRewardById(bule.Dan); err != nil { this.module.Errorln(err) return } if iswin { red.Changeintegral = int32(float64(redactive.KValue) * float64(1-1/float32(1+math.Pow(10, float64(float64(bule.Integral-red.Integral)/400))))) bule.Changeintegral = int32(float64(buleactive.KValue) * float64(0-1/float64(1+math.Pow(10, float64(float64(red.Integral-bule.Integral))/400)))) } else { red.Changeintegral = int32(float64(redactive.KValue) * float64(0-1/float64(1+math.Pow(10, float64(float64(bule.Integral-red.Integral)/400))))) bule.Changeintegral = int32(float64(redactive.KValue) * float64(1-1/float64(1+math.Pow(10, float64(float64(red.Integral-bule.Integral)/400))))) } if red.Integral+red.Changeintegral < 0 { red.Changeintegral = -red.Integral red.Integral = 0 } else { red.Integral = red.Integral + red.Changeintegral } if bule.Integral+bule.Changeintegral < 0 { bule.Changeintegral = -bule.Integral bule.Integral = 0 } else { bule.Integral = bule.Integral + bule.Changeintegral } } func (this *modelArena) recoverTicket(session comm.IUserSession, info *pb.DBArenaUser) { var ( duration time.Duration ticketitem *cfg.Gameatn ticket int32 ticketNum int32 ) if ticketitem = this.module.ModuleTools.GetGlobalConf().ArenaTicketCos; ticketitem == nil { this.module.Error("竞技场配置未找到!", log.Field{Key: "key", Value: "ArenaTicketCos"}) return } global := this.module.ModuleTools.GetGlobalConf() maxTick := global.ArenaTicketMax // 竞技场最大上限 maxTick += this.module.privilege.GetCountByPrivilegeId(session.GetUserId(), comm.PrivilegeType10) // 特权 ticket = int32(this.module.ModuleItems.QueryItemAmount(info.Uinfo.Uid, ticketitem.T)) if ticket < maxTick && info.Lastrtickettime > 0 { duration = configure.Now().Sub(time.Unix(info.Lastrtickettime, 0)) ticketNum = int32(math.Floor(duration.Minutes() / float64(global.ArenaTicketRecoveryTime))) if ticketNum > 0 { if ticketNum+ticket > global.ArenaTicketMax { ticketNum = global.ArenaTicketMax - ticket } this.module.DispenseRes(session, []*cfg.Gameatn{{A: ticketitem.A, T: ticketitem.T, N: ticketNum}}, true) info.Lastrtickettime = time.Unix(info.Lastrtickettime, 0).Add(time.Duration(ticketNum) * time.Minute).Unix() } } } // 更新埋点数据到db中 func (this *modelArena) getpandataModel() (model *arenaModel, err error) { var m *db.DBModel if !db.IsCross() { if m, err = this.module.GetCrossDBModel(this.TableName); err != nil { return } model = &arenaModel{module: this.module, model: m} } else { model = &arenaModel{module: this.module, model: this.DBModel} } return } //周结算 func (this *modelArena) weeksettlement(session comm.IUserSession, info *pb.DBArenaUser) { var ( conf *cfg.GameArenaActiveRewardData confs []*cfg.GameArenaActiveKingData totalnum int64 rank int32 reward []*cfg.Gameatn atno []*pb.UserAtno errdata *pb.ErrorData ok bool err error ) if utils.IsSameWeek(info.Lastweektime) { //跨周 if conf, err = this.module.configure.getMaxDanConf(); err != nil { return } if info.Dan == conf.LvId { //最高段位 if totalnum, err = this.module.modelRank.queryRankByScoreArea(conf.ScoreLow, -1); err != nil { this.module.Errorln(err) return } if info.Rank < int32(totalnum) { //存在于王者段位中 if confs, err = this.module.configure.getGameActiveKing(); err != nil { this.module.Errorln(err) return } for _, conf := range confs { rank += int32(math.Floor(float64(conf.Place) * float64(totalnum) / float64(1000))) if rank <= info.Dan { reward = conf.RewardWeek info.Dan2 = conf.LvId ok = true break } } } } if !ok { if conf, err = this.module.configure.getActiveRewardById(info.Dan); err != nil { return } reward = conf.ExReward info.Dan2 = 0 } if errdata, atno = this.module.DispenseAtno(session, reward, true); errdata != nil { this.module.Errorln(errdata) return } info.Weekaward = append(info.Weekaward, &pb.DBWeekAward{ Dan: info.Dan, Dan2: info.Dan2, Award: atno, }) session.SendMsg(string(this.module.GetType()), "settlementreward", &pb.ArenaSettlementRewardPush{Stype: 1, Award: atno}) } } // 比赛结算 func (this *modelArena) raceSettlement(session comm.IUserSession, info *pb.DBArenaUser) { var ( conf *cfg.GameArenaActiveRewardData err error ) if configure.Now().After(time.Unix(info.Settlementtime, 0)) { //结算赛季 if conf, err = this.module.configure.getActiveRewardById(info.Dan); err != nil { return } if conf.ScoreReturn >= 0 { info.Integral = conf.ScoreReturn info.Settlementtime = this.settlementTime().Unix() } } return } // 埋点专属模型 会封装特殊的数据转换接口 type arenaModel struct { module *Arena model *db.DBModel } // 查询用户装备数据 func (this *arenaModel) queryPlayerInfo(uId string) (result *pb.DBArenaUser, err error) { result = &pb.DBArenaUser{} if err = this.model.Get(uId, result); err != nil && err != mgo.MongodbNil { this.module.Errorln(err) return } return } func (this *arenaModel) reddot(session comm.IUserSession) (info *pb.DBArenaUser, ticket int32, activated bool) { var ( err error ) if info, err = this.queryPlayerInfo(session.GetUserId()); err != nil && err != mgo.MongodbNil { activated = false info = nil return } if err == mgo.MongodbNil { global := this.module.ModuleTools.GetGlobalConf() if global.ArenaTicketMax >= global.ArenaTicketCos.N { ticket = global.ArenaTicketMax activated = true return } activated = false } activated = true ticket = this.recoverTicket(session, info) return } func (this *arenaModel) recoverTicket(session comm.IUserSession, info *pb.DBArenaUser) int32 { var ( duration time.Duration ticketitem *cfg.Gameatn ticket int32 ticketNum int32 ) if ticketitem = this.module.ModuleTools.GetGlobalConf().ArenaTicketCos; ticketitem == nil { this.module.Error("竞技场配置未找到!", log.Field{Key: "key", Value: "ArenaTicketCos"}) return 0 } global := this.module.ModuleTools.GetGlobalConf() maxTick := global.ArenaTicketMax // 竞技场最大上限 maxTick += this.module.privilege.GetCountByPrivilegeId(session.GetUserId(), comm.PrivilegeType10) // 特权 ticket = int32(this.module.ModuleItems.QueryItemAmount(info.Uinfo.Uid, ticketitem.T)) if ticket < maxTick && info.Lastrtickettime > 0 { duration = configure.Now().Sub(time.Unix(info.Lastrtickettime, 0)) ticketNum = int32(math.Floor(duration.Minutes() / float64(global.ArenaTicketRecoveryTime))) if ticketNum > 0 { if ticketNum+ticket > global.ArenaTicketMax { ticketNum = global.ArenaTicketMax - ticket } this.module.DispenseRes(session, []*cfg.Gameatn{{A: ticketitem.A, T: ticketitem.T, N: ticketNum}}, true) info.Lastrtickettime = time.Unix(info.Lastrtickettime, 0).Add(time.Duration(ticketNum) * time.Minute).Unix() } } return ticket }