import { BackendSession } from "pinus"; import { GVG_ACTIVE_TYPE, GVG_ITEM, GVG_RESOURCE_TYPE, GVG_SEED_TYPE, GVG_TECH_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB } from "../../consts"; import { GVGLeagueFarmModel, GVGLeagueFarmType } from "../../db/GVGLeagueFarm"; import { GVGLeaguePrepareModel, GVGLeaguePrepareType, Tech } from "../../db/GVGLeaguePrepare"; import { GVGUserDailyDataModel, GVGUserDailyDataType } from "../../db/GVGUserDailyData"; import { GVGUserDataModel, GVGUserDataType } 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 { getArrayOfNumber, getRandEelm, getRandValueByMinMax, parseGoodStr, sortArrRandom } from "../../pubUtils/util"; import { addGVGReward } from "./gvgItemService"; import { getGVGConfig } from "./gvgService"; import { getProduceCoinCnt } from "./gvgItemService"; import { GVGLeagueType } from "../../db/GVGLeague"; import { getCities } from "../guildActivity/guildActivityService"; import { getGVGCities } from "./gvgBattleService"; 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 calProduce(obj: { food: number, mineral: number, wood: number }) { let { food = 0, mineral = 0, wood = 0 } = obj||{}; return food + mineral + wood; } /** * 获得活跃 * @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 getMyDistribute(userDailyData: GVGUserDailyDataType) { return { food: userDailyData?.food||0, mineral: userDailyData?.mineral||0, wood: userDailyData?.wood||0, score: userDailyData?.score||0, } } /** * 资源 * @param leagueCode 联军id * @param roleId 玩家id * @param resourceType 资源类型 * @param count 数量 * @returns */ export async function addResource(leagueCode: string, roleId: string, resourceType: GVG_RESOURCE_TYPE, count: number) { 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 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 myDistribute = getMyDistribute(userDailyData); return { resource: league.resources, lv: league.lv, myDistribute } } 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 unlockField(leagueCode: string, roleId: 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 result = await GVGLeagueFarmModel.lockFields(configId, leagueCode, farmId, roleId, 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(); let myFieldCnt = 0; // 玩家已有的田有 let myLockField = 0; // 玩家没有种只是锁住了的田 let allLands = getArrayOfNumber(gameData.gvgResource.get(farmId)?.sum??0); // 全部可使用的田 for(let leagueFarm of leagueFarms) { // 全联军 if(leagueFarm.addType) maxAddTypeCntMap.set(leagueFarm.addType, maxAddTypeCntMap.get(leagueFarm.addType) - 1); if(leagueFarm.unlockTime >= nowSeconds()) { let index = allLands.indexOf(leagueFarm.fieldId); if(index > -1) allLands.splice(index); } // 玩家数据 if(leagueFarm.lockRoleId == roleId && leagueFarm.unlockTime >= nowSeconds()) { if(leagueFarm.addType) { if(!myAddTypeCntMap.has(leagueFarm.addType)) myAddTypeCntMap.set(leagueFarm.addType, 0); myAddTypeCntMap.set(leagueFarm.addType, myAddTypeCntMap.get(leagueFarm.addType) + 1); } 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, maxAddTypeCntMap: Map, count: number) { let result = new Map(); // 加成类型, 数量 let minSpField = Math.floor((myFieldCnt + count) * gameData.gvgSpFieldRatio.min / 100); // 最少需要这么多个特殊田 let maxSpField = Math.floor((myFieldCnt + count) * gameData.gvgSpFieldRatio.max / 100); // 最多可以有这么多个特殊田 let myAllAddFieldCnt = 0; for(let [_, cnt] of myAddTypeCntMap) myAllAddFieldCnt += cnt; if(myAllAddFieldCnt >= maxSpField) return result; let randCnt = getRandValueByMinMax(minSpField, maxSpField); for(let [addType, ratio] of gameData.gvgFieldAddType) { // 按比例加田 let maxCnt = maxAddTypeCntMap.get(addType)||0; if(maxCnt == 0) continue; let cnt = Math.ceil(randCnt * ratio / 100); let resultCnt = maxCnt > cnt? cnt: maxCnt; myAllAddFieldCnt += resultCnt; maxAddTypeCntMap.set(addType, maxAddTypeCntMap.get(addType) - 1); result.set(addType, resultCnt); } if(myAllAddFieldCnt < minSpField) { for(let [addType, addCnt] of result) { // 如果还有多的塞一塞 let maxCnt = maxAddTypeCntMap.get(addType)||0; if(maxCnt > addCnt) { let inc = maxCnt - addCnt > minSpField - myAllAddFieldCnt? minSpField - myAllAddFieldCnt: maxCnt - addCnt; result.set(addType, count + inc); myAllAddFieldCnt += inc; if(myAllAddFieldCnt >= minSpField) break; } else { break; } } } 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)); console.log('#### index', index); if(index != -1) { add[index].count --; if(add[index].count <= 0) add.splice(index); } } console.log('rollbackUnPlantFields', add, 'plantResult', plantResult) 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 getmyDistributeRank(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) => calProduce(b.distribute) - calProduce(a.distribute)); 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 async function calFarmOutputs(myResultFields: GVGLeagueFarmType[], league: GVGLeagueType, leaguePrepare: GVGLeaguePrepareType) { let cities = await getGVGCities(league); let [foodSum = 0, rewardSum = new Array(), leagueRewardSum = new Array(), activeSum = 0] = []; for(let { seedType, addType, farmId } of myResultFields) { let { food, reward, leagueReward, active } = calResourceResult(seedType, addType, farmId, cities||[], leaguePrepare?.activeTech||[]); 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 += active; } return { foodSum, rewardSum, leagueRewardSum, activeSum } } function calResourceResult(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 {}; let { value: foodBase, reward, leagueReward, leagueConsume } = dicGVGItem; let addRatio = seedType == addType? GVG.GVG_SP_FIELD_ADD: 0; let farmRatio = dicFarm.fieldAdd; let cityRatio = getCitiesAdd(cities); let techRatio = getTechAdd(activeTech); let produceCoin = getProduceCoinCnt(leagueConsume); return { food: Math.floor(foodBase * (1 + (addRatio + farmRatio + cityRatio + techRatio)/100)), reward, leagueReward, active: produceCoin } } // TODO 城池加成,多城池取最大加成 function getCitiesAdd(cities: number[]) { return 0 } // 科技树加成 叠加 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; }