forked from OpenWF/SpaceNinjaServer
Compare commits
17 Commits
f3e56480e5
...
267357871b
Author | SHA1 | Date | |
---|---|---|---|
267357871b | |||
cf5ed0442d | |||
de36e2ee8d | |||
ca1b6c31b6 | |||
d66c474bfc | |||
781f01520f | |||
ac37702468 | |||
75c011e3cb | |||
4d4f885c8e | |||
66d1a65e63 | |||
48eefd8db1 | |||
4a6a5ea9cc | |||
95c0ad7892 | |||
a90d3a5156 | |||
d0c9409a2d | |||
bbde7b2141 | |||
5271123090 |
@ -1,7 +1,6 @@
|
||||
@echo off
|
||||
|
||||
echo Updating SpaceNinjaServer...
|
||||
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
|
||||
git fetch --prune
|
||||
git stash
|
||||
git reset --hard origin/main
|
||||
|
@ -19,6 +19,7 @@
|
||||
"infiniteEndo": false,
|
||||
"infiniteRegalAya": false,
|
||||
"infiniteHelminthMaterials": false,
|
||||
"dontSubtractConsumables": false,
|
||||
"unlockAllShipFeatures": false,
|
||||
"unlockAllShipDecorations": false,
|
||||
"unlockAllFlavourItems": false,
|
||||
|
@ -62,14 +62,7 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
|
||||
} satisfies IInnateDamageFingerprint)
|
||||
};
|
||||
}
|
||||
addEquipment(
|
||||
inventory,
|
||||
"CrewShipSalvagedWeapons",
|
||||
payload.ItemType,
|
||||
undefined,
|
||||
inventoryChanges,
|
||||
defaultOverwrites
|
||||
);
|
||||
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, defaultOverwrites, inventoryChanges);
|
||||
}
|
||||
|
||||
inventoryChanges.CrewShipRawSalvage = [
|
||||
|
@ -104,13 +104,14 @@ export const focusController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
case FocusOperation.SentTrainingAmplifier: {
|
||||
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
|
||||
const parts: string[] = [
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
|
||||
ModularParts: [
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
|
||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
|
||||
];
|
||||
const inventory = await getInventory(accountId);
|
||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
|
||||
]
|
||||
});
|
||||
occupySlot(inventory, InventorySlot.AMPS, false);
|
||||
await inventory.save();
|
||||
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
|
||||
|
84
src/controllers/api/gardeningController.ts
Normal file
84
src/controllers/api/gardeningController.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
|
||||
import { toStoreItem } from "@/src/services/itemDataService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IMongoDate } from "@/src/types/commonTypes";
|
||||
import { IMissionReward } from "@/src/types/missionTypes";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { IGardeningClient } from "@/src/types/shipTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { dict_en, ExportResources } from "warframe-public-export-plus";
|
||||
|
||||
export const gardeningController: RequestHandler = async (req, res) => {
|
||||
const data = getJSONfromString<IGardeningRequest>(String(req.body));
|
||||
if (data.Mode != "HarvestAll") {
|
||||
throw new Error(`unexpected gardening mode: ${data.Mode}`);
|
||||
}
|
||||
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const [inventory, personalRooms] = await Promise.all([
|
||||
getInventory(accountId, "MiscItems"),
|
||||
getPersonalRooms(accountId, "Apartment")
|
||||
]);
|
||||
|
||||
// Harvest plants
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const rewards: Record<string, IMissionReward[][]> = {};
|
||||
for (const planter of personalRooms.Apartment.Gardening.Planters) {
|
||||
rewards[planter.Name] = [];
|
||||
for (const plant of planter.Plants) {
|
||||
const itemType =
|
||||
"/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" +
|
||||
plant.PlantType.substring(plant.PlantType.length - 1);
|
||||
const itemCount = Math.random() < 0.775 ? 2 : 4;
|
||||
|
||||
addMiscItem(inventory, itemType, itemCount, inventoryChanges);
|
||||
|
||||
rewards[planter.Name].push([
|
||||
{
|
||||
StoreItem: toStoreItem(itemType),
|
||||
TypeName: itemType,
|
||||
ItemCount: itemCount,
|
||||
DailyCooldown: false,
|
||||
Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564,
|
||||
TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`,
|
||||
ProductCategory: "MiscItems"
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh garden
|
||||
personalRooms.Apartment.Gardening = createGarden();
|
||||
|
||||
await Promise.all([inventory.save(), personalRooms.save()]);
|
||||
|
||||
const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1];
|
||||
const plant = planter.Plants[planter.Plants.length - 1];
|
||||
res.json({
|
||||
GardenTagName: planter.Name,
|
||||
PlantType: plant.PlantType,
|
||||
PlotIndex: plant.PlotIndex,
|
||||
EndTime: toMongoDate(plant.EndTime),
|
||||
InventoryChanges: inventoryChanges,
|
||||
Gardening: personalRooms.toJSON<IPersonalRoomsClient>().Apartment.Gardening,
|
||||
Rewards: rewards
|
||||
} satisfies IGardeningResponse);
|
||||
};
|
||||
|
||||
interface IGardeningRequest {
|
||||
Mode: string;
|
||||
}
|
||||
|
||||
interface IGardeningResponse {
|
||||
GardenTagName: string;
|
||||
PlantType: string;
|
||||
PlotIndex: number;
|
||||
EndTime: IMongoDate;
|
||||
InventoryChanges: IInventoryChanges;
|
||||
Gardening: IGardeningClient;
|
||||
Rewards: Record<string, IMissionReward[][]>;
|
||||
}
|
@ -2,17 +2,24 @@ import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getShip } from "@/src/services/shipService";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { IGetShipResponse } from "@/src/types/shipTypes";
|
||||
import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { getLoadout } from "@/src/services/loadoutService";
|
||||
|
||||
export const getShipController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRoomsDb = await getPersonalRooms(accountId);
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRooms>();
|
||||
|
||||
// Setup gardening if it's missing. Maybe should be done as part of some quest completion in the future.
|
||||
if (personalRoomsDb.Apartment.Gardening.Planters.length == 0) {
|
||||
personalRoomsDb.Apartment.Gardening = createGarden();
|
||||
await personalRoomsDb.save();
|
||||
}
|
||||
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||
const loadout = await getLoadout(accountId);
|
||||
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");
|
||||
|
||||
|
@ -441,16 +441,9 @@ const finishComponentRepair = (
|
||||
const inventoryChanges = {
|
||||
...(category == "CrewShipWeaponSkins"
|
||||
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
||||
: addEquipment(
|
||||
inventory,
|
||||
category,
|
||||
salvageItem.ItemType,
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
: addEquipment(inventory, category, salvageItem.ItemType, {
|
||||
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||
}
|
||||
)),
|
||||
})),
|
||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,9 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
let defaultUpgrades: IDefaultUpgrade[] | undefined;
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {};
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
ModularParts: data.Parts
|
||||
};
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (category == "KubrowPets") {
|
||||
const traits = {
|
||||
@ -151,7 +153,7 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
}
|
||||
}
|
||||
defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||
addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
|
||||
addEquipment(inventory, category, data.WeaponType, defaultOverwrites, inventoryChanges);
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||
|
@ -21,7 +21,11 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const partTypeToParts: Record<string, string[]> = {};
|
||||
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
||||
if (data.partType && data.premiumPrice) {
|
||||
if (
|
||||
data.partType &&
|
||||
data.premiumPrice &&
|
||||
!data.excludeFromCodex // exclude pvp variants
|
||||
) {
|
||||
partTypeToParts[data.partType] ??= [];
|
||||
partTypeToParts[data.partType].push(uniqueName);
|
||||
}
|
||||
@ -41,24 +45,18 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
||||
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||
const inventoryChanges: IInventoryChanges = {
|
||||
...addEquipment(
|
||||
inventory,
|
||||
category,
|
||||
weaponInfo.ItemType,
|
||||
weaponInfo.ModularParts,
|
||||
{},
|
||||
{
|
||||
...addEquipment(inventory, category, weaponInfo.ItemType, {
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
||||
ItemName: payload.ItemName,
|
||||
Configs: configs,
|
||||
ModularParts: weaponInfo.ModularParts,
|
||||
Polarity: [
|
||||
{
|
||||
Slot: payload.PolarizeSlot,
|
||||
Value: payload.PolarizeValue
|
||||
}
|
||||
]
|
||||
}
|
||||
),
|
||||
}),
|
||||
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
||||
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
||||
};
|
||||
|
@ -4,7 +4,6 @@ import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inven
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
@ -27,7 +26,6 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
const dialogue = getDialogue(inventory, request.DialogueName);
|
||||
dialogue.Rank = request.Rank;
|
||||
dialogue.Chemistry = request.Chemistry;
|
||||
if (request.Data) {
|
||||
dialogue.QueuedDialogues = request.QueuedDialogues;
|
||||
for (const bool of request.Booleans) {
|
||||
dialogue.Booleans.push(bool);
|
||||
@ -45,8 +43,6 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
dialogue.Booleans.splice(index, 1);
|
||||
}
|
||||
}
|
||||
dialogue.Completed.push(request.Data);
|
||||
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
||||
for (const info of request.OtherDialogueInfos) {
|
||||
const otherDialogue = getDialogue(inventory, info.Dialogue);
|
||||
if (info.Tag != "") {
|
||||
@ -54,6 +50,9 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
otherDialogue.Chemistry += info.Value; // unsure
|
||||
}
|
||||
if (request.Data) {
|
||||
dialogue.Completed.push(request.Data);
|
||||
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
@ -74,7 +73,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||
});
|
||||
} else {
|
||||
logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
44
src/controllers/custom/addMissingMaxRankModsController.ts
Normal file
44
src/controllers/custom/addMissingMaxRankModsController.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus";
|
||||
|
||||
export const addMissingMaxRankModsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId, "Upgrades");
|
||||
|
||||
const maxOwnedRanks: Record<string, number> = {};
|
||||
for (const upgrade of inventory.Upgrades) {
|
||||
const fingerprint = JSON.parse(upgrade.UpgradeFingerprint ?? "{}") as { lvl?: number };
|
||||
if (fingerprint.lvl) {
|
||||
maxOwnedRanks[upgrade.ItemType] ??= 0;
|
||||
if (fingerprint.lvl > maxOwnedRanks[upgrade.ItemType]) {
|
||||
maxOwnedRanks[upgrade.ItemType] = fingerprint.lvl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [uniqueName, data] of Object.entries(ExportUpgrades)) {
|
||||
if (data.fusionLimit != 0 && data.type != "PARAZON" && maxOwnedRanks[uniqueName] != data.fusionLimit) {
|
||||
inventory.Upgrades.push({
|
||||
ItemType: uniqueName,
|
||||
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [uniqueName, data] of Object.entries(ExportArcanes)) {
|
||||
if (
|
||||
data.name != "/Lotus/Language/Items/GenericCosmeticEnhancerName" &&
|
||||
maxOwnedRanks[uniqueName] != data.fusionLimit
|
||||
) {
|
||||
inventory.Upgrades.push({
|
||||
ItemType: uniqueName,
|
||||
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
@ -96,7 +96,8 @@ import {
|
||||
IInvasionProgressDatabase,
|
||||
IInvasionProgressClient,
|
||||
IAccolades,
|
||||
IHubNpcCustomization
|
||||
IHubNpcCustomization,
|
||||
ILotusCustomization
|
||||
} from "../../types/inventoryTypes/inventoryTypes";
|
||||
import { IOid } from "../../types/commonTypes";
|
||||
import {
|
||||
@ -780,6 +781,10 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({
|
||||
Persona: String
|
||||
});
|
||||
|
||||
const evolutionProgressSchema = new Schema<IEvolutionProgress>(
|
||||
{
|
||||
Progress: Number,
|
||||
@ -1628,7 +1633,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter.
|
||||
//https://warframe.fandom.com/wiki/Lotus#The_New_War
|
||||
LotusCustomization: Schema.Types.Mixed,
|
||||
LotusCustomization: { type: lotusCustomizationSchema, default: undefined },
|
||||
|
||||
//Progress+Rank+ItemType(ZarimanPumpShotgun)
|
||||
//https://warframe.fandom.com/wiki/Incarnon
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
|
||||
import {
|
||||
IFavouriteLoadoutDatabase,
|
||||
IGardening,
|
||||
IGardeningDatabase,
|
||||
IPlacedDecosDatabase,
|
||||
IPictureFrameInfo,
|
||||
IRoom,
|
||||
ITailorShopDatabase,
|
||||
IApartmentDatabase
|
||||
IApartmentDatabase,
|
||||
IPlanterDatabase,
|
||||
IPlantDatabase,
|
||||
IPlantClient
|
||||
} from "@/src/types/shipTypes";
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
@ -77,15 +80,45 @@ favouriteLoadoutSchema.set("toJSON", {
|
||||
}
|
||||
});
|
||||
|
||||
const gardeningSchema = new Schema<IGardening>({
|
||||
Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
|
||||
const plantSchema = new Schema<IPlantDatabase>(
|
||||
{
|
||||
PlantType: String,
|
||||
EndTime: Date,
|
||||
PlotIndex: Number
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
plantSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
const client = obj as IPlantClient;
|
||||
const db = obj as IPlantDatabase;
|
||||
|
||||
client.EndTime = toMongoDate(db.EndTime);
|
||||
}
|
||||
});
|
||||
|
||||
const planterSchema = new Schema<IPlanterDatabase>(
|
||||
{
|
||||
Name: { type: String, required: true },
|
||||
Plants: { type: [plantSchema], default: [] }
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const gardeningSchema = new Schema<IGardeningDatabase>(
|
||||
{
|
||||
Planters: { type: [planterSchema], default: [] }
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
const apartmentSchema = new Schema<IApartmentDatabase>(
|
||||
{
|
||||
Rooms: [roomSchema],
|
||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||
Gardening: gardeningSchema // TODO: ensure this is correct
|
||||
Gardening: gardeningSchema
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -98,7 +131,9 @@ const apartmentDefault: IApartmentDatabase = {
|
||||
{ Name: "DuviriHallway", MaxCapacity: 1600 }
|
||||
],
|
||||
FavouriteLoadouts: [],
|
||||
Gardening: {}
|
||||
Gardening: {
|
||||
Planters: []
|
||||
}
|
||||
};
|
||||
|
||||
const orbiterSchema = new Schema<IOrbiter>(
|
||||
|
@ -48,6 +48,7 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
|
||||
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
|
||||
import { focusController } from "@/src/controllers/api/focusController";
|
||||
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
|
||||
import { gardeningController } from "@/src/controllers/api/gardeningController";
|
||||
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
|
||||
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
|
||||
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
|
||||
@ -240,6 +241,7 @@ apiRouter.post("/findSessions.php", findSessionsController);
|
||||
apiRouter.post("/fishmonger.php", fishmongerController);
|
||||
apiRouter.post("/focus.php", focusController);
|
||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
|
||||
apiRouter.post("/gardening.php", gardeningController);
|
||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
|
||||
apiRouter.post("/getAlliance.php", getAllianceController);
|
||||
apiRouter.post("/getFriends.php", getFriendsController);
|
||||
|
@ -10,6 +10,7 @@ import { getAccountInfoController } from "@/src/controllers/custom/getAccountInf
|
||||
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
|
||||
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
|
||||
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
|
||||
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
|
||||
|
||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
|
||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
|
||||
@ -35,6 +36,7 @@ customRouter.get("/getAccountInfo", getAccountInfoController);
|
||||
customRouter.get("/renameAccount", renameAccountController);
|
||||
customRouter.get("/ircDropped", ircDroppedController);
|
||||
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
||||
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
|
||||
|
||||
customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
|
@ -24,6 +24,7 @@ interface IConfig {
|
||||
infiniteEndo?: boolean;
|
||||
infiniteRegalAya?: boolean;
|
||||
infiniteHelminthMaterials?: boolean;
|
||||
dontSubtractConsumables?: boolean;
|
||||
unlockAllShipFeatures?: boolean;
|
||||
unlockAllShipDecorations?: boolean;
|
||||
unlockAllFlavourItems?: boolean;
|
||||
|
@ -154,23 +154,22 @@ export const addStartingGear = async (
|
||||
|
||||
//TODO: properly merge weapon bin changes it is currently static here
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"DataKnives",
|
||||
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
|
||||
undefined,
|
||||
inventoryChanges,
|
||||
{ XP: 450_000 }
|
||||
{ XP: 450_000, IsNew: false },
|
||||
inventoryChanges
|
||||
);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"Scoops",
|
||||
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
|
||||
undefined,
|
||||
{ IsNew: false },
|
||||
inventoryChanges
|
||||
);
|
||||
|
||||
@ -213,6 +212,15 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
for (const key in delta) {
|
||||
if (!(key in InventoryChanges)) {
|
||||
InventoryChanges[key] = delta[key];
|
||||
} else if (key == "MiscItems") {
|
||||
for (const deltaItem of delta[key]!) {
|
||||
const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType);
|
||||
if (existing) {
|
||||
existing.ItemCount += deltaItem.ItemCount;
|
||||
} else {
|
||||
InventoryChanges[key]!.push(deltaItem);
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(delta[key])) {
|
||||
const left = InventoryChanges[key] as object[];
|
||||
const right: object[] = delta[key];
|
||||
@ -522,14 +530,7 @@ export const addItem = async (
|
||||
]
|
||||
});
|
||||
}
|
||||
const inventoryChanges = addEquipment(
|
||||
inventory,
|
||||
weapon.productCategory,
|
||||
typeName,
|
||||
[],
|
||||
{},
|
||||
defaultOverwrites
|
||||
);
|
||||
const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites);
|
||||
if (weapon.additionalItems) {
|
||||
for (const item of weapon.additionalItems) {
|
||||
combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1));
|
||||
@ -630,12 +631,9 @@ export const addItem = async (
|
||||
switch (typeName.substr(1).split("/")[2]) {
|
||||
default: {
|
||||
return {
|
||||
...(await addPowerSuit(
|
||||
inventory,
|
||||
typeName,
|
||||
{},
|
||||
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
)),
|
||||
...(await addPowerSuit(inventory, typeName, {
|
||||
Features: premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
})),
|
||||
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase)
|
||||
};
|
||||
}
|
||||
@ -855,8 +853,8 @@ const addSentinelWeapon = (
|
||||
export const addPowerSuit = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
powersuitName: string,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features?: number
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): Promise<IInventoryChanges> => {
|
||||
const powersuit = ExportWarframes[powersuitName] as IPowersuit | undefined;
|
||||
const exalted = powersuit?.exalted ?? [];
|
||||
@ -870,15 +868,20 @@ export const addPowerSuit = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
const suitIndex =
|
||||
inventory.Suits.push({
|
||||
const suit: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
{
|
||||
ItemType: powersuitName,
|
||||
Configs: [],
|
||||
UpgradeVer: 101,
|
||||
XP: 0,
|
||||
Features: features,
|
||||
IsNew: true
|
||||
}) - 1;
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!suit.IsNew) {
|
||||
suit.IsNew = undefined;
|
||||
}
|
||||
const suitIndex = inventory.Suits.push(suit) - 1;
|
||||
inventoryChanges.Suits ??= [];
|
||||
inventoryChanges.Suits.push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
|
||||
return inventoryChanges;
|
||||
@ -1199,20 +1202,21 @@ export const addEquipment = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
category: TEquipmentKey,
|
||||
type: string,
|
||||
modularParts?: string[],
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
const equipment = Object.assign(
|
||||
const equipment: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
{
|
||||
ItemType: type,
|
||||
Configs: [],
|
||||
XP: 0,
|
||||
ModularParts: modularParts,
|
||||
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons" ? true : undefined
|
||||
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons"
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!equipment.IsNew) {
|
||||
equipment.IsNew = undefined;
|
||||
}
|
||||
const index = inventory[category].push(equipment) - 1;
|
||||
|
||||
inventoryChanges[category] ??= [];
|
||||
@ -1468,6 +1472,22 @@ export const addGearExpByCategory = (
|
||||
});
|
||||
};
|
||||
|
||||
export const addMiscItem = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
type: string,
|
||||
count: number,
|
||||
inventoryChanges: IInventoryChanges
|
||||
): void => {
|
||||
const miscItemChanges: IMiscItem[] = [
|
||||
{
|
||||
ItemType: type,
|
||||
ItemCount: count
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||
};
|
||||
|
||||
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => {
|
||||
const { MiscItems } = inventory;
|
||||
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
addMods,
|
||||
addRecipes,
|
||||
addShipDecorations,
|
||||
addSkin,
|
||||
addStanding,
|
||||
combineInventoryChanges,
|
||||
generateRewardSeed,
|
||||
@ -69,16 +70,22 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
||||
return rotations;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const missionIndex: number | undefined = ExportRegions[rewardInfo.node]?.missionIndex;
|
||||
|
||||
// For Rescue missions
|
||||
if (rewardInfo.node in ExportRegions && ExportRegions[rewardInfo.node].missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
if (missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
return [rewardInfo.rewardTier];
|
||||
}
|
||||
|
||||
const rotationCount = rewardInfo.rewardQualifications?.length || 0;
|
||||
|
||||
// Empty or absent rewardQualifications should not give rewards:
|
||||
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||
// Empty or absent rewardQualifications should not give rewards when:
|
||||
// - Completing only 1 zone of (E)SO (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||
if (rotationCount == 0 && missionIndex != 30 && missionIndex != 32) {
|
||||
return [0];
|
||||
}
|
||||
|
||||
const rotationPattern =
|
||||
tierOverride === undefined
|
||||
@ -184,6 +191,12 @@ export const addMissionInventoryUpdates = async (
|
||||
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
|
||||
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisHenchmenKills && inventory.Nemesis) {
|
||||
inventory.Nemesis.HenchmenKilled += inventoryUpdates.RewardInfo.NemesisHenchmenKills;
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
|
||||
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress;
|
||||
}
|
||||
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
|
||||
// e.g. for Profit-Taker Phase 1:
|
||||
// JobTier: -6,
|
||||
@ -265,7 +278,14 @@ export const addMissionInventoryUpdates = async (
|
||||
addMiscItems(inventory, value);
|
||||
break;
|
||||
case "Consumables":
|
||||
if (config.dontSubtractConsumables) {
|
||||
addConsumables(
|
||||
inventory,
|
||||
value.filter(x => x.ItemCount > 0)
|
||||
);
|
||||
} else {
|
||||
addConsumables(inventory, value);
|
||||
}
|
||||
break;
|
||||
case "Recipes":
|
||||
addRecipes(inventory, value);
|
||||
@ -411,6 +431,11 @@ export const addMissionInventoryUpdates = async (
|
||||
upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
|
||||
});
|
||||
break;
|
||||
case "WeaponSkins":
|
||||
for (const item of value) {
|
||||
addSkin(inventory, item.ItemType);
|
||||
}
|
||||
break;
|
||||
case "Boosters":
|
||||
value.forEach(booster => {
|
||||
addBooster(booster.ItemType, booster.ExpiryDate, inventory);
|
||||
@ -1277,6 +1302,9 @@ function getRandomMissionDrops(
|
||||
// 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
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
|
||||
} else if (RewardInfo.sortieId && region.missionIndex != 0) {
|
||||
// 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 = [];
|
||||
} else {
|
||||
rewardManifests = region.rewardManifests;
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||
import { addItem, getInventory } from "@/src/services/inventoryService";
|
||||
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
|
||||
import { IGardeningDatabase } from "../types/shipTypes";
|
||||
import { getRandomElement } from "./rngService";
|
||||
|
||||
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
|
||||
export const getPersonalRooms = async (
|
||||
accountId: string,
|
||||
projection?: string
|
||||
): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
|
||||
|
||||
if (!personalRooms) {
|
||||
throw new Error(`personal rooms not found for account ${accountId}`);
|
||||
@ -25,3 +30,64 @@ export const updateShipFeature = async (accountId: string, shipFeature: string):
|
||||
await addItem(inventory, shipFeature, -1);
|
||||
await inventory.save();
|
||||
};
|
||||
|
||||
export const createGarden = (): IGardeningDatabase => {
|
||||
const plantTypes = [
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantA",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantB",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantC",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantD",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantE",
|
||||
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantF"
|
||||
];
|
||||
const endTime = new Date((Math.trunc(Date.now() / 1000) + 79200) * 1000); // Plants will take 22 hours to grow
|
||||
return {
|
||||
Planters: [
|
||||
{
|
||||
Name: "Garden0",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
Name: "Garden1",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
Name: "Garden2",
|
||||
Plants: [
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 0
|
||||
},
|
||||
{
|
||||
PlantType: getRandomElement(plantTypes),
|
||||
EndTime: endTime,
|
||||
PlotIndex: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
@ -66,6 +66,18 @@ export const handlePurchase = async (
|
||||
if (!offer) {
|
||||
throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`);
|
||||
}
|
||||
if (offer.RegularPrice) {
|
||||
combineInventoryChanges(
|
||||
prePurchaseInventoryChanges,
|
||||
updateCurrency(inventory, offer.RegularPrice[0], false)
|
||||
);
|
||||
}
|
||||
if (offer.PremiumPrice) {
|
||||
combineInventoryChanges(
|
||||
prePurchaseInventoryChanges,
|
||||
updateCurrency(inventory, offer.PremiumPrice[0], true)
|
||||
);
|
||||
}
|
||||
if (offer.ItemPrices) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -170,6 +182,9 @@ export const handlePurchase = async (
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.RegularPrice, false)
|
||||
);
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
|
||||
const invItem: IMiscItem = {
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
|
||||
@ -229,6 +244,12 @@ export const handlePurchase = async (
|
||||
updateCurrency(inventory, offer.credits, false)
|
||||
);
|
||||
}
|
||||
if (typeof offer.platinum == "number") {
|
||||
combineInventoryChanges(
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.platinum, true)
|
||||
);
|
||||
}
|
||||
if (offer.itemPrices) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -239,6 +260,9 @@ export const handlePurchase = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
break;
|
||||
case 18: {
|
||||
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
|
||||
|
@ -161,6 +161,11 @@ export const handleInventoryItemConfigChange = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "LotusCustomization": {
|
||||
logger.debug(`saved LotusCustomization`, equipmentChanges.LotusCustomization);
|
||||
inventory.LotusCustomization = equipmentChanges.LotusCustomization;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
|
||||
logger.debug(`general Item config saved of type ${equipmentName}`, {
|
||||
|
@ -50,21 +50,27 @@ const sortieBossToFaction: Record<string, string> = {
|
||||
SORTIE_BOSS_PHORID: "FC_INFESTATION",
|
||||
SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION",
|
||||
SORTIE_BOSS_INFALAD: "FC_INFESTATION",
|
||||
SORTIE_BOSS_CORRUPTED_VOR: "FC_CORRUPTED"
|
||||
SORTIE_BOSS_CORRUPTED_VOR: "FC_OROKIN"
|
||||
};
|
||||
|
||||
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]
|
||||
FC_OROKIN: [14]
|
||||
};
|
||||
|
||||
const sortieFactionToFactionIndexes: Record<string, number[]> = {
|
||||
FC_GRINEER: [0],
|
||||
FC_CORPUS: [1],
|
||||
FC_INFESTATION: [0, 1, 2],
|
||||
FC_CORRUPTED: [3]
|
||||
FC_OROKIN: [3]
|
||||
};
|
||||
|
||||
const sortieFactionToSpecialMissionTileset: Record<string, string> = {
|
||||
FC_GRINEER: "GrineerGalleonTileset",
|
||||
FC_CORPUS: "CorpusShipTileset",
|
||||
FC_INFESTATION: "CorpusShipTileset"
|
||||
};
|
||||
|
||||
const sortieBossNode: Record<string, string> = {
|
||||
@ -273,14 +279,7 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
|
||||
if (
|
||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
||||
!isArchwingMission(value) &&
|
||||
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
|
||||
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
|
||||
value.missionIndex != 21 && // Exclude MT_PURIFY
|
||||
value.missionIndex != 22 && // Exclude MT_ARENA
|
||||
value.missionIndex != 23 && // Exclude MT_JUNCTION
|
||||
(value.missionIndex != 28 || value.systemIndex == 2) && // MT_LANDSCAPE only on Earth
|
||||
value.missionIndex < 29
|
||||
key in sortieTilesets
|
||||
) {
|
||||
if (!availableMissionIndexes.includes(value.missionIndex)) {
|
||||
availableMissionIndexes.push(value.missionIndex);
|
||||
@ -289,21 +288,36 @@ const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
|
||||
}
|
||||
}
|
||||
|
||||
const specialMissionTypes = [1, 3, 5, 9];
|
||||
if (!(sortieBossToFaction[boss] in sortieFactionToSpecialMissionTileset)) {
|
||||
for (const missionType of specialMissionTypes) {
|
||||
const i = availableMissionIndexes.indexOf(missionType);
|
||||
if (i != -1) {
|
||||
availableMissionIndexes.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedNodes: ISortieMission[] = [];
|
||||
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;
|
||||
let randomIndex;
|
||||
let node;
|
||||
let missionIndex;
|
||||
do {
|
||||
randomIndex = rng.randomInt(0, nodes.length - 1);
|
||||
node = nodes[randomIndex];
|
||||
|
||||
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 = ExportRegions[node].missionIndex;
|
||||
if (missionIndex != 28) {
|
||||
missionIndex = rng.randomElement(availableMissionIndexes);
|
||||
}
|
||||
} while (
|
||||
specialMissionTypes.indexOf(missionIndex) != -1 &&
|
||||
sortieTilesets[node as keyof typeof sortieTilesets] !=
|
||||
sortieFactionToSpecialMissionTileset[sortieBossToFaction[boss]]
|
||||
);
|
||||
|
||||
if (i == 2 && rng.randomInt(0, 2) == 2) {
|
||||
const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY");
|
||||
|
@ -328,7 +328,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
BlessingCooldown?: IMongoDate;
|
||||
CrewShipRawSalvage: ITypeCount[];
|
||||
CrewMembers: ICrewMemberClient[];
|
||||
LotusCustomization: ILotusCustomization;
|
||||
LotusCustomization?: ILotusCustomization;
|
||||
UseAdultOperatorLoadout?: boolean;
|
||||
NemesisAbandonedRewards: string[];
|
||||
LastInventorySync: IOid;
|
||||
|
@ -8,6 +8,8 @@ export interface IMissionReward {
|
||||
TypeName?: string;
|
||||
UpgradeLevel?: number;
|
||||
ItemCount: number;
|
||||
DailyCooldown?: boolean;
|
||||
Rarity?: number;
|
||||
TweetText?: string;
|
||||
ProductCategory?: string;
|
||||
FromEnemyCache?: boolean;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import {
|
||||
IApartment,
|
||||
IRoom,
|
||||
IPlacedDecosDatabase,
|
||||
ITailorShop,
|
||||
ITailorShopDatabase,
|
||||
TBootLocation,
|
||||
IApartmentDatabase
|
||||
IApartmentDatabase,
|
||||
IApartmentClient
|
||||
} from "@/src/types/shipTypes";
|
||||
import { Document, Model, Types } from "mongoose";
|
||||
|
||||
@ -21,10 +21,10 @@ export interface IOrbiter {
|
||||
BootLocation?: TBootLocation;
|
||||
}
|
||||
|
||||
export interface IPersonalRooms {
|
||||
export interface IPersonalRoomsClient {
|
||||
ShipInteriorColors: IColor;
|
||||
Ship: IOrbiter;
|
||||
Apartment: IApartment;
|
||||
Apartment: IApartmentClient;
|
||||
TailorShop: ITailorShop;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,8 @@ import {
|
||||
IDiscoveredMarker,
|
||||
ILockedWeaponGroupClient,
|
||||
ILoadOutPresets,
|
||||
IInvasionProgressClient
|
||||
IInvasionProgressClient,
|
||||
IWeaponSkinClient
|
||||
} from "./inventoryTypes/inventoryTypes";
|
||||
import { IGroup } from "./loginTypes";
|
||||
|
||||
@ -101,6 +102,7 @@ export type IMissionInventoryUpdateRequest = {
|
||||
}[];
|
||||
CollectibleScans?: ICollectibleEntry[];
|
||||
Upgrades?: IUpgradeClient[]; // riven challenge progress
|
||||
WeaponSkins?: IWeaponSkinClient[];
|
||||
StrippedItems?: {
|
||||
DropTable: string;
|
||||
DROP_MOD?: number[];
|
||||
@ -156,6 +158,8 @@ export interface IRewardInfo {
|
||||
lostTargetWave?: number;
|
||||
defenseTargetCount?: number;
|
||||
NemesisAbandonedRewards?: string[];
|
||||
NemesisHenchmenKills?: number;
|
||||
NemesisHintProgress?: number;
|
||||
EOM_AFK?: number;
|
||||
rewardQualifications?: string; // did a Survival for 5 minutes and this was "1"
|
||||
PurgatoryRewardQualifications?: string;
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
ICrewShipMembersClient,
|
||||
ICrewShipWeapon,
|
||||
IFlavourItem,
|
||||
ILoadoutConfigClient
|
||||
ILoadoutConfigClient,
|
||||
ILotusCustomization
|
||||
} from "./inventoryTypes/inventoryTypes";
|
||||
|
||||
export interface ISaveLoadoutRequest {
|
||||
@ -43,6 +44,7 @@ export interface ISaveLoadoutRequest {
|
||||
EquippedEmotes: string[];
|
||||
UseAdultOperatorLoadout: boolean;
|
||||
WeaponSkins: IItemEntry;
|
||||
LotusCustomization: ILotusCustomization;
|
||||
}
|
||||
|
||||
export type ISaveLoadoutRequestNoUpgradeVer = Omit<ISaveLoadoutRequest, "UpgradeVer">;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Types } from "mongoose";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { ILoadoutClient } from "./saveLoadoutTypes";
|
||||
|
||||
export interface IGetShipResponse {
|
||||
ShipOwnerId: string;
|
||||
Ship: IShip;
|
||||
Apartment: IApartment;
|
||||
Apartment: IApartmentClient;
|
||||
TailorShop: ITailorShop;
|
||||
LoadOutInventory: { LoadOutPresets: ILoadoutClient };
|
||||
}
|
||||
@ -51,28 +51,42 @@ export interface IRoom {
|
||||
PlacedDecos?: IPlacedDecosDatabase[];
|
||||
}
|
||||
|
||||
export interface IPlants {
|
||||
export interface IPlantClient {
|
||||
PlantType: string;
|
||||
EndTime: IOid;
|
||||
EndTime: IMongoDate;
|
||||
PlotIndex: number;
|
||||
}
|
||||
export interface IPlanters {
|
||||
|
||||
export interface IPlantDatabase extends Omit<IPlantClient, "EndTime"> {
|
||||
EndTime: Date;
|
||||
}
|
||||
|
||||
export interface IPlanterClient {
|
||||
Name: string;
|
||||
Plants: IPlants[];
|
||||
Plants: IPlantClient[];
|
||||
}
|
||||
|
||||
export interface IGardening {
|
||||
Planters?: IPlanters[];
|
||||
export interface IPlanterDatabase {
|
||||
Name: string;
|
||||
Plants: IPlantDatabase[];
|
||||
}
|
||||
|
||||
export interface IApartment {
|
||||
Gardening: IGardening;
|
||||
export interface IGardeningClient {
|
||||
Planters: IPlanterClient[];
|
||||
}
|
||||
|
||||
export interface IGardeningDatabase {
|
||||
Planters: IPlanterDatabase[];
|
||||
}
|
||||
|
||||
export interface IApartmentClient {
|
||||
Gardening: IGardeningClient;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadout[];
|
||||
}
|
||||
|
||||
export interface IApartmentDatabase {
|
||||
Gardening: IGardening;
|
||||
Gardening: IGardeningDatabase;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadoutDatabase[];
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ export interface IItemManifest {
|
||||
StoreItem: string;
|
||||
ItemPrices?: IItemPrice[];
|
||||
RegularPrice?: number[];
|
||||
PremiumPrice?: number[];
|
||||
Bin: string;
|
||||
QuantityMultiplier: number;
|
||||
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
|
||||
|
@ -510,8 +510,9 @@
|
||||
<div class="card mb-3">
|
||||
<h5 class="card-header" data-loc="general_bulkActions"></h5>
|
||||
<div class="card-body d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_bulkAddMods"></button>
|
||||
<button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_addMissingUnrankedMods"></button>
|
||||
<button class="btn btn-danger" onclick="doRemoveUnrankedMods();" data-loc="mods_removeUnranked"></button>
|
||||
<button class="btn btn-primary" onclick="doAddMissingMaxRankMods();" data-loc="mods_addMissingMaxRankMods"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -593,6 +594,10 @@
|
||||
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
|
||||
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="dontSubtractConsumables" />
|
||||
<label class="form-check-label" for="dontSubtractConsumables" data-loc="cheats_dontSubtractConsumables"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
|
||||
<label class="form-check-label" for="unlockAllShipFeatures" data-loc="cheats_unlockAllShipFeatures"></label>
|
||||
|
@ -1795,6 +1795,14 @@ function doRemoveUnrankedMods() {
|
||||
});
|
||||
}
|
||||
|
||||
function doAddMissingMaxRankMods() {
|
||||
revalidateAuthz(() => {
|
||||
fetch("/custom/addMissingMaxRankMods?" + window.authz).then(() => {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Powersuit Route
|
||||
|
||||
single.getRoute("#powersuit-route").on("beforeload", function () {
|
||||
|
@ -120,8 +120,9 @@ dict = {
|
||||
mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,
|
||||
mods_rivens: `Rivens`,
|
||||
mods_mods: `Mods`,
|
||||
mods_bulkAddMods: `Fehlende Mods hinzufügen`,
|
||||
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `Mods ohne Rang entfernen`,
|
||||
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>|DISPLAYNAME|</code> zu <code>administratorNames</code> in der config.json hinzu.`,
|
||||
cheats_server: `Server`,
|
||||
cheats_skipTutorial: `Tutorial überspringen`,
|
||||
@ -133,6 +134,7 @@ dict = {
|
||||
cheats_infiniteEndo: `Unendlich Endo`,
|
||||
cheats_infiniteRegalAya: `Unendlich Reines Aya`,
|
||||
cheats_infiniteHelminthMaterials: `Unendlich Helminth-Materialien`,
|
||||
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `Alle Schiffs-Funktionen freischalten`,
|
||||
cheats_unlockAllShipDecorations: `Alle Schiffsdekorationen freischalten`,
|
||||
cheats_unlockAllFlavourItems: `Alle <abbr title=\"Animationssets, Glyphen, Farbpaletten usw.\">Sammlerstücke</abbr> freischalten`,
|
||||
|
@ -119,8 +119,9 @@ dict = {
|
||||
mods_fingerprintHelp: `Need help with the fingerprint?`,
|
||||
mods_rivens: `Rivens`,
|
||||
mods_mods: `Mods`,
|
||||
mods_bulkAddMods: `Add Missing Mods`,
|
||||
mods_addMissingUnrankedMods: `Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `Remove Unranked Mods`,
|
||||
mods_addMissingMaxRankMods: `Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>|DISPLAYNAME|</code> to <code>administratorNames</code> in the config.json.`,
|
||||
cheats_server: `Server`,
|
||||
cheats_skipTutorial: `Skip Tutorial`,
|
||||
@ -132,6 +133,7 @@ dict = {
|
||||
cheats_infiniteEndo: `Infinite Endo`,
|
||||
cheats_infiniteRegalAya: `Infinite Regal Aya`,
|
||||
cheats_infiniteHelminthMaterials: `Infinite Helminth Materials`,
|
||||
cheats_dontSubtractConsumables: `Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `Unlock All Ship Features`,
|
||||
cheats_unlockAllShipDecorations: `Unlock All Ship Decorations`,
|
||||
cheats_unlockAllFlavourItems: `Unlock All <abbr title=\"Animation Sets, Glyphs, Palettes, etc.\">Flavor Items</abbr>`,
|
||||
|
@ -34,8 +34,8 @@ dict = {
|
||||
code_rerollsNumber: `Cantidad de reintentos`,
|
||||
code_viewStats: `Ver estadísticas`,
|
||||
code_rank: `Rango`,
|
||||
code_rankUp: `[UNTRANSLATED] Rank up`,
|
||||
code_rankDown: `[UNTRANSLATED] Rank down`,
|
||||
code_rankUp: `Subir de rango`,
|
||||
code_rankDown: `Bajar de rango`,
|
||||
code_count: `Cantidad`,
|
||||
code_focusAllUnlocked: `Todas las escuelas de enfoque ya están desbloqueadas.`,
|
||||
code_focusUnlocked: `¡Desbloqueadas |COUNT| nuevas escuelas de enfoque! Se necesita una actualización del inventario para reflejar los cambios en el juego. Visitar la navegación debería ser la forma más sencilla de activarlo.`,
|
||||
@ -86,21 +86,21 @@ dict = {
|
||||
inventory_hoverboards: `K-Drives`,
|
||||
inventory_moaPets: `Moa`,
|
||||
inventory_kubrowPets: `Bestias`,
|
||||
inventory_evolutionProgress: `[UNTRANSLATED] Incarnon Evolution Progress`,
|
||||
inventory_evolutionProgress: `Progreso de evolución Incarnon`,
|
||||
inventory_bulkAddSuits: `Agregar Warframes faltantes`,
|
||||
inventory_bulkAddWeapons: `Agregar armas faltantes`,
|
||||
inventory_bulkAddSpaceSuits: `Agregar Archwings faltantes`,
|
||||
inventory_bulkAddSpaceWeapons: `Agregar armas Archwing faltantes`,
|
||||
inventory_bulkAddSentinels: `Agregar centinelas faltantes`,
|
||||
inventory_bulkAddSentinelWeapons: `Agregar armas de centinela faltantes`,
|
||||
inventory_bulkAddEvolutionProgress: `[UNTRANSLATED] Add Missing Incarnon Evolution Progress`,
|
||||
inventory_bulkAddEvolutionProgress: `Completar el progreso de evolución Incarnon faltante`,
|
||||
inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`,
|
||||
inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`,
|
||||
inventory_bulkRankUpSpaceSuits: `Maximizar rango de todos los Archwings`,
|
||||
inventory_bulkRankUpSpaceWeapons: `Maximizar rango de todas las armas Archwing`,
|
||||
inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
|
||||
inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
|
||||
inventory_bulkRankUpEvolutionProgress: `[UNTRANSLATED] Max Rank All Incarnon Evolution Progress`,
|
||||
inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
|
||||
|
||||
quests_list: `Misiones`,
|
||||
quests_completeAll: `Completar todas las misiones`,
|
||||
@ -120,8 +120,9 @@ dict = {
|
||||
mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,
|
||||
mods_rivens: `Agrietados`,
|
||||
mods_mods: `Mods`,
|
||||
mods_bulkAddMods: `Agregar mods faltantes`,
|
||||
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `Quitar mods sin rango`,
|
||||
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>|DISPLAYNAME|</code> a <code>administratorNames</code> en el archivo config.json.`,
|
||||
cheats_server: `Servidor`,
|
||||
cheats_skipTutorial: `Omitir tutorial`,
|
||||
@ -133,6 +134,7 @@ dict = {
|
||||
cheats_infiniteEndo: `Endo infinito`,
|
||||
cheats_infiniteRegalAya: `Aya Real infinita`,
|
||||
cheats_infiniteHelminthMaterials: `Materiales Helminto infinitos`,
|
||||
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `Desbloquear todas las funciones de nave`,
|
||||
cheats_unlockAllShipDecorations: `Desbloquear todas las decoraciones de nave`,
|
||||
cheats_unlockAllFlavourItems: `Desbloquear todos los <abbr title="Conjuntos de animaciones, glifos, paletas, etc.">ítems estéticos</abbr>`,
|
||||
|
@ -120,8 +120,9 @@ dict = {
|
||||
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
|
||||
mods_rivens: `Rivens`,
|
||||
mods_mods: `Mods`,
|
||||
mods_bulkAddMods: `Ajouter les mods manquants`,
|
||||
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
|
||||
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>|DISPLAYNAME|</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`,
|
||||
cheats_server: `Serveur`,
|
||||
cheats_skipTutorial: `Passer le tutoriel`,
|
||||
@ -133,6 +134,7 @@ dict = {
|
||||
cheats_infiniteEndo: `Endo infini`,
|
||||
cheats_infiniteRegalAya: `Aya Raffiné infini`,
|
||||
cheats_infiniteHelminthMaterials: `Ressources d'Helminth infinies`,
|
||||
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `Débloquer tous les segments du vaisseau`,
|
||||
cheats_unlockAllShipDecorations: `Débloquer toutes les décorations du vaisseau`,
|
||||
cheats_unlockAllFlavourItems: `Débloquer tous les <abbr title=\"Animations, Glyphes, Palettes, etc.\">Flavor Items</abbr>`,
|
||||
|
@ -120,8 +120,9 @@ dict = {
|
||||
mods_fingerprintHelp: `Нужна помощь с отпечатком?`,
|
||||
mods_rivens: `Моды Разлома`,
|
||||
mods_mods: `Моды`,
|
||||
mods_bulkAddMods: `Добавить отсутствующие моды`,
|
||||
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
|
||||
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`,
|
||||
cheats_server: `Сервер`,
|
||||
cheats_skipTutorial: `Пропустить обучение`,
|
||||
@ -133,6 +134,7 @@ dict = {
|
||||
cheats_infiniteEndo: `Бесконечное эндо`,
|
||||
cheats_infiniteRegalAya: `Бесконечная Королевская Айя`,
|
||||
cheats_infiniteHelminthMaterials: `Бесконечные Выделения Гельминта`,
|
||||
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `Разблокировать все функции корабля`,
|
||||
cheats_unlockAllShipDecorations: `Разблокировать все украшения корабля`,
|
||||
cheats_unlockAllFlavourItems: `Разблокировать все <abbr title=\"Наборы анимаций, глифы, палитры и т. д.\">уникальные предметы</abbr>`,
|
||||
|
@ -120,8 +120,9 @@ dict = {
|
||||
mods_fingerprintHelp: `需要印记相关的帮助?`,
|
||||
mods_rivens: `裂罅MOD`,
|
||||
mods_mods: `Mods`,
|
||||
mods_bulkAddMods: `添加缺失MOD`,
|
||||
mods_addMissingUnrankedMods: `[UNTRANSLATED] Add Missing Unranked Mods`,
|
||||
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
|
||||
mods_addMissingMaxRankMods: `[UNTRANSLATED] Add Missing Max Rank Mods`,
|
||||
cheats_administratorRequirement: `您必须是管理员才能使用此功能。要成为管理员,请将 <code>|DISPLAYNAME|</code> 添加到 config.json 的 <code>administratorNames</code> 中。`,
|
||||
cheats_server: `服务器`,
|
||||
cheats_skipTutorial: `跳过教程`,
|
||||
@ -133,6 +134,7 @@ dict = {
|
||||
cheats_infiniteEndo: `无限内融核心`,
|
||||
cheats_infiniteRegalAya: `无限御品阿耶`,
|
||||
cheats_infiniteHelminthMaterials: `无限Helminth材料`,
|
||||
cheats_dontSubtractConsumables: `[UNTRANSLATED] Don't Subtract Consumables`,
|
||||
cheats_unlockAllShipFeatures: `解锁所有飞船功能`,
|
||||
cheats_unlockAllShipDecorations: `解锁所有飞船装饰`,
|
||||
cheats_unlockAllFlavourItems: `解锁所有<abbr title=\"动画组合、图标、调色板等\">装饰物品</abbr>`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user