Compare commits

...

4 Commits

Author SHA1 Message Date
993db78ee6 chore: move int cheats into account section
Re #2361
2025-10-19 21:05:18 +02:00
7fe00da2a4 fix: remove vors prize from questCompletionRewards (#2911)
Because this file overrides the public export, it means The Teacher quest would not be given.

Reviewed-on: OpenWF/SpaceNinjaServer#2911
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:48 -07:00
bac23a8465 fix(webui): use text type for email input (#2910)
We don't need the browser to validate the input because the game accepts emails with nothing before the @ which the browser may not.

Reviewed-on: OpenWF/SpaceNinjaServer#2910
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:39 -07:00
db112ee5ed chore: handle updateQuest request having CompletionDate (#2909)
The client date representation would produce a schema error

Reviewed-on: OpenWF/SpaceNinjaServer#2909
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-18 23:44:28 -07:00
12 changed files with 50 additions and 54 deletions

View File

@ -14,9 +14,6 @@
"unlockAllSkins": false, "unlockAllSkins": false,
"fullyStockedVendors": false, "fullyStockedVendors": false,
"skipClanKeyCrafting": false, "skipClanKeyCrafting": false,
"spoofMasteryRank": -1,
"relicRewardItemCountMultiplier": 1,
"nightwaveStandingMultiplier": 1,
"unfaithfulBugFixes": { "unfaithfulBugFixes": {
"ignore1999LastRegionPlayed": false, "ignore1999LastRegionPlayed": false,
"fixXtraCheeseTimer": false, "fixXtraCheeseTimer": false,

View File

@ -348,12 +348,12 @@ export const getInventoryResponse = async (
} }
} }
if (typeof config.spoofMasteryRank === "number" && config.spoofMasteryRank >= 0) { if (inventory.spoofMasteryRank && inventory.spoofMasteryRank >= 0) {
inventoryResponse.PlayerLevel = config.spoofMasteryRank; inventoryResponse.PlayerLevel = inventory.spoofMasteryRank;
if (!xpBasedLevelCapDisabled) { if (!xpBasedLevelCapDisabled) {
// This client has not been patched to accept any mastery rank, need to fake the XP. // This client has not been patched to accept any mastery rank, need to fake the XP.
inventoryResponse.XPInfo = []; inventoryResponse.XPInfo = [];
let numFrames = getExpRequiredForMr(Math.min(config.spoofMasteryRank, 5030)) / 6000; let numFrames = getExpRequiredForMr(Math.min(inventory.spoofMasteryRank, 5030)) / 6000;
while (numFrames-- > 0) { while (numFrames-- > 0) {
inventoryResponse.XPInfo.push({ inventoryResponse.XPInfo.push({
ItemType: "/Lotus/Powersuits/Mag/Mag", ItemType: "/Lotus/Powersuits/Mag/Mag",

View File

@ -14,7 +14,7 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
const inventory = await getInventory( const inventory = await getInventory(
account._id.toString(), account._id.toString(),
"ChallengesFixVersion ChallengeProgress SeasonChallengeHistory Affiliations CalendarProgress" "ChallengesFixVersion ChallengeProgress SeasonChallengeHistory Affiliations CalendarProgress nightwaveStandingMultiplier"
); );
let affiliationMods: IAffiliationMods[] = []; let affiliationMods: IAffiliationMods[] = [];
if (challenges.ChallengeProgress) { if (challenges.ChallengeProgress) {

View File

@ -8,7 +8,6 @@ import { logger } from "../utils/logger.ts";
import { addMiscItems, combineInventoryChanges } from "../services/inventoryService.ts"; import { addMiscItems, combineInventoryChanges } from "../services/inventoryService.ts";
import { handleStoreItemAcquisition } from "../services/purchaseService.ts"; import { handleStoreItemAcquisition } from "../services/purchaseService.ts";
import type { IInventoryChanges } from "../types/purchaseTypes.ts"; import type { IInventoryChanges } from "../types/purchaseTypes.ts";
import { config } from "../services/configService.ts";
export const crackRelic = async ( export const crackRelic = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
@ -29,10 +28,10 @@ export const crackRelic = async (
ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
weights weights
)!; )!;
if (config.relicRewardItemCountMultiplier !== undefined && (config.relicRewardItemCountMultiplier ?? 1) != 1) { if (inventory.relicRewardItemCountMultiplier && inventory.relicRewardItemCountMultiplier != 1) {
reward = { reward = {
...reward, ...reward,
itemCount: reward.itemCount * config.relicRewardItemCountMultiplier itemCount: reward.itemCount * inventory.relicRewardItemCountMultiplier
}; };
} }
logger.debug(`relic rolled`, reward); logger.debug(`relic rolled`, reward);

View File

@ -1469,6 +1469,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
nemesisHintProgressMultiplierGrineer: Number, nemesisHintProgressMultiplierGrineer: Number,
nemesisHintProgressMultiplierCorpus: Number, nemesisHintProgressMultiplierCorpus: Number,
nemesisExtraWeapon: Number, nemesisExtraWeapon: Number,
spoofMasteryRank: { type: Number, default: -1 },
relicRewardItemCountMultiplier: { type: Number, default: 1 },
nightwaveStandingMultiplier: { type: Number, default: 1 },
SubscribedToEmails: { type: Number, default: 0 }, SubscribedToEmails: { type: Number, default: 0 },
SubscribedToEmailsPersonalized: { type: Number, default: 0 }, SubscribedToEmailsPersonalized: { type: Number, default: 0 },

View File

@ -24,9 +24,6 @@ export interface IConfig {
unlockAllSkins?: boolean; unlockAllSkins?: boolean;
fullyStockedVendors?: boolean; fullyStockedVendors?: boolean;
skipClanKeyCrafting?: boolean; skipClanKeyCrafting?: boolean;
spoofMasteryRank?: number;
relicRewardItemCountMultiplier?: number;
nightwaveStandingMultiplier?: number;
unfaithfulBugFixes?: { unfaithfulBugFixes?: {
ignore1999LastRegionPlayed?: boolean; ignore1999LastRegionPlayed?: boolean;
fixXtraCheeseTimer?: boolean; fixXtraCheeseTimer?: boolean;
@ -149,7 +146,10 @@ export const configRemovedOptionsKeys = [
"fastClanAscension", "fastClanAscension",
"unlockAllFlavourItems", "unlockAllFlavourItems",
"unlockAllShipDecorations", "unlockAllShipDecorations",
"unlockAllDecoRecipes" "unlockAllDecoRecipes",
"spoofMasteryRank",
"relicRewardItemCountMultiplier",
"nightwaveStandingMultiplier"
]; ];
export const configPath = path.join(repoDir, args.configPath ?? "config.json"); export const configPath = path.join(repoDir, args.configPath ?? "config.json");

View File

@ -1,6 +1,5 @@
import type { IFriendInfo } from "../types/friendTypes.ts"; import type { IFriendInfo } from "../types/friendTypes.ts";
import { getInventory } from "./inventoryService.ts"; import { getInventory } from "./inventoryService.ts";
import { config } from "./configService.ts";
import { Account } from "../models/loginModel.ts"; import { Account } from "../models/loginModel.ts";
import type { Types } from "mongoose"; import type { Types } from "mongoose";
import { Friendship } from "../models/friendModel.ts"; import { Friendship } from "../models/friendModel.ts";
@ -13,8 +12,8 @@ export const addAccountDataToFriendInfo = async (info: IFriendInfo): Promise<voi
}; };
export const addInventoryDataToFriendInfo = async (info: IFriendInfo): Promise<void> => { export const addInventoryDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
const inventory = await getInventory(fromOid(info._id), "PlayerLevel ActiveAvatarImageType"); const inventory = await getInventory(fromOid(info._id), "PlayerLevel ActiveAvatarImageType spoofMasteryRank");
info.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank; info.PlayerLevel = inventory.spoofMasteryRank == -1 ? inventory.PlayerLevel : inventory.spoofMasteryRank;
info.ActiveAvatarImageType = inventory.ActiveAvatarImageType; info.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
}; };

View File

@ -2131,7 +2131,7 @@ export const addChallenges = async (
]; ];
} }
const standingToAdd = meta.standing! * (config.nightwaveStandingMultiplier ?? 1); const standingToAdd = meta.standing! * (inventory.nightwaveStandingMultiplier ?? 1);
affiliation.Standing += standingToAdd; affiliation.Standing += standingToAdd;
if (affiliationMods.length == 0) { if (affiliationMods.length == 0) {
affiliationMods.push({ Tag: nightwaveSyndicateTag }); affiliationMods.push({ Tag: nightwaveSyndicateTag });

View File

@ -13,7 +13,7 @@ import questCompletionItems from "../../static/fixed_responses/questCompletionRe
import type { ITypeCount } from "../types/commonTypes.ts"; import type { ITypeCount } from "../types/commonTypes.ts";
export interface IUpdateQuestRequest { export interface IUpdateQuestRequest {
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[]; QuestKeys: IQuestKeyClient[];
PS: string; PS: string;
questCompletion: boolean; questCompletion: boolean;
PlayerShipEvents: unknown[]; PlayerShipEvents: unknown[];
@ -36,6 +36,7 @@ export const updateQuestKey = async (
throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`); throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
} }
delete questKeyUpdate[0].CompletionDate;
inventory.QuestKeys[questKeyIndex].overwrite(questKeyUpdate[0]); inventory.QuestKeys[questKeyIndex].overwrite(questKeyUpdate[0]);
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};

View File

@ -61,6 +61,9 @@ export interface IAccountCheats {
nemesisHintProgressMultiplierGrineer?: number; nemesisHintProgressMultiplierGrineer?: number;
nemesisHintProgressMultiplierCorpus?: number; nemesisHintProgressMultiplierCorpus?: number;
nemesisExtraWeapon?: number; nemesisExtraWeapon?: number;
spoofMasteryRank?: number;
relicRewardItemCountMultiplier?: number;
nightwaveStandingMultiplier?: number;
} }
export interface IInventoryDatabase export interface IInventoryDatabase

View File

@ -1,10 +1,4 @@
{ {
"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain": [
{
"ItemType": "/Lotus/Types/NeutralCreatures/ErsatzHorse/ErsatzHorsePowerSuit",
"ItemCount": 1
}
],
"/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain": [{ "ItemType": "/Lotus/Types/Recipes/WarframeRecipes/BrokenFrameBlueprint", "ItemCount": 1 }], "/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain": [{ "ItemType": "/Lotus/Types/Recipes/WarframeRecipes/BrokenFrameBlueprint", "ItemCount": 1 }],
"/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain": [ "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain": [
{ {

View File

@ -83,7 +83,7 @@
<p data-loc="login_description"></p> <p data-loc="login_description"></p>
<form onsubmit="doLogin();return false;"> <form onsubmit="doLogin();return false;">
<label for="email" data-loc="login_emailLabel"></label> <label for="email" data-loc="login_emailLabel"></label>
<input class="form-control" type="email" id="email" required /> <input class="form-control" type="text" id="email" required />
<br /> <br />
<label for="password" data-loc="login_passwordLabel"></label> <label for="password" data-loc="login_passwordLabel"></label>
<input class="form-control" type="password" id="password" required /> <input class="form-control" type="password" id="password" required />
@ -1071,6 +1071,20 @@
<input class="form-check-input" type="checkbox" id="finishInvasionsInOneMission" /> <input class="form-check-input" type="checkbox" id="finishInvasionsInOneMission" />
<label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label> <label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label>
</div> </div>
<form class="form-group mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
<label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
<div class="input-group">
<select class="form-control" id="changeSyndicate"></select>
<button class="btn btn-secondary" type="submit" data-loc="cheats_changeButton"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<div class="input-group">
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" data-default="-1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2"> <form class="form-group mt-2">
<label class="form-label" for="nemesisHenchmenKillsMultiplierGrineer" data-loc="cheats_nemesisHenchmenKillsMultiplierGrineer"></label> <label class="form-label" for="nemesisHenchmenKillsMultiplierGrineer" data-loc="cheats_nemesisHenchmenKillsMultiplierGrineer"></label>
<div class="input-group"> <div class="input-group">
@ -1113,6 +1127,20 @@
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button> <button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div> </div>
</form> </form>
<form class="form-group mt-2">
<label class="form-label" for="relicRewardItemCountMultiplier" data-loc="cheats_relicRewardItemCountMultiplier"></label>
<div class="input-group">
<input class="form-control" id="relicRewardItemCountMultiplier" type="number" min="1" max="1000000" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
<div class="input-group">
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<div class="mt-2 mb-2 d-flex flex-wrap gap-2"> <div class="mt-2 mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="debounce(doUnlockAllShipFeatures);" data-loc="cheats_unlockAllShipFeatures"></button> <button class="btn btn-primary" onclick="debounce(doUnlockAllShipFeatures);" data-loc="cheats_unlockAllShipFeatures"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button> <button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
@ -1127,13 +1155,6 @@
<button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button> <button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button> <button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button>
</div> </div>
<form class="mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
<label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
<div class="input-group">
<select class="form-control" id="changeSyndicate"></select>
<button class="btn btn-secondary" type="submit" data-loc="cheats_changeButton"></button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
@ -1161,27 +1182,6 @@
<input class="form-check-input" type="checkbox" id="skipClanKeyCrafting" /> <input class="form-check-input" type="checkbox" id="skipClanKeyCrafting" />
<label class="form-check-label" for="skipClanKeyCrafting" data-loc="cheats_skipClanKeyCrafting"></label> <label class="form-check-label" for="skipClanKeyCrafting" data-loc="cheats_skipClanKeyCrafting"></label>
</div> </div>
<form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<div class="input-group">
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" data-default="-1" />
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2" onsubmit="doSaveConfigInt('relicRewardItemCountMultiplier'); return false;">
<label class="form-label" for="relicRewardItemCountMultiplier" data-loc="cheats_relicRewardItemCountMultiplier"></label>
<div class="input-group">
<input class="form-control" id="relicRewardItemCountMultiplier" type="number" min="1" max="1000000" data-default="1" />
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;">
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
<div class="input-group">
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" data-default="1" />
<button class="btn btn-secondary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>