Compare commits
15 Commits
6bfb25a4b2
...
4acb16e6d6
Author | SHA1 | Date | |
---|---|---|---|
4acb16e6d6 | |||
c6acab6c11 | |||
bb315eaafe | |||
327b834b07 | |||
39be095818 | |||
ef4973e694 | |||
7f69667171 | |||
dcdeb0cd34 | |||
8ce86ad4fd | |||
a2f1469779 | |||
dd32e082f3 | |||
74c7d86090 | |||
7fd4d50e07 | |||
7f805a1dcc | |||
919f12b8f9 |
@ -34,8 +34,8 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
message.r = true;
|
||||
await message.save();
|
||||
|
||||
const attachmentItems = message.att;
|
||||
const attachmentCountedItems = message.countedAtt;
|
||||
const attachmentItems = message.attVisualOnly ? undefined : message.att;
|
||||
const attachmentCountedItems = message.attVisualOnly ? undefined : message.countedAtt;
|
||||
|
||||
if (!attachmentItems && !attachmentCountedItems && !message.gifts) {
|
||||
res.status(200).end();
|
||||
|
@ -63,6 +63,7 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
guild.VaultShipDecorations!.find(x => x.ItemType == itemType)!.ItemCount -= 1;
|
||||
}
|
||||
}
|
||||
if (deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco") {
|
||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0) || config.noDecoBuildStage) {
|
||||
deco.CompletionTime = new Date();
|
||||
} else if (guild.AutoContributeFromVault && guild.VaultRegularCredits && guild.VaultMiscItems) {
|
||||
@ -97,6 +98,7 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, component._id));
|
||||
|
34
src/controllers/api/setDojoComponentColorsController.ts
Normal file
34
src/controllers/api/setDojoComponentColorsController.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, hasGuildPermission } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setDojoComponentColorsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "GuildId LevelKeys");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Decorator))) {
|
||||
res.json({ DojoRequestStatus: -1 });
|
||||
return;
|
||||
}
|
||||
const data = getJSONfromString<ISetDojoComponentColorsRequest>(String(req.body));
|
||||
const component = guild.DojoComponents.id(data.ComponentId)!;
|
||||
//const deco = component.Decos!.find(x => x._id.equals(data.DecoId))!;
|
||||
//deco.Pending = true;
|
||||
//component.PaintBot = new Types.ObjectId(data.DecoId);
|
||||
if ("lights" in req.query) {
|
||||
component.PendingLights = data.Colours;
|
||||
} else {
|
||||
component.PendingColors = data.Colours;
|
||||
}
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, component._id));
|
||||
};
|
||||
|
||||
interface ISetDojoComponentColorsRequest {
|
||||
ComponentId: string;
|
||||
DecoId: string;
|
||||
Colours: number[];
|
||||
}
|
25
src/controllers/api/setDojoComponentSettingsController.ts
Normal file
25
src/controllers/api/setDojoComponentSettingsController.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, hasGuildPermission } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setDojoComponentSettingsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "GuildId LevelKeys");
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Decorator))) {
|
||||
res.json({ DojoRequestStatus: -1 });
|
||||
return;
|
||||
}
|
||||
const component = guild.DojoComponents.id(req.query.componentId)!;
|
||||
const data = getJSONfromString<ISetDojoComponentSettingsRequest>(String(req.body));
|
||||
component.Settings = data.Settings;
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, component._id));
|
||||
};
|
||||
|
||||
interface ISetDojoComponentSettingsRequest {
|
||||
Settings: string;
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addChallenges, addSeasonalChallengeHistory, getInventory } from "@/src/services/inventoryService";
|
||||
import { addChallenges, getInventory } from "@/src/services/inventoryService";
|
||||
import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportNightwave } from "warframe-public-export-plus";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IAffiliationMods } from "@/src/types/purchaseTypes";
|
||||
|
||||
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
|
||||
@ -12,41 +10,19 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
|
||||
const inventory = await getInventory(accountId, "ChallengeProgress SeasonChallengeHistory Affiliations");
|
||||
let affiliationMods: IAffiliationMods[] = [];
|
||||
if (challenges.ChallengeProgress) {
|
||||
addChallenges(inventory, challenges.ChallengeProgress);
|
||||
affiliationMods = addChallenges(inventory, challenges.ChallengeProgress, challenges.SeasonChallengeCompletions);
|
||||
}
|
||||
if (challenges.SeasonChallengeHistory) {
|
||||
addSeasonalChallengeHistory(inventory, challenges.SeasonChallengeHistory);
|
||||
}
|
||||
const affiliationMods: IAffiliationMods[] = [];
|
||||
if (challenges.ChallengeProgress && challenges.SeasonChallengeCompletions) {
|
||||
for (const challenge of challenges.SeasonChallengeCompletions) {
|
||||
// Ignore challenges that weren't completed just now
|
||||
if (!challenges.ChallengeProgress.find(x => challenge.challenge.indexOf(x.Name) != -1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const meta = ExportNightwave.challenges[challenge.challenge];
|
||||
logger.debug("Completed challenge", meta);
|
||||
|
||||
let affiliation = inventory.Affiliations.find(x => x.Tag == ExportNightwave.affiliationTag);
|
||||
if (!affiliation) {
|
||||
affiliation =
|
||||
inventory.Affiliations[
|
||||
inventory.Affiliations.push({
|
||||
Tag: ExportNightwave.affiliationTag,
|
||||
Standing: 0
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
affiliation.Standing += meta.standing;
|
||||
|
||||
if (affiliationMods.length == 0) {
|
||||
affiliationMods.push({ Tag: ExportNightwave.affiliationTag });
|
||||
}
|
||||
affiliationMods[0].Standing ??= 0;
|
||||
affiliationMods[0].Standing += meta.standing;
|
||||
challenges.SeasonChallengeHistory.forEach(({ challenge, id }) => {
|
||||
const itemIndex = inventory.SeasonChallengeHistory.findIndex(i => i.challenge === challenge);
|
||||
if (itemIndex !== -1) {
|
||||
inventory.SeasonChallengeHistory[itemIndex].id = id;
|
||||
} else {
|
||||
inventory.SeasonChallengeHistory.push({ challenge, id });
|
||||
}
|
||||
});
|
||||
}
|
||||
await inventory.save();
|
||||
|
||||
|
@ -150,9 +150,11 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
if (!item.hidden) {
|
||||
const resultName = getItemName(item.resultType);
|
||||
if (resultName) {
|
||||
let itemName = getString(resultName, lang);
|
||||
if (item.num > 1) itemName = `${itemName} X ${item.num}`;
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: recipeNameTemplate.replace("|ITEM|", getString(resultName, lang))
|
||||
name: recipeNameTemplate.replace("|ITEM|", itemName)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -218,6 +220,11 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(key.name || "", lang),
|
||||
chainLength: key.chainStages.length
|
||||
});
|
||||
} else if (key.name) {
|
||||
res.miscitems.push({
|
||||
uniqueName,
|
||||
name: getString(key.name, lang)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,16 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { CRng } from "@/src/services/rngService";
|
||||
import { ExportNightwave, ExportRegions } from "warframe-public-export-plus";
|
||||
|
||||
const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
|
||||
import {
|
||||
EPOCH,
|
||||
getSortieTime,
|
||||
missionTags,
|
||||
sortieBosses,
|
||||
sortieBossNode,
|
||||
sortieBossToFaction,
|
||||
sortieFactionToFactionIndexes,
|
||||
sortieFactionToSystemIndexes
|
||||
} from "@/src/helpers/worlstateHelper";
|
||||
|
||||
export const worldStateController: RequestHandler = (req, res) => {
|
||||
const day = Math.trunc((Date.now() - EPOCH) / 86400000);
|
||||
@ -27,6 +35,7 @@ export const worldStateController: RequestHandler = (req, res) => {
|
||||
Time: config.worldState?.lockTime || Math.round(Date.now() / 1000),
|
||||
Goals: [],
|
||||
GlobalUpgrades: [],
|
||||
Sorties: [],
|
||||
LiteSorties: [],
|
||||
EndlessXpChoices: [],
|
||||
SeasonInfo: {
|
||||
@ -154,6 +163,142 @@ export const worldStateController: RequestHandler = (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Sortie cycling every day
|
||||
{
|
||||
let genDay;
|
||||
let dayStart;
|
||||
let dayEnd;
|
||||
const sortieRolloverToday = getSortieTime(day);
|
||||
if (Date.now() < sortieRolloverToday) {
|
||||
// Early in the day, generate sortie for `day - 1`, expiring at `sortieRolloverToday`.
|
||||
genDay = day - 1;
|
||||
dayStart = getSortieTime(genDay);
|
||||
dayEnd = sortieRolloverToday;
|
||||
} else {
|
||||
// Late in the day, generate sortie for `day`, expiring at `getSortieTime(day + 1)`.
|
||||
genDay = day;
|
||||
dayStart = sortieRolloverToday;
|
||||
dayEnd = getSortieTime(day + 1);
|
||||
}
|
||||
|
||||
const rng = new CRng(genDay);
|
||||
|
||||
const boss = rng.randomElement(sortieBosses);
|
||||
|
||||
const modifiers = [
|
||||
"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", // TODO: push this if the mission tileset is Grineer Forest
|
||||
"SORTIE_MODIFIER_HAZARD_FIRE", // TODO: push this if the mission tileset is Corpus Ship or Grineer Galleon
|
||||
"SORTIE_MODIFIER_HAZARD_ICE",
|
||||
"SORTIE_MODIFIER_HAZARD_COLD",
|
||||
"SORTIE_MODIFIER_SECONDARY_ONLY",
|
||||
"SORTIE_MODIFIER_SHOTGUN_ONLY",
|
||||
"SORTIE_MODIFIER_SNIPER_ONLY",
|
||||
"SORTIE_MODIFIER_RIFLE_ONLY",
|
||||
"SORTIE_MODIFIER_MELEE_ONLY",
|
||||
"SORTIE_MODIFIER_BOW_ONLY"
|
||||
];
|
||||
|
||||
if (sortieBossToFaction[boss] == "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_SHIELDS");
|
||||
if (sortieBossToFaction[boss] != "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_ARMOR");
|
||||
|
||||
const nodes: string[] = [];
|
||||
const availableMissionIndexes: number[] = [];
|
||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||
if (
|
||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
||||
value.name.indexOf("Archwing") == -1 &&
|
||||
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
|
||||
value.missionIndex != 5 && // Exclude MT_CAPTURE
|
||||
value.missionIndex != 21 && // Exclude MT_PURIFY
|
||||
value.missionIndex != 23 && // Exclude MT_JUNCTION
|
||||
value.missionIndex <= 28
|
||||
) {
|
||||
if (!availableMissionIndexes.includes(value.missionIndex)) {
|
||||
availableMissionIndexes.push(value.missionIndex);
|
||||
}
|
||||
nodes.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const selectedNodes: { missionType: string; modifierType: string; node: string }[] = [];
|
||||
const missionTypes = new Set();
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const randomIndex = rng.randomInt(0, nodes.length - 1);
|
||||
const node = nodes[randomIndex];
|
||||
let missionIndex = ExportRegions[node].missionIndex;
|
||||
|
||||
if (
|
||||
!["SolNode404", "SolNode411"].includes(node) && // for some reason the game doesn't like missionType changes for these missions
|
||||
missionIndex != 28 &&
|
||||
rng.randomInt(0, 2) == 2
|
||||
) {
|
||||
missionIndex = rng.randomElement(availableMissionIndexes);
|
||||
}
|
||||
|
||||
if (i == 2 && rng.randomInt(0, 2) == 2) {
|
||||
const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY");
|
||||
const modifierType = rng.randomElement(filteredModifiers);
|
||||
|
||||
if (boss == "SORTIE_BOSS_PHORID") {
|
||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node });
|
||||
nodes.splice(randomIndex, 1);
|
||||
continue;
|
||||
} else if (sortieBossNode[boss]) {
|
||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node: sortieBossNode[boss] });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const missionType = missionTags[missionIndex];
|
||||
|
||||
if (missionTypes.has(missionType)) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
const filteredModifiers =
|
||||
missionType === "MT_TERRITORY"
|
||||
? modifiers.filter(mod => mod != "SORTIE_MODIFIER_HAZARD_RADIATION")
|
||||
: modifiers;
|
||||
|
||||
const modifierType = rng.randomElement(filteredModifiers);
|
||||
|
||||
selectedNodes.push({ missionType, modifierType, node });
|
||||
nodes.splice(randomIndex, 1);
|
||||
missionTypes.add(missionType);
|
||||
}
|
||||
|
||||
worldState.Sorties.push({
|
||||
_id: { $oid: Math.trunc(dayStart / 1000).toString(16) + "d4d932c97c0a3acd" },
|
||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
|
||||
Seed: genDay,
|
||||
Boss: boss,
|
||||
Variants: selectedNodes
|
||||
});
|
||||
}
|
||||
|
||||
// Archon Hunt cycling every week
|
||||
{
|
||||
const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
|
||||
@ -298,6 +443,7 @@ interface IWorldState {
|
||||
Goals: IGoal[];
|
||||
SyndicateMissions: ISyndicateMission[];
|
||||
GlobalUpgrades: IGlobalUpgrade[];
|
||||
Sorties: ISortie[];
|
||||
LiteSorties: ILiteSortie[];
|
||||
NodeOverrides: INodeOverride[];
|
||||
EndlessXpChoices: IEndlessXpChoice[];
|
||||
@ -361,6 +507,20 @@ interface INodeOverride {
|
||||
CustomNpcEncounters?: string;
|
||||
}
|
||||
|
||||
interface ISortie {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards";
|
||||
Seed: number;
|
||||
Boss: string;
|
||||
Variants: {
|
||||
missionType: string;
|
||||
modifierType: string;
|
||||
node: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface ILiteSortie {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
|
130
src/helpers/worlstateHelper.ts
Normal file
130
src/helpers/worlstateHelper.ts
Normal file
@ -0,0 +1,130 @@
|
||||
export const missionTags = [
|
||||
"MT_ASSASSINATION",
|
||||
"MT_EXTERMINATION",
|
||||
"MT_SURVIVAL",
|
||||
"MT_RESCUE",
|
||||
"MT_SABOTAGE",
|
||||
"MT_CAPTURE",
|
||||
"MT_COUNTER_INTEL",
|
||||
"MT_INTEL",
|
||||
"MT_DEFENSE",
|
||||
"MT_MOBILE_DEFENSE",
|
||||
"MT_PVP",
|
||||
"MT_MASTERY",
|
||||
"MT_RECOVERY",
|
||||
"MT_TERRITORY",
|
||||
"MT_RETRIEVAL",
|
||||
"MT_HIVE",
|
||||
"MT_SALVAGE",
|
||||
"MT_EXCAVATE",
|
||||
"MT_RAID",
|
||||
"MT_PURGE",
|
||||
"MT_GENERIC",
|
||||
"MT_PURIFY",
|
||||
"MT_ARENA",
|
||||
"MT_JUNCTION",
|
||||
"MT_PURSUIT",
|
||||
"MT_RACE",
|
||||
"MT_ASSAULT",
|
||||
"MT_EVACUATION",
|
||||
"MT_LANDSCAPE",
|
||||
"MT_RESOURCE_THEFT",
|
||||
"MT_ENDLESS_EXTERMINATION",
|
||||
"MT_ENDLESS_DUVIRI",
|
||||
"MT_RAILJACK",
|
||||
"MT_ARTIFACT",
|
||||
"MT_CORRUPTION",
|
||||
"MT_VOID_CASCADE",
|
||||
"MT_ARMAGEDDON",
|
||||
"MT_VAULTS",
|
||||
"MT_ALCHEMY",
|
||||
"MT_ASCENSION",
|
||||
"MT_ENDLESS_CAPTURE",
|
||||
"MT_OFFERING",
|
||||
"MT_PVPVE"
|
||||
];
|
||||
|
||||
export const sortieBosses = [
|
||||
"SORTIE_BOSS_HYENA",
|
||||
"SORTIE_BOSS_KELA",
|
||||
"SORTIE_BOSS_VOR",
|
||||
"SORTIE_BOSS_RUK",
|
||||
"SORTIE_BOSS_HEK",
|
||||
"SORTIE_BOSS_KRIL",
|
||||
"SORTIE_BOSS_TYL",
|
||||
"SORTIE_BOSS_JACKAL",
|
||||
"SORTIE_BOSS_ALAD",
|
||||
"SORTIE_BOSS_AMBULAS",
|
||||
"SORTIE_BOSS_NEF",
|
||||
"SORTIE_BOSS_RAPTOR",
|
||||
"SORTIE_BOSS_PHORID",
|
||||
"SORTIE_BOSS_LEPHANTIS",
|
||||
"SORTIE_BOSS_INFALAD",
|
||||
"SORTIE_BOSS_CORRUPTED_VOR"
|
||||
];
|
||||
|
||||
export const sortieBossToFaction: Record<string, string> = {
|
||||
SORTIE_BOSS_HYENA: "FC_CORPUS",
|
||||
SORTIE_BOSS_KELA: "FC_GRINEER",
|
||||
SORTIE_BOSS_VOR: "FC_GRINEER",
|
||||
SORTIE_BOSS_RUK: "FC_GRINEER",
|
||||
SORTIE_BOSS_HEK: "FC_GRINEER",
|
||||
SORTIE_BOSS_KRIL: "FC_GRINEER",
|
||||
SORTIE_BOSS_TYL: "FC_GRINEER",
|
||||
SORTIE_BOSS_JACKAL: "FC_CORPUS",
|
||||
SORTIE_BOSS_ALAD: "FC_CORPUS",
|
||||
SORTIE_BOSS_AMBULAS: "FC_CORPUS",
|
||||
SORTIE_BOSS_NEF: "FC_CORPUS",
|
||||
SORTIE_BOSS_RAPTOR: "FC_CORPUS",
|
||||
SORTIE_BOSS_PHORID: "FC_INFESTATION",
|
||||
SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION",
|
||||
SORTIE_BOSS_INFALAD: "FC_INFESTATION",
|
||||
SORTIE_BOSS_CORRUPTED_VOR: "FC_CORRUPTED"
|
||||
};
|
||||
|
||||
export const sortieFactionToSystemIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0, 2, 3, 5, 6, 9, 11, 18],
|
||||
FC_CORPUS: [1, 4, 7, 8, 12, 15],
|
||||
FC_INFESTATION: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15],
|
||||
FC_CORRUPTED: [14]
|
||||
};
|
||||
|
||||
export const sortieFactionToFactionIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0],
|
||||
FC_CORPUS: [1],
|
||||
FC_INFESTATION: [0, 1, 2],
|
||||
FC_CORRUPTED: [3]
|
||||
};
|
||||
|
||||
export const sortieBossNode: Record<string, string> = {
|
||||
SORTIE_BOSS_HYENA: "SolNode127",
|
||||
SORTIE_BOSS_KELA: "SolNode193",
|
||||
SORTIE_BOSS_VOR: "SolNode108",
|
||||
SORTIE_BOSS_RUK: "SolNode32",
|
||||
SORTIE_BOSS_HEK: "SolNode24",
|
||||
SORTIE_BOSS_KRIL: "SolNode99",
|
||||
SORTIE_BOSS_TYL: "SolNode105",
|
||||
SORTIE_BOSS_JACKAL: "SolNode104",
|
||||
SORTIE_BOSS_ALAD: "SolNode53",
|
||||
SORTIE_BOSS_AMBULAS: "SolNode51",
|
||||
SORTIE_BOSS_NEF: "SettlementNode20",
|
||||
SORTIE_BOSS_RAPTOR: "SolNode210",
|
||||
SORTIE_BOSS_LEPHANTIS: "SolNode712",
|
||||
SORTIE_BOSS_INFALAD: "SolNode705"
|
||||
};
|
||||
|
||||
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
|
||||
|
||||
export const getSortieTime = (day: number): number => {
|
||||
const dayStart = EPOCH + day * 86400000;
|
||||
const date = new Date(dayStart);
|
||||
date.setUTCHours(12);
|
||||
const isDst = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: "America/Toronto",
|
||||
timeZoneName: "short"
|
||||
})
|
||||
.formatToParts(date)
|
||||
.find(part => part.type === "timeZoneName")!
|
||||
.value.includes("DT");
|
||||
return dayStart + (isDst ? 16 : 17) * 3600000;
|
||||
};
|
@ -29,7 +29,8 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
|
||||
MiscItems: { type: [typeCountSchema], default: undefined },
|
||||
CompletionTime: Date,
|
||||
RushPlatinum: Number,
|
||||
PictureFrameInfo: pictureFrameInfoSchema
|
||||
PictureFrameInfo: pictureFrameInfoSchema,
|
||||
Pending: Boolean
|
||||
});
|
||||
|
||||
const dojoLeaderboardEntrySchema = new Schema<IDojoLeaderboardEntry>(
|
||||
@ -57,6 +58,12 @@ const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
||||
DestructionTime: Date,
|
||||
Decos: [dojoDecoSchema],
|
||||
DecoCapacity: Number,
|
||||
PaintBot: Schema.Types.ObjectId,
|
||||
PendingColors: { type: [Number], default: undefined },
|
||||
Colors: { type: [Number], default: undefined },
|
||||
PendingLights: { type: [Number], default: undefined },
|
||||
Lights: { type: [Number], default: undefined },
|
||||
Settings: String,
|
||||
Leaderboard: { type: [dojoLeaderboardEntrySchema], default: undefined }
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,8 @@ import { typeCountSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export interface IMessageClient extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId"> {
|
||||
export interface IMessageClient
|
||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly"> {
|
||||
_id?: IOid;
|
||||
date: IMongoDate;
|
||||
startDate?: IMongoDate;
|
||||
@ -29,6 +30,7 @@ export interface IMessage {
|
||||
endDate?: Date;
|
||||
att?: string[];
|
||||
countedAtt?: ITypeCount[];
|
||||
attVisualOnly?: boolean;
|
||||
transmission?: string;
|
||||
arg?: Arg[];
|
||||
gifts?: IGift[];
|
||||
@ -108,6 +110,7 @@ const messageSchema = new Schema<IMessageDatabase>(
|
||||
att: { type: [String], default: undefined },
|
||||
gifts: { type: [giftSchema], default: undefined },
|
||||
countedAtt: { type: [typeCountSchema], default: undefined },
|
||||
attVisualOnly: Boolean,
|
||||
transmission: String,
|
||||
arg: {
|
||||
type: [
|
||||
@ -141,6 +144,7 @@ messageSchema.set("toJSON", {
|
||||
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.attVisualOnly;
|
||||
|
||||
messageClient.date = toMongoDate(messageDatabase.date);
|
||||
|
||||
|
@ -872,6 +872,7 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
|
||||
OffensiveUpgrade: String,
|
||||
DefensiveUpgrade: String,
|
||||
UpgradesExpiry: Date,
|
||||
UmbraDate: Date,
|
||||
ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined },
|
||||
Weapon: crewShipWeaponSchema,
|
||||
Customization: crewShipCustomizationSchema,
|
||||
@ -902,6 +903,9 @@ EquipmentSchema.set("toJSON", {
|
||||
if (db.UpgradesExpiry) {
|
||||
client.UpgradesExpiry = toMongoDate(db.UpgradesExpiry);
|
||||
}
|
||||
if (db.UmbraDate) {
|
||||
client.UmbraDate = toMongoDate(db.UmbraDate);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -105,7 +105,9 @@ import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestCo
|
||||
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
|
||||
import { setAllianceGuildPermissionsController } from "@/src/controllers/api/setAllianceGuildPermissionsController";
|
||||
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
|
||||
import { setDojoComponentColorsController } from "@/src/controllers/api/setDojoComponentColorsController";
|
||||
import { setDojoComponentMessageController } from "@/src/controllers/api/setDojoComponentMessageController";
|
||||
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
|
||||
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
|
||||
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
|
||||
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
|
||||
@ -261,7 +263,9 @@ apiRouter.post("/saveLoadout.php", saveLoadoutController);
|
||||
apiRouter.post("/saveSettings.php", saveSettingsController);
|
||||
apiRouter.post("/saveVaultAutoContribute.php", saveVaultAutoContributeController);
|
||||
apiRouter.post("/sell.php", sellController);
|
||||
apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
|
||||
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
|
||||
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
|
||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
|
||||
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
|
||||
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
|
||||
|
@ -141,6 +141,7 @@ export const getDojoClient = async (
|
||||
DojoComponents: []
|
||||
};
|
||||
const roomsToRemove: Types.ObjectId[] = [];
|
||||
const decosToRemoveNoRefund: { componentId: Types.ObjectId; decoId: Types.ObjectId }[] = [];
|
||||
let needSave = false;
|
||||
for (const dojoComponent of guild.DojoComponents) {
|
||||
if (!componentId || dojoComponent._id.equals(componentId)) {
|
||||
@ -150,7 +151,8 @@ export const getDojoClient = async (
|
||||
ppf: dojoComponent.ppf,
|
||||
Name: dojoComponent.Name,
|
||||
Message: dojoComponent.Message,
|
||||
DecoCapacity: dojoComponent.DecoCapacity ?? 600
|
||||
DecoCapacity: dojoComponent.DecoCapacity ?? 600,
|
||||
Settings: dojoComponent.Settings
|
||||
};
|
||||
if (dojoComponent.pi) {
|
||||
clientComponent.pi = toOid(dojoComponent.pi);
|
||||
@ -212,6 +214,21 @@ export const getDojoClient = async (
|
||||
PictureFrameInfo: deco.PictureFrameInfo
|
||||
};
|
||||
if (deco.CompletionTime) {
|
||||
if (
|
||||
deco.Type == "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco" &&
|
||||
Date.now() >= deco.CompletionTime.getTime()
|
||||
) {
|
||||
if (dojoComponent.PendingColors) {
|
||||
dojoComponent.Colors = dojoComponent.PendingColors;
|
||||
dojoComponent.PendingColors = undefined;
|
||||
}
|
||||
if (dojoComponent.PendingLights) {
|
||||
dojoComponent.Lights = dojoComponent.PendingLights;
|
||||
dojoComponent.PendingLights = undefined;
|
||||
}
|
||||
decosToRemoveNoRefund.push({ componentId: dojoComponent._id, decoId: deco._id });
|
||||
continue;
|
||||
}
|
||||
clientDeco.CompletionTime = toMongoDate(deco.CompletionTime);
|
||||
} else {
|
||||
clientDeco.RegularCredits = deco.RegularCredits;
|
||||
@ -220,6 +237,10 @@ export const getDojoClient = async (
|
||||
clientComponent.Decos.push(clientDeco);
|
||||
}
|
||||
}
|
||||
clientComponent.PendingColors = dojoComponent.PendingColors;
|
||||
clientComponent.Colors = dojoComponent.Colors;
|
||||
clientComponent.PendingLights = dojoComponent.PendingLights;
|
||||
clientComponent.Lights = dojoComponent.Lights;
|
||||
dojo.DojoComponents.push(clientComponent);
|
||||
}
|
||||
}
|
||||
@ -230,6 +251,15 @@ export const getDojoClient = async (
|
||||
}
|
||||
needSave = true;
|
||||
}
|
||||
for (const deco of decosToRemoveNoRefund) {
|
||||
logger.debug(`removing polychrome`, deco);
|
||||
const component = guild.DojoComponents.id(deco.componentId)!;
|
||||
component.Decos!.splice(
|
||||
component.Decos!.findIndex(x => x._id.equals(deco.decoId)),
|
||||
1
|
||||
);
|
||||
needSave = true;
|
||||
}
|
||||
if (needSave) {
|
||||
await guild.save();
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ const convertEquipment = (client: IEquipmentClient): IEquipmentDatabase => {
|
||||
InfestationDate: convertOptionalDate(client.InfestationDate),
|
||||
Expiry: convertOptionalDate(client.Expiry),
|
||||
UpgradesExpiry: convertOptionalDate(client.UpgradesExpiry),
|
||||
UmbraDate: convertOptionalDate(client.UmbraDate),
|
||||
CrewMembers: client.CrewMembers ? convertCrewShipMembers(client.CrewMembers) : undefined,
|
||||
Details: client.Details ? convertKubrowDetails(client.Details) : undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { Types } from "mongoose";
|
||||
import { SlotNames, IInventoryChanges, IBinChanges, slotNames } from "@/src/types/purchaseTypes";
|
||||
import { SlotNames, IInventoryChanges, IBinChanges, slotNames, IAffiliationMods } from "@/src/types/purchaseTypes";
|
||||
import {
|
||||
IChallengeProgress,
|
||||
IFlavourItem,
|
||||
@ -45,6 +45,7 @@ import {
|
||||
ExportGear,
|
||||
ExportKeys,
|
||||
ExportMisc,
|
||||
ExportNightwave,
|
||||
ExportRailjackWeapons,
|
||||
ExportRecipes,
|
||||
ExportResources,
|
||||
@ -538,15 +539,9 @@ export const addItem = async (
|
||||
if (!key) return {};
|
||||
return { QuestKeys: [key] };
|
||||
} else {
|
||||
const key = { ItemType: typeName, ItemCount: quantity };
|
||||
|
||||
const index = inventory.LevelKeys.findIndex(levelKey => levelKey.ItemType == typeName);
|
||||
if (index != -1) {
|
||||
inventory.LevelKeys[index].ItemCount += quantity;
|
||||
} else {
|
||||
inventory.LevelKeys.push(key);
|
||||
}
|
||||
return { LevelKeys: [key] };
|
||||
const levelKeyChanges = [{ ItemType: typeName, ItemCount: quantity }];
|
||||
addLevelKeys(inventory, levelKeyChanges);
|
||||
return { LevelKeys: levelKeyChanges };
|
||||
}
|
||||
}
|
||||
if (typeName in ExportDrones) {
|
||||
@ -664,6 +659,8 @@ export const addItem = async (
|
||||
return {
|
||||
MiscItems: miscItemChanges
|
||||
};
|
||||
} else if (typeName == "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") {
|
||||
return addCrewShipHarness(inventory, typeName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1240,6 +1237,10 @@ export const addRecipes = (inventory: TInventoryDatabaseDocument, itemsArray: IT
|
||||
applyArrayChanges(inventory.Recipes, itemsArray);
|
||||
};
|
||||
|
||||
export const addLevelKeys = (inventory: TInventoryDatabaseDocument, itemsArray: ITypeCount[]): void => {
|
||||
applyArrayChanges(inventory.LevelKeys, itemsArray);
|
||||
};
|
||||
|
||||
export const addMods = (inventory: TInventoryDatabaseDocument, itemsArray: IRawUpgrade[]): void => {
|
||||
const { RawUpgrades } = inventory;
|
||||
|
||||
@ -1304,35 +1305,52 @@ export const addFocusXpIncreases = (inventory: TInventoryDatabaseDocument, focus
|
||||
inventory.DailyFocus -= focusXpPlus.reduce((a, b) => a + b, 0);
|
||||
};
|
||||
|
||||
export const addSeasonalChallengeHistory = (
|
||||
export const addChallenges = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
itemsArray: ISeasonChallenge[]
|
||||
): void => {
|
||||
const category = inventory.SeasonChallengeHistory;
|
||||
|
||||
itemsArray.forEach(({ challenge, id }) => {
|
||||
const itemIndex = category.findIndex(i => i.challenge === challenge);
|
||||
ChallengeProgress: IChallengeProgress[],
|
||||
SeasonChallengeCompletions: ISeasonChallenge[] | undefined
|
||||
): IAffiliationMods[] => {
|
||||
ChallengeProgress.forEach(({ Name, Progress }) => {
|
||||
const itemIndex = inventory.ChallengeProgress.findIndex(i => i.Name === Name);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
category[itemIndex].id = id;
|
||||
inventory.ChallengeProgress[itemIndex].Progress = Progress;
|
||||
} else {
|
||||
category.push({ challenge, id });
|
||||
inventory.ChallengeProgress.push({ Name, Progress });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addChallenges = (inventory: TInventoryDatabaseDocument, itemsArray: IChallengeProgress[]): void => {
|
||||
const category = inventory.ChallengeProgress;
|
||||
|
||||
itemsArray.forEach(({ Name, Progress }) => {
|
||||
const itemIndex = category.findIndex(i => i.Name === Name);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
category[itemIndex].Progress = Progress;
|
||||
} else {
|
||||
category.push({ Name, Progress });
|
||||
const affiliationMods: IAffiliationMods[] = [];
|
||||
if (SeasonChallengeCompletions) {
|
||||
for (const challenge of SeasonChallengeCompletions) {
|
||||
// Ignore challenges that weren't completed just now
|
||||
if (!ChallengeProgress.find(x => challenge.challenge.indexOf(x.Name) != -1)) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
const meta = ExportNightwave.challenges[challenge.challenge];
|
||||
logger.debug("Completed challenge", meta);
|
||||
|
||||
let affiliation = inventory.Affiliations.find(x => x.Tag == ExportNightwave.affiliationTag);
|
||||
if (!affiliation) {
|
||||
affiliation =
|
||||
inventory.Affiliations[
|
||||
inventory.Affiliations.push({
|
||||
Tag: ExportNightwave.affiliationTag,
|
||||
Standing: 0
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
affiliation.Standing += meta.standing;
|
||||
|
||||
if (affiliationMods.length == 0) {
|
||||
affiliationMods.push({ Tag: ExportNightwave.affiliationTag });
|
||||
}
|
||||
affiliationMods[0].Standing ??= 0;
|
||||
affiliationMods[0].Standing += meta.standing;
|
||||
}
|
||||
}
|
||||
return affiliationMods;
|
||||
};
|
||||
|
||||
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
addFusionTreasures,
|
||||
addGearExpByCategory,
|
||||
addItem,
|
||||
addLevelKeys,
|
||||
addMiscItems,
|
||||
addMissionComplete,
|
||||
addMods,
|
||||
@ -77,11 +78,8 @@ export const addMissionInventoryUpdates = async (
|
||||
inventoryUpdates: IMissionInventoryUpdateRequest
|
||||
): Promise<IInventoryChanges> => {
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (
|
||||
inventoryUpdates.EndOfMatchUpload &&
|
||||
inventoryUpdates.Missions &&
|
||||
inventoryUpdates.Missions.Tag in ExportRegions
|
||||
) {
|
||||
if (inventoryUpdates.EndOfMatchUpload) {
|
||||
if (inventoryUpdates.Missions && inventoryUpdates.Missions.Tag in ExportRegions) {
|
||||
const node = ExportRegions[inventoryUpdates.Missions.Tag];
|
||||
if (node.miscItemFee) {
|
||||
addMiscItems(inventory, [
|
||||
@ -92,28 +90,19 @@ export const addMissionInventoryUpdates = async (
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo) {
|
||||
if (inventoryUpdates.RewardInfo.periodicMissionTag) {
|
||||
const tag = inventoryUpdates.RewardInfo.periodicMissionTag;
|
||||
const existingCompletion = inventory.PeriodicMissionCompletions.find(completion => completion.tag === tag);
|
||||
|
||||
if (existingCompletion) {
|
||||
existingCompletion.date = new Date();
|
||||
} else {
|
||||
inventory.PeriodicMissionCompletions.push({
|
||||
tag: tag,
|
||||
date: new Date()
|
||||
});
|
||||
if (inventoryUpdates.KeyToRemove) {
|
||||
if (!inventoryUpdates.KeyOwner || inventory.accountOwnerId.equals(inventoryUpdates.KeyOwner)) {
|
||||
addLevelKeys(inventory, [
|
||||
{
|
||||
ItemType: inventoryUpdates.KeyToRemove,
|
||||
ItemCount: -1
|
||||
}
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
|
||||
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (
|
||||
inventoryUpdates.MissionFailed &&
|
||||
inventoryUpdates.MissionStatus == "GS_FAILURE" &&
|
||||
inventoryUpdates.EndOfMatchUpload &&
|
||||
inventoryUpdates.ObjectiveReached &&
|
||||
!inventoryUpdates.LockedWeaponGroup
|
||||
) {
|
||||
@ -136,6 +125,25 @@ export const addMissionInventoryUpdates = async (
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo) {
|
||||
if (inventoryUpdates.RewardInfo.periodicMissionTag) {
|
||||
const tag = inventoryUpdates.RewardInfo.periodicMissionTag;
|
||||
const existingCompletion = inventory.PeriodicMissionCompletions.find(completion => completion.tag === tag);
|
||||
|
||||
if (existingCompletion) {
|
||||
existingCompletion.date = new Date();
|
||||
} else {
|
||||
inventory.PeriodicMissionCompletions.push({
|
||||
tag: tag,
|
||||
date: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
|
||||
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
|
||||
}
|
||||
}
|
||||
for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
|
||||
if (value === undefined) {
|
||||
logger.error(`Inventory update key ${key} has no value `);
|
||||
@ -187,7 +195,7 @@ export const addMissionInventoryUpdates = async (
|
||||
addRecipes(inventory, value);
|
||||
break;
|
||||
case "ChallengeProgress":
|
||||
addChallenges(inventory, value);
|
||||
addChallenges(inventory, value, inventoryUpdates.SeasonChallengeCompletions);
|
||||
break;
|
||||
case "FusionTreasures":
|
||||
addFusionTreasures(inventory, value);
|
||||
@ -594,10 +602,24 @@ export const addMissionRewards = async (
|
||||
if (!droptable) {
|
||||
logger.error(`unknown droptable ${si.DropTable}`);
|
||||
} else {
|
||||
const modsPool = droptable[0].items;
|
||||
const blueprintsPool = (droptable.length > 1 ? droptable[1] : droptable[0]).items;
|
||||
if (si.DROP_MOD) {
|
||||
for (let i = 0; i != si.DROP_MOD.length; ++i) {
|
||||
for (const pool of droptable) {
|
||||
const reward = getRandomReward(pool.items)!;
|
||||
logger.debug(`stripped droptable rolled`, reward);
|
||||
const reward = getRandomReward(modsPool)!;
|
||||
logger.debug(`stripped droptable (mods pool) rolled`, reward);
|
||||
await addItem(inventory, reward.type);
|
||||
MissionRewards.push({
|
||||
StoreItem: toStoreItem(reward.type),
|
||||
ItemCount: 1,
|
||||
FromEnemyCache: true // to show "identified"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (si.DROP_BLUEPRINT) {
|
||||
for (let i = 0; i != si.DROP_BLUEPRINT.length; ++i) {
|
||||
const reward = getRandomReward(blueprintsPool)!;
|
||||
logger.debug(`stripped droptable (blueprints pool) rolled`, reward);
|
||||
await addItem(inventory, reward.type);
|
||||
MissionRewards.push({
|
||||
StoreItem: toStoreItem(reward.type),
|
||||
|
@ -161,6 +161,7 @@ export interface IDojoClient {
|
||||
|
||||
export interface IDojoComponentClient {
|
||||
id: IOid;
|
||||
SortId?: IOid;
|
||||
pf: string; // Prefab (.level)
|
||||
ppf: string;
|
||||
pi?: IOid; // Parent ID. N/A to root.
|
||||
@ -175,16 +176,26 @@ export interface IDojoComponentClient {
|
||||
DestructionTime?: IMongoDate;
|
||||
Decos?: IDojoDecoClient[];
|
||||
DecoCapacity?: number;
|
||||
PaintBot?: IOid;
|
||||
PendingColors?: number[];
|
||||
Colors?: number[];
|
||||
PendingLights?: number[];
|
||||
Lights?: number[];
|
||||
Settings?: string;
|
||||
}
|
||||
|
||||
export interface IDojoComponentDatabase
|
||||
extends Omit<IDojoComponentClient, "id" | "pi" | "CompletionTime" | "DestructionTime" | "Decos"> {
|
||||
extends Omit<
|
||||
IDojoComponentClient,
|
||||
"id" | "SortId" | "pi" | "CompletionTime" | "DestructionTime" | "Decos" | "PaintBot"
|
||||
> {
|
||||
_id: Types.ObjectId;
|
||||
pi?: Types.ObjectId;
|
||||
CompletionTime?: Date;
|
||||
CompletionLogPending?: boolean;
|
||||
DestructionTime?: Date;
|
||||
Decos?: IDojoDecoDatabase[];
|
||||
PaintBot?: Types.ObjectId;
|
||||
Leaderboard?: IDojoLeaderboardEntry[];
|
||||
}
|
||||
|
||||
@ -200,6 +211,7 @@ export interface IDojoDecoClient {
|
||||
CompletionTime?: IMongoDate;
|
||||
RushPlatinum?: number;
|
||||
PictureFrameInfo?: IPictureFrameInfo;
|
||||
Pending?: boolean;
|
||||
}
|
||||
|
||||
export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "CompletionTime"> {
|
||||
|
@ -90,12 +90,13 @@ export interface IEquipmentSelection {
|
||||
export interface IEquipmentClient
|
||||
extends Omit<
|
||||
IEquipmentDatabase,
|
||||
"_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "CrewMembers" | "Details"
|
||||
"_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "UmbraDate" | "CrewMembers" | "Details"
|
||||
> {
|
||||
ItemId: IOid;
|
||||
InfestationDate?: IMongoDate;
|
||||
Expiry?: IMongoDate;
|
||||
UpgradesExpiry?: IMongoDate;
|
||||
UmbraDate?: IMongoDate;
|
||||
CrewMembers?: ICrewShipMembersClient;
|
||||
Details?: IKubrowPetDetailsClient;
|
||||
}
|
||||
@ -134,6 +135,7 @@ export interface IEquipmentDatabase {
|
||||
OffensiveUpgrade?: string;
|
||||
DefensiveUpgrade?: string;
|
||||
UpgradesExpiry?: Date;
|
||||
UmbraDate?: Date; // related to scrapped "echoes of umbra" feature
|
||||
ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
|
||||
Weapon?: ICrewShipWeapon;
|
||||
Customization?: ICrewShipCustomization;
|
||||
|
@ -49,6 +49,9 @@ export type IMissionInventoryUpdateRequest = {
|
||||
rewardsMultiplier?: number;
|
||||
GoalTag: string;
|
||||
LevelKeyName: string;
|
||||
KeyOwner?: string;
|
||||
KeyRemovalHash?: string;
|
||||
KeyToRemove?: string;
|
||||
ActiveBoosters?: IBooster[];
|
||||
RawUpgrades?: IRawUpgrade[];
|
||||
FusionTreasures?: IFusionTreasure[];
|
||||
@ -98,7 +101,8 @@ export type IMissionInventoryUpdateRequest = {
|
||||
Upgrades?: IUpgradeClient[]; // riven challenge progress
|
||||
StrippedItems?: {
|
||||
DropTable: string;
|
||||
DROP_MOD: number[];
|
||||
DROP_MOD?: number[];
|
||||
DROP_BLUEPRINT?: number[];
|
||||
}[];
|
||||
DeathMarks?: string[];
|
||||
Nemesis?: number;
|
||||
|
@ -62,23 +62,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"Sorties": [
|
||||
{
|
||||
"_id": { "$oid": "663a4c7d4d932c97c0a3acd7" },
|
||||
"Activation": { "$date": { "$numberLong": "1715097600000" } },
|
||||
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
|
||||
"Reward": "/Lotus/Types/Game/MissionDecks/SortieRewards",
|
||||
"Seed": 24491,
|
||||
"Boss": "SORTIE_BOSS_TYL",
|
||||
"ExtraDrops": [],
|
||||
"Variants": [
|
||||
{ "missionType": "MT_TERRITORY", "modifierType": "SORTIE_MODIFIER_ARMOR", "node": "SolNode122", "tileset": "GrineerOceanTileset" },
|
||||
{ "missionType": "MT_MOBILE_DEFENSE", "modifierType": "SORTIE_MODIFIER_LOW_ENERGY", "node": "SolNode184", "tileset": "GrineerGalleonTileset" },
|
||||
{ "missionType": "MT_LANDSCAPE", "modifierType": "SORTIE_MODIFIER_EXIMUS", "node": "SolNode228", "tileset": "EidolonTileset" }
|
||||
],
|
||||
"Twitter": true
|
||||
}
|
||||
],
|
||||
"SyndicateMissions": [
|
||||
{
|
||||
"_id": { "$oid": "663a4fc5ba6f84724fa48049" },
|
||||
|
@ -221,6 +221,11 @@ function fetchItemList() {
|
||||
name: loc("code_zanuka")
|
||||
});
|
||||
|
||||
data.miscitems.push({
|
||||
uniqueName: "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment",
|
||||
name: loc("code_pigment")
|
||||
});
|
||||
|
||||
const itemMap = {
|
||||
// Generics for rivens
|
||||
"/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: loc("code_archgun") },
|
||||
@ -260,6 +265,7 @@ function fetchItemList() {
|
||||
} else if (type == "uniqueLevelCaps") {
|
||||
uniqueLevelCaps = items;
|
||||
} else {
|
||||
const nameSet = new Set();
|
||||
items.forEach(item => {
|
||||
if (item.name.includes("<ARCHWING> ")) {
|
||||
item.name = item.name.replace("<ARCHWING> ", "");
|
||||
@ -306,20 +312,19 @@ function fetchItemList() {
|
||||
document
|
||||
.getElementById("datalist-" + type + "-" + item.partType.slice(5))
|
||||
.appendChild(option);
|
||||
}
|
||||
} else if (item.badReason != "notraw") {
|
||||
if (nameSet.has(item.name)) {
|
||||
//console.log(`Not adding ${item.uniqueName} to datalist for ${type} due to duplicate display name: ${item.name}`);
|
||||
} else {
|
||||
console.log(item.partType);
|
||||
nameSet.add(item.name);
|
||||
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", item.uniqueName);
|
||||
option.value = item.name;
|
||||
document.getElementById("datalist-" + type).appendChild(option);
|
||||
}
|
||||
}
|
||||
if (item.badReason != "notraw") {
|
||||
const option = document.createElement("option");
|
||||
option.setAttribute("data-key", item.uniqueName);
|
||||
option.value = item.name;
|
||||
document.getElementById("datalist-" + type).appendChild(option);
|
||||
}
|
||||
itemMap[item.uniqueName] = { ...item, type };
|
||||
});
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ dict = {
|
||||
code_setInactive: `Quest inaktiv setzen`,
|
||||
code_completed: `Abgeschlossen`,
|
||||
code_active: `Aktiv`,
|
||||
code_pigment: `Pigment`,
|
||||
login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
|
||||
login_emailLabel: `E-Mail-Adresse`,
|
||||
login_passwordLabel: `Passwort`,
|
||||
|
@ -52,6 +52,7 @@ dict = {
|
||||
code_setInactive: `Make the quest inactive`,
|
||||
code_completed: `Completed`,
|
||||
code_active: `Active`,
|
||||
code_pigment: `Pigment`,
|
||||
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
|
||||
login_emailLabel: `Email address`,
|
||||
login_passwordLabel: `Password`,
|
||||
|
@ -53,6 +53,7 @@ dict = {
|
||||
code_setInactive: `[UNTRANSLATED] Make the quest inactive`,
|
||||
code_completed: `[UNTRANSLATED] Completed`,
|
||||
code_active: `[UNTRANSLATED] Active`,
|
||||
code_pigment: `Pigment`,
|
||||
login_description: `Connexion avec les informations de connexion OpenWF.`,
|
||||
login_emailLabel: `Email`,
|
||||
login_passwordLabel: `Mot de passe`,
|
||||
|
@ -53,6 +53,7 @@ dict = {
|
||||
code_setInactive: `Сделать квест неактивным`,
|
||||
code_completed: `Завершено`,
|
||||
code_active: `Активный`,
|
||||
code_pigment: `Пигмент`,
|
||||
login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
|
||||
login_emailLabel: `Адрес электронной почты`,
|
||||
login_passwordLabel: `Пароль`,
|
||||
|
@ -53,6 +53,7 @@ dict = {
|
||||
code_setInactive: `[UNTRANSLATED] Make the quest inactive`,
|
||||
code_completed: `[UNTRANSLATED] Completed`,
|
||||
code_active: `[UNTRANSLATED] Active`,
|
||||
code_pigment: `颜料`,
|
||||
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)。`,
|
||||
login_emailLabel: `电子邮箱`,
|
||||
login_passwordLabel: `密码`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user