初步加入天梯接口

This commit is contained in:
liangtongchuan
2020-10-12 23:42:53 +08:00
parent 77c2b0b35a
commit d4a6ad42d1
10 changed files with 395 additions and 13 deletions

View File

@@ -7,7 +7,7 @@ import { getAp, setAp, WarReward } from './battleUtils';
import { WAR_TYPE, EVENT_START_BATTLE } from '../../../consts/consts';
import { checkDaily, checkDailyAndIncrease } from './dailyBattleHandler';
import { setBattleStatus, startEvent } from './eventBattleHandler';
import { queryMethod } from '@typegoose/typegoose';
import { checkTowerWar, towerBattleEnd } from '../../../services/battleService';
export default function(app: Application) {
return new NormalBattleHandler(app);
@@ -44,12 +44,19 @@ export class NormalBattleHandler {
}
let dailyNum = {};
let towerData = {};
if(warInfo.warType == WAR_TYPE.DAILY) {
let checkResult = await checkDaily(roleId, battleId, 1);
if(checkResult.status == -1) {
return {code: 202, data: checkResult.msg}
}
dailyNum = { type: checkResult.type, count: checkResult.count, sum: checkResult.sum };
} else if (warInfo.warType == WAR_TYPE.TOWER) {
let checkResult = await checkTowerWar(roleId, battleId);
if(checkResult.status) {
return {code: 202, data: checkResult.msg}
}
towerData = Object.assign(towerData, checkResult.data);
}
const battleCode = genCode(8);
const BattleRecord = await BattleRecordModel.updateBattleRecordByCode(battleCode, {
@@ -68,7 +75,7 @@ export class NormalBattleHandler {
return {
code: 200,
data: {
battleId, battleCode, status, apJson, dailyNum
battleId, battleCode, status, apJson, dailyNum, towerData
}
}
}
@@ -96,6 +103,7 @@ export class NormalBattleHandler {
const { battleCode, battleId, isSuccess, heroes, star } = msg;
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
let warInfo = getWarById(battleId);
if(!warInfo) {
return { code: 202, data: "缺少关卡信息" }
@@ -124,7 +132,12 @@ export class NormalBattleHandler {
return { code: 202, data: '体力不足' }
}
let channelService = this.app.get('channelService');
let dailyNum = {};
let towerRec = null;
let towerBonus = null;
let towerStatus = null;
if(warInfo.warType == WAR_TYPE.DAILY) {
let checkResult = await checkDailyAndIncrease(roleId, battleId, 1, false);
if(checkResult.status == -1) {
@@ -133,7 +146,15 @@ export class NormalBattleHandler {
dailyNum = { type: checkResult.type, count: checkResult.count, sum: checkResult.sum };
} else if (warInfo.warType == WAR_TYPE.EVENT) {
// 记录事件状态
await setBattleStatus(this.app, session, roleId, battleId, isSuccess, battleCode);
await setBattleStatus(roleId, battleId, isSuccess, battleCode, session.get('eventStatus'));
} else if (warInfo.warType == WAR_TYPE.TOWER) {
let towerEndResult = await towerBattleEnd(channelService, sid, roleId, battleCode, battleId, isSuccess, heroes);
if(towerEndResult.status == -1) {
return {code: 202, data: towerEndResult.msg};
}
towerRec = towerEndResult.data.newRec;
towerBonus = towerEndResult.data.bonusReward;
towerStatus = towerEndResult.data.towerStatus;
}
let warReward = new WarReward(roleId, roleName, battleId, isSuccess);
@@ -176,11 +197,14 @@ export class NormalBattleHandler {
// console.log('*******startEvent')
await startEvent(this.app, session);
}
// 返回值:
// towerStatus: false-本层未通过, true-本层已通过
// towerBonus: 天梯每隔几层的额外宝箱
return {
code: 200,
data: {
battleCode, battleId, status,
goods: reward,
battleCode, battleId, status, towerStatus,
goods: reward, towerBonus,
apJson,
dailyNum
}

View File

@@ -1,6 +1,12 @@
import { HANG_UP_CONSTS } from './../../../../../shared/consts/consts';
import { HangUpRecordModel } from './../../../db/HangUpRecord';
import { RoleModel } from './../../../db/Role';
import { TowerRecordModel } from './../../../../../shared/db/TowerRecord';
import { Application, BackendSession } from 'pinus';
import Role from '../../../db/Role';
import { getTowerDataByLv } from '../../../util/gamedata';
import { decodeArrayStr, decodeIdCntArrayStr } from '../../../util/util';
import { calcuHangUpReward } from '../../../services/battleService';
import { handleFixedReward } from '../../../services/rewardService';
export default function(app: Application) {
return new TowerBattleHandler(app);
@@ -10,18 +16,87 @@ export class TowerBattleHandler {
constructor(private app: Application) {
}
/**
* 获取天梯当前挑战状态
* @param session
*/
async getStatus(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let { towerLv } = await Role.findByRoleId(roleId);
let { towerLv } = await RoleModel.findByRoleId(roleId);
let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv);
let data = towerRec ? {
if (!towerRec) {
return { code: 201, data: '天梯记录异常' };
}
const data = {
curLv: towerLv,
usedHeroes: towerRec.heroes,
progress: towerRec.warStatus
} : '天梯记录异常';
};
return {
code: 200,
data
}
}
/**
* 重置天梯当前层的挑战记录
* @param towerLv 要重置的天梯层数
* @param session
*/
async resetLv(msg: {towerLv: number}, session: BackendSession) {
let roleId = session.get('roleId');
let { towerLv } = await RoleModel.findByRoleId(roleId);
if (msg.towerLv !== towerLv) {
return {code: 201, data: '只能重置当前层'};
}
const record = await TowerRecordModel.resetRecordByLv(roleId, towerLv);
if (!record) {
return { code: 201, data: '重置错误'};
}
return { code: 200, data: { record } };
}
async checkHangUpRewards(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
const { multi, timeReward } = await calcuHangUpReward(roleId);
if (multi == 0) {
return {
code: 201,
data: {
msg: '尚未到可收取时间',
}
}
}
const rewards = decodeIdCntArrayStr(timeReward, multi);
return {
code: 200,
data: {
msg: '成功',
rewards: Array.from(rewards, item => ({[item[0]]: item[1]}))
}
};
}
async recHangUpRewards(msg: {speedUp: boolean, speedUpCnt: number}, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
const { multi, timeReward, endLv, endTime } = await calcuHangUpReward(roleId);
if (multi == 0) {
return {
code: 201,
data: {
msg: '尚未到可收取时间',
}
}
}
const goods = await handleFixedReward(roleId, roleName, timeReward, multi);
const newRec = await HangUpRecordModel.updateRec(roleId, roleName, endLv, endTime);
return {
code : 200,
data: {
msg: '成功',
goods, hangUpRec: newRec
}
}
}
}

View File

@@ -0,0 +1,105 @@
import { HangUpRecordModel } from './../../../shared/db/HangUpRecord';
import { ChannelService } from 'pinus';
import { HANG_UP_CONSTS } from './../consts/consts';
import { BattleRecordModel } from './../../../gm-server/app/db/BattleRecord';
import { TowerRecordModel } from './../../../shared/db/TowerRecord';
import { RoleModel } from './../db/Role';
import { getTowerDataByLv } from "../util/gamedata"
import { decodeArrayStr } from '../util/util';
import { handleFixedReward } from './rewardService';
export async function checkTowerWar(roleId: string, battleId: number) {
const battleIdStr = `${battleId}`;
let { towerLv } = await RoleModel.findByRoleId(roleId);
const towerInfo = getTowerDataByLv(towerLv);
if (!towerInfo) {
console.error(`天梯层数异常lv ${towerLv} by ${roleId}`);
return { status: -1, msg: '天梯层数异常' };
}
const warArray = decodeArrayStr(towerInfo.warArray) || [];
if (warArray.indexOf(battleIdStr) === -1) {
return { status: -1, msg: '战斗 Id 异常' };
}
let { heroes = [], warStatus = [] } = await TowerRecordModel.getRecordByLv(roleId, towerLv);
const curWarStatus = warStatus.find(elem => elem.warId == battleId);
if (curWarStatus.status) {
return {status: -1, msg: '天梯关卡不能重复挑战'};
}
return { status: 0, data: {
heroes, warStatus: curWarStatus
}};
}
export async function towerBattleEnd(channelService: ChannelService, sid: string, roleId: string, battleCode: string, battleId: number, succeed: boolean, heroes: Array<number>) {
if (succeed) {
let battleRec = await BattleRecordModel.getBattleRecordByCode(battleCode);
if (battleRec.battleId != battleId) {
return { status: -1, msg: '战斗数据错误' };
}
let { towerLv, roleName } = await RoleModel.findByRoleId(roleId);
let { warStatus, heroes: recHeroes } = await TowerRecordModel.getRecordByLv(roleId, towerLv);
for (let hid of heroes) {
if (recHeroes.indexOf(hid) !== -1) {
return { status: -1, msg: '使用了重复的武将' };
}
}
let inc = 1;
warStatus.forEach(st => {
if (st.warId !== battleId && st.status === false) {
inc = 0;
}
})
let newRec = await TowerRecordModel.updateRecord(roleId, towerLv, battleCode, battleId, heroes, inc);
let bonusReward = null;
if (inc === 1) {
await RoleModel.towerLvUp(roleId);
const nextTowerInfo = getTowerDataByLv(towerLv + 1);
if (nextTowerInfo) {
const { wars } = nextTowerInfo;
const sts = wars.map(id => {
return {warId: id, status: false};
});
await TowerRecordModel.createRecord({roleId, lv: towerLv + 1, warStatus: sts});
}
const { bonus } = getTowerDataByLv(towerLv);
if (bonus) {
bonusReward = await handleFixedReward(roleId, roleName, bonus, 1);
}
if (towerLv + 1 >= HANG_UP_CONSTS.ENABLE_LV) {
await startHangUp(roleId, roleName);
channelService.pushMessageByUids('hangUpEnable', {code: 200, data: {enable: true}}, [{uid: roleId, sid}]);
}
}
return {
status: 0,
data: {
newRec,
bonusReward,
towerStatus: !!inc
}
};
}
}
async function startHangUp(roleId: string, roleName: string) {
await HangUpRecordModel.initRecord(roleId, roleName);
}
export async function calcuHangUpReward(roleId: string) {
let { towerLv } = await RoleModel.findByRoleId(roleId);
let { startLv, startTime} = await HangUpRecordModel.getCurRec(roleId);
let towerInfo = getTowerDataByLv(towerLv - 1); // towerLv 是当前层,奖励计算按照已经通过的层,即上一层
const curTime = new Date();
let deltaTime = curTime.getTime() - startTime.getTime();
if (deltaTime > HANG_UP_CONSTS.MAX_TIME) deltaTime = HANG_UP_CONSTS.MAX_TIME;
const multi = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME);
return {
endLv: towerLv - 1,
endTime: new Date(startTime.getTime() + multi * HANG_UP_CONSTS.UNIT_TIME),
timeReward: towerInfo.timeReward,
multi
};
}

View File

@@ -0,0 +1,46 @@
import { BATTLE_REWARD_TYPE, GOOD_TYPE } from './../../../shared/consts/consts';
import { EquipModel } from './../db/Equip';
import { CounterModel } from './../db/Counter';
import { decodeStr } from '../util/util';
import { getGoodById } from '../util/gamedata';
export async function handleFixedReward(roleId: string, roleName: string, rewardStr: string, multi: number) {
let reward = decodeStr('fixReward', rewardStr);
let rewards = [];
for(let obj of reward)
rewards.push({type: BATTLE_REWARD_TYPE.FIX_REWARD, ...obj, count: obj.count});
let returnGoods = new Array();
for(let goods of rewards) {
let goodInfo = getGoodById(goods.gid);
if(goodInfo.goodType == GOOD_TYPE.EQUIP) { // 装备
let result = await rewardWeapons(roleId, roleName, goodInfo, {id: goods.gid, cnt: goods.count });
for(let obj of result) {
returnGoods.push({dropType: goods.type, ...obj})
}
}
}
return returnGoods;
}
async function rewardWeapons (roleId: string, roleName: string, dicGood: any, weapon: {id:number,cnt:number }) {
let weaponsData = [];
let cnt = weapon.cnt;
while (cnt > 0) {
const seqId = await CounterModel.getNewCounter('eid');
const equipInfo = {
roleId,
roleName,
eid: weapon.id,
eName: dicGood.name,
seqId,
quality: dicGood.lv,
type: dicGood.goodType
}
const equip = await EquipModel.createEquip(equipInfo);
cnt -= 1;
weaponsData.push(equip);
}
return weaponsData;
}

View File

@@ -75,6 +75,10 @@ export function getWarById(warid: number) {
return allWarInfos.get(warid);
}
export function getTowerDataByLv(lv: number) {
return towerInfos.get(lv);
}
export function getGoodById(gid) {
let goodsInfo = gamedata['jsons']['dic_goods']||[];
return goodsInfo.find(cur => {

View File

@@ -108,3 +108,25 @@ import { GOOD_TYPE } from '../consts/consts';
return weaponsData;
}
}
/**
* 将 | 分隔的字符串解析为数组a|b|c 解析为[a, b, c]
* @param str 要解析的字符串
*/
export function decodeArrayStr(str: string) {
if(str == '&') str = '';
return str.split('|');
}
/**
* 将 | 和 & 分隔的字符串解析为 Mapa&b|c&d|e&f 解析为 Map {a=>b, c=>d, e=>f}
* @param str 要解析的字符串
*/
export function decodeIdCntArrayStr(str: string, multi: number) {
const strArr = decodeArrayStr(str);
const strMap = new Map();
strArr.forEach(item => {
const kv = item.split('&');
strMap.set(kv[0], multi? parseInt(kv[1]) * multi: kv[1]);
});
return strMap;
}

View File

@@ -61,3 +61,9 @@ export const EVENT_TYPE = {
};
export const EVENT_START_BATTLE = 101;
export const HANG_UP_CONSTS = {
ENABLE_LV: 2,
UNIT_TIME: 10 * 60 * 1000,
MAX_TIME: 12 * 60 * 60 * 1000
}

54
shared/db/HangUpRecord.ts Normal file
View File

@@ -0,0 +1,54 @@
import { HANG_UP_CONSTS } from './../consts/consts';
import BaseModel from './BaseModel';
import { index, getModelForClass, prop } from '@typegoose/typegoose';
/**
* 挂机记录表
*/
@index({ roleId: 1, endLv: 1 })
export default class HangUpRecord extends BaseModel {
@prop({ required: true })
roleId: string; // 角色 id
@prop({ required: true })
roleName: string; // 角色名
@prop({ required: true, default: 0 })
speedUpTime: number; // 加速时长
@prop({ required: true, default: 1 })
startLv: number; // 开始挂机时的天梯层数,收益使用当前层数 -1 计算,即用户已经通过的最高层
@prop({ required: true })
startTime: Date; // 开始挂机的时间
@prop({ required: false })
endLv: number; // 结束挂机时的层数
@prop({ required: false })
endTime: Date; // 结束挂机的时间
@prop({ required: true, default: false })
received: boolean; // 是否已经领取true-领取false-未领取
public static async initRecord(roleId: string, roleName: string, lean = true) {
const curTime = new Date();
const recDoc = new HangUpRecordModel();
const update = Object.assign({roleId, roleName, startTime: curTime}, recDoc.toJSON());
const rec = await HangUpRecordModel.findOneAndUpdate({roleId, startLv: HANG_UP_CONSTS.ENABLE_LV}, update, {upsert: true, new: true}).lean(lean);
return rec;
}
public static async getCurRec(roleId: string, lean = true) {
const rec = await HangUpRecordModel.findOne({roleId, received: false}).lean(lean);
return rec;
}
public static async updateRec(roleId: string, roleName: string, endLv: number, endTime: Date, lean = true) {
await HangUpRecordModel.findOneAndUpdate({roleId, received: false}, {endLv, endTime, received: true}).lean(lean);
const recDoc = new HangUpRecordModel();
const update = Object.assign({roleId, roleName, startTime: endTime}, recDoc.toJSON());
const newRec = await HangUpRecordModel.findOneAndUpdate({roleId, startLv: endLv}, update, {upsert: true, new: true}).lean(lean);
return newRec;
}
}
export const HangUpRecordModel = getModelForClass(HangUpRecord);

View File

@@ -97,6 +97,9 @@ export default class Role extends BaseModel {
@prop({ required: true })
eventStatus: number; // 奇遇开启状态, 0-未开启 1-开启了第一场事件 2-完全开启
// @prop({ required: false })
// hangUpTime: Date; // 当前挂机开始时间
public static async findByUid(uid: number, serverId: number, lean = true) {
const role = await RoleModel.findOne({ 'userInfo.uid': uid, serverId }).lean(lean);
return role;
@@ -131,6 +134,10 @@ export default class Role extends BaseModel {
public static async setEventStatus(roleId: string, eventStatus: number, lean = true) {
const role = await RoleModel.findOneAndUpdate({ roleId }, { eventStatus }).lean(lean);
}
public static async towerLvUp(roleId: string, lean = true) {
let role = await RoleModel.findOneAndUpdate({roleId}, {$inc: {towerLv: 1}}).lean(lean);
return role;
}
}

View File

@@ -1,11 +1,13 @@
import { RoleModel } from './Role';
import BaseModel from './BaseModel';
import { index, getModelForClass, prop } from '@typegoose/typegoose';
import { stringify } from 'querystring';
class WarStatus {
@prop({ required: true })
warId: number; // 策划表中战斗编号
@prop({ required: true })
battleCode: string; // 服务器生成的战斗唯一标识
@prop({ required: false })
battleCode?: string; // 服务器生成的战斗唯一标识
@prop({ required: true })
status: boolean; // 是否通过true-通过false-未通过
}
@@ -30,11 +32,48 @@ export default class TowerRecord extends BaseModel {
speedUpTime: number; // 加速总时长
@prop({ required: true })
hangUpTime: Date; // 挂机开始时间
@prop({ required: true, default: false })
passed: boolean; // 此层是否通过true-通过false-未通过
public static async createRecord(towerInfo: {roleId: string, lv: number, warStatus: Array<WarStatus>}, lean = true) {
const recDoc = new TowerRecordModel();
const update = Object.assign(towerInfo, recDoc.toJSON());
const { roleId, lv } = towerInfo;
let rec = await TowerRecordModel.findOneAndUpdate({roleId, lv}, update, {upsert: true, new: true}).lean(lean);
return rec;
}
/**
* 更新天梯记录
* @param roleId 角色 Id
* @param battleCode 服务器生成的战斗唯一标识
* @param warId 策划表中的战斗 Id
* @param heroes 使用的英雄列表
* @param inc 升级的层数
* @param lean
*/
public static async updateRecord(roleId: string, lv: number, battleCode: string, warId: number, heroes: Array<number>, inc: number, lean = true) {
const rec = await TowerRecordModel.findOneAndUpdate(
{roleId, lv, 'warStatus.warId': warId},
{$set: {'warStatus.$.status': true, 'warStatus.$.battleCode': battleCode, passed: inc === 1 ? true : false}, $push: {heroes: {$each: heroes}}},
{new: true, upsert: true}
).lean(lean);
return rec;
}
public static async getRecordByLv(roleId: string, lv: number, lean = true) {
const rec = await TowerRecordModel.findOne({roleId, lv}).lean(lean);
return rec;
}
public static async resetRecordByLv(roleId: string, lv: number, lean = true) {
const rec = await TowerRecordModel.findOneAndUpdate(
{roleId, lv, passed: false},
{$set: {'warStatus.$.status': false, 'warStatus.$.battleCode': '', heroes: []}},
{new: true}).lean(lean);
return rec;
}
}
export const TowerRecordModel = getModelForClass(TowerRecord);