diff --git a/game-server/app/servers/gm/handler/gmServerHandler.ts b/game-server/app/servers/gm/handler/gmServerHandler.ts index f7a8b0749..9bf601a67 100644 --- a/game-server/app/servers/gm/handler/gmServerHandler.ts +++ b/game-server/app/servers/gm/handler/gmServerHandler.ts @@ -5,8 +5,8 @@ import { UpdateHiddenDataParam, CreatePvpConfigParam, CreateServerParam, UpdateR import { RegionModel, RegionType } from '../../../db/Region'; import { gameData } from '../../../pubUtils/data'; import { Maintenance, ServerlistModel, ServerlistUpdate } from '../../../db/Serverlist'; -import { nowSeconds } from '../../../pubUtils/timeUtil'; -import { calHiddenData, createNewServer, getFutureTime, getPastTime } from '../../../services/gmService'; +import { nowSeconds, getFutureTime, getPastTime } from '../../../pubUtils/timeUtil'; +import { calHiddenData, createNewServer } from '../../../services/gmService'; import { isNumber } from 'util'; import { MarqueeModel } from '../../../db/Marquee'; import { setApiIsCloseToRemote } from '../../../services/chatService'; diff --git a/game-server/app/servers/guild/handler/gvgHandler.ts b/game-server/app/servers/guild/handler/gvgHandler.ts index b5141755d..552ac29ad 100644 --- a/game-server/app/servers/guild/handler/gvgHandler.ts +++ b/game-server/app/servers/guild/handler/gvgHandler.ts @@ -1,5 +1,5 @@ import { Application, BackendSession, ChannelService, HandlerService, pinus } from "pinus"; -import { BATTLE_FEAT_ID, DATA_NAME, GVG_ACTIVE_TYPE, GVG_PERIOD, GVG_SERVER_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB, LEAGUE_MANAGE_TYPE, STATUS } from "../../../consts"; +import { GVG_ITEM, DATA_NAME, GVG_ACTIVE_TYPE, GVG_PERIOD, GVG_SERVER_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB, LEAGUE_MANAGE_TYPE, STATUS } from "../../../consts"; import { GVGLeagueModel } from "../../../db/GVGLeague"; import { GVGLeaguePrepareModel } from "../../../db/GVGLeaguePrepare"; import { GVGMainData, LeagueDistributeInfo, LeagueMemberDistributeInfo, LeagueMemberListInfo } from "../../../domain/gvgField/returnData"; @@ -12,13 +12,13 @@ import { GVGUserItemModel } from "../../../db/GVGUserItem"; import { GVGUserTaskModel } from "../../../db/GVGUserTask"; import { GVG } from "../../../pubUtils/dicParam"; import { RoleModel } from "../../../db/Role"; -import { addGVGActive, addGVGTechActive, calProduce, checkPreTech, checkTechIsIng, getDailyLoginReward, getGVGDailyItems } from "../../../services/gvg/gvgPrepareService"; +import { addGVGTechActive, calProduce, checkPreTech, checkTechIsIng, getDailyLoginReward, getMyDistribute } from "../../../services/gvg/gvgPrepareService"; import { GVGUserDailyDataModel } from "../../../db/GVGUserDailyData"; import { gameData } from "../../../pubUtils/data"; import { lockLeagueData } from "../../../services/redLockService"; import { addGVGReward, handleGVGCost } from "../../../services/gvg/gvgItemService"; import { RewardInter } from "../../../pubUtils/interface"; -import { GuildModel } from "../../../db/Guild"; +import { getGVGCities } from "../../../services/gvg/gvgBattleService"; export default function (app: Application) { new HandlerService(app, {}); @@ -61,10 +61,13 @@ export class GVGHandler { let tasks = await GVGUserTaskModel.findByRole(configId, league.leagueCode, roleId); data.setPlayerInfo(userData, getMyAuth(league, roleId), await checkCanPrepare(roleId), checkCanChooseJob(userData?.job, leaguePrepare), items, tasks); - let { reward: dailyReward } = await getDailyLoginReward(roleId, roleName, sid, league.leagueCode, userData?.job); - if(dailyReward) data.setFistLogin(dailyReward); + let dailyReward = await getDailyLoginReward(roleId, roleName, sid, league.leagueCode, userData?.job); + if(dailyReward) data.setFistLogin(dailyReward.reward); data.setLeagueCe(await calLeagueCe(league)); + + let cities = await getGVGCities(league); + data.setCities(cities); } return resResult(STATUS.SUCCESS, data) @@ -107,14 +110,14 @@ export class GVGHandler { myUserData = await GVGUserDataModel.chooseJob(configId, myLeague.leagueCode, roleId, job); // 更新prepare leaguePrepare = await GVGLeaguePrepareModel.chooseJob(configId, myLeague.leagueCode, job); - let { reward: dailyReward } = await getDailyLoginReward(roleId, roleName, sid, myLeague.leagueCode, myUserData?.job); + let dailyReward = await getDailyLoginReward(roleId, roleName, sid, myLeague.leagueCode, myUserData?.job); return resResult(STATUS.SUCCESS, { leagueCode: myLeague.leagueCode, job: myUserData.job, producerCnt: leaguePrepare.producerCnt, fighterCnt: leaguePrepare.fighterCnt, - leagueGoods: dailyReward? dailyReward: [] + leagueGoods: dailyReward? dailyReward.reward: [] }); } @@ -161,15 +164,10 @@ export class GVGHandler { result.push(disObj) } - const myDistribute = await GVGUserDailyDataModel.findByRole(configId, myLeague.leagueCode, roleId); + const userDailyData = await GVGUserDailyDataModel.findByRole(configId, myLeague.leagueCode, roleId); return resResult(STATUS.SUCCESS, { - myDistribute: { - food: myDistribute?.food||0, - mineral: myDistribute?.mineral||0, - wood: myDistribute?.wood||0, - score: myDistribute?.score||0, - }, + myDistribute: getMyDistribute(userDailyData), list: result }); } @@ -269,7 +267,7 @@ export class GVGHandler { } // 扣战功 - const costResult = await handleGVGCost(roleId, myLeague.leagueCode, sid, [{ id: BATTLE_FEAT_ID, count: battleFeats }], [], ITEM_CHANGE_REASON.GVG_ACTIVE_TECH); + const costResult = await handleGVGCost(roleId, myLeague.leagueCode, sid, [{ id: GVG_ITEM.BATTLE_FEAT, count: battleFeats }], [], ITEM_CHANGE_REASON.GVG_ACTIVE_TECH); if(!costResult) return resResult(STATUS.GVG_BATTLE_FEAT_NOT_ENOUGH); leaguePrepare = await GVGLeaguePrepareModel.addProgress(configId, myLeague.leagueCode, techId, battleFeats); diff --git a/game-server/app/servers/guild/handler/gvgManageHandler.ts b/game-server/app/servers/guild/handler/gvgManageHandler.ts index 00cd1c196..646314d2a 100644 --- a/game-server/app/servers/guild/handler/gvgManageHandler.ts +++ b/game-server/app/servers/guild/handler/gvgManageHandler.ts @@ -5,7 +5,7 @@ import { GuildModel, GuildType } from "../../../db/Guild"; import { UserGuildModel } from "../../../db/UserGuild"; import { GVGLeagueModel, GVGLeagueType } from "../../../db/GVGLeague"; import { GVGLeagueApplyModel } from "../../../db/GVGLeagueApply"; -import { calLeagueCe, getGroupIdOfServer, getGVGPeriodData, getGVGServerType, getServersOfSameGroup } from "../../../services/gvg/gvgService"; +import { calLeagueCe, getGroupIdOfServer, getGVGConfig, getGVGPeriodData, getGVGServerType, getServersOfSameGroup } from "../../../services/gvg/gvgService"; import { checkCanManage, checkGuildLeader, checkLeagueAuth, createLeague, getLeagueApplyData, getLeagueInviteData, getMyAuth, joinGuildToLeague } from "../../../services/gvg/gvgTeamService"; import { LeagueGuildInfo, LeagueListInfo, LeagueMemberListInfo, LeagueSimpleInfo } from "../../../domain/gvgField/returnData"; import { getAllServerName, getServerName } from "../../../services/redisService"; @@ -16,6 +16,7 @@ import { sendMessageToGuildWithSuc, sendMessageToUserWithSuc } from "../../../se import { GVGLeaguePrepareModel } from "../../../db/GVGLeaguePrepare"; import { createGroupMsg, pushGroupMsgToRoom } from "../../../services/chatService"; import { sendMailToGuildByContent } from "../../../services/mailService"; +import { getGVGCities } from "../../../services/gvg/gvgBattleService"; export default function (app: Application) { new HandlerService(app, {}); @@ -94,6 +95,9 @@ export class GVGManageHandler { result.setHasApply(hasApply); // TODO 占领城池 + const cities = await getGVGCities(league); + result.setCities(cities); + return resResult(STATUS.SUCCESS, { league: result } ); @@ -323,7 +327,7 @@ export class GVGManageHandler { const league = await GVGLeagueModel.findByCode(targetLeagueCode); if(!league) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); - let { configId } = getGVGPeriodData(); + let { configId } = getGVGConfig(); const guilds = await GuildModel.findByCodes(league.guildCodes); const members = league.members||[]; let roleIds = members.map(member => member.roleId); @@ -355,7 +359,7 @@ export class GVGManageHandler { const league = await GVGLeagueModel.findByCode(targetLeagueCode); if(!league) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); - let { configId } = getGVGPeriodData(); + let { configId } = getGVGConfig(); const members = league.members||[]; let roleIds = members.map(member => member.roleId); @@ -499,7 +503,7 @@ export class GVGManageHandler { const checkAuth = await checkLeagueAuth(roleId, myLeague, LEAGUE_MANAGE_TYPE.ABDICATE); if(!checkAuth) return resResult(STATUS.GVG_HAS_NO_AUTH); - let { configId } = getGVGPeriodData(); + let { configId } = getGVGConfig(); const members = myLeague.members||[]; const candidates = members.filter(cur => cur.auth == LEAGUE_AUTH.SUB_LEADER); diff --git a/game-server/app/servers/guild/handler/gvgProduceHandler.ts b/game-server/app/servers/guild/handler/gvgProduceHandler.ts new file mode 100644 index 000000000..b3259a089 --- /dev/null +++ b/game-server/app/servers/guild/handler/gvgProduceHandler.ts @@ -0,0 +1,296 @@ +import { Application, BackendSession, ChannelService, HandlerService } from "pinus"; +import { GVG_ACTIVE_TYPE, GVG_ITEM, GVG_ITEM_TYPE, GVG_PERIOD, GVG_RESOURCE_TYPE, ITEM_CHANGE_REASON, MAIL_TYPE, STATUS } from "../../../consts"; +import { GVGLeagueModel } from "../../../db/GVGLeague"; +import { GVGLeagueFarmModel, GVGLeagueFarmType } from "../../../db/GVGLeagueFarm"; +import { GVGLeaguePrepareModel } from "../../../db/GVGLeaguePrepare"; +import { RoleModel } from "../../../db/Role"; +import { LeagueFarmListInfo, LeagueFarmMember, LeagueField } from "../../../domain/gvgField/returnData"; +import { gameData, getGVGResourceBasesByType } from "../../../pubUtils/data"; +import { ItemInter, RewardInter } from "../../../pubUtils/interface"; +import { nowSeconds } from "../../../pubUtils/timeUtil"; +import { resResult } from "../../../pubUtils/util"; +import { addGVGReward, getProduceCoinCnt, handleGVGCost } from "../../../services/gvg/gvgItemService"; +import { addGVGActive, addResource, calFarmOutputs, getCostSeedReward, getItemIdOfSeedType, getLeagueFarmShowResult, getLockFieldCnt, getmyDistributeRank, rollbackUnPlantFields, unlockField } from "../../../services/gvg/gvgPrepareService"; +import { getGVGConfig, getGVGPeriodData } from "../../../services/gvg/gvgService"; +import { sendMailByContent } from "../../../services/mailService"; +import { getAllServerName, getRoleOnlineInfo } from "../../../services/redisService"; + + +export default function (app: Application) { + new HandlerService(app, {}); + return new GVGProduceHandler(app); +} + +export class GVGProduceHandler { + channelService: ChannelService; + constructor(private app: Application) { + this.channelService = app.get('channelService'); + } + + // 内政令兑换 + async exchangeItem(msg: { id: number, count: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const sid = session.get('sid'); + const guildCode = session.get('guildCode'); + const { id, count } = msg; + + const dicGVGItem = gameData.gvgItem.get(id); + if(!dicGVGItem) return resResult(STATUS.DIC_DATA_NOT_FOUND); + if(dicGVGItem.type != GVG_ITEM_TYPE.SEED && dicGVGItem.type != GVG_ITEM_TYPE.SCOOP && dicGVGItem.type != GVG_ITEM_TYPE.AXE) { + return resResult(STATUS.GVG_ITEMS_NOT_PRODUCE); + } + + const myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const leagueItems = dicGVGItem.leagueConsume.map(cur => ({ id: cur.id, count: cur.count * count })); + const items = dicGVGItem.consume.map(cur => ({ id: cur.id, count: cur.count * count })); + + const costResult = await handleGVGCost(roleId, myLeague.leagueCode, sid, leagueItems, items, ITEM_CHANGE_REASON.GVG_EXCHANGE_SEEDS); + if(!costResult) return resResult(STATUS.GVG_ITEMS_NOT_ENOUGH); + + const leagueGoods = await addGVGReward(roleId, roleName, myLeague.leagueCode, sid, [{ id, count }], [], ITEM_CHANGE_REASON.GVG_EXCHANGE_SEEDS); + + return resResult(STATUS.SUCCESS, { + leagueGoods + }); + } + + // 获取农场/矿山/林场状态 + async getProduceList(msg: { type: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + + const { type } = msg; + + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, myLeague.leagueCode); + const leagueLv = leaguePrepare?.lv||0; + + const leagueFarms = await GVGLeagueFarmModel.findByType(configId, myLeague.leagueCode, type); + + const dicFarms = getGVGResourceBasesByType(type); + const result: LeagueFarmListInfo[] = []; + for(let { id, lv } of dicFarms) { + if(lv > leagueLv) continue; + const leagueFarm = leagueFarms.filter(cur => cur.farmId == id && cur.unlockTime >= nowSeconds()); // 已种植单位 + const canHarvestFarms = leagueFarms.filter(cur => cur.farmId == id && cur.lockRoleId == roleId && cur.harvestTime > 0 && cur.harvestTime <= nowSeconds()); + let obj = new LeagueFarmListInfo(id, leagueFarm.length, canHarvestFarms.length > 0); + result.push(obj); + } + + return resResult(STATUS.SUCCESS, { + type, + list: result + }); + } + + // 不能进的农场,获取其他人的状态 + async getOtherFarms(msg: { farmId: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + const { farmId } = msg; + + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, myLeague.leagueCode); + + const fields = await GVGLeagueFarmModel.findPlantedByFarmId(configId, myLeague.leagueCode, farmId); + const roleIds: string[] = [], fieldByRoleId = new Map(); + for(let field of fields) { + if(roleIds.indexOf(field.lockRoleId) == -1) { + roleIds.push(field.lockRoleId); + fieldByRoleId.set(field.lockRoleId, []); + } + fieldByRoleId.get(field.lockRoleId).push(field); + } + + const serverNames = await getAllServerName(); + const roles = await RoleModel.findByRoleIds(roleIds); + const result: LeagueFarmMember[] = []; + for(let role of roles) { + let obj = new LeagueFarmMember(role, serverNames); + let myFields = fieldByRoleId.get(role.roleId)||[] + obj.setByFields(myFields); + const { foodSum } = await calFarmOutputs(myFields, myLeague, leaguePrepare); + obj.setOutput(foodSum); + result.push(obj); + } + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId, false), + members: result + }); + } + + // 小游戏界面 + async getMyFarm(msg: { farmId: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + + const { farmId } = msg; + + let { period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + await unlockField(myLeague.leagueCode, roleId, farmId); + const myRank = await getmyDistributeRank(myLeague.leagueCode, myLeague.members, roleId); + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId), + myRank, + }); + } + + // 种田 + async plant(msg: { farmId: number, results: { fieldId: number, seedType: number }[] }, session: BackendSession) { + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const guildCode = session.get('guildCode'); + const sid = session.get('sid'); + + const { farmId, results } = msg; + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const cost: RewardInter[] = [], updateFields: { fieldId: number, seedType: number, time: number }[] = []; + for(let { fieldId, seedType } of results) { + let itemId = getItemIdOfSeedType(seedType); + let dicItem = gameData.gvgItem.get(itemId); + updateFields.push({ fieldId, seedType, time: dicItem.ripeTime }); + // 算消耗的种子 + let costObj = cost.find(({ id }) => id == itemId); + costObj? costObj.count++: cost.push({ id: itemId, count: 1 }); + } + const costResult = await handleGVGCost(roleId, myLeague.leagueCode, sid, cost, [], ITEM_CHANGE_REASON.PLANT) + if(!costResult) return resResult(STATUS.GVG_ITEMS_NOT_ENOUGH); + + const plantResult = await GVGLeagueFarmModel.plant(configId, myLeague.leagueCode, farmId, updateFields, roleId); + await rollbackUnPlantFields(cost, plantResult, session, myLeague.leagueCode); // 可能会有田已经被其他人占领的情况,将种子还回去 + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId) + }); + } + + // 收获 + async harvest(msg: { farmId: number, fieldIds: number[] }, session: BackendSession) { + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const sid = session.get('sid'); + const guildCode = session.get('guildCode'); + + const { farmId, fieldIds } = msg; + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, myLeague.leagueCode); + + const myResultFields = await GVGLeagueFarmModel.harvest(configId, myLeague.leagueCode, farmId, fieldIds, roleId); // 解锁 + if(myResultFields.length <= 0) return resResult(STATUS.GVG_CANNOT_HARVEST); + // 计算产量 + const { foodSum, rewardSum, leagueRewardSum, activeSum } = await calFarmOutputs(myResultFields, myLeague, leaguePrepare); + // 资源 + let resourceResult = await addResource(myLeague.leagueCode, roleId, GVG_RESOURCE_TYPE.FOOD, foodSum)||{}; + // 相关物品 + let leagueGoods = await addGVGReward(roleId, roleName, myLeague.leagueCode, sid, leagueRewardSum, rewardSum, ITEM_CHANGE_REASON.HARVEST) + // 活跃 + const active = await addGVGActive(myLeague.leagueCode, roleId, GVG_ACTIVE_TYPE.COST_PRODUCE_COIN, activeSum); + // 收获后再次预锁定一批田 + await unlockField(myLeague.leagueCode, roleId, farmId); + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId), + leagueGoods, + active, + ...resourceResult + }); + } + + // 关闭页面 + async plantEnd(msg: { farmId: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + + const { farmId } = msg; + let { configId } = getGVGConfig(); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + await GVGLeagueFarmModel.releaseLock(configId, myLeague.leagueCode, farmId, roleId); + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId), + }); + } + + // 帮收 + async helpHarvest(msg: { farmId: number, fieldId: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const guildCode = session.get('guildCode'); + + const { farmId, fieldId } = msg; + let { configId, period } = getGVGPeriodData(); + if(period != GVG_PERIOD.PREPARE) return resResult(STATUS.GVG_NOT_PREPARE_PERIOD); + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + const leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, myLeague.leagueCode); + + const resultField = await GVGLeagueFarmModel.helpHarvest(configId, myLeague.leagueCode, farmId, fieldId); // 解锁 + if(!resultField) return resResult(STATUS.GVG_CANNOT_HARVEST); + + let targetRoleId = resultField.lockRoleId; + let targetOnlineInfo = await getRoleOnlineInfo(targetRoleId); + + // 计算产量 + const { foodSum, rewardSum, leagueRewardSum, activeSum } = await calFarmOutputs([resultField], myLeague, leaguePrepare); + // 资源 + await addResource(myLeague.leagueCode, targetRoleId, GVG_RESOURCE_TYPE.FOOD, foodSum)||{}; + await sendMailByContent(MAIL_TYPE.HELP_HARVEST, targetRoleId, { sendName: roleName, goods: rewardSum }); + // 相关物品 + await addGVGReward(targetRoleId, roleName, myLeague.leagueCode, targetOnlineInfo.sid, leagueRewardSum, [], ITEM_CHANGE_REASON.HARVEST) + // 活跃 + await addGVGActive(myLeague.leagueCode, targetRoleId, GVG_ACTIVE_TYPE.COST_PRODUCE_COIN, activeSum); + + return resResult(STATUS.SUCCESS, { + ...await getLeagueFarmShowResult(myLeague.leagueCode, roleId, farmId), + }); + } + + // 获取资源 + async debugAddResource(msg: { type: number, count: number }, session: BackendSession) { + const roleId = session.get('roleId'); + const guildCode = session.get('guildCode'); + const { type, count } = msg; + + let myLeague = await GVGLeagueModel.findLeagueByGuild(guildCode); + if(!myLeague) return resResult(STATUS.GVG_LEAGUE_NOT_EXIST); + + let resourceResult = await addResource(myLeague.leagueCode, roleId, type, count); + if(!resourceResult) return resResult(STATUS.WRONG_PARMS); + + return resResult(STATUS.SUCCESS, resourceResult); + } +} \ No newline at end of file diff --git a/game-server/app/services/checkParam.ts b/game-server/app/services/checkParam.ts index 40823db81..5bb1c3dcd 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -1,6 +1,5 @@ import { isArray, isBoolean, isNumber, isString } from "underscore"; -import { BLOCK_OPEATE, DEBUG_MAGIC_WORD, GM_MAIL_TYPE, GUILD_AUTH, GUILD_STRUCTURE, LEAGUE_JOB, LINEUP_NUM, MSG_TYPE } from "../consts"; -import { GVG } from "../pubUtils/dicParam"; +import { BLOCK_OPEATE, DEBUG_MAGIC_WORD, GM_MAIL_TYPE, GUILD_AUTH, GUILD_STRUCTURE, GVG_RESOURCE_TYPE, LEAGUE_JOB, LINEUP_NUM, MSG_TYPE } from "../consts"; import { pvpEndParamInter } from "../pubUtils/interface"; import { isDevelopEnv } from "./utilService"; @@ -1235,6 +1234,12 @@ export function checkRouteParam(route: string, msg: any) { if(!isNumber(msg.techId)) return false; break; } + + case "guild.gvgProduceHandler.getProduceList": + { + if(!checkIsInEnum(GVG_RESOURCE_TYPE, msg.type)) return false; + break; + } case "order.orderHandler.applyOrder": { let { productID, payType, activityId, paramStr, useVoucher } = msg; diff --git a/game-server/app/services/gmService.ts b/game-server/app/services/gmService.ts index bc9243590..9170b163f 100644 --- a/game-server/app/services/gmService.ts +++ b/game-server/app/services/gmService.ts @@ -7,7 +7,7 @@ import { pinus } from "pinus"; import { ServerlistModel, ServerlistType } from "../db/Serverlist"; import { sendMailsByGmMail } from "./mailService"; import GMMail, { GMMailModel } from '../db/GMMail'; -import { nowSeconds } from "../pubUtils/timeUtil"; +import { getPastTime, nowSeconds } from "../pubUtils/timeUtil"; import { CreateServerParam } from "../domain/backEndField/params"; import { SignInData } from "../domain/activityField/signInField"; import { RegionModel, RegionType } from "../db/Region"; @@ -406,16 +406,4 @@ export async function calHiddenData(uid: number) { } } await HiddenDataModel.updateHiddenData(getPastTime(), heroes, goods, uid); -} - -export function getPastTime() { - return moment('2022-01-01').unix(); -} - -export function getFutureTime() { - return moment('2100-01-01').unix(); -} - -export function initServerZone() { - } \ No newline at end of file diff --git a/game-server/app/services/gvg/gvgBattleService.ts b/game-server/app/services/gvg/gvgBattleService.ts new file mode 100644 index 000000000..cae0702b8 --- /dev/null +++ b/game-server/app/services/gvg/gvgBattleService.ts @@ -0,0 +1,10 @@ +import { GVGLeagueType } from "../../db/GVGLeague"; + +/** + * TODO 获取本联军上周占领的城池 + * @param league + * @returns number[] 城池id + */ +export async function getGVGCities(league: GVGLeagueType) { + return [] +} \ No newline at end of file diff --git a/game-server/app/services/gvg/gvgItemService.ts b/game-server/app/services/gvg/gvgItemService.ts index bc238d7bb..d709138cf 100644 --- a/game-server/app/services/gvg/gvgItemService.ts +++ b/game-server/app/services/gvg/gvgItemService.ts @@ -1,9 +1,8 @@ -import { GVG_RETURN_ITEM_TYPE, ITEM_CHANGE_REASON, LEAGUE_ITEM_REFRESH_TYPE, PUSH_ROUTE } from "../../consts"; +import { GVG_RETURN_ITEM_TYPE, ITEM_CHANGE_REASON, LEAGUE_ITEM_REFRESH_TYPE, GVG_ITEM, PUSH_ROUTE } from "../../consts"; import { GVGUserItemModel } from "../../db/GVGUserItem"; import { gameData } from "../../pubUtils/data"; import { RewardInter } from "../../pubUtils/interface"; -import { DAY_TO_SECOND, getZeroPoint } from "../../pubUtils/timeUtil"; -import { getFutureTime } from "../gmService"; +import { DAY_TO_SECOND, getZeroPoint, getFutureTime } from "../../pubUtils/timeUtil"; import { sendMessageToUserWithSuc } from "../pushService"; import { addItems, handleCost } from "../role/rewardService"; import { getGVGConfig } from "./gvgService"; @@ -73,4 +72,14 @@ export async function handleGVGCost(roleId: string, leagueCode: string, sid: str const decreaseResult = await GVGUserItemModel.decreaseItem(configId, leagueCode, roleId, leagueItems); return decreaseResult; +} + +export function getProduceCoinCnt(items: RewardInter[]) { + let produceCoinCnt = 0; + for(let { id, count } of items) { + if(id == GVG_ITEM.PRODUCE_COIN) { + produceCoinCnt = count; break; + } + } + return produceCoinCnt; } \ No newline at end of file diff --git a/game-server/app/services/gvg/gvgPrepareService.ts b/game-server/app/services/gvg/gvgPrepareService.ts index 7fc4e3e68..0b7518b9e 100644 --- a/game-server/app/services/gvg/gvgPrepareService.ts +++ b/game-server/app/services/gvg/gvgPrepareService.ts @@ -1,12 +1,22 @@ -import { GVG_ACTIVE_TYPE, ITEM_CHANGE_REASON, LEAGUE_JOB } from "../../consts"; -import { Tech } from "../../db/GVGLeaguePrepare"; -import { GVGUserDailyDataModel } from "../../db/GVGUserDailyData"; +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 { gameData } from "../../pubUtils/data"; +import { GVGUserItemModel } from "../../db/GVGUserItem"; +import { LeagueField } from "../../domain/gvgField/returnData"; +import { calLeagueLv, gameData, getFieldMaxAddType } from "../../pubUtils/data"; import { GVG } from "../../pubUtils/dicParam"; -import { parseGoodStr } from "../../pubUtils/util"; +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); @@ -41,11 +51,11 @@ export function calProduce(obj: { food: number, mineral: number, wood: number }) * @param type GVG_ACTIVE_TYPE * @returns */ -export async function addGVGActive(leagueCode: string, roleId: string, type: GVG_ACTIVE_TYPE) { +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); + let result = await GVGUserDataModel.addActive(configId, leagueCode, roleId, type, add * count); return result.active; } @@ -75,4 +85,285 @@ export async function getDailyLoginReward(roleId: string, roleName: string, sid: 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; } \ No newline at end of file diff --git a/shared/consts/constModules/gvgConst.ts b/shared/consts/constModules/gvgConst.ts index b50bd8228..c030d63f9 100644 --- a/shared/consts/constModules/gvgConst.ts +++ b/shared/consts/constModules/gvgConst.ts @@ -52,11 +52,47 @@ export enum GVG_RETURN_ITEM_TYPE { GVG_ITEM = 2, // GVG道具 } -export const BATTLE_FEAT_ID = 12; // 战功 - export enum GVG_ACTIVE_TYPE { COST_PRODUCE_COIN = 1, // 消耗内政令 COST_FIGHT_COIN = 2, // 消耗征战令 TECH_ACTIVATE = 3, // 激活千机阁 LOGIN = 4, // 登录逐鹿中原界面 +} + +export enum GVG_ITEM_TYPE { + SEED = 1, // 种子 + SCOOP = 2, // 铲子 + AXE = 3, // 斧子 + COIN = 4, // 令 + BATTLE_FEATS = 5, // 战功 + BATTLE_ITEM = 6, // 激战期道具 +} + +export enum GVG_ITEM { + WHEAT = 1, // 小麦 + CORN = 2, // 粟米 + RICE = 3, // 水稻 + PRODUCE_COIN = 10, // 内政令 + BATTLE_FEAT = 12, // 战功 +} + +export enum GVG_RESOURCE_TYPE { + FOOD = 1, // 粮食 + MINERAL = 2, // 矿物 + WOOD = 3, // 木堆 +} + +export enum GVG_SEED_TYPE { + WHEAT = 1, // 小麦 + CORN = 2, // 粟米 + RICE = 3, // 水稻 +} + +export enum GVG_TECH_TYPE { + PRODUCE_UP = 1, // 产量上升 + FIGHT_UP = 2, // 征战天下攻击提升 + BATTLE_UP = 3, // 激战期攻击提升 + BATTLE_REVIVE_GAP = 4, // 激战期复活间隔减少 + BATTLE_ITEM_CATAPULT = 5, // 成员获得投石车(箭塔) + BATTLE_ITEM_KNIFE = 6, // 成员活动匕首(攻城车) } \ No newline at end of file diff --git a/shared/consts/constModules/mailConst.ts b/shared/consts/constModules/mailConst.ts index ea2666061..ade6a710c 100644 --- a/shared/consts/constModules/mailConst.ts +++ b/shared/consts/constModules/mailConst.ts @@ -62,6 +62,7 @@ export enum MAIL_TYPE { REBATE = 32, // 返利邮件 GROUP_SHOP_REFUND = 33, // 退费 ARTIFACT_OVER = 34, // 退费 + HELP_HARVEST = 38, // 帮收 }; export const SEND_NAME = '系统'; diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index a82200e5d..98ccde142 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -569,6 +569,8 @@ export const FILENAME = { DIC_GVG_PERIOD: 'dic_zyz_GVGPeriod', DIC_GVG_TECH: 'dic_zyz_GVGTech', DIC_GVG_ITEM: 'dic_zyz_GVGItems', + DIC_GVG_LEAGUE_LV: 'dic_zyz_GVGLeagueLv', + DIC_GVG_RESOURCE_BASE: 'dic_zyz_GVGResourceBase', } export const WAR_RELATE_TABLES = [ @@ -1071,6 +1073,10 @@ export enum ITEM_CHANGE_REASON { ARTIFACT_REBUILD = 160, // 宝物重铸 GVG_ACTIVE_TECH = 161, // 千机阁解锁 GVG_LOGIN = 162, // 每日登录 + GVG_EXCHANGE_SEEDS = 163, // 兑换种子等 + PLANT = 164, // 种田 + PLANT_ROLLBACK = 165, // 种田的时候田已经被抢了回滚 + HARVEST = 166, // 收获 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index ca4a4c6f3..a19c8809a 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -308,6 +308,15 @@ export const STATUS = { GVG_HAS_CHOOSE_JOB: { code: 21302, simStr: '已经选择过职能' }, GVG_NOT_PREPARE_PERIOD: { code: 21303, simStr: '您只能在准备期进行该操作' }, GVG_JOB_LIMIT: { code: 21304, simStr: '职能选择达到上限' }, + GVG_ITEMS_NOT_ENOUGH: { code: 21305, simStr: '道具不足' }, + GVG_ITEMS_NOT_PRODUCE: { code: 21306, simStr: '道具不可使用内政令兑换' }, + GVG_TECH_PRE_POSITION_NOT_UNLOCK: { code: 21307, simStr: '前置科技未激活' }, + GVG_TECH_QUEUECNT_MAX: { code: 21308, simStr: '解锁队列到达上限' }, + GVG_TECH_LV_NOT_ENOUGH: { code: 21309, simStr: '联军等级不足不可解锁' }, + GVG_TECH_HAS_UNLOCKED: { code: 21310, simStr: '该科技已解锁' }, + GVG_TECH_NOT_QUEUE: { code: 21311, simStr: '该科技不在解锁队列中' }, + GVG_BATTLE_FEAT_NOT_ENOUGH: { code: 21312, simStr: '战功不足' }, + GVG_CANNOT_HARVEST: { code: 21313, simStr: '暂不可收获' }, // 通用 30000 - 30099 DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' }, @@ -321,12 +330,6 @@ export const STATUS = { CONSUME_TYPE_ERR: { code: 30008, simStr: '道具类型错误' }, DB_DATA_NOT_FOUND: { code: 30009, simStr: '数据库数据未找到' }, BOX_CAN_NOT_RECEIVE: { code: 30010, simStr: '宝箱不可领取' }, - GVG_TECH_PRE_POSITION_NOT_UNLOCK: { code: 30011, simStr: '前置科技未激活' }, - GVG_TECH_QUEUECNT_MAX: { code: 30012, simStr: '解锁队列到达上限' }, - GVG_TECH_LV_NOT_ENOUGH: { code: 30013, simStr: '联军等级不足不可解锁' }, - GVG_TECH_HAS_UNLOCKED: { code: 30014, simStr: '该科技已解锁' }, - GVG_TECH_NOT_QUEUE: { code: 30015, simStr: '该科技不在解锁队列中' }, - GVG_BATTLE_FEAT_NOT_ENOUGH: { code: 30016, simStr: '战功不足' }, // 武将养成通用 30100 - 30199 diff --git a/shared/db/GVGLeagueFarm.ts b/shared/db/GVGLeagueFarm.ts new file mode 100644 index 000000000..2222d6057 --- /dev/null +++ b/shared/db/GVGLeagueFarm.ts @@ -0,0 +1,126 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, } from '@typegoose/typegoose'; +import { getFutureTime, nowSeconds } from '../pubUtils/timeUtil'; +import { genCode } from '../pubUtils/util'; +import { GVG } from '../pubUtils/dicParam'; + +@index({ leagueCode: 1, configId: 1 }) +@index({ fieldId: 1 }) +@index({ batchCode: 1 }) +export default class GVGLeagueFarm extends BaseModel { + + @prop({ required: true }) + leagueCode: string; // 联军唯一code + + @prop({ required: true }) + configId: number; + + @prop({ required: true }) + farmId: number; // 农庄id + + @prop({ required: true }) + type: number; // 农庄类型 + + @prop({ required: true }) + fieldId: number; // 农庄id + + @prop({ required: true }) + unlockTime: number; // 解锁时间,打开界面时预锁定一批填,种下去之后锁定时间延长到收获时间 + + @prop({ required: true }) + lockRoleId: string; // 锁定的人 + + @prop({ required: true }) + harvestTime: number; // 收获时间,种植下去后将时间更新,收获之后设为0 + + @prop({ required: true }) + seedType: number; // 实际种的种子类型 + + @prop({ required: true }) + addType: number; // 种子加成 + + @prop({ required: true }) + batchCode: string; // 批量号 + + public static async findByType(configId: number, leagueCode: string, type: number) { + const result: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ configId, leagueCode, type }).select('-_id').lean(); + return result; + } + + public static async findByFarmId(configId: number, leagueCode: string, farmId: number) { + const result: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ configId, leagueCode, farmId }).select('-_id').lean(); + return result; + } + + public static async findLockedByFarmId(configId: number, leagueCode: string, farmId: number) { + const result: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ configId, leagueCode, farmId, unlockTime: { $gte: nowSeconds() }}).select('-_id').lean(); + return result; + } + + + public static async findPlantedByFarmId(configId: number, leagueCode: string, farmId: number) { + const result: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ configId, leagueCode, farmId, unlockTime: { $gte: nowSeconds() }, harvestTime: { $gte: nowSeconds() }}).select('-_id').lean(); + return result; + } + + /** + * 首次打开页面时锁定田 + * @param configId + * @param leagueCode + * @param lands [{fieldId: number, time: number, addType: number}] fieldId:田地id, time: 锁定时间 addType: 种子加成 + * @returns + */ + public static async lockFields(configId: number, leagueCode: string, farmId: number, roleId: string, lands: { fieldId: number, addType: number }[]) { + // 先创建 + await GVGLeagueFarmModel.bulkWrite(lands.map(({ fieldId }) => { + return { updateOne: { filter: { configId, leagueCode, farmId, fieldId }, update: { $setOnInsert: { unlockTime: 0, harvestTime: 0, seedType: 0, addType: 0 } }, upsert: true} } + })); + const result = await GVGLeagueFarmModel.bulkWrite(lands.map(({ fieldId, addType }) => { + return { updateOne: { filter: { configId, leagueCode, farmId, fieldId, unlockTime: { $lt: nowSeconds() } }, update: { $set: { addType, unlockTime: nowSeconds() + GVG.GVG_FARM_LOCK_TIME, lockRoleId: roleId } } } } + })); + return result; + } + + public static async findByFarmIdAndRoleId(configId: number, leagueCode: string, farmId: number, roleId: string) { + const result: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ configId, leagueCode, farmId, lockRoleId: roleId, unlockTime: { $gte: nowSeconds() } }).sort({ fieldId: 1 }).select('-_id -createdAt -updatedAt -__v -leagueCode -configId -roleId').lean(); + return result; + } + + public static async plant(configId: number, leagueCode: string, farmId: number, fields: { fieldId: number, seedType: number, time: number }[], roleId: string) { + const batchCode = genCode(10); + const result = await GVGLeagueFarmModel.bulkWrite(fields.map(({ fieldId, seedType, time }) => { + return { updateOne: { filter: { configId, leagueCode, farmId, fieldId, lockRoleId: roleId }, update: { $set: { seedType, unlockTime: getFutureTime(), harvestTime: nowSeconds() + time, batchCode }} } } + })); + if(result.modifiedCount == 0) return []; + const fieldResult: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ batchCode }).sort({ fieldId: 1 }).select('-_id -createdAt -updatedAt -__v -configId -leagueCode').lean(); + return fieldResult; + } + + public static async harvest(configId: number, leagueCode: string, farmId: number, fieldIds: number[], roleId: string) { + const batchCode = genCode(10); + const result = await GVGLeagueFarmModel.bulkWrite(fieldIds.map(fieldId => { + return { updateOne: { filter: { configId, leagueCode, farmId, fieldId, lockRoleId: roleId, harvestTime: { $lt: nowSeconds() } }, update: { $set: { unlockTime: 0, harvestTime: 0, lockRoleId: '', batchCode }} } } + })); + if(result.modifiedCount == 0) return []; + const fieldResult: GVGLeagueFarmType[] = await GVGLeagueFarmModel.find({ batchCode }).sort({ fieldId: 1 }).select('-_id -createdAt -updatedAt -__v -configId -leagueCode').lean(); + return fieldResult; + } + + public static async helpHarvest(configId: number, leagueCode: string, farmId: number, fieldId: number) { + const result: GVGLeagueFarmType = await GVGLeagueFarmModel.findOneAndUpdate( + { configId, leagueCode, farmId, fieldId, harvestTime: { $lt: nowSeconds() }, unlockTime: { $gt: nowSeconds() } }, + { $set: { unlockTime: 0, harvestTime: 0, lockRoleId: '' }}, { new: true }).lean(); + return result + } + + public static async releaseLock(configId: number, leagueCode: string, farmId: number, roleId: string) { + await GVGLeagueFarmModel.updateMany({ configId, leagueCode, farmId, lockRoleId: roleId }, { $set: { unlockTime: 0, lockRoleId: '' } }); + } +} + +export const GVGLeagueFarmModel = getModelForClass(GVGLeagueFarm); + +export interface GVGLeagueFarmType extends Pick, keyof GVGLeagueFarm> { + id: number; +}; +export type GVGLeagueFarmUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/db/GVGLeaguePrepare.ts b/shared/db/GVGLeaguePrepare.ts index 582e2b14b..82d3237a7 100644 --- a/shared/db/GVGLeaguePrepare.ts +++ b/shared/db/GVGLeaguePrepare.ts @@ -109,19 +109,36 @@ export default class GVGLeaguePrepare extends BaseModel { public static async unlockTech(configId: number, leagueCode: string, techId: number, maxProgress: number) { const result: GVGLeaguePrepareType = await GVGLeaguePrepareModel.findOneAndUpdate({ - configId, leagueCode, techQueueCnt: { $lt: GVG.GVG_LEAGUE_TECH_LIST } }, { $push: { techQueue: new Tech(techId, maxProgress) }, $inc: { techQueueCnt: 1 } }, { new: true }).lean(); + configId, leagueCode, techQueueCnt: { $lt: GVG.GVG_LEAGUE_TECH_LIST } + }, { $push: { techQueue: new Tech(techId, maxProgress) }, $inc: { techQueueCnt: 1 } }, { new: true }).lean(); return result; } public static async addProgress(configId: number, leagueCode: string, techId: number, addProgress: number) { const result: GVGLeaguePrepareType = await GVGLeaguePrepareModel.findOneAndUpdate({ - configId, leagueCode, 'techQueue.id': techId }, { $inc: { 'techQueue.$.progress': addProgress } }, { new: true }).lean(); + configId, leagueCode, 'techQueue.id': techId + }, { $inc: { 'techQueue.$.progress': addProgress } }, { new: true }).lean(); return result; } public static async activate(configId: number, leagueCode: string, techId: number) { const result: GVGLeaguePrepareType = await GVGLeaguePrepareModel.findOneAndUpdate({ - configId, leagueCode }, { $push: { activeTech: techId}, $pull: { techQueue: { id: techId } } }, { new: true }).lean(); + configId, leagueCode + }, { $push: { activeTech: techId}, $pull: { techQueue: { id: techId } }, $inc: { techQueueCnt: -1 } }, { new: true }).lean(); + return result; + } + + public static async addResource(configId: number, leagueCode: string, food: number, mineral: number, wood: number) { + const result: GVGLeaguePrepareType = await GVGLeaguePrepareModel.findOneAndUpdate({ + configId, leagueCode + }, { + $inc: { 'resources.food': food, 'resources.mineral': mineral, 'resources.wood': wood } + }, { new: true }).lean(); + return result; + } + + public static async setLv(configId: number, leagueCode: string, lv: number) { + const result: GVGLeaguePrepareType = await GVGLeaguePrepareModel.findOneAndUpdate({ configId, leagueCode }, { $set: { lv }}, { new: true }).lean(); return result; } } diff --git a/shared/db/GVGUserDailyData.ts b/shared/db/GVGUserDailyData.ts index 37b10444b..d3205d839 100644 --- a/shared/db/GVGUserDailyData.ts +++ b/shared/db/GVGUserDailyData.ts @@ -61,6 +61,11 @@ export default class GVGUserDailyData extends BaseModel { const result: GVGUserDailyDataType = await GVGUserDailyDataModel.findOneAndUpdate({ configId, leagueCode, roleId, time: getZeroPoint() }, { hasLogin: true }, { upsert: true, new: true }).lean(); return result; } + + public static async addResource(configId: number, leagueCode: string, roleId: string, food: number, mineral: number, wood: number) { + const result: GVGUserDailyDataType = await GVGUserDailyDataModel.findOneAndUpdate({ configId, leagueCode, roleId, time: getZeroPoint()}, { $inc: { food, mineral, wood }}, { new: true, upsert: true }).lean(); + return result; + } } export const GVGUserDailyDataModel = getModelForClass(GVGUserDailyData); diff --git a/shared/db/GVGUserData.ts b/shared/db/GVGUserData.ts index 150549118..8e22c7eb7 100644 --- a/shared/db/GVGUserData.ts +++ b/shared/db/GVGUserData.ts @@ -103,6 +103,15 @@ export default class GVGUserData extends BaseModel { const result: GVGUserDataType = await GVGUserDataModel.findOneAndUpdate({ configId, leagueCode, roleId }, { $inc: { active }, $push: { activeRec: new ActiveRec(activeType, active) } }, { new: true, upsert: true}).select('active').lean(); return result; } + + public static async addResource(configId: number, leagueCode: string, roleId: string, food: number, mineral: number, wood: number) { + const result: GVGUserDataType = await GVGUserDataModel.findOneAndUpdate({ + configId, leagueCode, roleId + }, { + $inc: { 'distribute.food': food, 'distribute.mineral': mineral, 'distribute.wood': wood } + }, { new: true, upsert: true }).lean(); + return result; + } } export const GVGUserDataModel = getModelForClass(GVGUserData); diff --git a/shared/db/GVGUserItem.ts b/shared/db/GVGUserItem.ts index defea91f2..b698581d5 100644 --- a/shared/db/GVGUserItem.ts +++ b/shared/db/GVGUserItem.ts @@ -48,6 +48,11 @@ export default class GVGUserItem extends BaseModel { console.log(result.matchedCount, items.length) return result.matchedCount == items.length; } + + public static async findByItem(configId: number, leagueCode: string, roleId: string, id: number) { + const result: GVGUserItemType = await GVGUserItemModel.findOne({ configId, leagueCode, roleId, id }).select('count').lean(); + return result; + } } export const GVGUserItemModel = getModelForClass(GVGUserItem); diff --git a/shared/domain/gvgField/returnData.ts b/shared/domain/gvgField/returnData.ts index 60e5032f5..b2fa3d925 100644 --- a/shared/domain/gvgField/returnData.ts +++ b/shared/domain/gvgField/returnData.ts @@ -4,6 +4,8 @@ import { GVGLeaguePrepareType } from "../../db/GVGLeaguePrepare"; import { Distribute, GVGUserDataType } from "../../db/GVGUserData"; import { GuildType } from "../../db/Guild"; import { LEAGUE_JOB } from "../../consts"; +import { GVGLeagueFarmType } from "../../db/GVGLeagueFarm"; +import { nowSeconds } from "../../pubUtils/timeUtil"; class LeagueLeaderInfo { name: string; // 盟主名 @@ -124,6 +126,10 @@ export class GVGMainData { setLeagueCe(leagueCe: number) { this.leagueCe = leagueCe; } + + setCities(cities: number[]) { + this.cities = cities; + } } export class LeagueListInfo { @@ -280,4 +286,61 @@ export class LeagueDistributeInfo { setMembers(members: LeagueMemberDistributeInfo[]) { this.members = members; } +} + +export class LeagueFarmListInfo { + farmId: number; + count: number; // 已种植数量 + canHarvest: boolean; + + constructor(farmId: number, count: number, canHarvest: boolean) { + this.farmId = farmId; + this.count = count; + this.canHarvest = canHarvest; + } +} + +export class LeagueField { + fieldId: number; // 田的编号 + seedType: number; // 已种植了的种子类型 0-未种 1-小麦 2-玉米 3-水稻 + addType: number; // 特殊加成类型 0-普通田 1-小麦加成 2-玉米 3-水稻 + harvestTime: number; // 收获时间, 10位时间戳 + unlockTime: number; // 锁定时间,会按照内政令的数量给玩家预锁定一批填,超过时间限制就不锁给他了也会分给其他人了 + + constructor(leagueFarm: GVGLeagueFarmType) { + if(!leagueFarm) return; + this.fieldId = leagueFarm.fieldId; + this.seedType = leagueFarm.seedType; + this.addType = leagueFarm.addType; + this.harvestTime = leagueFarm.harvestTime; + this.unlockTime = leagueFarm.unlockTime; + } +} + +export class LeagueFarmMember { + roleId: string; // 种田玩家 + roleName: string; + guildName: string; + serverName: string; + count: number; // 种植地块 + output: number; // 预计产量 + canHelp: boolean; // 是否可以帮收 + harvestTime: number; // 收成时间,10位时间戳 + + constructor(role: RoleType, serverNames: any) { + this.roleId = role.roleId; + this.roleName = role.roleName; + this.guildName = role.guildName; + this.serverName = serverNames[role.serverId]; + } + + setByFields(fields: GVGLeagueFarmType[]) { + this.count = fields.length; + this.harvestTime = Math.min(...fields.map(cur => cur.harvestTime))||0; + this.canHelp = !!fields.find(cur => cur.harvestTime < nowSeconds()); + } + + setOutput(output: number) { + this.output = output; + } } \ No newline at end of file diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 6e8dca831..30a49bb34 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -121,6 +121,8 @@ import { dicGiftPackagePlan, loadGiftPackagePlan } from "./dictionary/DicGiftPac import { dicGVGPeriod, loadGVGPeriod } from './dictionary/DicGVGPeriod'; import { dicGVGTech, loadGVGTech } from "./dictionary/DicGVGTech"; import { dicGVGItem, loadGVGItem } from "./dictionary/DicGVGItems"; +import { dicGVGLeagueLv, loadGVGLeagueLv } from "./dictionary/DicGVGLeagueLv"; +import { dicGVGResourceBase, dicGVGResourceBaseByType, dicGVGResourceBaseByLv, loadGVGResourceBase, DicGVGResourceBase } from './dictionary/DicGVGResourceBase'; export const gameData = { daily: dicDaily, @@ -302,6 +304,12 @@ export const gameData = { gvgTech: dicGVGTech, gvgItem: dicGVGItem, gvgActive: new Map(), + gvgLeagueLv: dicGVGLeagueLv, + gvgResource: dicGVGResourceBase, + gvgResourceBaseByType: dicGVGResourceBaseByType, + gvgResourceBaseByLv: dicGVGResourceBaseByLv, + gvgFieldAddType: new Map(), + gvgSpFieldRatio: { min: 0, max: 0}, }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 @@ -1081,6 +1089,57 @@ function parseGVGActive() { } } +export function calLeagueLv(resources: { food: number, mineral: number, wood: number }) { + let lv = 1; + for(let [curLv, dic] of gameData.gvgLeagueLv) { + lv = curLv; + if(resources.food < dic.food || resources.mineral < dic.mineral || resources.wood < dic.wood) { + break; + } + } + return lv +} + +export function getGVGResourceBasesByType(type: number) { + const ids = gameData.gvgResourceBaseByType.get(type)||[]; + const result: DicGVGResourceBase[] = []; + for(let id of ids) { + let dicResource = gameData.gvgResource.get(id); + if(dicResource) result.push(dicResource); + } + return result; +} + +export function getGVGResourceBaseByTypeAndLv(type: number, lv: number) { + let id = gameData.gvgResourceBaseByLv.get(`${type}_${lv}`); + return gameData.gvgResource.get(id); +} + + +// 获取农庄小游戏最多能有多少特殊加成田 +export function getFieldMaxAddType(farmId: number) { + let add = new Map(); + let dicResource = gameData.gvgResource.get(farmId); + if(!dicResource) return add; + for(let [type, ratio] of gameData.gvgFieldAddType) { + add.set(type, Math.floor(dicResource.sum * ratio / 100)) + } + return add; +} + +function parseGVGFieldAdd() { + let arr = decodeArrayListStr(param.GVG.GVG_FIELD_TYPE_RATIO); + for(let [type, ratio] of arr) { + gameData.gvgFieldAddType.set(parseInt(type), parseFloat(ratio)); + } +} + +function parseGVGSpFieldRatio() { + let arr = param.GVG.GVG_SP_FIELD_RATIO.split('&'); + gameData.gvgSpFieldRatio.min = parseFloat(arr[0]); + gameData.gvgSpFieldRatio.max = parseFloat(arr[0]); +} + // 初始加载 function initDatas() { parseDicParam(); @@ -1102,6 +1161,8 @@ function parseDicParam() { decodeLadderBuyCost(); parseComBattleRewardTime(); parseGVGActive(); + parseGVGFieldAdd(); + parseGVGSpFieldRatio(); } /** @@ -1280,6 +1341,8 @@ function loadDatas() { loadGVGPeriod(); loadGVGTech(); loadGVGItem(); + loadGVGLeagueLv(); + loadGVGResourceBase(); } // 重载dicParam diff --git a/shared/pubUtils/dicParam.ts b/shared/pubUtils/dicParam.ts index c6da869be..aee864177 100644 --- a/shared/pubUtils/dicParam.ts +++ b/shared/pubUtils/dicParam.ts @@ -384,4 +384,8 @@ export const GVG = { GVG_SERVICETYPE_VESTIGE: '1&1|2&3', // 单服和跨服随机出几个遗迹点 GVG_ARMY_LEAGUE_TIME: 3, // GVG开启军团等级开启限制 GVG_ROLE_TYPE: '1&贤臣|2&猛将', // GVG中职能选择 + GVG_SP_FIELD_ADD: 0.2, + GVG_FARM_LOCK_TIME: 180, + GVG_FIELD_TYPE_RATIO: '1&10|2&20|3&10', + GVG_SP_FIELD_RATIO: '30&60', }; diff --git a/shared/pubUtils/dictionary/DicGVGItems.ts b/shared/pubUtils/dictionary/DicGVGItems.ts index 70c171df4..8631479ef 100644 --- a/shared/pubUtils/dictionary/DicGVGItems.ts +++ b/shared/pubUtils/dictionary/DicGVGItems.ts @@ -22,6 +22,8 @@ export interface DicGVGItem { reward: RewardInter[]; // 消耗后可获得的这张表里的奖励 leagueReward: RewardInter[]; + // 成熟时间 + ripeTime: number; } export const dicGVGItem = new Map(); diff --git a/shared/pubUtils/dictionary/DicGVGLeagueLv.ts b/shared/pubUtils/dictionary/DicGVGLeagueLv.ts new file mode 100644 index 000000000..6f22ef2d8 --- /dev/null +++ b/shared/pubUtils/dictionary/DicGVGLeagueLv.ts @@ -0,0 +1,34 @@ +// GVG道具 +import { FILENAME } from '../../consts' +import { RewardInter } from '../interface'; +import { parseGoodStr, readFileAndParse } from '../util' + +export interface DicGVGLeagueLv { + // 等级 + lv: number; + // 粮食 + food: number; + // 矿产 + mineral: number; + // 木材 + wood: number; + // 消耗后可获得的奖励 + reward: RewardInter[]; +} + +export const dicGVGLeagueLv = new Map(); +export function loadGVGLeagueLv() { + dicGVGLeagueLv.clear(); + + let arr = readFileAndParse(FILENAME.DIC_GVG_LEAGUE_LV); + let food = 0, mineral = 0, wood = 0; + arr.forEach(o => { + food += o.food; + mineral += o.mineral; + wood += o.wood; + dicGVGLeagueLv.set(o.lv, { + lv: o.lv, reward: parseGoodStr(o.reward), food, mineral, wood, + }); + }); + arr = undefined; +} \ No newline at end of file diff --git a/shared/pubUtils/dictionary/DicGVGResourceBase.ts b/shared/pubUtils/dictionary/DicGVGResourceBase.ts new file mode 100644 index 000000000..cd4ae4828 --- /dev/null +++ b/shared/pubUtils/dictionary/DicGVGResourceBase.ts @@ -0,0 +1,74 @@ +// GVG千机阁 +import { FILENAME } from '../../consts'; +import { decodeArrayListStr, parseNumberList, readFileAndParse } from '../util'; +const _ = require('lodash'); + +export interface DicGVGResourceBase { + // 唯一id + id: number; + // 名称 + name: string + // 农庄等级 + lv: number; + // 类型 1-农田 2-矿场 3-木堆 + type: number; + // 限制类型 + limitType: number; + // 限制参数 + limitParams: number[]; + // 农田产量加成 + fieldAdd: number; + // 矿山铁矿的含铁量 + mineralValue: { type: number, output: number, count: number }[]; + // 单木框产量 + woodOutput: number; + // 总数 + sum: number; +} + +type KeysEnum = { [P in keyof Required]: true }; +const DicGVGResourceBaseKeys: KeysEnum = { + id: true, name: true, lv: true, type: true, limitType: true, limitParams: true, fieldAdd: true, mineralValue: true, woodOutput: true, sum: true +} +export const dicGVGResourceBase = new Map(); // id => DicGVGResourceBase +export const dicGVGResourceBaseByType = new Map(); // type => [id] +export const dicGVGResourceBaseByLv = new Map(); // type + lv => [id] +export function loadGVGResourceBase() { + dicGVGResourceBase.clear(); + dicGVGResourceBaseByType.clear(); + dicGVGResourceBaseByLv.clear(); + + let arr = readFileAndParse(FILENAME.DIC_GVG_RESOURCE_BASE); + arr.forEach(o => { + o.limitParams = parseNumberList(o.limitParams); + if(o.type == 1) { // 农田 + o.fieldAdd = parseFloat(o.value.split('&')[1]); + if(isNaN(o.fieldAdd)) throw new Error('data table format wrong'); + } else if (o.type == 2) { // 矿山 + o.mineralValue = parseMineralValue(o.value); + } else if (o.type == 3) { + o.woodOutput = parseFloat(o.value.split('&')[1]); + if(isNaN(o.woodOutput)) throw new Error('data table format wrong'); + } + dicGVGResourceBase.set(o.id, _.pick(o, Object.keys(DicGVGResourceBaseKeys))); + dicGVGResourceBaseByLv.set(`${o.type}_${o.lv}`, o.id); + if(!dicGVGResourceBaseByType.has(o.type)) { + dicGVGResourceBaseByType.set(o.type, []); + } + dicGVGResourceBaseByType.get(o.type)?.push(o.id); + }); + arr = undefined; +} + +function parseMineralValue(str: string) { + let result = new Array<{ type: number, output: number, count: number }>(); + if (!str) return result; + let decodeArr = decodeArrayListStr(str); + for (let [type, output, count] of decodeArr) { + if (isNaN(parseInt(type)) || isNaN(parseInt(output)) || isNaN(parseInt(count))) { + throw new Error('data table format wrong'); + } + result.push({ type: parseInt(type), output: parseInt(output), count: parseInt(count) }); + } + return result +} \ No newline at end of file diff --git a/shared/pubUtils/dictionary/DicGVGTech.ts b/shared/pubUtils/dictionary/DicGVGTech.ts index 9ea7cb79a..152066acc 100644 --- a/shared/pubUtils/dictionary/DicGVGTech.ts +++ b/shared/pubUtils/dictionary/DicGVGTech.ts @@ -1,6 +1,6 @@ // GVG千机阁 import { FILENAME } from '../../consts' -import { decodeArrayListStr, readFileAndParse } from '../util' +import { decodeArrayListStr, parseNumberList, readFileAndParse } from '../util' export interface DicGVGTech { // 唯一id @@ -12,7 +12,7 @@ export interface DicGVGTech { // 效果类型 1-资源产量上传 2-征战中原提升 3-激战期提升 4-复活cd减少 5-箭塔 6-攻城车 type: number; // 资源提升量啊,复活cd减少量啊之类的值 - param: number; + param: number[]; // 点排序 nodeSort: number; // 联军等级限制 @@ -30,6 +30,7 @@ export function loadGVGTech() { let arr = readFileAndParse(FILENAME.DIC_GVG_TECH); arr.forEach(o => { o.prepositionId = parsePrePositionId(o.prepositionId); + o.param = parseNumberList(o.param) dicGVGTech.set(o.id, o); }); arr = undefined; diff --git a/shared/pubUtils/timeUtil.ts b/shared/pubUtils/timeUtil.ts index 167790d1f..0fce21cfc 100644 --- a/shared/pubUtils/timeUtil.ts +++ b/shared/pubUtils/timeUtil.ts @@ -1,3 +1,4 @@ +import moment = require('moment'); import { REFRESH_TIME, TIME_OUTPUT_TYPE, SHOP_REFRESH_TYPE } from '../consts'; import { isDebugTime } from './sdkUtil'; @@ -559,3 +560,12 @@ export function setWeek(d?: number) { week = d; if(d == 7) week = 0; } + + +export function getPastTime() { + return moment('2022-01-01').unix(); +} + +export function getFutureTime() { + return moment('2100-01-01').unix(); +} \ No newline at end of file diff --git a/shared/pubUtils/util.ts b/shared/pubUtils/util.ts index d8d8df711..2fcacb04c 100644 --- a/shared/pubUtils/util.ts +++ b/shared/pubUtils/util.ts @@ -821,4 +821,10 @@ export function getGachaRemainFloor(gachaId: number, userFloor: Floor[]) { export function isDevelopEnv(env: string) { const envs = ['development', 'monitor', 'alpha', 'dev']; return envs.indexOf(env) != -1; +} + +export function getArrayOfNumber(len: number) { + let arr: number[] = []; + for(let i = 1; i <= len; i++) arr.push(i); + return arr; } \ No newline at end of file diff --git a/shared/resource/jsons/dic_email_content.json b/shared/resource/jsons/dic_email_content.json index e339045a1..3e802dbae 100644 --- a/shared/resource/jsons/dic_email_content.json +++ b/shared/resource/jsons/dic_email_content.json @@ -264,5 +264,12 @@ "sendName": "您忠诚的小跟班", "content": "亲爱的主公,您被任命为联军%d的盟主", "time": 720 + }, + { + "id": 38, + "title": "&", + "sendName": "您忠诚的小跟班", + "content": "帮收", + "time": 720 } ] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_GVGItems.json b/shared/resource/jsons/dic_zyz_GVGItems.json index f8599eac3..36a28ca5f 100644 --- a/shared/resource/jsons/dic_zyz_GVGItems.json +++ b/shared/resource/jsons/dic_zyz_GVGItems.json @@ -10,7 +10,8 @@ "leagueConsume": "10&1", "value": 100, "reward": "31002&5000", - "leagueReward": "12&100" + "leagueReward": "12&100", + "ripeTime": 43200 }, { "id": 2, @@ -23,7 +24,8 @@ "leagueConsume": "10&1", "value": 150, "reward": "31002&5001", - "leagueReward": "12&150" + "leagueReward": "12&150", + "ripeTime": 36000 }, { "id": 3, @@ -36,7 +38,8 @@ "leagueConsume": "10&1", "value": 200, "reward": "31002&5002", - "leagueReward": "12&150" + "leagueReward": "12&150", + "ripeTime": 43200 }, { "id": 4, @@ -49,7 +52,8 @@ "leagueConsume": "10&2", "value": 15, "reward": "31002&5003", - "leagueReward": "12&200" + "leagueReward": "12&200", + "ripeTime": 0 }, { "id": 5, @@ -62,7 +66,8 @@ "leagueConsume": "10&2", "value": 20, "reward": "31002&5004", - "leagueReward": "12&300" + "leagueReward": "12&300", + "ripeTime": 0 }, { "id": 6, @@ -75,7 +80,8 @@ "leagueConsume": "10&2", "value": 30, "reward": "31002&5005", - "leagueReward": "12&300" + "leagueReward": "12&300", + "ripeTime": 0 }, { "id": 7, @@ -88,7 +94,8 @@ "leagueConsume": "10&2", "value": 25, "reward": "31002&5006", - "leagueReward": "12&200" + "leagueReward": "12&200", + "ripeTime": 0 }, { "id": 8, @@ -101,7 +108,8 @@ "leagueConsume": "10&2", "value": 30, "reward": "31002&5007", - "leagueReward": "12&300" + "leagueReward": "12&300", + "ripeTime": 0 }, { "id": 9, @@ -114,7 +122,8 @@ "leagueConsume": "10&2", "value": 40, "reward": "31002&5008", - "leagueReward": "12&300" + "leagueReward": "12&300", + "ripeTime": 0 }, { "id": 10, @@ -127,7 +136,8 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 }, { "id": 11, @@ -140,7 +150,8 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 }, { "id": 12, @@ -153,7 +164,8 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 }, { "id": 13, @@ -166,7 +178,8 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 }, { "id": 14, @@ -179,7 +192,8 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 }, { "id": 15, @@ -192,6 +206,7 @@ "leagueConsume": "&", "value": 0, "reward": "&", - "leagueReward": "&" + "leagueReward": "&", + "ripeTime": 0 } ] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_GVGResourceBase.json b/shared/resource/jsons/dic_zyz_GVGResourceBase.json index 5fa89472b..8a0c61634 100644 --- a/shared/resource/jsons/dic_zyz_GVGResourceBase.json +++ b/shared/resource/jsons/dic_zyz_GVGResourceBase.json @@ -7,7 +7,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "1&3", - "value": "1&0", + "value": "1&0", "sum": 100 }, { @@ -18,7 +18,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "1&5", - "value": "1&20", + "value": "1&20", "sum": 100 }, { @@ -29,7 +29,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "1&6", - "value": "1&30", + "value": "1&30", "sum": 100 }, { @@ -40,7 +40,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "2&6", - "value": "1&40", + "value": "1&40", "sum": 100 }, { @@ -51,7 +51,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "2&10", - "value": "1&50", + "value": "1&50", "sum": 100 }, { @@ -62,7 +62,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "2&12", - "value": "1&60", + "value": "1&60", "sum": 100 }, { @@ -73,7 +73,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "3&6", - "value": "1&70", + "value": "1&70", "sum": 100 }, { @@ -84,7 +84,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "3&10", - "value": "1&80", + "value": "1&80", "sum": 100 }, { @@ -95,7 +95,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "3&12", - "value": "1&90", + "value": "1&90", "sum": 100 }, { @@ -106,7 +106,7 @@ "limit": "X名武将X星", "limitType": 1, "limitParams": "5&12", - "value": "1&100", + "value": "1&100", "sum": 100 }, { @@ -117,7 +117,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&1", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -128,7 +128,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&2", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -139,7 +139,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&3", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -150,7 +150,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&4", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -161,7 +161,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&5", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -172,7 +172,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "1&6", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -183,7 +183,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "4&3", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -194,7 +194,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "4&4", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -205,7 +205,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "4&5", - "value": "1&100&10|2&150&5|3&150&10", + "value": "1&100&10|2&150&5|3&150&10", "sum": 200 }, { @@ -216,7 +216,7 @@ "limit": "X件装备X星", "limitType": 2, "limitParams": "4&6", - "value": "2&150&10|3&200&10|4&150&10", + "value": "2&150&10|3&200&10|4&150&10", "sum": 200 }, { @@ -227,7 +227,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 100000, - "value": "3&100", + "value": "3&100", "sum": 100 }, { @@ -238,7 +238,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 200000, - "value": "3&150", + "value": "3&150", "sum": 100 }, { @@ -249,7 +249,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 300000, - "value": "3&200", + "value": "3&200", "sum": 100 }, { @@ -260,7 +260,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 400000, - "value": "3&250", + "value": "3&250", "sum": 100 }, { @@ -271,7 +271,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 500000, - "value": "3&300", + "value": "3&300", "sum": 100 }, { @@ -282,7 +282,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 600000, - "value": "3&350", + "value": "3&350", "sum": 100 }, { @@ -293,7 +293,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 700000, - "value": "3&400", + "value": "3&400", "sum": 100 }, { @@ -304,7 +304,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 800000, - "value": "3&450", + "value": "3&450", "sum": 100 }, { @@ -315,7 +315,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 900000, - "value": "3&500", + "value": "3&500", "sum": 100 }, { @@ -326,7 +326,7 @@ "limit": "最强6人战力达XXXX", "limitType": 3, "limitParams": 1000000, - "value": "3&600", + "value": "3&600", "sum": 100 } ] \ No newline at end of file