Files
ZYZ/game-server/app/servers/battle/handler/rougeHandler.ts

1325 lines
62 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 { Application, BackendSession, pinus, HandlerService, } from 'pinus';
import { RougelikeRecordModel } from '../../../db/RougelikeRecord';
import { genCode, getRandEelmWithWeight, resResult } from '../../../pubUtils/util';
import { COLLECTION_TYPE, ITEM_CHANGE_REASON, REST_POINT_TYPE, ROUGE_LIKE_CARD_TYPE, ROUGE_LIKE_CHOOSE_REWARD, ROUGE_LIKE_NODE_TYPE, ROUGE_LIKE_STATUS, TASK_ROUGE_CARD_TYPE, TASK_TYPE } from '../../../consts';
import { Quest, RestPoint, RougelikeRecordDetailModel } from '../../../db/RougelikeRecordDetail';
import { STATUS } from '../../../consts/statusCode';
import { RougelikeCardModel, RougelikeCardPara, RougelikeCardType } from '../../../db/RougelikeCard';
import { Card, RougelikeCharaModel, RougelikeCharaPara, RougelikeCharaType } from '../../../db/RougelikeChara';
import { CollectionReturnParam, CommonCard, CommonChara, RewardInter, RewardOption, RougeDamageInter, SlotCard, layerNode } from '../../../pubUtils/interface';
import { clone } from 'underscore';
import { RougelikeScoreModel } from '../../../db/RougelikeScore';
import { RougelikeTechModel } from '../../../db/RougelikeTech';
import { RougelikeCollectionModel } from '../../../db/RougelikeCollection';
import { RougelikeLayerModel, RougelikeLayerType } from '../../../db/RougelikeLayer';
import { RoleModel } from '../../../db/Role';
import { gameData } from '../../../pubUtils/data';
import { chooseNode, getCardCount, getGame, getInitCharaCard, getLayerNodeReward, getLayerRewardOneData, getMap, getMaxHp, getRandomSpirit, getRougeData, repaireSendScoreReward, updateChalleng, updateMaxHp } from '../../../services/battle/rougeService';
import { checkPreRougeTech, getCurTechData, updateEffectId } from '../../../services/battle/rougeTechService';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { HeroModel } from '../../../db/Hero';
import { ROUGELIKE } from '../../../pubUtils/dicParam';
import { HandleAddCard, addSameTypeCollect, addSingleCollect, formateCharasOrCards } from '../../../services/battle/rougeCollectService';
import { DicRougePassiveCollect } from '../../../pubUtils/dictionary/DicRougePassiveCollect';
import { DicRougeHolyCard } from '../../../pubUtils/dictionary/DicRougeHolyCard';
import { DicRougeSkillCard } from '../../../pubUtils/dictionary/DicRougeSkillCard';
import { DicRougeOptionGroup } from '../../../pubUtils/dictionary/DicRougeOptionGroup';
import { addItems, getRougeTechScoreObject, handleCost } from '../../../services/role/rewardService';
import { getEffectWhenGameStart, getCharaHp, getAddCoin, getNoBossRecoveryHp, getBattleRecoveryNum, getRecoveryExtendHp, getTrainCardDiscount, checkCanChooseSkillCard, checkCanReRandomReward } from '../../../services/battle/rougeEffectService';
import { RougelikeExtendModel } from '../../../db/RougelikeExtend';
import * as util from 'util';
import { DicRougePassiveCard } from '../../../pubUtils/dictionary/DicRougePassiveCard';
import { checkTask } from '../../../services/task/taskService';
export default function (app: Application) {
new HandlerService(app, {});
return new RougeHandler(app);
}
export class RougeHandler {
constructor(private app: Application) {
}
/**
* 获取大界面数据(即进入游戏获取当前是否有进行中的一场数据)
* @param msg
* @param session
* @returns
*/
async getData(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let data = await getRougeData(roleId);
return resResult(STATUS.SUCCESS, data);
}
/**
* 获取一场试炼正在进行中的游戏数据
* @param msg
* @param session
*/
async getGame(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
return resResult(STATUS.SUCCESS, { ...await getGame(roleId) })
}
/**
* 获取初始角色卡
* @param msg
* @param session
* @returns
*/
async getInitCharaCard(msg: { type: number, grade: number }, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
const { type, grade } = msg;
const dbRecord = await RougelikeRecordModel.findByRoleIdAndStatus(roleId, ROUGE_LIKE_STATUS.SUCCESS)
// 存在一场正在进行中,不能开启新一场
if (dbRecord) return resResult(STATUS.ROUGELIKE_GAME_PLAYING);
let typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData) return resResult(STATUS.NO_TYPE_GRADE);
const { lv = 0 } = await RoleModel.findByRoleId(roleId, 'lv');
if (lv < (typeGradeData.lvLimit || 0)) return resResult(STATUS.LIMIT_LV);
// 开启新一场
const gameCode = genCode(8);
const charaCards = getInitCharaCard() || [];
let handleAddFun = new HandleAddCard(session, gameCode);
for (let ele of charaCards) {
handleAddFun.pushChara(ele.id, await getMaxHp(roleId, gameCode, ele.id, type, grade), []);
}
let result = await handleAddFun.save();
await RougelikeRecordModel.updateByGameCode(gameCode, {
$set: {
roleId, grade, type, authorType: 0, curLayer: 0,
maxLayer: typeGradeData.layerCount, status: 0, takeoutReward: [],
},
$inc: { score: 0, coin: 0, techScore: 0 }
})
return resResult(STATUS.SUCCESS, { gameCode, ...result })
}
/**
* 角色卡刷新
* @param msg
* @param session
* @returns
*/
async refreshInitCharaCard(msg: { gameCode: string }, session: BackendSession) {
const roleId = session.get('roleId');
const { gameCode } = msg;
let dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
const { type = 0, grade = 0 } = dbRecord;
await RougelikeCharaModel.deleteCharaByRoleId(roleId);
const charaCards = getInitCharaCard() || [];
let handleAddFun = new HandleAddCard(session, gameCode);
for (let ele of charaCards) {
handleAddFun.pushChara(ele.id, await getMaxHp(roleId, gameCode, ele.id, type, grade), []);
}
let result = await handleAddFun.save();
return resResult(STATUS.SUCCESS, { gameCode, charas: result?.addCharas || [] })
}
/**
* 开启一场游戏
* @param msg
* @param session
* @returns
*/
async startGame(msg: { gameCode: string, authorType: number }, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let serverId = session.get('serverId');
const { gameCode, authorType } = msg;
// 检测流派
const authorTypeData = gameData.rougeAuthorType.get(authorType);
if (!authorTypeData) return resResult(STATUS.NO_AUTHOR_TYPE);
let dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
// 未选角色
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME)
// 存在一场正在进行中,不能开启新一场
if (dbRecord.status != ROUGE_LIKE_STATUS.CHOOSECHARA) return resResult(STATUS.ROUGELIKE_GAME_PLAYING);
const { type, grade, curLayer, maxLayer, } = dbRecord
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData) return resResult(STATUS.NO_TYPE_GRADE);
const typeGradeByIdData = gameData.rougeTypeGradeById.get(typeGradeData.limitId);
if (typeGradeByIdData) {
const dbExtend = await RougelikeExtendModel.findByRoleIdAndLimitId(roleId, typeGradeData.limitId)
if (!dbExtend) return resResult(STATUS.TYPE_UNLOCK);
}
let nodes = getMap(typeGradeData.layerPlan, typeGradeData.layerCount);
await RougelikeLayerModel.bulkWriteUpdate(nodes.map(cur => { return { ...cur, gameCode, roleId, status: 0 }; }));
await RougelikeRecordModel.updateByGameCode(gameCode, { $set: { authorType, status: ROUGE_LIKE_STATUS.INPROGRESS } });
let { addCoin, cardIds } = await getEffectWhenGameStart(roleId, gameCode, authorType);
dbRecord = await RougelikeRecordModel.updateByGameCode(gameCode, { $inc: { coin: addCoin, coinTotal: addCoin } });
//任务
if (addCoin > 0) {
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_GET_COIN, { addCoin });
}
const charas: CommonChara[] = formateCharasOrCards(await RougelikeCharaModel.findByGameCode(gameCode), ROUGE_LIKE_CARD_TYPE.CHARA)?.charas || [];
let handleAddFun = new HandleAddCard(session, gameCode);
handleAddFun.pushHolyCards(cardIds);
let result = await handleAddFun.save();
result = await handleAddFun.getHolyEffect();
return resResult(STATUS.SUCCESS, { gameCode, grade, coin: dbRecord.coin, coinTotal: dbRecord.coinTotal, type, authorType, curLayer, maxLayer, nodes, charas: [...charas, ...result.addCharas], cards: result.addCards })
}
/**
* 结束游戏
* @param msg
* @param session
* @returns
*/
async gameEnd(msg: { gameCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
let serverId = session.get('serverId');
const { gameCode } = msg;
let dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
let { type, grade, status = 0, takeoutReward = [], hasReceivedTakeout = false, curLayer = 0, } = dbRecord;
if (status == ROUGE_LIKE_STATUS.SUCCESS) return resResult(STATUS.ROUGELIKE_GAME_END);
status = ROUGE_LIKE_STATUS.SUCCESS;
const dbLayers = await RougelikeLayerModel.findByGameCode(gameCode);
let takeoutRewardMap = takeoutReward.reduce((map, cur) => { map.set(cur.id, cur.count); return map; }, new Map<number, number>());
let spiritIds: number[] = [];
let addScore = 0, addTech = 0, isPass = true;
if (dbLayers.length == 0) isPass = false;
for (const { layer, hasPass = false, layerNodes } of dbLayers) {
if (layer > dbRecord.curLayer) {
isPass = false;
continue;
};
const layerNodeOne = layerNodes.find(cur => cur.isChoose == 1);
let nodeType = layerNodeOne?.type || 0;
const detailCode = layerNodeOne?.detailCode;
//忽略挑战关
if (!hasPass && nodeType != ROUGE_LIKE_NODE_TYPE.CHALLENGE) {
isPass = false;
continue;
}
if (nodeType == ROUGE_LIKE_NODE_TYPE.QUEST_POINT) {
const dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode)
nodeType = dbDetail?.questType || 0;
}
const layerReward = getLayerRewardOneData(type, grade, layer, nodeType);
const { takeoutReward = [], score = 0, tech = 0, spiritPlan } = clone(layerReward);
addScore += score, addTech += tech;
for (const { id, count } of takeoutReward) takeoutRewardMap.set(id, takeoutRewardMap.get(id) || 0 + count);
for (const { id, count } of spiritPlan) { spiritIds.push(...getRandomSpirit(id, count)); }
}
let spiritMap = new Map<number, RewardInter>();
spiritIds.forEach((spiritId) => {
spiritMap.set(spiritId, { id: spiritId, count: (spiritMap.get(spiritId)?.count || 0) + 1 });
})
takeoutReward = [...Array.from(takeoutRewardMap, ([id, count]) => ({ id, count })), ...spiritMap.values()];
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
dbRecord = await RougelikeRecordModel.updateByGameCode(gameCode, { $set: { status, takeoutReward }, $inc: { score: addScore, techScore: addTech } });
let { score = 0, techScore = 0 } = dbRecord;
let dbScore = await RougelikeScoreModel.incScore(roleId, score);
let goods = await addItems(roleId, roleName, sid, [getRougeTechScoreObject(techScore)], ITEM_CHANGE_REASON.ROUGE_TECH_SCORE);
let firstReward = [];
if (isPass) {
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData || !typeGradeData.id) return resResult(STATUS.NO_TYPE_GRADE);
let dbExtend = await RougelikeExtendModel.findByRoleIdAndLimitId(roleId, typeGradeData.id);
if ((dbExtend?.limitId || 0) != typeGradeData.id) {
firstReward = await addItems(roleId, roleName, sid, typeGradeData?.firstReward || [], ITEM_CHANGE_REASON.ROUGE_FIRST_REWARD);
await RougelikeExtendModel.update(roleId, typeGradeData.id, firstReward, gameCode);
}
await checkTask(serverId, roleId, sid, TASK_TYPE.FUND_ROUGE, { rougeId: typeGradeData.id, count: 1 });
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_GAME_COUNT, { count: 1 });
}
return resResult(STATUS.SUCCESS, {
gameCode, goods, score, techScore, hasReceivedTakeout, firstReward,
takeoutReward, weeklyScore: dbScore?.score || 0, takeoutRewardCnt: dbScore?.takeoutRewardCnt || 0
});
}
/**
* 选择外带奖励
* @param msg
* @param session
*/
async takeoutReward(msg: { gameCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
const { gameCode } = msg;
let dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
let { takeoutReward, hasReceivedTakeout } = dbRecord;
if (hasReceivedTakeout) return resResult(STATUS.ROUGELIKE_TAKEOUT_HAS_RECEIVED);
let dbScore = await RougelikeScoreModel.receiveTakeoutReward(roleId, ROUGELIKE.TAKEOUT_REWARD_CNT);
if (!dbScore) return resResult(STATUS.ROUGELIKE_TAKEOUT_CNT_NOT_ENOUGH);
dbRecord = await RougelikeRecordModel.takeout(gameCode);
let goods = await addItems(roleId, roleName, sid, takeoutReward, ITEM_CHANGE_REASON.ROUGE_TAKE_OUT_REWARD)
return resResult(STATUS.SUCCESS, { gameCode, hasReceivedTakeout: dbRecord?.hasReceivedTakeout || false, takeoutRewardCnt: dbScore?.takeoutRewardCnt || 0, goods });
}
/**
* 选择关卡节点
* @param msg
* @param session
*/
async chooseNode(msg: { gameCode: string, layer: number, detailCode: string }, session: BackendSession) {
let chooseNodeStartTime = Date.now();
let roleId = session.get('roleId');
const { gameCode, layer, detailCode } = msg;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
const { curLayer, maxLayer, type, grade } = dbRecord;
if (layer < curLayer || layer > maxLayer) return resResult(STATUS.NODE_NO_CHOOSE);
let dbCurLayer = {} as RougelikeLayerType, dbPreLayer = {} as RougelikeLayerType;
let dbLayerNodes = await RougelikeLayerModel.findByGameCodeAndLayers(gameCode, [layer, layer - 1]);
if (!dbLayerNodes) return resResult(STATUS.NODE_NO_CHOOSE);
for (let val of dbLayerNodes) {
if (val.layer == layer) {
if (val.hasPass) return resResult(STATUS.NODE_NO_CHOOSE);//当前关完成
dbCurLayer = val;
}
// 注意过滤挑战点
if ((val.layer || 0) == layer - 1 && layer - 1 > 0) {
let dbDetail = await RougelikeRecordDetailModel.findByGameCodeAndLayer(gameCode, layer - 1);
if (!val.hasPass && (!dbDetail || dbDetail.nodeType != ROUGE_LIKE_NODE_TYPE.CHALLENGE)) return resResult(STATUS.NODE_NO_CHOOSE);//前一关未完成
dbPreLayer = val;
}
}
let dbIsChoose = dbCurLayer.layerNodes.find(cur => cur.isChoose == ROUGE_LIKE_CHOOSE_REWARD.CHOOSE);
if (dbIsChoose && dbIsChoose.detailCode != detailCode) return resResult(STATUS.NODE_NO_CHOOSE); //该层已经选择过一关卡,不能再次选择其他关卡
let dbCurLayerChooseNode: layerNode = dbCurLayer.layerNodes.find(cur => cur.detailCode == detailCode);
if (!dbCurLayerChooseNode) return resResult(STATUS.NODE_NO_CHOOSE);
if (dbPreLayer && Object.keys(dbPreLayer).length > 0) {
let dbPreLayerChooseNode = dbPreLayer.layerNodes.find(cur => cur.isChoose != 0);
if (dbCurLayerChooseNode.preNodeIndexs.indexOf(dbPreLayerChooseNode.index) == -1) return resResult(STATUS.NODE_NO_CHOOSE);
}
const curNode = await chooseNode(dbRecord, dbCurLayerChooseNode, layer)
console.log('*********************************nodeType=%s, chooseNodeTime=%s', curNode?.nodeType, Date.now() - chooseNodeStartTime);
return resResult(STATUS.SUCCESS, { detailCode, layer, curNode })
}
// 挑战关卡
async checkBattle(msg: { gameCode: string, detailCode: string, warId: number, charaCodes: string[] }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
const { gameCode, detailCode, warId, charaCodes } = msg;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.REWARD_NO_CHOOSE);
let { status = 0, rewards, layer, nodeType, warId: detailWarId = 0 } = dbDetail;
if (status != 0 || warId != detailWarId) return resResult(STATUS.WAR_NO_CHOOSE);
let dicWar = gameData.war.get(detailWarId);
if (!dicWar) return resResult(STATUS.DIC_DATA_NOT_FOUND);
const battleCode = genCode(8); // 关卡唯一值
const dbCharas = await RougelikeCharaModel.findByCodes(gameCode, charaCodes);
if (!dbCharas || dbCharas.length == 0) return resResult(STATUS.REWARD_NO_CHOOSE);
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: {
roleId, roleName, battleId: warId, status: 0, warName: dicWar.gk_name, warType: dicWar.warType,
record: {
rougeOriginal: dbCharas.map(cur => {
const { charaId, cards = [], hp = 0, ap = 0, shield = 0, roundSkill = 0, apSkill = 0 } = cur;
return { charaId, cards, hp, ap, roundSkill, apSkill }
})
}
}
}, true);
return resResult(STATUS.SUCCESS, { gameCode, battleCode });
}
/**
* 结束关卡
* @param msg
* @param session
* @returns
*/
async battleEnd(msg: { gameCode: string, detailCode: string, battleCode: string, warId: number, status: number, round: number, rougeDamage: RougeDamageInter[] }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
let serverId = session.get('serverId');
let { gameCode, detailCode, battleCode, warId, status, round, rougeDamage } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail || (dbDetail.warId || 0) != warId || warId == 0 || (dbDetail.status || 0) != 0) return resResult(STATUS.WAR_NO_CHOOSE);
let { layer, nodeType, questType = 0 } = dbDetail;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
const dbCharas = await RougelikeCharaModel.findByCodes(gameCode, rougeDamage.map(cur => { return cur.charaCode }));
if (!dbCharas || dbCharas.length == 0 || dbCharas.length != rougeDamage.length) return resResult(STATUS.REWARD_NO_CHOOSE);
await BattleRecordModel.findOneAndUpdate({ battleCode }, {
$set: { status, 'record.round': round, 'record.rougeDamage': rougeDamage, }
}, { new: true, upsert: true }).lean(true);
const dbCharaMap = dbCharas.reduce((result, cur) => { result.set(cur.charaCode, { ...cur }); return result; }, new Map<string, RougelikeCharaType>());
let isAp: boolean = false, isRound: boolean = false, arrAuthorType: number[] = [], isHpZero = false;
for (let obj of rougeDamage) {
if (obj.isAp) isAp = true;
if (obj.isRound) isRound = true;
const chara = dbCharaMap.get(obj.charaCode);
if (!chara) return resResult(STATUS.BATTLE_ABNORMAL)
obj.maxHp = (chara.maxHp || 0);
if (!chara.cards || chara.cards.length == 0) console;
for (const { cardId } of chara.cards) {
const passiveCardData = gameData.rougePassiveCard.get(cardId);
if (nodeType == ROUGE_LIKE_NODE_TYPE.BOSS && passiveCardData && passiveCardData.authorType) arrAuthorType.push(passiveCardData.authorType);
}
if (!obj.hp || obj.hp == 0) isHpZero = true;
}
await updateChalleng(dbRecord, roleId, sid, gameCode, layer, rougeDamage, isAp, isRound);
let updateCharas: RougelikeCharaPara[] = [], charas: CommonChara[] = [];
let recoveryHp = false;
if (status != 1 && nodeType != ROUGE_LIKE_NODE_TYPE.BOSS && isHpZero) {
recoveryHp = await getNoBossRecoveryHp(roleId, gameCode);
if (recoveryHp) status = 1;
}
let hpRatio = 0;
if (status == 1) {
hpRatio = await getCharaHp(roleId, gameCode);
}
const minHp = rougeDamage.reduce((min, cur) => { return Math.min(min, cur.hp); }, Infinity);
let recoveryNum = 0;
if (minHp == 0) recoveryNum = await getBattleRecoveryNum(roleId, gameCode);
let updateCharasMap = new Map();
for (let { charaCode, hp, ap, shield, others } of rougeDamage) {
const chara = dbCharaMap.get(charaCode);
if (!chara) return resResult(STATUS.BATTLE_ABNORMAL)
if (chara.maxHp < hp) hp = chara.maxHp;
if (recoveryHp && hp == 0) hp = chara.maxHp;
if (hpRatio > 0 && hp != 0) hp = Math.min(Math.floor(hp + (chara.maxHp * hpRatio / 100)), chara.maxHp);
if ((recoveryNum--) > 0 && hp == 0) { hp = chara.maxHp; }
charas.push({ charaCode, charaId: chara.charaId, seqId: chara.seqId, cards: chara.cards, hp, maxHp: chara.maxHp, ap, shield, others, apSkill: chara.apSkill, roundSkill: chara.roundSkill })
updateCharas.push({ gameCode, charaCode, hp, ap, shield, others });
updateCharasMap.set(charaCode, { hp, ap, shield, others });
}
let incCoin = 0;
if (status == 1) {
let { coinRatio, coinAdd } = await getAddCoin(roleId, gameCode, nodeType);
let newNodeType = nodeType;
if (questType > 0) newNodeType = questType;
const layerReward = getLayerRewardOneData(dbRecord.type, dbRecord.grade, layer, newNodeType);
incCoin = Math.floor((layerReward.coin || 0) * (1 + coinRatio / 100) + coinAdd);
await RougelikeRecordModel.updateByGameCode(gameCode, { $inc: { coin: incCoin, coinTotal: incCoin } });
//battleEnd 主动将hasPass置为true
await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } });
}
// let coinReward = rewards.find(cur => cur.rewardType == 0);
// if (coinReward) coinReward.chooseNum = Math.floor(coinReward.chooseNum * (1 + coinRatio / 100) + coinAdd);
// if (coinRatio != 0 || coinAdd != 0) {
// await RougelikeRecordDetailModel.findOneAndUpdate({ gameCode, detailCode, 'rewards.rewardType': 0 }, { 'rewards.$.chooseNum': coinReward.chooseNum, }, { new: true, upsert: true }).lean(true);
// }
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { status } })
if (incCoin > 0) {
charas = await updateMaxHp(roleId, gameCode, dbRecord.type, dbRecord.grade, updateCharasMap)
//任务
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_GET_COIN, { addCoin: incCoin });
} else {
await RougelikeCharaModel.bulkWriteUpdate(updateCharas);
}
if (status == 1 && arrAuthorType.length > 0 && arrAuthorType.every(cur => cur == arrAuthorType[0])) {
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_BOSS_PASS, { authorType: arrAuthorType[0], count: 1 });
}
return resResult(STATUS.SUCCESS, { status, gameCode, detailCode, charas, incCoin });
}
// 重置
async reRandomReward(msg: { gameCode: string, detailCode: string, rewardType: number }, session: BackendSession) {
const roleId: string = session.get('roleId');
const { gameCode, detailCode, rewardType } = msg;
// 检查数量
let { canReRandom, costCoin } = await checkCanReRandomReward(roleId, gameCode, rewardType);
if (!canReRandom) return resResult(STATUS.ROUGE_TECH_NOT_UNLOCKED);
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.REWARD_NO_CHOOSE);
let dbDetailReward = dbDetail.rewards.find(cur => cur.rewardType == rewardType);
if (!dbDetailReward || dbDetailReward.reRandRewardCnt > 0) return resResult(STATUS.ROUGE_RE_RANDOM_CNT_OVER);
const typeGradeData = gameData.rougeTypeGrade.get(dbRecord.type + '_' + dbRecord.grade);
const nodeData = gameData.rougeNode.get(dbDetail.nodeId);
if (!typeGradeData || !nodeData) return resResult(STATUS.DIC_DATA_NOT_FOUND);
const layerPlanData = gameData.rougeLayerPlan.get(typeGradeData.layerPlan + '_' + dbDetail.layer);
if (!layerPlanData) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let costResult = await RougelikeRecordModel.costCoin(gameCode, costCoin);
if (!costResult) return resResult(STATUS.COIN_NOT_ENOUGH);
let replaceReward = await getLayerNodeReward(roleId, gameCode, dbRecord.authorType, dbDetail.nodeId, layerPlanData.rewardPlan, dbDetail.layer, dbDetail.questType);
let { rewards } = dbDetail;
if (replaceReward && replaceReward.rewards.length > 0) {
let newRewards = [...rewards.filter(reward => reward.rewardType != rewardType), ...replaceReward.rewards.filter(reward => reward.rewardType == rewardType)].map(reward => ({ ...reward, reRandRewardCnt: 1 })).sort((a, b) => a.groupIndex - b.groupIndex);
dbDetail = await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { rewards: newRewards } });
}
return resResult(STATUS.SUCCESS, { rewards: dbDetail.rewards });
}
/**
* 选择奖励
* @param msg
* @param session
* @returns
*/
async chooseReward(msg: { gameCode: string, detailCode: string, groupIndex: number, optionIndexs: number[] }, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
const { gameCode, detailCode, groupIndex, optionIndexs } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.REWARD_NO_CHOOSE);
let { status, rewards, layer, nodeType, warId = 0, challenge } = dbDetail;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
// 检测有关卡的是否挑战成功
if (warId != 0 && status != 1) return resResult(STATUS.REWARD_NO_CHOOSE);
if (!rewards || rewards.length == 0) return resResult(STATUS.REWARD_NO_CHOOSE);
let { rewardType, options, groupStatus, chooseNum } = rewards.find((obj) => obj.groupIndex == groupIndex);
// if (groupStatus == ROUGE_LIKE_CHOOSE_REWARD.CHOOSE) return resResult(STATUS.REWARD_NO_CHOOSE);
let handleAddFun = new HandleAddCard(session, gameCode, dbDetail);
let incCoin = 0, redCoin = 0;
// //处理下试炼币
// if (rewardType == 0) {
// incCoin = chooseNum;
// groupStatus = ROUGE_LIKE_CHOOSE_REWARD.CHOOSE;
// const result = await RougelikeRecordDetailModel.findOneAndUpdate({ gameCode, detailCode, 'rewards.groupIndex': groupIndex }, { 'rewards.$.groupStatus': groupStatus, }, { new: true, upsert: true }).lean(true);
// if (result.rewards.find((obj) => obj.groupStatus != ROUGE_LIKE_CHOOSE_REWARD.CHOOSE) == undefined) {
// await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } })
// }
// await RougelikeRecordModel.updateByGameCode(gameCode, { $inc: { coin: incCoin, coinTotal: incCoin } })
// return resResult(STATUS.SUCCESS, { addCharas: await updateMaxHp(roleId, gameCode, dbRecord.type, dbRecord.grade), addCards: [], incCoin, redCoin });
// }
// 特性卡可以选多次
let passiveChooseCount = 0;
if (rewardType == ROUGE_LIKE_CARD_TYPE.PASSIVE || rewardType == ROUGE_LIKE_CARD_TYPE.CHARA) {
passiveChooseCount = options.reduce((accumulator, obj) => {
if (obj.optionStatus === ROUGE_LIKE_CHOOSE_REWARD.CHOOSE) return accumulator + 1;
return accumulator;
}, 0);
if (chooseNum - passiveChooseCount < optionIndexs.length) return resResult(STATUS.REWARD_NO_CHOOSE);
}
if (optionIndexs.length > chooseNum) return resResult(STATUS.REWARD_NO_CHOOSE);
let tempOptions: RewardOption[] = options.filter(opt => optionIndexs.includes(opt.optionIndex));
if (tempOptions.length > chooseNum) return resResult(STATUS.REWARD_NO_CHOOSE);
for (let { rewardId, passiveCardIds, optionStatus, optionIndex } of tempOptions) {
if (optionStatus != 0) return resResult(STATUS.REWARD_NO_CHOOSE);
let cardCode = genCode(8);
if (rewardType == ROUGE_LIKE_CARD_TYPE.CHARA) {
// 更新角色卡
handleAddFun.pushChara(rewardId, await getMaxHp(roleId, gameCode, rewardId, dbRecord.type, dbRecord.grade), passiveCardIds || []);
//休整点 消耗
if (nodeType == ROUGE_LIKE_NODE_TYPE.REST_POINT) {
let charaData = gameData.rougeChara.get(rewardId);
if (!charaData) return;
redCoin += charaData.recruitConsume || 0;
}
} else {
// 更新特性卡or圣物
handleAddFun.pushCard(rewardId, rewardType);
}
optionStatus = ROUGE_LIKE_CHOOSE_REWARD.CHOOSE;
options.forEach((ele) => { if (ele.optionIndex == optionIndex) ele.optionStatus = optionStatus; });
}
if ((dbRecord.coin || 0) < redCoin) return resResult(STATUS.COIN_NOT_ENOUGH);
if (redCoin > 0) await RougelikeRecordModel.updateByGameCode(gameCode, { $inc: { coin: -redCoin } });
groupStatus = ROUGE_LIKE_CHOOSE_REWARD.CHOOSE;
if (nodeType == ROUGE_LIKE_NODE_TYPE.CHALLENGE) {
// 挑战关领奖更新
await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } })
} else {
// 更新进度
await updateChalleng(dbRecord, roleId, sid, gameCode, layer, []); // 更新挑战进度
}
await RougelikeRecordDetailModel.updateRewardByGroupIndex(gameCode, detailCode, groupIndex, groupStatus, options);
let addResult = await handleAddFun.save();
addResult = await handleAddFun.getHolyEffect();
return resResult(STATUS.SUCCESS, { ...addResult, incCoin, redCoin });
}
/**
* 商店购买
* @param msg
* @param session
* @returns
*/
async shopBuy(msg: { gameCode: string, detailCode: string, optionIndex: number }, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let { gameCode, detailCode, optionIndex } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.SHOP_NO_BUY);
let { status = 0, shops, layer, nodeType } = dbDetail;
if (status == 1 || !shops || shops.length == 0) return resResult(STATUS.SHOP_NO_BUY);
let shop = shops.find((obj) => obj.optionIndex == optionIndex);
if (!shop || shop.optionStatus != ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE) return resResult(STATUS.SHOP_NO_BUY);
let dbRecord = await RougelikeRecordModel.findByGameCode(gameCode);
if (!dbRecord || (dbRecord.coin || 0) < shop.discountPrice) return resResult(STATUS.COIN_NOT_ENOUGH);
shop.optionStatus = ROUGE_LIKE_CHOOSE_REWARD.CHOOSE;
await RougelikeRecordDetailModel.updateShopByCode(gameCode, detailCode, optionIndex, shop.optionStatus);
await RougelikeRecordModel.updateByGameCode(gameCode, { $set: { coin: (dbRecord.coin || 0) - shop.discountPrice } });
let handleAddFun = new HandleAddCard(session, gameCode, dbDetail);
handleAddFun.pushCard(shop.rewardId, shop.rewardType);
let result = await handleAddFun.save();
result = await handleAddFun.getHolyEffect();
return resResult(STATUS.SUCCESS, { curShop: shop, ...result, redCoin: -shop.discountPrice });
}
/**
* 结束这一节点
* @param msg
* @param session
* @returns
*/
async nodeEnd(msg: { gameCode: string, detailCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
const { gameCode, detailCode } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.NO_EXIT_NODE);
let { layer, nodeType, status = 0, shops, warId = 0 } = dbDetail;
if ((warId != 0 && status != 1)) {
//检测关卡是否挑战成功
return resResult(STATUS.NODE_NO_END)
}
status = 1;
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { status } });
if (nodeType != ROUGE_LIKE_NODE_TYPE.CHALLENGE) {
await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } });
}
return resResult(STATUS.SUCCESS, { gameCode, detailCode, status, ...await getGame(roleId) })
}
/**
* 问号点选择题
* @param msg
* @param session
* @returns
*/
async chooseOption(msg: { gameCode: string, detailCode: string, eventOptions: number[] }, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let serverId = session.get('serverId');
const { gameCode, detailCode, eventOptions } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.NODE_NO_CHOOSE);
let { layer, nodeType, status = 0, question = {} as Quest } = dbDetail;
if (status == 1) return resResult(STATUS.NODE_NO_CHOOSE);
let checkAfterGroup = 0, optionGroupIds: number[] = [];
let handleAddFun = new HandleAddCard(session, gameCode, dbDetail);
for (let [index, id] of eventOptions.entries()) {
const rougeEventOptionData = gameData.rougeEventOption.get(id);
if (!rougeEventOptionData) return resResult(STATUS.NODE_NO_CHOOSE);
const { randomEventId, optionGroup, afterGroup, holyCardPlan } = rougeEventOptionData;
if (randomEventId != (question.randomEventId || 0)) return resResult(STATUS.NODE_NO_CHOOSE);
if (index != 0 && checkAfterGroup != optionGroup) return resResult(STATUS.NODE_NO_CHOOSE);
if (!optionGroupIds.includes(optionGroup)) optionGroupIds.push(optionGroup);
if (!question.EventOptions) question.EventOptions = [];
question.EventOptions.push(id);
checkAfterGroup = afterGroup;
const rougeHolyCardPlanData = gameData.rougeHolyCardPlan.get(holyCardPlan);
if (!rougeHolyCardPlanData || rougeHolyCardPlanData.length == 0) continue;
let newCards = [];
let cardsMap = await getCardCount(gameCode, ROUGE_LIKE_CARD_TYPE.HOLY);
for (let obj of rougeHolyCardPlanData) {
let holyCardData = gameData.rougeHolyCard.get(obj.cardId);
if (!holyCardData) continue;
const getLimit = cardsMap.get(obj.cardId) || 0;
if (getLimit >= (holyCardData?.getLimit || 0)) continue; //处理限制获取数量
newCards.push(obj);
}
let cardId = 0;
if (newCards.length > 0) {
cardId = getRandEelmWithWeight(newCards).dic.cardId;
}
if(cardId > 0) handleAddFun.pushHolyCard(cardId);
}
status = 1;
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { question, status } })
await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } })
let result = await handleAddFun.save();
result = await handleAddFun.getHolyEffect();
await addSameTypeCollect(roleId, sid, gameCode, COLLECTION_TYPE.EVENT, optionGroupIds);
//任务
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_EVENT_COLLECT, { eventGroups: optionGroupIds });
return resResult(STATUS.SUCCESS, { ...result })
}
/**
* 休整点恢复
* @param msg
* @param session
* @returns
*/
async recovery(msg: { gameCode: string, detailCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
const { gameCode, detailCode } = msg;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.NODE_NO_CHOOSE);
let { restPoints = [], nodeType, status, rewards } = dbDetail;
if (status == 1 || nodeType != ROUGE_LIKE_NODE_TYPE.REST_POINT) return resResult(STATUS.NODE_NO_CHOOSE);
let restPoint = restPoints.find(cur => cur.restType == REST_POINT_TYPE.RECOVERY) || new RestPoint();
if (restPoint.recoveryCnt && restPoint.recoveryCnt > 0) return resResult(STATUS.NO_TIME_RECOVERY);
restPoint.recoveryCnt = 1;
restPoint.restType = REST_POINT_TYPE.RECOVERY;
restPoint.recoveryCharas = [];
let dbCharas = await RougelikeCharaModel.findByGameCode(gameCode);
let updateCharas: RougelikeCharaPara[] = [];
let hpRatio = await getRecoveryExtendHp(roleId, gameCode);
for (let { gameCode, charaCode, charaId, cards, hp = 0, maxHp = 0, ap, shield, roundSkill, apSkill } of dbCharas) {
if (hp == 0) continue;
let beforeHp = hp;
hp = Math.floor(Math.min(hp + maxHp * ROUGELIKE.RECOVERY_RATIO / 100 + maxHp * hpRatio / 100, maxHp));
updateCharas.push({ gameCode, charaCode, hp });
restPoint.recoveryCharas.push({ charaCode, charaId, beforeHp, afterHp: hp });
}
restPoints.push({ restType: restPoint.restType, recoveryCnt: restPoint.recoveryCnt, recoveryCharas: restPoint.recoveryCharas });
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { restPoints } });
await RougelikeCharaModel.bulkWriteUpdate(updateCharas);
const charas: CommonChara[] = formateCharasOrCards(dbCharas, ROUGE_LIKE_CARD_TYPE.CHARA)?.charas || [];
return resResult(STATUS.SUCCESS, { restPoints, charas, reward: { rewards } })
}
/**
* 休整点强化
* @param msg
* @param session
*/
async trainCard(msg: { gameCode: string, detailCode: string, cardCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
const { gameCode, detailCode, cardCode } = msg;
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode)
if (!dbRecord) return resResult(STATUS.NO_ROUGELIKE_GAME);
if (dbRecord.status != ROUGE_LIKE_STATUS.INPROGRESS) return resResult(STATUS.ROUGELIKE_GAME_END);
let dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
if (!dbDetail) return resResult(STATUS.NODE_NO_CHOOSE);
let { layer, nodeType, status = 0, question, restPoints = [], rewards } = dbDetail;
if (status == 1 || nodeType != ROUGE_LIKE_NODE_TYPE.REST_POINT) return resResult(STATUS.NODE_NO_CHOOSE);
let dbCard = await RougelikeCardModel.findByCode(gameCode, cardCode);
if (!dbCard) return resResult(STATUS.NO_CARD);
let { type, cardId, charaId = 0, lv = 0 } = dbCard;
const passiveCardData = gameData.rougePassiveCard.get(cardId);
if (!passiveCardData) return resResult(STATUS.NO_CARD);
const passiveCardByGroupData = gameData.rougePassiveCardByGroup.get(passiveCardData.group || 0);
if (passiveCardByGroupData.length == 0 || passiveCardByGroupData.length == 1) return resResult(STATUS.HIGH_LV);
let passiveCardDataMap = passiveCardByGroupData.reduce((result, cur) => { result.set(cur.lv, cur); return result; }, new Map<number, DicRougePassiveCard>());
if (!passiveCardDataMap.has(lv + 1)) return resResult(STATUS.HIGH_LV);
const strengthConsume = passiveCardData.strengthConsume || 0;
const { lv: cardLv, id } = passiveCardDataMap.get(lv + 1);
if (lv == cardLv) return resResult(STATUS.HIGH_LV);
let realConsume = Math.floor(strengthConsume * await getTrainCardDiscount(roleId, gameCode) / 100);
if ((dbRecord.coin || 0) < realConsume) return (STATUS.COIN_NOT_ENOUGH);
let restPoint = restPoints.find(cur => cur.restType == REST_POINT_TYPE.TRAIN) || new RestPoint();
restPoint.restType = REST_POINT_TYPE.TRAIN;
if (!restPoint.trainCardCnt) restPoint.trainCardCnt = 0;
restPoint.trainCardCnt += 1;
if (!restPoint.trainCards) restPoint.trainCards = [];
restPoint.trainCards.push({ cardCode, cardId: id, beforeLv: lv, afterLv: lv + 1 });
if (restPoints.find(cur => cur.restType == restPoint.restType) == undefined) {
restPoints.push({ restType: restPoint.restType, trainCardCnt: restPoint.trainCardCnt, trainCards: restPoint.trainCards })
}
else restPoints.forEach(obj => {
if (obj.restType == restPoint.restType) {
obj.trainCardCnt = restPoint.trainCardCnt;
obj.trainCards = restPoint.trainCards;
}
})
await RougelikeRecordModel.updateByGameCode(gameCode, { $inc: { coin: -realConsume } });
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { restPoints } });
const newDbCard = await RougelikeCardModel.updateByCode(gameCode, cardCode, { $set: { cardId: id }, $inc: { lv: 1 } });
const cards: CommonCard[] = formateCharasOrCards([newDbCard], ROUGE_LIKE_CARD_TYPE.PASSIVE)?.cards || [];
if (charaId > 0) {
const dbCharas = await RougelikeCharaModel.findByGameCodeAndCharaId(gameCode, charaId);
let isEnd = false;
for (let { cards = [], charaCode } of dbCharas) {
for (let obj of cards) {
if (cardCode != obj.cardCode) continue;
obj.cardId = id;
await RougelikeCharaModel.findOneAndUpdate({ gameCode, charaCode, 'cards.cardCode': cardCode }, { 'cards.$.cardId': id }, { new: true, upsert: true }).lean(true);
isEnd = true;
break;
}
if (isEnd) break;
}
}
return resResult(STATUS.SUCCESS, { restPoints, cards, reward: { rewards }, redCoin: -realConsume, })
}
/**
* 选择一张技能卡
* @param msg
* @returns
*/
async chooseSkillCard(msg: { gameCode: string, charaCode: string, skillType: number, id: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let sid: string = session.get('sid');
let serverId: number = session.get('serverId');
const { gameCode, charaCode, skillType, id } = msg;
let dbChara = await RougelikeCharaModel.findByCode(gameCode, charaCode);
if (!dbChara) return resResult(STATUS.NO_CARD);
if (id > 0) {
const dicSkillCard = gameData.rougeSkillCard.get(id);
if (!dicSkillCard) return resResult(STATUS.DIC_DATA_NOT_FOUND);
if (dicSkillCard.skillType != skillType) return resResult(STATUS.WRONG_PARMS);
// 检查数量
if (!await checkCanChooseSkillCard(roleId, gameCode, id, dbChara.cards))
return resResult(STATUS.ROUGE_PASSIVE_CARD_NOT_ENOUGH);
}
let chara = await RougelikeCharaModel.putOnOrOffSkillCard(gameCode, charaCode, skillType, id);
if (!dbChara) return resResult(STATUS.NO_CARD);
// 更新图鉴
await addSingleCollect(roleId, sid, gameCode, COLLECTION_TYPE.SKILL_CARD, id);
// 任务
let dicSkillCard = gameData.rougeSkillCard.get(id);
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_AUTHOR_CARD_COLLECT, { authorType: dicSkillCard.authorType, cardType: TASK_ROUGE_CARD_TYPE.SKILL, cardIds: [id] });
return resResult(STATUS.SUCCESS, {
curChara: new CommonChara(chara)
})
}
/**
* 安装或者卸下特性卡
* @param msg
* @param session
*/
async putOnOrOffCard(msg: { gameCode: string, charaCode: string, cards: SlotCard[] }, session: BackendSession) {
let roleId: string = session.get('roleId');
let { gameCode, charaCode, cards } = msg;
// 获取角色卡db数据
let dbChara = await RougelikeCharaModel.findByCode(gameCode, charaCode);
if (!dbChara) return resResult(STATUS.NO_CARD);
if (!dbChara.cards || dbChara.cards.length == 0) return resResult(STATUS.SLOT_UNLOCK);
let dbCharaCardsMap = dbChara.cards.reduce((result, cur) => { result.set(cur.index, cur); return result; }, new Map<number, Card>());
// 获取需要安装、卸下的特性卡db数据
let cardCodes: string[] = [];
cards.forEach((ele) => { cardCodes.push(ele.cardCode); });
dbChara.cards.forEach((ele) => { cardCodes.push(ele.cardCode); })
let dbCards: RougelikeCardType[] = await RougelikeCardModel.findByGameCodeAndCardCodes(gameCode, cardCodes);
if (dbCards.length == 0) return resResult(STATUS.NO_CARD);
let dbCardsMap = dbCards.reduce((result, cur) => { result.set(cur.cardCode, cur); return result; }, new Map<string, RougelikeCardType>());
let newCardsMap = new Map<string, RougelikeCardType>();
for (let { index, cardCode } of cards) {
// 检测卡槽解锁
let slot = dbCharaCardsMap.get(index)
if (!slot) return resResult(STATUS.SLOT_UNLOCK);
//处理旧的cardCode
let oldSotCardCode = clone(slot.cardCode);
let updateObj = dbCardsMap.get(oldSotCardCode);
if (updateObj) {
updateObj.charaId = 0;
dbCardsMap.set(oldSotCardCode, updateObj);
newCardsMap.set(oldSotCardCode, updateObj);
}
//处理新的cardCode安装
let update = dbCardsMap.get(cardCode);
dbCharaCardsMap.set(index, { index, cardCode: cardCode, cardId: update?.cardId || 0 });
if (update) {
if (update.type != ROUGE_LIKE_CARD_TYPE.PASSIVE) return resResult(STATUS.NO_PASSIVE);
update.charaId = dbChara.charaId;
dbCardsMap.set(cardCode, update);
newCardsMap.set(cardCode, update);
}
}
let newCards = Array.from(dbCharaCardsMap.values());
let tempCards = new Map<number, number>();
for (let { cardId } of newCards) {
if (!cardId) continue;
if (tempCards.has(cardId)) return resResult(STATUS.ROUGE_NO_CAN_PUT_SAME_ID);
tempCards.set(cardId, cardId);
}
// 如果卸下之后技能卡需要的被动卡不够,需要把技能卡卸下
let { apSkill, roundSkill } = dbChara;
if (apSkill > 0 && !await checkCanChooseSkillCard(roleId, gameCode, apSkill, newCards)) apSkill = 0;
if (roundSkill > 0 && !await checkCanChooseSkillCard(roleId, gameCode, roundSkill, newCards)) roundSkill = 0;
//更新角色卡db, 特性卡db
let newChara = await RougelikeCharaModel.updateByCode(gameCode, charaCode, { $set: { cards: newCards, apSkill, roundSkill } });
await RougelikeCardModel.bulkWriteUpdate(Array.from(newCardsMap.values()).map(obj => ({ gameCode, cardCode: obj.cardCode, charaId: obj.charaId })))
let curCards = Array.from(dbCardsMap.values()).map(obj => ({ cardCode: obj.cardCode, type: obj.type, charaId: obj.charaId, lv: obj.lv }));
return resResult(STATUS.SUCCESS, { curCards, curChara: new CommonChara(newChara) })
}
async exchangeChara(msg: { gameCode: string, oldCharaCode: string, newCharaCode: string }, session: BackendSession) {
let roleId: string = session.get('roleId');
const { gameCode, oldCharaCode, newCharaCode } = msg;
const result = await RougelikeCharaModel.findByCodes(gameCode, [oldCharaCode, newCharaCode]);
if (result.length != 2) return (STATUS.NO_CARD);
let oldChara = {} as RougelikeCharaType, newChara = {} as RougelikeCharaType;
for (let val of result) {
if (val.charaCode == oldCharaCode) oldChara = val;
if (val.charaCode == newCharaCode) newChara = val;
}
let temp = clone(newChara.seqId);
newChara.seqId = clone(oldChara.seqId);
oldChara.seqId = temp;
// 更新角色卡
await RougelikeCharaModel.bulkWriteUpdate([oldChara, newChara]);
let charas = formateCharasOrCards([oldChara, newChara], ROUGE_LIKE_CARD_TYPE.CHARA).charas;
// // 处理新卡
// let { charaCode, charaId, maxHp, seqId } = newChara;
// newChara = clone(oldChara);
// newChara.charaCode = charaCode;
// newChara.charaId = charaId;
// newChara.maxHp = maxHp;
// let cardCodes = [];
// let updateArr: RougelikeCardPara[] = [];
// for (let val of newChara.cards) {
// cardCodes.push(val.cardCode);
// updateArr.push({ gameCode, cardCode: val.cardCode, charaId });
// }
// // 处理旧卡
// oldChara.cards.forEach((cur) => { cur.cardCode = '', cur.cardId = 0 });
// oldChara.ap = oldChara.apSkill = oldChara.roundSkill = 0;
// oldChara.seqId = seqId;
// // 更新角色卡
// await RougelikeCharaModel.bulkWriteUpdate([oldChara, newChara]);
// let cards = formateCharasOrCards(await RougelikeCardModel.findByGameCodeAndCardCodes(gameCode, cardCodes), ROUGE_LIKE_CARD_TYPE.PASSIVE).cards;
// // 更新card
// await RougelikeCardModel.bulkWriteUpdate(updateArr);
// let charas = formateCharasOrCards([oldChara, newChara], ROUGE_LIKE_CARD_TYPE.CHARA).charas;
return resResult(STATUS.SUCCESS, { charas });
}
/**
* 解锁科技点
* @param msg
* @param session
*/
async unlockTech(msg: { techId: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let sid: string = session.get('sid');
let serverId: number = session.get('serverId');
const { techId } = msg;
let dicTech = gameData.rougeTech.get(techId);
if (!dicTech) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let techRecord = await RougelikeTechModel.findByRoleId(roleId);
let { unlockedTech = [] } = techRecord || {};
if (unlockedTech.includes(techId)) return resResult(STATUS.ROUGE_TECH_HAS_UNLOCKED);
if (!checkPreRougeTech(techId, unlockedTech)) return resResult(STATUS.ROUGE_TECH_PRE_NOT_UNLOCKED);
// 检查消耗
let costResult = await handleCost(roleId, sid, dicTech.cost, ITEM_CHANGE_REASON.ROUGE_UNLOCK_TECH);
if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
let circleIds = gameData.rougeCircleByTech.get(techId) || [];
techRecord = await RougelikeTechModel.unlockTech(roleId, techId, circleIds);
if (!techRecord) return resResult(STATUS.ROUGE_TECH_SCORE_NOT_ENOUGH);
await updateEffectId(techRecord);
let resultData = getCurTechData(techRecord, techId);
//任务
await checkTask(serverId, roleId, sid, TASK_TYPE.ROUGE_TECH_OPEN, { count: 1 });
return resResult(STATUS.SUCCESS, {
...resultData
});
}
/**
* 放置法阵
* @param msg
* @param session
* @returns
*/
async putOnCircle(msg: { circleId: number, hid: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
const { circleId, hid } = msg;
let techId = gameData.rougeTechCircle.get(circleId);
if (!techId) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let dicTech = gameData.rougeTech.get(techId);
if (!dicTech) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let techRecord = await RougelikeTechModel.findByRoleId(roleId);
if (!techRecord) return resResult(STATUS.ROUGE_CIRCLE_NOT_UNLOCK);
let { unlockedTech = [] } = techRecord;
if (!unlockedTech.includes(techId)) return resResult(STATUS.ROUGE_CIRCLE_NOT_UNLOCK);
let ce = 0;
if (hid != 0) {
let hero = await HeroModel.findByHidAndRole(hid, roleId, 'ce');
if (!hero) return resResult(STATUS.HERO_NOT_FIND);
ce = hero.ce || 0;
}
techRecord = await RougelikeTechModel.putOnCircle(roleId, circleId, hid, ce);
if (!techRecord) return resResult(STATUS.ROUGE_CIRCLE_NOT_UNLOCK);
await updateEffectId(techRecord);
let resultData = getCurTechData(techRecord, techId);
return resResult(STATUS.SUCCESS, {
...resultData
});
}
/**
* 领取图鉴奖励
* @param msg
* @param session
* @returns
*/
async receiveCollectionReward(msg: { type: number, id: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let roleName: string = session.get('roleName');
let sid: string = session.get('sid');
let { type, id } = msg;
let dic: DicRougePassiveCollect | DicRougeHolyCard | DicRougeSkillCard | DicRougeOptionGroup;
let num = 1;
if (type == COLLECTION_TYPE.PASSIVE_CARD_SUM) {
dic = gameData.rougePassiceCollect.get(id);
num = dic?.num || 0;
} else if (type == COLLECTION_TYPE.HOLY_CARD) {
dic = gameData.rougeHolyCard.get(id);
} else if (type == COLLECTION_TYPE.SKILL_CARD) {
dic = gameData.rougeSkillCard.get(id);
} else if (type == COLLECTION_TYPE.EVENT) {
dic = gameData.rougeOptionGroup.get(id);
}
if (!dic) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let collect = await RougelikeCollectionModel.findByRoleAndId(roleId, type, id);
if (!collect) return resResult(STATUS.ROUGE_COLLECT_NOT_ENOUGH);
if (collect.received.includes(id)) return resResult(STATUS.ROUGE_COLLECT_HAS_RECEIVED);
collect = await RougelikeCollectionModel.receive(roleId, type, id, num);
if (!collect) return resResult(STATUS.ROUGE_COLLECT_NOT_ENOUGH);
let goods = await addItems(roleId, roleName, sid, dic.collectReward, ITEM_CHANGE_REASON.RECEIVE_COLLECT_REWARD);
return resResult(STATUS.SUCCESS, {
goods, curCollection: new CollectionReturnParam(collect)
});
}
/**
* 领取积分奖励
* @param msg
* @param session
* @returns
*/
async receiveScore(msg: { index: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let sid: string = session.get('sid');
let roleName: string = session.get('roleName');
let { index } = msg;
const lastMaxLv = await RougelikeExtendModel.getLastMaxLv(roleId);
const rougeScoreRewardByLvData = gameData.rougeScoreRewardByLv.get(lastMaxLv);
if (!rougeScoreRewardByLvData) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let dicScore = rougeScoreRewardByLvData.get(index);
if (!dicScore) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let scoreData = await RougelikeScoreModel.findByRoleId(roleId);
if (!scoreData || scoreData.score < dicScore.score) return resResult(STATUS.ROUGE_SCORE_NOT_ENOUGH);
if (scoreData.received.includes(index)) return resResult(STATUS.ROUGE_SCORE_HAS_RECEIVED);
scoreData = await RougelikeScoreModel.receive(roleId, dicScore.score, index);
if (!scoreData) return resResult(STATUS.ROUGE_SCORE_NOT_ENOUGH);
let goods = await addItems(roleId, roleName, sid, dicScore.reward, ITEM_CHANGE_REASON.RECEIVE_ROUGE_SCORE_REWARD);
return resResult(STATUS.SUCCESS, {
receivedScore: scoreData.received,
goods
});
}
async challengeRandom(msg: { detailCode: string }, session: BackendSession) {
let roleId: string = session.get('roleId');
const { detailCode } = msg;
let dbDetail = await RougelikeRecordDetailModel.findByDetailCode(detailCode);
if (!dbDetail) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
let { gameCode, layer, nodeId, nodeType, rewards = [], challenge } = dbDetail;
if (rewards.length > 0 || nodeType != ROUGE_LIKE_NODE_TYPE.CHALLENGE || (challenge?.status || 0) < 2) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
const dbRecord = await RougelikeRecordModel.findByGameCode(gameCode);
if (!dbRecord) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
const { authorType, type, grade } = dbRecord;
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
const layerPlanData = gameData.rougeLayerPlan.get(typeGradeData.layerPlan + '_' + layer);
if (!layerPlanData) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
let result = await getLayerNodeReward(roleId, gameCode, authorType, nodeId, layerPlanData.rewardPlan, layer);
if (!result || !result.rewards) return resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
rewards = result.rewards;
dbDetail = await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: { rewards } });
if (!dbDetail || !dbDetail.rewards) resResult(STATUS.ROUGE_CHALLENGE_RANDOM_FAIL);
return resResult(STATUS.SUCCESS, { gameCode, detailCode, reward: { rewards: dbDetail.rewards } })
}
/**
* debug接口清空科技树
* @param msg
* @param session
* @returns
*/
async debugClearTech(msg: {}, session: BackendSession) {
let roleId: string = session.get('roleId');
await RougelikeTechModel.clearTech(roleId);
return resResult(STATUS.SUCCESS);
}
async debugAddCard(msg: { type: number, id: number }, session: BackendSession) {
let { type, id } = msg;
let roleId: string = session.get('roleId');
let sid: string = session.get('sid');
const dbRecord = await RougelikeRecordModel.findByRoleIdAndStatus(roleId, ROUGE_LIKE_STATUS.SUCCESS);
if (!dbRecord) {
return resResult(STATUS.NO_ROUGELIKE_GAME);
}
let handleAddFun = new HandleAddCard(session, dbRecord.gameCode);
if (type == ROUGE_LIKE_CARD_TYPE.CHARA) {
handleAddFun.pushChara(id, await getMaxHp(roleId, dbRecord?.gameCode || '', id, dbRecord.type, dbRecord.grade), []);
} else if (type == ROUGE_LIKE_CARD_TYPE.HOLY) {
handleAddFun.pushHolyCard(id);
} else if (type == ROUGE_LIKE_CARD_TYPE.PASSIVE) {
handleAddFun.pushPassiveCard(id);
} else {
return resResult(STATUS.WRONG_PARMS);
}
let result = await handleAddFun.save();
result = await handleAddFun.getHolyEffect();
return resResult(STATUS.SUCCESS, result);
}
// debug接口
async debugAddCollection(msg: { type: number, id: number }, session: BackendSession) {
let { type, id } = msg;
let roleId: string = session.get('roleId');
let sid: string = session.get('sid');
const dbRecord = await RougelikeRecordModel.findByRoleIdAndStatus(roleId, ROUGE_LIKE_STATUS.SUCCESS);
if (!dbRecord) {
return resResult(STATUS.NO_ROUGELIKE_GAME);
}
await addSingleCollect(roleId, sid, dbRecord.gameCode, type, id);
return resResult(STATUS.SUCCESS);
}
async debugAddScore(msg: { score: number }, session: BackendSession) {
let { score } = msg;
let roleId: string = session.get('roleId');
let result = await RougelikeScoreModel.incScore(roleId, score);
return resResult(STATUS.SUCCESS, { weeklyScore: result.score })
}
async debugRepaireScoreReward() {
await repaireSendScoreReward();
return resResult(STATUS.SUCCESS);
}
/**
* debug添加试炼通关
* @param msg
* @param session
* @returns
*/
async debugAddLimitId(msg: { id: number }, session: BackendSession) {
const { id } = msg;
const roleId: string = session.get('roleId');
const typeGradeByIdData = gameData.rougeTypeGradeById.get(id);
if (typeGradeByIdData) {
// for (let i = 3; i <= 42; i++) {
await RougelikeExtendModel.update(roleId, id, [], genCode(8));
// }
}
return resResult(STATUS.SUCCESS);
}
}