diff --git a/game-server/app/servers/role/handler/shopHandler.ts b/game-server/app/servers/role/handler/shopHandler.ts index fe226f555..181ca4f8c 100644 --- a/game-server/app/servers/role/handler/shopHandler.ts +++ b/game-server/app/servers/role/handler/shopHandler.ts @@ -1,19 +1,21 @@ import { Application, BackendSession, pinus } from "pinus"; import { gameData, hasShopType } from "../../../pubUtils/data"; -import { parseGoodStr, resResult } from "../../../pubUtils/util"; +import { arrToMap, parseGoodStr, resResult } from "../../../pubUtils/util"; import { STATUS, GUILD_STRUCTURE, ITID, CONSUME_TYPE, HERO_QUALITY_TYPE, HERO_GROW_MAX, ITEM_CHANGE_REASON } from "../../../consts"; import { UserShopModel } from "../../../db/UserShop"; import { handleCost, addItems } from "../../../services/role/rewardService"; import { SHOP } from "../../../pubUtils/dicParam"; import { HeroModel } from "../../../db/Hero"; import { checkShopInPurchase, getShopDicById, getShopListByType, getShopPrice } from "../../../services/shopService"; -import { RewardInter } from "../../../pubUtils/interface"; +import { RewardInter, recycleSoulFastPara } from "../../../pubUtils/interface"; import { UserShopTypeModel } from "../../../db/UserShopType"; import { nowSeconds } from "../../../pubUtils/timeUtil"; -import { isGoodsHidden } from "../../../services/dataService"; +import { isGoodsHidden, isHeroHidden } from "../../../services/dataService"; import { RoleModel } from './../../../db/Role'; +import { calUpSixColorsResidueFragment } from "../../../services/role/checkMaterial"; +import { ItemModel } from "../../../db/Item"; -export default function(app: Application) { +export default function (app: Application) { return new ShopHandler(app); } @@ -27,7 +29,7 @@ export class ShopHandler { let serverId = session.get('serverId'); let { shop, type } = msg; - if(!hasShopType(shop, type)) { + if (!hasShopType(shop, type)) { return resResult(STATUS.WRONG_PARMS); } @@ -40,7 +42,7 @@ export class ShopHandler { let roleId = session.get('roleId'); let { shop, type } = msg; - if(!hasShopType(shop, type)) { + if (!hasShopType(shop, type)) { return resResult(STATUS.WRONG_PARMS); } @@ -59,15 +61,15 @@ export class ShopHandler { let { activityId = 0, shopItemId, count } = msg; let dicShopItem = await getShopDicById(activityId, shopItemId, roleId, serverId); - if(!dicShopItem) return resResult(STATUS.DIC_DATA_NOT_FOUND); - if(dicShopItem['productID'] && dicShopItem['productID'] != '&') return resResult(STATUS.CAN_NOT_PURCHASE); - if(dicShopItem['beginTime'] && dicShopItem['beginTime'] > nowSeconds()) return resResult(STATUS.SHOP_CLOSED); - if(dicShopItem['endTime'] && dicShopItem['endTime'] < nowSeconds()) return resResult(STATUS.SHOP_CLOSED); + if (!dicShopItem) return resResult(STATUS.DIC_DATA_NOT_FOUND); + if (dicShopItem['productID'] && dicShopItem['productID'] != '&') return resResult(STATUS.CAN_NOT_PURCHASE); + if (dicShopItem['beginTime'] && dicShopItem['beginTime'] > nowSeconds()) return resResult(STATUS.SHOP_CLOSED); + if (dicShopItem['endTime'] && dicShopItem['endTime'] < nowSeconds()) return resResult(STATUS.SHOP_CLOSED); let userShop = await UserShopModel.findByRoleAndItem(roleId, dicShopItem, seasonNum); - - let checkResult = await checkShopInPurchase(session, activityId, count, userShop?.count||0, dicShopItem); - if(checkResult.code != STATUS.SUCCESS.code) { + + let checkResult = await checkShopInPurchase(session, activityId, count, userShop?.count || 0, dicShopItem); + if (checkResult.code != STATUS.SUCCESS.code) { return resResult(checkResult); } @@ -76,11 +78,11 @@ export class ShopHandler { if (!totalCanBuyTimes || totalCanBuyTimes <= 0) { return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); } - + // 消耗 - let cost = getShopPrice(dicShopItem.money, count, userShop?.count||0, dicShopItem.price); + let cost = getShopPrice(dicShopItem.money, count, userShop?.count || 0, dicShopItem.price); let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.SHOP_PURCHASE); - if(!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); + if (!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); // 次数 userShop = await UserShopModel.purchase(roleId, roleName, activityId, dicShopItem, count, seasonNum); @@ -97,7 +99,7 @@ export class ShopHandler { // 获得 let reward = [{ id: dicShopItem.goodId, - count: count * (dicShopItem.num||1) + count: count * (dicShopItem.num || 1) }]; let goods = await addItems(roleId, roleName, sid, reward, ITEM_CHANGE_REASON.SHOP_PURCHASE); @@ -105,7 +107,7 @@ export class ShopHandler { shopItemId, count, goods }); - + } // 将魂回收 @@ -115,25 +117,29 @@ export class ShopHandler { let sid = session.get('sid'); let { goodsId, count } = msg; - if(isGoodsHidden(goodsId)) return resResult(STATUS.ITEM_IS_HIDDEN); + if (isGoodsHidden(goodsId)) return resResult(STATUS.ITEM_IS_HIDDEN); let dicGoods = gameData.goods.get(goodsId); - if(!dicGoods) return resResult(STATUS.DIC_DATA_NOT_FOUND); + 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.SOUL) { + if (!dicItid) return resResult(STATUS.DIC_DATA_NOT_FOUND); + if (dicItid.type != CONSUME_TYPE.SOUL) { return resResult(STATUS.ITEM_NOT_SOUL); } let hero = await HeroModel.findByHidAndRole(dicGoods.hid, roleId); - if(!hero) return resResult(STATUS.HERO_NOT_MAX); - if(hero.colorStar != HERO_GROW_MAX.COLORSTAR) return resResult(STATUS.HERO_NOT_MAX); + if (!hero) return resResult(STATUS.HERO_NOT_MAX); + if (hero.colorStar != HERO_GROW_MAX.COLORSTAR) { + let residueFragment = await calUpSixColorsResidueFragment(roleId, hero, dicGoods.hid); + if (!residueFragment || count > residueFragment) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + // return resResult(STATUS.HERO_NOT_MAX); + } // 检查自己的数量 let cost = [{ id: goodsId, count }]; let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.RECYCLE_SOUL); - if(!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); + if (!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); let reward: RewardInter[] = []; switch (dicGoods.quality) { @@ -147,7 +153,7 @@ export class ShopHandler { reward = parseGoodStr(SHOP.HERO_SOUL_UR); break; } // 增加货币 - let goods = await addItems(roleId, roleName, sid, reward.map(cur => { return {id: cur.id, count: cur.count * count} }), ITEM_CHANGE_REASON.RECYCLE_SOUL); + let goods = await addItems(roleId, roleName, sid, reward.map(cur => { return { id: cur.id, count: cur.count * count } }), ITEM_CHANGE_REASON.RECYCLE_SOUL); return resResult(STATUS.SUCCESS, { goodsId, count, @@ -155,8 +161,78 @@ export class ShopHandler { }); } + + /** + * 将魂一键回收 + * @param msg + * @param session + * @returns + */ + async recycleSoulFast(msg: { recycleSoulFastPara: recycleSoulFastPara[] }, session: BackendSession) { + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + let sid = session.get('sid'); + let { recycleSoulFastPara } = msg; + + // 检测参数,防止hid、goodsId重复 + let checkHid = new Map(); + let checkGoodsId = new Map(); + for (let { hid, goodsId, count } of recycleSoulFastPara) { + if (checkHid.get(hid) || checkGoodsId.get(goodsId)) return resResult(STATUS.WRONG_PARMS); + checkHid.set(hid, 1); + checkGoodsId.set(goodsId, count); + } + + let items = await ItemModel.find({ roleId, id: { $in: recycleSoulFastPara.map(obj => obj.goodsId) } }); + if (!items || items.length == 0) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + + const itemsMap = arrToMap(items, obj => obj.id); + + let allAddItem = []; + let allCostItem = []; + for (let { hid, goodsId, count } of recycleSoulFastPara) { + let dicGoods = gameData.goods.get(goodsId); + if (!dicGoods) return resResult(STATUS.WRONG_PARMS); + if (hid != dicGoods.hid) return resResult(STATUS.WRONG_PARMS);//校验一下hid和goodsId是否对应 + + let residueFragment = 0; + const curItemCount = itemsMap.get(goodsId)?.count || 0; + if (curItemCount <= 0) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + + let hero = await HeroModel.findByHidAndRole(hid, roleId); + if (!hero) return resResult(STATUS.HERO_IS_HIDDEN); + if (hero.colorStar != HERO_GROW_MAX.COLORSTAR) { + residueFragment = await calUpSixColorsResidueFragment(roleId, hero, hid, curItemCount); + if (!residueFragment || count > residueFragment) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + } + + allCostItem.push({ id: goodsId, count: count }); + + switch (dicGoods.quality) { + case HERO_QUALITY_TYPE.BLUE: + allAddItem.push(...parseGoodStr(SHOP.HERO_SOUL_BULE)); break + case HERO_QUALITY_TYPE.PURPLE: + allAddItem.push(...parseGoodStr(SHOP.HERO_SOUL_PURPLE)); break; + case HERO_QUALITY_TYPE.GOLD: + allAddItem.push(...parseGoodStr(SHOP.HERO_SOUL_GOLDEN)); break; + case HERO_QUALITY_TYPE.UR: + allAddItem.push(...parseGoodStr(SHOP.HERO_SOUL_UR)); break; + } + allAddItem.map(cur => { return { id: cur.id, count: cur.count * count } }) + } + + let costResult = await handleCost(roleId, sid, allCostItem, ITEM_CHANGE_REASON.RECYCLE_SOUL); + if (!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); + // 增加货币 + let goods = await addItems(roleId, roleName, sid, allAddItem, ITEM_CHANGE_REASON.RECYCLE_SOUL); + + return resResult(STATUS.SUCCESS, { goods }); + } + + + // !测试接口。 去除限购次数 - async debugClearPurchaseLimit(msg: { }, session: BackendSession) { + async debugClearPurchaseLimit(msg: {}, session: BackendSession) { let roleId = session.get('roleId'); await UserShopModel.deleteAccount(roleId); diff --git a/game-server/app/services/checkParam.ts b/game-server/app/services/checkParam.ts index 2484aa347..c0890bf98 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -1980,6 +1980,14 @@ export function checkRouteParam(route: string, msg: any) { if (!checkNaturalNumbers(msg.goodsId, msg.count)) return false; break; } + case "role.shopHandler.recycleSoulFast": + { + if (!checkNaturalArray(msg.recycleSoulFastPara)) return false; + for (let { hid, goodsId, count } of msg.recycleSoulFastPara) { + if (!checkNaturalNumbers(hid, goodsId, count)) return false; + } + break; + } case "role.taskHandler.receiveTask": { if (!checkNaturalNumbers(msg.type, msg.id)) return false; diff --git a/game-server/app/services/role/checkMaterial.ts b/game-server/app/services/role/checkMaterial.ts index 54e06297d..9c6521ba1 100644 --- a/game-server/app/services/role/checkMaterial.ts +++ b/game-server/app/services/role/checkMaterial.ts @@ -1,9 +1,11 @@ -import { CURRENCY_TYPE, HANDLE_REWARD_TYPE, CURRENCY_BY_TYPE, CONSUME_TYPE, ITID_STONE_LIMIT } from '../../consts'; +import { CURRENCY_TYPE, HANDLE_REWARD_TYPE, CURRENCY_BY_TYPE, CONSUME_TYPE, ITID_STONE_LIMIT, HERO_GROW_MAX, ABI_STAGE } from '../../consts'; import { RoleModel, RoleType } from '../../db/Role'; import { ItemModel, ItemType } from '../../db/Item'; import { ItemInter, RewardInter, } from '../../pubUtils/interface'; -import { gameData } from '../../pubUtils/data'; +import { gameData, getHeroStarByQuality, getHeroWakeByQuality } from '../../pubUtils/data'; import { sortItems } from './util'; +import { HeroType } from '../../db/Hero'; + export class CheckMeterial { private roleId: string; @@ -351,3 +353,68 @@ export async function getComposeStoneCostAndAdd(curItem, itId) { +/** + * 计算升到6彩后剩余碎片 + * @param roleId + * @param hid + * @returns + */ +export async function calUpSixColorsResidueFragment(roleId: string, hero: HeroType, hid: number, count?: number) { + let needFragment = 0; + + // if (isHeroHidden(hid)) return; + + let dicHero = gameData.hero.get(hid); + if (!dicHero) return; + let { pieceId, jobid } = dicHero; + + let dicJob = gameData.job.get(jobid); + if (!dicJob) return; + + // let hero = await HeroModel.findByHidAndRole(hid, roleId); + // if (!hero) return; + + let curItemCount = count; + if (!count) { + let item = await ItemModel.findbyRoleAndGid(roleId, pieceId); + curItemCount = item?.count || 0; + } + if (curItemCount < 0) return; + + let { star: oldStar, starStage: oldStarStage, quality: oldQuality, colorStar: oldColorStar, colorStarStage: oldColorStarStage, } = hero; + + // 计算升星 + if (oldStar < HERO_GROW_MAX.STAR) { + for (let starIndex = oldStar; starIndex < HERO_GROW_MAX.STAR; starIndex++) { + const curDicHeroStar = getHeroStarByQuality(dicJob.job_class, oldQuality, starIndex); + needFragment += (curDicHeroStar?.advanceUpFragmentNum || 0) * (ABI_STAGE.END - oldStarStage); + } + } + if (curItemCount < needFragment) return; + + // 计算升品 + if (oldQuality < HERO_GROW_MAX.QUALITY) { + for (let qualityIndex = oldQuality; qualityIndex < HERO_GROW_MAX.QUALITY; qualityIndex++) { + const curDicHeroQualityUp = gameData.heroQualityUp.get(qualityIndex); + needFragment += curDicHeroQualityUp?.fragmentNum || 0 + } + } + if (curItemCount < needFragment) return; + + // 计算觉醒升6彩 + if (oldColorStar < HERO_GROW_MAX.COLORSTAR) { + for (let colorStarIndex = oldColorStar; colorStarIndex < 6; colorStarIndex++) { + const curDicHeroStar = getHeroWakeByQuality(dicJob.job_class, dicHero.quality, colorStarIndex) + if (colorStarIndex == 0) { + needFragment += curDicHeroStar?.fragmentNum || 0; + continue; + } + needFragment += (curDicHeroStar?.fragmentNum || 0) * (ABI_STAGE.END - oldColorStarStage) + + } + } + if (curItemCount < needFragment) return; + + + return curItemCount - needFragment; +} \ No newline at end of file diff --git a/game-server/test/Client.ts b/game-server/test/Client.ts index 5cc242d3e..a50ed9e0c 100644 --- a/game-server/test/Client.ts +++ b/game-server/test/Client.ts @@ -66,7 +66,7 @@ export class Client { expect(msg.data.roleId).to.be.a('string'); expect(msg.data.channelName).to.be.a('string'); }); - this._client.request('connector.entryHandler.enter', { serverId: this.serverId, token }, (enterRes) => { + this._client.request('connector.entryHandler.enter', { serverId: this.serverId, token, version:"0.1.1.211229" }, (enterRes) => { // 消息回调 checkSuccessResponse(enterRes); this._client.request('connector.entryHandler.debugGetRole', { magicWord: DEBUG_MAGIC_WORD }, (enterRes) => { diff --git a/game-server/test/role.test.ts b/game-server/test/role.test.ts index 425696390..ff3254d94 100644 --- a/game-server/test/role.test.ts +++ b/game-server/test/role.test.ts @@ -90,7 +90,43 @@ describe('全局养成测试', function () { }); */ -describe('玉石养成测试', function () { +// describe('玉石养成测试', function () { +// let pinusClient: PinusWSClient; +// let roleInfo; + +// before(function (done) { +// const c = new Client(); +// const timer = setInterval(() => { +// if (c.client) { +// pinusClient = c.client; +// roleInfo = c.roleInfo; +// clearInterval(timer); +// done(); +// } +// }, 500); +// }); + +// after(function (done) { +// pinusClient.disconnect(); +// // disconnect 后等待 500ms,供服务器清理环境、退出频道等 +// setTimeout(() => { +// done(); +// }, 500); +// }); +// // { roleId: 'EAmqtKscfy', roleName: 'kYEXBWNk', serverId: 1, values: { reward: addItem } +// it('宝石一键合成', function (done) { +// // pinusClient.request('role.heroHandler.addItem', { id: 60001, count: 10 }, (res) => { +// // console.log("--------------------添加道具", res) +// // done(); +// // }) +// pinusClient.request('role.equipHandler.composeStoneByItId', { itId: 42 }, (res) => { +// console.log("-------------------- res", res) +// done(); +// }) +// }) +// }); + +describe('将魂', function () { let pinusClient: PinusWSClient; let roleInfo; @@ -113,20 +149,28 @@ describe('玉石养成测试', function () { done(); }, 500); }); - // { roleId: 'EAmqtKscfy', roleName: 'kYEXBWNk', serverId: 1, values: { reward: addItem } - it('宝石一键合成', function (done) { - // pinusClient.request('role.heroHandler.addItem', { id: 60001, count: 10 }, (res) => { + + // it('将魂回收', function (done) { + // pinusClient.request('role.heroHandler.addItem', { id: 21019, count: 500 }, (res) => { // console.log("--------------------添加道具", res) // done(); // }) - pinusClient.request('role.equipHandler.composeStoneByItId', { itId: 42 }, (res) => { - console.log("-------------------- res", res) - done(); - }) - }) -}); + // pinusClient.request('role.shopHandler.recycleSoul', { goodsId: 21019, count: 1 }, (res) => { + // console.log("-------------------- res", res) + // done(); + // }) -// function addItem() { -// let rewardInter; -// rewardInter.push({ id: 60001, count: 10 }); -// } \ No newline at end of file + // pinusClient.request('role.shopHandler.recycleSoulFast', { recycleSoulFastPara }, (res) => { + // console.log("-------------------- res", res) + // done(); + // }) + +// }) +// }); +// export interface recycleSoulFastPara { +// hid: number; +// goodsId: number; +// count: number; +// } +// let recycleSoulFastPara: recycleSoulFastPara[] = []; +// recycleSoulFastPara.push({ hid: 19, goodsId: 21019, count: 1 }); \ No newline at end of file diff --git a/shared/pubUtils/interface.ts b/shared/pubUtils/interface.ts index 88a429b4e..8c23e135d 100644 --- a/shared/pubUtils/interface.ts +++ b/shared/pubUtils/interface.ts @@ -8,10 +8,10 @@ export interface RewardInter { expireTime?: number; } -export interface ItemInter { +export interface ItemInter { id: number; - count?: number; - seqId?: number|string; + count?: number; + seqId?: number | string; type?: number; isPay?: boolean; hid?: number; @@ -61,7 +61,13 @@ export interface mailData { sendName: string; } -export interface guildInter { +export interface guildInter { myUserGuild: UserGuildType; - hisUserGuild: UserGuildType; -} \ No newline at end of file + hisUserGuild: UserGuildType; +} + +export interface recycleSoulFastPara { + hid: number; + goodsId: number; + count: number; +} \ No newline at end of file