116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import { STEP_CHEST_RATE, STEP_SCORE_MAX, STEP_SCORE_MIN } from 'common/Constants'
|
|
import { ActivityChest, ActivityChestClass, ChestStatusEnum } from 'models/ActivityChest'
|
|
import { ZError, ZRedisClient } from 'zutils'
|
|
import { DocumentType } from '@typegoose/typegoose'
|
|
|
|
const chestCfg = require('../../configs/chest.json')
|
|
const chestBonusItems = require('../../configs/chest_bonus_item.json')
|
|
const chestLevelMap = new Map()
|
|
|
|
let STEP_CHEST_LEVEL = chestCfg.chests.map((cfg: any) => cfg.probability)
|
|
STEP_CHEST_LEVEL.sort((a: number, b: number) => a - b)
|
|
chestCfg.chests.forEach((cfg: any) => {
|
|
chestLevelMap.set(cfg.level, cfg)
|
|
})
|
|
|
|
const generateBounsCfg = (cfg: any) => {
|
|
let randoms = []
|
|
for (let i = 0; i < cfg.bounsCount; i++) {
|
|
randoms.push(Math.random())
|
|
}
|
|
let total = randoms.reduce((a: number, b: number) => a + b)
|
|
randoms = randoms.map((r: number) => r / total)
|
|
let ys = []
|
|
randoms.forEach((r: number) => {
|
|
let tmp = cfg.maxBounsScore * r
|
|
tmp = Math.min(1, Math.round(tmp))
|
|
ys.push(tmp)
|
|
})
|
|
ys.sort((a: number, b: number) => b - a)
|
|
return ys
|
|
}
|
|
|
|
export const generateNewChest = (uid: string, activity: string, level = 1, status = ChestStatusEnum.LOCKED) => {
|
|
let cfg = chestLevelMap.get(level)
|
|
if (!cfg) {
|
|
throw new ZError(11, 'chest cfg not found')
|
|
}
|
|
let scoreInit = Math.floor(Math.random() * (cfg.initScoreMax - cfg.initScoreMin + 1) + cfg.initScoreMin)
|
|
let chest = new ActivityChest({
|
|
user: uid,
|
|
activity: activity,
|
|
level: level,
|
|
maxBounsCount: cfg.maxBounsCount,
|
|
bounsCfg: generateBounsCfg(cfg),
|
|
scoreInit,
|
|
status,
|
|
})
|
|
return chest
|
|
}
|
|
|
|
export const generateChestLevel = function (): number {
|
|
const levelDefine = STEP_CHEST_LEVEL
|
|
let randomLevel = Math.floor(Math.random() * levelDefine[levelDefine.length - 1])
|
|
let level = 1
|
|
for (let i = 0; i < levelDefine.length; i++) {
|
|
if (randomLevel < levelDefine[i]) {
|
|
level = i + 1
|
|
break
|
|
}
|
|
}
|
|
return level
|
|
}
|
|
/**
|
|
* 生成步数奖励
|
|
* // TODO:: 增加白名单和特殊种类nft的奖励
|
|
* @param uid
|
|
* @param activity
|
|
* @param step
|
|
* @returns
|
|
*/
|
|
export const generateStepReward = (uid: string, activity: string, step: number) => {
|
|
let score = 0
|
|
let chests = []
|
|
for (let i = 0; i < step; i++) {
|
|
if (Math.random() < STEP_CHEST_RATE) {
|
|
let randomLevel = generateChestLevel()
|
|
let chest = generateNewChest(uid, activity, randomLevel, ChestStatusEnum.NORMAL)
|
|
chests.push(chest)
|
|
} else {
|
|
score += Math.floor(Math.random() * (STEP_SCORE_MAX - STEP_SCORE_MIN + 1) + STEP_SCORE_MIN)
|
|
}
|
|
}
|
|
return { score, chests }
|
|
}
|
|
|
|
export const generateChestBonus = async (chest: DocumentType<ActivityChestClass>) => {
|
|
let rewards = []
|
|
// 如果宝箱的助力用户数量小于最大助力次数, 则不生成奖励
|
|
if (chest.bonusUsers.length < chest.maxBounsCount) {
|
|
return []
|
|
}
|
|
let bonusItem
|
|
for (let i = 0; i < chestBonusItems.length; i++) {
|
|
let item = chestBonusItems[i]
|
|
if (Math.random() < item.probability / 100) {
|
|
bonusItem = item
|
|
break
|
|
}
|
|
}
|
|
if (!bonusItem) {
|
|
return []
|
|
}
|
|
const redisKey = `chest_bonus_${bonusItem.type}`
|
|
const itemId = await new ZRedisClient().spop(redisKey)
|
|
if (!itemId) {
|
|
return []
|
|
}
|
|
rewards.push({
|
|
id: itemId,
|
|
type: bonusItem.type,
|
|
name: bonusItem.name,
|
|
desc: bonusItem.desc,
|
|
amount: 1,
|
|
})
|
|
}
|