Files
ZYZ/game-server/app/services/gvg/gvgPrepareService.ts
2023-03-27 19:37:39 +08:00

523 lines
23 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 { BackendSession } from "pinus";
import { GVG_ACTIVE_TYPE, GVG_ITEM, GVG_PERIOD, GVG_RESOURCE_TYPE, GVG_SEED_TYPE, GVG_TECH_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB, MAIL_TYPE, PUSH_ROUTE } from "../../consts";
import { GVGLeagueFarmModel, GVGLeagueFarmType } from "../../db/GVGLeagueFarm";
import { GVGLeaguePrepareModel, Tech } from "../../db/GVGLeaguePrepare";
import { GVGUserDailyDataModel, GVGUserDailyDataType } from "../../db/GVGUserDailyData";
import { Contribute, GVGUserDataModel } from "../../db/GVGUserData";
import { GVGUserItemModel } from "../../db/GVGUserItem";
import { LeagueField } from "../../domain/gvgField/returnData";
import { calLeagueLv, gameData, getFieldMaxAddType } from "../../pubUtils/data";
import { GVG } from "../../pubUtils/dicParam";
import { RewardInter } from "../../pubUtils/interface";
import { nowSeconds } from "../../pubUtils/timeUtil";
import { arrToMap, getArrayOfNumber, getRandEelm, decodeIdCntArrayStr, parseGoodStr, sortArrRandom } from "../../pubUtils/util";
import { addGVGReward, combinePushItem, getProduceCoinCnt } from "./gvgItemService";
import { getGVGConfig, getGVGPeriodData } from "./gvgService";
import { GVGVestigeRankModel } from "../../db/GVGVestigeRank";
import { addLeagueLvUpMessage, addResourceRecord } from "./gvgRecService";
import { GVGLeagueType } from "../../db/GVGLeague";
import { sendMessageToGuildWithSuc, sendMessageToUserWithSuc } from "../pushService";
import { sendMailByContent } from "../mailService";
export function checkPreTech(techId: number, activeQueue: number[], techQueue: Tech[]) {
const dicTech = gameData.gvgTech.get(techId);
if(!dicTech) return false;
let hasOrUnlock = dicTech.prepositionId.length == 0;
for(let andArr of dicTech.prepositionId) { // or关系
let hasAndUnlock = true;
for(let id of andArr) { // and关系
if(!checkTechIsIng(id, activeQueue, techQueue)) {
hasAndUnlock = false; break;
}
}
if(hasAndUnlock) hasOrUnlock = true;
}
return hasOrUnlock;
}
export function checkTechIsIng(techId: number, activeQueue: number[], techQueue: Tech[]) {
return techQueue.findIndex(cur => cur.id == techId) != -1 || activeQueue.indexOf(techId) != -1
}
export function checkTechProgress(techId: number, techQueue: Tech[]) {
let queue = techQueue.find(cur => cur.id == techId);
return queue && queue.progress == 0;
}
export function calProducerContribute(obj: { food: number, mineral: number, wood: number }) {
let { food = 0, mineral = 0, wood = 0 } = obj||{};
let ratio = decodeIdCntArrayStr(GVG.GVG_PRODUCE_GAME_RATIO, 1);
let foodRatio = ratio.get(GVG_RESOURCE_TYPE.FOOD.toString())||0;
let mineralRatio = ratio.get(GVG_RESOURCE_TYPE.MINERAL.toString())||0;
let woodRatio = ratio.get(GVG_RESOURCE_TYPE.WOOD.toString())||0;
return food * foodRatio + mineral * mineralRatio + wood * woodRatio;
}
export function calFighterContribute(obj: { score: number }) {
return obj?.score||0
}
export async function getGVGActive(leagueCode: string, roleId: string) {
let { configId } = getGVGConfig();
let result = await GVGUserDataModel.findByRole(configId, leagueCode, roleId);
return result?.active||0;
}
/**
* 获得活跃
* @param leagueCode
* @param roleId
* @param type GVG_ACTIVE_TYPE
* @returns
*/
export async function addGVGActive(leagueCode: string, roleId: string, type: GVG_ACTIVE_TYPE, count = 1) {
let { configId } = getGVGConfig();
let add = gameData.gvgActive.get(type)||0;
let result = await GVGUserDataModel.addActive(configId, leagueCode, roleId, type, add * count);
return result.active;
}
export async function addGVGTechActive(leagueCode: string, roleId: string) {
let { configId } = getGVGConfig();
let check = await GVGUserDailyDataModel.checkGetTechActive(configId, leagueCode, roleId);
if(!check) {
await GVGUserDailyDataModel.setHasGetTechActive(configId, leagueCode, roleId);
return await addGVGActive(leagueCode, roleId, GVG_ACTIVE_TYPE.TECH_ACTIVATE);
} else {
const data = await GVGUserDataModel.findByRole(configId, leagueCode, roleId);
return data?.active||0;
}
}
export function getGVGDailyItems(job: number) {
return job == LEAGUE_JOB.PRODUCER? parseGoodStr(GVG.GVG_PRODUCER_GET): parseGoodStr(GVG.GVG_FIGHTER_GET);
}
export async function getDailyLoginReward(roleId: string, roleName: string, sid: string, leagueCode: string, job: number) {
let { configId } = getGVGConfig();
if(job && !await GVGUserDailyDataModel.checkHasLogin(configId, leagueCode, roleId)) {
// 发每日奖励
await GVGUserDailyDataModel.setHasLogin(configId, leagueCode, roleId);
let leagueGoods = await addGVGReward(roleId, roleName, leagueCode, sid, getGVGDailyItems(job), [], ITEM_CHANGE_REASON.GVG_LOGIN)
let active = await addGVGActive(leagueCode, roleId, GVG_ACTIVE_TYPE.LOGIN);
return { active, reward: leagueGoods }
}
return null;
}
export function getMyContribute(userDailyData: GVGUserDailyDataType, myRanks: {vestigeId: number, rank: number}[]) {
let score = 0;
for(let { vestigeId, rank} of myRanks) {
let dicRankMap = gameData.gvgVestige.get(vestigeId);
if(dicRankMap && dicRankMap.get(rank)) score += dicRankMap.get(rank).score;
}
return {
food: userDailyData?.food||0,
mineral: userDailyData?.mineral||0,
wood: userDailyData?.wood||0,
score: userDailyData?.score||score,
}
}
/**
* 资源
* @param leagueCode 联军id
* @param roleId 玩家id
* @param resourceType 资源类型
* @param count 数量
* @returns
*/
export async function addResource(myLeague: GVGLeagueType, roleId: string, roleName: string, resourceType: GVG_RESOURCE_TYPE, count: number, sid?: string) {
let { leagueCode } = myLeague;
let { configId } = getGVGConfig();
let resources = getResourceCnt(resourceType, count);
if(!resources) return false;
let { food = 0, mineral = 0, wood = 0 } = resources;
// 联军更新资源
let league = await GVGLeaguePrepareModel.addResource(configId, leagueCode, food, mineral, wood);
let originLv = league?.lv||1;
// 联军升级
let lv = calLeagueLv(league.resources);
league = await GVGLeaguePrepareModel.setLv(configId, leagueCode, lv);
// 个人贡献更新
await GVGUserDataModel.addResource(configId, leagueCode, roleId, food, mineral, wood);
// 个人今日贡献更新
let userDailyData = await GVGUserDailyDataModel.addResource(configId, leagueCode, roleId, food, mineral, wood);
let myRanks = await GVGVestigeRankModel.findAllByRole(roleId);
let myContribute = getMyContribute(userDailyData, myRanks);
addResourceRecord(roleId, roleName, leagueCode, resourceType, count);
if(lv > originLv) addLeagueLvUpMessage(roleId, roleName, myLeague, lv);
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.GVG_CONTRIBUTE_UPDATE, { leagueCode, job: LEAGUE_JOB.PRODUCER, sumContribute: calProducerContribute(myContribute) }, sid);
return { resources: league.resources, leagueLv: league.lv, myContribute }
}
function getResourceCnt(resourceType: GVG_RESOURCE_TYPE, count: number) {
switch(resourceType) {
case GVG_RESOURCE_TYPE.FOOD:
return { food: count };
case GVG_RESOURCE_TYPE.MINERAL:
return { mineral: count };
case GVG_RESOURCE_TYPE.WOOD:
return { wood: count };
}
}
export async function lockField(leagueCode: string, roleId: string, roleName: string, farmId: number) {
let { configId } = getGVGConfig();
const produceCoinCnt = await getLockFieldCnt(leagueCode, roleId);
if(produceCoinCnt == 0) return
const leagueFarms = await GVGLeagueFarmModel.findByFarmId(configId, leagueCode, farmId);
// 查询现有的田
let { maxAddTypeCntMap, myAddTypeCntMap, allLands, myFieldCnt, myLockField } = calCntFromFarms(farmId, leagueFarms, roleId);
if(allLands.length <= 0 || myLockField >= produceCoinCnt) return; // 田数量不足 or 已经锁过了
let allAdd = calAddType(myFieldCnt, myAddTypeCntMap, maxAddTypeCntMap, produceCoinCnt - myLockField);
let randLand = sortArrRandom(getRandEelm(allLands, produceCoinCnt - myLockField));
let lands: { fieldId: number, addType: number }[] = [];
for(let fieldId of randLand) {
let addType = 0;
for(let [_addType, addCnt] of allAdd) {
if(addCnt > 0) {
addType = _addType;
allAdd.set(_addType, addCnt - 1);
break;
}
}
lands.push({ fieldId, addType });
}
// 更新田
let dicFarm = gameData.gvgResource.get(farmId);
await GVGLeagueFarmModel.lockFields(configId, leagueCode, farmId, dicFarm?.type, roleId, roleName, lands);
return await GVGLeagueFarmModel.findByFarmIdAndRoleId(configId, leagueCode, farmId, roleId);
}
function calCntFromFarms(farmId: number, leagueFarms: GVGLeagueFarmType[], roleId: string) {
let maxAddTypeCntMap = getFieldMaxAddType(farmId); // 加成田最大数量
let myAddTypeCntMap = new Map<number, number>();
let myFieldCnt = 0; // 玩家已有的田有
let myLockField = 0; // 玩家没有种只是锁住了的田
let allLands = getArrayOfNumber(gameData.gvgResource.get(farmId)?.sum??0); // 全部可使用的田
for(let leagueFarm of leagueFarms) {
// 加成
for(let {addType, roleId: addTypeRoleId} of leagueFarm.addTypes) {
maxAddTypeCntMap.set(addType, maxAddTypeCntMap.get(addType) - 1);
if(addTypeRoleId == roleId) {
if(!myAddTypeCntMap.has(addType)) myAddTypeCntMap.set(addType, 0);
myAddTypeCntMap.set(addType, myAddTypeCntMap.get(addType) + 1);
}
}
if(leagueFarm.unlockTime >= nowSeconds()) {
let index = allLands.indexOf(leagueFarm.fieldId);
if(index > -1) allLands.splice(index, 1);
}
// 玩家数据
if(leagueFarm.lockRoleId == roleId && leagueFarm.unlockTime >= nowSeconds()) {
myFieldCnt++;
if(leagueFarm.harvestTime == 0) myLockField++;
}
}
return { maxAddTypeCntMap, myAddTypeCntMap, allLands, myFieldCnt, myLockField }
}
/**
*
* @param myFieldCnt 我已种的田的数量
* @param myAddTypeCntMap 我已种的田的有加成的田分别的数量
* @param maxAddTypeCntMap 联军最多可以加成田的数量
* @param count 本次可以加的田
* @returns
*/
function calAddType(myFieldCnt: number, myAddTypeCntMap: Map<number, number>, maxAddTypeCntMap: Map<number, number>, count: number) {
let result = new Map<number, number>(); // 加成类型, 数量
for(let [addType, ratio] of gameData.gvgPlayerFieldAddType) { // 按比例加田
let maxCnt = maxAddTypeCntMap.get(addType)||0;
if(maxCnt == 0) continue;
let needCnt = Math.floor((myFieldCnt + count) * ratio / 100); // 玩家按照比例需要的加成数量
let cnt = myAddTypeCntMap.get(addType)||0; // 我当前的加成数量
let resultCnt = needCnt - cnt;
if(resultCnt < 0) resultCnt = 0;
if(resultCnt > maxCnt) resultCnt = maxCnt;
maxAddTypeCntMap.set(addType, maxAddTypeCntMap.get(addType) - resultCnt);
result.set(addType, resultCnt);
}
return result;
}
export async function rollbackUnPlantFields(cost: RewardInter[], plantResult: GVGLeagueFarmType[], session: BackendSession, leagueCode: string) {
const roleId = session.get('roleId');
const roleName = session.get('roleName');
const sid = session.get('sid');
let add: RewardInter[] = [];
for(let { id, count } of cost) add.push({ id, count });
for(let { seedType } of plantResult) {
let index = add.findIndex(({ id }) => id == getItemIdOfSeedType(seedType));
if(index != -1) {
add[index].count --;
if(add[index].count <= 0) add.splice(index);
}
}
await addGVGReward(roleId, roleName, leagueCode, sid, add, [], ITEM_CHANGE_REASON.PLANT_ROLLBACK);
return add;
}
// 计算实际消耗的种子
export function getCostSeedReward(plantResult: GVGLeagueFarmType[]) {
const reward: RewardInter[] = [], leagueReward: RewardInter[] = [];
for(let { seedType } of plantResult) {
let itemId = getItemIdOfSeedType(seedType);
let dicGVGItem = gameData.gvgItem.get(itemId);
if(!dicGVGItem) continue;
for(let { id, count } of dicGVGItem.reward) {
let rewardObj = reward.find(({ id }) => id == id);
rewardObj? rewardObj.count += count: reward.push({ id, count: count });
}
for(let { id, count } of dicGVGItem.leagueReward) {
let rewardObj = leagueReward.find(({ id }) => id == id);
rewardObj? rewardObj.count += count: leagueReward.push({ id, count: count });
}
}
return { reward, leagueReward }
}
export function getItemIdOfSeedType(seedType: GVG_SEED_TYPE) {
switch(seedType) {
case GVG_SEED_TYPE.WHEAT:
return GVG_ITEM.WHEAT;
case GVG_SEED_TYPE.CORN:
return GVG_ITEM.CORN;
case GVG_SEED_TYPE.RICE:
return GVG_ITEM.RICE;
}
}
export async function getLockFieldCnt(leagueCode: string, roleId: string) {
let { configId } = getGVGConfig();
const itemIds = [GVG_ITEM.WHEAT, GVG_ITEM.CORN, GVG_ITEM.RICE, GVG_ITEM.PRODUCE_COIN];
const items = await GVGUserItemModel.findByRoleAndIds(configId, leagueCode, roleId, itemIds);
return items.reduce((pre, cur) => pre + cur.count, 0);
}
export async function getmyContributeRank(leagueCode: string, members: { roleId: string }[], targetRoleId: string) {
let { configId } = getGVGConfig();
const roleIds = members.map(member => member.roleId);
const userDatas = await GVGUserDataModel.findByRoles(configId, leagueCode, roleIds);
userDatas.sort((a, b) => calProducerContribute(b.contribute) - calProducerContribute(a.contribute));
for(let i = 0; i < userDatas.length; i++) {
if(userDatas[i].roleId == targetRoleId) return i + 1;
}
return 0
}
export async function getLeagueFarmShowResult(leagueCode: string, roleId: string, farmId: number, needField = true) {
let { configId } = getGVGConfig();
const leagueFarms = await GVGLeagueFarmModel.findLockedByFarmId(configId, leagueCode, farmId);
if(!needField) return { farmId, count: leagueFarms.length };
const fields = await GVGLeagueFarmModel.findByFarmIdAndRoleId(configId, leagueCode, farmId, roleId);
const result = fields.map(field => new LeagueField(field));
return { farmId, count: leagueFarms.length, fields: result }
}
export function calFarmOutput(seedType: number, addType: number, farmId: number, cities: number[], activeTech: number[]) {
// 种子基础产量 * ( 1 + 格子加成 + 农庄等级加成 + 战力城池加成 + 科技树加成)
let itemId = getItemIdOfSeedType(seedType);
let dicGVGItem = gameData.gvgItem.get(itemId);
let dicFarm = gameData.gvgResource.get(farmId);
if(!dicGVGItem || !dicFarm) return { output: 0, outputStr: '' };
let { value: foodBase } = dicGVGItem;
let addRatio = seedType == addType? GVG.GVG_SP_FIELD_ADD * 100: 0;
let farmRatio = dicFarm.fieldAdd;
let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.FOOD);
let techRatio = getTechAdd(activeTech);
return {
output: Math.floor(foodBase * (1 + (addRatio + farmRatio + cityRatio + techRatio)/100)),
outputStr: `Math.floor(${foodBase} * (1 + (${addRatio} + ${farmRatio} + ${cityRatio} + ${techRatio})/100))`
}
}
export async function sumOutputByResults(myResultFields: GVGLeagueFarmType[]) {
let [foodSum = 0, rewardSum = new Array<RewardInter>(), leagueRewardSum = new Array<RewardInter>(), activeSum = 0] = [];
for(let { seedType, output: food } of myResultFields) {
let itemId = getItemIdOfSeedType(seedType);
let dicGVGItem = gameData.gvgItem.get(itemId);
if(!dicGVGItem) continue;
let { reward, leagueReward, leagueConsume } = dicGVGItem;
let produceCoin = getProduceCoinCnt(leagueConsume);
foodSum += food;
for(let { id, count } of reward) {
let rewardObj = rewardSum.find(cur => cur.id == id);
rewardObj? rewardObj.count += count: rewardSum.push({id, count});
}
for(let { id, count } of leagueReward) {
let rewardObj = leagueRewardSum.find(cur => cur.id == id);
rewardObj? rewardObj.count += count: leagueRewardSum.push({id, count});
}
activeSum += produceCoin;
}
return { foodSum, rewardSum, leagueRewardSum, activeSum }
}
/**
* 计算挖矿产量
* @param farmId 矿场id
* @param result 小游戏的结果
* @param cities 上周占领城镇加成
* @param activeTech 千机阁加成
* @returns
*/
export function calMineOutput(farmId: number, result: [{ type: number, count: number }], cities: number[], activeTech: number[]) {
// 1001.5【中铁块单块产量倍数】4【可挖出的中铁块数量】1002【大铁块单块产量倍数】8【可挖出的大铁块数量】1003【特大铁块单块产量倍数】8【可挖出的特大铁块数量】10.5【大城产量加成】)
let dicMine = gameData.gvgResource.get(farmId);
if(!dicMine) return { output: 0, outputStr: '' };
let output = 0, addStrArr = [];
for(let { type, count } of result) {
let dic = dicMine.mineralValue.find(cur => cur.type == type);
if(!dic) continue;
output += dic.output * count;
addStrArr.push(`${dic.output}*${count}`);
}
let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.MINERAL);
let techRatio = getTechAdd(activeTech);
return {
output: Math.floor(output * (1 + (cityRatio + techRatio)/100)),
outputStr: `Math.floor(${addStrArr.join('+')} * (1 + (${cityRatio} + ${techRatio})/100))`
}
}
/**
* 计算木块产量
* @param farmId 林场id
* @param result 小游戏的结果
* @param cities 上周占领城镇加成
* @param activeTech 千机阁加成
* @returns
*/
export function calForestryOutput(farmId: number, result: number, cities: number[], activeTech: number[]) {
// 1002【10级木堆木块单块基础产量倍数】10.3【城镇加成倍数】40【使用铁斧头最大分块】
let dicForestry = gameData.gvgResource.get(farmId);
if(!dicForestry) return { output: 0, outputStr: '' };
let base = dicForestry.woodOutput;
let cityRatio = getCitiesAdd(cities, GVG_RESOURCE_TYPE.WOOD);
let techRatio = getTechAdd(activeTech);
return {
output: Math.floor( base * (1 + (cityRatio + techRatio)/100) * result),
outputStr: `Math.floor(${base} * (1 + (${cityRatio} + ${techRatio})/100) * ${result})`
}
}
// 城池加成,多城池取最大加成
function getCitiesAdd(cities: number[], resourceType: number) {
let outputAdd: number = 0;
for(let cityId of cities) {
let dicCity = gameData.gvgCity.get(cityId);
let dicCityAdd = gameData.gvgCityAdd.get(dicCity?.cityType);
if(!dicCity || !dicCityAdd) continue;
if(resourceType == GVG_RESOURCE_TYPE.FOOD) {
outputAdd += dicCityAdd.foodAdd;
} else if (resourceType == GVG_RESOURCE_TYPE.MINERAL) {
outputAdd += dicCityAdd.mineralAdd;
} else if (resourceType == GVG_RESOURCE_TYPE.WOOD) {
outputAdd += dicCityAdd.woodAdd;
}
}
return outputAdd;
}
// 科技树加成 叠加
function getTechAdd(activeTech: number[]) {
let ratio = 0;
for(let techId of activeTech) {
let dicTech = gameData.gvgTech.get(techId);
if(dicTech.type == GVG_TECH_TYPE.PRODUCE_UP) {
ratio += dicTech.param[0];
}
}
return ratio;
}
// 锁定
export async function lockMineOrForestry(leagueCode: string, roleId: string, farmId: number, itemId: number) {
let { configId } = getGVGConfig();
const leagueFarms = await GVGLeagueFarmModel.findByFarmId(configId, leagueCode, farmId);
// 查询现有的田
let fieldId = getUnlockedMine(farmId, leagueFarms);
if(!fieldId) return null;
let dicFarm = gameData.gvgResource.get(farmId);
let lockResult = await GVGLeagueFarmModel.lockMineOrForestry(configId, leagueCode, farmId, dicFarm?.type, roleId, fieldId, itemId);
if(!lockResult) return null;
return lockResult;
}
function getUnlockedMine(farmId: number, leagueFarms: GVGLeagueFarmType[]) {
let leagueFarmMap = arrToMap(leagueFarms, obj => obj.fieldId);
let fieldId = 0;
let sum = gameData.gvgResource.get(farmId)?.sum??0;
for(let i = 1; i <= sum; i++) {
if(!leagueFarmMap.has(i)) {
fieldId = i; break;
}
}
return fieldId;
}
export function checkItemCanUserInFarm(farmId: number, itemId: number) {
let dicFarm = gameData.gvgResource.get(farmId);
let dicGVGItem = gameData.gvgItem.get(itemId);
if(!dicFarm || !dicGVGItem) return false;
return dicFarm.type == dicGVGItem.type;
}
// 推送千机阁状态变化
export async function pushTechChange(guildCodes: string[], isActive: boolean, id: number, progress: number, maxProgress: number) {
for(let guildCode of guildCodes) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.LEAGUE_TECH_CHANGE, {
id, progress, maxProgress, isActive
});
}
}
export async function pushTechRollback(guildCodes: string[], ids: number[]) {
for(let guildCode of guildCodes) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.LEAGUE_TECH_ROLLBACK, {
ids
});
}
}
export async function sendUngotBoxReward(configId: number, leagueCode: string, roleId: string, sid: string) {
let { period } = getGVGPeriodData();
if(period != GVG_PERIOD.BATTLE) return;
let userData = await GVGUserDataModel.checkBox(configId, leagueCode, roleId);
if(userData) {
let { contribute = new Contribute(), box = [] } = userData||{};
let rewards: RewardInter[] = [], leagueReward: RewardInter[] = [], receiveBoxes: number[] = [];
for(let [boxId, dicBox] of gameData.gvgContributeBox) {
if(box.indexOf(boxId) != -1) continue;
let sumContribute = dicBox.job == LEAGUE_JOB.PRODUCER? calProducerContribute(contribute): calFighterContribute(contribute);
if(sumContribute < dicBox.boxPoint) continue;
receiveBoxes.push(boxId);
combinePushItem(rewards, dicBox.boxReward);
combinePushItem(leagueReward, dicBox.boxLeagueReward);
}
if(leagueReward.length > 0) await addGVGReward(roleId, '', leagueCode, sid, leagueReward, [], ITEM_CHANGE_REASON.GVG_VESTIGE_RECEIVE_RANK);
if(rewards.length > 0) await sendMailByContent(MAIL_TYPE.GVG_BOX_REWARD, roleId, { params: [`${configId}`, receiveBoxes.join()], goods: rewards });
await GVGUserDataModel.receiveBoxes(configId, leagueCode, roleId, receiveBoxes);
}
}