diff --git a/game-server/app/servers/role/handler/itemHandler.ts b/game-server/app/servers/role/handler/itemHandler.ts index 22925dcf8..d027c1009 100644 --- a/game-server/app/servers/role/handler/itemHandler.ts +++ b/game-server/app/servers/role/handler/itemHandler.ts @@ -1,6 +1,6 @@ import { Application, BackendSession } from "pinus"; -import { STATUS, EQUIP_STRENGTHEN_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, HERO_SYSTEM_TYPE, CONSUME_TYPE, HERO_GROW_MAX, MSG_SOURCE, JEWEL_PUSH_LV, TASK_TYPE } from "../../../consts"; -import { ItemInter } from "../../../pubUtils/interface"; +import { STATUS, EQUIP_STRENGTHEN_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, HERO_SYSTEM_TYPE, CONSUME_TYPE, HERO_GROW_MAX, MSG_SOURCE, JEWEL_PUSH_LV, TASK_TYPE, DEBUG_MAGIC_WORD } from "../../../consts"; +import { ItemInter, RewardInter } from "../../../pubUtils/interface"; import { resResult, parseGoodStr, getRandValueByMinMax, getRandEelm } from "../../../pubUtils/util"; import { addItems, handleCost, decreaseItems } from "../../../services/rewardService"; @@ -17,7 +17,8 @@ import { indexOf, findIndex } from 'underscore'; import { pushEquipRefineSucMsg, pushNormalEquipMsg, pushNormalItemMsg } from "../../../services/chatService"; import { checkTaskWithHero, checkTaskWithEquip, checkTask, checkTaskWithArgs, checkTaskConditionEquipSuitJewelStage, checkActivityTask } from "../../../services/taskService"; import { useGiftPackage } from "../../../services/activity/giftPackageService"; -import { getAp, setAp } from "../../../services/actionPointService"; +import { getAp, setAp, setApBuyTimes } from "../../../services/actionPointService"; +import { ActionPointModel } from "../../../db/ActionPoint"; export default function (app: Application) { return new ItemHandler(app); @@ -100,7 +101,52 @@ export class ItemHandler { } - public async debugIncAp(msg: { ap: number }, session: BackendSession) { + // 购买体力道具 + public async buyApItem(msg: { id: number, count: number }, session: BackendSession) { + let { id, count } = msg; + const roleId: string = session.get('roleId'); + const roleName: string = session.get('roleName'); + const sid: string = session.get('sid'); + + if(count <= 0) return resResult(STATUS.WRONG_PARMS); + + let dicGoods = gameData.goods.get(id); + if (!dicGoods) { + return resResult(STATUS.DIC_DATA_NOT_FOUND); + } + let dicItid = ITID.get(dicGoods.itid); + if (!dicItid) return resResult(STATUS.DIC_DATA_NOT_FOUND); + if (dicItid.type != CONSUME_TYPE.AP) { + return resResult(STATUS.ROLE_METERIAL_ERROR); + } + + let role = await RoleModel.findByRoleId(roleId, 'lv'); + let { buyTimes = 0 } = await getAp(roleId, role.lv); + if(buyTimes + count > gameData.apMaxBuyTimes.max) return resResult(STATUS.AP_BUY_TIMES_LACK); + + let cost: RewardInter[] = []; + for(let i = 0; i < count; i++) { + let curCost = gameData.apBuy.get(++buyTimes); + if(curCost) cost = cost.concat(curCost); + } + + let consumeResult = await handleCost(roleId, sid, cost); + if (!consumeResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); + + let goods = await addItems(roleId, roleName, sid, [{id, count}]); + let apJson = await setApBuyTimes(roleId, role.lv, sid, count); + + return resResult(STATUS.SUCCESS, { + goods, apJson + }); + } + + public async debugIncAp(msg: { magicWord: string, ap: number }, session: BackendSession) { + const { magicWord } = msg; + if (magicWord !== DEBUG_MAGIC_WORD) { + return resResult(STATUS.TOKEN_ERR); + } + const roleId = session.get('roleId'); const sid = session.get('sid'); const funcs = session.get('funcs'); @@ -110,11 +156,28 @@ export class ItemHandler { if(!apJson) return resResult(STATUS.BATTLE_ACTION_POINT_LACK) return resResult(STATUS.SUCCESS, { apJson }); } - public async debugGetAp(msg: { }, session: BackendSession) { + + public async debugGetAp(msg: { magicWord: string }, session: BackendSession) { + const { magicWord } = msg; + if (magicWord !== DEBUG_MAGIC_WORD) { + return resResult(STATUS.TOKEN_ERR); + } let roleId = session.get('roleId'); let role = await RoleModel.findByRoleId(roleId, 'lv'); let apJson = await getAp(roleId, role.lv); return resResult(STATUS.SUCCESS, { apJson }); } + + public async debugResetBuyTimes(msg: { magicWord: string }, session: BackendSession) { + const { magicWord } = msg; + if (magicWord !== DEBUG_MAGIC_WORD) { + return resResult(STATUS.TOKEN_ERR); + } + let roleId = session.get('roleId'); + let role = await RoleModel.findByRoleId(roleId, 'lv'); + await ActionPointModel.resetBuyTimes(roleId); + let apJson = await getAp(roleId, role.lv); + return resResult(STATUS.SUCCESS, { apJson }); + } } \ No newline at end of file diff --git a/game-server/app/services/actionPointService.ts b/game-server/app/services/actionPointService.ts index de44d6fac..ddd76e140 100644 --- a/game-server/app/services/actionPointService.ts +++ b/game-server/app/services/actionPointService.ts @@ -7,39 +7,47 @@ import { TASK_TYPE, STATUS } from '../consts'; import { checkActivityTask, checkTask } from './taskService'; import { getDicApByLv } from '../pubUtils/data'; import { pinus } from 'pinus'; -import { resResult } from '../pubUtils/util'; +import { resResult, shouldRefresh } from '../pubUtils/util'; import { RoleModel } from '../db/Role'; +import { pick } from 'underscore'; +import { AP } from '../pubUtils/dicParam'; /** * 获取当前体力 * @param roleId 玩家id * @param lv 玩家等级 */ -export async function getAp(roleId: string, lv: number) { +export async function getAp(roleId: string, lv: number, fromGetAp = false) { const now = Date.now(); const dicAp = getDicApByLv(lv); let dataAp = await ActionPointModel.getAp(roleId); const maxAp = dicAp.maxAp; // 最大体力值 - const per = dicAp.perApRestoreTime * 1000; // 恢复时间(ms) - let { ap, refTime } = dataAp; + const per = AP.PERAPRESTORETIME * 1000; // 恢复时间(ms) + let { ap, refTime, buyRefTime = 0, buyTimes = 0 } = dataAp; + if(shouldRefresh(new Date(buyRefTime), new Date())) { + buyTimes = 0; + } + + let result; if (ap >= maxAp) { // 体力溢出不需要做时间的处理 - return { ap, maxAp, refTime: now, apRemainTime: 0, apMaxRemainTime: 0, isOver: true } + result = { ap, maxAp, refTime: now, apRemainTime: 0, apMaxRemainTime: 0, isOver: true, buyTimes, buyRefTime }; } else { if (refTime > now) refTime = now; // refTime:每次记录时候最近的一个整的时间点,绝对不会大于now let n = Math.floor((now - refTime) / per); // 上次记录到现在可以增长多少体力 ap += n; // 增加上 if (ap >= maxAp) { // 加上后溢出了 ap = maxAp; - return { ap, maxAp, refTime: now, apRemainTime: 0, apMaxRemainTime: 0, isOver: true } + result = { ap, maxAp, refTime: now, apRemainTime: 0, apMaxRemainTime: 0, isOver: true, buyTimes, buyRefTime }; } else { refTime += n * per; // 更新refTime到离现在最近的一个整的时间点 let apRemainTime = Math.floor((refTime + per - now) / 1000); // 恢复下一点需要多少时间 - let apMaxRemainTime = (maxAp - ap - 1) * dicAp.perApRestoreTime + apRemainTime; - return { ap, maxAp, refTime, apRemainTime, apMaxRemainTime, isOver: false } + let apMaxRemainTime = (maxAp - ap - 1) * AP.PERAPRESTORETIME + apRemainTime; + result = { ap, maxAp, refTime, apRemainTime, apMaxRemainTime, isOver: false, buyTimes, buyRefTime }; } } + return fromGetAp?result: pick(result, ['ap', 'maxAp', 'apRemainTime', 'apMaxRemainTime', 'buyTimes']); } /** @@ -53,8 +61,8 @@ export async function getAp(roleId: string, lv: number) { export async function setAp(roleId: string, lv: number, changeAp: number, sid: string, funcs: number[]) { const now = Date.now(); const dicAp = getDicApByLv(lv); - let ApResult = await getAp(roleId, lv); - let { ap, maxAp, refTime, apRemainTime, apMaxRemainTime, isOver } = ApResult; // 更新ap + let ApResult = await getAp(roleId, lv, true); + let { ap, maxAp, refTime, apRemainTime, apMaxRemainTime, isOver, buyTimes } = ApResult; // 更新ap ap += changeAp; if (ap >= maxAp) { // 溢出 refTime = now; @@ -65,8 +73,8 @@ export async function setAp(roleId: string, lv: number, changeAp: number, sid: s await ActionPointModel.saveAp(roleId, ap, refTime); if (isOver && ap < maxAp) { // 特殊处理,溢出之后做减少体力的操作时 - apRemainTime = dicAp.perApRestoreTime; - apMaxRemainTime = (maxAp - ap) * dicAp.perApRestoreTime; + apRemainTime = AP.PERAPRESTORETIME; + apMaxRemainTime = (maxAp - ap) * AP.PERAPRESTORETIME; } if (changeAp < 0) { @@ -74,7 +82,7 @@ export async function setAp(roleId: string, lv: number, changeAp: number, sid: s let { serverId } = await RoleModel.findByRoleId(roleId); await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.BATTLE_COST_AP, -1 * changeAp,); } - let apJson = { ap, maxAp, refTime, apMaxRemainTime, apRemainTime }; + let apJson = { ap, maxAp, refTime, apMaxRemainTime, apRemainTime, buyTimes }; if (changeAp != 0) { let uids = [{ uid: roleId, sid }]; pinus.app.get('channelService').pushMessageByUids('onApUpdate', resResult(STATUS.SUCCESS, { apJson }), uids); @@ -82,3 +90,24 @@ export async function setAp(roleId: string, lv: number, changeAp: number, sid: s return apJson } + +export async function setApBuyTimes(roleId: string, lv: number, sid: string, incTimes: number) { + if(incTimes <= 0) return false; + + let ApResult = await getAp(roleId, lv, true); + let { ap, maxAp, refTime, apRemainTime, apMaxRemainTime, buyTimes, buyRefTime } = ApResult; // 更新ap + + if(shouldRefresh(new Date(buyRefTime), new Date())) { + buyTimes = 0; + buyRefTime = new Date(); + } + buyTimes += incTimes; + await ActionPointModel.saveBuyTimes(roleId, buyTimes, buyRefTime); + + let apJson = { ap, maxAp, refTime, apMaxRemainTime, apRemainTime, buyTimes }; + if(incTimes != 0) { + let uids = [{ uid: roleId, sid }]; + pinus.app.get('channelService').pushMessageByUids('onApUpdate', resResult(STATUS.SUCCESS, { apJson }), uids); + } + return apJson; +} \ No newline at end of file diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 65a532e40..ba2af0511 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -449,7 +449,8 @@ export const FILENAME = { DIC_SERVER_NAME: 'dic_zyz_serverName', DIC_SERVER_GROUP_NAME: 'dic_zyz_serverGropName', DIC_GK_FES: 'dic_zyz_gk_activity_festival', - DIC_AP: 'dic_zyz_ap' + DIC_AP: 'dic_zyz_ap', + DIC_AP_BUY_COST: 'dic_zyz_daliyAp' } export const WAR_RELATE_TABLES = [ diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 5fcdc2153..0320f3821 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -221,6 +221,7 @@ export const STATUS = { NOT_CONSUME_GOODS: { code: 30004, simStr: '非消耗品' }, AP_IS_FULL: { code: 30005, simStr: '满体力不可使用道具' }, ROLE_IS_NOT_INIT: { code: 30006, simStr: '玩家未初始化' }, + AP_BUY_TIMES_LACK: { code: 30007, simStr: '购买次数不足' }, // 武将养成通用 30100 - 30199 diff --git a/shared/db/ActionPoint.ts b/shared/db/ActionPoint.ts index 06ae33bb3..514ed9621 100644 --- a/shared/db/ActionPoint.ts +++ b/shared/db/ActionPoint.ts @@ -15,16 +15,31 @@ export default class ActionPoint extends BaseModel { @prop({ required: true }) ap: number; // 当前体力 + @prop({ required: true }) + buyRefTime: number; // 购买刷新时间,时间戳 + @prop({ required: true }) + buyTimes: number; // 当前体力 + public static async getAp(roleId: string, lean = true) { - let result: ActionPointType = await ActionPointModel.findOne({roleId}).select('ap refTime').lean(lean); + let result: ActionPointType = await ActionPointModel.findOne({roleId}).select('ap refTime buyRefTime buyTimes').lean(lean); if(!result) { - result = await ActionPointModel.findOneAndUpdate({roleId}, { ap: 0, refTime: 0 }, {new: true, upsert: true}).lean(lean) + result = await ActionPointModel.findOneAndUpdate({roleId}, { $setOnInsert: { ap: 0, refTime: 0, buyRefTime: 0, buyTimes: 0 } }, {new: true, upsert: true}).lean(lean) } return result; } public static async saveAp(roleId: string, ap: number, refTime: number, lean = true) { - let result: ActionPointType = await ActionPointModel.findOneAndUpdate({roleId}, { ap, refTime }, {upsert: true, new: true}).lean(lean); + let result: ActionPointType = await ActionPointModel.findOneAndUpdate({roleId}, { $set: {ap, refTime}, $setOnInsert: {buyRefTime: 0, buyTimes: 0} }, {upsert: true, new: true}).lean(lean); + return result; + } + + public static async saveBuyTimes(roleId: string, buyTimes: number, buyRefTime: number) { + let result: ActionPointType = await ActionPointModel.findOneAndUpdate({ roleId }, { $set: {buyTimes, buyRefTime}, $setOnInsert: { ap: 0, refTime: 0 } }, {upsert: true, new: true}).lean(); + return result; + } + + public static async resetBuyTimes(roleId: string) { + let result: ActionPointType = await ActionPointModel.findOneAndUpdate({ roleId }, { $set: { buyRefTime: 0 } }).lean(); return result; } diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 52f819e65..c5e9aa811 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -86,6 +86,7 @@ import { dicActivityType, loadActivityType } from './dictionary/DicActivityType' import { dicTaskTypeDesc, loadTaskType } from './dictionary/DicTaskType'; import { dicServerName, dicServerGroupName, loadServerName } from "./dictionary/DicServerName"; import { dicAp, loadAp, dicApMaxLevel } from './dictionary/DicAp'; +import { dicApBuy, dicApMaxBuyTimes, loadApBuy } from "./dictionary/DicApBuy"; export const gameData = { blurprtCompose: dicBlueprtCompose, @@ -210,7 +211,9 @@ export const gameData = { serverNames: dicServerName, serverGroupNames: dicServerGroupName, ap: dicAp, - apMaxLevel: dicApMaxLevel + apMaxLevel: dicApMaxLevel, + apBuy: dicApBuy, + apMaxBuyTimes: dicApMaxBuyTimes }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 @@ -811,6 +814,7 @@ function loadDatas() { loadTaskType(); loadServerName(); loadAp(); + loadApBuy(); } // 重载dicParam diff --git a/shared/pubUtils/dictionary/DicApBuy.ts b/shared/pubUtils/dictionary/DicApBuy.ts new file mode 100644 index 000000000..f8fdc80ae --- /dev/null +++ b/shared/pubUtils/dictionary/DicApBuy.ts @@ -0,0 +1,22 @@ +import { readFileAndParse, parseGoodStr } from '../util' +import { FILENAME } from '../../consts' +import { RewardInter } from '../interface'; + +export interface DicApBuy { + // 购买次数 + readonly times: number; + // 消耗 + readonly cost: RewardInter[]; +} + +export const dicApMaxBuyTimes = { max: 0 }; +export const dicApBuy = new Map(); +export function loadApBuy() { + let arr = readFileAndParse(FILENAME.DIC_AP_BUY_COST); + arr.forEach(o => { + o.cost = parseGoodStr(o.cost); + dicApBuy.set(o.times, o.cost); + if(o.times > dicApMaxBuyTimes.max) dicApMaxBuyTimes.max = o.times; + }); + arr = undefined; +} \ No newline at end of file