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 } 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, 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'; 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(roleId, sid, 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 startGame(msg: { gameCode: string, authorType: number }, session: BackendSession) { let roleId = session.get('roleId'); let sid = session.get('sid'); 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 } }); const charas: CommonChara[] = formateCharasOrCards(await RougelikeCharaModel.findByGameCode(gameCode), ROUGE_LIKE_CARD_TYPE.CHARA)?.charas || []; let handleAddFun = new HandleAddCard(roleId, sid, 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'); 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()); let spiritIds: number[] = []; let addCoin = 0, addScore = 0, addTech = 0, isFirstReward = true; if (dbLayers.length == 0) isFirstReward = false; for (const { layer, hasPass = false, layerNodes } of dbLayers) { if (layer > dbRecord.curLayer) { isFirstReward = 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) { isFirstReward = 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 = [], coin = 0, score = 0, tech = 0, spiritPlan } = clone(layerReward); addCoin += coin, addScore += score, addTech += tech; for (const { id, count } of takeoutReward) takeoutRewardMap.set(id, takeoutRewardMap.get(id) || 0 + count); spiritIds.push(...getRandomSpirit(spiritPlan)); } let spiritMap = new Map(); 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, coin: addCoin, techScore: addTech } }); let { score = 0, coin = 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 (isFirstReward) { 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); } } return resResult(STATUS.SUCCESS, { gameCode, goods, score, techScore, coin, 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 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); } return resResult(STATUS.SUCCESS, { detailCode, layer, curNode: await chooseNode(dbRecord, dbCurLayerChooseNode, layer) }) } // 挑战关卡 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 { 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()); let isAp: boolean = false, isRound: boolean = 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); } await updateChalleng(dbRecord, roleId, sid, gameCode, layer, rougeDamage, isAp, isRound); let updateCharas: RougelikeCharaPara[] = [], charas: CommonChara[] = []; let recoveryHp = false; if (status != 0 && nodeType != ROUGE_LIKE_NODE_TYPE.BOSS) { 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 = chara.maxHp; if (hpRatio > 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 } }); } // 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) } else { await RougelikeCharaModel.bulkWriteUpdate(updateCharas); } 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); let { rewards } = dbDetail; 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(roleId, sid, 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 (rewardType == ROUGE_LIKE_CARD_TYPE.PASSIVE) { groupStatus = ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE; if (chooseNum - passiveChooseCount == optionIndexs.length) { groupStatus = ROUGE_LIKE_CHOOSE_REWARD.CHOOSE; await updateChalleng(dbRecord, roleId, sid, gameCode, layer, []); // 更新挑战进度 } } rewards.forEach((ele) => { if (ele.rewardType == rewardType) ele.groupStatus = groupStatus; }); // 挑战领奖 let dbPara = {}; if (rewards.find((obj) => obj.groupStatus != ROUGE_LIKE_CHOOSE_REWARD.CHOOSE && rewardType != ROUGE_LIKE_CARD_TYPE.CHARA) == undefined) { if (nodeType == ROUGE_LIKE_NODE_TYPE.CHALLENGE) { challenge.status = 3; dbPara['challenge'] = challenge; } await RougelikeLayerModel.updateByGameCode(gameCode, layer, { $set: { hasPass: true } }) } await RougelikeRecordDetailModel.updateRewardByGroupIndex(gameCode, detailCode, groupIndex, groupStatus, options, { $set: dbPara }); 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.price) 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.price } }); let handleAddFun = new HandleAddCard(roleId, sid, 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.price }); } /** * 结束这一节点 * @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'); 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(roleId, sid, 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; let cardId = 0; const rougeHolyCardPlanData = gameData.rougeHolyCardPlan.get(holyCardPlan); if (!rougeHolyCardPlanData || rougeHolyCardPlanData.length == 0) continue; if (rougeHolyCardPlanData.length == 1) cardId = rougeHolyCardPlanData[0].cardId; else cardId = getRandEelmWithWeight(rougeHolyCardPlanData).dic.cardId; 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); 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, maxHp = 0, ap, shield, roundSkill, apSkill } of dbCharas) { 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, 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()); 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 || []; 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'); 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); 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()); // 获取需要安装、卸下的特性卡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()); let newCardsMap = new Map(); 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 { 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'); 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); 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; let dicScore = gameData.rougeScoreReward.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 }); } /** * 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(roleId, sid, 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); } }