import { HeroModel } from './../db/Hero'; import { HangUpRecordModel } from './../db/HangUpRecord'; import { pinus } from 'pinus'; import { HANG_UP_CONSTS, TOWER_TASK_CONST, REDIS_KEY, TASK_TYPE, TIME_OUTPUT_TYPE, GOLD_COST_RATIO } from './../consts'; import { BattleRecordModel } from './../db/BattleRecord'; import { TowerRecordModel } from './../db/TowerRecord'; import { RoleModel, RoleType } from './../db/Role'; import { shouldRefresh, resResult, cal, getRandEelmWithWeight, calculateNum, genCode } from '../pubUtils/util'; import { STATUS } from '../consts/statusCode'; import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec'; import { TowerTaskRecModel } from '../db/TowerTaskRec'; import { cloneDeep } from 'lodash'; import { Rank } from './rankService'; import { checkActivityTask, checkTask } from './taskService'; import { getRandExpedition, gameData } from '../pubUtils/data'; import { ItemInter, RewardInter } from '../pubUtils/interface'; import { getTimeFunM } from '../pubUtils/timeUtil'; /** * 获取当前镇念塔状态 * @param role */ export async function getTowerStatus(role: RoleType) { let { towerLv, roleId, serverId } = role; if (!towerLv) { towerLv = 1; let role = await RoleModel.towerLvUp(roleId); // 更新redis let r = new Rank(REDIS_KEY.TOWER_RANK, { serverId }); await r.setRankWithRoleInfo(roleId, towerLv, role.towerUpTime.getTime(), role); } let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv); if (!towerRec) { const towerInfo = gameData.tower.get(towerLv); const { warArray } = towerInfo; const sts = warArray.map(id => { return {warId: id, status: false}; }); towerRec = await TowerRecordModel.createRecord({roleId, lv: towerLv, warStatus: sts}); // return { code: 201, data: '天梯记录异常' }; } return { canHungUp: towerLv >= HANG_UP_CONSTS.ENABLE_LV, hungUpEnableLv: HANG_UP_CONSTS.ENABLE_LV, canSendTask: true, curLv: towerLv, usedHeroes: towerRec.heroes, progress: towerRec.warStatus }; } /** * 获取镇念塔挂机收益 * @param roleId */ export async function getHungupRewards(roleId: string) { const result = await calcuHangUpReward(roleId); if(result.status == -1) { return result; } let {timeReward, startTime, deltaTime} = result.data; let {hangUpSpdUpCnt, lastSpdUpTime} = await RoleModel.findByRoleId(roleId); let curTime = new Date(); if (!lastSpdUpTime || (shouldRefresh(lastSpdUpTime, curTime) && hangUpSpdUpCnt <= HANG_UP_CONSTS.MAX_SPD_UP_CNT)) { hangUpSpdUpCnt = HANG_UP_CONSTS.MAX_SPD_UP_CNT; } let num = HANG_UP_CONSTS.MAX_SPD_UP_CNT - hangUpSpdUpCnt + 1; let nextCostGold = calculateNum(GOLD_COST_RATIO.TOWER_HANG_SPDUP, {num}, 50); return { status: 0, resResult: null, data: { startTime, hangUpPassTime: Math.floor(deltaTime/1000), hangUpSpdUpCnt, nextCostGold, rewards: timeReward } } } /** * 获取派遣任务列表 * @param role */ export async function getTasks(role: RoleType) { let { roleId, roleName, towerLv, towerTaskRefTime, towerTaskReCnt = 0 } = role; let curTime = new Date(); let curTasks = await TowerTaskRecModel.getCurTasks(roleId); // 当前显示中的任务 const needRefresh = shouldRefresh(towerTaskRefTime, curTime); if(needRefresh) { const batchCode = genCode(8); let {waitingTaskCode, doingTaskCode, doingIds} = getDoingOrWaitingTasks(curTasks, curTime); await TowerTaskRecModel.hideTask(roleId, waitingTaskCode); // 隐藏没有在做的任务 await TowerTaskRecModel.updateBatchCode(roleId, doingTaskCode, batchCode); // 更新留下来的旧任务的batchCode await createCurTasks(towerLv, batchCode, roleId, roleName, doingTaskCode.length, TOWER_TASK_CONST.RAND_CNT-doingTaskCode.length, doingIds); // 新建任务 curTasks = await TowerTaskRecModel.getCurTasks(roleId); // 重置派遣次数 const role = await RoleModel.resetTowerCnt(roleId, curTime); towerTaskReCnt = role.towerTaskReCnt; } let refRemainTime = getRemainTime(curTime); let nextCostGold = calculateNum(GOLD_COST_RATIO.TOWER_TASK_REF, {num: towerTaskReCnt + 1}, 50); return {curTasks: treatTask(curTasks, curTime), refRemainTime, nextCostGold} } export async function checkTowerWar(roleId: string, battleId: number, heroes: Array) { let { towerLv } = await RoleModel.findByRoleId(roleId); const towerInfo = gameData.tower.get(towerLv); if (!towerInfo) { console.error(`天梯层数异常,lv ${towerLv} by ${roleId}`); return { status: -1, resResult: resResult(STATUS.TOWER_INFO_NOT_FOUND) }; } const warIds = towerInfo.warArray; if (warIds.indexOf(battleId) === -1) { return { status: -1, resResult: resResult(STATUS.TOWER_WRONG_BATTLE_ID) }; } const tower = await TowerRecordModel.getRecordByLv(roleId, towerLv); if (!tower) { return { status: -1, resResult: resResult(STATUS.TOWER_NOT_FOUND) }; } let { heroes: recHeroes = [], warStatus = [] } = tower; for (let hid of heroes) { if (recHeroes.indexOf(hid) !== -1) { return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_HERO) }; } } const curWarStatus = warStatus.find(elem => elem.warId == battleId); if (curWarStatus && curWarStatus.status) { return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_CHALLENGE) }; } return { status: 0, data: { heroes: recHeroes, warStatus: curWarStatus } }; } export async function towerBattleEnd(sid: string, funcs: number[], roleId: string, serverId: number, battleCode: string, battleId: number, succeed: boolean, heroes: Array) { if (succeed) { let battleRec = await BattleRecordModel.getBattleRecordByCode(battleCode); if (battleRec.battleId != battleId) { return { status: -1, resResult: resResult(STATUS.BATTLE_ID_NOT_MATCH) }; } 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, resResult: resResult(STATUS.TOWER_DUPLICATE_CHALLENGE) }; } } 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 towerReward: RewardInter[] = null; if (inc === 1) { let role = await RoleModel.towerLvUp(roleId); // 更新redis let r = new Rank(REDIS_KEY.TOWER_RANK, { serverId }); await r.setRankWithRoleInfo(roleId, towerLv + 1, role.towerUpTime.getTime(), role); const nextTowerInfo = gameData.tower.get(towerLv + 1); if (nextTowerInfo) { const { warArray } = nextTowerInfo; const sts = warArray.map(id => { return { warId: id, status: false }; }); await TowerRecordModel.createRecord({ roleId, lv: towerLv + 1, warStatus: sts }); } const { reward } = gameData.tower.get(towerLv); if (reward) towerReward = reward; if (towerLv + 1 == HANG_UP_CONSTS.ENABLE_LV) { await startHangUp(roleId, roleName); pinus.app.get('channelService').pushMessageByUids('hangUpEnable', resResult(STATUS.SUCCESS, { enable: true }), [{ uid: roleId, sid }]); } // 任务 await checkTask(roleId, sid, funcs, TASK_TYPE.BATTLE_TOWER_LV, role.towerLv, false, {}); await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.BATTLE_TOWER_LV, 1, { towerLv: role.towerLv }); } return { status: 0, data: { newRec, towerReward, towerStatus: !!inc } }; } } async function startHangUp(roleId: string, roleName: string) { await HangUpRecordModel.initRecord(roleId, roleName); } export async function checkHangUpSpdUpCnt(roleId: string, cnt: number, curTime: Date) { const role = await RoleModel.findByRoleId(roleId); let { hangUpSpdUpCnt, gold, lastSpdUpTime } = role; if (!lastSpdUpTime || (shouldRefresh(lastSpdUpTime, curTime) && hangUpSpdUpCnt <= HANG_UP_CONSTS.MAX_SPD_UP_CNT)) { hangUpSpdUpCnt = HANG_UP_CONSTS.MAX_SPD_UP_CNT; } if (cnt > hangUpSpdUpCnt) { return { status: -1, resResult: resResult(STATUS.TOWER_NOT_ENOUGH_HANG_UP_TIME) } } return { status: 0, data: { hangUpSpdUpCnt, gold, lastSpdUpTime } } } export async function calcuHangUpReward(roleId: string, speedUp = false, speedUpCnt = 1, curTime = new Date()) { let { towerLv = 1, hangUpSpdUpCnt = 0, lastSpdUpTime } = await RoleModel.findByRoleId(roleId); if (towerLv < HANG_UP_CONSTS.ENABLE_LV) { return { status: -1, resResult: resResult(STATUS.TOWER_HANG_UP_NOT_START) } } let towerInfo = gameData.tower.get(towerLv - 1); // towerLv 是当前层,奖励计算按照已经通过的层,即上一层 let timeReward = []; // 奖励 let needReceiveGoods = []; // 由于小数,未能领取的奖励 let baseReward = towerInfo.rewardOfcollect; let { startTime } = await HangUpRecordModel.getCurRec(roleId); let endTime = curTime; // 挂机结束时间,现在到开始时间,经历了10的倍数的时间 let deltaTime = curTime.getTime() - startTime.getTime(); // 累计的挂机时间,受最大时间限制 if (deltaTime > HANG_UP_CONSTS.MAX_TIME) { deltaTime = HANG_UP_CONSTS.MAX_TIME; endTime = curTime; // 累积到超过24小时,那么结束时间和下一次开启时间就取整了 } else { let multiReal = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME);// 距开始挂机实际过去的时间单位 endTime = new Date(startTime.getTime() + multiReal * HANG_UP_CONSTS.UNIT_TIME) } if (speedUp) { // 加速,直接收取6小时收益,小数的累积和普通收取独立 if (hangUpSpdUpCnt >= speedUpCnt || !lastSpdUpTime || shouldRefresh(lastSpdUpTime, new Date)) { // 可加速 let multi = Math.floor(HANG_UP_CONSTS.SPD_UP_REC_TIME / HANG_UP_CONSTS.UNIT_TIME); let spdUpRec = await HangUpSpdUpRecModel.getSpdUpRec(roleId, towerLv - 1); let goods = []; if (spdUpRec) { let { notReceivedGoodsList = [], cnt } = spdUpRec; let notReceivedGoods = notReceivedGoodsList.find(cur => cur.cnt == cnt); goods = notReceivedGoods ? notReceivedGoods.goods : []; } for (let { id, count } of baseReward) { let newCount = cal.mul(count, multi); let oldGoods = goods.find(cur => cur.gid == id); if (oldGoods) newCount = cal.add(newCount, oldGoods.count); let roundCount = Math.floor(newCount); if (newCount > roundCount) { needReceiveGoods.push({ gid: id, count: cal.sub(newCount, roundCount) }); } if (roundCount > 0) { timeReward.push({ id, count: roundCount }) } } } } else { let lastRec = await HangUpRecordModel.getLastRec(roleId); let notReceivedGoods = lastRec ? lastRec.notReceivedGoods : []; let multi = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME); // 结算奖励的倍数,受最大时间限制 // console.log(deltaTime, multi, baseReward); for (let { id, count } of baseReward) { let newCount = cal.mul(count, multi); let oldGoods = notReceivedGoods.find(cur => cur.gid == id); if (oldGoods) newCount = cal.add(newCount, oldGoods.count); let roundCount = Math.floor(newCount); if (newCount > roundCount) { needReceiveGoods.push({ gid: id, count: cal.sub(newCount, roundCount) }); } if (roundCount > 0) { timeReward.push({ id, count: roundCount }) } } } return { status: 0, data: { endLv: towerLv - 1, startTime, deltaTime, endTime, timeReward, needReceiveGoods } }; } async function checkCond(roleId: string, heroes, type: number, param: number, cnt: number) { let heroCnt = 0; switch (type) { case 1: for (let seqId of heroes) { const { star, colorStar } = await HeroModel.findBySeqIdAndRole(seqId, roleId); if (star + colorStar >= param) { heroCnt++; } } break; case 2: for (let seqId of heroes) { let { hid } = await HeroModel.findBySeqIdAndRole(seqId, roleId); const hInfo = gameData.hero.get(hid); if (hInfo.camp === param) { heroCnt++; } } break; case 3: for (let seqId of heroes) { let { hid } = await HeroModel.findBySeqIdAndRole(seqId, roleId); const hInfo = gameData.hero.get(hid); if (gameData.job.get(hInfo.jobid).job_class === param) { heroCnt++; } } break; default: break; } if (heroCnt >= cnt) { return true; } return false; } export async function checkTaskConditions(roleId: string, heroes: number[], condition: { type: number, param: number, count: number }[]) { let res = true; for (let { type, param, count } of condition) { const checkRes = await checkCond(roleId, heroes, type, param, count); if (!checkRes) { res = false; break; } } return res; } /** * 创建新的派遣任务 * @param towerLv 玩家层数 * @param roleId 玩家id * @param roleName 玩家名 * @param hideNum 隐藏的任务数量 */ export async function createCurTasks(towerLv: number, batchCode: string, roleId: string, roleName: string, hideNum: number, showNum: number, rids: Array) { let randomList = []; let dicTask = gameData.towerTask; for (let [_id, task] of dicTask) { let { suitFloor: { min, max } } = task; let flag = max ? (towerLv >= min && towerLv <= max) : (towerLv >= min); if (flag) { randomList.push(task); } } let returnTasks = new Array(); const arr = [{ refreshStatus: 0, num: hideNum }, { refreshStatus: 1, num: showNum }]; for (let { refreshStatus, num } of arr) { let taskIds = []; for (let i = 0; i < num; i++) { let list = randomList.filter(cur => !rids.includes(cur.taskId)); let { dic: tmp } = getRandEelmWithWeight(list); if (tmp) { taskIds.push(tmp.taskId); rids.push(tmp.taskId) } } const curTasks = await TowerTaskRecModel.createTasks(roleId, roleName, batchCode, taskIds, refreshStatus); if (refreshStatus == 1) { returnTasks = curTasks; } } return returnTasks; } export function getRemainTime(curTime: Date) { let nextTime = getTimeFunM(curTime).getAfterDayWithHour(1); return Math.floor((nextTime - curTime.getTime()) / 1000); } export function treatTask(recs: Array, curTime: Date) { return recs.map(cur => { let { heroes, batchCode, taskId, taskCode, status, completeTime } = cur; let getStatusResult = getTaskStatus(status, completeTime, curTime); return { heroes, batchCode, taskId, taskCode, completeTime, ...getStatusResult } }); } // 刷新任务,正在进行中的部分保留,没有进行中的刷掉 export function getDoingOrWaitingTasks(taskList: Array, curTime: Date) { let doingTaskCode = new Array(), waitingTaskCode = new Array(), doingIds = new Array(); for (let task of taskList) { let { status } = getTaskStatus(task.status, task.completeTime, curTime); if (status == 1 || status == 2) { doingTaskCode.push(task.taskCode); doingIds.push(task.taskId); } else if (status == 0 || status == 3) { waitingTaskCode.push(task.taskCode); } } return { doingTaskCode, waitingTaskCode, doingIds } } export function getTaskStatus(status: number, completeTime: Date, curTime: Date) { let remainTime = 0; if (status == 1) { // console.log(curTime.getTime(), completeTime.getTime()) remainTime = Math.floor((completeTime.getTime() - curTime.getTime()) / 1000); if (remainTime < 0) { status = 2; remainTime = 0; } } return { status, remainTime } } export async function getTasksReward(roleId: string, curTime: Date) { let oldTasks = await TowerTaskRecModel.getCurTasks(roleId); let goods: ItemInter[] = []; for (let task of oldTasks) { let { completeTime, reward, termsForAdd, additionalReward } = gameData.towerTask.get(task.taskId); if (task.sendTime && task.sendTime.getTime() + completeTime * 1000 < curTime.getTime()) { goods = goods.concat(reward); if (termsForAdd) { const result = checkTaskConditions(roleId, task.heroes, termsForAdd); if (result) { goods = goods.concat(additionalReward); } } } } return goods } /** * 获取随机机器人数据 * @param cnt 机器人数量 * @param withAttr 是否返回属性,false: 仅返回阵容 */ export function getRandRobot(cnt = 1, withAttr = false) { let setInfo = getRandExpedition(cnt); if (!setInfo || setInfo.length !== cnt) return null; let robots = []; for (let info of setInfo) { if (!info || !info.warId) continue; const warInfo = gameData.warJson.get(info.warId); if (!warInfo || !warInfo.length) continue; let heroes = []; for (let hero of warInfo) { if (hero && hero.relation === 2 && hero.actorId) { heroes.push(hero.actorId); } } robots.push(heroes); } return robots; } /** * 拷贝敌军数组并添加当前血量字段 * @param source 原敌军数组,不包含当前血量字段 */ export function transBossHpArr(source: Array<{ dataId: number, hp: number, actorId: number }>): Array<{ dataId: number, hp: number, curHp: number, actorId: number }> { let desArr = []; source.forEach(elem => { let { hp } = elem; desArr.push(Object.assign(elem, { curHp: hp })); }); return cloneDeep(desArr); }