diff --git a/game-server/app/servers/activity/handler/gachaHandler.ts b/game-server/app/servers/activity/handler/gachaHandler.ts index d7496844f..04a3e1e53 100644 --- a/game-server/app/servers/activity/handler/gachaHandler.ts +++ b/game-server/app/servers/activity/handler/gachaHandler.ts @@ -67,11 +67,10 @@ export class GachaHandler { if ((gachaId == GACHA_ID.ASSIGN || gachaId == GACHA_ID.TIMELIMIT) && !pickHero) return resResult(STATUS.GACHA_NOT_ASSIGN); let userHeroes = await HeroModel.findByRole(roleId); - - let gachaPull = new GachaPull(gachaId, lv, userHeroes); - gachaPull.setByUserGacha(dicGacha, userGacha); - let { items, heroInfo, resultList, activityData } = gachaPull.pull(count); - let { hope, floor } = gachaPull.getUserGachaParam(userGacha); + let gachaPull = new GachaPull(gachaId); + gachaPull.setByUserGacha(dicGacha, userGacha, false); + let { resultList, heroInfo, items } = gachaPull.pull(count, userHeroes); + let { hope, floor } = gachaPull.getUserGachaParam(); let costNum = count; if (count == 1 && dicGacha.free.count > 0) { // 单抽的时候免费 @@ -370,9 +369,9 @@ export class GachaHandler { } let userHeroes = await HeroModel.findByRole(roleId); - let gachaPull = new GachaPull(gachaId, 0, userHeroes); - gachaPull.setByUserGacha({...dicGacha, floorReward: 0}, userGacha); - let { resultList } = gachaPull.pull(count); + let gachaPull = new GachaPull(gachaId); + gachaPull.setByUserGacha({...dicGacha, floorReward: 0}, userGacha, true); + let { resultList } = gachaPull.pull(count, userHeroes); userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { guideResultList: resultList, guideCount: guideCount + 1 }) @@ -447,11 +446,9 @@ export class GachaHandler { simpleResult = candidate.list; } - let { lv } = await RoleModel.findByRoleId(roleId); let userHeroes = await HeroModel.findByRole(roleId); - - let gachaPull = new GachaPull(gachaId, lv, userHeroes); - let { items, heroInfo, resultList, activityData } = gachaPull.pullBySimpleResult(simpleResult); + let gachaPull = new GachaPull(gachaId); + let { items, heroInfo, resultList } = gachaPull.pullBySimpleResult(simpleResult, userHeroes); userGacha = await UserGachaModel.updateInfo(roleId, gachaId, 0, { guideResultCount: count }); diff --git a/game-server/app/servers/activity/handler/newHeroGachaHandler.ts b/game-server/app/servers/activity/handler/newHeroGachaHandler.ts index 92c3901df..03fbacd17 100644 --- a/game-server/app/servers/activity/handler/newHeroGachaHandler.ts +++ b/game-server/app/servers/activity/handler/newHeroGachaHandler.ts @@ -78,13 +78,11 @@ export class NewHeroGachaHandler { return resResult(STATUS.ACTIVITY_DATA_ERROR); } - let { lv } = await RoleModel.findByRoleId(roleId); - let userHeroes = await HeroModel.findByRole(roleId); - let gachaPull = new GachaPull(GACHA_ID.TIMELIMIT, lv, userHeroes); + let gachaPull = new GachaPull(GACHA_ID.TIMELIMIT); gachaPull.setByActivity(item); - let { items, heroInfo, resultList, activityData } = gachaPull.pull(count); + let { items, heroInfo, resultList } = gachaPull.pull(count, userHeroes); let { hasGetFloor, floorCount } = gachaPull.getActivityParam(); // 消耗东西 diff --git a/game-server/app/services/activity/gachaService.ts b/game-server/app/services/activity/gachaService.ts index acfcb4d0c..1f13f6055 100644 --- a/game-server/app/services/activity/gachaService.ts +++ b/game-server/app/services/activity/gachaService.ts @@ -1,10 +1,10 @@ import { GachaData, Floor, GachaResult, Hope, GachaListReturn } from "../../domain/activityField/gachaField";; import { DicGacha } from "../../pubUtils/dictionary/DicGacha"; import { UserGachaType, UserGachaModel } from "../../db/UserGacha"; -import { shouldRefresh, getRandEelm, getRandEelmWithWeight } from "../../pubUtils/util"; +import { shouldRefresh, getRandEelm, getRandEelmWithWeight, getRandSingleIndex, getRandSingleEelm } from "../../pubUtils/util"; import { REFRESH_TIME, GACHA_TO_FLOOR, GACHA_FLOOR_TYPE, GACHA_CONTENT_TYPE, HERO_QUALITY_TYPE, GACHA_OCCUPY_HID, IT_TYPE, ITID, CONSUME_TYPE, SPECIAL_ATTR, TIME_OUTPUT_TYPE, GACHA_ID } from "../../consts"; import { getTimeFunD, getZeroPointD } from "../../pubUtils/timeUtil"; -import { gameData, getDicGachaFloor } from "../../pubUtils/data"; +import { gameData, getDicGachaFloor, getGachaContentOfHeroQuality } from "../../pubUtils/data"; import { RoleModel } from "../../db/Role"; import { RewardInter } from "../../pubUtils/interface"; import { CreateHeroParam } from "../../domain/roleField/hero"; @@ -89,394 +89,354 @@ export async function getVisitedHeroList(roleId: string) { return visitedHero; } +class PlayerGachaRecord { + public lv: number; // 玩家等级 + public hope: Hope[] = []; // 玩家记录的心愿单达成情况 + public floor: Floor[] = []; + public pickHero: number = 0; + constructor(userGacha: UserGachaType, item: NewHeroGachaItem) { + if(userGacha) { + this.floor = userGacha.floor; + this.hope = userGacha.hope; + this.pickHero = userGacha.pickHero; + } + if(item) { + this.floor.push({ id: GACHA_FLOOR_TYPE.ASSIGN, count: item.count, hasGetFloor: item.hasGetFloor }); + } + } + + public getFloorCountByFloorType(floorType: GACHA_FLOOR_TYPE) { + let userFloor = this.floor.find(cur => cur.id == floorType); + if(userFloor) { + return { floorCount: userFloor.count, hasGetFloor: userFloor.hasGetFloor } + } else { + return { floorCount: 0, hasGetFloor: false } + } + } + + public setFloorCount(floorType: GACHA_FLOOR_TYPE, count: number, hasGetFloor: boolean) { + let index = this.floor.findIndex(cur => cur.id == floorType); + if(index == -1) { + this.floor.push({ id: floorType, count, hasGetFloor }) + } else { + this.floor[index].count = count; + this.floor[index].hasGetFloor = hasGetFloor; + } + } + + public getHopeHero(randHopePosition: number) { + let userHope = this.hope.find(cur => cur.id == randHopePosition); + if(!userHope || userHope.hasGet) return 0; + return userHope.hid||0; + } + + public setUserHope(hid: number) { + if(hid <= 0) return; + let userHope = this.hope.find(cur => cur.hid == hid); + if(userHope && !userHope.hasGet) userHope.hasGet = true; + } + + public getUserGachaParam() { + return { hope: this.hope, floor: this.floor } + } +} + +class GachaResults { + count: number = 0; + list: GachaResult[] = []; + + addResult(contentId: number, id?: number) { + this.count++; + this.list.push(new GachaResult(contentId, id)); + } + + public shouldPurpleFloor(floorCount: number) { + return this.count == floorCount && !this.hasPurple(); + } + + public hasPurple() { + return this.list.findIndex(gachaResult => this.checkContentIsHero(gachaResult.contentId, HERO_QUALITY_TYPE.PURPLE)) != -1; + } + + private checkContentIsHero(contentId: number, quality: HERO_QUALITY_TYPE) { + let {type, param} = gameData.gachaContent.get(contentId); + return type == GACHA_CONTENT_TYPE.HERO && param[0] >= quality; + } + + public getRandItemNotPurple() { + let indexes = this.filterIndex(item => { + return !this.checkContentIsHero(item.contentId, HERO_QUALITY_TYPE.PURPLE); + }); + let index = getRandSingleEelm(indexes); + return this.list[index]; + } + + + private filterIndex(callback: (item: GachaResult) => boolean) { + let result: number[] = []; + for(let i = 0; i < this.list.length; i++) { + if(callback(this.list[i])) { + result.push(i); + } + } + return result; + } +} /** * 抽卡方法 */ export class GachaPull { private gachaType: GACHA_ID; // 抽卡类型 - private dicFloorType: GACHA_FLOOR_TYPE; // 保底类型 金将保底,指定保底 // 字典 - private dicFloorCount: number; // 保底次数 + private dicFloor: Map; // 保底次数 private percent: { id: number, weight: number, goodId?: number }[]; - private dicFloor: { id: number, weight: number, goodId?: number }; // 保底的一项,percent中的一项 + private bigReward: { id: number, weight: number, goodId?: number }; // 保底的一项,percent中的一项 // 玩家数据 - private lv: number; // 玩家等级 - private userHeroes: number[]; // 玩家的所有武将 - private floorCount: number = 0; // 记录下的保底次数 - private hasGetFloor: boolean = false; // 玩家是否达到保底 - private hope: Hope[] = []; // 玩家记录的心愿单达成情况 + private player: PlayerGachaRecord; + // 结果 + private result = new GachaResults(); - constructor(gachaType: GACHA_ID, lv: number, userHeroes: HeroType[]) { + constructor(gachaType: GACHA_ID, ) { this.gachaType = gachaType; - if (gachaType == GACHA_ID.NORMAL) { // 元宝招募,金色武将保底,按次数给保底,抽到就重新计算次数,单抽也算 - this.dicFloorType = GACHA_FLOOR_TYPE.GOLD; - } else if (gachaType == GACHA_ID.ASSIGN || gachaType == GACHA_ID.TIMELIMIT) { // 求贤若渴,和活动抽卡金色保底,伪随机,n次内给且只给一个,单抽也算 - this.dicFloorType = GACHA_FLOOR_TYPE.ASSIGN; - } else { - this.dicFloorType = GACHA_FLOOR_TYPE.GOLD; - } - this.userHeroes = userHeroes.map(cur => cur.hid); - this.lv = lv; } // 一般抽卡setter - public setByUserGacha(dicGacha: DicGacha, userGacha: UserGachaType) { + public setByUserGacha(dicGacha: DicGacha, userGacha: UserGachaType, isGuide: boolean) { // 字典方面 - let percent = dicGacha.percent; - // 设置pickup的武将 - let newPercent = []; - for (let { id, weight } of percent) { - if (id == 4) { - newPercent.push({ id, weight, goodId: userGacha.pickHero }) - } else if ( id == 14 || id == 15) { - let dicHero = gameData.hero.get(userGacha.pickHero); - newPercent.push({ id, weight, goodId: dicHero.pieceId }); - } else { - newPercent.push({ id, weight }); - } - } - this.percent = newPercent; + this.percent = dicGacha.percent; if (dicGacha.floorReward > 0) { - this.dicFloor = this.percent[dicGacha.floorReward - 1]; + this.bigReward = this.percent[dicGacha.floorReward - 1]; } - this.dicFloorCount = getDicGachaFloor(this.dicFloorType); + if(!isGuide) this.setDicFloor(); // 玩家数据 - let userFloor = userGacha.floor.find(cur => cur.id == this.dicFloorType); - if (userFloor) { - this.floorCount = userFloor.count; - this.hasGetFloor = userFloor.hasGetFloor || false; - } - this.hope = userGacha.hope; + this.player = new PlayerGachaRecord(userGacha, null); } + + private setDicFloor() { + let dicFloor = GACHA_TO_FLOOR.get(this.gachaType)||[]; + for(let id of dicFloor) { + this.dicFloor.set(id, getDicGachaFloor(id)); + } + } + // 新武将活动setter public setByActivity(item: NewHeroGachaItem) { - this.floorCount = item.count; - this.hasGetFloor = item.hasGetFloor; this.percent = item.percent; - this.dicFloorCount = item.floorCount; if (item.floorReward >= 0) { - this.dicFloor = item.percent[item.floorReward] + this.bigReward = item.percent[item.floorReward] } + this.dicFloor.set(GACHA_FLOOR_TYPE.ASSIGN, item.floorCount); + this.player = new PlayerGachaRecord(null, item); } - private getFloorResult(base: { id: number, weight: number, goodId?: number }) { - if (!this.dicFloor) return base; - let isTarget = base.id == this.dicFloor.id; // 是否抽到的就是那个保底 - if (this.dicFloorType == GACHA_FLOOR_TYPE.GOLD) { // 元宝招募 橙色 按次数保底,抽到就重计 - if (isTarget || this.floorCount + 1 >= this.dicFloorCount) { - this.floorCount = 0; - return this.dicFloor; - } else { - this.floorCount++; - return base; - } - } else if (this.dicFloorType == GACHA_FLOOR_TYPE.ASSIGN) { - let result = base; - if (isTarget || this.floorCount + 1 >= this.dicFloorCount) { - if (this.hasGetFloor) { // 已经获得过一次保底了, 不给,换个给 - let { dic } = getRandEelmWithWeight(this.percent.filter(cur => cur.id != this.dicFloor.id)); - result = dic - } else { - this.hasGetFloor = true; - result = this.dicFloor; - } - } - this.floorCount++; - if (this.floorCount >= this.dicFloorCount) { - this.hasGetFloor = false; - this.floorCount = 0; - } - // console.log('******', this.floorCount, this.dicFloorCount, this.hasGetFloor) - return result; - } else { - return base - } + public pull(count: number, userHeroes: HeroType[]) { + this.pullContent(count); + this.processFloors(); + this.processDetail(); + this.processHope(); + let { heroInfo, items, resultList } = this.getFinalResult(userHeroes) + return { resultList, heroInfo, items }; } - /** - * 根据contentId获得抽卡结果(包括心愿单) - * @param contentId dic_zyz_gachaContent的id - * @param lv 玩家等级 - * @param hope 玩家心愿单 - */ - private getResultFromContentId(contentId: number, goodId?: number) { - let dic = gameData.gachaContent.get(contentId); - let { type, param, count } = dic; - if (type == GACHA_CONTENT_TYPE.HERO) { - let pool: number[] = []; - if (this.hope.length > 0) { - pool = this.getPoolByHope(this.hope, param[0]); // 根据心愿单获得卡池 - } else { - pool = getAllHeroByQuality(param[0]); // 没有心愿单就根据武将颜色 - } - let hero = getRandEelm(pool); - let result = new GachaResult(contentId); - result.setHero(hero[0]); - if (goodId > 0) { - result.setHero(goodId); - } - return result - } else { - let pool: number[] = []; - if (type == GACHA_CONTENT_TYPE.HERO_PIECE) { - pool = getAllItemByQuality(IT_TYPE.HERO_PIECE, param[0]); - } else if (type == GACHA_CONTENT_TYPE.JEWEL) { - pool = getAllJewelByLv(param[0]); - } else if (type == GACHA_CONTENT_TYPE.TERAPH_MATERIAL) { - pool = param; - } - - if(pool.length <= 0) { - if(contentId <= 0) return false - return this.getResultFromContentId(contentId - 1, goodId); - } - let item = getRandEelm(pool); - let result = new GachaResult(contentId); - result.setItem(item[0], count); - if (goodId > 0) { - result.setItem(goodId, count); - } - return result; - } - } - - /** - * 心愿 - * @param hope 玩家数据里的心愿单 - * @param qualtiy 武将品质 - */ - private getPoolByHope(hope: Hope[], qualtiy: number) { - if (qualtiy == HERO_QUALITY_TYPE.GOLD) { - let hopeMap = new Map(); - let hasGetHope: number[] = []; - for (let h of hope) { - if (h.hid > 0) { - hopeMap.set(h.id, h); - if (h.hasGet) hasGetHope.push(h.id); - } - } - - let list = gameData.gachaHope.map(cur => { - return { ...cur, hasGet: hopeMap.get(cur.id)?.hasGet || false }; - }) - let { dic: { id, hasGet } } = getRandEelmWithWeight(list); - if (id == 0 || hasGet) { - return getAllHeroByQuality(qualtiy).filter(cur => !hasGetHope.includes(cur)); - } else { - if (hopeMap.has(id)) { - let curHope = hopeMap.get(id); - curHope.hasGet = true; - return [curHope.hid]; - } else { - return getAllHeroByQuality(qualtiy).filter(cur => !hasGetHope.includes(cur)); - } - } - } else { - return getAllHeroByQuality(qualtiy); - } - } - - public pull(count: number) { - let items: RewardInter[] = [], heroInfo: CreateHeroParam[] = [], resultList: GachaResult[] = []; - - let activityData = [];//活动需要统计抽中的英雄、碎片品质 + // 根据基础的percent抽基地 + private pullContent(count: number) { for (let i = 0; i < count; i++) { // 按照一般概率抽出 let { dic } = getRandEelmWithWeight(this.percent); - let { id: contentId, goodId } = this.getFloorResult(dic); // 根据保底替换 - contentId = this.getPurpleFloor(count, i, contentId, resultList); - let result = this.getResultFromContentId(contentId, goodId); - if(!result) continue; + if(dic) this.result.addResult(dic.id, dic.goodId); + } + } + + // 保底 + private processFloors() { + for(let [id, floorCount] of this.dicFloor) { + switch(id) { + case GACHA_FLOOR_TYPE.PURPLE: + this.setPurpleFloor(floorCount); + break; + case GACHA_FLOOR_TYPE.GOLD: + this.setGoldFloor(floorCount); + break; + case GACHA_FLOOR_TYPE.ASSIGN: + this.setAssignFloor(floorCount); + } + } + } + + // 紫将保底,10连抽必出一个紫将 + private setPurpleFloor(floorCount: number) { + if(this.result.shouldPurpleFloor(floorCount)) { + let item = this.result.getRandItemNotPurple(); + item.setContentId(getGachaContentOfHeroQuality(HERO_QUALITY_TYPE.PURPLE)); + } + } + + // 元宝招募保底,金将保底,按次数给保底,抽到就重新计算次数,单抽也算 + public setGoldFloor(dicFloorCount: number) { + let floorType = GACHA_FLOOR_TYPE.GOLD; + let { floorCount: historyCount } = this.player.getFloorCountByFloorType(floorType); + for(let gachaResult of this.result.list) { + let isTarget = this.bigReward.id == gachaResult.contentId; + if(++historyCount >= dicFloorCount || isTarget) { + gachaResult.setContentId(this.bigReward.id, this.bigReward.goodId); + historyCount = 0; + } + } + this.player.setFloorCount(floorType, historyCount, false); + } + + // 求贤若渴,和活动抽卡金色保底,伪随机,n次内给且只给一个,单抽也算 + public setAssignFloor(dicFloorCount: number) { + let floorType = GACHA_FLOOR_TYPE.ASSIGN; + let { floorCount: historyCount, hasGetFloor } = this.player.getFloorCountByFloorType(floorType); + for(let gachaResult of this.result.list) { + let isTarget = this.bigReward.id == gachaResult.contentId; + if(++historyCount >= dicFloorCount || isTarget) { + if(hasGetFloor) { // 已经获得过一次保底了, 不给,换个给 + gachaResult.setContentId(getGachaContentOfHeroQuality(HERO_QUALITY_TYPE.PURPLE)); + } else { + gachaResult.setContentId(this.bigReward.id, this.bigReward.goodId); + hasGetFloor = false; + } + gachaResult.setContentId(this.bigReward.id, this.bigReward.goodId); + historyCount = 0; + } + if(historyCount >= dicFloorCount) { + hasGetFloor = false; + historyCount = 0; + } + } + this.player.setFloorCount(floorType, historyCount, hasGetFloor); + } + + // 具体的卡池 + private processDetail() { + for(let gachaResult of this.result.list) { + let detail = new ContentDetail(gachaResult.contentId, gachaResult.pickHero||this.player.pickHero); + let id = detail.getResult(); + gachaResult.setHidOrGid(id, detail.count); + } + } + + // 心愿单 + private processHope() { + if(this.gachaType != GACHA_ID.NORMAL) return; + for(let gachaResult of this.result.list) { + if(gachaResult.contentId != getGachaContentOfHeroQuality(HERO_QUALITY_TYPE.GOLD)) continue; // 只有橙将 + let { dic: { id: randHopePosition } } = getRandEelmWithWeight(gameData.gachaHope); + if(randHopePosition > 0) { // 按玩家设置的心愿单塞 + let hopeHero = this.player.getHopeHero(randHopePosition) + if(hopeHero > 0) gachaResult.setHidOrGid(hopeHero); + } + this.player.setUserHope(gachaResult.hid); // 设置心愿单中了没有 + } + } + + // 创建信息 + public getFinalResult(userHeroes: HeroType[]) { + return this.transferToFinalResult(this.result.list, userHeroes); + } + + public pullBySimpleResult(simpleResult: {contentId: number, hid: number }[], userHeroes: HeroType[]) { + let results = simpleResult.map(obj => { + return new GachaResult(obj.contentId, obj.hid); + }); + return this.transferToFinalResult(results, userHeroes); + } + + private transferToFinalResult(results: GachaResult[], userHeroes: HeroType[]) { + let hids = userHeroes.map(cur => cur.hid); + let items: RewardInter[] = [], heroInfo: CreateHeroParam[] = []; + + for (let result of results) { if (result.hid > 0) { - let hasHero = this.userHeroes.indexOf(result.hid) != -1; + let hasHero = hids.indexOf(result.hid) != -1; if (hasHero) { // 已有转换为碎片 - let dicHero = gameData.hero.get(result.hid); - activityData.push({ hid: result.hid, quality: dicHero.quality }); let { pieceId, count } = transPiece(result.hid); result.transferToPiece(pieceId, count); items.push({ id: pieceId, count }); } else { heroInfo.push({ hid: result.hid, count: 1 })//默认1个英雄 - this.userHeroes.push(result.hid); + hids.push(result.hid); } } else { items.push({ id: result.id, count: result.count }); } - resultList.push(result); } - - return { items, heroInfo, resultList, activityData } - } - - public pullBySimpleResult(simpleResult: {contentId: number, hid: number}[]) { - let items: RewardInter[] = [], heroInfo: CreateHeroParam[] = [], resultList: GachaResult[] = []; - let activityData = [];//活动需要统计抽中的英雄、碎片品质 - - for (let { contentId, hid } of simpleResult) { - let result = new GachaResult(contentId); - result.setHero(hid); - - let hasHero = this.userHeroes.indexOf(result.hid) != -1; - if (hasHero) { // 已有转换为碎片 - let dicHero = gameData.hero.get(result.hid); - activityData.push({ hid: result.hid, quality: dicHero.quality }); - let { pieceId, count } = transPiece(hid); - result.transferToPiece(pieceId, count); - items.push({ id: pieceId, count }); - } else { - heroInfo.push({ hid, count: 1 })//默认1个英雄 - this.userHeroes.push(result.hid); - } - resultList.push(result); - } - - return { items, heroInfo, resultList, activityData } - } - - private getPurpleFloor(count: number, index: number, contentId: number, resultList: GachaResult[]) { - if (this.gachaType == GACHA_ID.NORMAL && count == 10 && index == count - 1) { // 10连到最后一抽 - let { type, param } = gameData.gachaContent.get(contentId); - if (type != GACHA_CONTENT_TYPE.HERO || param[0] < HERO_QUALITY_TYPE.PURPLE) { - let hasPurple = false; - for (let { contentId } of resultList) { - let { type, param } = gameData.gachaContent.get(contentId); - if (type == GACHA_CONTENT_TYPE.HERO && param[0] >= HERO_QUALITY_TYPE.PURPLE) { - hasPurple = true; break; - } - } - if (!hasPurple) { - return gameData.gachaContentHero.get(HERO_QUALITY_TYPE.PURPLE); - } - } - } - return contentId + return { items, heroInfo, resultList: results } } // 获得需要储存的数据 // 一般抽卡getter - public getUserGachaParam(userGacha: UserGachaType) { - let floor = userGacha.floor || []; - let curFloor = floor.find(cur => cur.id == this.dicFloorType); - if (curFloor) { - curFloor.hasGetFloor = this.hasGetFloor; - curFloor.count = this.floorCount; - } else { - floor.push({ id: this.dicFloorType, hasGetFloor: this.hasGetFloor, count: this.floorCount }) - } - return { hope: this.hope, floor } + public getUserGachaParam() { + return this.player.getUserGachaParam() } // 活动getter public getActivityParam() { - return { hasGetFloor: this.hasGetFloor, floorCount: this.floorCount }; + let { hasGetFloor, floorCount } = this.player.getFloorCountByFloorType(GACHA_FLOOR_TYPE.ASSIGN); + return { hasGetFloor, floorCount }; } } +class ContentDetail { + contentId: number; + type: number; + count: number; + param: number[]; + pickHero: number; -/** - * @description 获得保底结果 - * @param gachaId 招募表id - * @param base 按普通概率获得的东西 内容参考gachaContent - * @param floor 玩家保底数据 - */ -export function getFloorResult(gachaId: number, base: number, floor: Floor[]) { - let dicGachaContent = gameData.gachaContent.get(base); // 普通随机得出的结果是哪一类; - if (!dicGachaContent) { - console.error('dic_zyz_gachaContent error'); - return false; // DIC_NOT_FOUND 直接报错 + constructor(contentId: number, pickHero: number) { + this.contentId = contentId; + let dicGachaContet = gameData.gachaContent.get(contentId); + this.type = dicGachaContet.type; + this.count = dicGachaContet.count; + this.param = dicGachaContet.param; + this.pickHero = pickHero; } - let touchedFloor = 0, touchedHeroQuality: number; // 触发的保底的id - let dicFloorType = GACHA_TO_FLOOR.get(gachaId); - for (let id of dicFloorType) { - let { type, param } = dicGachaContent; - let heroQuality = getHeroQuality(id); - if (heroQuality == false) continue; - - let isTarget = type == GACHA_CONTENT_TYPE.HERO && param[0] == heroQuality; - if (!isTarget) { // 触发保底 - let percent = countFloorPercent(id, floor); - let rand = Math.random(); - if (rand < percent) { // 可以替换为保底 - if (touchedFloor > 0 && touchedFloor < id) { // 如果已经触发过保底了 - // 更新被覆盖掉的那个保底数量 - updateUserFloor(floor, touchedFloor, false); - } - touchedFloor = id; - touchedHeroQuality = heroQuality; - } else { - updateUserFloor(floor, id, false); - } - } else { - updateUserFloor(floor, id, true); + public getResult() { + switch(this.contentId) { + case GACHA_CONTENT_TYPE.HERO: + return this.randomHeroByQuality(this.param[0]); + case GACHA_CONTENT_TYPE.HERO_PIECE: + return this.randomHeroPieceByQuality(this.param[0]); + case GACHA_CONTENT_TYPE.STONE: + return this.randomStoneByLv(this.param[0]); + case GACHA_CONTENT_TYPE.ITEMS: + return getRandSingleEelm(this.param); } } - if (touchedFloor > 0) { - updateUserFloor(floor, touchedFloor, true); - return gameData.gachaContentHero.get(touchedHeroQuality); - } else { - return base; - } -} - -/** - * @description 更新玩家保底数据 - * @param floor 玩家保底数据 - * @param id 更新的保底类型 - * @param needReset 是否重置保底 - */ -function updateUserFloor(floor: Floor[], id: number, needReset: boolean) { - let curFloor = floor.find(cur => cur.id == id); - if (!curFloor) { - curFloor = { id, count: 0 }; - floor.push(curFloor); - } - if (needReset) { - curFloor.count = 0; - } else { - curFloor.count++; - } -} - -/** - * 计算保底概率 - * @param id 保底类型 - * @param floor 玩家保底数据 - */ -function countFloorPercent(id: number, floor: Floor[]) { - let curFloor = floor.find(cur => cur.id == id); - let count = curFloor ? curFloor.count : 0; - // 保底概率,暂时使用线性公式,由策划使用后选择 - let floorCount = getDicGachaFloor(id); - return count / floorCount; -} - -/** - * 获取保底武将的品质 - * @param id 保底类型id in GACHA_FLOOR_TYPE - */ -function getHeroQuality(id: number) { - if (id == GACHA_FLOOR_TYPE.PURPLE) { - return HERO_QUALITY_TYPE.PURPLE; - } else if (id == GACHA_FLOOR_TYPE.GOLD) { - return HERO_QUALITY_TYPE.GOLD; - } else if (id == GACHA_FLOOR_TYPE.ASSIGN) { - return 0 - } else { - return false; + private randomHeroByQuality(quality: number) { + if(quality == 0) return this.pickHero; + + let heroes = getAllHeroByQuality(quality); + return getRandSingleEelm(heroes); } + + + private randomHeroPieceByQuality(quality: number) { + let pieces = getAllItemByQuality(IT_TYPE.HERO_PIECE, quality); + return getRandSingleEelm(pieces); + } + + + private randomStoneByLv(lv: number) { + let stones = getAllStoneByLv(lv); + return getRandSingleEelm(stones); + } + } + /** * 根据品质获得武将池 * @param quality 品质 */ export function getAllHeroByQuality(quality: number) { - if (quality == 0) return [GACHA_OCCUPY_HID]; - let allHero: number[] = []; let allQuality: number[] = []; for (let { actorId } of gameData.recruit) { @@ -494,7 +454,7 @@ export function getAllHeroByQuality(quality: number) { return allHero; } -function getAllItemByQuality(itid: number, quality: number) { +export function getAllItemByQuality(itid: number, quality: number) { let allPiece: number[] = []; for (let [id, dicGoods] of gameData.goods) { if (dicGoods.itid == itid) { @@ -506,7 +466,7 @@ function getAllItemByQuality(itid: number, quality: number) { return allPiece; } -function getAllJewelByLv(lv: number) { +export function getAllStoneByLv(lv: number) { let items: number[] = []; for(let [id, dicStone] of gameData.stone) { if(dicStone.lv == lv) items.push(id); diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index f2d95e015..f694aca23 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -780,8 +780,8 @@ export enum GACHA_CONTENT_TYPE { HERO = 1, // 武将 param为武将品质 HERO_PIECE = 2, // 武将碎片 武将品质 // BLUEPRT = 3, // 藏宝图 藏宝图品质 - JEWEL = 4, // 宝石 宝石等级 - TERAPH_MATERIAL = 5, // 强化神像用的材料 材料物品id + STONE = 4, // 宝石 宝石等级 + ITEMS = 5, // 物品id // SUIT_PAPER = 6, // 套装图纸 } diff --git a/shared/db/UserGacha.ts b/shared/db/UserGacha.ts index e3515bf49..0a832b335 100644 --- a/shared/db/UserGacha.ts +++ b/shared/db/UserGacha.ts @@ -3,7 +3,7 @@ import { index, getModelForClass, prop, DocumentType, modelOptions } from '@type import { getZeroPointD } from '../pubUtils/timeUtil'; import { RECRUIT } from '../pubUtils/dicParam'; -class Floor { +export class Floor { @prop({ required: true }) id: number; // 保底类型 1-紫将保底 2-金将保底 3-指定将保底 @prop({ required: true }) diff --git a/shared/domain/activityField/gachaField.ts b/shared/domain/activityField/gachaField.ts index 0fb406fdf..dc1bd3ccd 100644 --- a/shared/domain/activityField/gachaField.ts +++ b/shared/domain/activityField/gachaField.ts @@ -4,7 +4,7 @@ import { prop } from '@typegoose/typegoose'; import { UserGachaType } from '../../db/UserGacha'; import { getTimeFun } from '../../pubUtils/timeUtil'; import { DicGacha } from '../../pubUtils/dictionary/DicGacha'; -import { GACHA_OCCUPY_HID, } from '../../consts'; +import { GACHA_CONTENT_TYPE, } from '../../consts'; import { gameData } from '../../pubUtils/data'; import { getFloorStatus } from '../../pubUtils/util'; @@ -52,6 +52,8 @@ export class Floor { id: number; // 保底类型 1-紫将保底 2-金将保底 3-指定将保底 @prop({ required: true }) count: number; // 抽卡次数 + @prop({ required: true }) + hasGetFloor: boolean; // 是否抽到保底了 } /** @@ -87,7 +89,7 @@ export class GachaListReturn { refFreeTime: number = 0; // 免费次数下次刷新时间 count: number = 0; // 整体已抽卡次数 guideResultCount: number = 0; // 引导已抽卡次数 - floor: Floor[] = []; // 整体保底 + floor: {id: number, count: number}[] = []; // 整体保底 hope: Hope[] = []; // 心愿单 point: number = 0; // 积分 turntable: Turntable[] = []; // 转盘记录 @@ -126,14 +128,26 @@ export class GachaResult { id: number = 0; // 道具id @prop({ required: true }) count: number = 0; // 道具数量 + pickHero: number = 0; - constructor(contentId: number) { + constructor(contentId: number, id?: number) { this.contentId = contentId; + if(id) this.pickHero = id; } - setSetPickHero(hid: number) { - if (hid > 0 && this.hid == GACHA_OCCUPY_HID) { - this.hid = hid; + setContentId(contentId: number, id?: number) { + this.contentId = contentId; + if(id) this.pickHero = id; + } + + setHidOrGid(id: number, count?: number) { + let dicGachaContent = gameData.gachaContent.get(id); + if(!dicGachaContent) return; + + if(dicGachaContent.type == GACHA_CONTENT_TYPE.HERO) { + this.setHero(id); + } else { + this.setItem(id, count); } } @@ -144,7 +158,7 @@ export class GachaResult { setItem(id: number, count: number) { this.id = id; - this.count = count; + if(count) this.count = count; } transferToPiece(id: number, count: number) { diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 8cd5d5f85..a5f14e5fa 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -13,7 +13,7 @@ import { dicTower, loadTower } from "./dictionary/DicTower"; import { dicTowerTask, loadTowerTask } from "./dictionary/DicTowerTask"; import { dicWar, dicWarPvp, dicDailyWarByType, loadWar, dicHeroIdByWar } from "./dictionary/DicWar"; import { dicWarJson, loadWarJson } from "./dictionary/DicWarJson"; -import { AUCTION_TIME } from "../consts"; +import { AUCTION_TIME, GACHA_CONTENT_TYPE } from "../consts"; import { dicFashions, dicFashionsByHeroId, loadFashions } from "./dictionary/DicFashions"; import { friendShips, friendShipHidAandIds, loadFriendShip } from "./dictionary/DicFriendShip"; import { maxFriendShipLv, dicFriendShipLevelMap, loadFriendShipLevel } from "./dictionary/DicFriendShipLevel"; @@ -548,6 +548,13 @@ export function getFriendLvByExp(exp: number) { return resultLv; } +export function getGachaContentOfHeroQuality(quality: number) { + for(let [id, { type, param }] of gameData.gachaContent) { + if(type == GACHA_CONTENT_TYPE.HERO && param[0] == quality) return id; + } + return 0 +} + /** * 根据主公当前经验获得当前等级 * @param exp 累积经验