diff --git a/game-server/app/servers/battle/handler/towerBattleHandler.ts b/game-server/app/servers/battle/handler/towerBattleHandler.ts index 3b78422c8..4461c7bc8 100644 --- a/game-server/app/servers/battle/handler/towerBattleHandler.ts +++ b/game-server/app/servers/battle/handler/towerBattleHandler.ts @@ -1,10 +1,12 @@ -// import { HANG_UP_CONSTS } from './../../../consts/consts'; +import { TOWER_TASK_CONST } from './../../../consts/consts'; +import { TowerTaskRecModel } from './../../../db/TowerTaskRec'; +import { HangUpSpdUpRecModel } from './../../../db/HangUpSpdUpRec'; import { HangUpRecordModel } from './../../../db/HangUpRecord'; import { RoleModel } from './../../../db/Role'; import { TowerRecordModel } from './../../../db/TowerRecord'; import { Application, BackendSession } from 'pinus'; -import { getTowerDataByLv } from '../../../util/gamedata'; -import { decodeArrayStr, decodeIdCntArrayStr } from '../../../util/util'; +import { getTaskIdByQuality, getTowerDataByLv } from '../../../util/gamedata'; +import { decodeArrayStr, decodeIdCntArrayStr, getRandEelm } from '../../../util/util'; import { calcuHangUpReward } from '../../../services/battleService'; import { handleFixedReward } from '../../../services/rewardService'; @@ -87,7 +89,7 @@ export class TowerBattleHandler { }; } - async recHangUpRewards(msg: {speedUp: boolean, speedUpCnt: number}, session: BackendSession) { + async recHangUpRewards(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); let roleName = session.get('roleName'); const { multi, timeReward, endLv, endTime } = await calcuHangUpReward(roleId); @@ -109,4 +111,67 @@ export class TowerBattleHandler { } } } -} \ No newline at end of file + + async hangUpSpeedUp(msg: {speedUpCnt: number}, session: BackendSession) { + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + if (msg.speedUpCnt <= 0) { + return { + code: 201, + data: { + msg: '加速参数错误', + } + }; + } + const curTime = new Date(); + const { multi, timeReward, endLv } = await calcuHangUpReward(roleId, true, msg.speedUpCnt, curTime); + if (multi <= 0) { + return { + code: 201, + data: { + msg: '今日加速次数不足' + } + }; + } + + const role = await RoleModel.hangUpSpdUp(roleId, multi, curTime); + if (!role) { + return { + code: 201, + data: { + msg: '不满足加速条件' + } + }; + } + const spdUpRec = await HangUpSpdUpRecModel.updateRec(roleId, roleName, multi, endLv); + const goods = await handleFixedReward(roleId, roleName, timeReward, multi); + return { + code : 200, + data: { + msg: '成功', + goods, rewardLv: endLv, spdUpRec + } + }; + } + + async getTask(msg: {}, session: BackendSession) { + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + let curTask = await TowerTaskRecModel.getCurTask(roleId); + if (!curTask) { + let taskIds = []; + TOWER_TASK_CONST.RAND_CNT.forEach(qualityId => { + const ids = getTaskIdByQuality(qualityId); + taskIds.concat(getRandEelm(ids, 1)); + }) + curTask = await TowerTaskRecModel.createTask(roleId, roleName, taskIds); + } + return { + code: 200, + data: { + msg: '成功', + curTask + } + } + } +} diff --git a/game-server/app/services/battleService.ts b/game-server/app/services/battleService.ts index b02dbfde7..c60553550 100644 --- a/game-server/app/services/battleService.ts +++ b/game-server/app/services/battleService.ts @@ -5,7 +5,7 @@ import { BattleRecordModel } from './../db/BattleRecord'; import { TowerRecordModel } from './../db/TowerRecord'; import { RoleModel } from './../db/Role'; import { getTowerDataByLv } from "../util/gamedata" -import { decodeArrayStr } from '../util/util'; +import { decodeArrayStr, shouldRefresh } from '../util/util'; import { handleFixedReward } from './rewardService'; export async function checkTowerWar(roleId: string, battleId: number, heroes: Array) { @@ -97,18 +97,26 @@ async function startHangUp(roleId: string, roleName: string) { await HangUpRecordModel.initRecord(roleId, roleName); } -export async function calcuHangUpReward(roleId: string) { - let { towerLv } = await RoleModel.findByRoleId(roleId); +export async function calcuHangUpReward(roleId: string, speedUp = false, speedUpCnt = 1, curTime = new Date()) { + let { towerLv, hangUpSpdUpCnt, lastSpdUpTime } = await RoleModel.findByRoleId(roleId); let { startLv, startTime} = await HangUpRecordModel.getCurRec(roleId); let towerInfo = getTowerDataByLv(towerLv - 1); // towerLv 是当前层,奖励计算按照已经通过的层,即上一层 - const curTime = new Date(); - let deltaTime = curTime.getTime() - startTime.getTime(); - if (deltaTime > HANG_UP_CONSTS.MAX_TIME) deltaTime = HANG_UP_CONSTS.MAX_TIME; - const multi = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME); + let multi = 1; + if (speedUp) { + if (hangUpSpdUpCnt >= speedUpCnt || shouldRefresh(lastSpdUpTime, new Date, HANG_UP_CONSTS.REFRESH_TIME, 1)) { + multi = speedUpCnt; + } else { + multi = 0; + } + } else { + let deltaTime = curTime.getTime() - startTime.getTime(); + if (deltaTime > HANG_UP_CONSTS.MAX_TIME) deltaTime = HANG_UP_CONSTS.MAX_TIME; + multi = Math.floor(deltaTime / HANG_UP_CONSTS.UNIT_TIME); + } return { endLv: towerLv - 1, endTime: new Date(startTime.getTime() + multi * HANG_UP_CONSTS.UNIT_TIME), timeReward: towerInfo.timeReward, multi }; -} +} \ No newline at end of file diff --git a/game-server/app/util/gamedata.ts b/game-server/app/util/gamedata.ts index 48b794dc9..f9c728496 100644 --- a/game-server/app/util/gamedata.ts +++ b/game-server/app/util/gamedata.ts @@ -5,6 +5,8 @@ let gamedata = {}; const wars = ['dic_zyz_gk_main', 'dic_zyz_gk_mainElite', 'dic_zyz_gk_daily', 'dic_zyz_gk_event', 'dic_zyz_gk_tower', 'dic_zyz_gk_expedition']; // 关卡相关的表 const allWarInfos = new Map(); const towerInfos = new Map(); +const towerTaskInfos = new Map(); +const towerTasksByQuality = new Map>(); function parseWarData() { let result = null; @@ -29,6 +31,19 @@ function parseTowerData() { }); } +function parseTowerTaskData() { + const towerTaskFile = 'dic_zyz_tower_tasks'; + const towerTaskData = gamedata['jsons'][towerTaskFile] || []; + towerTaskData.forEach(elem => { + if (elem && elem.taskId) { + towerTaskInfos.set(elem.taskId, elem); + let tasks = towerTasksByQuality.get(elem.quality) || []; + tasks.push(elem.taskId); + towerTasksByQuality.set(elem.quality, tasks); + } + }); +} + function initData (folder) { if(!gamedata.hasOwnProperty(folder)) { gamedata[folder] = {}; @@ -55,6 +70,7 @@ function initData (folder) { function parseData() { parseWarData(); parseTowerData(); + parseTowerTaskData(); } initData('jsons'); // 加载一般json @@ -91,3 +107,13 @@ export function getGoodById(gid) { return cur.good_id == gid }); } + +export function getTaskById(tid: number) { + const taskInfo = towerTaskInfos.get(tid); + return taskInfo; +} + +export function getTaskIdByQuality(quality: number) { + const taskIds = towerTasksByQuality.get(quality); + return taskIds; +} \ No newline at end of file diff --git a/game-server/app/util/util.ts b/game-server/app/util/util.ts index 8d1e82567..8e31f71e2 100644 --- a/game-server/app/util/util.ts +++ b/game-server/app/util/util.ts @@ -4,7 +4,8 @@ import { CounterModel } from '../db/Counter'; import { EquipModel } from '../db/Equip'; import { HeroModel } from '../db/Hero'; import { GOOD_TYPE } from '../consts/consts'; - +const moment = require('moment'); + export function genCode(len) { const chars = '123456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijklmnopqrstuvwxyz'; const charArr = chars.split(''); @@ -189,4 +190,41 @@ export function decodeIdCntArrayStr(str: string, multi: number) { index -= weight; } return result + } +/** + * 传入两个时间,返回按照时间差计算,第二个时间比第一个晚几天 + * @param preTime 之前的时间 + * @param proTime 之后的时间 + */ +export function deltaDays(preTime: Date, proTime: Date): number { + return moment(proTime).diff(moment(preTime), "days"); +} + +/** + * 计算按照每 x 天 y 点刷新一次,是否应该刷新 + * @param preTime 基准时间 + * @param curTime 当前时间 + * @param hour 几点刷新 + * @param deltaDay 间隔几天刷新,默认每天刷新(deltaDay = 1) + */ +export function shouldRefresh(preTime: Date, curTime: Date, hour: number, deltaDay = 1): boolean { + let refeshTime = curTime.setHours(hour, 0, 0, 0); + if (refeshTime - preTime.getTime() > deltaDay * 24 * 60 * 60 * 1000 && curTime.getTime() >= refeshTime) { + return true; + } + return false; +} + +export function getRandEelm(source: Array = [], cnt = 1): Array { + if (cnt > source.length) return null; + if (cnt === source.length) return source; + let idxs = new Set(); + for (let i = 1; i < cnt; ++i) { + let rand = Math.floor(Math.random() * source.length); + idxs.add(rand); + if (idxs.size == cnt) { + break; + } + } + return source.filter((item, idx) => idxs.has(idx)); } \ No newline at end of file diff --git a/game-server/package-lock.json b/game-server/package-lock.json index 44b8e3a47..669fb732d 100644 --- a/game-server/package-lock.json +++ b/game-server/package-lock.json @@ -769,6 +769,11 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz?cache=0&sync_timestamp=1601983423917&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.29.1.tgz", + "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=" + }, "mongodb": { "version": "3.6.1", "resolved": "https://registry.npm.taobao.org/mongodb/download/mongodb-3.6.1.tgz", diff --git a/game-server/package.json b/game-server/package.json index 47a6a5ba4..7d4672b43 100644 --- a/game-server/package.json +++ b/game-server/package.json @@ -21,6 +21,7 @@ "@types/redis": "^2.8.25", "bluebird": "^3.5.1", "crc": "^3.5.0", + "moment": "^2.29.1", "mongoose": "^5.10.4", "pinus": "^1.4.9", "pinus-robot": "^1.4.9", diff --git a/shared/consts/consts.ts b/shared/consts/consts.ts index b263983b7..72c974730 100644 --- a/shared/consts/consts.ts +++ b/shared/consts/consts.ts @@ -70,7 +70,14 @@ export const EXPEDITION_INCREASE_POINT = 2; // 远征每成功一次累计的点 export const HANG_UP_CONSTS = { ENABLE_LV: 2, UNIT_TIME: 10 * 60 * 1000, - MAX_TIME: 12 * 60 * 60 * 1000 + MAX_TIME: 12 * 60 * 60 * 1000, + MAX_SPD_UP_CNT: 6, + REFRESH_TIME: 5 +} + +export const TOWER_TASK_CONST = { + REFRESH_TIME: 5, + RAND_CNT: [1, 1, 1, 1, 1], } export const WAR_JSON_ATTRIBUTE_TYPE = { diff --git a/shared/db/HangUpRecord.ts b/shared/db/HangUpRecord.ts index b192cb5f7..554b3fc6a 100644 --- a/shared/db/HangUpRecord.ts +++ b/shared/db/HangUpRecord.ts @@ -32,7 +32,7 @@ export default class HangUpRecord extends BaseModel { public static async initRecord(roleId: string, roleName: string, lean = true) { const curTime = new Date(); const recDoc = new HangUpRecordModel(); - const update = Object.assign({roleId, roleName, startTime: curTime}, recDoc.toJSON()); + const update = Object.assign(recDoc.toJSON(), {roleId, roleName, startTime: curTime}); const rec = await HangUpRecordModel.findOneAndUpdate({roleId, startLv: HANG_UP_CONSTS.ENABLE_LV}, update, {upsert: true, new: true}).lean(lean); return rec; } @@ -45,7 +45,7 @@ export default class HangUpRecord extends BaseModel { public static async updateRec(roleId: string, roleName: string, endLv: number, endTime: Date, lean = true) { await HangUpRecordModel.findOneAndUpdate({roleId, received: false}, {endLv, endTime, received: true}).lean(lean); const recDoc = new HangUpRecordModel(); - const update = Object.assign({roleId, roleName, startTime: endTime}, recDoc.toJSON()); + const update = Object.assign(recDoc.toJSON(), {roleId, roleName, startTime: endTime}); const newRec = await HangUpRecordModel.findOneAndUpdate({roleId, startLv: endLv}, update, {upsert: true, new: true}).lean(lean); return newRec; } diff --git a/shared/db/HangUpSpdUpRec.ts b/shared/db/HangUpSpdUpRec.ts new file mode 100644 index 000000000..c0bcb5f51 --- /dev/null +++ b/shared/db/HangUpSpdUpRec.ts @@ -0,0 +1,29 @@ +import { HANG_UP_CONSTS } from './../consts/consts'; +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop } from '@typegoose/typegoose'; + +/** + * 挂机记录表 + */ +@index({ roleId: 1, lv: 1 }) + +export default class HangUpSpdUpRec extends BaseModel { + @prop({ required: true }) + roleId: string; // 角色 id + @prop({ required: true }) + roleName: string; // 角色名 + + @prop({required: true, default: 1}) + cnt: number; // 加速次数 + @prop({required: true, default: 1}) + lv: number; // 天梯层数 + + public static async updateRec(roleId, roleName, multi, lv, lean = true ) { + if (multi <= 0) return null; + let rec = await HangUpSpdUpRecModel.findOneAndUpdate({roleId, lv, roleName}, {$inc: {cnt: multi}}, {upsert: true, new: true}).lean(lean); + return rec; + } + +} + +export const HangUpSpdUpRecModel = getModelForClass(HangUpSpdUpRec); diff --git a/shared/db/Item.ts b/shared/db/Item.ts index 31eeaa8c5..474c7d0b1 100644 --- a/shared/db/Item.ts +++ b/shared/db/Item.ts @@ -37,7 +37,7 @@ export default class Item extends BaseModel { public static async createItem(itemInfo: {roleId: string, roleName: string, itemid: number, seqId: number, itemName: string, count: number}, lean = true) { const doc = new ItemModel(); - const update = Object.assign(itemInfo, doc.toJSON()); + const update = Object.assign(doc.toJSON(), itemInfo); const item = await ItemModel.findOneAndUpdate({ seqId: itemInfo.seqId }, update, {upsert: true, new: true}).lean(lean); return item; } diff --git a/shared/db/Role.ts b/shared/db/Role.ts index 05c47abdf..7d502ad60 100644 --- a/shared/db/Role.ts +++ b/shared/db/Role.ts @@ -1,6 +1,8 @@ +import { HANG_UP_CONSTS } from './../consts/consts'; import BaseModel from './BaseModel'; import { index, getModelForClass, prop } from '@typegoose/typegoose'; import User from './User'; +import { shouldRefresh } from '../util/util'; /** * 角色字段接口 @@ -99,6 +101,11 @@ export default class Role extends BaseModel { // @prop({ required: false }) // hangUpTime: Date; // 当前挂机开始时间 + @prop({ required: true, default: HANG_UP_CONSTS.MAX_SPD_UP_CNT}) + hangUpSpdUpCnt: number; // 挂机加速次数 + @prop({ required: true, default: new Date()}) + lastSpdUpTime: Date; // 最后一次挂机加速时间 + @prop({ required: true }) expeditionPoint: number; // 远征点数 @@ -118,7 +125,7 @@ export default class Role extends BaseModel { const user = await User.findUserByUid(uid); if (!user) return null; const doc = new RoleModel(); - const update = Object.assign(roleInfo, { userInfo: user, serverType: user.serverType, serverId }, doc.toJSON()); + const update = Object.assign(doc.toJSON(), roleInfo, { userInfo: user, serverType: user.serverType, serverId }); const role = await RoleModel.findOneAndUpdate({ 'userInfo.uid': uid, serverId }, update, { upsert: true, new: true }).lean(lean); return role; } @@ -141,7 +148,20 @@ export default class Role extends BaseModel { } public static async towerLvUp(roleId: string, lean = true) { - let role = await RoleModel.findOneAndUpdate({roleId}, {$inc: {towerLv: 1}}).lean(lean); + let role = await RoleModel.findOneAndUpdate({roleId}, {$inc: {towerLv: 1}}, {new: true}).lean(lean); + return role; + } + + public static async hangUpSpdUp(roleId: string, cnt: number, curTime: Date, lean = true) { + if (cnt < 0) return null; + + const {lastSpdUpTime} = await RoleModel.findOneAndUpdate({roleId}, {$inc: {hangUpSpdUpCnt: -cnt}, lastSpdUpTime: curTime}, {new: true}).lean(lean); + let role = null; + if (shouldRefresh(lastSpdUpTime, curTime, HANG_UP_CONSTS.REFRESH_TIME, 1) && cnt <= HANG_UP_CONSTS.MAX_SPD_UP_CNT) { + role = await RoleModel.findOneAndUpdate({roleId}, {hangUpSpdUpCnt: HANG_UP_CONSTS.MAX_SPD_UP_CNT - cnt, lastSpdUpTime: curTime}, {new: true}).lean(lean); + } else { + role = await RoleModel.findOneAndUpdate({roleId, hangUpSpdUpCnt: {$gte: cnt}}, {$inc: {hangUpSpdUpCnt: -cnt}, lastSpdUpTime: curTime}, {new: true}).lean(lean); + } return role; } diff --git a/shared/db/TowerRecord.ts b/shared/db/TowerRecord.ts index 4295add22..1aebfc237 100644 --- a/shared/db/TowerRecord.ts +++ b/shared/db/TowerRecord.ts @@ -35,7 +35,7 @@ export default class TowerRecord extends BaseModel { public static async createRecord(towerInfo: {roleId: string, lv: number, warStatus: Array}, lean = true) { const recDoc = new TowerRecordModel(); - const update = Object.assign(recDoc.toJSON(), towerInfo); + const update = Object.assign(towerInfo, recDoc.toJSON()); const { roleId, lv } = towerInfo; let rec = await TowerRecordModel.findOneAndUpdate({roleId, lv}, {$set: update}, {upsert: true, new: true}).lean(lean); return rec; diff --git a/shared/db/TowerTaskRec.ts b/shared/db/TowerTaskRec.ts new file mode 100644 index 000000000..00b798c07 --- /dev/null +++ b/shared/db/TowerTaskRec.ts @@ -0,0 +1,88 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop } from '@typegoose/typegoose'; + +class TowerTask { + @prop({ required: true }) + id: number; // 任务唯一 Id,来自任务表 + @prop({ required: true}) + taskCode: string; // 服务器生成的任务唯一编号 + @prop({ required: true, type: Number, default: []}) + heroes: Array; // 派遣武将 hid + @prop({ required: true }) + status: boolean; // 派遣任务当前状态,0-可派遣,1-已派遣,2-已完成,3-已领取 +} + +function genCodeTmp(len) { + const chars = '123456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijklmnopqrstuvwxyz'; + const charArr = chars.split(''); + let code = ''; + for (let i = 0; i < len; i++) { + code += charArr[Math.floor(Math.random() * charArr.length)]; + } + return code; +} + +/** + * 天梯派遣记录表 + */ +@index({ roleId: 1, status: 1 }) +@index({ batchCode: 1 }) +export default class TowerTaskRec extends BaseModel { + @prop({ required: true }) + roleId: string; // 角色 id + @prop({ required: true }) + roleName: string; // 角色名 + + @prop({ required: true}) + batchCode: string; // 本批派遣任务唯一标识 + @prop({ required: true, default: 0}) + status: number; // 本批任务是否完成,0-未完成,1-已完成,2-已领取 + @prop({ required: true, type: TowerTask, default: [] }) + tasks: Array; // 本批次任务列表 + @prop({ required: true, type: Number, default: [] }) + heroes: Array; // 此批派遣使用的全部武将 + + public static async getCurTask(roleId: string, lean = true) { + const curTime = new Date(); + curTime.setHours(5, 0, 0, 0); + const rec = await TowerTaskRecModel.findOne({roleId, createdAt: {$gte: curTime}}).lean(lean); + return rec; + } + + public static async createTask(roleId: string, roleName: string, taskIds: Array, lean = true) { + const batchCode = genCodeTmp(8); + const tasksData = taskIds.map(id => { + const taskCode = genCodeTmp(6); + const taskData = Object.assign(new TowerTask(), {id, taskCode}); + return taskData; + }); + const recDoc = new TowerTaskRecModel(); + const update = Object.assign(recDoc.toJSON(), {tasks: tasksData, roleId, roleName}); + const rec = await TowerTaskRecModel.findOneAndUpdate({roleId, batchCode}, update, {upsert: true, new: true}).lean(lean); + return rec; + } + + public static async updateTask(roleId: string, batchCode: string, tasks: Array<{taskCode:string, heroes: Array}>, lean = true) { + let allHeroes = []; + tasks.forEach(task => { + allHeroes.concat(task.heroes); + }) + const curRec = await TowerTaskRecModel.findOne({batchCode, heroes: {$nin: allHeroes}}).lean(lean); + if (!curRec) return null; + + let {status, tasks: recTasks, heroes: recHeroes} = curRec; + if (status == 2 || status == 1) return curRec; + for (let task of tasks) { + for (let recTask of recTasks) { + if (task.taskCode == recTask.taskCode) { + recTask.heroes = task.heroes; + recHeroes.concat(task.heroes); + } + } + } + const newRec = await TowerTaskRecModel.findOneAndUpdate({batchCode}, {heroes: recHeroes, tasks: recTasks}, {new: true}).lean(lean); + return newRec; + } +} + +export const TowerTaskRecModel = getModelForClass(TowerTaskRec); diff --git a/shared/resource/jsons/dic_zyz_tower_tasks.json b/shared/resource/jsons/dic_zyz_tower_tasks.json new file mode 100644 index 000000000..07cd7f845 --- /dev/null +++ b/shared/resource/jsons/dic_zyz_tower_tasks.json @@ -0,0 +1,72 @@ +[ + { + "taskId": 1, + "taskName": "任务1", + "quality": 1, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "2&2&1", + "actorCnt": 1, + "completeTime": 30 + }, + { + "taskId": 2, + "taskName": "任务2", + "quality": 2, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "1&5&2", + "actorCnt": 2, + "completeTime": 40 + }, + { + "taskId": 3, + "taskName": "任务3", + "quality": 3, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "3&1&1", + "actorCnt": 1, + "completeTime": 50 + }, + { + "taskId": 4, + "taskName": "任务4", + "quality": 4, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "1&5&2|2&2&1|3&1&1", + "actorCnt": 1, + "completeTime": 50 + }, + { + "taskId": 5, + "taskName": "任务5", + "quality": 4, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "1&5&2|2&2&1|3&1&1", + "actorCnt": 1, + "completeTime": 30 + }, + { + "taskId": 6, + "taskName": "任务6", + "quality": 5, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "1&5&2|2&2&1|3&1&1", + "actorCnt": 1, + "completeTime": 30 + }, + { + "taskId": 7, + "taskName": "任务7", + "quality": 5, + "reward": "1&2|2&1", + "bonus": "3&2|4&1", + "bonusCondition": "1&5&2|2&2&1|3&1&1", + "actorCnt": 1, + "completeTime": 30 + } +] \ No newline at end of file