diff --git a/game-server/app/servers/battle/handler/pvpHandler.ts b/game-server/app/servers/battle/handler/pvpHandler.ts index 0d3686d2d..2e2d96a3e 100644 --- a/game-server/app/servers/battle/handler/pvpHandler.ts +++ b/game-server/app/servers/battle/handler/pvpHandler.ts @@ -1,13 +1,18 @@ import {Application, BackendSession} from 'pinus'; const _ = require('underscore'); import { gameData } from '../../../pubUtils/data'; -import { checkPvp, initPvpInfo, refreshEnemies, getEnemies, } from '../../../services/pvpService'; +import { checkPvp, initPvpInfo, refreshEnemies, getEnemies, getPlusScore, getLvByScore, } from '../../../services/pvpService'; import { RoleModel, RoleType } from '../../../db/Role'; import { STATUS } from '../../../consts/statusCode'; -import { resResult } from '../../../pubUtils/util'; +import { resResult, reduceCe, genCode } from '../../../pubUtils/util'; import { SystemConfigModel } from '../../../db/SystemConfig' -import { PvpDefenseModel } from '../../../db/PvpDefense'; +import { PvpDefenseModel, PvpDefenseType, OppPlayers } from '../../../db/PvpDefense'; +import { oppHeroesDefenseInter, pvpEndParamInter } from '../../../pubUtils/interface'; +import { HeroType } from '../../../db/Hero'; +import { CeAttrNumber } from '../../../db/generalField'; +import { checkBattleHeroesByHid } from '../../../services/normalBattleService'; +import { BattleRecordModel } from '../../../db/BattleRecord'; export default function(app: Application) { return new PvpHandler(app); } @@ -49,6 +54,192 @@ export class PvpHandler { pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers }); let result = getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum); - return resResult(STATUS.SUCCESS, { data: result }); + return resResult(STATUS.SUCCESS, { oppPlayers: result, refOppCnt: 0 }); + } + + // 获取对手阵容具体信息 + async getOppoPlayer (msg: { roleId: string }, session: BackendSession) { + let roleId = session.get('roleId'); + + let oppoRoleId = msg.roleId; + let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId); + let { oppPlayers } = pvpDefense; + let role = pvpDefense.role; + + let curOpp = oppPlayers.find(cur => cur.roleId == oppoRoleId); + if(!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND); + + let system = await SystemConfigModel.findSystemConfig(); + let mapWarJson = gameData.warJson.get(system.warId); + + let heroes = new Array(); + if(curOpp.isRobot) { // 机器人 + let { pos, robot } = curOpp; + let { warId } = robot; + let dicWarJson = gameData.warJson.get(warId); + let dicOpp = gameData.pvpOpponent.get(pos); + for(let json of dicWarJson) { + let curDicMapJson = mapWarJson.find(cur => cur.dataId == json.dataId); + const { actorId, actorName, attribute, skill, seid, star, spine, relation } = json; + if(relation == 2 && actorId > 0) { // 默认格子 + let newAttribute = new CeAttrNumber(); + for(let attrName in newAttribute) { + newAttribute[attrName] = Math.floor(attribute[attrName] * role.topFiveCe / 10000 * dicOpp.ratio); + } + newAttribute['speed'] = 0; + newAttribute['ap'] = 0; + + let heroInfo = { actorId, actorName, skill, seid, star, spine, attribute: newAttribute, lv: role.lv }; + heroes.push({ + ...curDicMapJson, ...heroInfo + }); + } + } + } else { // 真人 + let oppDef = curOpp.oppDef; + let role = oppDef.role; + let { globalCeAttr } = role; + for(let { actorId, hero, dataId, order } of oppDef.heroes) { + let curDicMapJson = mapWarJson.find(cur => cur.dataId == dataId); + let dicHero = gameData.hero.get(actorId); + let h = hero; + let { ceAttr } = h; + let newAttribute = new CeAttrNumber(); + for(let attrName in newAttribute) { + let { base, ratioUp, fixUp, equipUp } = ceAttr[attrName]; + let { ratioUp: ratioUp2, fixUp: fixUp2 } = globalCeAttr[attrName]; + let result = base * ( 1 + ratioUp + ratioUp2) + fixUp + fixUp2 + equipUp; + newAttribute[attrName] += reduceCe(result); + } + newAttribute['speed'] = 0; + newAttribute['ap'] = 0; + + let heroInfo = { outIndex: order, actorId, actorName: dicHero.name, skill:0, seid:'&', star: h.star, spine: 0, attribute: newAttribute, lv: h.lv }; + heroes.push({ + ...curDicMapJson, ...heroInfo + }); + } + + } + + return resResult(STATUS.SUCCESS, { roleId: oppoRoleId, pos: curOpp.pos, heroes }); + } + + // 开战 + async pvpStart (msg: { warId: number, heroes: number[], roleId: string }, session: BackendSession) { + const { warId, heroes, roleId: oppRoleId } = msg; + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + + let warInfo = gameData.war.get(warId); + if(!warInfo) { + return resResult(STATUS.BATTLE_MISS_INFO); + } + + let checkHeroes = await checkBattleHeroesByHid(roleId, heroes); + if(!checkHeroes) return resResult(STATUS.BATTLE_HERO_NOT_FOUND); + + const pvpDefense = await PvpDefenseModel.findByRoleId(roleId); + if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN); + let { oppPlayers } = pvpDefense; + let curOpp = oppPlayers.find(cur => cur.roleId == oppRoleId); + if(!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND) + + const battleCode = genCode(8); // 关卡唯一值 + + await BattleRecordModel.updateBattleRecordByCode(battleCode, { + $set: { + roleId, roleName, warId, + status: 0, + warName: warInfo.gk_name, + warType: warInfo.warType, + record: { heroes, pos: curOpp.pos, oppRoleId } + } + }, true); + + return resResult(STATUS.SUCCESS, { + battleCode + }); + } + + // 结算 + async pvpEnd (msg: { battleCode: string, isSuccess: boolean, myHeroes: pvpEndParamInter[], oppHeroes: pvpEndParamInter[] }, session: BackendSession) { + + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + + const { battleCode, isSuccess, myHeroes, oppHeroes } = msg; + + const BattleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode, true); + if(!BattleRecord || BattleRecord.status != 0) { + return resResult(STATUS.BATTLE_STATUS_WRONG); + } + + let flag = 1; // 对比hero信息 + let { record: { heroes: dbHeroes, oppRoleId, pos } } = BattleRecord; + for(let {hid} of myHeroes) { + if(dbHeroes.indexOf(hid) == -1) flag = 0; + } + if(!flag) { + return resResult(STATUS.BATTLE_INFO_VALIDATE_ERR); + } + + // 更新军功 + let pvpDefense = await PvpDefenseModel.findByRoleId(roleId); + if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN); + let { oppPlayers, winStreakNum, heroScores, score } = pvpDefense; + let curOpp = oppPlayers.find(cur => cur.roleId == oppRoleId && cur.pos == pos); + if(!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND); + + const dicOpp = gameData.pvpOpponent.get(pos); + let plusScore = getPlusScore(winStreakNum); + + let showHeroScores = new Array<{hid: number, addScore: number, plusScore: number, score: number}>(); + for(let { hid, damage, heal, hurt } of myHeroes) { + let curHeroScore = heroScores.find(cur => cur.hid == hid); + if(isSuccess) { + if(!curHeroScore) { + curHeroScore = { + hid, score: dicOpp.score + plusScore + }; + heroScores.push(curHeroScore); + } else { + curHeroScore.score += dicOpp.score + plusScore; + } + score += dicOpp.score + plusScore; + showHeroScores.push({ + hid, addScore: dicOpp.score, plusScore, score: curHeroScore.score + }); + winStreakNum ++; + } else { + showHeroScores.push({ + hid, addScore: 0, plusScore: 0, score: curHeroScore.score + }); + winStreakNum = 0; + } + } + let pLv = getLvByScore(heroScores); + + // 刷新对手 + const role = await RoleModel.findByRoleId(roleId); + let newOppPlayers: Array = await refreshEnemies(role, score, pLv); + + pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers: newOppPlayers, heroScores, score, pLv, winStreakNum }); + let result = getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum); + + + // 更新battleRecord + await BattleRecordModel.updateBattleRecordByCode(battleCode, { + $set: { status: isSuccess?1:2 } + }, true); + + // TODO 记录战报 + + return resResult(STATUS.SUCCESS, { + battleCode, isSuccess, + score, pLv, myRank: 0, + heroScores: showHeroScores, + oppPlayers: result + }); } } diff --git a/game-server/app/services/expeditionService.ts b/game-server/app/services/expeditionService.ts index 43df42f63..1fbe62e5d 100644 --- a/game-server/app/services/expeditionService.ts +++ b/game-server/app/services/expeditionService.ts @@ -76,6 +76,7 @@ export async function matchPlayers(roleId: string, scale: number, range: number, enemyObj.enemyId = roleId; enemyObj.ce = defCe; + // TODO 修改获取attribute let heroIndex = 0; for(let enemy of dicWarJson) { if(enemy.relation == 2) { diff --git a/game-server/app/services/normalBattleService.ts b/game-server/app/services/normalBattleService.ts index d2b3073f3..b9b3e902b 100644 --- a/game-server/app/services/normalBattleService.ts +++ b/game-server/app/services/normalBattleService.ts @@ -60,6 +60,18 @@ export async function checkBattleHeroes(roleId: string, heroes: Array) { return flag; } +export async function checkBattleHeroesByHid(roleId: string, heroes: Array) { + let flag = true; + if(heroes.length <= 0) flag = false; + + const findHeroes = await HeroModel.findByHidRange(heroes, roleId); + if(findHeroes.length != heroes.length) flag = false; + for(let hid of heroes) { + let hero = findHeroes.find(cur => cur.hid == hid); + if(!hero) flag = false; + } + return flag; +} export async function updateWarStar(roleId: string, battleId: number, warType: number, star: number) { let role = await RoleModel.findByRoleId(roleId); diff --git a/game-server/app/services/playerEventService.ts b/game-server/app/services/playerEventService.ts index 81450943d..4146e3f07 100644 --- a/game-server/app/services/playerEventService.ts +++ b/game-server/app/services/playerEventService.ts @@ -15,5 +15,12 @@ export async function eventOnPlayerLvUp(roleId: string, lv: number, addFuncs: Ar await checkPvp(role); addFuncs.push(FUNCS_ID.PVP); } + } + + if (!dataFuncs.includes(FUNCS_ID.EVENT)) {//开启奇遇 + let res = getFuncsSwitch(FUNCS_ID.EVENT); + if (!res || lv >= res.param) { + addFuncs.push(FUNCS_ID.EVENT); + } } } diff --git a/game-server/app/services/pvpService.ts b/game-server/app/services/pvpService.ts index 0ab62ea2a..d15383bc2 100644 --- a/game-server/app/services/pvpService.ts +++ b/game-server/app/services/pvpService.ts @@ -1,15 +1,15 @@ /** * 体力系统 */ -import { PvpDefenseModel, Heroes, OppPlayers, Robot, PvpDefenseType } from '../db/PvpDefense'; +import { PvpDefenseModel, Heroes, OppPlayers, Robot, PvpDefenseType, HeroScores } from '../db/PvpDefense'; import { RoleType } from '../db/Role'; import { PVP_HERO_POS, ROBOT_NAME } from '../consts'; import { dicPvpOpponent, DicPvpOpponent } from "../pubUtils/dictionary/DicPvpOpponent"; import { getRandomIndexByLen, genCode, getRandomByLen } from '../pubUtils/util'; import { oppPlayersInter } from '../pubUtils/interface'; -import { gameData } from "../pubUtils/data"; - +import { gameData, getPLvByScore } from "../pubUtils/data"; +import { PVP } from '../pubUtils/dicParam'; export async function initPvpInfo(role: RoleType) { let heroes: Array = []; @@ -56,6 +56,7 @@ export function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) { result.push({ pos, roleId, roleName, headHid, sHid, pLv, defCe, addScore: dicOpponent.score, + rankLv: 0, plusScore: getPlusScore(winStreakNum) }); } else { @@ -66,6 +67,7 @@ export function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) { result.push({ pos, roleId, roleName, headHid, sHid, pLv, defCe, addScore: dicOpponent.score, + rankLv: 0, // TODO读取排名 plusScore: getPlusScore(winStreakNum) }); } @@ -105,10 +107,11 @@ async function matchPlayer(oppPlayers: OppPlayers[], roleId: string, pLv: number range.splice(index, 1); if(range.length <= 0) return false; index = getRandomIndexByLen(range.length); - result = range[range.length]; + result = range[index]; } oppPlayers.push({ + roleId: result.roleId, oppDef: result._id, pos, isRobot: false, @@ -128,8 +131,11 @@ function matchRobot(oppPlayers: OppPlayers[], myCe: number, pLv: number, dicOpp: let roleId = generateRobotRoleId(); let roleName = getRandomByLen(ROBOT_NAME); - let robot = new Robot(roleId, roleName, Math.floor(myCe * ratio), Math.floor(pLv + (minLv + maxLv)/2), result.war_id) + let hisPLv = Math.floor(pLv + (minLv + maxLv)/2); + if(hisPLv < 1) hisPLv = 1 + let robot = new Robot(roleId, roleName, Math.floor(myCe * ratio), hisPLv, result.war_id) oppPlayers.push({ + roleId, oppDef: null, pos, isRobot: true, @@ -151,6 +157,17 @@ export function checkRoleIsRobot(roleId: string) { // 根据连胜次数,获得加成的积分 export function getPlusScore(win: number) { let result = win - 1; - if(result > 10) result = 10; + if(result < 0) result = 0; + if(result > PVP.PVP_WINREWARD_UPLIMIT) result = PVP.PVP_WINREWARD_UPLIMIT; return result; +} + +export function getLvByScore(heroScores: HeroScores[]) { + heroScores.sort((a, b) => b.score - a.score); + let score = 0; + for(let i = 0; i < 5; i++) { + if(!heroScores[i]) break; + score += heroScores[i].score; + } + return getPLvByScore(score); } \ No newline at end of file diff --git a/gm-server/app/service/users.ts b/gm-server/app/service/users.ts index a82a253cb..b59ee367b 100644 --- a/gm-server/app/service/users.ts +++ b/gm-server/app/service/users.ts @@ -171,7 +171,7 @@ export default class GMUsers extends Service { let {uid, tel} = role.userInfo; result.push({ key: roleId, roleId, roleName, serverId, lv, vLv, uid, tel, heroCount, equipCount, itemCount, gold, coin, - hasDefense: defense?'是':'否', defCe: defense?defense.ce:0 + hasDefense: defense?'是':'否', defCe: defense?defense.defCe:0 }); } return ctx.service.utils.resResult(STATUS.SUCCESS, { list: result }); @@ -378,7 +378,7 @@ export default class GMUsers extends Service { }) } - public async saveHeroToDefense(roleId: string, roleName: string, hid: number) { + public async saveHeroToDefense(roleId: string, _roleName: string, hid: number) { const {ctx} = this; let hero = await HeroModel.findByHidAndRole(hid, roleId); let role = await RoleModel.findByRoleId(roleId); @@ -406,7 +406,7 @@ export default class GMUsers extends Service { let defense = await PvpDefenseModel.findByRoleId(roleId); if(!defense) { - defense = await PvpDefenseModel.createPvpDefense({roleId, roleName, heroes: [heroInfo], ce}); + // defense = await PvpDefenseModel.createPvpDefense({roleId, roleName, heroes: [], defCe:ce}); } else { defense = await PvpDefenseModel.addHeroToDefense(roleId, heroInfo, ce); } diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 607bb04cf..97d7bc309 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -105,6 +105,10 @@ export const STATUS = { COM_BATTLE_REWARDED: { code: 20628, simStr: '奖励已领取' }, COM_BATTLE_HEROES_ERR: { code: 20629, simStr: '阵容异常' }, + // PVP 20700-20799 + PVP_ROLE_NOT_FOUND: { code: 20700, simStr: '未找到该对手' }, + PVP_NOT_OPEN: { code: 20701, simStr: 'pvp尚未开启' }, + // 共斗藏宝图合成 COM_BLUEPRT_QUALITY_CANNOT_COMPOSE: { code: 20630, simStr: '该品质藏宝图不可合成' }, COM_BLUEPRT_COUNT_ERROR: { code: 20631, simStr: '材料数量不足' }, diff --git a/shared/db/BattleRecord.ts b/shared/db/BattleRecord.ts index 60c680837..948c90a17 100644 --- a/shared/db/BattleRecord.ts +++ b/shared/db/BattleRecord.ts @@ -7,6 +7,15 @@ import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoos @index({ roleId: 1, battleId: 1 }) @index({ battleCode: 1 }) +class Record { + @prop({ required: true, type: Number }) + heroes: Array ; // 武将id + @prop({ required: false }) + oppRoleId?: string; // pvp对手 + @prop({ required: false }) + pos?: number; // pvp位置 +} + export default class BattleRecord extends BaseModel { @prop({ required: true }) roleId: string; // 角色 id @@ -26,10 +35,8 @@ export default class BattleRecord extends BaseModel { status: number; // 关卡状态 0-挑战中 1-挑战成功 2-挑战失败 @prop({ required: true, default: 0 }) star: number; // 关卡状态 0-挑战中 1-挑战成功 2-挑战失败 - @prop({ required: true }) - record: { // 使用的武将等记录 - heroes: Array ; // 武将id - }; + @prop({ required: true, _id: false }) + record: Record; // 使用的武将等记录 public static async getBattleRecordByCode(battleCode: string, lean = true) { const result: BattleRecordType = await BattleRecordModel.findOne({ battleCode }).lean(lean); diff --git a/shared/db/Hero.ts b/shared/db/Hero.ts index 8816abfb8..13deccb4a 100644 --- a/shared/db/Hero.ts +++ b/shared/db/Hero.ts @@ -153,6 +153,12 @@ export default class Hero extends BaseModel { const hero: HeroType[] = await HeroModel.find({ seqId: {$in: seqIds}, roleId }).lean(lean); return hero; } + + public static async findByHidRange(hids: Array, roleId: string, lean = true) { + const hero: HeroType[] = await HeroModel.find({ hid: {$in: hids}, roleId }).lean(lean); + return hero; + } + public static async findBySeqIdAndRole(seqId: number, roleId: string, lean = true) { const hero: HeroType = await HeroModel.findOne({ seqId, roleId }).lean(lean); return hero; diff --git a/shared/db/PvpDefense.ts b/shared/db/PvpDefense.ts index 637899124..f0b7e618d 100644 --- a/shared/db/PvpDefense.ts +++ b/shared/db/PvpDefense.ts @@ -10,6 +10,8 @@ interface pvpUpdateInter { oppPlayers?: OppPlayers[]; score?: number; pLv?:number; + heroScores?: HeroScores[]; + winStreakNum?: number; } export class Heroes { @@ -74,6 +76,8 @@ export class Robot { } export class OppPlayers { + @prop({ required: true }) + roleId: string; @prop({ ref: 'PvpDefense', type: mongoose.Schema.Types.ObjectId }) oppDef: Ref; @prop({ required: true }) @@ -84,7 +88,7 @@ export class OppPlayers { robot: Robot } -class HeroScores { +export class HeroScores { @prop({ required: true }) hid: number; @prop({ required: true }) @@ -180,8 +184,8 @@ export default class PvpDefense extends BaseModel { .populate({ path: 'oppPlayers.oppDef', populate: { - path: 'role', - select: 'headHid sHid topFiveCe roleId roleName' + path: 'role heroes.hero', + select: 'headHid sHid topFiveCe roleId roleName hid ceAttr globalCeAttr' } }).lean(); return result; diff --git a/shared/pubUtils/dicParam.ts b/shared/pubUtils/dicParam.ts index 76b782f41..bb660521f 100644 --- a/shared/pubUtils/dicParam.ts +++ b/shared/pubUtils/dicParam.ts @@ -33,4 +33,5 @@ export const PVP = { PVP_CHALLENGE_FINALTIMES: 60, // PVP赛季最后一天挑战次数刷新时间(分钟) PVP_SEASON_DAYS: 30, // PVP一个赛季的时间 PVP_OPPONENT_FREEREFRESH: 3, // pvp挑战对手每日免费刷新次数 + PVP_WINREWARD_UPLIMIT: 10, // 连胜奖励军功加成上限 }; diff --git a/shared/pubUtils/interface.ts b/shared/pubUtils/interface.ts index 18c70de01..ed0ba1e51 100644 --- a/shared/pubUtils/interface.ts +++ b/shared/pubUtils/interface.ts @@ -66,4 +66,33 @@ export interface oppPlayersInter { defCe: number; // 防守阵容战力 addScore: number; // 战胜后可获军功 plusScore: number; // 连胜加成军功 + rankLv: number; +} + +export interface oppHeroesDefenseInter { + actorId: number; // 武将id + actorName: string; // 武将名 + dataId: number; // 出兵表唯一id + relation: number; // 地方还是我方 + direction: number; // 方向 + outIndex: number; // 玩家设置的出场顺序,即order字段 + x: number; // 战场x坐标 + y: number; // 战场y坐标 + var: number; // 变量 + lv: number; // 等级 + hide: number; // 是否隐藏 + initial_ai: number; // ai类型 + attribute: Attributes; + star: number; // 星级 + skill: string|number; // 技能 + seid: string; // 技能 + spine: string|number; // 动画 + +} + +export interface pvpEndParamInter { + hid: number; + damage: number; + heal: number; + hurt: number; } \ No newline at end of file