import { Application, BackendSession, HandlerService, } from "pinus"; import { resResult, shouldRefresh, getRandSingleEelm, getGachaRemainFloor } from "../../../pubUtils/util"; import { STATUS, GACHA_TYPE, HERO_QUALITY_TYPE, TASK_TYPE, ITEM_CHANGE_REASON, } from "../../../consts"; import { gameData } from "../../../pubUtils/data"; import { UserGachaModel } from "../../../db/UserGacha"; import { refreshGacha, getGachaList, getVisitedHeroList, GachaPull, GachaResults, getDicGachaByGachaCnt, getNormalGachaId, getDicGachas } from "../../../services/activity/gachaService"; import { RoleModel } from "../../../db/Role"; import { HeroModel } from "../../../db/Hero"; import { handleCost, addItems } from "../../../services/role/rewardService"; import { getZeroPointD, getTimeFun } from "../../../pubUtils/timeUtil"; import { UserGachaRecModel } from "../../../db/UserGachaRec"; import { RECRUIT } from "../../../pubUtils/dicParam"; import { getActivityById } from "../../../services/activity/activityService"; import { checkTaskInGacha } from "../../../services/task/taskService"; import { createHeroes } from "../../../services/role/createHero"; import { getGuideGachaData } from "../../../services/activity/gachaService"; import { getPlayerNewHeroGachaData } from "../../../services/activity/newHeroService"; import { isHeroHidden } from "../../../services/dataService"; import { GachaResultIndb } from "../../../domain/activityField/gachaField"; import { CreateHeroParam } from "../../../domain/roleField/hero"; import { ItemInter, RewardInter } from "../../../pubUtils/interface"; export default function (app: Application) { new HandlerService(app, {}); return new GachaHandler(app); } export class GachaHandler { constructor(private app: Application) { } /** * @description 获取抽卡列表 元宝招募、友情点招募、求贤若渴 * @param {{}} msg * @param {BackendSession} session * @memberof GachaHandler */ async getGachaList(msg: {}, session: BackendSession) { const { } = msg; const roleId: string = session.get('roleId'); let role = await RoleModel.findByRoleId(roleId, 'gachaHasGuide'); const list = await getGachaList(roleId); return resResult(STATUS.SUCCESS, { hasInit: !!role.gachaHasGuide, list }); } /** * @description 抽卡,所有抽卡类型都 * @param {{ gachaId: number, activityId: number, count: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async pull(msg: { gachaId: number, activityId: number, count: number }, session: BackendSession) { const { gachaId, activityId, count } = msg; const roleId: string = session.get('roleId'); const roleName: string = session.get('roleName'); const sid: string = session.get('sid'); const serverId: number = session.get('serverId'); let dicGacha = gameData.gacha.get(gachaId); if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND); let userGacha = await UserGachaModel.findByRole(roleId, gachaId, activityId); userGacha = await refreshGacha(dicGacha, userGacha); let { freeCount, pickHero, count: historyCount, hope, floor } = userGacha; if ((dicGacha.gachaType == GACHA_TYPE.ASSIGN || dicGacha.gachaType == GACHA_TYPE.ACTIVITY) && !pickHero) return resResult(STATUS.GACHA_NOT_ASSIGN); let resultList: GachaResultIndb[] = [], heroInfo: CreateHeroParam[] = [], items: RewardInter[] = [], consume: ItemInter[] = []; for(let { dic, min, max } of getDicGachas(dicGacha.gachaType, historyCount, count)) { if(dicGacha.gachaType != GACHA_TYPE.NORMAL && dic.id != gachaId) continue; // console.log('##########', dic, min, max) let _count = max + 1 - min; let gachaPull = new GachaPull(dic, { hope, floor, pickHero }); let userHeroes = await HeroModel.findByRole(roleId); let { resultList: _resultList, heroInfo: _heroInfo, items: _items } = gachaPull.pull(_count, userHeroes); let { hope: _hope, floor: _floor } = gachaPull.getUserGachaParam(); resultList.push(..._resultList); heroInfo.push(..._heroInfo); items.push(..._items); hope = _hope; floor = _floor; let costNum = _count; if (count == 1 && dic.free.count > 0) { // 单抽的时候免费 if (count > dic.free.count - freeCount) { costNum = _count - dic.free.count + freeCount; freeCount = dic.free.count; } else { costNum = 0; freeCount += _count; } } // 消耗东西 if (costNum > 0) { let cost = dicGacha.cost.map(cur => { return { id: cur.id, count: cur.count * costNum } }); consume.push(...cost); } } // console.log('##### consume', consume) // 消耗东西 if (consume.length > 0) { let costResult = await handleCost(roleId, sid, consume, ITEM_CHANGE_REASON.GACHA_PULL); if (!costResult) return resResult(STATUS.GACHA_COST_NOT_ENOUGH); } // 给东西 // console.log('****', heroInfo) let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo); await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS); // 更新数据 userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { freeCount, hope, floor, count: historyCount + count }); let newDicGacha = getDicGachaByGachaCnt(dicGacha, userGacha.count); await UserGachaRecModel.createRec(roleId, gachaId, activityId, count, resultList); if(dicGacha.isTask) await checkTaskInGacha(serverId, roleId, sid, count, resultHeroes, resultList, false); return resResult(STATUS.SUCCESS, { gachaId, activityId, isFree: freeCount < newDicGacha.free.count, cost: newDicGacha.cost, count: userGacha.count, point: userGacha.point, floor, hope, // heroes: resultHeroes, addHeros: heroes, result: resultList, remainFloor: getGachaRemainFloor(gachaId, floor) }); } /** * @description 设置心愿单 * @param {{ gachaId: number, hope: { id: number, hid: number }[] }} msg * @param {BackendSession} session * @memberof GachaHandler */ async setHope(msg: { gachaId: number, hope: { id: number, hid: number }[] }, session: BackendSession) { const { gachaId, hope } = msg; const roleId: string = session.get('roleId'); for (let { hid } of hope) { if (hid != 0) { let dicRecruit = gameData.recruit.get(hid); if (!dicRecruit || !dicRecruit.canHope) { return resResult(STATUS.GACHA_HOPE_NOT_GOLD); } } } let userGacha = await UserGachaModel.findByRole(roleId, gachaId, 0); let { hope: userHope = []} = await refreshGacha(gameData.gacha.get(gachaId), userGacha); for (let { id, hid } of hope) { let curHope = userHope.find(cur => cur.id == id); if(curHope) { if(curHope.hasGet) { continue; } else { curHope.hid = hid; curHope.hasGet = false; } } else { userHope.push({ id, hid, hasGet: false }) } } userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { hope: userHope }); return resResult(STATUS.SUCCESS, { gachaId, hope: userGacha.hope }); } /** * @description 转盘 * @param {{ gachaId: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async drawTurnTable(msg: { gachaId: number }, session: BackendSession) { const { gachaId } = msg; const roleId: string = session.get('roleId'); const roleName: string = session.get('roleName'); const sid: string = session.get('sid'); const serverId: number = session.get('serverId'); let userGacha = await UserGachaModel.findByRole(roleId, gachaId, 0); let { point, turntable, costPoint } = userGacha; if (point < RECRUIT.RECRUIT_BONUS_RECRUIT) return resResult(STATUS.GACHA_TURNTABLE_POINT_NOT_ENOUGH); let turntablePool: { quality: number, count: number, planId: number }[] = []; for (let { quality, count, planId } of gameData.gachaTurntable) { let myTurntable = turntable.find(cur => cur.quality == quality); if (!myTurntable || !myTurntable.hasGet) { turntablePool.push({ quality, count, planId }); } } if (turntablePool.length <= 0) { // 重置 turntable = []; turntablePool = gameData.gachaTurntable; } let randTurntable = getRandSingleEelm(turntablePool); let { quality, count, planId } = randTurntable; let userHeroes = await HeroModel.findByRole(roleId); let gachaResults = new GachaResults(); gachaResults.addPlan(planId); gachaResults.processDetail(null, count); let { items, heroInfo, resultList } = gachaResults.transferToFinalResult(userHeroes); // 给东西 // console.log('****', heroInfo) let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo); await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS); // 更新数据 await UserGachaRecModel.createRec(roleId, 0, 0, count, resultList); // 记录 let myTurntable = turntable.find(cur => cur.quality == quality); if (!myTurntable) { myTurntable = { quality, hasGet: false }; turntable.push(myTurntable); } myTurntable.hasGet = true; // 扣除积分 userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { turntable, costPoint: costPoint + 1 }); return resResult(STATUS.SUCCESS, { gachaId, point: userGacha.point, turntable: userGacha.turntable, result: resultList, addHeros: heroes, // heroes: resultHeroes }); } /** * @description 求贤若渴&限时招募里面设置pick武将 * @param {{ gachaId: number, pickHero: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async setPickHero(msg: { gachaId: number, activityId: number, pickHero: number }, session: BackendSession) { const { gachaId, activityId = 0, pickHero } = msg; const roleId: string = session.get('roleId'); const serverId: number = session.get('serverId'); if(isHeroHidden(pickHero)) return resResult(STATUS.HERO_IS_HIDDEN); let dicHero = gameData.hero.get(pickHero); if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND); if(activityId > 0) { let data = await getPlayerNewHeroGachaData(activityId, serverId, roleId); if(!data.isPickHero(pickHero)) return resResult(STATUS.GACHA_CAN_NOT_PICK); } let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { pickHero }); return resResult(STATUS.SUCCESS, { gachaId, activityId, pickHero: userGacha.pickHero }); } /** * @description 今天拜访过的武将 * @param {{ }} msg * @param {BackendSession} session * @memberof GachaHandler */ async getVisitedHero(msg: {}, session: BackendSession) { const roleId: string = session.get('roleId'); let visitedHero = await getVisitedHeroList(roleId); return resResult(STATUS.SUCCESS, { hids: visitedHero }); } /** * @description 拜访武将 * @param {{ hid: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async visitHero(msg: { hid: number }, session: BackendSession) { const roleId: string = session.get('roleId'); const roleName: string = session.get('roleName'); const sid: string = session.get('sid'); const { hid } = msg; if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN); let dicHero = gameData.hero.get(hid); if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND); let dicRecruit = gameData.recruit.get(hid); if (!dicRecruit || !dicRecruit.canVisit) return resResult(STATUS.GACHA_CAN_NOT_PICK); let { pieceId } = dicHero; let gachaId = getNormalGachaId(); let { visitedHero, refVisitedTime } = await UserGachaModel.findByRole(roleId, gachaId, 0); if (shouldRefresh(refVisitedTime, new Date())) { visitedHero = []; refVisitedTime = getZeroPointD(); } if (visitedHero.includes(hid)) { return resResult(STATUS.GACHA_HAS_VISITED); } if (visitedHero.length >= RECRUIT.RECRUIT_DAILY_RECRUIT_SHARD) { return resResult(STATUS.GACHA_VISITED_COUNT_OVER); } visitedHero.push(hid); let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { visitedHero, refVisitedTime }); let goods = await addItems(roleId, roleName, sid, [{ id: pieceId, count: RECRUIT.RECRUIT_SHARD_LIMIT }], ITEM_CHANGE_REASON.VISIT_HERO); return resResult(STATUS.SUCCESS, { hids: userGacha.visitedHero, goods }); } /** * @description 获取特殊抽卡数据 * @param {{}} msg * @param {BackendSession} session * @memberof GachaHandler */ async getGuideGachaData(msg: { activityId: number }, session: BackendSession) { const roleId: string = session.get('roleId'); const serverId: number = session.get('serverId'); const { activityId } = msg; if(!activityId) return resResult(STATUS.SUCCESS); let playerData = await getGuideGachaData(serverId, activityId, roleId); return resResult(STATUS.SUCCESS, { playerData }); } /** * @description 预抽卡 * @param {{}} msg * @param {BackendSession} session * @memberof GachaHandler */ async guidePull(msg: { activityId: number; }, session: BackendSession) { const roleId: string = session.get('roleId'); const serverId: number = session.get('serverId'); const sid: string = session.get('sid'); const count = 10; const { activityId } = msg; let playerData = await getGuideGachaData(serverId, activityId, roleId); if(!playerData) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK); const gachaId = playerData.gachaId; const dicGacha = gameData.gacha.get(gachaId); if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND); if(!playerData.canPull()) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK); if(playerData.pullCnt == 0) { // 消耗东西 let cost = dicGacha.cost.map(cur => { return { id: cur.id, count: cur.count * count } }); let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.GACHA_PULL); if (!costResult) return resResult(STATUS.GACHA_COST_NOT_ENOUGH); } let userHeroes = await HeroModel.findByRole(roleId); let gachaPull = new GachaPull(dicGacha); let { resultList } = gachaPull.pull(count, userHeroes); let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { guideResultList: resultList }, { pullCnt: 1 }); playerData.setPlayerData(userGacha); if(playerData.autoSave()) { // 最后几次自动保存 await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates }); } return resResult(STATUS.SUCCESS, { activityId, pullCnt: playerData.pullCnt, result: playerData.latest, candidates: playerData.candidates }); } /** * @description 将结果保存到候选列表 * @param {{ id: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async saveToCandidates(msg: { activityId: number, id: number }, session: BackendSession) { const roleId: string = session.get('roleId'); const serverId: number = session.get('serverId'); const { activityId, id } = msg; let playerData = await getGuideGachaData(serverId, activityId, roleId); if(!playerData) return resResult(STATUS.GACHA_GUIDE_PULL_CNT_LACK); let gachaId = playerData.gachaId; const dicGacha = gameData.gacha.get(gachaId); if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND); let result = playerData.setCandidate(id); if(!result) return resResult(STATUS.GACHA_GUIDE_NOT_DO) let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates }); playerData.setPlayerData(userGacha); return resResult(STATUS.SUCCESS, { activityId, pullCnt: playerData.pullCnt, candidates: playerData.candidates }); } /** * @description 确定使用某个结果 * @param {{ id: number }} msg * @param {BackendSession} session * @memberof GachaHandler */ async decide(msg: { activityId: number, id: number }, session: BackendSession) { const { activityId, id } = msg; const roleId: string = session.get('roleId'); const roleName: string = session.get('roleName'); const serverId: number = session.get('serverId'); const sid: string = session.get('sid'); const count = 10; let playerData = await getGuideGachaData(serverId, activityId, roleId); if(!playerData || !playerData.canDecide()) return resResult(STATUS.GACHA_GUIDE_HAS_DONE); let gachaId = playerData.gachaId; const dicGacha = gameData.gacha.get(gachaId); if (!dicGacha) return resResult(STATUS.DIC_DATA_NOT_FOUND); let candidate = playerData.chooseCandidate(id); if(!candidate) return resResult(STATUS.GACHA_GUIDE_NO_CANDIDATE); let userHeroes = await HeroModel.findByRole(roleId); let results = new GachaResults(); results.addBySimpleResult(candidate.list); let { items, heroInfo, resultList } = results.transferToFinalResult(userHeroes); let userGacha = await UserGachaModel.updateInfo(roleId, gachaId, activityId, { candidates: playerData.candidates }); playerData.setPlayerData(userGacha); // 给东西 let { heroes, resultHeroes } = await createHeroes(roleId, roleName, sid, serverId, heroInfo); await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.GACHA_ITEMS); // 更新数据 await UserGachaRecModel.createRec(roleId, gachaId, 0, count, resultList); // 任务 if(dicGacha.isTask) await checkTaskInGacha(serverId, roleId, sid, count, resultHeroes, resultList, true); return resResult(STATUS.SUCCESS, { activityId, pullCnt: playerData.pullCnt, addHeros: heroes, result: resultList }); } async debugClearGachaRecord(msg: {}, session: BackendSession) { const roleId = session.get('roleId'); let serverId = session.get('serverId'); await UserGachaModel.deleteMany({ roleId }); return resResult(STATUS.SUCCESS); } async test(msg: { gachaId: number, count: number, isGuide: boolean }, session: BackendSession) { const { gachaId, count, isGuide } = msg; const roleId: string = session.get('roleId'); const sid: string = session.get('sid'); const dicGacha = gameData.gacha.get(gachaId); console.log(`====== map of content ${gachaId} ====== start`) let mapOfContent = new Map(); let hope = []; let floor = []; let n = Math.ceil(count/10/200); let sum = 0; function cal(num: number) { for(let i = 0; i < 200; i++) { console.log(num, i) let gachaPull = new GachaPull(dicGacha, { floor, hope, pickHero: 1 }); let { resultList } = gachaPull.pull(10, []); let { hope: _hope, floor: _floor } = gachaPull.getUserGachaParam(); floor = _floor; hope = _hope; gachaPull = undefined; for(let { planId } of resultList ) { if(!mapOfContent.has(planId)) { mapOfContent.set(planId, 1); } else { mapOfContent.set(planId, mapOfContent.get(planId) + 1); } sum++; } } } let num = 0; cal(num); let interval = setInterval(() => { if (++num < n) { cal(num); } else { console.log(`====== map of content ${gachaId} sum: ${sum} ======`) console.log('planId \t count') for(let [ planId, count ] of mapOfContent) { console.log(`${planId} \t ${count}`); } clearInterval(interval); } }, 50) return resResult(STATUS.SUCCESS); } }