import { HeroModel } from './../db/Hero'; import { HangUpRecordModel } from './../db/HangUpRecord'; import { HANG_UP_CONSTS, REDIS_KEY, TASK_TYPE, TOWER_FORBIDDEN_CHARA_TYPE, TOWER_TASK_STATUS, MAIL_TYPE } from './../consts'; import { BattleRecordModel } from './../db/BattleRecord'; import { TowerRecordModel } from './../db/TowerRecord'; import { RoleModel, RoleType } from './../db/Role'; import { shouldRefresh, resResult, cal, getRandEelmWithWeight, genCode, } from '../pubUtils/util'; import { STATUS } from '../consts/statusCode'; import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec'; import { TaskHero, TowerTaskRecModel, TowerTaskRecType } from '../db/TowerTaskRec'; import { Rank } from './rankService'; import { checkTask } from './task/taskService'; import { getRandExpedition, gameData } from '../pubUtils/data'; import { ItemInter, RewardInter } from '../pubUtils/interface'; import { getTimeFunM } from '../pubUtils/timeUtil'; import { ComRoleStatusHero } from '../db/ComBattleTeam'; import { sendMailByContent } from './mailService'; import { DicTowerTask } from '../pubUtils/dictionary/DicTowerTask'; import * as dicParam from '../pubUtils/dicParam'; import { getVipHungupReward } from './activity/monthlyTicketService'; /** * 获取当前镇念塔状态 * @param role */ export async function getTowerStatus(role: RoleType) { let { towerLv, roleId, serverId, towerReceived } = role; if (!towerLv) { let role = await RoleModel.towerLvUp(roleId); towerLv = role.towerLv; } let towerRec = await getTowerRecByLv(roleId, towerLv); return { canHungUp: canHunUp(towerLv), canSendTask: towerLv - 1 >= dicParam.TOWER_SEARCH.TOWER_SEARCH_STARTLIMITED, sendTaskEnableLv: dicParam.TOWER_SEARCH.TOWER_SEARCH_STARTLIMITED, curLv: towerLv, usedHeroes: towerRec.heroes, progress: towerRec.warStatus, receivedBox: towerReceived||[], }; } export async function getTowerRecByLv(roleId: string, towerLv: number) { 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 towerRec; } /** * 获取镇念塔挂机收益 * @param roleId */ export async function getHungupRewards(roleId: string, role?: RoleType) { if(!role) role = await RoleModel.findByRoleId(roleId); const result = await calcuHangUpReward(role); if(!result) return false; let { hangUpSpdUpCnt = 0, lastSpdUpTime } = role; let {timeReward, startTime, deltaTime} = result; let curTime = new Date(); if (shouldRefresh(lastSpdUpTime, curTime)) { hangUpSpdUpCnt = 0; } let nextCostGold = getManyHangSpdUpCostGold(hangUpSpdUpCnt, 1); return { startTime, hangUpPassTime: Math.floor(deltaTime/1000), hangUpSpdUpCnt: dicParam.TOWER_BOOST.TOWER_BOOSTTIME - hangUpSpdUpCnt, nextCostGold, rewards: timeReward } } /** * 获取派遣任务列表 * @param role */ export async function getTasks(role: RoleType) { let { roleId, roleName, towerLv, towerTaskRefTime, towerTaskReCnt = 0 } = role; // towerLv:从1开始,下次打得层数,dicParam内的:从0开始,打过的层数 if(towerLv - 1 < dicParam.TOWER_SEARCH.TOWER_SEARCH_STARTLIMITED) { return {curTasks: [], refRemainTime: 0, nextCostGold: 0} } let curTime = new Date(); let curTasks = await TowerTaskRecModel.getCurTasks(roleId); // 当前显示中的任务 const needRefresh = shouldRefresh(towerTaskRefTime, curTime); if(needRefresh) { const batchCode = genCode(8); let preTasks = await TowerTaskRecModel.getPreTasks(roleId); if(preTasks && preTasks.length) { let checkResult = checkTaskRewards(preTasks[0].batchCode, preTasks ); if(!checkResult) return false; await sendMailByContent(MAIL_TYPE.TOWER_TASK_REWARD, roleId, { goods: checkResult.rewards }); await TowerTaskRecModel.finishTask(preTasks[0].batchCode, preTasks.map(cur => cur.taskCode)); } curTasks = await refreshTasks(towerLv, batchCode, roleId, roleName, []); // 新建任务 // 重置派遣次数 role = await RoleModel.resetTowerCnt(roleId, curTime); towerTaskReCnt = role.towerTaskReCnt; } let refRemainTime = getRemainTime(curTime); let nextCostGold = getTowerTaskCostGold(role.towerTaskReCnt, role.towerTaskRefTime); return {curTasks: treatTask(curTasks, curTime), refRemainTime, nextCostGold} } export function checkTaskRewards(batchCode: string, tasks: TowerTaskRecType[]) { const curTime = new Date(); let compTasks: string[] = []; let rewards: RewardInter[] = []; for (let task of tasks) { if(task.batchCode != batchCode) return false; if ( task.status === TOWER_TASK_STATUS.DOINING) { let {completeTime, reward, termsForAdd, additionalReward} = gameData.towerTask.get(task.taskId); if (task.sendTime && task.sendTime.getTime() + completeTime * 1000 < curTime.getTime()) { compTasks.push(task.taskCode); rewards.push(...reward); if(termsForAdd) { const result = checkTaskConditions(task.heroes, termsForAdd); if(result) { rewards.push(...additionalReward); } } } } } return { compTasks, rewards }; } export async function checkTowerWar(roleId: string, battleId: number, seqIds: 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; let dicWar = gameData.war.get(battleId); let { fobiddenCharactor } = dicWar; for (let hid of seqIds) { if (recHeroes.indexOf(hid) !== -1) { return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_HERO) }; } } for(let hid of heroes) { if(checkForbiddenChar(hid, fobiddenCharactor)) { return { status: -1, resResult: resResult(STATUS.TOWER_HERO_FORBIDDEN) }; } } 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 function checkForbiddenChar(hid: number, fobiddenCharactor: {type: number, id: number}[]) { let dicHero = gameData.hero.get(hid); let dicJob = gameData.job.get(dicHero.jobid); let flag = false; for(let { type, id } of fobiddenCharactor) { switch(type) { case TOWER_FORBIDDEN_CHARA_TYPE.CHAR: if(id == hid) flag = true; break; case TOWER_FORBIDDEN_CHARA_TYPE.JOB: if(id == dicJob.job_class) flag = true; break; case TOWER_FORBIDDEN_CHARA_TYPE.CAMP: if(id == dicHero.camp) flag = true; break; } if(flag) break; } return flag; } export async function towerBattleEnd(sid: string, 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); // console.log('*******', towerLv, heroes.join(), recHeroes.join()) for (let hid of heroes) { if (recHeroes.indexOf(hid) !== -1) { return { status: -1, resResult: resResult(STATUS.TOWER_DUPLICATE_HERO) }; } } 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, role.towerLv - 1, role.towerUpTime.getTime(), role); await createNewTowerRecord(roleId, role.towerLv); const { reward } = gameData.tower.get(role.towerLv - 1); if (reward) towerReward = reward; await checkAndStartHungUp(roleId, roleName, role.towerLv); // 任务 await checkTask(serverId, roleId, sid, TASK_TYPE.BATTLE_TOWER_LV, { towerLv: role.towerLv - 1 }); } return { status: 0, data: { newRec, towerReward, towerStatus: !!inc } }; } } /** * 创建下一层记录 * @param roleId * @param nextTowerLv 下一层塔 */ export async function createNewTowerRecord(roleId: string, nextTowerLv: number) { const nextTowerInfo = gameData.tower.get(nextTowerLv); if (nextTowerInfo) { const { warArray } = nextTowerInfo; const sts = warArray.map(id => { return { warId: id, status: false }; }); await TowerRecordModel.createRecord({ roleId, lv: nextTowerLv, warStatus: sts }); } } /** * 当可以挂机的时候开始挂机 * @param roleId * @param roleName * @param towerLv 当前楼层,升过级之后的层 */ export async function checkAndStartHungUp(roleId: string, roleName: string, towerLv: number) { if (towerLv - 1 == dicParam.TOWER_HANG_UP.TOWER_HANG_UP_ENABLE_LV) { await HangUpRecordModel.initRecord(roleId, roleName, towerLv); } } /** * 是否可以挂机,如果是10,那么要打过10层,即11层时可以开始挂机 * @param towerLv * @returns */ export function canHunUp(towerLv: number) { return towerLv - 1 >= dicParam.TOWER_HANG_UP.TOWER_HANG_UP_ENABLE_LV } export async function calcuHangUpReward(role: RoleType, speedUp = false, speedUpCnt = 1, curTime = new Date()) { let { roleId, towerLv = 1, hangUpSpdUpCnt = 0, lastSpdUpTime, vipStartTime } = role; if (!canHunUp(towerLv)) { return false } let towerInfo = gameData.tower.get(towerLv - 1); // towerLv 是当前层,奖励计算按照已经通过的层,即上一层 let baseReward = towerInfo.rewardOfcollect; if (speedUp) { // 加速,直接收取6小时收益,小数的累积和普通收取独立 if (hangUpSpdUpCnt + speedUpCnt <= dicParam.TOWER_BOOST.TOWER_BOOSTTIME || !lastSpdUpTime || shouldRefresh(lastSpdUpTime, new Date)) { // 可加速 let startTime = curTime; let endTime = new Date(curTime.getTime() + HANG_UP_CONSTS.SPD_UP_REC_TIME); let deltaTime = endTime.getTime() - startTime.getTime(); // 累计的挂机时间,受最大时间限制 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 : []; } let { timeReward, needReceiveGoods } = getVipHungupReward(startTime.getTime(), endTime.getTime(), baseReward, goods, vipStartTime); return { endLv: towerLv, startTime, deltaTime, endTime, timeReward, needReceiveGoods }; } else { return false } } else { 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; startTime = new Date(curTime.getTime() - deltaTime); 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) } let lastRec = await HangUpRecordModel.getLastRec(roleId); let notReceivedGoods = lastRec ? lastRec.notReceivedGoods : []; let { timeReward, needReceiveGoods } = getVipHungupReward(startTime.getTime(), endTime.getTime(), baseReward, notReceivedGoods, vipStartTime); return { endLv: towerLv, startTime, deltaTime, endTime, timeReward, needReceiveGoods }; } } function checkCond(heroes: TaskHero[], type: number, param: number, cnt: number) { let heroCnt = 0; switch (type) { case 1: for (let { star, colorStar } of heroes) { if (star + colorStar >= param) { heroCnt++; } } break; case 2: for (let { camp } of heroes) { if (camp === param) { heroCnt++; } } break; case 3: for (let { jobClass } of heroes) { if (jobClass === param) { heroCnt++; } } break; default: break; } if (heroCnt >= cnt) { return true; } return false; } export function checkTaskConditions(heroes: TaskHero[], condition: { type: number, param: number, count: number }[]) { let res = true; for (let { type, param, count } of condition) { const checkRes = checkCond(heroes, type, param, count); if (!checkRes) { res = false; break; } } return res; } export async function refreshTasks(towerLv: number, batchCode: string, roleId: string, roleName: string, oldTasks:TowerTaskRecType[]) { let needFloor = !oldTasks || oldTasks.length <= 0; // 首次刷新需要一个保底 let ridTaskIds: number[] = []; let positions = new Map (); // 需要创建或更新 for(let i = 1; i <= dicParam.TOWER_SEARCH.TOWER_SEARCH_LIMITED; i++) { positions.set(i, null); } let tasks: TowerTaskRecType[] = []; for(let task of oldTasks) { if( task.status == TOWER_TASK_STATUS.WAITING ) { positions.set(task.position, task); } else { positions.delete(task.position); ridTaskIds.push(task.taskId); tasks.push(task); } } for(let [ position, task ] of positions) { let dicOldTask = task? gameData.towerTask.get(task.taskId): null; let dicTask = randomTask(towerLv, dicOldTask?dicOldTask.quality: 0, ridTaskIds); if(!dicTask) break; // 任务池没有可刷新了 let { taskId, quality } = dicTask; if(quality == 4 || quality == 5) needFloor = false; if(task) { // 更新 tasks.push(await TowerTaskRecModel.updateTask(roleId, batchCode, position, taskId)); } else { tasks.push(await TowerTaskRecModel.createTask(roleId, roleName, batchCode, taskId, position)); } ridTaskIds.push(taskId); } if(needFloor) { // 塞一个保底 let dicTask = randomTask(towerLv, 3, ridTaskIds); if(dicTask) { let index = Math.floor(Math.random() * tasks.length); tasks[index] = await TowerTaskRecModel.updateTask(roleId, batchCode, tasks[index].position, dicTask.taskId); } } return tasks } function randomTask(towerLv: number, oldQuality: number, rids: number[]) { let qualityRange = oldQuality + 1; let randomList: DicTowerTask[] = []; while(randomList.length <= 0) { // 如果这个品质范围里面没有,就降一品质再找 for (let [_id, task] of gameData.towerTask) { if(task.quality >= qualityRange && !rids.includes(task.taskId)) { let { suitFloor: { min, max } } = task; let flag = max ? (towerLv >= min && towerLv <= max) : (towerLv >= min); if (flag) { randomList.push(task); } } } if(randomList.length <= 0) qualityRange--; if(qualityRange == 0) return null; } let { dic } = getRandEelmWithWeight(randomList); return dic; } // /** // * 创建新的派遣任务 // * @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 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++) { // } // 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, position } = cur; let getStatusResult = getTaskStatus(status, completeTime, curTime); return { position, heroes, batchCode, taskId, taskCode, completeTime, ...getStatusResult } }).sort((a, b) => { return a.position - b.position; }); } // 刷新任务,正在进行中的部分保留,没有进行中的刷掉 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(task.heroes, termsForAdd); if (result) { goods.push(...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: ComRoleStatusHero[] = []; for (let hero of warInfo) { if (hero && hero.relation === 2 && hero.actorId) { let dicHero = gameData.hero.get(hero.actorId); heroes.push({ id: hero.actorId, skinId: hero.actorId, star: hero.star||dicHero.initialStars, colorStar: 0, lv: hero.lv, quality: dicHero.quality }); } } robots.push(heroes); } return robots; } /** * 获得下一次刷新派遣的花费 * @param times 玩家表上的次数 * @param refTime 玩家表上的刷新时间 * @returns */ export function getTowerTaskCostGold(times: number, refTime: Date) { if(shouldRefresh(refTime, new Date())) { times = 0; } let cost = (times + 1) * dicParam.TOWER_SEARCH.TOWER_SEARCH_REFRESHRULE_FEE; if(cost > dicParam.TOWER_SEARCH.TOWER_SEARCH_REFRESHRULE_MAX) cost = dicParam.TOWER_SEARCH.TOWER_SEARCH_REFRESHRULE_MAX; return cost; } /** * 多次加速挂机消耗的元宝 * @param originCnt 玩家表里原来的次数 * @param count 这次加速几次 * @returns */ export function getManyHangSpdUpCostGold(originCnt: number, count: number) { let gold = 0; for(let i = 1; i <= count; i++) { let num = originCnt + i; gold += getHangSpdUpCostGold(num); } return gold } /** * 计算加速挂机消耗的元宝 * @param times 第几次,如果你没有加速过,这里给1 * @returns */ export function getHangSpdUpCostGold(times:number) { if(times <= dicParam.TOWER_BOOST.TOWER_BOOSTFREETIME) { return 0 } else { let cost = (times - dicParam.TOWER_BOOST.TOWER_BOOSTFREETIME) * dicParam.TOWER_BOOST.TOWER_BOOSTCAST; if(cost > dicParam.TOWER_BOOST.TOWER_BOOSTCASTMAX) cost = dicParam.TOWER_BOOST.TOWER_BOOSTCASTMAX; return cost; } }