Compare commits

...

10 Commits

Author SHA1 Message Date
d15805bb19 feat: bounty bonus standing 2025-05-07 16:08:37 +02:00
bb606f3a95 fix: get bounty info by id to handle rollover (#1998)
Closes #1988

Reviewed-on: OpenWF/SpaceNinjaServer#1998
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 19:05:23 -07:00
4b12fe12cb feat: handle mechsuits in sellController (#1996)
Closes #1995

Reviewed-on: OpenWF/SpaceNinjaServer#1996
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 19:04:53 -07:00
4f28688837 feat: add CompletedVorsPrize to getAccountInfo response (#1994)
Closes #1941

Reviewed-on: OpenWF/SpaceNinjaServer#1994
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 19:04:39 -07:00
203b3e20d9 fix: refuse to give starting gear again (#1993)
Reviewed-on: OpenWF/SpaceNinjaServer#1993
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 19:04:22 -07:00
005690daa4 fix: handle "skip prologue" to visit orbiter in U14 (#1999)
Reviewed-on: OpenWF/SpaceNinjaServer#1999
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 07:37:30 -07:00
5fefd189af fix: login failure on U15 (#1997)
Reviewed-on: OpenWF/SpaceNinjaServer#1997
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 06:05:12 -07:00
f6cbc02c47 fix: ignore client providing not-an-id in recipe ids array (#1990)
Closes #1989

Reviewed-on: OpenWF/SpaceNinjaServer#1990
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 02:29:23 -07:00
da6d75c748 fix: don't give sortie assassination rewards if mission type differs (#1992)
Closes #1987

Reviewed-on: OpenWF/SpaceNinjaServer#1992
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 02:29:11 -07:00
460deed3ed fix: login failure on U16 (#1991)
Reviewed-on: OpenWF/SpaceNinjaServer#1991
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-05-06 02:29:03 -07:00
11 changed files with 371 additions and 306 deletions

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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[];

View File

@ -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}`);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}; };

View File

@ -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!];

View File

@ -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 => {

View File

@ -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 {

View File

@ -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;
} }