From a0f5746af8e6281e9cd01b3294df74a5e91d19c7 Mon Sep 17 00:00:00 2001 From: zhl Date: Tue, 11 May 2021 11:09:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A2=98=E7=9B=AE=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/api.md | 10 ++- src/api/controllers/puzzle.controller.ts | 58 +++++++++++-- src/config/parsers/mission_vo.ts | 8 +- src/models/content/Puzzle.ts | 2 +- src/models/match/PuzzleRank.ts | 39 +++++++++ src/models/match/PuzzleSession.ts | 16 ++++ src/services/GameLogic.ts | 101 +++++++++++++++++++++++ src/utils/string.util.ts | 10 +++ 8 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 src/models/match/PuzzleRank.ts diff --git a/doc/api.md b/doc/api.md index a0453bc..a92d540 100644 --- a/doc/api.md +++ b/doc/api.md @@ -29,8 +29,9 @@ | 字段 | 说明 | | -------- | -------------------------------------- | -| shop | 店铺id | +| shop | 店铺id, 比如 607ff59d4a4e16687a3b7079 | | level | 关卡id | +| type | 是否降低难度 0: 普通, 1: 降低难度 | 3. Response: JSON @@ -47,7 +48,8 @@ "刘永福" ], "category": "体育-体育", // 类型 - "type": 1 // 题目类型 1: 普通的文字选择题, 2: 图形 + "type": 1, // 题目类型 1: 普通的文字选择题, 2: 图形 + "quality": 1 // 题目难度 }] } @@ -137,6 +139,7 @@ | -------- | -------------------------------------- | | count | 需要获取的数量 | | session | 当局的id, 从关卡题目列表中获取 | +| quality | 难度 | 3. Response: JSON @@ -151,7 +154,8 @@ "刘永福" ], "category": "体育-体育", // 类型 - "type": 1 // 题目类型 1: 普通的文字选择题, 2: 图形 + "type": 1, // 题目类型 1: 普通的文字选择题, 2: 图形 + "quality": 1 // 题目难度 }] diff --git a/src/api/controllers/puzzle.controller.ts b/src/api/controllers/puzzle.controller.ts index 6034b30..0680b80 100644 --- a/src/api/controllers/puzzle.controller.ts +++ b/src/api/controllers/puzzle.controller.ts @@ -18,25 +18,44 @@ import { RoomState } from '../../services/RoomState' import { retry } from '../../utils/promise.util' import { RoomLockErr } from '../../common/RoomLockErr' import { Schedule } from '../../clock/Schedule' -import { calcScore, startGame, transformRecord } from '../../services/GameLogic' +import { + calcScore, calcSingleScore, + checkSingleFinish, fetchLevelCfg, fetchSinglePuzzleType, + startGame, + transformRecord, updateSingleRank +} from '../../services/GameLogic' import { ObjectId } from 'bson' +import { Shop } from '../../models/shop/Shop' +import { isObjectId } from '../../utils/string.util' class PuzzleController extends BaseController { + //TODO:: 增加字段, 表明该次是普通开局还是降低难度开局 @role('anon') @router('post /api/:accountid/puzzle/list') async list(req, res) { - let { shop, level, accountid } = req.params + let { shop, level, accountid, type } = req.params level = +level || 1 - const cfgs: mission_vo[] = Array.from(global.$cfg.get(BaseConst.MISSION).values()) - const cfg = cfgs.find(o => o.number == level) || cfgs[cfgs.length - 1] - let count = cfg.beforehand_enemy || 10 - let records = await Puzzle.randomQuestions({}, count) + if (!shop || isObjectId(shop)) { + throw new ZError(10, '没有店铺id或者店铺id格式不正确, 测试使用: 607ff59d4a4e16687a3b7079') + } + const cfg = fetchLevelCfg(level) + let params = {} + let typeArr = await fetchSinglePuzzleType({shopId: shop, levelCfg: cfg}) + if (typeArr?.length > 0) { + params = {$or: [{tag: {$in: typeArr}}, {sub_tag: {$in: typeArr}}]} + } + let record1 = await Puzzle.randomQuestions(Object.assign(params, {quality: 1}), 5) + let record2 = await Puzzle.randomQuestions(Object.assign(params, {quality: 2}), 5) + let record3 = await Puzzle.randomQuestions(Object.assign(params, {quality: 3}), 5) + const records = (record1.concat(record2)).concat(record3) let history = new PuzzleSession({ shop, level }) history.members.set(accountid, new PuzzleStatusClass()) history.questions = records.map(o => o._id) history.expire = Date.now() + (cfg.time || 90) * 1000 history.type = 0 + history.difficultyMode = type || 0 + history.qtypes = typeArr await history.save() const results = transformRecord(records) return { @@ -100,6 +119,23 @@ class PuzzleController extends BaseController { // if (checkSubFinish(history, id)) { // await sendOneQuestion(history) // } + } else if (mode == 0) { + let result = checkSingleFinish(history) + if (result) { + history.status = 9 + //TODO:: 计算积分, 并更新排行榜 + let score = calcSingleScore() + let rankObj = { + shop: history.shop, + level: history.level, + accountId: accountid, + score, + mode: 0, + session: history.id + } + await updateSingleRank(rankObj) + await history.save() + } } return { result, answer: record.a1, stats: history.members } } @@ -107,7 +143,7 @@ class PuzzleController extends BaseController { @role('anon') @router('post /api/:accountid/puzzle/more') async moreLevelQuestions(req, res) { - let {session,accountid, count} = req.params + let {session,accountid, count, quality} = req.params count = count ? +count : 10 if (!session) { throw new ZError(11, 'param mismatch') @@ -125,7 +161,13 @@ class PuzzleController extends BaseController { throw new ZError(14, 'not in current match') } let ids = history.questions.map(o => new ObjectId(o)) - let options = {'_id': {$nin: ids}} + let options: any = {'_id': {$nin: ids}} + if (quality) { + options.quality = quality + } + if (history.qtypes.length > 0) { + Object.assign(options, {$or: [{tag: {$in: history.qtypes}}, {sub_tag: {$in: history.qtypes}}]}) + } let records = await Puzzle.randomQuestions(options, count) const results = transformRecord(records) history.questions = history.questions.concat(records.map(o => o._id)) diff --git a/src/config/parsers/mission_vo.ts b/src/config/parsers/mission_vo.ts index 3c517e0..e698038 100644 --- a/src/config/parsers/mission_vo.ts +++ b/src/config/parsers/mission_vo.ts @@ -7,7 +7,7 @@ export class mission_vo implements Cfg{ public number: number; public commonlyadd: number; public hardadd: number; - public hp: number; + public lastdif: number; public hp: number; public hpstar: number; public time: number; @@ -22,7 +22,7 @@ export class mission_vo implements Cfg{ this.number = data.number; this.commonlyadd = data.commonlyadd; this.hardadd = data.hardadd; - this.hp = data.hp; + this.lastdif = data.lastdif; this.hp = data.hp; this.hpstar = data.hpstar; this.time = data.time; @@ -32,7 +32,7 @@ export class mission_vo implements Cfg{ this.beforehand_enemy = data.beforehand_enemy; this.keyID = data.keyID; }; - + public isOK (uniqueID: number, param1: any, param2: any): boolean { if((param1 == undefined || param1 == null) && (param2 == undefined || param2 == null)){ return this.id == uniqueID; @@ -42,4 +42,4 @@ export class mission_vo implements Cfg{ } return this.id == uniqueID && this.id == param1 && this.id == param2; }; -}; \ No newline at end of file +}; diff --git a/src/models/content/Puzzle.ts b/src/models/content/Puzzle.ts index 35e4452..9bf46d6 100644 --- a/src/models/content/Puzzle.ts +++ b/src/models/content/Puzzle.ts @@ -99,7 +99,7 @@ class PuzzleClass extends BaseModule { } public static async randomQuestions(this: ReturnModelType, options: any, count: number) { let filters = {status: 1, is_hide: 0, deleted: 0, dp: 1} - Object.assign(options) + Object.assign(filters, options) return Puzzle.aggregate([ { $match: filters }, { $sample: { size: count } } diff --git a/src/models/match/PuzzleRank.ts b/src/models/match/PuzzleRank.ts new file mode 100644 index 0000000..c53d67c --- /dev/null +++ b/src/models/match/PuzzleRank.ts @@ -0,0 +1,39 @@ +import { BaseModule } from '../Base' +import { dbconn } from '../../decorators/dbconn' +import { + getModelForClass, + index, + modelOptions, + prop +} from '@typegoose/typegoose' + +/** + * 排名数据 + */ +@dbconn() +@index({ shop: 1, level: 1, accountId: 1, mode: 1}, { unique: true }) +@modelOptions({ schemaOptions: { collection: 'puzzle_rank', timestamps: true } }) +export class PuzzleRankClass extends BaseModule { + @prop() + public shop: string + + @prop() + public level: number + + @prop() + public accountId: string + + @prop() + public score: number + + @prop() + public session: string + + @prop() + public mode: number + + + +} + +export const PuzzleRank = getModelForClass(PuzzleRankClass, { existingConnection: PuzzleRankClass.db }) diff --git a/src/models/match/PuzzleSession.ts b/src/models/match/PuzzleSession.ts index 1000673..c62fab4 100644 --- a/src/models/match/PuzzleSession.ts +++ b/src/models/match/PuzzleSession.ts @@ -73,6 +73,15 @@ export class PuzzleSessionClass extends BaseModule { @prop({default: 0}) public type: number + /** + * 模式 + * 0: 普通 + * 1: 减低难度 + * @type {number} + */ + @prop({default: 0}) + public difficultyMode: number + /** * 当前的问题 * @type {number} @@ -95,6 +104,13 @@ export class PuzzleSessionClass extends BaseModule { */ @prop({default: 0}) public status: number + + /** + * 已选择的题库分类 + * @type {string[]} + */ + @prop() + public qtypes: string[] /** * 开始时间 * 对于多人比赛来说, 需要一个自动开始时间 diff --git a/src/services/GameLogic.ts b/src/services/GameLogic.ts index a7a956b..14cad67 100644 --- a/src/services/GameLogic.ts +++ b/src/services/GameLogic.ts @@ -11,6 +11,10 @@ import { BaseConst } from '../constants/BaseConst' import { QCategoryCache } from './QCategoryCache' import { PuzzleSession } from '../models/match/PuzzleSession' import { GameEnv } from '../config/GameEnv' +import { PuzzleRank } from '../models/match/PuzzleRank' +import { Shop } from '../models/shop/Shop' +import { mission_vo } from '../config/parsers/mission_vo' +import { ShopActivity } from '../models/shop/ShopActivity' export function transformRecord(records: any[]) { @@ -112,3 +116,100 @@ export function calcScore(timeKey: string, combo: number) { const cfg = new GameEnv() return cfg.pvpBaseScore + time / 100 * cfg.pvpTimeRate + combo * cfg.pvpComboRate } + +/** + * 更新排行榜 + * @param {string} shop 店铺id + * @param {number} level 关卡id + * @param {string} accountId 帐号id + * @param {number} score 分数 + * @param {string} session + * @param {number} mode 模式: 0: 单人 1: pvp + * @return {Promise} + */ +export async function updateSingleRank({shop, level, accountId, score, session, mode } + : {shop: string, + level: number, + accountId: string, + score: number, + mode: number + session: string}) { + + let record = (await PuzzleRank.findOrCreate({shop, level, accountId, mode})).doc + if (record.score < score) { + record.score = score + record.session = session + await record.save() + } +} + +/** + * TODO:: + * 单人模式检查游戏是否结束 + */ +export function checkSingleFinish(history: any) { + + return false +} + +/** + * TODO:: + * 计算单人模式的得分 + * @return {number} + */ +export function calcSingleScore() { + return 0 +} + +/** + * 确定pvp模式题目类型 + * @param {string} shopId + * @param {string} aid 活动配置 + * @return {Promise} + */ +export async function fetchPvpPuzzleType({shopId, aid} : {shopId: string, aid?: string}) { + + //{$or:[{tag: {$in: ['a', 'SS']}}, {sub_tag: {$in: ['a', 'SS']}}]} + let typeArr: string[] = [] + if (aid) { + let actData = await ShopActivity.findById(aid) + if (actData?.qtypes?.length > 0) { + typeArr = actData.qtypes + } + } + if (typeArr.length === 0 ) { + let shop = await Shop.findById(shopId) + if (shop?.qtypes?.length > 0) { + typeArr = shop.qtypes + } + } + // let params = {$or: [{tag: {$in: typeArr}}, {sub_tag: {$in: typeArr}}]} + return typeArr +} + +/** + * 获取单人模式题目类型 + * @param {string} shopId + * @param {mission_vo} levelCfg + * @return {Promise} + */ +export async function fetchSinglePuzzleType({shopId, levelCfg} : {shopId: string, levelCfg?: mission_vo}) { + let typeArr: string[] = [] + let shop = await Shop.findById(shopId) + if (shop?.qtypes?.length > 0) { + typeArr = shop.qtypes + } + if (typeArr.length === 0 && levelCfg?.keyID) { + typeArr = levelCfg.keyID.split(':') + } + return typeArr +} + +/** + * 获取关卡配置信息 + * @param {number} level + */ +export function fetchLevelCfg(level: number) { + const cfgs: mission_vo[] = Array.from(global.$cfg.get(BaseConst.MISSION).values()) + return cfgs.find(o => o.number == level) || cfgs[cfgs.length - 1] +} diff --git a/src/utils/string.util.ts b/src/utils/string.util.ts index f26ac8e..6eeb46a 100644 --- a/src/utils/string.util.ts +++ b/src/utils/string.util.ts @@ -48,3 +48,13 @@ export function isTrue(obj) { return obj === 'true' || obj === 'TRUE' || obj === 'True' || obj === 'on' || obj ==='ON' || obj === true || obj === 1 || obj === '1' || obj === 'YES' || obj === 'yes'; } + +/** + * 验证ObjectId格式是否正确 + * @param {string} id + * @return {boolean} + */ +export function isObjectId(id: string): boolean { + //mongoose.Types.ObjectId.isValid(id) + return /^[a-fA-F0-9]{24}$/.test(id) +}