Files
ZYZ/game-server/app/services/gvg/gvgPrepareService.ts
2023-02-17 10:58:05 +08:00

369 lines
16 KiB
TypeScript

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<number, number>();
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<number, number>, maxAddTypeCntMap: Map<number, number>, count: number) {
let result = new Map<number, number>(); // 加成类型, 数量
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<RewardInter>(), leagueRewardSum = new Array<RewardInter>(), 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;
}