diff --git a/game-server/app/servers/activity/handler/chongyangHandler.ts b/game-server/app/servers/activity/handler/chongyangHandler.ts new file mode 100644 index 000000000..afb3691ce --- /dev/null +++ b/game-server/app/servers/activity/handler/chongyangHandler.ts @@ -0,0 +1,125 @@ +import { Application, BackendSession, HandlerService, } from 'pinus'; +import { genCode, resResult } from '../../../pubUtils/util'; +import { ITEM_CHANGE_REASON, STATUS } from '../../../consts'; +import { getPlayerChongYangData, getPlayerChongYangDataShow } from '../../../services/activity/chongyangService'; +import { ActivityChongYangRecModel } from '../../../db/ActivityChongYangRec'; +import { RewardInter } from '../../../pubUtils/interface'; +import { stringToRewardParam } from '../../../services/activity/giftPackageService'; +import { addItems } from '../../../services/role/rewardService'; + + +export default function (app: Application) { + new HandlerService(app, {}); + return new ChongYangHandler(app); +} + +export class ChongYangHandler { + constructor(private app: Application) { + } + + async getData(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerChongYangDataShow(activityId, serverId, roleId); + + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } + + async gameStart(msg: { activityId: number, dayIndex: number }, session: BackendSession) { + const { activityId, dayIndex } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerChongYangData(activityId, serverId, roleId); + if (!playerData || !playerData.sceneMap) return resResult(STATUS.ACTIVITY_MISSING); + let scene = playerData.sceneMap.get(dayIndex); + if (!scene) return resResult(STATUS.ACTIVITY_MISSING); + + // 检测挑战次数 + if (scene.playCnt >= scene.maxPlayCnt) return resResult(STATUS.ACTIVITY_CHONGYANG_NO_NUM); + // 检测解锁 + if (dayIndex > playerData.dayIndexUnlock) return resResult(STATUS.ACTIVITY_CHONGYANG_LOCK); + + const gameCode = genCode(10); + await ActivityChongYangRecModel.gameRecord(serverId, activityId, playerData.roundIndex, roleId, { + todayIndex: playerData.todayIndex, gameCode, time: new Date(), rewards: '', isSuccess: false, dayIndex, + }); + + return resResult(STATUS.SUCCESS, { activityId, gameCode }); + } + + async gameEnd(msg: { activityId: number, gameCode: string, isSuccess: boolean, dayIndex: number }, session: BackendSession) { + const { activityId, gameCode, isSuccess, dayIndex } = 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'); + + let playerData = await getPlayerChongYangData(activityId, serverId, roleId); + if (!playerData || !playerData.sceneMap) return resResult(STATUS.ACTIVITY_MISSING); + let scene = playerData.sceneMap.get(dayIndex); + if (!scene) return resResult(STATUS.ACTIVITY_MISSING); + + // 检测挑战次数 + if (scene.playCnt >= scene.maxPlayCnt) return resResult(STATUS.ACTIVITY_CHONGYANG_NO_NUM); + // 检测解锁 + if (dayIndex > playerData.dayIndexUnlock) return resResult(STATUS.ACTIVITY_CHONGYANG_LOCK); + + let curRecord = playerData.gameRecords.find(cur => cur.gameCode == gameCode); + if (!curRecord) return resResult(STATUS.ACTIVITY_CHONGYANG_GAMECODE_NOT_FOUND); + if (curRecord.isSuccess) return resResult(STATUS.ACTIVITY_CHONGYANG_GAMECODE_NOT_FOUND); + + let rewards: string = ''; + let isReward = playerData.gameRecords.find(cur => cur.dayIndex == dayIndex && cur.isSuccess); + if (isReward == undefined) rewards = scene.rewards; + + let playerRecord = await ActivityChongYangRecModel.gameEnd(serverId, activityId, playerData.roundIndex, roleId, gameCode, isSuccess, new Date(), rewards); + if (!playerRecord) return resResult(STATUS.ACTIVITY_CHONGYANG_GAMECODE_NOT_FOUND); + playerData = await getPlayerChongYangData(activityId, serverId, roleId); + scene = playerData.sceneMap.get(dayIndex); + + let goods: RewardInter[] = []; + if (isSuccess) { + goods = await addItems(roleId, roleName, sid, stringToRewardParam(rewards), ITEM_CHANGE_REASON.CHONGYANG_REWARD) + } + + return resResult(STATUS.SUCCESS, { + activityId, + playCnt: scene.playCnt, + maxPlayCnt: scene.maxPlayCnt, + gameCode, + goods + }); + } + + async buyCnt(msg: { activityId: number, dayIndex: number, count: number }, session: BackendSession) { + const { activityId, dayIndex, count } = msg; + const roleId = session.get('roleId'); + const sid = session.get('sid'); + const serverId = session.get('serverId'); + + console.log('---------xxxxxxxxxxxxxx------------------buyCnt') + let playerData = await getPlayerChongYangData(activityId, serverId, roleId); + if (!playerData || !playerData.sceneMap) return resResult(STATUS.ACTIVITY_MISSING); + let scene = playerData.sceneMap.get(dayIndex); + if (!scene) return resResult(STATUS.ACTIVITY_MISSING); + + // 检测购买次数 + if (scene.buyCnt >= scene.dailyBuyCnt || scene.buyCnt + count > scene.dailyBuyCnt) return resResult(STATUS.ACTIVITY_CHONGYANG_BUY_COUNT_OVER); + // 检测解锁 + if (dayIndex > playerData.dayIndexUnlock) return resResult(STATUS.ACTIVITY_CHONGYANG_LOCK); + + // 保存数据 + let playerRecord = await ActivityChongYangRecModel.buyCnt(serverId, activityId, playerData.roundIndex, roleId, dayIndex, count); + if (!playerRecord) return resResult(STATUS.ACTIVITY_CHONGYANG_GAMECODE_NOT_FOUND); + + playerData = await getPlayerChongYangData(activityId, serverId, roleId); + scene = playerData.sceneMap.get(dayIndex); + + return resResult(STATUS.SUCCESS, { activityId, dailyBuyCnt: scene.dailyBuyCnt, buyCnt: scene.buyCnt, }); + } +} \ No newline at end of file diff --git a/game-server/app/services/activity/activityService.ts b/game-server/app/services/activity/activityService.ts index 5198ad888..173255fc2 100644 --- a/game-server/app/services/activity/activityService.ts +++ b/game-server/app/services/activity/activityService.ts @@ -52,6 +52,7 @@ import { getPlayerEntertainDataShow } from './entertainService'; import { getPlayerQixiDataShow } from './qixiService'; import { getPlayerMidAutumnDataShow } from './midAutumnService'; import { getPlayerAuthorGachaDataShow } from './authorGachaService'; +import { getPlayerChongYangDataShow } from './chongyangService'; /** * 获取活动数据 @@ -302,7 +303,11 @@ export async function getActivity(serverId: number, roleId: string, uid: number, activityData = await getPlayerAuthorGachaDataShow(activityId, serverId, roleId); break; } - + case ACTIVITY_TYPE.CHONGYANG: + { + activityData = await getPlayerChongYangDataShow(activityId, serverId, roleId); + break; + } default: { console.log('未知活动类型.........', activityType) break; diff --git a/game-server/app/services/activity/chongyangService.ts b/game-server/app/services/activity/chongyangService.ts new file mode 100644 index 000000000..44178862c --- /dev/null +++ b/game-server/app/services/activity/chongyangService.ts @@ -0,0 +1,23 @@ +import { ActivityChongYangRecModel } from "../../db/ActivityChongYangRec"; +import { ChongYangData } from "../../domain/activityField/chongyangField"; +import { getRoleCreateTime, getServerCreateTime } from "../redisService"; +import { getActivityById } from "./activityService"; + +export async function getPlayerChongYangData(activityId: number, serverId: number, roleId: string) { + let activityData = await getActivityById(activityId); + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new ChongYangData(activityData, createTime, serverTime); + let playerRecord = await ActivityChongYangRecModel.findData(serverId, activityId, playerData.roundIndex, roleId); + playerData.setPlayerRecords(playerRecord); + return playerData; +} + + +export async function getPlayerChongYangDataShow(activityId: number, serverId: number, roleId: string) { + let playerData = await getPlayerChongYangData(activityId, serverId, roleId); + if (playerData && playerData.canShow && playerData.canShow()) { + return playerData.getShowResult(); + } + return null +} \ No newline at end of file diff --git a/game-server/app/services/checkParam.ts b/game-server/app/services/checkParam.ts index 21f738658..5565829e0 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -2291,6 +2291,28 @@ export function checkRouteParam(route: string, msg: any) { if (!checkNaturalNumbers(msg.activityId, msg.count)) return false; break; } + case 'activity.chongyangHandler.getData': + { + if (!checkNaturalNumbers(msg.activityId)) return false; + break; + } + case 'activity.chongyangHandler.gameStart': + { + if (!checkNaturalNumbers(msg.activityId, msg.dayIndex)) return false; + break; + } + case 'activity.chongyangHandler.gameEnd': + { + if (!checkNaturalNumbers(msg.activityId, msg.dayIndex)) return false; + if (!checkNaturalStrings(msg.gameCode)) return false; + if (!checkBoolean(msg.isSuccess)) return false; + break; + } + case 'activity.chongyangHandler.buyCnt': + { + if (!checkNaturalNumbers(msg.activityId, msg.dayIndex, msg.count)) return false; + break; + } case 'activity.activityHandler.debugActivityMemory': case 'activity.popUpShopHandler.debugPushPopUpShop': case 'activity.popUpShopHandler.debugPushPopUpInterval': diff --git a/shared/consts/constModules/activityConst.ts b/shared/consts/constModules/activityConst.ts index 62f2c80ef..78c4a9309 100644 --- a/shared/consts/constModules/activityConst.ts +++ b/shared/consts/constModules/activityConst.ts @@ -76,7 +76,7 @@ export enum ACTIVITY_TYPE { GROWTH_FUND_ROUGE_VIP = 61, //学宫高级密卷 GROWTH_FUND_AUTHOR = 62, //列传普通密卷 GROWTH_FUND_AUTHOR_VIP = 63, //列传高级密卷 - + CHONGYANG = 64, //重阳集会 } /** diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 4dd56c821..f17d2202e 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1278,6 +1278,7 @@ export enum ITEM_CHANGE_REASON { RECEIVE_COLLECT_REWARD = 202, // 领取图鉴奖励 RECEIVE_ROUGE_SCORE_REWARD = 203, // 领取积分奖励 REBORN_COST = 204, // 重生优化,传承 + CHONGYANG_REWARD = 205, // 重阳集会 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index d06b41118..d54ef6b7b 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -707,6 +707,15 @@ export const STATUS = { ACTIVITY_QIXI_CANNOT_BUY: { code: 50081, simStr: '不可在免费次数未用完的时候购买次数' }, ACTIVITY_QIXI_CANNOT_SWEEP: { code: 50082, simStr: '必须在当天手动玩一次后才可以碾压' }, ACTIVITY_MID_AUTUMN_NO_NUM: { code: 50083, simStr: '游戏次数不足' }, + ACTIVITY_CHONGYANG_NO_NUM: { code: 50084, simStr: '游戏次数不足' }, + ACTIVITY_CHONGYANG_LOCK: { code: 50085, simStr: '游戏层数未解锁' }, + ACTIVITY_CHONGYANG_GAMECODE_NOT_FOUND: { code: 50086, simStr: '未找到该id' }, + ACTIVITY_CHONGYANG_BUY_COUNT_OVER: { code: 50087, simStr: '购买次数不足' }, + + + + + // GM后台相关状态 60000 - 69999 GM_ERR_PASSWORD: { code: 60001, simStr: '账号或密码错误' }, diff --git a/shared/db/ActivityChongYangRec.ts b/shared/db/ActivityChongYangRec.ts new file mode 100644 index 000000000..289a12bbd --- /dev/null +++ b/shared/db/ActivityChongYangRec.ts @@ -0,0 +1,75 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + +/** + * 重阳集会 +*/ + +export class ChongYangGameRecord { + @prop({ required: true }) + todayIndex: number; // 今天index + @prop({ required: true }) + gameCode: string; // 唯一挑战id + @prop({ required: true }) + time: Date; // 时间 + @prop({ required: true }) + rewards: string; // 奖励 + @prop({ required: true }) + isSuccess: boolean; // 是否通过 + @prop({ required: true }) + dayIndex: number; //第几天(层) +} + +export class ChongYangBuyRecord { + @prop({ required: true }) + dayIndex: number; //第几天(层) + @prop({ required: true }) + buyCnt: number; // 购买次数 +} + +@index({ roleId: 1, activityId: 1 }) + +export default class Activity_ChongYang_Rec extends BaseModel { + @prop({ required: true }) + serverId: number; // 服Id + + @prop({ required: true }) + activityId: number; // 活动Id + + @prop({ required: true }) + roundIndex: number; // 第几轮 + + @prop({ required: true }) + roleId: string; // 用户Id + + @prop({ required: true, type: ChongYangBuyRecord, default: [], _id: false }) + buyRecords: ChongYangBuyRecord[]; // 购买次数 + + @prop({ required: true, type: ChongYangGameRecord, _id: false }) + gameRecords: ChongYangGameRecord[]; // 记录 + + public static async findData(serverId: number, activityId: number, roundIndex: number, roleId: string) { + let result: ActivityChongYangRecModelType = await ActivityChongYangRecModel.findOne({ serverId, roleId, activityId, roundIndex }).lean(); + return result; + } + + public static async gameRecord(serverId: number, activityId: number, roundIndex: number, roleId: string, gameRecords: ChongYangGameRecord) { + let result: ActivityChongYangRecModelType = await ActivityChongYangRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex }, { $push: { gameRecords }, $setOnInsert: { buyCnt: 0 } }, { new: true, upsert: true }).lean(); + return result; + } + + public static async buyCnt(serverId: number, activityId: number, roundIndex: number, roleId: string, dayIndex: number, count: number) { + let result: ActivityChongYangRecModelType = await ActivityChongYangRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'buyRecords.dayIndex': dayIndex }, { $inc: { 'buyRecords.$.buyCnt': count }, $setOnInsert: { gameRecord: [] } }, { new: true, upsert: true }).lean(); + return result; + } + + public static async gameEnd(serverId: number, activityId: number, roundIndex: number, roleId: string, gameCode: string, isSuccess: boolean, time: Date, rewards: string) { + let result: ActivityChongYangRecModelType = await ActivityChongYangRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'gameRecords.gameCode': gameCode }, { $set: { 'gameRecords.$.time': time, 'gameRecords.$.hasPass': true, 'gameRecords.$.isSuccess': isSuccess, 'gameRecords.$.rewards': rewards } }, { new: true }).lean(); + return result; + } +} + +export const ActivityChongYangRecModel = getModelForClass(Activity_ChongYang_Rec); + +export interface ActivityChongYangRecModelType extends Pick, keyof Activity_ChongYang_Rec> { } +export type ActivityChongYangRecModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/domain/activityField/chongyangField.ts b/shared/domain/activityField/chongyangField.ts new file mode 100644 index 000000000..0775fa66f --- /dev/null +++ b/shared/domain/activityField/chongyangField.ts @@ -0,0 +1,88 @@ +// 节日活动 - 重阳集会 +import { ActivityModelType } from '../../db/Activity'; +import { ActivityChongYangRecModelType, ChongYangBuyRecord, ChongYangGameRecord } from '../../db/ActivityChongYangRec'; +import { ActivityBase } from './activityField'; + +// 后台格式 +interface ChongYangSceneDataInDb { + dayIndex: number; // 第几天(层) + sceneId: number; // 场景id + img: string; // 场景图片 + buyCost: string; // 购买一次次数的消耗,type&id&count + dailyBuyCnt: number; // 每天可以购买的次数 + dailyFreeCnt: number; // 每天可以免费的次数 + rewards: string; // 每局的奖励 type&id&count +} + +interface ChongYangDataReturn extends ChongYangSceneDataInDb { + // maxFreeCnt: number; // 累计到现在可以免费的次数 + // freeCnt: number; // 累计到现在已经使用免费的次数 + // maxBuyCnt: number; // 累积到现在可以购买的次数 + buyCnt: number; // 累积到现在已经购买了的次数,buyCnt < maxBuyCnt 的时候才能购买 + playCnt: number; // 累计到现在游戏的次数 + maxPlayCnt: number; // 累计到现在可以游戏的最大次数 + +} + + +export class ChongYangData extends ActivityBase { + sceneMap = new Map(); + gameRecords: ChongYangGameRecord[] = []; + buyRecords: ChongYangBuyRecord[] = []; + dayIndexUnlock: number = 1; //当前开启到了第几天 + + constructor(activityData: ActivityModelType, createTime: number, serverTime: number) { + super(activityData, createTime, serverTime) + this.initData(activityData.data) + } + + public initData(data: string): void { + let chongyangData: ChongYangSceneDataInDb[] = JSON.parse(data); + if (!chongyangData || chongyangData.length == 0) return; + for (const obj of chongyangData) { + const { dayIndex, dailyFreeCnt, dailyBuyCnt } = obj; + + let maxFreeCnt = dailyFreeCnt; + this.sceneMap.set(dayIndex, { ...obj, buyCnt: 0, playCnt: 0, maxPlayCnt: maxFreeCnt }); + } + } + + public setPlayerRecords(playerData: ActivityChongYangRecModelType) { + this.updatePlayerRecord(playerData); + } + + public updatePlayerRecord(playerData: ActivityChongYangRecModelType) { + if (!playerData) return; + let dayIndexUnlockAndSuccess = 1; + let buyRecordMap = new Map() + if (playerData.buyRecords) buyRecordMap = playerData.buyRecords.reduce((map, cur) => { map.set(cur.dayIndex, { ...cur }); return map; }, new Map()); + if (playerData.gameRecords) { + this.gameRecords = playerData.gameRecords; + for (let data of playerData.gameRecords) { + let { isSuccess, dayIndex } = data; + if (isSuccess) { + dayIndexUnlockAndSuccess = Math.max(dayIndexUnlockAndSuccess, dayIndex); + let scene = this.sceneMap.get(dayIndex); + scene.buyCnt = buyRecordMap.get(dayIndex)?.buyCnt || 0; + scene.playCnt += 1; + scene.maxPlayCnt = scene.buyCnt + scene.dailyFreeCnt; + this.sceneMap.set(dayIndex, scene); + }; + this.dayIndexUnlock = Math.max(this.dayIndexUnlock, dayIndex); + } + } + + if (this.todayIndex > this.dayIndexUnlock && this.dayIndexUnlock == dayIndexUnlockAndSuccess) { + this.dayIndexUnlock += 1; + } + + } + + public getShowResult() { + return { + ...this.getBaseKeys(), + scenes: [...this.sceneMap.values()], + dayIndexUnlock: this.dayIndexUnlock, + } + } +} \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_activityType.json b/shared/resource/jsons/dic_zyz_activityType.json index 62f6ef931..a632adb97 100644 --- a/shared/resource/jsons/dic_zyz_activityType.json +++ b/shared/resource/jsons/dic_zyz_activityType.json @@ -352,5 +352,11 @@ "activityType": 63, "name": "MID_AUTUMN", "string": "列传高级密卷" + }, + { + "id": 64, + "activityType": 64, + "name": "MID_AUTUMN", + "string": "重阳集会活动" } ] \ No newline at end of file