diff --git a/game-server/app/services/warRewardService.ts b/game-server/app/services/warRewardService.ts index ce5b2722f..992e6ab65 100644 --- a/game-server/app/services/warRewardService.ts +++ b/game-server/app/services/warRewardService.ts @@ -4,22 +4,25 @@ */ import { BattleDropModel } from '../db/BattleDrop'; -import { getWarById } from '../pubUtils/gamedata'; -import { decodeStr } from '../pubUtils/util'; -import { BATTLE_REWARD_TYPE } from '../consts/consts'; +import { getWarById, getBluePrtByQuality } from '../pubUtils/gamedata'; +import { decodeStr, getRefTime, getRandomWithWeight, getRandomByLen } from '../pubUtils/util'; +import { BATTLE_REWARD_TYPE, BLUEPRT_CONST, BLUEPRT_CHANCE } from '../consts/consts'; import { handleReward } from './rewardService'; +import { BattleBlueprtDropModel } from '../db/BattleBlueprtDrop' +import { RoleModel } from '../db/Role'; export class WarReward { - roleId: string; - roleName: string; - battleId: number; - condition: Map; - warInfo: any; - isSuccess: boolean; - rewards: Array<{type: number, gid: number, count: number, times: number}>; - fixReward: string; - conditionReward: string; - randomReward: string; + private roleId: string; + private roleName: string; + private battleId: number; + private condition: Map; + private warInfo: any; + private isSuccess: boolean; + private rewards: Array<{type: number, gid: number, count: number, times: number}>; + private fixReward: string; + private conditionReward: string; + private randomReward: string; + private costAp: number; constructor(roleId: string, roleName: string, battleId: number, isSuccess: boolean) { this.roleId = roleId; @@ -34,6 +37,7 @@ export class WarReward { this.randomReward = RandomReward; this.isSuccess = isSuccess; + this.costAp = this.warInfo.cost; } public setCondition(id: number, isOk: boolean) { @@ -101,7 +105,7 @@ export class WarReward { let { getNum = 0, allNum = 0, getSum = 0, allSum = 0 } = dropHistory; for(let i = 0; i < num; i ++) { let flag = false; // 是否可以获得 - if(allNum + 1 > frequency) { + if(allNum >= frequency) { allNum = 0; getNum = 0; } allNum++; allSum++; @@ -122,6 +126,58 @@ export class WarReward { } } + private async handlerBlueprtReward(num: number) { + + const refTime = getRefTime(new Date(), BLUEPRT_CONST.REFRESH_TIME); + const battleBlueprtDrop = await BattleBlueprtDropModel.findByTime(this.roleId, refTime); + if(battleBlueprtDrop) { + let { getNum = 0, curCostAp = 0, getSum = 0, costAp = 0 } = battleBlueprtDrop; + for(let i = 0; i < num; i ++) { + let flag = false; // 是否可以获得 + if( curCostAp >= BLUEPRT_CONST.PER_AP) { + curCostAp = curCostAp - BLUEPRT_CONST.PER_AP; getNum = 0; + } + costAp += this.costAp; curCostAp += this.costAp; + if(getNum == 0) { + let r = Math.random(); + console.log(r, 1/BLUEPRT_CONST.PER_AP*curCostAp); + if(r <= 1/BLUEPRT_CONST.PER_AP*curCostAp || (curCostAp >= BLUEPRT_CONST.PER_AP) ) { + flag = true; // 独立概率随机 + } + } + if(getSum >= BLUEPRT_CONST.DAILY_CNT) { + flag = false; + } + if(flag) { + getNum ++; getSum++; + const obj = await this.randomBlueprt(); + if(obj) { + this.rewards.push({type: BATTLE_REWARD_TYPE.RANDOM_REWARD, times: i +1, ...obj}); + } + } + } + await BattleBlueprtDropModel.updateByTime(this.roleId, refTime, { + getNum, curCostAp, getSum, costAp + }); + } + } + + private async randomBlueprt() { + const { lv } = await RoleModel.findByRoleId(this.roleId); + const result = BLUEPRT_CHANCE.find(cur => {return cur.min <= lv && cur.max >= lv}); + console.log('****', lv, result) + if(result) { + const dicOdds = decodeStr('possibility', result.chance); + const {dic: {id}} = getRandomWithWeight(dicOdds); + + const blueprtList = getBluePrtByQuality(id); + const gid = getRandomByLen(blueprtList); + return {gid, count:1} + } else { + return null + } + } + public async saveReward(num: number) { this.rewards = new Array(); // let warType = this.warInfo.warType; @@ -131,6 +187,8 @@ export class WarReward { if(this.fixReward) this.handleFixReward(num, this.fixReward); if(this.conditionReward) this.handleConditionReward(num, this.conditionReward); if(this.randomReward) await this.handleRandomReward(num, this.randomReward); + + if(this.costAp > 0) await this.handlerBlueprtReward(num); } let returnGoods = await handleReward(this.roleId, this.roleName, this.rewards); diff --git a/shared/consts/consts.ts b/shared/consts/consts.ts index d5d5f100a..c37da6d52 100644 --- a/shared/consts/consts.ts +++ b/shared/consts/consts.ts @@ -64,7 +64,13 @@ const itid_array = [ { id: 23, name: '消耗类物品(材料类)', goodType: GOOD_TYPE.CONSUMES, field: "consumeGoods" }, { id: 24, name: '消耗类物品(宝箱类)', goodType: GOOD_TYPE.CONSUMES, field: "consumeGoods" }, { id: 26, name: '武将碎片', goodType: GOOD_TYPE.CONSUMES, field: "souls" }, - { id: 27, name: '货币', goodType: GOOD_TYPE.CONSUMES, field: "currency", isCurrency: true } + { id: 27, name: '货币', goodType: GOOD_TYPE.CONSUMES, field: "currency", isCurrency: true }, + { id: 28, name: '藏宝图', goodType: GOOD_TYPE.CONSUMES, field: "consumeGoods" }, + { id: 29, name: '礼器', goodType: GOOD_TYPE.EQUIP }, + { id: 30, name: '宝甲', goodType: GOOD_TYPE.EQUIP }, + { id: 31, name: '名驹', goodType: GOOD_TYPE.EQUIP }, + { id: 32, name: '典籍', goodType: GOOD_TYPE.EQUIP }, + { id: 33, name: '神兵', goodType: GOOD_TYPE.EQUIP } ]; export const ITID = new Map(); @@ -104,6 +110,21 @@ export const WAR_TYPE = { MYSTERY_ELITE: 13 // 秘境精英 }; +// 藏宝图掉落参数 +export const BLUEPRT_CONST = { + REFRESH_TIME: 5, // 每天几点刷新 + DAILY_CNT: 5, // 每天最多掉落多少张 + PER_AP: 15 // 每多少体力掉落1张藏宝图 +}; + +// 藏宝图概率 +export const BLUEPRT_CHANCE = [ + {min: 20, max: 40, chance: "1&100"}, + {min: 41, max: 60, chance: "1&70|2&30"}, + {min: 61, max: 80, chance: "2&70|3&30"}, + {min: 81, max: 100, chance: "2&60|3&40"} +]; + // 事件,是否开启保存随机记录方式 export const EVENT_RANDOM_TYPE_ONE_OPEN = false; // 奇遇事件每次刷新几个 diff --git a/shared/db/BattleBlueprtDrop.ts b/shared/db/BattleBlueprtDrop.ts new file mode 100644 index 000000000..0ecac679b --- /dev/null +++ b/shared/db/BattleBlueprtDrop.ts @@ -0,0 +1,42 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop } from '@typegoose/typegoose'; + +/** + * 藏宝图掉落记录 + */ +@index({ roleId: 1, battleId: 1 }) + +export default class BattleBlueprtDrop extends BaseModel { + @prop({ required: true }) + roleId: string; // 角色 id + @prop({ required: true }) + refTime: Date; // 刷新日期 + + @prop({ required: true, default: 0 }) + getNum: number; // 获取次数,当curCostAp超过ap重置 + @prop({ required: true }) + curCostAp: number; // 这一轮消耗的ap,单独设置出了以防以后修改PER_AP + @prop({ required: true, default: 0 }) + getSum: number; // 获取道具的全部次数 + @prop({ required: true }) + costAp: number; // 当天消耗的ap + + public static async findByTime(roleId: string, refTime: Date, lean = true) { + const result = await BattleBlueprtDropModel.findOneAndUpdate({ roleId, refTime }, {}, {new: true, upsert: true}).lean(lean); + return result; + } + + public static async updateByTime(roleId: string, refTime: Date, params: {getNum: number, curCostAp: number, getSum: number, costAp: number}, lean = true) { + const result = await BattleBlueprtDropModel.findOneAndUpdate({roleId, refTime}, {$set: params}).lean(lean); + return result; + } + + public static async deleteAccount(roleId: string, lean = true) { + let result = await BattleBlueprtDropModel.deleteMany({roleId}).lean(lean); + return result||{}; + } + + +} + +export const BattleBlueprtDropModel = getModelForClass(BattleBlueprtDrop); diff --git a/shared/pubUtils/gamedata.ts b/shared/pubUtils/gamedata.ts index fd030c1f8..3cd12a6a0 100644 --- a/shared/pubUtils/gamedata.ts +++ b/shared/pubUtils/gamedata.ts @@ -2,6 +2,7 @@ import fs = require('fs'); import path = require('path'); import { ABI_TYPE } from '../consts/abilityConst'; import { decodeIdCntArrayStr } from './util'; +import { IT_TYPE } from '../consts/consts'; let gamedata = {}; const wars = ['dic_zyz_gk_main', 'dic_zyz_gk_mainElite', 'dic_zyz_gk_daily', 'dic_zyz_gk_event', 'dic_zyz_gk_tower', 'dic_zyz_gk_expedition', 'dic_zyz_gk_dungeon','dic_zyz_gk_dungeonElite']; // 关卡相关的表 @@ -19,6 +20,8 @@ const expeditionInfo = new Map (); const comBtlInfo = new Map(); const btlBossHp = new Map(); const blueprtBossHp = new Map(); +const goodInfo = new Map(); +const blueprt = new Map>(); function parseWarData() { let result = null; @@ -190,6 +193,21 @@ function parseComBtlData() { }); } +function parseGood() { + const file = 'dic_goods'; + const data = gamedata['jsons'][file] || []; + data.forEach(elem => { + if (elem && elem.good_id) { + goodInfo.set(elem.good_id, elem); + if(elem.itid == IT_TYPE.BLUEPRT) { + let arr = blueprt.get(elem.quality)||new Array(); + arr.push(elem.good_id); + blueprt.set(elem.quality, arr); + } + } + }); +} + function initData (folder: string) { if(!gamedata.hasOwnProperty(folder)) { gamedata[folder] = {}; @@ -227,6 +245,7 @@ function parseData() { parseOlySeidList(); parseExpedition(); parseComBtlData(); + parseGood(); } initData('jsons'); // 加载一般json @@ -258,10 +277,11 @@ export function getTowerDataByLv(lv: number) { } export function getGoodById(gid) { - let goodsInfo = gamedata['jsons']['dic_goods']||[]; - return goodsInfo.find(cur => { - return cur.good_id == gid - }); + return goodInfo.get(gid); +} + +export function getBluePrtByQuality(quality: number) { + return blueprt.get(quality)|| new Array(); } export function getTaskById(tid: number) { diff --git a/shared/pubUtils/util.ts b/shared/pubUtils/util.ts index 2380e9a67..a6dcbf968 100644 --- a/shared/pubUtils/util.ts +++ b/shared/pubUtils/util.ts @@ -98,6 +98,12 @@ const moment = require('moment'); result = { type: parseInt(type), param: parseInt(param), cnt: parseInt(cnt) }; break; } + case 'possibility': { + let [id, weight] = arr; + if(isNaN(id) || isNaN(weight)) throw new Error('data table format wrong'); + result = { id: parseInt(id), weight: parseInt(weight) }; + break; + } } return result; }; @@ -163,6 +169,10 @@ export function decodeIdCntArrayStr(str: string, multi: number) { return ce; } + export function getRandomByLen(arr: Array) { + let len = arr.length; + return arr[Math.floor(Math.random() * len)] + } export function getRandomWithWeight(randomList: any) { let len = randomList.reduce((pre, cur) => {