This commit is contained in:
Master 2024-06-29 13:40:41 +08:00
commit 6771578d2c
29 changed files with 571 additions and 594 deletions

View File

@ -1 +1,2 @@
static/webui/libs/
*.html

View File

@ -0,0 +1,66 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { WeaponTypeInternal } from "@/src/services/itemDataService";
import { ArtifactPolarity, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
const modularWeaponCategory: (WeaponTypeInternal | "Hoverboards")[] = [
"LongGuns",
"Pistols",
"Melee",
"OperatorAmps",
"Hoverboards" // Not sure about hoverboards just coppied from modual crafting
];
interface IGildWeaponRequest {
ItemName: string;
Recipe: string; // /Lotus/Weapons/SolarisUnited/LotusGildKitgunBlueprint
PolarizeSlot?: number;
PolarizeValue?: ArtifactPolarity;
ItemId: string;
Category: WeaponTypeInternal | "Hoverboards";
}
// In export there no recipes for gild action, so reputation and ressources only consumed visually
// eslint-disable-next-line @typescript-eslint/no-misused-promises
export const gildWeaponController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const data: IGildWeaponRequest = getJSONfromString(String(req.body));
data.ItemId = String(req.query.ItemId);
if (!modularWeaponCategory.includes(req.query.Category as WeaponTypeInternal | "Hoverboards")) {
throw new Error(`Unknown modular weapon Category: ${req.query.Category}`);
}
data.Category = req.query.Category as WeaponTypeInternal | "Hoverboards";
const inventory = await getInventory(accountId);
if (!inventory[data.Category]) {
throw new Error(`Category ${req.query.Category} not found in inventory`);
}
const weaponIndex = inventory[data.Category].findIndex(x => String(x._id) === data.ItemId);
if (weaponIndex === -1) {
throw new Error(`Weapon with ${data.ItemId} not found in category ${req.query.Category}`);
}
const weapon = inventory[data.Category][weaponIndex];
weapon.Features = EquipmentFeatures.GILDED; // maybe 9 idk if DOUBLE_CAPACITY is also given
weapon.ItemName = data.ItemName;
weapon.XP = 0;
if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
weapon.Polarity = [
{
Slot: data.PolarizeSlot,
Value: data.PolarizeValue
}
];
}
inventory[data.Category][weaponIndex] = weapon;
await inventory.save();
res.json({
InventoryChanges: {
[data.Category]: [weapon]
}
});
};

View File

@ -7,7 +7,7 @@ import { IGiveKeyChainTriggeredItemsRequest } from "@/src/types/questTypes";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const payload = getJSONfromString(req.body as string) as IGiveKeyChainTriggeredItemsRequest;
const payload = getJSONfromString(String(req.body)) as IGiveKeyChainTriggeredItemsRequest;
const result = await giveKeyChainTriggeredItems(accountId, payload.KeyChain, payload.ChainStage);
if (result) res.json(result);
else res.json({});

View File

@ -7,7 +7,7 @@ import { IGiveKeyChainTriggeredMessageRequest } from "@/src/types/questTypes";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const payload = getJSONfromString(req.body as string) as IGiveKeyChainTriggeredMessageRequest;
const payload = getJSONfromString(String(req.body)) as IGiveKeyChainTriggeredMessageRequest;
const result = giveKeyChainTriggeredMessage(accountId, payload.KeyChain, payload.ChainStage);
if (result != null) res.json(result);
else res.status(200).end();

View File

@ -7,7 +7,7 @@ import { ISession } from "@/src/types/session";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const hostSessionController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const hostSessionRequest = JSON.parse(req.body as string) as ISession;
const hostSessionRequest = JSON.parse(String(req.body)) as ISession;
logger.debug("HostSession Request", { hostSessionRequest });
const session = createNewSession(hostSessionRequest, accountId);
logger.debug(`New Session Created`, { session });

View File

@ -21,7 +21,7 @@ import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
export const inventorySlotsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
//const body = JSON.parse(req.body as string) as IInventorySlotsRequest;
//const body = JSON.parse(String(req.body)) as IInventorySlotsRequest;
//console.log(body);

View File

@ -10,7 +10,7 @@ export const saveLoadoutController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
try {
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
const body: ISaveLoadoutRequest = JSON.parse(String(req.body)) as ISaveLoadoutRequest;
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -6,7 +6,7 @@ import { RequestHandler } from "express";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
export const setShipCustomizationsController: RequestHandler = async (req, res) => {
try {
const setShipCustomizationsRequest = JSON.parse(req.body as string) as ISetShipCustomizationsRequest;
const setShipCustomizationsRequest = JSON.parse(String(req.body)) as ISetShipCustomizationsRequest;
const setShipCustomizationsResponse = await setShipCustomizations(setShipCustomizationsRequest);
res.json(setShipCustomizationsResponse);

View File

@ -7,7 +7,7 @@ import { handleSetShipDecorations } from "@/src/services/shipCustomizationsServi
// eslint-disable-next-line @typescript-eslint/no-misused-promises
export const shipDecorationsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const shipDecorationsRequest = JSON.parse(req.body as string) as IShipDecorationsRequest;
const shipDecorationsRequest = JSON.parse(String(req.body)) as IShipDecorationsRequest;
try {
const placedDecoration = await handleSetShipDecorations(accountId, shipDecorationsRequest);

View File

@ -12,7 +12,7 @@ export interface IUnlockShipFeatureRequest {
export const unlockShipFeatureController: RequestHandler = async (req, res) => {
const accountId = parseString(req.query.accountId);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
const shipFeatureRequest = JSON.parse(req.body as string) as IUnlockShipFeatureRequest;
const shipFeatureRequest = JSON.parse(String(req.body)) as IUnlockShipFeatureRequest;
await unlockShipFeature(accountId, shipFeatureRequest.Feature);
res.send([]);
};

View File

@ -7,7 +7,7 @@ import { updateQuest } from "@/src/services/questService";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const updateQuestController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const payload = getJSONfromString(req.body as string) as IUpdateQuestRequest;
const payload = getJSONfromString(String(req.body)) as IUpdateQuestRequest;
const result = await updateQuest(accountId, payload);
res.json(result);
};

View File

@ -3,7 +3,6 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { IStatsView } from "@/src/types/statTypes";
import { config } from "@/src/services/configService";
import view from "@/static/fixed_responses/view.json";
import allScans from "@/static/fixed_responses/allScans.json";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
@ -15,7 +14,7 @@ const viewController: RequestHandler = async (req, res) => {
return;
}
const responseJson: IStatsView = view;
const responseJson: IStatsView = {};
responseJson.Weapons = [];
for (const item of inventory.XPInfo) {
responseJson.Weapons.push({

View File

@ -36,9 +36,10 @@ import {
IPeriodicMissionCompletionDatabase,
IPeriodicMissionCompletionResponse,
ILoreFragmentScan,
IEvolutionProgress
} from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes";
IEvolutionProgress,
IDefaultUpgrade
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IOid } from "@/src/types/commonTypes";
import {
IAbilityOverride,
IColor,
@ -634,6 +635,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
DailyAffiliationNecraloid: Number,
DailyAffiliationZariman: Number,
DailyAffiliationKahl: Number,
DailyAffiliationCavia: Number,
//Daily Focus limit
DailyFocus: Number,
@ -966,7 +968,7 @@ type InventoryDocumentProps = {
OperatorAmps: Types.DocumentArray<IEquipmentDatabase>;
FlavourItems: Types.DocumentArray<IFlavourItem>;
RawUpgrades: Types.DocumentArray<IRawUpgrade>;
Upgrades: Types.DocumentArray<ICrewShipSalvagedWeaponSkin>;
Upgrades: Types.DocumentArray<IDefaultUpgrade>;
MiscItems: Types.DocumentArray<IMiscItem>;
Boosters: Types.DocumentArray<IBooster>;
OperatorLoadOuts: Types.DocumentArray<IOperatorConfigClient>;

View File

@ -27,13 +27,16 @@ const EquipmentSelectionSchema = new Schema<IEquipmentSelection>(
const loadoutConfigSchema = new Schema<ILoadoutConfigDatabase>(
{
FocusSchool: String,
PresetIcon: String,
Favorite: Boolean,
n: String,
s: EquipmentSelectionSchema,
p: EquipmentSelectionSchema,
l: EquipmentSelectionSchema,
m: EquipmentSelectionSchema
n: String, // Loadout name
s: EquipmentSelectionSchema, // Suit
l: EquipmentSelectionSchema, // Primary weapon
p: EquipmentSelectionSchema, // Secondary weapon
m: EquipmentSelectionSchema, // Melee weapon
h: EquipmentSelectionSchema, // Gravimag weapon
a: EquipmentSelectionSchema // Necromech exalted weapon
},
{
id: false

View File

@ -64,6 +64,7 @@ import { updateQuestController } from "@/src/controllers/api/updateQuestControll
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
import { updateThemeController } from "@/src/controllers/api/updateThemeController";
import { upgradesController } from "@/src/controllers/api/upgradesController";
import { gildWeaponController } from "../controllers/api/gildWeaponController";
const apiRouter = express.Router();
@ -111,6 +112,7 @@ apiRouter.post("/genericUpdate.php", genericUpdateController);
apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
apiRouter.post("/gildWeapon.php", gildWeaponController);
apiRouter.post("/guildTech.php", guildTechController);
apiRouter.post("/hostSession.php", hostSessionController);
apiRouter.post("/infestedFoundry.php", infestedFoundryController);

View File

@ -19,15 +19,18 @@ webuiRouter.use("/webui", (req, res, next) => {
});
// Serve virtual routes
webuiRouter.get("/webui/settings", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/inventory", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/mods", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/settings", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/cheats", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
});
// Serve static files
webuiRouter.use("/webui", express.static(path.join(rootDir, "static/webui")));

View File

@ -14,7 +14,10 @@ import {
ISeasonChallenge,
ITypeCount,
InventorySlot,
IWeaponSkinClient
IWeaponSkinClient,
IDefaultUpgrade,
KubrowPetEggItemType,
IKubrowPetEgg
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate } from "../types/genericUpdate";
import {
@ -26,9 +29,10 @@ import {
import { logger } from "@/src/utils/logger";
import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/itemDataService";
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes";
import { ExportCustoms, ExportFlavour, ExportRecipes, ExportResources } from "warframe-public-export-plus";
import { IEquipmentClient, IEquipmentDatabase } from "../types/inventoryTypes/commonInventoryTypes";
import { ExportArcanes, ExportCustoms, ExportFlavour, ExportFusionBundles, ExportRecipes, ExportResources, ExportSentinels, ExportUpgrades } from "warframe-public-export-plus";
import { updateQuestKeys } from "./questService";
import { toOid } from "../helpers/inventoryHelpers";
export const createInventory = async (
accountOwnerId: Types.ObjectId,
@ -75,6 +79,7 @@ export const addItem = async (
): Promise<{ InventoryChanges: IInventoryChanges }> => {
// Strict typing
if (typeName in ExportRecipes) {
const recipe = ExportRecipes[typeName];
const inventory = await getInventory(accountId);
const recipeChanges = [
{
@ -82,6 +87,13 @@ export const addItem = async (
ItemCount: quantity
} satisfies ITypeCount
];
recipe.ingredients.forEach(ingredient => {
const itemType = ingredient.ItemType.replace("Component", "Blueprint");
recipeChanges.push({
ItemType: itemType,
ItemCount: ingredient.ItemCount
} satisfies ITypeCount);
});
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
@ -92,6 +104,9 @@ export const addItem = async (
}
if (typeName in ExportResources) {
const inventory = await getInventory(accountId);
const resource = ExportResources[typeName];
switch (resource.productCategory) {
case "MiscItems": {
const miscItemChanges = [
{
ItemType: typeName,
@ -106,6 +121,39 @@ export const addItem = async (
}
};
}
case "ShipDecorations": {
const changes = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
}
};
}
case "KubrowPetEggs": {
const changes = [
{
ItemId: toOid(new Types.ObjectId()),
ItemType: KubrowPetEggItemType.LotusTypesGameKubrowPetEggsKubrowEgg,
ExpirationDate: { $date: { $numberLong: "9999999999999" } }
} satisfies IKubrowPetEgg
];
inventory.KubrowPetEggs.push(...changes);
await inventory.save();
return {
InventoryChanges: {
KubrowPetEggs: changes
}
};
}
}
}
if (typeName in ExportCustoms) {
return {
InventoryChanges: {
@ -120,6 +168,53 @@ export const addItem = async (
}
};
}
if (typeName in ExportUpgrades) {
return {
InventoryChanges: {
Upgrades: [await addUpgrade(typeName, accountId)]
}
};
}
if (typeName in ExportFusionBundles) {
const inventory = await getInventory(accountId);
inventory.FusionPoints += ExportFusionBundles[typeName].fusionPoints;
await inventory.save();
return {
InventoryChanges: {
FusionPoints: inventory.FusionPoints
}
};
}
if (typeName in ExportArcanes) {
return {
InventoryChanges: {
Upgrades: [await addUpgrade(typeName, accountId)]
}
};
}
if (typeName in ExportSentinels) {
const inventory = await getInventory(accountId);
const sentinelData = ExportSentinels[typeName];
const sentinel = await addSentinel(typeName, accountId);
await updateSlots(accountId, InventorySlot.SENTINELS, 0, 1);
if (!inventory.SentinelWeapons.find(i => i.ItemType == (sentinelData.defaultWeapon as string))) {
const sentinelWeapon = await addSentinelWeapon(sentinelData.defaultWeapon as string, accountId);
return {
InventoryChanges: {
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
Sentinels: [sentinel],
SentinelWeapons: [sentinelWeapon]
}
};
}
return {
InventoryChanges: {
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
Sentinels: [sentinel]
}
};
}
// Path-based duck typing
switch (typeName.substring(1).split("/")[1]) {
@ -268,13 +363,20 @@ export const addItem = async (
};
//TODO: maybe genericMethod for all the add methods, they share a lot of logic
export const addSentinel = async (sentinelName: string, accountId: string) => {
export const addSentinel = async (sentinelName: string, accountId: string): Promise<IEquipmentDatabase> => {
const inventory = await getInventory(accountId);
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: [], XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.Sentinels[sentinelIndex - 1].toJSON();
};
export const addSentinelWeapon = async (weaponName: string, accountId: string): Promise<IEquipmentDatabase> => {
const inventory = await getInventory(accountId);
const sentinelWeaponIndex = inventory.SentinelWeapons.push({ ItemType: weaponName, Configs: [], XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.SentinelWeapons[sentinelWeaponIndex - 1].toJSON();
};
export const addPowerSuit = async (powersuitName: string, accountId: string): Promise<IEquipmentClient> => {
const specialItems = getExalted(powersuitName);
if (specialItems != false) {
@ -288,7 +390,7 @@ export const addPowerSuit = async (powersuitName: string, accountId: string): Pr
return changedInventory.Suits[suitIndex - 1].toJSON();
};
export const addMechSuit = async (mechsuitName: string, accountId: string) => {
export const addMechSuit = async (mechsuitName: string, accountId: string): Promise<IEquipmentDatabase> => {
const specialItems = getExalted(mechsuitName);
if (specialItems != false) {
for await (const specialItem of specialItems) {
@ -301,7 +403,7 @@ export const addMechSuit = async (mechsuitName: string, accountId: string) => {
return changedInventory.MechSuits[suitIndex - 1].toJSON();
};
export const addSpecialItem = async (itemName: string, accountId: string) => {
export const addSpecialItem = async (itemName: string, accountId: string): Promise<IEquipmentDatabase> => {
const inventory = await getInventory(accountId);
const specialItemIndex = inventory.SpecialItems.push({
ItemType: itemName,
@ -314,7 +416,7 @@ export const addSpecialItem = async (itemName: string, accountId: string) => {
return changedInventory.SpecialItems[specialItemIndex - 1].toJSON();
};
export const addSpaceSuit = async (spacesuitName: string, accountId: string) => {
export const addSpaceSuit = async (spacesuitName: string, accountId: string): Promise<IEquipmentDatabase> => {
const inventory = await getInventory(accountId);
const suitIndex = inventory.SpaceSuits.push({ ItemType: spacesuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
@ -756,3 +858,17 @@ export const upgradeMod = async (artifactsData: IArtifactsRequest, accountId: st
throw error;
}
};
export const addHerse = async (ItemType: string, accountId: string): Promise<IEquipmentDatabase> => {
const inventory = await getInventory(accountId);
const herseIndex = inventory.Horses.push({ ItemType: ItemType, Configs: [], UpgradeVer: 101 });
const changedInventory = await inventory.save();
return changedInventory.Horses[herseIndex - 1].toJSON();
};
export const addUpgrade = async (ItemType: string, accountId: string): Promise<IDefaultUpgrade> => {
const inventory = await getInventory(accountId);
const upgradeIndex = inventory.Upgrades.push({ ItemType: ItemType, UpgradeFingerprint: "{}" });
const changedInventory = await inventory.save();
return changedInventory.Upgrades[upgradeIndex - 1].toJSON();
};

View File

@ -86,6 +86,7 @@ export enum EquipmentFeatures {
DOUBLE_CAPACITY = 1,
UTILITY_SLOT = 2,
GRAVIMAG_INSTALLED = 4,
GILDED = 8,
ARCANE_SLOT = 32,
INCARNON_GENESIS = 512
}

View File

@ -252,6 +252,7 @@ export interface IInventoryResponse {
DailyAffiliationZariman: number;
NemesisAbandonedRewards: string[];
DailyAffiliationKahl: number;
DailyAffiliationCavia: number;
LastInventorySync: IOid;
NextRefill: IMongoDate; // Next time argon crystals will have a decay tick
FoundToday?: IMiscItem[]; // for Argon Crystals
@ -621,11 +622,11 @@ export interface ILoadoutConfigClient {
Favorite?: boolean;
n?: string; // Loadout name
s?: IEquipmentSelection; // Suit
p?: IEquipmentSelection;
p?: IEquipmentSelection; // Secondary weapon
l?: IEquipmentSelection; // Primary weapon
m?: IEquipmentSelection; // Melee weapon
h?: IEquipmentSelection; // Gravimag weapon
a?: IEquipmentSelection;
a?: IEquipmentSelection; // Necromech exalted weapon
ItemId: IOid;
Remove?: boolean; // when client wants to remove a config, it only includes ItemId & Remove.
}
@ -807,6 +808,13 @@ export interface IQuestProgress {
b?: any[];
}
export interface IDefaultUpgrade {
ItemType: string;
UpgradeFingerprint?: string;
ItemId?: IOid;
_id?: Types.ObjectId;
}
export interface IRawUpgrade {
ItemType: string;
ItemCount: number;

View File

@ -14,7 +14,7 @@ export interface IPurchaseParams {
ExpectedPrice: number;
}
export type IInventoryChanges = Record<string, IBinChanges | object[]>;
export type IInventoryChanges = Record<string, IBinChanges | number | object[]>;
export type IBinChanges = {
count: number;

View File

@ -1 +0,0 @@
{}

View File

@ -101,6 +101,7 @@
"DailyAffiliationNecraloid": 16000,
"DailyAffiliationZariman": 16000,
"DailyAffiliationKahl": 16000,
"DailyAffiliationCavia": 16000,
"DailyFocus": 250000,
"GiftsRemaining": 8,
"LibraryAvailableDailyTaskInfo": {

View File

@ -29,6 +29,7 @@
"DailyAffiliationVentkids": 16000,
"DailyAffiliationVox": 16000,
"DailyAffiliationZariman": 16000,
"DailyAffiliationCavia": 16000,
"DailyFocus": 250000,
"DuviriInfo": { "Seed": 5898912197983600352, "NumCompletions": 0 },
"GiftsRemaining": 8,

View File

@ -1,13 +0,0 @@
{
"InventoryChanges": {
"WeaponBin": { "count": 1, "platinum": 0, "Slots": -1 },
"Suits": [
{
"ItemType": "/Lotus/Powersuits/Ninja/Ninja",
"Configs": [],
"ItemId": { "$oid": "123123123123" }
}
],
"RegularCredits": -25000
}
}

View File

@ -1 +0,0 @@
{}

View File

@ -1 +0,0 @@
{}

View File

@ -1,273 +0,0 @@
// for https://www.warframe.com/ru/droptables
/* eslint-disable */
(() => {
const missionNames = {
"Mercury/Larunda Relay": "MercuryHUB",
"Venus/Vesper Relay": "VenusHUB",
"Earth/Strata Relay": "EarthHUB",
"Mars/Maroo's Bazaar": "TradeHUB1",
"Saturn/Kronia Relay": "SaturnHUB",
"Eris/Kuiper Relay": "ErisHUB",
"Europa/Leonov Relay": "EuropaHUB",
"Pluto/Orcus Relay": "PlutoHUB",
"Venus/Romula": "ClanNode0",
"Venus/Malva": "ClanNode1",
"Earth/Coba": "ClanNode2",
"Earth/Tikal": "ClanNode3",
"Jupiter/Sinai": "ClanNode4",
"Jupiter/Cameria": "ClanNode5",
"Europa/Larzac": "ClanNode6",
"Europa/Cholistan": "ClanNode7",
"Mars/Kadesh": "ClanNode8",
"Mars/Wahiba": "ClanNode9",
"Phobos/Memphis": "ClanNode10",
"Phobos/Zeugma": "ClanNode11",
"Saturn/Caracol": "ClanNode12",
"Saturn/Piscinas": "ClanNode13",
"Sedna/Amarna": "ClanNode14",
"Sedna/Sangeru": "ClanNode15",
"Uranus/Ur": "ClanNode16",
"Uranus/Assur": "ClanNode17",
"Eris/Akkad": "ClanNode18",
"Eris/Zabala": "ClanNode19",
"Neptune/Yursa": "ClanNode20",
"Neptune/Kelashin": "ClanNode21",
"Ceres/Seimeni": "ClanNode22",
"Ceres/Gabii": "ClanNode23",
"Pluto/Sechura": "ClanNode24",
"Pluto/Hieracon": "ClanNode25",
"Phobos/Roche": "SettlementNode1",
"Phobos/Skyresh": "SettlementNode2",
"Phobos/Stickney": "SettlementNode3",
"Phobos/Kepler": "SettlementNode10",
"Phobos/Gulliver": "SettlementNode11",
"Phobos/Monolith": "SettlementNode12",
"Phobos/Shklovsky": "SettlementNode14",
"Phobos/Sharpless": "SettlementNode15",
"Phobos/Iliad": "SettlementNode20",
"Neptune/Galatea": "SolNode1",
"Venus/Aphrodite": "SolNode2",
"Pluto/Acheron": "SolNode4",
"Neptune/Despina": "SolNode6",
"Uranus/Rosalind": "SolNode9",
"Jupiter/Thebe": "SolNode10",
"Mars/Tharsis": "SolNode11",
"Mercury/Elion": "SolNode12",
"Mars/Ultor": "SolNode14",
"Earth/Pacific": "SolNode15",
"Mars/Augustus": "SolNode16",
"Neptune/Proteus": "SolNode17",
"Saturn/Rhea": "SolNode18",
"Saturn/Enceladus": "SolNode19",
"Saturn/Telesto": "SolNode20",
"Pluto/Narcissus": "SolNode21",
"Venus/Tessera": "SolNode22",
"Venus/Cytherean": "SolNode23",
"Earth/Oro": "SolNode24",
"Jupiter/Callisto": "SolNode25",
"Earth/Lith": "SolNode26",
"Earth/E Prime": "SolNode27",
"Mercury/Terminus": "SolNode28",
"Mars/Olympus": "SolNode30",
"Saturn/Anthe": "SolNode31",
"Saturn/Tethys": "SolNode32",
"Uranus/Ariel": "SolNode33",
"Uranus/Sycorax": "SolNode34",
"Mars/Martialis": "SolNode36",
"Pluto/Minthe": "SolNode38",
"Earth/Everest": "SolNode39",
"Mars/Arval": "SolNode41",
"Saturn/Helene": "SolNode42",
"Pluto/Cerberus": "SolNode43",
"Mars/Ara": "SolNode45",
"Mars/Spear": "SolNode46",
"Pluto/Regna": "SolNode48",
"Neptune/Larissa": "SolNode49",
"Saturn/Numa": "SolNode50",
"Pluto/Hades": "SolNode51",
"Jupiter/Themisto": "SolNode53",
"Pluto/Cypress": "SolNode56",
"Neptune/Sao": "SolNode57",
"Mars/Hellas": "SolNode58",
"Earth/Eurasia": "SolNode59",
"Uranus/Caliban": "SolNode60",
"Venus/Ishtar": "SolNode61",
"Neptune/Neso": "SolNode62",
"Earth/Mantle": "SolNode63",
"Uranus/Umbriel": "SolNode64",
"Mars/Gradivus": "SolNode65",
"Venus/Unda": "SolNode66",
"Saturn/Dione": "SolNode67",
"Mars/Vallis": "SolNode68",
"Uranus/Ophelia": "SolNode69",
"Saturn/Cassini": "SolNode70",
"Pluto/Outer Terminus": "SolNode72",
"Jupiter/Ananke": "SolNode73",
"Jupiter/Carme": "SolNode74",
"Earth/Cervantes": "SolNode75",
"Pluto/Hydra": "SolNode76",
"Neptune/Triton": "SolNode78",
"Earth/Cambria": "SolNode79",
"Pluto/Palus": "SolNode81",
"Saturn/Calypso": "SolNode82",
"Uranus/Cressida": "SolNode83",
"Neptune/Nereid": "SolNode84",
"Earth/Gaia": "SolNode85",
"Jupiter/Ganymede": "SolNode87",
"Jupiter/Adrastea": "SolNode88",
"Earth/Mariana": "SolNode89",
"Saturn/Keeler": "SolNode93",
"Mercury/Apollodorus": "SolNode94",
"Saturn/Titan": "SolNode96",
"Jupiter/Amalthea": "SolNode97",
"Uranus/Desdemona": "SolNode98",
"Mars/War": "SolNode99",
"Jupiter/Elara": "SolNode100",
"Venus/Kiliken": "SolNode101",
"Pluto/Oceanum": "SolNode102",
"Mercury/M Prime": "SolNode103",
"Venus/Fossa": "SolNode104",
"Uranus/Titania": "SolNode105",
"Mars/Alator": "SolNode106",
"Venus/Venera": "SolNode107",
"Mercury/Tolstoj": "SolNode108",
"Venus/Linea": "SolNode109",
"Mars/Ares": "SolNode113",
"Uranus/Puck": "SolNode114",
"Neptune/Laomedeia": "SolNode118",
"Mercury/Caloris": "SolNode119",
"Jupiter/Carpo": "SolNode121",
"Uranus/Stephano": "SolNode122",
"Venus/V Prime": "SolNode123",
"Jupiter/Io": "SolNode125",
"Jupiter/Metis": "SolNode126",
"Neptune/Psamathe": "SolNode127",
"Venus/E Gate": "SolNode128",
"Venus/Orb Vallis": "SolNode129",
"Mercury/Lares": "SolNode130",
"Ceres/Pallas": "SolNode131",
"Ceres/Bode": "SolNode132",
"Ceres/Thon": "SolNode135",
"Ceres/Nuovo": "SolNode137",
"Ceres/Ludi": "SolNode138",
"Ceres/Lex": "SolNode139",
"Ceres/Kiste": "SolNode140",
"Ceres/Ker": "SolNode141",
"Ceres/Exta": "SolNode144",
"Ceres/Draco": "SolNode146",
"Ceres/Cinxia": "SolNode147",
"Ceres/Casta": "SolNode149",
"Eris/Brugia": "SolNode153",
"Eris/Isos": "SolNode162",
"Eris/Kala-azar": "SolNode164",
"Eris/Nimus": "SolNode166",
"Eris/Oestrus": "SolNode167",
"Eris/Saxis": "SolNode171",
"Eris/Xini": "SolNode172",
"Eris/Solium": "SolNode173",
"Eris/Naeglar": "SolNode175",
"Sedna/Kappa": "SolNode177",
"Sedna/Adaro": "SolNode181",
"Sedna/Vodyanoi": "SolNode183",
"Sedna/Rusalka": "SolNode184",
"Sedna/Berehynia": "SolNode185",
"Sedna/Selkie": "SolNode187",
"Sedna/Kelpie": "SolNode188",
"Sedna/Naga": "SolNode189",
"Sedna/Nakki": "SolNode190",
"Sedna/Marid": "SolNode191",
"Sedna/Merrow": "SolNode193",
"Sedna/Hydron": "SolNode195",
"Sedna/Charybdis": "SolNode196",
"Sedna/Yam": "SolNode199",
"Europa/Abaddon": "SolNode203",
"Europa/Armaros": "SolNode204",
"Europa/Baal": "SolNode205",
"Europa/Morax": "SolNode209",
"Europa/Naamah": "SolNode210",
"Europa/Ose": "SolNode211",
"Europa/Paimon": "SolNode212",
"Europa/Sorath": "SolNode214",
"Europa/Valac": "SolNode215",
"Europa/Valefor": "SolNode216",
"Europa/Orias": "SolNode217",
"Europa/Kokabiel": "SolNode220",
"Mercury/Boethius": "SolNode223",
"Mercury/Odin": "SolNode224",
"Mercury/Suisei": "SolNode225",
"Mercury/Pantheon": "SolNode226",
"Earth/Plains of Eidolon": "SolNode228",
"Deimos/Cambion Drift": "SolNode229",
"Lua/Plato": "SolNode300",
"Lua/Grimaldi": "SolNode301",
"Lua/Tycho": "SolNode302",
"Lua/Copernicus": "SolNode304",
"Lua/Stöfler": "SolNode305",
"Lua/Pavlov": "SolNode306",
"Lua/Zeipel": "SolNode307",
"Lua/Apollo": "SolNode308",
"Void/Teshub": "SolNode400",
"Void/Hepit": "SolNode401",
"Void/Taranis": "SolNode402",
"Void/Tiwaz": "SolNode403",
"Void/Stribog": "SolNode404",
"Void/Ani": "SolNode405",
"Void/Ukko": "SolNode406",
"Void/Oxomoco": "SolNode407",
"Void/Belenus": "SolNode408",
"Void/Mot": "SolNode409",
"Void/Aten": "SolNode410",
"Void/Marduk": "SolNode411",
"Void/Mithra": "SolNode412",
"undefined/Jordas Golem Assassinate": "SolNode701",
"undefined/Mutalist Alad V Assassinate": "SolNode705",
"Deimos/Horend": "SolNode706",
"Deimos/Hyf": "SolNode707",
"Deimos/Phlegyas": "SolNode708",
"Deimos/Dirus": "SolNode709",
"Deimos/Formido": "SolNode710",
"Deimos/Terrorem": "SolNode711",
"Deimos/Magnacidium": "SolNode712",
"Deimos/Exequias": "SolNode713",
"Jupiter/The Ropalolyst": "SolNode740",
"Kuva Fortress/Koro": "SolNode741",
"Kuva Fortress/Nabuk": "SolNode742",
"Kuva Fortress/Rotuma": "SolNode743",
"Kuva Fortress/Taveuni": "SolNode744",
"Kuva Fortress/Tamu": "SolNode745",
"Kuva Fortress/Dakata": "SolNode746",
"Kuva Fortress/Pago": "SolNode747",
"Kuva Fortress/Garus": "SolNode748",
"Venus/Montes": "SolNode902",
"Earth/Erpo": "SolNode903",
"Mars/Syrtis": "SolNode904",
"Jupiter/Galilea": "SolNode905",
"Saturn/Pandora": "SolNode906",
"Uranus/Caelus": "SolNode907"
};
const result = {};
let lastItem = [];
let lastItemIndex;
let rotation;
Array.from(document.querySelectorAll("table")[0].children[0].children).forEach(element => {
if (element.classList.contains("blank-row")) {
if (lastItemIndex) result[lastItemIndex] = lastItem;
lastItem = [];
lastItemIndex = undefined;
rotation = undefined;
} else if (element.children[0].getAttribute("colspan") == 2) {
if (!lastItemIndex) {
const mission = element.children[0].textContent;
const formatedMission = mission.substring(0, mission.indexOf(" ("));
lastItemIndex = missionNames[formatedMission];
} else {
rotation = element.children[0].textContent.replace("Rotation ", "");
}
} else {
const name = element.children[0].textContent;
const chance = parseFloat(element.children[1].textContent.match(/(\d+\.\d+)/)[0]);
lastItem.push({ chance, name, ...(rotation !== undefined && { rotation }) });
}
});
return JSON.stringify(result);
})();

View File

@ -9,23 +9,12 @@
<body>
<nav class="navbar sticky-top bg-body-tertiary">
<div class="container">
<button
class="navbar-toggler d-lg-none"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#sidebar"
aria-controls="sidebar"
aria-label="Toggle sidebar"
>
<button class="navbar-toggler d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar" aria-controls="sidebar" aria-label="Toggle sidebar">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand">OpenWF WebUI</a>
<div class="nav-item dropdown">
<button
class="nav-link dropdown-toggle displayname"
data-bs-toggle="dropdown"
aria-expanded="false"
></button>
<button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/webui/" onclick="logout();">Logout</a></li>
</ul>
@ -48,40 +37,19 @@
<div class="navbar p-0">
<ul class="navbar-nav justify-content-end">
<li class="nav-item">
<a
class="nav-link"
href="/webui/inventory"
data-bs-dismiss="offcanvas"
data-bs-target="#sidebar"
>
Inventory
</a>
<a class="nav-link" href="/webui/inventory" data-bs-dismiss="offcanvas" data-bs-target="#sidebar">Inventory</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/webui/mods"
data-bs-dismiss="offcanvas"
data-bs-target="#sidebar"
>
Mods
</a>
<a class="nav-link" href="/webui/mods" data-bs-dismiss="offcanvas" data-bs-target="#sidebar">Mods</a>
</li>
<li class="nav-item">
<a
class="nav-link"
href="/webui/settings"
data-bs-dismiss="offcanvas"
data-bs-target="#sidebar"
>
Settings
</a>
<a class="nav-link" href="/webui/cheats" data-bs-dismiss="offcanvas" data-bs-target="#sidebar">Cheats</a>
</li>
</ul>
</div>
</div>
</div>
<div>
<div class="w-100">
<div data-route="/webui/" data-title="Login | OpenWF WebUI">
<p>Login using your OpenWF account credentials.</p>
<form onsubmit="doLogin();return false;">
@ -112,12 +80,8 @@
<div class="card mb-4">
<h5 class="card-header">Warframes</h5>
<div class="card-body">
<form class="input-group" onsubmit="doAcquireWarframe();return false;">
<input
class="form-control"
id="warframe-to-acquire"
list="datalist-warframes"
/>
<form class="input-group mb-3" onsubmit="doAcquireWarframe();return false;">
<input class="form-control" id="warframe-to-acquire" list="datalist-warframes" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
@ -130,7 +94,7 @@
<div class="card mb-4">
<h5 class="card-header">Weapons</h5>
<div class="card-body">
<form class="input-group" onsubmit="doAcquireWeapon();return false;">
<form class="input-group mb-3" onsubmit="doAcquireWeapon();return false;">
<input class="form-control" id="weapon-to-acquire" list="datalist-weapons" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
@ -154,26 +118,14 @@
<form class="card-body" onsubmit="doAcquireRiven();return false;">
<select class="form-control mb-3" id="addriven-type">
<option value="LotusArchgunRandomModRare">LotusArchgunRandomModRare</option>
<option value="LotusModularMeleeRandomModRare">
LotusModularMeleeRandomModRare
</option>
<option value="LotusModularPistolRandomModRare">
LotusModularPistolRandomModRare
</option>
<option value="LotusModularMeleeRandomModRare">LotusModularMeleeRandomModRare</option>
<option value="LotusModularPistolRandomModRare">LotusModularPistolRandomModRare</option>
<option value="LotusPistolRandomModRare">LotusPistolRandomModRare</option>
<option value="LotusRifleRandomModRare" selected>
LotusRifleRandomModRare
</option>
<option value="LotusRifleRandomModRare" selected>LotusRifleRandomModRare</option>
<option value="LotusShotgunRandomModRare">LotusShotgunRandomModRare</option>
<option value="PlayerMeleeWeaponRandomModRare">
PlayerMeleeWeaponRandomModRare
</option>
<option value="PlayerMeleeWeaponRandomModRare">PlayerMeleeWeaponRandomModRare</option>
</select>
<textarea
id="addriven-fingerprint"
class="form-control mb-3"
placeholder="Fingerprint"
></textarea>
<textarea id="addriven-fingerprint" class="form-control mb-3" placeholder="Fingerprint"></textarea>
<button class="btn btn-primary" style="margin-right: 5px" type="submit">Add</button>
<a href="riven-tool/" target="_blank">Need help with the fingerprint?</a>
</form>
@ -191,7 +143,7 @@
<div class="card mb-4">
<h5 class="card-header">Mods</h5>
<div class="card-body">
<form class="input-group" onsubmit="doAcquireMod();return false;">
<form class="input-group mb-3" onsubmit="doAcquireMod();return false;">
<input class="form-control" id="mod-to-acquire" list="datalist-mods" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
@ -203,8 +155,12 @@
</div>
</div>
</div>
<div data-route="/webui/settings" data-title="Settings | OpenWF WebUI">
<form onsubmit="doChangeSettings();return false;">
<div data-route="/webui/cheats, /webui/settings" data-title="Cheats | OpenWF WebUI">
<div class="row">
<div class="col-lg-4">
<div class="card mb-4">
<h5 class="card-header">Server</h5>
<form class="card-body" onsubmit="doChangeSettings();return false;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="skipStoryModeChoice" />
<label class="form-check-label" for="skipStoryModeChoice">Skip Story Mode Choice</label>
@ -231,9 +187,7 @@
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteResources" />
<label class="form-check-label" for="infiniteResources">
Infinite Credits and Platinum
</label>
<label class="form-check-label" for="infiniteResources">Infinite Credits and Platinum</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
@ -241,9 +195,7 @@
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllShipDecorations" />
<label class="form-check-label" for="unlockAllShipDecorations">
Unlock All Ship Decorations
</label>
<label class="form-check-label" for="unlockAllShipDecorations">Unlock All Ship Decorations</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllFlavourItems" />
@ -261,7 +213,7 @@
Universal Polarity Everywhere
</label>
</div>
<div class="form-group mt-2 mb-2">
<div class="form-group mt-2">
<label class="form-label" for="spoofMasteryRank">
Spoofed Mastery Rank (-1 to disable)
</label>
@ -271,6 +223,37 @@
</form>
</div>
</div>
<div class="col-lg-4">
<div class="card mb-4">
<h5 class="card-header">Account</h5>
<div class="card-body">
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();">Unlock All Focus Schools</button>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card mb-4">
<h5 class="card-header">Client</h5>
<div id="client-cheats-nok" class="card-body">
Client cheats are currently unavailable. This could be because your client is not running or using a DLL without an HTTP interface.
</div>
<div id="client-cheats-ok" class="card-body d-none">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="skip_mission_start_timer" />
<label class="form-check-label" for="skip_mission_start_timer">
Skip Mission Start Timer
</label>
</div>
<div class="form-group mt-3">
<label class="form-label" for="fov_override">FOV Override (0 to disable)</label>
<input id="fov_override" class="form-range" type="range" min="0" value="0" max="2260000" step="10000">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<datalist id="datalist-warframes"></datalist>
<datalist id="datalist-weapons"></datalist>

View File

@ -701,3 +701,83 @@ function doChangeSettings() {
});
});
}
// Cheats route
fetch("http://localhost:61558/ping", { mode: "no-cors" }).then(() => {
$("#client-cheats-nok").addClass("d-none");
$("#client-cheats-ok").removeClass("d-none");
fetch("http://localhost:61558/skip_mission_start_timer")
.then(res => res.text())
.then(res => {
document.getElementById("skip_mission_start_timer").checked = res == "1";
});
document.getElementById("skip_mission_start_timer").onchange = function () {
fetch("http://localhost:61558/skip_mission_start_timer?" + this.checked);
};
fetch("http://localhost:61558/fov_override")
.then(res => res.text())
.then(res => {
document.getElementById("fov_override").value = parseFloat(res) * 10000;
});
document.getElementById("fov_override").oninput = function () {
fetch("http://localhost:61558/fov_override?" + this.value);
};
});
function doUnlockAllFocusSchools() {
revalidateAuthz(() => {
$.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1").done(async data => {
const missingFocusUpgrades = {
"/Lotus/Upgrades/Focus/Attack/AttackFocusAbility": true,
"/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility": true,
"/Lotus/Upgrades/Focus/Ward/WardFocusAbility": true,
"/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility": true,
"/Lotus/Upgrades/Focus/Power/PowerFocusAbility": true
};
if (data.FocusUpgrades) {
for (const focusUpgrade of data.FocusUpgrades) {
if (focusUpgrade.ItemType in missingFocusUpgrades) {
delete missingFocusUpgrades[focusUpgrade.ItemType];
}
}
}
for (const upgradeType of Object.keys(missingFocusUpgrades)) {
await unlockFocusSchool(upgradeType);
}
if (Object.keys(missingFocusUpgrades).length == 0) {
alert("All focus schools are already unlocked.");
} else {
alert(
"Unlocked " +
Object.keys(missingFocusUpgrades).length +
" new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that."
);
}
});
});
}
function unlockFocusSchool(upgradeType) {
return new Promise(resolve => {
// Deselect current FocusAbility so we will be able to unlock the way for free
$.post({
url: "/api/focus.php?" + window.authz + "&op=5",
contentType: "text/plain",
data: "{}"
}).done(function () {
// Unlock the way now
$.post({
url: "/api/focus.php?" + window.authz + "&op=2",
contentType: "text/plain",
data: JSON.stringify({
FocusType: upgradeType
})
}).done(function () {
resolve();
});
});
});
}