import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; import { EXTERIOR } from '../pubUtils/dicParam'; import { nowSeconds } from '../pubUtils/timeUtil'; import { RoleUpdate } from './Role'; import { COM_TEAM_STATUS } from '../consts'; export class ComRoleStatusHero { @prop({ required: true }) id: number; @prop({ required: true }) skinId: number; @prop({ required: true }) quality: number; @prop({ required: true }) star: number; @prop({ required: true }) colorStar: number; @prop({ required: true }) lv: number; @prop({ required: true }) damage: number = 0; constructor(hero: { hid: number, skinId: number, quality: number, star: number, colorStar: number, lv: number }) { this.id = hero.hid; this.skinId = hero.skinId; this.quality = hero.quality; this.star = hero.star; this.colorStar = hero.colorStar; this.lv = hero.lv; } addDamage(damage: number) { this.damage += damage; } } export class ComBattleReward { @prop({ required: true }) type: number; @prop({ required: true }) id: number; @prop({ required: true }) count: number; } export class RoleStatus { @prop({ required: true }) roleId: string; @prop({ required: true }) roleName: string; @prop({ required: true }) sid: string; @prop({ required: true }) guildCode: string; @prop({ required: true }) isCap: boolean; @prop({ required: true, default: 0 }) totalDmg: number; // 战斗状态 0-挑战中 1-挑战成功 2-挑战失败 @prop({ required: true, default: 0 }) battleStatus: number; // 队伍中每个玩家的战斗编号 @prop({ required: false, default: '' }) battleCode: string; // 玩家所用阵容 @prop({ required: false, type: ComRoleStatusHero, default: [], _id: false }) heroes: ComRoleStatusHero[]; // 阵亡武将 @prop({ required: false, type: Number, default: [] }) killed: number[]; // 是否情谊助阵 @prop({ required: false, default: false }) isFrd: boolean; // 头像 @prop({ required: true, default: EXTERIOR.EXTERIOR_FACE }) head: number; // 相框 @prop({ required: true, default: EXTERIOR.EXTERIOR_FACECASE }) frame: number; // 形象 @prop({ required: true, default: EXTERIOR.EXTERIOR_APPEARANCE }) spine: number; // 前五战力 @prop({ required: true, default: 0 }) topLineupCe: number; // 主公等级 @prop({ required: true, default: 1 }) lv: number; // 是否机器人 @prop({ required: true, default: false }) isRobot: boolean; // 是否领奖 @prop({ required: true, default: false }) gotReward: boolean; // 队长奖励 @prop({ required: true, default: [], type: ComBattleReward, _id: false }) rewards: ComBattleReward[]; // 好友间伤害加成 @prop({ required: true, default: 0 }) frdRatio: number = 0; @prop({ required: true, default: 0 }) startActionTime: number = 0; constructor(role: RoleUpdate, sid: string, isCap: boolean, isFrd: boolean, heroes: ComRoleStatusHero[] = [], isRobot = false) { if(role) { this.roleId = role.roleId||''; this.roleName = role.roleName||''; this.guildCode = role.guildCode||''; this.head = role.head||EXTERIOR.EXTERIOR_FACE; this.frame = role.frame||EXTERIOR.EXTERIOR_FACECASE; this.spine = role.spine||EXTERIOR.EXTERIOR_APPEARANCE; this.topLineupCe = role.topLineupCe||0; this.lv = role.lv||1; } this.sid = sid; this.isCap = isCap; this.totalDmg = 0; this.isFrd = isFrd; this.heroes = heroes; this.killed = []; this.isRobot = isRobot; this.rewards = []; } addFrdRatio(frdRatio: number) { if(!this.frdRatio) { this.frdRatio = frdRatio; } if(this.frdRatio < frdRatio) { this.frdRatio = frdRatio; } } addHeroDamage(hid: number, damage: number) { let hero = this.heroes.find(cur => cur.id == hid); if(hero) hero.addDamage(damage); } } export class BossHp { // boss 战场标识 @prop({ required: true, default: 0 }) dataId: number; // boss 战场标识 @prop({ required: true, default: 0 }) actorId: number; // 总血量 @prop({ required: true, default: 0 }) hp: number; // 当前血量 @prop({ required: true, default: 0 }) curHp: number; } @index({ teamCode: 1 }) @index({ roleIds: 1 }) @index({ capId: 1 }) @index({ status: 1, blueprtId: 1, pub: 1}) @index({ status: 1, inviteTarget: 1}) export default class ComBattleTeam extends BaseModel { // 队伍唯一编号 @prop({ required: true }) teamCode: string; @prop({ required: true, type: String, default: [], _id: false }) roleIds: string[]; // 队伍是否开放加入 @prop({ required: true, default: true }) pub: boolean; // 对应藏宝图 Id @prop({ required: true }) blueprtId: number; // 藏宝图品阶,用来匹配 @prop({ required: true }) lv: number; // 战斗状态 0:未开始,1:已开始,2:胜利,3:失败 @prop({ required: true, default: 0 }) status: number; @prop({ required: true, type: RoleStatus, default: [], _id: false }) roleStatus: RoleStatus[]; // 队长 roleId @prop({ required: true }) capId: string; // 战力限制 @prop({ required: true, default: 0 }) ceLimit: number; // 队伍中人数 @prop({ required: true, default: 1 }) roleCnt: number; // 单个 boss 血量状态 @prop({ required: false, type: BossHp, default: [], _id: false }) bossHpArr: BossHp[]; // 是否超时 @prop({ required: true, default: false }) timeout: boolean; // 是否撤退 @prop({ required: true, default: false }) retreat: boolean; // 黑名单 blacklist: string[] = []; // 开始时间 @prop({ required: true, default: 0 }) startTime: number = 0; // 是否有特殊时间奖励 @prop({ required: true, default: 0 }) hasTimeExtraReward: boolean = false; // 结束时间 @prop({ required: true, default: 0 }) endTime: number = 0; // 是否发出邀请了 @prop({ required: true, default: false }) isInviting: boolean = false; // 邀请时间 @prop({ required: true, default: 0 }) inviteTime: number = 0; // 邀请对象 @prop({ required: true, default: [], type: String, _id: false }) inviteTarget: string[] = []; public static async createTeam(teamData: ComBattleTeamParam, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamData.teamCode }, {$set :{...teamData, roleCnt: teamData.roleIds.length}}, {upsert: true, new: true}).lean(lean); return team; } public static async findByRoleId(roleId: string) { const team: ComBattleTeamType = await ComBattleTeamModel.findOne({ roleIds: { $in: [roleId] }}).lean(); return team; } public static async checkHasTeamByRoleId(roleId: string) { return await ComBattleTeamModel.exists({ roleIds: { $in: [roleId] }, status: COM_TEAM_STATUS.FIGHTING }); } public static async addRole(teamCode: string, roleStatus: RoleStatus, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, roleCnt: { $lte: 2 } }, {$push: {roleIds: roleStatus.roleId, roleStatus}, $inc: {roleCnt: 1}}, {new: true}).lean(lean); return team; } public static async removeRole(teamCode: string, roleIdToRm: string, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode }, {$pull: {roleIds: roleIdToRm, roleStatus: {roleId: roleIdToRm}}, $inc: {roleCnt: -1}}, {new: true}).lean(lean); return team; } public static async updateRoleStFrd(teamCode: string, roleId: string, isFrd: boolean, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId }, { $set: {'roleStatus.$.isFrd': isFrd}}, {new: true}).lean(lean); return team; } public static async removeTeam(teamCode: string, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndDelete({ teamCode }).lean(lean); return team; } public static async updateHeroes(teamCode: string, roleId: string, heroes: ComRoleStatusHero[], lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.heroes': heroes}}, {new: true}).lean(lean); return team; } public static async setupBattleInfo(teamCode: string, roleId: string, heroes: ComRoleStatusHero[], battleCode: string, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.heroes': heroes, 'roleStatus.$.battleCode': battleCode}}, {new: true}).lean(lean); return team; } public static async updateRewardSt(teamCode: string, roleId: string, gotReward: boolean, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode, 'roleStatus.roleId': roleId}, {$set: {'roleStatus.$.gotReward': gotReward}}).lean(lean); return team; } public static async syncTeamData(teamData: {teamCode: string, status: number, roleStatus: RoleStatus[], bossHpArr: BossHp[], endTime?: number}, timeout = false, retreat = false) { // console.log('syncTeamData bossHpArr: ', teamData.bossHpArr); const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamData.teamCode }, {$set :{...teamData, roleCnt: teamData.roleStatus.length, timeout, retreat}}, {new: true}).lean(); return team; } public static async updateStatusByCode(teamCode: string, status: number, startTime: number, endTime: number, hasTimeExtraReward: boolean) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({ teamCode: teamCode }, { $set: {status, startTime, endTime, hasTimeExtraReward }}, {new: true}).lean(); return team; } public static async updateStatusByStatus(preStatus: number, newStatus: number, lean = true) { const team = await ComBattleTeamModel.updateMany({ status: preStatus }, {status: newStatus}, {new: true}).lean(lean); return team; } public static async updateResult(teamCode: string, roleId: string, isSuccess: boolean, _lean = true) { let team = await ComBattleTeamModel.findOne({teamCode}).select('status roleStatus'); if(!team) return; if (isSuccess === true) { team.status = 2; team.roleStatus.forEach(st => { if (st.battleStatus !== 2) { st.battleStatus = 1; } }) } else if (isSuccess === false) { let loseCnt = 1; team.roleStatus.forEach(st => { if (st.battleStatus === 2 && st.roleId !== roleId) { loseCnt += 1; } else if (st.roleId === roleId && !st.battleStatus) { st.battleStatus = 2; } }); if (loseCnt === team.roleStatus.length) { team.status = 3; } } await team.save(); return team; } public static async getTeamByCapAndStatus(roleId: string, status: number, lean = true) { const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({capId: roleId, status}).lean(lean); return teams; } public static async getBlueprtInUse(roleId: string, status: number, blueprtId: number, lean = true) { const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({capId: roleId, status, blueprtId}).lean(lean); return teams; } public static async getTeamByBlueprt(blueprtIds: number[], status: number, pub = true, limit = 50, lean = true) { const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({blueprtId: {$in: blueprtIds}, status, pub}).limit(limit).lean(lean); return teams; } public static async getOneTeamByQualityAndSt(qualityArr: number[], status: number, ce = 0, pub = true, cntLmt = 2, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOne({quality: {$in: qualityArr}, status, ceLimit: {$lte: ce}, pub, roleCnt: {$lte: cntLmt}}).lean(lean); return team; } public static async getOtherTeamByLvAndSt(roleId: string, lvs: number[], status: number, ce = 0, pub = true, cntLmt = 2, lean = true) { const curTime = new Date(Date.now() - 10 * 60 * 1000); // 10分钟之前 const team: ComBattleTeamType[] = await ComBattleTeamModel.find({ lv: { $in: lvs }, status, ceLimit: {$lte: ce}, pub, roleCnt: {$lte: cntLmt}, roleIds: {$nin: [roleId]}, updatedAt: {$gte: curTime}}).lean(lean); return team; } public static async getTeamByRoleAndTime(roleId: string, qualityArr?: number[], time?: Date, isAssist?: boolean, limitCnt = 50, lean = true) { let query = {roleIds: roleId}; if (qualityArr) { query = Object.assign(query, {quality: {$in: qualityArr}}); } if (time) { query = Object.assign(query, {createdAt: {$gte: time}}); } if (isAssist) { query = Object.assign(query, {capId: {$ne: roleId}}); } const teams: ComBattleTeamType[] = await ComBattleTeamModel.find(query).limit(limitCnt).sort({createdAt: -1}).lean(lean); return teams; } public static async getAssistTeamsByTime(roleId: string, time?: Date, isAssist?: boolean, lean = true) { let query = {roleIds: roleId, status: {$in: [0, 1, 2]}}; // 失败不计入助战 if (time) { query = Object.assign(query, {createdAt: {$gte: time}}); } if (isAssist) { query = Object.assign(query, {capId: {$ne: roleId}}); } const teams: ComBattleTeamType[] = await ComBattleTeamModel.find(query).lean(lean); return teams; } public static async getCapExtraRewardCnt(roleId: string, time: Date) { const teams: ComBattleTeamType[] = await ComBattleTeamModel.find({ capId: roleId, createdAt: { $gte: time }, status: {$in: [0, 1, 2]}, hasTimeExtraReward: true }).lean(); return teams; } public static async getTeamByRoleAndBattleCode(roleId: string, battleCode: string, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOne({roleIds: roleId, 'roleStatus.battleCode': battleCode}).lean(lean); return team; } public static async getTeamByCode(teamCode: string, lean = true) { const team: ComBattleTeamType = await ComBattleTeamModel.findOne({teamCode}).lean(lean); return team; } public static async invite(teamCode: string, target: string) { const team: ComBattleTeamType = await ComBattleTeamModel.findOneAndUpdate({teamCode}, { $set: { isInviting: true, inviteTime: nowSeconds()}, $push: { inviteTarget: target }}, {new: true}).lean(); return team; } public static async findInvitations(roleId: string, guildCode: string, minLv: number, maxLv: number, ce: number, refreshTime: number, limit: number) { const invitations: ComBattleTeamType[] = await ComBattleTeamModel.find({ inviteTarget: { $in: [guildCode, roleId] },status: 0, isInviting: true, lv: { $gte: minLv, $lte: maxLv }, ceLimit: { $lte: ce }, inviteTime: { $gte: refreshTime } }).sort({ inviteTime: -1 }).limit(limit).lean(); return invitations; } } export const ComBattleTeamModel = getModelForClass(ComBattleTeam); export interface ComBattleTeamType extends Pick, keyof ComBattleTeam>{}; export type ComBattleTeamParam = Partial;