Files
ZYZ/game-server/app/services/gachaService.ts
2021-06-15 15:42:21 +08:00

313 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { GachaData, Floor, GachaResult, Hope, GachaListReturn } from "../domain/activityField/gachaField";;
import { ActivityModel } from "../db/Activity";
import { DicGacha } from "../pubUtils/dictionary/DicGacha";
import { UserGachaType, UserGachaModel } from "../db/UserGacha";
import { shouldRefresh, getRandEelm, getRandEelmWithWeight } 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";
/**
* 获取招募列表
* @param roleId
*/
export async function getGachaList(roleId: string) {
let userGachaList = await UserGachaModel.findAllByRole(roleId);
let list: GachaListReturn[] = [];
for (let [id, dicGacha] of gameData.gacha) {
if (id == GACHA_ID.TIMELIMIT) continue; // 不包括限时
let userGacha = userGachaList.find(cur => cur.gachaId == id);
if (userGacha)
userGacha = await refreshGacha(dicGacha, userGacha);
let param = new GachaListReturn(dicGacha, userGacha);
list.push(param);
}
return list;
}
/**
* 获取活动页签里的限时卡池
*
* @param roleId 玩家id
* @param aid 活动id
*/
export async function getLimitGacha(serverId: number, roleId: string, activityId: number) {
let activityData = await ActivityModel.findActivity(activityId);
if (!activityData) return false;
let gachaData = new GachaData(activityData);
let userGacha = await UserGachaModel.findByRole(roleId, gachaData.gachaId, activityId);
userGacha = await refreshGacha(gameData.gacha.get(gachaData.gachaId), userGacha);
gachaData.setUserGacha(userGacha);
return gachaData
}
/**
* 刷新免费次数
* @param dicGacha
* @param userGacha
*/
export async function refreshGacha(dicGacha: DicGacha, userGacha: UserGachaType) {
let { day, count } = dicGacha.free;
if (count <= 0) {
return userGacha;
}
let { roleId, gachaId, refFreeTime, refHopeTime, hope } = userGacha;
if (shouldRefresh(refFreeTime, new Date(), REFRESH_TIME, day)) {
let ref = <Date>getTimeFunD().getAfterDayByGap(refFreeTime, day);
userGacha = await UserGachaModel.refreshFreeCount(roleId, gachaId, 0, ref);
}
if (shouldRefresh(refHopeTime, new Date(), REFRESH_TIME)) {
hope = hope.map(cur => { return { ...cur, hasGet: false } });
let ref = getZeroPointD();
userGacha = await UserGachaModel.refreshHopeCount(roleId, gachaId, 0, hope, ref);
}
return userGacha
}
export async function getVisitedHeroList(roleId: string) {
let { visitedHero, refVisitedTime } = await UserGachaModel.findByRole(roleId, GACHA_ID.NORMAL, 0);
if (shouldRefresh(refVisitedTime, new Date())) {
visitedHero = [];
}
return visitedHero;
}
/**
* @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 直接报错
}
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);
}
}
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;
}
}
/**
* 根据contentId获得抽卡结果包括心愿单
* @param contentId dic_zyz_gachaContent的id
* @param lv 玩家等级
* @param hope 玩家心愿单
*/
export function getResultFromContentId(contentId: number, lv: number, hope: Hope[]) {
let dic = gameData.gachaContent.get(contentId);
let { type, param, count } = dic;
if (type == GACHA_CONTENT_TYPE.HERO) {
let pool: number[] = getPoolByHope(hope, param[0]);
let hero = getRandEelm(pool);
let result = new GachaResult(contentId);
result.setHero(hero[0]);
return result
} else {
let pool: number[] = [];
if (type == GACHA_CONTENT_TYPE.HERO_PIECE) {
pool = getAllItemByQuality(IT_TYPE.HERO_PIECE, param[0], lv);
} else if (type == GACHA_CONTENT_TYPE.BLUEPRT) {
pool = getAllItemByQuality(IT_TYPE.BLUEPRT, param[0], lv);
} else if (type == GACHA_CONTENT_TYPE.JEWEL) {
pool = getAllJewelByLv(param[0]);
} else if (type == GACHA_CONTENT_TYPE.TERAPH_MATERIAL) {
pool = param;
} else if (type == GACHA_CONTENT_TYPE.SUIT_PAPER) {
pool = getSuitPaper(lv);
}
let item = getRandEelm(pool);
let result = new GachaResult(contentId);
result.setItem(item[0], count);
return result;
}
}
/**
* 心愿
* @param hope 玩家数据里的心愿单
* @param qualtiy 武将品质
*/
function getPoolByHope(hope: Hope[], qualtiy: number) {
if (qualtiy == HERO_QUALITY_TYPE.GOLD) {
let hopeMap = new Map<number, Hope>();
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);
}
}
/**
* 根据品质获得武将池
* @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) {
let dicHero = gameData.hero.get(actorId);
if (dicHero.quality == quality) {
allHero.push(actorId);
}
if (allQuality.indexOf(dicHero.quality) == -1) allQuality.push(dicHero.quality);
}
allQuality.sort((a, b) => a - b);
if (allQuality.length == 0) return [];
if (allHero.length == 0) {
allHero = getAllHeroByQuality(allQuality[0]);
}
return allHero;
}
function getAllItemByQuality(itid: number, quality: number, lv: number) {
let allPiece: number[] = [];
for (let [id, dicGoods] of gameData.goods) {
if (dicGoods.itid == itid) {
if ((quality == 0 || dicGoods.quality == quality) || dicGoods.lvLimited <= lv) {
allPiece.push(id);
}
}
}
return allPiece;
}
function getAllJewelByLv(lv: number) {
let itids: number[] = [];
for (let [id, { type }] of ITID) {
if (type == CONSUME_TYPE.JEWEL) itids.push(id);
}
let items: number[] = [];
for (let [id, dicGoods] of gameData.goods) {
if (itids.includes(dicGoods.itid)) {
if (lv == 0 || dicGoods.lvLimited == lv) {
items.push(id);
}
}
}
return items;
}
function getSuitPaper(lv: number) {
let items: number[] = [];
for (let [id, dicGoods] of gameData.goods) {
if (dicGoods.itid == IT_TYPE.PAPER) {
if (dicGoods.lvLimited <= lv) {
items.push(id);
}
}
}
return items;
}