From 712f2f52af03b60941d50c9e77cbec1529372243 Mon Sep 17 00:00:00 2001 From: dutlist <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Mon, 17 Jun 2024 03:31:49 +0200 Subject: [PATCH 1/9] inital dynamic worldState --- config.json.example | 1 + .../dynamic/worldStateController.ts | 22 +- src/models/worldStateModel.ts | 484 ++++++++++++++++++ src/services/configService.ts | 1 + src/services/worldStateService.ts | 17 + src/types/worldStateTypes.ts | 339 ++++++++++++ static/fixed_responses/worldState.json | 3 +- 7 files changed, 859 insertions(+), 8 deletions(-) create mode 100644 src/models/worldStateModel.ts create mode 100644 src/services/worldStateService.ts create mode 100644 src/types/worldStateTypes.ts diff --git a/config.json.example b/config.json.example index e874d450..fe7e4bb2 100644 --- a/config.json.example +++ b/config.json.example @@ -20,5 +20,6 @@ "unlockAllShipDecorations": true, "unlockAllFlavourItems": true, "unlockAllSkins": true, + "useStaticWorldState": true, "spoofMasteryRank": -1 } diff --git a/src/controllers/dynamic/worldStateController.ts b/src/controllers/dynamic/worldStateController.ts index 32cfdfa0..fa42d3bb 100644 --- a/src/controllers/dynamic/worldStateController.ts +++ b/src/controllers/dynamic/worldStateController.ts @@ -1,13 +1,23 @@ import { RequestHandler } from "express"; import worldState from "@/static/fixed_responses/worldState.json"; import buildConfig from "@/static/data/buildConfig.json"; +import { IWorldState } from "@/src/types/worldStateTypes"; +import { config } from "@/src/services/configService"; +import { getWorldState } from "@/src/services/worldStateService"; -const worldStateController: RequestHandler = (_req, res) => { - res.json({ - ...worldState, - BuildLabel: buildConfig.buildLabel, - Time: Math.round(Date.now() / 1000) - }); +const worldStateController: RequestHandler = async (_req, res) => { + let ws: IWorldState = {} + if(config.useStaticWorldState){ + ws = worldState; + ws.BuildLabel = buildConfig.buildLabel; + ws.Time = Math.round(Date.now() / 1000); + } else { + ws = (await getWorldState()).toJSON(); + ws.BuildLabel = buildConfig.buildLabel; + ws.Time = Math.round(Date.now() / 1000); + } + + res.json(ws); }; export { worldStateController }; diff --git a/src/models/worldStateModel.ts b/src/models/worldStateModel.ts new file mode 100644 index 00000000..535d82e7 --- /dev/null +++ b/src/models/worldStateModel.ts @@ -0,0 +1,484 @@ +import { model, Schema } from "mongoose"; +import { IEvent, IFlashSale, IJob, ILink, IMessage, IPVPChallengeInstance, ICategory, IPVPChallengeInstanceParam, IWorldState, IMission, IAlert, ICountedItems, IReward, IBaseWorldStateObject, ISortie, ILiteSortie, ISortieMission, ISyndicateMission, IActiveMission, IGlobalUpgrade, IInGameMarket, ILandingPage, IInvasion, IInvasionMissionInfo, INodeOverride, IVoidTrader, IVoidTraderItem, IVoidTraderScheduleInfo, IVoidStorm, IPrimeAccessAvailability, IDailyDeal, ILibraryInfo, IEndlessXpChoice, IFeaturedGuild, IActiveChallenge, ISeasonInfo } from "@/src/types/worldStateTypes"; + +const messageSchema = new Schema( + { + LanguageCode: String, + Message: String + }, + { _id: false } +); + +const linkSchema = new Schema( + { + LanguageCode: String, + Link: String + }, + { _id: false } +); + +const EventSchema = new Schema( + { + Messages: [messageSchema], + Prop: String, + Links: [linkSchema], + Date: Date, + Icon: String, + EventStartDate: Date, + EventEndDate: Date, + ImageUrl: String, + Priority: Boolean, + MobileOnly: Boolean, + HideEndDateModifier: Boolean, + Community: Boolean + } +); + + +EventSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const CountedItemsSchema = new Schema( + { + ItemType: String, + ItemCount: Number + }, + { _id: false } +); + +const RewardSchema = new Schema( + { + credits: Number, + xp: Number, + items: [String], + countedItems: [CountedItemsSchema], + }, + { _id: false } +) + +const MissionSchema = new Schema( + { + location: String, + missionType: String, + faction: String, + difficulty: Number, + missionReward: RewardSchema, + levelOverride: String, + enemySpec: String, + minEnemyLevel: Number, + maxEnemyLevel: Number, + descText: String, + maxWaveNum: Number, + exclusiveWeapon: String, + nightmare: Boolean, + archwingRequired: Boolean, + isSharkwing: Boolean, + advancedSpawners: [String], + requiredItems: [String], + consumeRequiredItems: Boolean, + vipAgent: Boolean, + leadersAlwaysAllowed: Boolean, + goalTag: String, + levelAuras: [String], + }, + { _id: false } +); + +const AlertSchema = new Schema({ + Activation: Date, + Expiry: Date, + MissionInfo: MissionSchema, + ForceUnlock: Boolean, + Tag: String +}); + +AlertSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const SortieMissionSchema = new Schema({ + missionType: String, + modifierType: String, + node: String, + tileset: String +}); + +SortieMissionSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const LiteSortieSchema = new Schema({ + Activation: Date, + Expiry: Date, + Reward: String, + Seed: String, + Boss: String, + Missions: [SortieMissionSchema] +}); + +LiteSortieSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const SortieSchema = new Schema({ + Activation: Date, + Expiry: Date, + Reward: String, + Seed: String, + Boss: String, + Variants: [SortieMissionSchema], + Twitter: Boolean +}); + +SortieSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const JobSchema = new Schema( + { + jobType: String, + rewards: String, + masteryReq: Number, + minEnemyLevel: Number, + maxEnemyLevel: Number, + xpAmounts: [Number], + endless: Boolean, + bonusXpMultiplier: Number, + locationTag: String, + isVault: Boolean + }, + { _id: false } +); + +const SyndicateMissionSchema = new Schema({ + Activation: Date, + Expiry: Date, + Tag: String, + Seed: String, + Nodes: [String], + Jobs: [JobSchema] +}); + +SyndicateMissionSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + + +const ActiveMissionSchema = new Schema({ + Activation: Date, + Expiry: Date, + Region: String, + Seed: String, + Node: String, + MissionType: String, + Modifier: String, + Hard: Boolean +}); + +ActiveMissionSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const GlobalUpgradeSchema = new Schema({ + Activation: Date, + Expiry: Date, + UpgradeType: String, + OperationType: String, + Value: String +}); + +GlobalUpgradeSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const FlashSaleSchema = new Schema( + { + TypeName: String, + StartDate: Date, + EndDate: Date, + ShowInMarket: Boolean, + HideFromMarket: Boolean, + SupporterPack: Boolean, + Discount: Number, + RegularOverride: Number, + PremiumOverride: Number, + BogoBuy: Number, + BogoGet: Number + }, + { _id: false } +); + +const ShopCategorySchema = new Schema( + { + CategoryName: String, + Name: String, + Icon: String, + AddToMenu: Boolean, + Items: [String] + }, + { _id: false } +); + +const LandingPageSchema = new Schema( + { + Categories: ShopCategorySchema + }, + { _id: false } +); + +const InGameMarketSchema = new Schema( + { + LandingPage: LandingPageSchema + }, + { _id: false } +) + +const InvasionMissionInfoSchema = new Schema( + { + seed: Number, + faction: String + }, + { _id: false } +) + +const InvasionSchema = new Schema({ + Activation: Date, + Faction: String, + DefenderFaction: String, + Node: String, + Count: Number, + Goal: Number, + LocTag: String, + Completed: Boolean, + ChainID: Schema.Types.ObjectId, + AttackerReward: RewardSchema, + AttackerMissionInfo: InvasionMissionInfoSchema, + DefenderReward: RewardSchema, + DefenderMissionInfo: InvasionMissionInfoSchema, +}); + +InvasionSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const NodeOverrideSchema = new Schema({ + Activation: Date, + Expiry: Date, + Node: String, + Faction: String, + CustomNpcEncounters: [String], + LevelOverride: String, +}); + +NodeOverrideSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const VoidTraderItemSchema = new Schema( + { + ItemType: String, + PrimePrice: Number, + RegularPrice: Number + }, + { _id: false } +); + +const VoidTraderScheduleInfoSchema = new Schema( + { + Expiry: Date, + PreviewHiddenUntil: Date, + FeaturedItem: String + }, + { _id: false } +); + +const VoidTraderSchema = new Schema( + { + Activation: Date, + Expiry: Date, + Character: String, + Node: String, + Completed: Boolean, + Manifest: [VoidTraderItemSchema], + EvergreenManifest: [VoidTraderItemSchema], + ScheduleInfo: [VoidTraderScheduleInfoSchema], + } +); + +VoidTraderSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const VoidStormSchema = new Schema({ + Activation: Date, + Expiry: Date, + Node: String, + ActiveMissionTier: String +}); + +VoidStormSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const PrimeAccessAvailabilitySchema = new Schema( + { + State: String + }, + { _id: false } +); + +const DailyDealSchema = new Schema( + { + Activation: Date, + Expiry: Date, + StoreItem: String, + Discount: Number, + OriginalPrice: Number, + SalePrice: Number, + AmountTotal: Number, + AmountSold: Number + }, + { _id: false } +); + +const LibraryInfoSchema = new Schema( + { + LastCompletedTargetType: String + }, + { _id: false } +); + +const PVPChallengeInstanceParam = new Schema( + { + n: String, + v: Number + }, + { _id: false } +); + +const PVPChallengeInstanceSchema = new Schema( + { + challengeTypeRefID: String, + startDate: Date, + endDate: Date, + params: [PVPChallengeInstanceParam], + isGenerated: Boolean, + PVPMode: String, + subChallenges: [Schema.Types.ObjectId], + Category: String + } +); + +PVPChallengeInstanceSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const EndlessXpChoiceSchema = new Schema( + { + Category: String, + Choices: [String] + }, + { _id: false } +); + +const FeaturedGuildShema = new Schema({ + Name: String, + Tier: Number, + Emblem: Boolean, + OriginalPlatform: Number, + AllianceId: Schema.Types.ObjectId +}); + +FeaturedGuildShema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const ActiveChallengeSchema = new Schema({ + Activation: Date, + Expiry: Date, + Daily: Boolean, + Challenge: String +}); + +FeaturedGuildShema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject._id = { $oid: returnedObject._id.toString()}; + } +}); + +const SeasonInfoSchema = new Schema( + { + AffiliationTag: String, + Season: Number, + Phase: Number, + Params: String, + ActiveChallenges: [ActiveChallengeSchema] + }, { _id: false } +); + +const WorldStateSchema = new Schema( + { + Events: [EventSchema], + // Goals: [GoalSchema], + Alerts: [AlertSchema], + Sorties: [SortieSchema], + LiteSorties: [LiteSortieSchema], + SyndicateMissions: [SyndicateMissionSchema], + ActiveMissions: [ActiveMissionSchema], + GlobalUpgrades: [GlobalUpgradeSchema], + FlashSales: [FlashSaleSchema], + InGameMarket: InGameMarketSchema, + Invasions: [InvasionSchema], + NodeOverrides: [NodeOverrideSchema], + VoidTraders: [VoidTraderSchema], + PrimeVaultTraders: [VoidTraderSchema], + VoidStorms: [VoidStormSchema], + PrimeAccessAvailability: PrimeAccessAvailabilitySchema, + DailyDeals: [DailyDealSchema], + LibraryInfo: LibraryInfoSchema, + PVPChallengeInstances: [PVPChallengeInstanceSchema], + ProjectPct: [Number], + EndlessXpChoices: [EndlessXpChoiceSchema], + FeaturedGuilds: [FeaturedGuildShema], + SeasonInfo: SeasonInfoSchema, + Tmp: String + } +); + +WorldStateSchema.set("toJSON", { + transform(_document, returnedObject) { + delete returnedObject.__v; + delete returnedObject._id; + } +}); + +export const WorldState = model("WorldState", WorldStateSchema); \ No newline at end of file diff --git a/src/services/configService.ts b/src/services/configService.ts index 283b31bf..f8ea42c8 100644 --- a/src/services/configService.ts +++ b/src/services/configService.ts @@ -37,6 +37,7 @@ interface IConfig { unlockAllFlavourItems?: boolean; unlockAllSkins?: boolean; spoofMasteryRank?: number; + useStaticWorldState?: boolean; } interface ILoggerConfig { diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts new file mode 100644 index 00000000..dd3cdbca --- /dev/null +++ b/src/services/worldStateService.ts @@ -0,0 +1,17 @@ +import { WorldState } from "@/src/models/worldStateModel"; +import buildConfig from "@/static/data/buildConfig.json"; + +export const createWorldState = async () => { + const worldState = new WorldState() + await worldState.save(); + return worldState; +} + +export const getWorldState = async () => { + let ws = await WorldState.findOne(); + if (!ws) { + ws = await createWorldState(); + } + + return ws; +}; \ No newline at end of file diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts new file mode 100644 index 00000000..b2cb5219 --- /dev/null +++ b/src/types/worldStateTypes.ts @@ -0,0 +1,339 @@ +import { IMongoDate, IOid } from "@/src/types/commonTypes"; + +export interface IMessage { + LanguageCode?: string; + Message: string; +}; + +export interface ILink { + LanguageCode?: string; + Link: string; +}; + +export interface IBaseWorldStateObject { + Activation: IMongoDate; + Expiry: IMongoDate; + _id?: IOid +} + +export interface IReward { + credits?: number; + xp?: number; + items?: string[]; + countedItems?: ICountedItems[]; +} + +export interface ICountedItems { + ItemType: string; + ItemCount: number; +} + +export interface IMission { + location: string; + missionType: string; + faction: string; + difficulty: number; + missionReward: IReward; + levelOverride: string; + enemySpec: string; + minEnemyLevel: number; + maxEnemyLevel: number; + descText: string; + maxWaveNum?: number; + exclusiveWeapon?: string; + nightmare?: boolean; + archwingRequired?: boolean; + isSharkwing?: boolean; + advancedSpawners?: string[]; + requiredItems?: string[]; + consumeRequiredItems?: boolean; + vipAgent?: boolean; + leadersAlwaysAllowed?: boolean; + goalTag?: string; + levelAuras?: string[]; +} + +export interface IEvent { + Messages: IMessage[]; + Prop?: string; + ImageUrl?: string; + Links?: ILink[]; + Icon?: string; + Community?: boolean; + Priority?: boolean; + EventStartDate?: IMongoDate; + EventEndDate?: IMongoDate; + MobileOnly?: boolean; + HideEndDateModifier?: boolean; + Date?: IMongoDate; + _id?: IOid; +}; + +export interface IGoal extends IBaseWorldStateObject { + Node: string; + ScoreVar: string; + ScoreLocTag: string; + Count: number; + HealthPct: number; + Regions: number[]; + Desc: string; + ToolTip: string; + OptionalInMission: boolean; + Tag: string; + UpgradeIds: IOid[]; + Personal: boolean; + Community: boolean; + Goal: number; + Reward: IReward; + InterimGoals: number[]; + InterimRewards: IReward[]; +}; + +export interface IAlert extends IBaseWorldStateObject { + MissionInfo: IMission; + ForceUnlock: boolean; + Tag: string; +} + +export interface ISortieMission { + missionType: string; + modifierType?: string; + node: string; + tileset?: string +} + +export interface ISortie extends Omit { + // ExtraDrops: []; Unknown + Variants: ISortieMission[]; + Twitter: boolean; +} + +export interface ILiteSortie extends IBaseWorldStateObject { + Reward: string; + Seed: number; + Boss: String; + Missions: ISortieMission[] +} + +export interface IJob { + jobType?: string; + rewards: string; + masteryReq: number; + minEnemyLevel: number; + maxEnemyLevel: number; + endless?: boolean; + bonusXpMultiplier?: number; + xpAmounts: number[]; + locationTag?: string; + isVault?: boolean; +} +export interface ISyndicateMission extends IBaseWorldStateObject { + Tag: string; + Seed: number; + Nodes: string[]; + Jobs?: IJob[]; +} + +export interface IActiveMission extends IBaseWorldStateObject { + Region: number; + Seed: number; + Node: string; + MissionType: string; + Modifier?: string; + Hard?: boolean; +} + +export interface IGlobalUpgrade extends IBaseWorldStateObject { + UpgradeType: string; + OperationType: string; + Value: string; +} + +export interface IFlashSale { + StartDate: IMongoDate; + EndDate: IMongoDate; + TypeName: string; + ShowInMarket: boolean; + HideFromMarket: boolean; + SupporterPack: boolean; + Discount: number; + RegularOverride: number; + PremiumOverride: number; + BogoBuy: number; + BogoGet: number; +} + + +export interface IInGameMarket { + LandingPage: ILandingPage; +} + +export interface ILandingPage { + Categories: ICategory[]; +} + +export interface ICategory { + CategoryName: string; + Name: string; + Icon: string; + AddToMenu?: boolean; + Items: string[]; +} + +export interface IInvasion extends Omit { + Faction: string; + DefenderFaction: string; + Node: string; + Count: number; + Goal: number; + LocTag: string; + Completed: boolean; + ChainID: IOid; + AttackerReward: IReward; + AttackerMissionInfo: IInvasionMissionInfo; + DefenderReward: IReward; + DefenderMissionInfo: IInvasionMissionInfo; +} + +export interface IInvasionMissionInfo { + seed: number; + faction: string; +} + +export interface INodeOverride { + Activation?: IMongoDate; + Expiry?: IMongoDate; + Node: string, + Faction?: string, + CustomNpcEncounters?: string[]; + LevelOverride?: string; +} + +export interface IVoidTrader extends IBaseWorldStateObject { + Character?: string; + Node: string; + Completed?: boolean; + Manifest?: IVoidTraderItem[]; + EvergreenManifest?: IVoidTraderItem[]; + ScheduleInfo?: IVoidTraderScheduleInfo[]; +} + +export interface IVoidTraderItem { + ItemType: string; + PrimePrice?: number; + RegularPrice?: number; +} + +export interface IVoidTraderScheduleInfo extends Omit { + PreviewHiddenUntil?: IMongoDate; + FeaturedItem?: string; +} + + +export interface IVoidStorm extends IBaseWorldStateObject { + Node: string; + ActiveMissionTier: string; +} + +export interface IPrimeAccessAvailability { + State: string; +} + +export interface IDailyDeal extends Omit { + StoreItem: string; + Discount: number; + OriginalPrice: number; + SalePrice: number; + AmountTotal: number; + AmountSold: number; +} + +export interface ILibraryInfo { + LastCompletedTargetType: string; +} + +export interface IPVPChallengeInstance { + challengeTypeRefID: string; + startDate: IMongoDate; + endDate: IMongoDate; + params: IPVPChallengeInstanceParam[]; + isGenerated: boolean; + PVPMode: string; + subChallenges: IOid[]; + Category: string; +} + +export interface IPVPChallengeInstanceParam { + n: string; + v: number; +} + + +export interface IEndlessXpChoice { + Category: string; + Choices: string[]; +} + +export interface IFeaturedGuild { + _id: IOid; + Name: string; + Tier: number; + Emblem: boolean; + OriginalPlatform: number; + AllianceId?: IOid; +} + +export interface ISeasonInfo extends Omit { + AffiliationTag: string; + Season: number; + Phase: number; + Params: string; + ActiveChallenges: IActiveChallenge[]; +} + +export interface IActiveChallenge extends IBaseWorldStateObject { + Daily?: boolean; + Challenge: string; +} + +export interface IWorldState { + WorldSeed?: string; + Version?: number; + MobileVersion?: string; + BuildLabel?: string; + Time?: number; + Events?: IEvent[]; + Goals?: IGoal[]; + Alerts?: IAlert[]; + Sorties?: ISortie[]; + LiteSorties?: ILiteSortie[]; + SyndicateMissions?: ISyndicateMission[]; + ActiveMissions?: IActiveMission[]; + GlobalUpgrades?: IGlobalUpgrade[]; + FlashSales?: IFlashSale[]; + InGameMarket?: IInGameMarket; + Invasions?: IInvasion[]; + NodeOverrides?: INodeOverride[]; + VoidTraders?: IVoidTrader[]; + PrimeVaultTraders?: IVoidTrader[]; + VoidStorms?: IVoidStorm[]; + PrimeAccessAvailability?: IPrimeAccessAvailability; + DailyDeals?: IDailyDeal[]; + LibraryInfo?: ILibraryInfo; + PVPChallengeInstances?: IPVPChallengeInstance[]; + ProjectPct?: number[]; + EndlessXpChoices?: IEndlessXpChoice[]; + ForceLogoutVersion?: number; + FeaturedGuilds?: IFeaturedGuild[]; + SeasonInfo?: ISeasonInfo; + Tmp?: string; + + // Unkown + // HubEvent?: []; + // PersistentEnemies?: []; + // PVPAlternativeModes?: []; + // PVPActiveTournaments?: []; + // ConstructionProjects?: []; + // TwitchPromos?: []; + // ExperimentRecommended?: []; +} \ No newline at end of file diff --git a/static/fixed_responses/worldState.json b/static/fixed_responses/worldState.json index aa30c06b..d42de99b 100644 --- a/static/fixed_responses/worldState.json +++ b/static/fixed_responses/worldState.json @@ -889,8 +889,7 @@ "Expiry": { "$date": { "$numberLong": "1717696800000" } }, "PreviewHiddenUntil": { "$date": { "$numberLong": "1714068000000" } }, "FeaturedItem": "/Lotus/Types/StoreItems/Packages/MegaPrimeVault/MPVEquinoxWukongPrimeDualPack" - }, - { "Expiry": { "$date": { "$numberLong": "1720116000000" } }, "PreviewHiddenUntil": { "$date": { "$numberLong": "1716487200000" } } } + } ] } ], From 306c1683db8442a7e35f926c60cbf4984b6c4cfc Mon Sep 17 00:00:00 2001 From: AMelonInsideLemon Date: Mon, 17 Jun 2024 01:32:16 +0000 Subject: [PATCH 2/9] Apply prettier changes --- .../dynamic/worldStateController.ts | 6 +- src/models/worldStateModel.ts | 218 ++++++++++-------- src/services/worldStateService.ts | 6 +- src/types/worldStateTypes.ts | 25 +- 4 files changed, 140 insertions(+), 115 deletions(-) diff --git a/src/controllers/dynamic/worldStateController.ts b/src/controllers/dynamic/worldStateController.ts index fa42d3bb..428c9834 100644 --- a/src/controllers/dynamic/worldStateController.ts +++ b/src/controllers/dynamic/worldStateController.ts @@ -6,8 +6,8 @@ import { config } from "@/src/services/configService"; import { getWorldState } from "@/src/services/worldStateService"; const worldStateController: RequestHandler = async (_req, res) => { - let ws: IWorldState = {} - if(config.useStaticWorldState){ + let ws: IWorldState = {}; + if (config.useStaticWorldState) { ws = worldState; ws.BuildLabel = buildConfig.buildLabel; ws.Time = Math.round(Date.now() / 1000); @@ -16,7 +16,7 @@ const worldStateController: RequestHandler = async (_req, res) => { ws.BuildLabel = buildConfig.buildLabel; ws.Time = Math.round(Date.now() / 1000); } - + res.json(ws); }; diff --git a/src/models/worldStateModel.ts b/src/models/worldStateModel.ts index 535d82e7..21fb345a 100644 --- a/src/models/worldStateModel.ts +++ b/src/models/worldStateModel.ts @@ -1,5 +1,42 @@ import { model, Schema } from "mongoose"; -import { IEvent, IFlashSale, IJob, ILink, IMessage, IPVPChallengeInstance, ICategory, IPVPChallengeInstanceParam, IWorldState, IMission, IAlert, ICountedItems, IReward, IBaseWorldStateObject, ISortie, ILiteSortie, ISortieMission, ISyndicateMission, IActiveMission, IGlobalUpgrade, IInGameMarket, ILandingPage, IInvasion, IInvasionMissionInfo, INodeOverride, IVoidTrader, IVoidTraderItem, IVoidTraderScheduleInfo, IVoidStorm, IPrimeAccessAvailability, IDailyDeal, ILibraryInfo, IEndlessXpChoice, IFeaturedGuild, IActiveChallenge, ISeasonInfo } from "@/src/types/worldStateTypes"; +import { + IEvent, + IFlashSale, + IJob, + ILink, + IMessage, + IPVPChallengeInstance, + ICategory, + IPVPChallengeInstanceParam, + IWorldState, + IMission, + IAlert, + ICountedItems, + IReward, + IBaseWorldStateObject, + ISortie, + ILiteSortie, + ISortieMission, + ISyndicateMission, + IActiveMission, + IGlobalUpgrade, + IInGameMarket, + ILandingPage, + IInvasion, + IInvasionMissionInfo, + INodeOverride, + IVoidTrader, + IVoidTraderItem, + IVoidTraderScheduleInfo, + IVoidStorm, + IPrimeAccessAvailability, + IDailyDeal, + ILibraryInfo, + IEndlessXpChoice, + IFeaturedGuild, + IActiveChallenge, + ISeasonInfo +} from "@/src/types/worldStateTypes"; const messageSchema = new Schema( { @@ -17,27 +54,24 @@ const linkSchema = new Schema( { _id: false } ); -const EventSchema = new Schema( - { - Messages: [messageSchema], - Prop: String, - Links: [linkSchema], - Date: Date, - Icon: String, - EventStartDate: Date, - EventEndDate: Date, - ImageUrl: String, - Priority: Boolean, - MobileOnly: Boolean, - HideEndDateModifier: Boolean, - Community: Boolean - } -); - +const EventSchema = new Schema({ + Messages: [messageSchema], + Prop: String, + Links: [linkSchema], + Date: Date, + Icon: String, + EventStartDate: Date, + EventEndDate: Date, + ImageUrl: String, + Priority: Boolean, + MobileOnly: Boolean, + HideEndDateModifier: Boolean, + Community: Boolean +}); EventSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -54,10 +88,10 @@ const RewardSchema = new Schema( credits: Number, xp: Number, items: [String], - countedItems: [CountedItemsSchema], + countedItems: [CountedItemsSchema] }, { _id: false } -) +); const MissionSchema = new Schema( { @@ -82,7 +116,7 @@ const MissionSchema = new Schema( vipAgent: Boolean, leadersAlwaysAllowed: Boolean, goalTag: String, - levelAuras: [String], + levelAuras: [String] }, { _id: false } ); @@ -97,7 +131,7 @@ const AlertSchema = new Schema({ AlertSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -110,7 +144,7 @@ const SortieMissionSchema = new Schema({ SortieMissionSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -125,7 +159,7 @@ const LiteSortieSchema = new Schema({ LiteSortieSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -141,7 +175,7 @@ const SortieSchema = new Schema({ SortieSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -172,11 +206,10 @@ const SyndicateMissionSchema = new Schema({ SyndicateMissionSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); - const ActiveMissionSchema = new Schema({ Activation: Date, Expiry: Date, @@ -190,7 +223,7 @@ const ActiveMissionSchema = new Schema({ ActiveMissionSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -204,7 +237,7 @@ const GlobalUpgradeSchema = new Schema({ GlobalUpgradeSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -248,7 +281,7 @@ const InGameMarketSchema = new Schema( LandingPage: LandingPageSchema }, { _id: false } -) +); const InvasionMissionInfoSchema = new Schema( { @@ -256,7 +289,7 @@ const InvasionMissionInfoSchema = new Schema( faction: String }, { _id: false } -) +); const InvasionSchema = new Schema({ Activation: Date, @@ -271,12 +304,12 @@ const InvasionSchema = new Schema({ AttackerReward: RewardSchema, AttackerMissionInfo: InvasionMissionInfoSchema, DefenderReward: RewardSchema, - DefenderMissionInfo: InvasionMissionInfoSchema, + DefenderMissionInfo: InvasionMissionInfoSchema }); InvasionSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -286,12 +319,12 @@ const NodeOverrideSchema = new Schema({ Node: String, Faction: String, CustomNpcEncounters: [String], - LevelOverride: String, + LevelOverride: String }); NodeOverrideSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -313,22 +346,20 @@ const VoidTraderScheduleInfoSchema = new Schema( { _id: false } ); -const VoidTraderSchema = new Schema( - { - Activation: Date, - Expiry: Date, - Character: String, - Node: String, - Completed: Boolean, - Manifest: [VoidTraderItemSchema], - EvergreenManifest: [VoidTraderItemSchema], - ScheduleInfo: [VoidTraderScheduleInfoSchema], - } -); +const VoidTraderSchema = new Schema({ + Activation: Date, + Expiry: Date, + Character: String, + Node: String, + Completed: Boolean, + Manifest: [VoidTraderItemSchema], + EvergreenManifest: [VoidTraderItemSchema], + ScheduleInfo: [VoidTraderScheduleInfoSchema] +}); VoidTraderSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -341,7 +372,7 @@ const VoidStormSchema = new Schema({ VoidStormSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -381,22 +412,20 @@ const PVPChallengeInstanceParam = new Schema( { _id: false } ); -const PVPChallengeInstanceSchema = new Schema( - { - challengeTypeRefID: String, - startDate: Date, - endDate: Date, - params: [PVPChallengeInstanceParam], - isGenerated: Boolean, - PVPMode: String, - subChallenges: [Schema.Types.ObjectId], - Category: String - } -); +const PVPChallengeInstanceSchema = new Schema({ + challengeTypeRefID: String, + startDate: Date, + endDate: Date, + params: [PVPChallengeInstanceParam], + isGenerated: Boolean, + PVPMode: String, + subChallenges: [Schema.Types.ObjectId], + Category: String +}); PVPChallengeInstanceSchema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -418,7 +447,7 @@ const FeaturedGuildShema = new Schema({ FeaturedGuildShema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -431,7 +460,7 @@ const ActiveChallengeSchema = new Schema({ FeaturedGuildShema.set("toJSON", { transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString()}; + returnedObject._id = { $oid: returnedObject._id.toString() }; } }); @@ -442,37 +471,36 @@ const SeasonInfoSchema = new Schema( Phase: Number, Params: String, ActiveChallenges: [ActiveChallengeSchema] - }, { _id: false } + }, + { _id: false } ); -const WorldStateSchema = new Schema( - { - Events: [EventSchema], - // Goals: [GoalSchema], - Alerts: [AlertSchema], - Sorties: [SortieSchema], - LiteSorties: [LiteSortieSchema], - SyndicateMissions: [SyndicateMissionSchema], - ActiveMissions: [ActiveMissionSchema], - GlobalUpgrades: [GlobalUpgradeSchema], - FlashSales: [FlashSaleSchema], - InGameMarket: InGameMarketSchema, - Invasions: [InvasionSchema], - NodeOverrides: [NodeOverrideSchema], - VoidTraders: [VoidTraderSchema], - PrimeVaultTraders: [VoidTraderSchema], - VoidStorms: [VoidStormSchema], - PrimeAccessAvailability: PrimeAccessAvailabilitySchema, - DailyDeals: [DailyDealSchema], - LibraryInfo: LibraryInfoSchema, - PVPChallengeInstances: [PVPChallengeInstanceSchema], - ProjectPct: [Number], - EndlessXpChoices: [EndlessXpChoiceSchema], - FeaturedGuilds: [FeaturedGuildShema], - SeasonInfo: SeasonInfoSchema, - Tmp: String - } -); +const WorldStateSchema = new Schema({ + Events: [EventSchema], + // Goals: [GoalSchema], + Alerts: [AlertSchema], + Sorties: [SortieSchema], + LiteSorties: [LiteSortieSchema], + SyndicateMissions: [SyndicateMissionSchema], + ActiveMissions: [ActiveMissionSchema], + GlobalUpgrades: [GlobalUpgradeSchema], + FlashSales: [FlashSaleSchema], + InGameMarket: InGameMarketSchema, + Invasions: [InvasionSchema], + NodeOverrides: [NodeOverrideSchema], + VoidTraders: [VoidTraderSchema], + PrimeVaultTraders: [VoidTraderSchema], + VoidStorms: [VoidStormSchema], + PrimeAccessAvailability: PrimeAccessAvailabilitySchema, + DailyDeals: [DailyDealSchema], + LibraryInfo: LibraryInfoSchema, + PVPChallengeInstances: [PVPChallengeInstanceSchema], + ProjectPct: [Number], + EndlessXpChoices: [EndlessXpChoiceSchema], + FeaturedGuilds: [FeaturedGuildShema], + SeasonInfo: SeasonInfoSchema, + Tmp: String +}); WorldStateSchema.set("toJSON", { transform(_document, returnedObject) { @@ -481,4 +509,4 @@ WorldStateSchema.set("toJSON", { } }); -export const WorldState = model("WorldState", WorldStateSchema); \ No newline at end of file +export const WorldState = model("WorldState", WorldStateSchema); diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index dd3cdbca..4e100c13 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -2,10 +2,10 @@ import { WorldState } from "@/src/models/worldStateModel"; import buildConfig from "@/static/data/buildConfig.json"; export const createWorldState = async () => { - const worldState = new WorldState() + const worldState = new WorldState(); await worldState.save(); return worldState; -} +}; export const getWorldState = async () => { let ws = await WorldState.findOne(); @@ -14,4 +14,4 @@ export const getWorldState = async () => { } return ws; -}; \ No newline at end of file +}; diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index b2cb5219..36f12d13 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -3,17 +3,17 @@ import { IMongoDate, IOid } from "@/src/types/commonTypes"; export interface IMessage { LanguageCode?: string; Message: string; -}; +} export interface ILink { LanguageCode?: string; Link: string; -}; +} export interface IBaseWorldStateObject { Activation: IMongoDate; Expiry: IMongoDate; - _id?: IOid + _id?: IOid; } export interface IReward { @@ -67,7 +67,7 @@ export interface IEvent { HideEndDateModifier?: boolean; Date?: IMongoDate; _id?: IOid; -}; +} export interface IGoal extends IBaseWorldStateObject { Node: string; @@ -87,7 +87,7 @@ export interface IGoal extends IBaseWorldStateObject { Reward: IReward; InterimGoals: number[]; InterimRewards: IReward[]; -}; +} export interface IAlert extends IBaseWorldStateObject { MissionInfo: IMission; @@ -99,7 +99,7 @@ export interface ISortieMission { missionType: string; modifierType?: string; node: string; - tileset?: string + tileset?: string; } export interface ISortie extends Omit { @@ -112,7 +112,7 @@ export interface ILiteSortie extends IBaseWorldStateObject { Reward: string; Seed: number; Boss: String; - Missions: ISortieMission[] + Missions: ISortieMission[]; } export interface IJob { @@ -163,7 +163,6 @@ export interface IFlashSale { BogoGet: number; } - export interface IInGameMarket { LandingPage: ILandingPage; } @@ -203,8 +202,8 @@ export interface IInvasionMissionInfo { export interface INodeOverride { Activation?: IMongoDate; Expiry?: IMongoDate; - Node: string, - Faction?: string, + Node: string; + Faction?: string; CustomNpcEncounters?: string[]; LevelOverride?: string; } @@ -229,7 +228,6 @@ export interface IVoidTraderScheduleInfo extends Omit Date: Mon, 17 Jun 2024 03:35:44 +0200 Subject: [PATCH 3/9] fix eslint --- src/types/worldStateTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index b2cb5219..21f15615 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -111,7 +111,7 @@ export interface ISortie extends Omit { export interface ILiteSortie extends IBaseWorldStateObject { Reward: string; Seed: number; - Boss: String; + Boss: string; Missions: ISortieMission[] } From c735b997520dbb4b47f0feb71b96dbf11f0229f3 Mon Sep 17 00:00:00 2001 From: dutlist <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Mon, 17 Jun 2024 03:38:10 +0200 Subject: [PATCH 4/9] fix eslint? --- src/services/worldStateService.ts | 1 - src/types/worldStateTypes.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index 4e100c13..c236be3b 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -1,5 +1,4 @@ import { WorldState } from "@/src/models/worldStateModel"; -import buildConfig from "@/static/data/buildConfig.json"; export const createWorldState = async () => { const worldState = new WorldState(); diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index 36f12d13..c8d6b8ac 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -111,7 +111,7 @@ export interface ISortie extends Omit { export interface ILiteSortie extends IBaseWorldStateObject { Reward: string; Seed: number; - Boss: String; + Boss: string; Missions: ISortieMission[]; } From 7e8485df3b4c80dc30ab5bd7b3cd7ae052c6ced8 Mon Sep 17 00:00:00 2001 From: dutlist <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Mon, 17 Jun 2024 03:40:33 +0200 Subject: [PATCH 5/9] fix eslit 3 attempt of fixing eslit --- src/models/worldStateModel.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/worldStateModel.ts b/src/models/worldStateModel.ts index 21fb345a..68e1d018 100644 --- a/src/models/worldStateModel.ts +++ b/src/models/worldStateModel.ts @@ -13,7 +13,6 @@ import { IAlert, ICountedItems, IReward, - IBaseWorldStateObject, ISortie, ILiteSortie, ISortieMission, From 554a1a6b227b34ec936fbad417622df8676e7532 Mon Sep 17 00:00:00 2001 From: dutlist <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Sat, 22 Jun 2024 23:56:30 +0200 Subject: [PATCH 6/9] Autoupdate, Syndicates, void fisures, sotries sotries needs more data(currently all worldstate data is taken from wfcd worldstate data package) --- config.json.example | 2 +- src/app.ts | 2 + src/constants/worldStateConstants.ts | 3659 +++++++++++++++++ .../dynamic/worldStateController.ts | 5 +- src/helpers/general.ts | 9 + src/helpers/worldstateHelpers.ts | 30 + src/models/worldStateModel.ts | 143 +- src/services/worldStateService.ts | 607 ++- src/types/worldStateTypes.ts | 33 +- static/webui/index.html | 4 + 10 files changed, 4422 insertions(+), 72 deletions(-) create mode 100644 src/constants/worldStateConstants.ts create mode 100644 src/helpers/worldstateHelpers.ts diff --git a/config.json.example b/config.json.example index 4b9375f6..220f522e 100644 --- a/config.json.example +++ b/config.json.example @@ -20,7 +20,7 @@ "unlockAllShipDecorations": true, "unlockAllFlavourItems": true, "unlockAllSkins": true, - "useStaticWorldState": true, "universalPolarityEverywhere": true, + "useStaticWorldState": true, "spoofMasteryRank": -1 } diff --git a/src/app.ts b/src/app.ts index 514d4336..26a9ac87 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,9 +15,11 @@ import { statsRouter } from "@/src/routes/stats"; import { webuiRouter } from "@/src/routes/webui"; import { connectDatabase } from "@/src/services/mongoService"; import { registerLogFileCreationListener } from "@/src/utils/logger"; +import { worldStateRunner } from "@/src/services/worldStateService"; void registerLogFileCreationListener(); void connectDatabase(); +void worldStateRunner(); const app = express(); diff --git a/src/constants/worldStateConstants.ts b/src/constants/worldStateConstants.ts new file mode 100644 index 00000000..0fe0adcf --- /dev/null +++ b/src/constants/worldStateConstants.ts @@ -0,0 +1,3659 @@ +export const SortiesMissionTypes = [ + "MT_ARTIFACT", + "MT_ASSAULT", + "MT_ASSASSINATION", + "MT_DEFENSE", + "MT_DISRUPTION", + "MT_EVACUATION", + "MT_EXCAVATE", + "MT_EXTERMINATION", + "MT_HIVE", + "MT_INTEL", + "MT_LANDSCAPE", + "MT_MOBILE_DEFENSE", + "MT_RESCUE", + "MT_RETRIEVAL", + "MT_SABOTAGE", + "MT_SECTOR", + "MT_SURVIVAL", + "MT_TERRITORY" +]; + +export const voidTiers = ["VoidT1", "VoidT2", "VoidT3", "VoidT4", "VoidT5", "VoidT6"]; + +export const modifierTypes = [ + "SORTIE_MODIFIER_LOW_ENERGY", + "SORTIE_MODIFIER_IMPACT", + "SORTIE_MODIFIER_SLASH", + "SORTIE_MODIFIER_PUNCTURE", + "SORTIE_MODIFIER_EXIMUS", + "SORTIE_MODIFIER_MAGNETIC", + "SORTIE_MODIFIER_CORROSIVE", + "SORTIE_MODIFIER_VIRAL", + "SORTIE_MODIFIER_ELECTRICITY", + "SORTIE_MODIFIER_RADIATION", + "SORTIE_MODIFIER_GAS", + "SORTIE_MODIFIER_FIRE", + "SORTIE_MODIFIER_EXPLOSION", + "SORTIE_MODIFIER_FREEZE", + "SORTIE_MODIFIER_TOXIN", + "SORTIE_MODIFIER_POISON", + "SORTIE_MODIFIER_HAZARD_RADIATION", + "SORTIE_MODIFIER_HAZARD_MAGNETIC", + "SORTIE_MODIFIER_HAZARD_FOG", + "SORTIE_MODIFIER_HAZARD_FIRE", + "SORTIE_MODIFIER_HAZARD_ICE", + "SORTIE_MODIFIER_HAZARD_COLD", + "SORTIE_MODIFIER_ARMOR", + "SORTIE_MODIFIER_SHIELDS", + "SORTIE_MODIFIER_SECONDARY_ONLY", + "SORTIE_MODIFIER_SHOTGUN_ONLY", + "SORTIE_MODIFIER_SNIPER_ONLY", + "SORTIE_MODIFIER_RIFLE_ONLY", + "SORTIE_MODIFIER_MELEE_ONLY", + "SORTIE_MODIFIER_BOW_ONLY" +]; + +export const liteSortiesBoss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_PAAZUL"]; + +/* data from wfcd worldstate-data sucks, + missing bosses: + SORTIE_BOSS_CORRUPTED_VOR + also missing tilesets and void missions + */ +export const endStates = [ + { + bossName: "SORTIE_BOSS_VOR", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_HEK", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_RUK", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_KELA", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_SURVIVAL", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_KRIL", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_TYL", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_JACKAL", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_ALAD", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_AMBULAS", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXTERMINATION", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_HYENA", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_RESCUE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_SABOTAGE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_INTEL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_NEF", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_RAPTOR", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_PHORID", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "Hive", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_LEPHANTIS", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_INTEL", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_INTEL", + "MT_MOBILE_DEFENSE", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "Hive", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + }, + { + bossName: "SORTIE_BOSS_INFALAD", + regions: [ + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_DEFENSE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_TERRITORY", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_EXCAVATE", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_SURVIVAL", + "Hive", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_SURVIVAL", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_INTEL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_RETRIEVAL", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_EXTERMINATION", + "MT_EXCAVATE", + "MT_RESCUE", + "MT_SABOTAGE", + "MT_INTEL", + "MT_DEFENSE", + "MT_MOBILE_DEFENSE", + "MT_TERRITORY", + "MT_EXTERMINATION", + "MT_ASSASSINATION" + ] + }, + { + missions: [ + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_EXTERMINATION", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_DEFENSE", + "MT_ASSASSINATION" + ] + } + ] + } +]; + +export const validFisureMissionIndex = [ + 1, // Exterminate + 2, // Survival + 3, // Rescue + 4, // Sabotage + 5, // Capture + 7, // Spy + 8, // Defense + 9, // Mobile Defense + 13, // Interception + 15, // Hive + 17, // Excavation + 26, // Assault + 33, // Disruption + 34, // Void Flood + 35, // Void Cascade + 38 // Alchemy +]; + +export const omniaNodes = ["SolNode309", "SolNode310", "SolNode230", "SolNode718", "SolNode232", "SolNode717"]; + +export const voidFisuresMissionTypes: { [key: number]: string } = { + 1: "MT_EXTERMINATION", + 2: "MT_SURVIVAL", + 3: "MT_RESCUE", + 4: "MT_SABOTAGE", + 5: "MT_CAPTURE", + 7: "MT_INTEL", + 8: "MT_DEFENSE", + 9: "MT_MOBILE_DEFENSE", + 13: "MT_TERRITORY", + 15: "MT_HIVE", + 17: "MT_EXCAVATE", + 26: "MT_ASSAULT", + 33: "MT_ARTIFACT", + 34: "MT_VOID_FLOOD", // Maybe + 35: "MT_VOID_CASCADE", + 38: "MT_ALCHEMY" // Maybe +}; + +export const ZarimanNormalJobs = [ + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAreaDefenseBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAssassinateBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosCrpSurvivorBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndessExcavateBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndesspurifyBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosExcavateBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosGrnSurvivorBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosKeypiecesBounty", + "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosPurifyBounty" +]; + +export const CertusNarmerJobs = [ + "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AssassinateBountyAss", + "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyExt", + "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyLib" +]; +/* +export const CertusGhoulalertJobs = [ + "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyAss", + "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyExt", + "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyBunt", + "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyRes" +] +export const CertusPlagueStarJob = "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty" +*/ +export const CertusNormalJobs = [ + "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyAss", + "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyCap", + "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountyCap", + "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountyExt", + "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountyLib", + "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountySab", + "/Lotus/Types/Gameplay/Eidolon/Jobs/CaptureBountyCapOne", + "/Lotus/Types/Gameplay/Eidolon/Jobs/CaptureBountyCapTwo", + "/Lotus/Types/Gameplay/Eidolon/Jobs/ReclamationBountyCache", + "/Lotus/Types/Gameplay/Eidolon/Jobs/ReclamationBountyCap", + "/Lotus/Types/Gameplay/Eidolon/Jobs/ReclamationBountyTheft", + "/Lotus/Types/Gameplay/Eidolon/Jobs/RescueBountyResc", + "/Lotus/Types/Gameplay/Eidolon/Jobs/SabotageBountySab" +]; + +export const factionSyndicates = [ + "ArbitersSyndicate", + "CephalonSudaSyndicate", + "NewLokaSyndicate", + "PerrinSyndicate", + "RedVeilSyndicate", + "SteelMeridianSyndicate" +]; +export const neutralJobsSyndicates = ["EntratiSyndicate", "CetusSyndicate", "SolarisSyndicate"]; +export const neutralSyndicates = ["ZarimanSyndicate", "EntratiLabSyndicate"]; +export const restSyndicates = [ + "NecraloidSyndicate", + "KahlSyndicate", + "EventSyndicate", + "QuillsSyndicate", + "VoxSyndicate", + "VentKidsSyndicate", + "RadioLegionSyndicate", + "RadioLegion2Syndicate", + "RadioLegion3Syndicate", + "RadioLegionIntermissionSyndicate", + "RadioLegionIntermission2Syndicate", + "RadioLegionIntermission3Syndicate", + "RadioLegionIntermission4Syndicate", + "RadioLegionIntermission5Syndicate", + "RadioLegionIntermission6Syndicate", + "RadioLegionIntermission8Syndicate", + "RadioLegionIntermission7Syndicate", + "RadioLegionIntermission9Syndicate", + "RadioLegionIntermission10Syndicate" +]; + +export const FortunaNarmerJobs = [ + "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobAssassinate", + "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobExterminate", + "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusTheftJobExcavation" +]; +export const FortunaNormalJobs = [ + "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobAmbush", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobExcavation", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobRecovery", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusChaosJobAssassinate", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusChaosJobExcavation", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusCullJobAssassinate", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusCullJobExterminate", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusCullJobResource", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusHelpingJobcaches", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusHelpingJobResource", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusHelpingJobSpy", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusIntelJobRecovery", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusIntelJobResource", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusIntelJobSpy", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusPreservationJobDefense", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusPreservationJobRecovery", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusPreservationJobResource", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusSpyJobSpy", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusTheftJobAmbush", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusTheftJobExcavation", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusTheftJobResource", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusWetworkJobAssassinate", + "/Lotus/Types/Gameplay/Venus/Jobs/VenusWetworkJobSpy" +]; diff --git a/src/controllers/dynamic/worldStateController.ts b/src/controllers/dynamic/worldStateController.ts index 428c9834..25463140 100644 --- a/src/controllers/dynamic/worldStateController.ts +++ b/src/controllers/dynamic/worldStateController.ts @@ -1,18 +1,17 @@ import { RequestHandler } from "express"; import worldState from "@/static/fixed_responses/worldState.json"; import buildConfig from "@/static/data/buildConfig.json"; -import { IWorldState } from "@/src/types/worldStateTypes"; import { config } from "@/src/services/configService"; import { getWorldState } from "@/src/services/worldStateService"; const worldStateController: RequestHandler = async (_req, res) => { - let ws: IWorldState = {}; + let ws: { [k: string]: any } = {}; if (config.useStaticWorldState) { ws = worldState; ws.BuildLabel = buildConfig.buildLabel; ws.Time = Math.round(Date.now() / 1000); } else { - ws = (await getWorldState()).toJSON(); + ws = await getWorldState(); ws.BuildLabel = buildConfig.buildLabel; ws.Time = Math.round(Date.now() / 1000); } diff --git a/src/helpers/general.ts b/src/helpers/general.ts index 23fb9df6..bb5cd914 100644 --- a/src/helpers/general.ts +++ b/src/helpers/general.ts @@ -74,4 +74,13 @@ export const isObject = (objectCandidate: unknown): objectCandidate is Record { + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +export const getRandomKey = (keys: string[]) => { + const randomIndex = Math.floor(Math.random() * keys.length); + return keys[randomIndex]; +}; + export { isString, isNumber, parseString, parseNumber, parseDateNumber, parseBoolean, parseEmail }; diff --git a/src/helpers/worldstateHelpers.ts b/src/helpers/worldstateHelpers.ts new file mode 100644 index 00000000..7504d54b --- /dev/null +++ b/src/helpers/worldstateHelpers.ts @@ -0,0 +1,30 @@ +import { ExportRegions } from "warframe-public-export-plus"; +import { unixTimesInMs } from "@/src/constants/timeConstants"; +import { logger } from "../utils/logger"; + +export const getRandomNodes = (n: number) => { + const nodes = Object.entries(ExportRegions).map(([key]) => { + return { + nodeKey: key + }; + }); // may be filter that? + const output: string[] = []; + for (let i = 0; i < n; i++) { + logger.debug(i); + const randomIndex = Math.floor(Math.random() * nodes.length); + output[i] = nodes[randomIndex].nodeKey; + } + return output; +}; + +export const getCurrentRotation = () => { + const intervalMilliseconds = 2.5 * unixTimesInMs.hour; + const rotations = ["A", "B", "C"]; + + const now = new Date(); + const currentTimeMs = now.getTime(); + + const intervalIndex = Math.floor(currentTimeMs / intervalMilliseconds) % 3; + + return rotations[intervalIndex]; +}; diff --git a/src/models/worldStateModel.ts b/src/models/worldStateModel.ts index 68e1d018..45b128c1 100644 --- a/src/models/worldStateModel.ts +++ b/src/models/worldStateModel.ts @@ -57,10 +57,10 @@ const EventSchema = new Schema({ Messages: [messageSchema], Prop: String, Links: [linkSchema], - Date: Date, + Date: Number, Icon: String, - EventStartDate: Date, - EventEndDate: Date, + EventStartDate: Number, + EventEndDate: Number, ImageUrl: String, Priority: Boolean, MobileOnly: Boolean, @@ -71,6 +71,9 @@ const EventSchema = new Schema({ EventSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Date = { $date: { $numberLong: returnedObject.Date.toString() } }; + returnedObject.EventStartDate = { $date: { $numberLong: returnedObject.EventStartDate.toString() } }; + returnedObject.EventEndDate = { $date: { $numberLong: returnedObject.EventEndDate.toString() } }; } }); @@ -121,8 +124,8 @@ const MissionSchema = new Schema( ); const AlertSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, MissionInfo: MissionSchema, ForceUnlock: Boolean, Tag: String @@ -131,27 +134,26 @@ const AlertSchema = new Schema({ AlertSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); -const SortieMissionSchema = new Schema({ - missionType: String, - modifierType: String, - node: String, - tileset: String -}); - -SortieMissionSchema.set("toJSON", { - transform(_document, returnedObject) { - returnedObject._id = { $oid: returnedObject._id.toString() }; - } -}); +const SortieMissionSchema = new Schema( + { + missionType: String, + modifierType: String, + node: String, + tileset: String + }, + { _id: false } +); const LiteSortieSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Reward: String, - Seed: String, + Seed: Number, Boss: String, Missions: [SortieMissionSchema] }); @@ -159,15 +161,18 @@ const LiteSortieSchema = new Schema({ LiteSortieSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); const SortieSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Reward: String, - Seed: String, + Seed: Number, Boss: String, + ExtraDrops: [String], Variants: [SortieMissionSchema], Twitter: Boolean }); @@ -175,6 +180,8 @@ const SortieSchema = new Schema({ SortieSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); @@ -195,10 +202,10 @@ const JobSchema = new Schema( ); const SyndicateMissionSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Tag: String, - Seed: String, + Seed: Number, Nodes: [String], Jobs: [JobSchema] }); @@ -206,14 +213,16 @@ const SyndicateMissionSchema = new Schema({ SyndicateMissionSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); const ActiveMissionSchema = new Schema({ - Activation: Date, - Expiry: Date, - Region: String, - Seed: String, + Activation: Number, + Expiry: Number, + Region: Number, + Seed: Number, Node: String, MissionType: String, Modifier: String, @@ -223,12 +232,14 @@ const ActiveMissionSchema = new Schema({ ActiveMissionSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); const GlobalUpgradeSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, UpgradeType: String, OperationType: String, Value: String @@ -237,14 +248,16 @@ const GlobalUpgradeSchema = new Schema({ GlobalUpgradeSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); const FlashSaleSchema = new Schema( { TypeName: String, - StartDate: Date, - EndDate: Date, + StartDate: Number, + EndDate: Number, ShowInMarket: Boolean, HideFromMarket: Boolean, SupporterPack: Boolean, @@ -257,6 +270,13 @@ const FlashSaleSchema = new Schema( { _id: false } ); +FlashSaleSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject.StartDate = { $date: { $numberLong: returnedObject.StartDate.toString() } }; + returnedObject.EndDate = { $date: { $numberLong: returnedObject.EndDate.toString() } }; + } +}); + const ShopCategorySchema = new Schema( { CategoryName: String, @@ -291,7 +311,7 @@ const InvasionMissionInfoSchema = new Schema( ); const InvasionSchema = new Schema({ - Activation: Date, + Activation: Number, Faction: String, DefenderFaction: String, Node: String, @@ -309,12 +329,13 @@ const InvasionSchema = new Schema({ InvasionSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; } }); const NodeOverrideSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Node: String, Faction: String, CustomNpcEncounters: [String], @@ -324,6 +345,8 @@ const NodeOverrideSchema = new Schema({ NodeOverrideSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); @@ -338,16 +361,23 @@ const VoidTraderItemSchema = new Schema( const VoidTraderScheduleInfoSchema = new Schema( { - Expiry: Date, - PreviewHiddenUntil: Date, + Expiry: Number, + PreviewHiddenUntil: Number, FeaturedItem: String }, { _id: false } ); +VoidTraderScheduleInfoSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; + returnedObject.PreviewHiddenUntil = { $date: { $numberLong: returnedObject.PreviewHiddenUntil.toString() } }; + } +}); + const VoidTraderSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Character: String, Node: String, Completed: Boolean, @@ -359,12 +389,14 @@ const VoidTraderSchema = new Schema({ VoidTraderSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); const VoidStormSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Node: String, ActiveMissionTier: String }); @@ -372,6 +404,8 @@ const VoidStormSchema = new Schema({ VoidStormSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); @@ -384,8 +418,8 @@ const PrimeAccessAvailabilitySchema = new Schema( const DailyDealSchema = new Schema( { - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, StoreItem: String, Discount: Number, OriginalPrice: Number, @@ -396,6 +430,13 @@ const DailyDealSchema = new Schema( { _id: false } ); +DailyDealSchema.set("toJSON", { + transform(_document, returnedObject) { + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; + } +}); + const LibraryInfoSchema = new Schema( { LastCompletedTargetType: String @@ -413,8 +454,8 @@ const PVPChallengeInstanceParam = new Schema( const PVPChallengeInstanceSchema = new Schema({ challengeTypeRefID: String, - startDate: Date, - endDate: Date, + startDate: Number, + endDate: Number, params: [PVPChallengeInstanceParam], isGenerated: Boolean, PVPMode: String, @@ -425,6 +466,8 @@ const PVPChallengeInstanceSchema = new Schema({ PVPChallengeInstanceSchema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.startDate = { $date: { $numberLong: returnedObject.startDate.toString() } }; + returnedObject.endDate = { $date: { $numberLong: returnedObject.endDate.toString() } }; } }); @@ -451,8 +494,8 @@ FeaturedGuildShema.set("toJSON", { }); const ActiveChallengeSchema = new Schema({ - Activation: Date, - Expiry: Date, + Activation: Number, + Expiry: Number, Daily: Boolean, Challenge: String }); @@ -460,6 +503,8 @@ const ActiveChallengeSchema = new Schema({ FeaturedGuildShema.set("toJSON", { transform(_document, returnedObject) { returnedObject._id = { $oid: returnedObject._id.toString() }; + returnedObject.Activation = { $date: { $numberLong: returnedObject.Activation.toString() } }; + returnedObject.Expiry = { $date: { $numberLong: returnedObject.Expiry.toString() } }; } }); diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index c236be3b..7e7c6430 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -1,8 +1,43 @@ import { WorldState } from "@/src/models/worldStateModel"; +import { unixTimesInMs } from "@/src/constants/timeConstants"; +import { + IActiveMission, + ILiteSortie, + ISortie, + ISyndicateMission, + IVoidStorm, + IWorldState +} from "@/src/types/worldStateTypes"; +import { getRandomNumber, getRandomKey } from "@/src/helpers/general"; +import { getRandomNodes, getCurrentRotation } from "@/src/helpers/worldstateHelpers"; +import { ExportRailjack, ExportRegions } from "warframe-public-export-plus"; +import { logger } from "@/src/utils/logger"; +import { + factionSyndicates, + neutralJobsSyndicates, + neutralSyndicates, + restSyndicates, + CertusNormalJobs, + CertusNarmerJobs, + ZarimanNormalJobs, + voidFisuresMissionTypes, + validFisureMissionIndex, + omniaNodes, + liteSortiesBoss, + endStates, + modifierTypes, + SortiesMissionTypes, + voidTiers, + FortunaNarmerJobs, + FortunaNormalJobs +} from "@/src/constants/worldStateConstants"; export const createWorldState = async () => { const worldState = new WorldState(); await worldState.save(); + await updateSyndicateMissions(); + await updateVoidFisures(); + await updateSorties(); return worldState; }; @@ -11,6 +46,574 @@ export const getWorldState = async () => { if (!ws) { ws = await createWorldState(); } - - return ws; + return ws.toJSON(); +}; + +export const worldStateRunner = async () => { + await getWorldState(); + setInterval(async () => { + logger.info("Update worldState"); + await updateSyndicateMissions(); + await updateVoidFisures(); + await updateSorties(); + }, unixTimesInMs.minute); +}; + +const updateSyndicateMissions = async (): Promise => { + const currentDate = Date.now(); + const oneDayIntervalStart = + Math.floor(currentDate / unixTimesInMs.day) * unixTimesInMs.day + 16 * unixTimesInMs.hour; + const oneDayIntervalEnd = oneDayIntervalStart + unixTimesInMs.day; + + const neutralJobsIntervalStart = Math.floor(currentDate / (2.5 * unixTimesInMs.hour)) * (2.5 * unixTimesInMs.hour); + const neutralJobsIntervalEnd = neutralJobsIntervalStart + 2.5 * unixTimesInMs.hour; + + const neutralSeed = getRandomNumber(1, 99999); + + try { + const ws = await WorldState.findOne(); + if (!ws) throw new Error("Missing worldState"); + + const syndicateArray = ws.SyndicateMissions || []; + + const existingTags = syndicateArray.map(syndicate => syndicate.Tag); + + const createNewSyndicateEntry = (tag: string): ISyndicateMission => { + switch (true) { + case factionSyndicates.includes(tag): + return { + Tag: tag, + Seed: getRandomNumber(1, 99999), + Nodes: getRandomNodes(7), + Activation: oneDayIntervalStart, + Expiry: oneDayIntervalEnd + }; + case neutralJobsSyndicates.includes(tag): + return { + Tag: tag, + Seed: neutralSeed, + Nodes: [], + Activation: neutralJobsIntervalStart, + Expiry: neutralJobsIntervalEnd, + Jobs: getJobs(tag) + }; + case neutralSyndicates.includes(tag): + return { + Tag: tag, + Seed: neutralSeed, + Nodes: [], + Activation: neutralJobsIntervalStart, + Expiry: neutralJobsIntervalEnd + }; + case restSyndicates.includes(tag): + return { + Tag: tag, + Seed: getRandomNumber(1, 99999), + Nodes: [], + Activation: oneDayIntervalStart, + Expiry: oneDayIntervalEnd + }; + default: + throw new Error(`Unhandled syndicate tag: ${tag}`); + } + }; + + [...factionSyndicates, ...neutralJobsSyndicates, ...neutralSyndicates, ...restSyndicates].forEach(tag => { + if (!existingTags.includes(tag)) { + syndicateArray.push(createNewSyndicateEntry(tag)); + } else { + const syndicateIndex = existingTags.indexOf(tag); + const shouldUpdate = currentDate >= syndicateArray[syndicateIndex].Expiry; + + if (shouldUpdate) { + syndicateArray[syndicateIndex] = { + ...syndicateArray[syndicateIndex], + Tag: tag, + Seed: + neutralJobsSyndicates.includes(tag) || neutralSyndicates.includes(tag) + ? neutralSeed + : getRandomNumber(1, 99999), + Nodes: + neutralJobsSyndicates.includes(tag) || neutralSyndicates.includes(tag) + ? [] + : getRandomNodes(7), + Activation: + neutralJobsSyndicates.includes(tag) || neutralSyndicates.includes(tag) + ? neutralJobsIntervalStart + : oneDayIntervalStart, + Expiry: + neutralJobsSyndicates.includes(tag) || neutralSyndicates.includes(tag) + ? neutralJobsIntervalEnd + : oneDayIntervalEnd, + Jobs: neutralJobsSyndicates.includes(tag) ? getJobs(tag) : undefined + }; + } + } + }); + + ws.SyndicateMissions = syndicateArray; + + await ws.save(); + return ws; + } catch (error) { + throw new Error(`Error while updating Syndicates ${error}`); + } +}; + +const getJobs = (tag: string) => { + const rotration = getCurrentRotation(); + switch (tag) { + case "CetusSyndicate": + const Certusjobs = [ + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATable${rotration}Rewards`, + masteryReq: 0, + minEnemyLevel: 5, + maxEnemyLevel: 15, + xpAmounts: [410, 410, 410] + }, + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTable${rotration}Rewards`, + masteryReq: 1, + minEnemyLevel: 10, + maxEnemyLevel: 30, + xpAmounts: [750, 750, 750] + }, + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTable${rotration}Rewards`, + masteryReq: 2, + minEnemyLevel: 20, + maxEnemyLevel: 40, + xpAmounts: [580, 580, 580, 850] + }, + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTable${rotration}Rewards`, + masteryReq: 3, + minEnemyLevel: 30, + maxEnemyLevel: 50, + xpAmounts: [580, 580, 580, 580, 1130] + }, + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${rotration}Rewards`, + masteryReq: 5, + minEnemyLevel: 40, + maxEnemyLevel: 60, + xpAmounts: [710, 710, 710, 710, 1390] + }, + { + jobType: CertusNormalJobs[Math.floor(Math.random() * CertusNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${rotration}Rewards`, + masteryReq: 10, + minEnemyLevel: 100, + maxEnemyLevel: 100, + xpAmounts: [840, 840, 840, 840, 1660] + }, + { + jobType: CertusNarmerJobs[Math.floor(Math.random() * CertusNarmerJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTable${rotration}Rewards`, + masteryReq: 0, + minEnemyLevel: 50, + maxEnemyLevel: 70, + xpAmounts: [820, 820, 820, 820, 1610] + } + ]; + return Certusjobs; + + case "SolarisSyndicate": + const FortunaJobs = [ + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATable${rotration}Rewards`, + masteryReq: 0, + minEnemyLevel: 5, + maxEnemyLevel: 15, + xpAmounts: [410, 410, 410] + }, + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTable${rotration}Rewards`, + masteryReq: 1, + minEnemyLevel: 10, + maxEnemyLevel: 30, + xpAmounts: [750, 750, 750] + }, + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTable${rotration}Rewards`, + masteryReq: 2, + minEnemyLevel: 20, + maxEnemyLevel: 40, + xpAmounts: [580, 580, 580, 850] + }, + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTable${rotration}Rewards`, + masteryReq: 3, + minEnemyLevel: 30, + maxEnemyLevel: 50, + xpAmounts: [580, 580, 580, 580, 1130] + }, + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${rotration}Rewards`, + masteryReq: 5, + minEnemyLevel: 40, + maxEnemyLevel: 60, + xpAmounts: [710, 710, 710, 710, 1390] + }, + { + jobType: FortunaNormalJobs[Math.floor(Math.random() * FortunaNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${rotration}Rewards`, + masteryReq: 10, + minEnemyLevel: 100, + maxEnemyLevel: 100, + xpAmounts: [840, 840, 840, 840, 1660] + }, + { + jobType: FortunaNarmerJobs[Math.floor(Math.random() * FortunaNarmerJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusNarmerTable${rotration}Rewards`, + masteryReq: 0, + minEnemyLevel: 50, + maxEnemyLevel: 70, + xpAmounts: [820, 820, 820, 820, 1610] + } + ]; + return FortunaJobs; + + case "EntratiSyndicate": + const ZarimanJobs = [ + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 0, + minEnemyLevel: 5, + maxEnemyLevel: 15, + xpAmounts: [5, 5, 5] + }, + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 1, + minEnemyLevel: 15, + maxEnemyLevel: 25, + xpAmounts: [9, 9, 9] + }, + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 5, + minEnemyLevel: 25, + maxEnemyLevel: 30, + endless: true, + xpAmounts: [14, 14, 14] + }, + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 2, + minEnemyLevel: 30, + maxEnemyLevel: 40, + xpAmounts: [19, 19, 19, 29] + }, + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 3, + minEnemyLevel: 40, + maxEnemyLevel: 60, + xpAmounts: [21, 21, 21, 21, 41] + }, + { + jobType: ZarimanNormalJobs[Math.floor(Math.random() * ZarimanNormalJobs.length)], + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETable${getRandomKey(["A", "B", "C"])}Rewards`, + masteryReq: 10, + minEnemyLevel: 100, + maxEnemyLevel: 100, + xpAmounts: [25, 25, 25, 25, 50] + }, + { + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierATable${getCurrentRotation()}Rewards`, + masteryReq: 5, + minEnemyLevel: 30, + maxEnemyLevel: 40, + xpAmounts: [2, 2, 2, 4], + locationTag: "ChamberB", + isVault: true + }, + { + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierBTable${getCurrentRotation()}Rewards`, + masteryReq: 5, + minEnemyLevel: 40, + maxEnemyLevel: 50, + xpAmounts: [4, 4, 4, 5], + locationTag: "ChamberA", + isVault: true + }, + { + rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierCTable${getCurrentRotation()}Rewards`, + masteryReq: 5, + minEnemyLevel: 50, + maxEnemyLevel: 60, + xpAmounts: [5, 5, 5, 7], + locationTag: "ChamberC", + isVault: true + } + ]; + return ZarimanJobs; + + default: + throw new Error(`Error while updating Syndicates: Unknown Jobs syndicate ${tag}`); + } +}; + +const updateVoidFisures = async () => { + const curDate = Date.now(); + try { + const ws = await WorldState.findOne(); + if (!ws) throw new Error("Missing worldState"); + const voidFisures = ws.ActiveMissions; + const voidStorms = ws.VoidStorms; + const voidFisuresByTier: { [key: string]: IActiveMission[] } = { + VoidT1: [], + VoidT2: [], + VoidT3: [], + VoidT4: [], + VoidT5: [], + VoidT6: [] + }; + const voidStormsByTier: { [key: string]: IVoidStorm[] } = { + VoidT1: [], + VoidT2: [], + VoidT3: [], + VoidT4: [], + VoidT5: [], + VoidT6: [] + }; + + if (voidFisures) { + voidFisures.forEach(mission => { + const tier = mission.Modifier; + if (tier) { + if (!voidFisuresByTier[tier]) { + voidFisuresByTier[tier] = []; + } + voidFisuresByTier[tier].push(mission); + } + }); + } + + if (voidStorms) { + voidStorms.forEach(mission => { + const tier = mission.ActiveMissionTier; + if (tier) { + if (!voidStormsByTier[tier]) { + voidStormsByTier[tier] = []; + } + voidStormsByTier[tier].push(mission); + } + }); + } + + voidTiers.forEach(voidTier => { + if (voidFisuresByTier[voidTier].length < 3) { + const nodeData = getRandomFisureNode(false, voidTier == "VoidT6"); + logger.debug(voidTier); + if (!nodeData.missionIndex) nodeData.missionIndex = 1; + const node = { + Region: nodeData.systemIndex, + Seed: getRandomNumber(1, 99999), + Activation: curDate, + Expiry: curDate + Math.floor(Math.random() * unixTimesInMs.hour), + Node: nodeData.nodeKey, + MissionType: voidFisuresMissionTypes[nodeData.missionIndex], + Modifier: voidTier, + Hard: Math.random() < 0.1 + } as IActiveMission; + voidFisures?.push(node); + } + + if (voidStormsByTier[voidTier].length < 2) { + const nodeData = getRandomFisureNode(true, voidTier == "VoidT6"); + logger.debug(voidTier); + const node = { + Activation: curDate, + Expiry: curDate + Math.floor(Math.random() * unixTimesInMs.hour), + Node: nodeData.nodeKey, + ActiveMissionTier: voidTier + } as IVoidStorm; + voidStorms?.push(node); + } + }); + await ws.save(); + return ws; + } catch (error) { + throw new Error(`Error while updating VoidFisures: ${error}`); + } +}; + +const getRandomFisureNode = (isRailJack: boolean, isOmnia: boolean) => { + const validNodes = Object.entries(ExportRegions) + .map(([key, node]) => { + return { + ...node, + nodeKey: key + }; + }) + .filter(node => { + return validFisureMissionIndex.includes(node.missionIndex) && !node.missionName.includes("Archwing"); + }); + + if (isRailJack) { + const railJackNodes = Object.keys(ExportRailjack.nodes); + const randomKey = railJackNodes[Math.floor(Math.random() * railJackNodes.length)]; + return { + nodeKey: randomKey + }; + } else if (isOmnia) { + const validOmniaNodes = validNodes.filter(node => { + return omniaNodes.includes(node.nodeKey); + }); + const randomNode = validOmniaNodes[Math.floor(Math.random() * validOmniaNodes.length)]; + return { + nodeKey: randomNode.nodeKey, + systemIndex: randomNode.systemIndex, + missionIndex: randomNode.missionIndex + }; + } else { + const randomNode = validNodes[Math.floor(Math.random() * validNodes.length)]; + return { + nodeKey: randomNode.nodeKey, + systemIndex: randomNode.systemIndex, + missionIndex: randomNode.missionIndex + }; + } +}; + +const updateSorties = async () => { + const currentDate = Date.now(); + const oneDayIntervalStart = + Math.floor(currentDate / unixTimesInMs.day) * unixTimesInMs.day + 16 * unixTimesInMs.hour; + const oneDayIntervalEnd = oneDayIntervalStart + unixTimesInMs.day; + const oneWeekIntervalStart = + Math.floor(currentDate / (unixTimesInMs.day * 7)) * unixTimesInMs.day * 7 + 16 * unixTimesInMs.hour; + const oneWeekIntervalEnd = oneDayIntervalStart + unixTimesInMs.day * 7; + const nodes = Object.entries(ExportRegions).map(([key, node]) => { + return { + nodeSystemIndex: node.systemIndex, + nodeKey: key + }; + }); + + try { + const ws = await WorldState.findOne(); + if (!ws) throw new Error("Missing worldState"); + const liteSorties: ILiteSortie[] = ws?.LiteSorties; + const sorties: ISortie[] = ws?.Sorties; + + [...liteSorties, ...sorties].forEach((sortie, index, array) => { + if (currentDate >= sortie.Expiry) array.splice(index, 1); + }); + + if (liteSorties.length < 1) { + const sortie: ILiteSortie = { + Activation: oneWeekIntervalStart, + Expiry: oneWeekIntervalEnd, + Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards", + Seed: getRandomNumber(1, 99999), + Boss: liteSortiesBoss[Math.floor(Math.random() * liteSortiesBoss.length)], + Missions: [ + { + missionType: SortiesMissionTypes[Math.floor(Math.random() * SortiesMissionTypes.length)], + node: nodes[Math.floor(Math.random() * nodes.length)].nodeKey + }, + { + missionType: SortiesMissionTypes[Math.floor(Math.random() * SortiesMissionTypes.length)], + node: nodes[Math.floor(Math.random() * nodes.length)].nodeKey + }, + { + missionType: SortiesMissionTypes[Math.floor(Math.random() * SortiesMissionTypes.length)], + node: nodes[Math.floor(Math.random() * nodes.length)].nodeKey + } + ] + }; + liteSorties.push(sortie); + } + + if (sorties.length < 1) { + const randomBoss = endStates[Math.floor(Math.random() * endStates.length)]; + const randomRegionIndex = [ + Math.floor(Math.random() * randomBoss.regions.length), + Math.floor(Math.random() * randomBoss.regions.length), + Math.floor(Math.random() * randomBoss.regions.length) + ]; + const randomRegionIndexFake = randomRegionIndex; + randomRegionIndexFake.forEach((element, index, array) => { + if (element == 13) { + array[index] = element + 2; + } else if (element == 14) { + array[index] = element + 1; + } + }); + const filteredNodes = [ + nodes.filter(node => { + return randomRegionIndexFake[0] === node.nodeSystemIndex; + }), + nodes.filter(node => { + return randomRegionIndexFake[1] === node.nodeSystemIndex; + }), + nodes.filter(node => { + return randomRegionIndexFake[2] === node.nodeSystemIndex; + }) + ]; + const sortie: ISortie = { + Activation: oneDayIntervalStart, + Expiry: oneDayIntervalEnd, + ExtraDrops: [], + Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards", + Seed: getRandomNumber(1, 99999), + Boss: randomBoss.bossName, + Variants: [ + { + missionType: + randomBoss.regions[randomRegionIndex[0]].missions[ + Math.floor(Math.random() * randomBoss.regions[randomRegionIndex[0]].missions.length) + ], + modifierType: modifierTypes[Math.floor(Math.random() * modifierTypes.length)], + node: filteredNodes[0][Math.floor(Math.random() * filteredNodes[0].length)].nodeKey, + tileset: "CorpusShipTileset" + }, + { + missionType: + randomBoss.regions[randomRegionIndex[1]].missions[ + Math.floor(Math.random() * randomBoss.regions[randomRegionIndex[1]].missions.length) + ], + modifierType: modifierTypes[Math.floor(Math.random() * modifierTypes.length)], + node: filteredNodes[1][Math.floor(Math.random() * filteredNodes[1].length)].nodeKey, + tileset: "OrokinMoonTilesetCorpus" + }, + { + missionType: + randomBoss.regions[randomRegionIndex[2]].missions[ + Math.floor(Math.random() * randomBoss.regions[randomRegionIndex[2]].missions.length) + ], + modifierType: modifierTypes[Math.floor(Math.random() * modifierTypes.length)], + node: filteredNodes[2][Math.floor(Math.random() * filteredNodes[2].length)].nodeKey, + tileset: "CorpusShipTileset" + } + ], + Twitter: true + }; + sorties.push(sortie); + } + + await ws.save(); + return ws; + } catch (error) { + throw new Error(`Error while updating Sorties ${error}`); + } }; diff --git a/src/types/worldStateTypes.ts b/src/types/worldStateTypes.ts index c8d6b8ac..a9e02a88 100644 --- a/src/types/worldStateTypes.ts +++ b/src/types/worldStateTypes.ts @@ -1,4 +1,4 @@ -import { IMongoDate, IOid } from "@/src/types/commonTypes"; +import { IOid } from "@/src/types/commonTypes"; export interface IMessage { LanguageCode?: string; @@ -11,8 +11,8 @@ export interface ILink { } export interface IBaseWorldStateObject { - Activation: IMongoDate; - Expiry: IMongoDate; + Activation: number; + Expiry: number; _id?: IOid; } @@ -61,12 +61,11 @@ export interface IEvent { Icon?: string; Community?: boolean; Priority?: boolean; - EventStartDate?: IMongoDate; - EventEndDate?: IMongoDate; + EventStartDate?: number; + EventEndDate?: number; MobileOnly?: boolean; HideEndDateModifier?: boolean; - Date?: IMongoDate; - _id?: IOid; + Date?: number; } export interface IGoal extends IBaseWorldStateObject { @@ -103,7 +102,7 @@ export interface ISortieMission { } export interface ISortie extends Omit { - // ExtraDrops: []; Unknown + ExtraDrops: string[]; //Unknown Variants: ISortieMission[]; Twitter: boolean; } @@ -150,8 +149,8 @@ export interface IGlobalUpgrade extends IBaseWorldStateObject { } export interface IFlashSale { - StartDate: IMongoDate; - EndDate: IMongoDate; + StartDate: number; + EndDate: number; TypeName: string; ShowInMarket: boolean; HideFromMarket: boolean; @@ -200,8 +199,8 @@ export interface IInvasionMissionInfo { } export interface INodeOverride { - Activation?: IMongoDate; - Expiry?: IMongoDate; + Activation?: number; + Expiry?: number; Node: string; Faction?: string; CustomNpcEncounters?: string[]; @@ -224,7 +223,7 @@ export interface IVoidTraderItem { } export interface IVoidTraderScheduleInfo extends Omit { - PreviewHiddenUntil?: IMongoDate; + PreviewHiddenUntil?: number; FeaturedItem?: string; } @@ -252,8 +251,8 @@ export interface ILibraryInfo { export interface IPVPChallengeInstance { challengeTypeRefID: string; - startDate: IMongoDate; - endDate: IMongoDate; + startDate: number; + endDate: number; params: IPVPChallengeInstanceParam[]; isGenerated: boolean; PVPMode: string; @@ -302,8 +301,8 @@ export interface IWorldState { Events?: IEvent[]; Goals?: IGoal[]; Alerts?: IAlert[]; - Sorties?: ISortie[]; - LiteSorties?: ILiteSortie[]; + Sorties: ISortie[]; + LiteSorties: ILiteSortie[]; SyndicateMissions?: ISyndicateMission[]; ActiveMissions?: IActiveMission[]; GlobalUpgrades?: IGlobalUpgrade[]; diff --git a/static/webui/index.html b/static/webui/index.html index 4db8f2ef..4e4daa04 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -261,6 +261,10 @@ Universal Polarity Everywhere +
+ + +