forked from OpenWF/SpaceNinjaServer
Compare commits
10 Commits
4e57bcd1ae
...
d15805bb19
Author | SHA1 | Date | |
---|---|---|---|
d15805bb19 | |||
bb606f3a95 | |||
4b12fe12cb | |||
4f28688837 | |||
203b3e20d9 | |||
005690daa4 | |||
5fefd189af | |||
f6cbc02c47 | |||
da6d75c748 | |||
460deed3ed |
@ -25,6 +25,9 @@ import { logger } from "@/src/utils/logger";
|
|||||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers";
|
import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers";
|
||||||
|
import { version_compare } from "@/src/services/worldStateService";
|
||||||
|
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||||
|
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||||
|
|
||||||
export const inventoryController: RequestHandler = async (request, response) => {
|
export const inventoryController: RequestHandler = async (request, response) => {
|
||||||
const account = await getAccountForRequest(request);
|
const account = await getAccountForRequest(request);
|
||||||
@ -312,6 +315,12 @@ export const getInventoryResponse = async (
|
|||||||
inventoryResponse.Nemesis = undefined;
|
inventoryResponse.Nemesis = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buildLabel && version_compare(buildLabel, "2018.02.22.14.34") < 0) {
|
||||||
|
const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString());
|
||||||
|
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||||
|
inventoryResponse.Ship = personalRooms.Ship;
|
||||||
|
}
|
||||||
|
|
||||||
return inventoryResponse;
|
return inventoryResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
email: loginRequest.email,
|
email: loginRequest.email,
|
||||||
password: loginRequest.password,
|
password: loginRequest.password,
|
||||||
DisplayName: name,
|
DisplayName: name,
|
||||||
CountryCode: loginRequest.lang.toUpperCase(),
|
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
|
||||||
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
||||||
CrossPlatformAllowed: true,
|
CrossPlatformAllowed: true,
|
||||||
ForceLogoutVersion: 0,
|
ForceLogoutVersion: 0,
|
||||||
@ -91,7 +91,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
|
|
||||||
account.ClientType = loginRequest.ClientType;
|
account.ClientType = loginRequest.ClientType;
|
||||||
account.Nonce = nonce;
|
account.Nonce = nonce;
|
||||||
account.CountryCode = loginRequest.lang.toUpperCase();
|
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
|
||||||
account.BuildLabel = buildLabel;
|
account.BuildLabel = buildLabel;
|
||||||
}
|
}
|
||||||
await account.save();
|
await account.save();
|
||||||
@ -107,10 +107,15 @@ const createLoginResponse = (myAddress: string, account: IDatabaseAccountJson, b
|
|||||||
AmazonAuthToken: account.AmazonAuthToken,
|
AmazonAuthToken: account.AmazonAuthToken,
|
||||||
AmazonRefreshToken: account.AmazonRefreshToken,
|
AmazonRefreshToken: account.AmazonRefreshToken,
|
||||||
Nonce: account.Nonce,
|
Nonce: account.Nonce,
|
||||||
IRC: config.myIrcAddresses ?? [myAddress],
|
|
||||||
NRS: config.NRS,
|
|
||||||
BuildLabel: buildLabel
|
BuildLabel: buildLabel
|
||||||
};
|
};
|
||||||
|
if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
|
||||||
|
resp.NRS = config.NRS;
|
||||||
|
}
|
||||||
|
if (version_compare(buildLabel, "2015.05.14.16.29") >= 0) {
|
||||||
|
// U17 and up
|
||||||
|
resp.IRC = config.myIrcAddresses ?? [myAddress];
|
||||||
|
}
|
||||||
if (version_compare(buildLabel, "2018.11.08.14.45") >= 0) {
|
if (version_compare(buildLabel, "2018.11.08.14.45") >= 0) {
|
||||||
// U24 and up
|
// U24 and up
|
||||||
resp.ConsentNeeded = account.ConsentNeeded;
|
resp.ConsentNeeded = account.ConsentNeeded;
|
||||||
|
@ -45,6 +45,9 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
||||||
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
||||||
}
|
}
|
||||||
|
if (payload.Items.MechSuits) {
|
||||||
|
requiredFields.add(InventorySlot.MECHSUITS);
|
||||||
|
}
|
||||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) {
|
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) {
|
||||||
requiredFields.add(InventorySlot.SENTINELS);
|
requiredFields.add(InventorySlot.SENTINELS);
|
||||||
}
|
}
|
||||||
@ -136,6 +139,12 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
freeUpSlot(inventory, InventorySlot.SPACEWEAPONS);
|
freeUpSlot(inventory, InventorySlot.SPACEWEAPONS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (payload.Items.MechSuits) {
|
||||||
|
payload.Items.MechSuits.forEach(sellItem => {
|
||||||
|
inventory.MechSuits.pull({ _id: sellItem.String });
|
||||||
|
freeUpSlot(inventory, InventorySlot.MECHSUITS);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (payload.Items.Sentinels) {
|
if (payload.Items.Sentinels) {
|
||||||
payload.Items.Sentinels.forEach(sellItem => {
|
payload.Items.Sentinels.forEach(sellItem => {
|
||||||
inventory.Sentinels.pull({ _id: sellItem.String });
|
inventory.Sentinels.pull({ _id: sellItem.String });
|
||||||
@ -285,6 +294,7 @@ interface ISellRequest {
|
|||||||
SpaceSuits?: ISellItem[];
|
SpaceSuits?: ISellItem[];
|
||||||
SpaceGuns?: ISellItem[];
|
SpaceGuns?: ISellItem[];
|
||||||
SpaceMelee?: ISellItem[];
|
SpaceMelee?: ISellItem[];
|
||||||
|
MechSuits?: ISellItem[];
|
||||||
Sentinels?: ISellItem[];
|
Sentinels?: ISellItem[];
|
||||||
SentinelWeapons?: ISellItem[];
|
SentinelWeapons?: ISellItem[];
|
||||||
MoaPets?: ISellItem[];
|
MoaPets?: ISellItem[];
|
||||||
|
@ -41,7 +41,7 @@ export const startRecipeController: RequestHandler = async (req, res) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i != recipe.ingredients.length; ++i) {
|
for (let i = 0; i != recipe.ingredients.length; ++i) {
|
||||||
if (startRecipeRequest.Ids[i]) {
|
if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
|
||||||
const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
|
const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
|
||||||
if (category != "LongGuns" && category != "Pistols" && category != "Melee") {
|
if (category != "LongGuns" && category != "Pistols" && category != "Melee") {
|
||||||
throw new Error(`unexpected equipment ingredient type: ${category}`);
|
throw new Error(`unexpected equipment ingredient type: ${category}`);
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
|
import { AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
export const getAccountInfoController: RequestHandler = async (req, res) => {
|
export const getAccountInfoController: RequestHandler = async (req, res) => {
|
||||||
const account = await getAccountForRequest(req);
|
const account = await getAccountForRequest(req);
|
||||||
|
const inventory = await getInventory(account._id.toString(), "QuestKeys");
|
||||||
const info: IAccountInfo = {
|
const info: IAccountInfo = {
|
||||||
DisplayName: account.DisplayName
|
DisplayName: account.DisplayName,
|
||||||
|
IsAdministrator: isAdministrator(account),
|
||||||
|
CompletedVorsPrize: !!inventory.QuestKeys.find(
|
||||||
|
x => x.ItemType == "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"
|
||||||
|
)?.Completed
|
||||||
};
|
};
|
||||||
if (isAdministrator(account)) {
|
|
||||||
info.IsAdministrator = true;
|
|
||||||
}
|
|
||||||
const guildMember = await GuildMember.findOne({ accountId: account._id, status: 0 }, "guildId rank");
|
const guildMember = await GuildMember.findOne({ accountId: account._id, status: 0 }, "guildId rank");
|
||||||
if (guildMember) {
|
if (guildMember) {
|
||||||
const guild = (await Guild.findById(guildMember.guildId, "Ranks AllianceId"))!;
|
const guild = (await Guild.findById(guildMember.guildId, "Ranks AllianceId"))!;
|
||||||
@ -31,7 +34,8 @@ export const getAccountInfoController: RequestHandler = async (req, res) => {
|
|||||||
|
|
||||||
interface IAccountInfo {
|
interface IAccountInfo {
|
||||||
DisplayName: string;
|
DisplayName: string;
|
||||||
IsAdministrator?: boolean;
|
IsAdministrator: boolean;
|
||||||
|
CompletedVorsPrize: boolean;
|
||||||
GuildId?: string;
|
GuildId?: string;
|
||||||
GuildPermissions?: number;
|
GuildPermissions?: number;
|
||||||
GuildRank?: number;
|
GuildRank?: number;
|
||||||
|
@ -225,6 +225,7 @@ apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
|
|||||||
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
|
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
|
||||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
|
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
|
||||||
apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
|
apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
|
||||||
|
apiRouter.post("/commitStoryModeDecision.php", (_req, res) => { res.end(); }); // U14 (maybe wanna actually unlock the ship features?)
|
||||||
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
|
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
|
||||||
apiRouter.post("/confirmGuildInvitation.php", confirmGuildInvitationPostController);
|
apiRouter.post("/confirmGuildInvitation.php", confirmGuildInvitationPostController);
|
||||||
apiRouter.post("/contributeGuildClass.php", contributeGuildClassController);
|
apiRouter.post("/contributeGuildClass.php", contributeGuildClassController);
|
||||||
|
@ -149,6 +149,11 @@ export const addStartingGear = async (
|
|||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
startingGear?: TPartialStartingGear
|
startingGear?: TPartialStartingGear
|
||||||
): Promise<IInventoryChanges> => {
|
): Promise<IInventoryChanges> => {
|
||||||
|
if (inventory.ReceivedStartingGear) {
|
||||||
|
throw new Error(`account has already received starting gear`);
|
||||||
|
}
|
||||||
|
inventory.ReceivedStartingGear = true;
|
||||||
|
|
||||||
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
|
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
|
||||||
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
|
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
|
||||||
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
|
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
|
||||||
@ -197,11 +202,6 @@ export const addStartingGear = async (
|
|||||||
combineInventoryChanges(inventoryChanges, inventoryDelta);
|
combineInventoryChanges(inventoryChanges, inventoryDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inventory.ReceivedStartingGear) {
|
|
||||||
logger.warn(`account already had starting gear but asked for it again?!`);
|
|
||||||
}
|
|
||||||
inventory.ReceivedStartingGear = true;
|
|
||||||
|
|
||||||
return inventoryChanges;
|
return inventoryChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,9 +58,10 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js
|
|||||||
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
|
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
|
||||||
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
||||||
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
||||||
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
|
import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClassicBounties } from "./worldStateService";
|
||||||
import { config } from "./configService";
|
import { config } from "./configService";
|
||||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||||
|
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
|
||||||
|
|
||||||
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
|
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
|
||||||
// For Spy missions, e.g. 3 vaults cracked = A, B, C
|
// For Spy missions, e.g. 3 vaults cracked = A, B, C
|
||||||
@ -1086,16 +1087,16 @@ export const addMissionRewards = async (
|
|||||||
|
|
||||||
if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
|
if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [jobType, unkIndex, hubNode, syndicateId, locationTag] = rewardInfo.jobId.split("_");
|
const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = rewardInfo.jobId.split("_");
|
||||||
const worldState = getWorldState();
|
const syndicateMissions: ISyndicateMissionInfo[] = [];
|
||||||
let syndicateEntry = worldState.SyndicateMissions.find(m => m._id.$oid === syndicateId);
|
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||||
if (!syndicateEntry) syndicateEntry = worldState.SyndicateMissions.find(m => m.Tag === syndicateId); // Sometimes syndicateId can be tag
|
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||||
let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
|
let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
|
||||||
if (syndicateEntry.Tag === "EntratiSyndicate") {
|
if (syndicateEntry.Tag === "EntratiSyndicate") {
|
||||||
const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
|
const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
|
||||||
if (vault) currentJob = vault;
|
if (vault) currentJob = vault;
|
||||||
let medallionAmount = currentJob.xpAmounts[rewardInfo.JobStage];
|
let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
|
["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
|
||||||
@ -1118,7 +1119,11 @@ export const addMissionRewards = async (
|
|||||||
} else {
|
} else {
|
||||||
if (rewardInfo.JobTier! >= 0) {
|
if (rewardInfo.JobTier! >= 0) {
|
||||||
AffiliationMods.push(
|
AffiliationMods.push(
|
||||||
addStanding(inventory, syndicateEntry.Tag, currentJob.xpAmounts[rewardInfo.JobStage])
|
addStanding(
|
||||||
|
inventory,
|
||||||
|
syndicateEntry.Tag,
|
||||||
|
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
|
if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
|
||||||
@ -1357,9 +1362,24 @@ function getRandomMissionDrops(
|
|||||||
// Invasion assassination has Phorid has the boss who should drop Nyx parts
|
// Invasion assassination has Phorid has the boss who should drop Nyx parts
|
||||||
// TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic
|
// TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic
|
||||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
|
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
|
||||||
} else if (RewardInfo.sortieId && region.missionIndex != 0) {
|
} else if (RewardInfo.sortieId) {
|
||||||
// Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes. Assassinations are an exception to this.
|
// Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes. Assassinations are an exception to this.
|
||||||
rewardManifests = [];
|
if (region.missionIndex == 0) {
|
||||||
|
const arr = RewardInfo.sortieId.split("_");
|
||||||
|
let sortieId = arr[1];
|
||||||
|
if (sortieId == "Lite") {
|
||||||
|
sortieId = arr[2];
|
||||||
|
}
|
||||||
|
const sortie = getSortie(idToDay(sortieId));
|
||||||
|
const mission = sortie.Variants.find(x => x.node == arr[0])!;
|
||||||
|
if (mission.missionType == "MT_ASSASSINATION") {
|
||||||
|
rewardManifests = region.rewardManifests;
|
||||||
|
} else {
|
||||||
|
rewardManifests = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rewardManifests = [];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rewardManifests = region.rewardManifests;
|
rewardManifests = region.rewardManifests;
|
||||||
}
|
}
|
||||||
@ -1368,13 +1388,12 @@ function getRandomMissionDrops(
|
|||||||
if (RewardInfo.jobId) {
|
if (RewardInfo.jobId) {
|
||||||
if (RewardInfo.JobStage! >= 0) {
|
if (RewardInfo.JobStage! >= 0) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [jobType, unkIndex, hubNode, syndicateId, locationTag] = RewardInfo.jobId.split("_");
|
const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = RewardInfo.jobId.split("_");
|
||||||
let isEndlessJob = false;
|
let isEndlessJob = false;
|
||||||
if (syndicateId) {
|
if (syndicateMissionId) {
|
||||||
const worldState = getWorldState();
|
const syndicateMissions: ISyndicateMissionInfo[] = [];
|
||||||
let syndicateEntry = worldState.SyndicateMissions.find(m => m._id.$oid === syndicateId);
|
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||||
if (!syndicateEntry) syndicateEntry = worldState.SyndicateMissions.find(m => m.Tag === syndicateId);
|
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||||
|
|
||||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||||
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
|
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
ICalendarSeason,
|
ICalendarSeason,
|
||||||
ILiteSortie,
|
ILiteSortie,
|
||||||
ISeasonChallenge,
|
ISeasonChallenge,
|
||||||
|
ISortie,
|
||||||
ISortieMission,
|
ISortieMission,
|
||||||
|
ISyndicateMissionInfo,
|
||||||
IWorldState
|
IWorldState
|
||||||
} from "../types/worldStateTypes";
|
} from "../types/worldStateTypes";
|
||||||
|
|
||||||
@ -193,12 +195,6 @@ const pushSyndicateMissions = (
|
|||||||
idSuffix: string,
|
idSuffix: string,
|
||||||
syndicateTag: string
|
syndicateTag: string
|
||||||
): void => {
|
): void => {
|
||||||
const dayStart = getSortieTime(day);
|
|
||||||
if (Date.now() >= dayStart) {
|
|
||||||
return; // The client does not seem to respect activation.
|
|
||||||
}
|
|
||||||
const dayEnd = getSortieTime(day + 1);
|
|
||||||
|
|
||||||
const nodeOptions: string[] = [...syndicateMissions];
|
const nodeOptions: string[] = [...syndicateMissions];
|
||||||
|
|
||||||
const rng = new CRng(seed);
|
const rng = new CRng(seed);
|
||||||
@ -209,6 +205,8 @@ const pushSyndicateMissions = (
|
|||||||
nodeOptions.splice(index, 1);
|
nodeOptions.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dayStart = getSortieTime(day);
|
||||||
|
const dayEnd = getSortieTime(day + 1);
|
||||||
worldState.SyndicateMissions.push({
|
worldState.SyndicateMissions.push({
|
||||||
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + idSuffix },
|
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + idSuffix },
|
||||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||||
@ -219,16 +217,7 @@ const pushSyndicateMissions = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
|
export const getSortie = (day: number): ISortie => {
|
||||||
const dayStart = getSortieTime(day);
|
|
||||||
if (!isBeforeNextExpectedWorldStateRefresh(dayStart)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const dayEnd = getSortieTime(day + 1);
|
|
||||||
if (Date.now() >= dayEnd) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const seed = new CRng(day).randomInt(0, 0xffff);
|
const seed = new CRng(day).randomInt(0, 0xffff);
|
||||||
const rng = new CRng(seed);
|
const rng = new CRng(seed);
|
||||||
|
|
||||||
@ -375,7 +364,9 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
|
|||||||
missionTypes.add(missionType);
|
missionTypes.add(missionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
worldState.Sorties.push({
|
const dayStart = getSortieTime(day);
|
||||||
|
const dayEnd = getSortieTime(day + 1);
|
||||||
|
return {
|
||||||
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "d4d932c97c0a3acd" },
|
_id: { $oid: ((dayStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "d4d932c97c0a3acd" },
|
||||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||||
@ -383,14 +374,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
|
|||||||
Seed: seed,
|
Seed: seed,
|
||||||
Boss: boss,
|
Boss: boss,
|
||||||
Variants: selectedNodes
|
Variants: selectedNodes
|
||||||
});
|
};
|
||||||
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48049", "ArbitersSyndicate");
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804a", "CephalonSudaSyndicate");
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804e", "NewLokaSyndicate");
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48050", "PerrinSyndicate");
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4805e", "RedVeilSyndicate");
|
|
||||||
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48061", "SteelMeridianSyndicate");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
||||||
@ -474,6 +458,254 @@ const pushWeeklyActs = (worldState: IWorldState, week: number): void => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[], bountyCycle: number): void => {
|
||||||
|
const table = String.fromCharCode(65 + (bountyCycle % 3));
|
||||||
|
const vaultTable = String.fromCharCode(65 + ((bountyCycle + 1) % 3));
|
||||||
|
const deimosDTable = String.fromCharCode(65 + (bountyCycle % 2));
|
||||||
|
|
||||||
|
// TODO: xpAmounts need to be calculated based on the jobType somehow?
|
||||||
|
|
||||||
|
const seed = new CRng(bountyCycle).randomInt(0, 0xffff);
|
||||||
|
const bountyCycleStart = bountyCycle * 9000000;
|
||||||
|
const bountyCycleEnd = bountyCycleStart + 9000000;
|
||||||
|
|
||||||
|
{
|
||||||
|
const rng = new CRng(seed);
|
||||||
|
syndicateMissions.push({
|
||||||
|
_id: {
|
||||||
|
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
|
||||||
|
},
|
||||||
|
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
||||||
|
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
||||||
|
Tag: "CetusSyndicate",
|
||||||
|
Seed: seed,
|
||||||
|
Nodes: [],
|
||||||
|
Jobs: [
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATable${table}Rewards`,
|
||||||
|
masteryReq: 0,
|
||||||
|
minEnemyLevel: 5,
|
||||||
|
maxEnemyLevel: 15,
|
||||||
|
xpAmounts: [430, 430, 430]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTable${table}Rewards`,
|
||||||
|
masteryReq: 1,
|
||||||
|
minEnemyLevel: 10,
|
||||||
|
maxEnemyLevel: 30,
|
||||||
|
xpAmounts: [620, 620, 620]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTable${table}Rewards`,
|
||||||
|
masteryReq: 2,
|
||||||
|
minEnemyLevel: 20,
|
||||||
|
maxEnemyLevel: 40,
|
||||||
|
xpAmounts: [670, 670, 670, 990]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTable${table}Rewards`,
|
||||||
|
masteryReq: 3,
|
||||||
|
minEnemyLevel: 30,
|
||||||
|
maxEnemyLevel: 50,
|
||||||
|
xpAmounts: [570, 570, 570, 570, 1110]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 40,
|
||||||
|
maxEnemyLevel: 60,
|
||||||
|
xpAmounts: [740, 740, 740, 740, 1450]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
|
||||||
|
masteryReq: 10,
|
||||||
|
minEnemyLevel: 100,
|
||||||
|
maxEnemyLevel: 100,
|
||||||
|
xpAmounts: [840, 840, 840, 840, 1660]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(eidolonNarmerJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTable${table}Rewards`,
|
||||||
|
masteryReq: 0,
|
||||||
|
minEnemyLevel: 50,
|
||||||
|
maxEnemyLevel: 70,
|
||||||
|
xpAmounts: [840, 840, 840, 840, 1650]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const rng = new CRng(seed);
|
||||||
|
syndicateMissions.push({
|
||||||
|
_id: {
|
||||||
|
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000025"
|
||||||
|
},
|
||||||
|
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
||||||
|
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
||||||
|
Tag: "SolarisSyndicate",
|
||||||
|
Seed: seed,
|
||||||
|
Nodes: [],
|
||||||
|
Jobs: [
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATable${table}Rewards`,
|
||||||
|
masteryReq: 0,
|
||||||
|
minEnemyLevel: 5,
|
||||||
|
maxEnemyLevel: 15,
|
||||||
|
xpAmounts: [340, 340, 340]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTable${table}Rewards`,
|
||||||
|
masteryReq: 1,
|
||||||
|
minEnemyLevel: 10,
|
||||||
|
maxEnemyLevel: 30,
|
||||||
|
xpAmounts: [660, 660, 660]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTable${table}Rewards`,
|
||||||
|
masteryReq: 2,
|
||||||
|
minEnemyLevel: 20,
|
||||||
|
maxEnemyLevel: 40,
|
||||||
|
xpAmounts: [610, 610, 610, 900]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTable${table}Rewards`,
|
||||||
|
masteryReq: 3,
|
||||||
|
minEnemyLevel: 30,
|
||||||
|
maxEnemyLevel: 50,
|
||||||
|
xpAmounts: [600, 600, 600, 600, 1170]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 40,
|
||||||
|
maxEnemyLevel: 60,
|
||||||
|
xpAmounts: [690, 690, 690, 690, 1350]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
|
||||||
|
masteryReq: 10,
|
||||||
|
minEnemyLevel: 100,
|
||||||
|
maxEnemyLevel: 100,
|
||||||
|
xpAmounts: [840, 840, 840, 840, 1660]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(venusNarmerJobs),
|
||||||
|
rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTableBRewards",
|
||||||
|
masteryReq: 0,
|
||||||
|
minEnemyLevel: 50,
|
||||||
|
maxEnemyLevel: 70,
|
||||||
|
xpAmounts: [780, 780, 780, 780, 1540]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const rng = new CRng(seed);
|
||||||
|
syndicateMissions.push({
|
||||||
|
_id: {
|
||||||
|
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000002"
|
||||||
|
},
|
||||||
|
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
||||||
|
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
||||||
|
Tag: "EntratiSyndicate",
|
||||||
|
Seed: seed,
|
||||||
|
Nodes: [],
|
||||||
|
Jobs: [
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATable${table}Rewards`,
|
||||||
|
masteryReq: 0,
|
||||||
|
minEnemyLevel: 5,
|
||||||
|
maxEnemyLevel: 15,
|
||||||
|
xpAmounts: [5, 5, 5]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTable${table}Rewards`,
|
||||||
|
masteryReq: 1,
|
||||||
|
minEnemyLevel: 15,
|
||||||
|
maxEnemyLevel: 25,
|
||||||
|
xpAmounts: [12, 12, 12]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetEndlessJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTable${table}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 25,
|
||||||
|
maxEnemyLevel: 30,
|
||||||
|
endless: true,
|
||||||
|
xpAmounts: [14, 14, 14]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTable${deimosDTable}Rewards`,
|
||||||
|
masteryReq: 2,
|
||||||
|
minEnemyLevel: 30,
|
||||||
|
maxEnemyLevel: 40,
|
||||||
|
xpAmounts: [17, 17, 17, 25]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
|
||||||
|
masteryReq: 3,
|
||||||
|
minEnemyLevel: 40,
|
||||||
|
maxEnemyLevel: 60,
|
||||||
|
xpAmounts: [22, 22, 22, 22, 43]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jobType: rng.randomElement(microplanetJobs),
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
|
||||||
|
masteryReq: 10,
|
||||||
|
minEnemyLevel: 100,
|
||||||
|
maxEnemyLevel: 100,
|
||||||
|
xpAmounts: [25, 25, 25, 25, 50]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierATable${vaultTable}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 30,
|
||||||
|
maxEnemyLevel: 40,
|
||||||
|
xpAmounts: [2, 2, 2, 4],
|
||||||
|
locationTag: "ChamberB",
|
||||||
|
isVault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierBTable${vaultTable}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 40,
|
||||||
|
maxEnemyLevel: 50,
|
||||||
|
xpAmounts: [4, 4, 4, 5],
|
||||||
|
locationTag: "ChamberA",
|
||||||
|
isVault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierCTable${vaultTable}Rewards`,
|
||||||
|
masteryReq: 5,
|
||||||
|
minEnemyLevel: 50,
|
||||||
|
maxEnemyLevel: 60,
|
||||||
|
xpAmounts: [5, 5, 5, 7],
|
||||||
|
locationTag: "ChamberC",
|
||||||
|
isVault: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const birthdays: number[] = [
|
const birthdays: number[] = [
|
||||||
1, // Kaya
|
1, // Kaya
|
||||||
45, // Lettie
|
45, // Lettie
|
||||||
@ -816,249 +1048,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
Nodes: []
|
Nodes: []
|
||||||
});
|
});
|
||||||
|
|
||||||
const table = String.fromCharCode(65 + (bountyCycle % 3));
|
pushClassicBounties(worldState.SyndicateMissions, bountyCycle);
|
||||||
const vaultTable = String.fromCharCode(65 + ((bountyCycle + 1) % 3));
|
|
||||||
const deimosDTable = String.fromCharCode(65 + (bountyCycle % 2));
|
|
||||||
|
|
||||||
// TODO: xpAmounts need to be calculated based on the jobType somehow?
|
|
||||||
|
|
||||||
const seed = new CRng(bountyCycle).randomInt(0, 0xffff);
|
|
||||||
|
|
||||||
{
|
|
||||||
const rng = new CRng(seed);
|
|
||||||
worldState.SyndicateMissions.push({
|
|
||||||
_id: {
|
|
||||||
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
|
|
||||||
},
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
|
||||||
Tag: "CetusSyndicate",
|
|
||||||
Seed: seed,
|
|
||||||
Nodes: [],
|
|
||||||
Jobs: [
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATable${table}Rewards`,
|
|
||||||
masteryReq: 0,
|
|
||||||
minEnemyLevel: 5,
|
|
||||||
maxEnemyLevel: 15,
|
|
||||||
xpAmounts: [430, 430, 430]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTable${table}Rewards`,
|
|
||||||
masteryReq: 1,
|
|
||||||
minEnemyLevel: 10,
|
|
||||||
maxEnemyLevel: 30,
|
|
||||||
xpAmounts: [620, 620, 620]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTable${table}Rewards`,
|
|
||||||
masteryReq: 2,
|
|
||||||
minEnemyLevel: 20,
|
|
||||||
maxEnemyLevel: 40,
|
|
||||||
xpAmounts: [670, 670, 670, 990]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTable${table}Rewards`,
|
|
||||||
masteryReq: 3,
|
|
||||||
minEnemyLevel: 30,
|
|
||||||
maxEnemyLevel: 50,
|
|
||||||
xpAmounts: [570, 570, 570, 570, 1110]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 40,
|
|
||||||
maxEnemyLevel: 60,
|
|
||||||
xpAmounts: [740, 740, 740, 740, 1450]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
|
|
||||||
masteryReq: 10,
|
|
||||||
minEnemyLevel: 100,
|
|
||||||
maxEnemyLevel: 100,
|
|
||||||
xpAmounts: [840, 840, 840, 840, 1660]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(eidolonNarmerJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTable${table}Rewards`,
|
|
||||||
masteryReq: 0,
|
|
||||||
minEnemyLevel: 50,
|
|
||||||
maxEnemyLevel: 70,
|
|
||||||
xpAmounts: [840, 840, 840, 840, 1650]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const rng = new CRng(seed);
|
|
||||||
worldState.SyndicateMissions.push({
|
|
||||||
_id: {
|
|
||||||
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000025"
|
|
||||||
},
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
|
||||||
Tag: "SolarisSyndicate",
|
|
||||||
Seed: seed,
|
|
||||||
Nodes: [],
|
|
||||||
Jobs: [
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATable${table}Rewards`,
|
|
||||||
masteryReq: 0,
|
|
||||||
minEnemyLevel: 5,
|
|
||||||
maxEnemyLevel: 15,
|
|
||||||
xpAmounts: [340, 340, 340]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTable${table}Rewards`,
|
|
||||||
masteryReq: 1,
|
|
||||||
minEnemyLevel: 10,
|
|
||||||
maxEnemyLevel: 30,
|
|
||||||
xpAmounts: [660, 660, 660]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTable${table}Rewards`,
|
|
||||||
masteryReq: 2,
|
|
||||||
minEnemyLevel: 20,
|
|
||||||
maxEnemyLevel: 40,
|
|
||||||
xpAmounts: [610, 610, 610, 900]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTable${table}Rewards`,
|
|
||||||
masteryReq: 3,
|
|
||||||
minEnemyLevel: 30,
|
|
||||||
maxEnemyLevel: 50,
|
|
||||||
xpAmounts: [600, 600, 600, 600, 1170]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 40,
|
|
||||||
maxEnemyLevel: 60,
|
|
||||||
xpAmounts: [690, 690, 690, 690, 1350]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
|
|
||||||
masteryReq: 10,
|
|
||||||
minEnemyLevel: 100,
|
|
||||||
maxEnemyLevel: 100,
|
|
||||||
xpAmounts: [840, 840, 840, 840, 1660]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(venusNarmerJobs),
|
|
||||||
rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/NarmerTableBRewards",
|
|
||||||
masteryReq: 0,
|
|
||||||
minEnemyLevel: 50,
|
|
||||||
maxEnemyLevel: 70,
|
|
||||||
xpAmounts: [780, 780, 780, 780, 1540]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const rng = new CRng(seed);
|
|
||||||
worldState.SyndicateMissions.push({
|
|
||||||
_id: {
|
|
||||||
$oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000002"
|
|
||||||
},
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
|
||||||
Tag: "EntratiSyndicate",
|
|
||||||
Seed: seed,
|
|
||||||
Nodes: [],
|
|
||||||
Jobs: [
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATable${table}Rewards`,
|
|
||||||
masteryReq: 0,
|
|
||||||
minEnemyLevel: 5,
|
|
||||||
maxEnemyLevel: 15,
|
|
||||||
xpAmounts: [5, 5, 5]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTable${table}Rewards`,
|
|
||||||
masteryReq: 1,
|
|
||||||
minEnemyLevel: 15,
|
|
||||||
maxEnemyLevel: 25,
|
|
||||||
xpAmounts: [12, 12, 12]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetEndlessJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierBTable${table}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 25,
|
|
||||||
maxEnemyLevel: 30,
|
|
||||||
endless: true,
|
|
||||||
xpAmounts: [14, 14, 14]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTable${deimosDTable}Rewards`,
|
|
||||||
masteryReq: 2,
|
|
||||||
minEnemyLevel: 30,
|
|
||||||
maxEnemyLevel: 40,
|
|
||||||
xpAmounts: [17, 17, 17, 25]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
|
|
||||||
masteryReq: 3,
|
|
||||||
minEnemyLevel: 40,
|
|
||||||
maxEnemyLevel: 60,
|
|
||||||
xpAmounts: [22, 22, 22, 22, 43]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
jobType: rng.randomElement(microplanetJobs),
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
|
|
||||||
masteryReq: 10,
|
|
||||||
minEnemyLevel: 100,
|
|
||||||
maxEnemyLevel: 100,
|
|
||||||
xpAmounts: [25, 25, 25, 25, 50]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierATable${vaultTable}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 30,
|
|
||||||
maxEnemyLevel: 40,
|
|
||||||
xpAmounts: [2, 2, 2, 4],
|
|
||||||
locationTag: "ChamberB",
|
|
||||||
isVault: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierBTable${vaultTable}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 40,
|
|
||||||
maxEnemyLevel: 50,
|
|
||||||
xpAmounts: [4, 4, 4, 5],
|
|
||||||
locationTag: "ChamberA",
|
|
||||||
isVault: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/VaultBountyTierCTable${vaultTable}Rewards`,
|
|
||||||
masteryReq: 5,
|
|
||||||
minEnemyLevel: 50,
|
|
||||||
maxEnemyLevel: 60,
|
|
||||||
xpAmounts: [5, 5, 5, 7],
|
|
||||||
locationTag: "ChamberC",
|
|
||||||
isVault: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} while (isBeforeNextExpectedWorldStateRefresh(bountyCycleEnd) && ++bountyCycle);
|
} while (isBeforeNextExpectedWorldStateRefresh(bountyCycleEnd) && ++bountyCycle);
|
||||||
|
|
||||||
if (config.worldState?.creditBoost) {
|
if (config.worldState?.creditBoost) {
|
||||||
@ -1099,8 +1089,25 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
|
// Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
|
||||||
pushSortieIfRelevant(worldState, day - 1);
|
{
|
||||||
pushSortieIfRelevant(worldState, day);
|
const rollover = getSortieTime(day);
|
||||||
|
|
||||||
|
if (Date.now() < rollover) {
|
||||||
|
worldState.Sorties.push(getSortie(day - 1));
|
||||||
|
}
|
||||||
|
if (isBeforeNextExpectedWorldStateRefresh(rollover)) {
|
||||||
|
worldState.Sorties.push(getSortie(day));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The client does not seem to respect activation for classic syndicate missions, so only pushing current ones.
|
||||||
|
const rng = new CRng(Date.now() >= rollover ? day : day - 1);
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48049", "ArbitersSyndicate");
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804a", "CephalonSudaSyndicate");
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804e", "NewLokaSyndicate");
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48050", "PerrinSyndicate");
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4805e", "RedVeilSyndicate");
|
||||||
|
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48061", "SteelMeridianSyndicate");
|
||||||
|
}
|
||||||
|
|
||||||
// Archon Hunt cycling every week
|
// Archon Hunt cycling every week
|
||||||
worldState.LiteSorties.push(getLiteSortie(week));
|
worldState.LiteSorties.push(getLiteSortie(week));
|
||||||
@ -1178,8 +1185,16 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
|||||||
return worldState;
|
return worldState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const idToBountyCycle = (id: string): number => {
|
||||||
|
return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const idToDay = (id: string): number => {
|
||||||
|
return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 86400_000);
|
||||||
|
};
|
||||||
|
|
||||||
export const idToWeek = (id: string): number => {
|
export const idToWeek = (id: string): number => {
|
||||||
return (parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800000;
|
return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800_000);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLiteSortie = (week: number): ILiteSortie => {
|
export const getLiteSortie = (week: number): ILiteSortie => {
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
IOperatorConfigDatabase
|
IOperatorConfigDatabase
|
||||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { IFingerprintStat, RivenFingerprint } from "@/src/helpers/rivenHelper";
|
import { IFingerprintStat, RivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||||
|
import { IOrbiter } from "../personalRoomsTypes";
|
||||||
|
|
||||||
export type InventoryDatabaseEquipment = {
|
export type InventoryDatabaseEquipment = {
|
||||||
[_ in TEquipmentKey]: IEquipmentDatabase[];
|
[_ in TEquipmentKey]: IEquipmentDatabase[];
|
||||||
@ -373,6 +374,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
|||||||
BrandedSuits?: IOid[];
|
BrandedSuits?: IOid[];
|
||||||
LockedWeaponGroup?: ILockedWeaponGroupClient;
|
LockedWeaponGroup?: ILockedWeaponGroupClient;
|
||||||
HubNpcCustomizations?: IHubNpcCustomization[];
|
HubNpcCustomizations?: IHubNpcCustomization[];
|
||||||
|
Ship?: IOrbiter; // U22 and below, response only
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAffiliation {
|
export interface IAffiliation {
|
||||||
|
@ -35,11 +35,11 @@ export interface ILoginRequest {
|
|||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
time: number;
|
time: number;
|
||||||
s: string;
|
s?: string;
|
||||||
lang: string;
|
lang?: string;
|
||||||
date: number;
|
date: number;
|
||||||
ClientType: string;
|
ClientType?: string;
|
||||||
PS: string;
|
PS?: string;
|
||||||
kick?: boolean;
|
kick?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export interface ILoginResponse extends IAccountAndLoginResponseCommons {
|
|||||||
platformCDNs?: string[];
|
platformCDNs?: string[];
|
||||||
NRS?: string[];
|
NRS?: string[];
|
||||||
DTLS?: number;
|
DTLS?: number;
|
||||||
IRC: string[];
|
IRC?: string[];
|
||||||
HUB?: string;
|
HUB?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user