Files
ZYZ/game-server/app/services/battleService.ts

631 lines
23 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { HeroModel, HeroType } 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, MSG_SOURCE } 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';
import { pushTowerMsg } from './sysChatService';
/**
* 获取当前镇念塔状态
* @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: HeroType[]) {
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, job} of heroes) {
if(checkForbiddenChar(hid, job, 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, job: number, fobiddenCharactor: {type: number, id: number}[]) {
let dicHero = gameData.hero.get(hid);
let dicJob = gameData.job.get(job);
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<number>) {
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 });
pushTowerMsg(roleId, roleName, serverId, MSG_SOURCE.TOWER_SUC, 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<number, TowerTaskRecType> (); // 需要创建或更新
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<number>) {
// 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 = <number>getTimeFunM(curTime).getAfterDayWithHour(1);
return Math.floor((nextTime - curTime.getTime()) / 1000);
}
export function treatTask(recs: Array<TowerTaskRecType>, 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<any>, curTime: Date) {
let doingTaskCode = new Array<string>(), waitingTaskCode = new Array<string>(), doingIds = new Array<number>();
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(new ComRoleStatusHero({
hid: 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;
}
}