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 { arrToMap, getArrayOfNumber, getRandEelm, getRandValueByMinMax, parseGoodStr, sortArrRandom } from "../../pubUtils/util"; import { addGVGReward } from "./gvgItemService"; import { getGVGConfig } from "./gvgService"; import { getProduceCoinCnt } from "./gvgItemService"; import { GVGLeagueFarmRecModel } from "../../db/GVGLeagueFarmRec"; import { addResourceRecord } from "./gvgRecService"; 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 calProducerDistribute(obj: { food: number, mineral: number, wood: number }) { let { food = 0, mineral = 0, wood = 0 } = obj||{}; return food + mineral + wood; } export function calFighterDistribute(obj: { score: number }) { return obj?.score||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 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, roleName: 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); addResourceRecord(roleId, roleName, leagueCode, resourceType, count); return { resource: league.resources, leagueLv: 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 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 }); } // 更新田 await GVGLeagueFarmModel.lockFields(configId, leagueCode, farmId, 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(); 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, maxAddTypeCntMap: Map, count: number) { let result = new Map(); // 加成类型, 数量 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 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) => calProducerDistribute(b.distribute) - calProducerDistribute(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 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: 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(), leagueRewardSum = new Array(), 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[]) { // =(100*1.5【中铁块单块产量倍数】*4【可挖出的中铁块数量】+100*2【大铁块单块产量倍数】*8【可挖出的大铁块数量】+100*3【特大铁块单块产量倍数】*8【可挖出的特大铁块数量】)*(1+0.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[]) { // =100*2【10级木堆木块单块基础产量倍数】*(1+0.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 outputAdds: 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) { outputAdds.push(dicCityAdd.foodAdd); } else if (resourceType == GVG_RESOURCE_TYPE.MINERAL) { outputAdds.push(dicCityAdd.mineralAdd); } else if (resourceType == GVG_RESOURCE_TYPE.WOOD) { outputAdds.push(dicCityAdd.woodAdd); } } return Math.max(...outputAdds); } // 科技树加成 叠加 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; }