merge upstream

This commit is contained in:
Corvus 2025-07-01 08:21:01 -07:00
commit 290866f5c8
26 changed files with 179 additions and 37 deletions

View File

@ -60,6 +60,7 @@
"unlockAllSimarisResearchEntries": false, "unlockAllSimarisResearchEntries": false,
"disableDailyTribute": false, "disableDailyTribute": false,
"spoofMasteryRank": -1, "spoofMasteryRank": -1,
"relicRewardItemCountMultiplier": 1,
"nightwaveStandingMultiplier": 1, "nightwaveStandingMultiplier": 1,
"unfaithfulBugFixes": { "unfaithfulBugFixes": {
"ignore1999LastRegionPlayed": false, "ignore1999LastRegionPlayed": false,

8
package-lock.json generated
View File

@ -23,7 +23,7 @@
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"undici": "^7.10.0", "undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.74", "warframe-public-export-plus": "^0.5.76",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0", "winston-daily-rotate-file": "^5.0.0",
@ -3386,9 +3386,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.74", "version": "0.5.76",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.74.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.76.tgz",
"integrity": "sha512-pA7dxA0lKn9w/2Sc97oxnn+CEzL1SrT9XriNLTDF4Xp+2SBEpGcfbqbdR9ljPQJopIbrc9Zy02R+uBQVomcwyA==" "integrity": "sha512-0gX3NTWaxFyzUmqBSUHhPY8pMRX92iXQFqoBuMQlMG1+6uC6JMKtwP5t8cuXR3pvV2vkaCi/cDWjP1JUChkZ9g=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -37,7 +37,7 @@
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"undici": "^7.10.0", "undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.74", "warframe-public-export-plus": "^0.5.76",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0", "winston-daily-rotate-file": "^5.0.0",

View File

@ -5,6 +5,7 @@ import {
getGuildVault, getGuildVault,
hasAccessToDojo, hasAccessToDojo,
hasGuildPermission, hasGuildPermission,
processCompletedGuildTechProject,
processFundedGuildTechProject, processFundedGuildTechProject,
processGuildTechProjectContributionsUpdate, processGuildTechProjectContributionsUpdate,
removePigmentsFromGuildMembers, removePigmentsFromGuildMembers,
@ -51,8 +52,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
}; };
if (project.CompletionDate) { if (project.CompletionDate) {
techProject.CompletionDate = toMongoDate(project.CompletionDate); techProject.CompletionDate = toMongoDate(project.CompletionDate);
if (Date.now() >= project.CompletionDate.getTime()) { if (
needSave ||= setGuildTechLogState(guild, project.ItemType, 4, project.CompletionDate); Date.now() >= project.CompletionDate.getTime() &&
setGuildTechLogState(guild, project.ItemType, 4, project.CompletionDate)
) {
processCompletedGuildTechProject(guild, project.ItemType);
needSave = true;
} }
} }
techProjects.push(techProject); techProjects.push(techProject);

View File

@ -9,6 +9,7 @@ import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inve
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus"; import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService"; import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
import { import {
addEmailItem,
addMiscItems, addMiscItems,
allDailyAffiliationKeys, allDailyAffiliationKeys,
cleanupInventory, cleanupInventory,
@ -110,7 +111,58 @@ export const inventoryController: RequestHandler = async (request, response) =>
} }
if (inventory.CalendarProgress) { if (inventory.CalendarProgress) {
const previousYearIteration = inventory.CalendarProgress.Iteration;
getCalendarProgress(inventory); // handle year rollover; the client expects to receive an inventory with an up-to-date CalendarProgress getCalendarProgress(inventory); // handle year rollover; the client expects to receive an inventory with an up-to-date CalendarProgress
// also handle sending of kiss cinematic at year rollover
if (
inventory.CalendarProgress.Iteration != previousYearIteration &&
inventory.DialogueHistory &&
inventory.DialogueHistory.Dialogues
) {
let kalymos = false;
for (const { dialogueName, kissEmail } of [
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/ArthurKissEmailItem"
},
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/EleanorKissEmailItem"
},
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/LettieKissEmailItem"
},
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/AmirKissEmailItem"
},
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/AoiKissEmailItem"
},
{
dialogueName: "/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue",
kissEmail: "/Lotus/Types/Items/EmailItems/QuincyKissEmailItem"
}
]) {
const dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == dialogueName);
if (dialogue) {
if (dialogue.Rank == 7) {
await addEmailItem(inventory, kissEmail);
kalymos = false;
break;
}
if (dialogue.Rank == 6) {
kalymos = true;
}
}
}
if (kalymos) {
await addEmailItem(inventory, "/Lotus/Types/Items/EmailItems/KalymosKissEmailItem");
}
}
} }
cleanupInventory(inventory); cleanupInventory(inventory);

View File

@ -48,10 +48,8 @@ export const loginRewardsController: RequestHandler = async (req, res) => {
response.DailyTributeInfo.HasChosenReward = true; response.DailyTributeInfo.HasChosenReward = true;
response.DailyTributeInfo.ChosenReward = randomRewards[0]; response.DailyTributeInfo.ChosenReward = randomRewards[0];
response.DailyTributeInfo.NewInventory = await claimLoginReward(inventory, randomRewards[0]); response.DailyTributeInfo.NewInventory = await claimLoginReward(inventory, randomRewards[0]);
await inventory.save();
setAccountGotLoginRewardToday(account); setAccountGotLoginRewardToday(account);
await account.save(); await Promise.all([inventory.save(), account.save()]);
sendWsBroadcastTo(account._id.toString(), { update_inventory: true }); sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
} }

View File

@ -35,10 +35,8 @@ export const loginRewardsSelectionController: RequestHandler = async (req, res)
chosenReward = randomRewards.find(x => x.StoreItemType == body.ChosenReward)!; chosenReward = randomRewards.find(x => x.StoreItemType == body.ChosenReward)!;
inventoryChanges = await claimLoginReward(inventory, chosenReward); inventoryChanges = await claimLoginReward(inventory, chosenReward);
} }
await inventory.save();
setAccountGotLoginRewardToday(account); setAccountGotLoginRewardToday(account);
await account.save(); await Promise.all([inventory.save(), account.save()]);
sendWsBroadcastTo(account._id.toString(), { update_inventory: true }); sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
res.json({ res.json({

View File

@ -57,8 +57,12 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
component.DecoCapacity -= meta.capacityCost; component.DecoCapacity -= meta.capacityCost;
} }
} else { } else {
const [itemType, meta] = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)!; const entry = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type);
if (!itemType || meta.dojoCapacityCost === undefined) { if (!entry) {
throw new Error(`unknown deco type: ${deco.Type}`);
}
const [itemType, meta] = entry;
if (meta.dojoCapacityCost === undefined) {
throw new Error(`unknown deco type: ${deco.Type}`); throw new Error(`unknown deco type: ${deco.Type}`);
} }
component.DecoCapacity -= meta.dojoCapacityCost; component.DecoCapacity -= meta.dojoCapacityCost;
@ -75,7 +79,13 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
if (meta) { if (meta) {
processDojoBuildMaterialsGathered(guild, meta); processDojoBuildMaterialsGathered(guild, meta);
} }
} else if (guild.AutoContributeFromVault && guild.VaultRegularCredits && guild.VaultMiscItems) { } else if (
deco.Type.startsWith("/Lotus/Objects/Tenno/Dojo/NpcPlaceables/") ||
(guild.AutoContributeFromVault && guild.VaultRegularCredits && guild.VaultMiscItems)
) {
if (!guild.VaultRegularCredits || !guild.VaultMiscItems) {
throw new Error(`dojo visitor placed without anything in vault?!`);
}
if (guild.VaultRegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) { if (guild.VaultRegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) {
let enoughMiscItems = true; let enoughMiscItems = true;
for (const ingredient of meta.ingredients) { for (const ingredient of meta.ingredients) {

View File

@ -19,6 +19,7 @@ import { sendWsBroadcastTo } from "@/src/services/webService";
export const sellController: RequestHandler = async (req, res) => { export const sellController: RequestHandler = async (req, res) => {
const payload = JSON.parse(String(req.body)) as ISellRequest; const payload = JSON.parse(String(req.body)) as ISellRequest;
//console.log(JSON.stringify(payload, null, 2));
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requiredFields = new Set<keyof TInventoryDatabaseDocument>(); const requiredFields = new Set<keyof TInventoryDatabaseDocument>();
if (payload.SellCurrency == "SC_RegularCredits") { if (payload.SellCurrency == "SC_RegularCredits") {
@ -184,6 +185,11 @@ export const sellController: RequestHandler = async (req, res) => {
inventory.Drones.pull({ _id: sellItem.String }); inventory.Drones.pull({ _id: sellItem.String });
}); });
} }
if (payload.Items.KubrowPetPrints) {
payload.Items.KubrowPetPrints.forEach(sellItem => {
inventory.KubrowPetPrints.pull({ _id: sellItem.String });
});
}
if (payload.Items.CrewMembers) { if (payload.Items.CrewMembers) {
payload.Items.CrewMembers.forEach(sellItem => { payload.Items.CrewMembers.forEach(sellItem => {
inventory.CrewMembers.pull({ _id: sellItem.String }); inventory.CrewMembers.pull({ _id: sellItem.String });
@ -312,6 +318,7 @@ interface ISellRequest {
OperatorAmps?: ISellItem[]; OperatorAmps?: ISellItem[];
Hoverboards?: ISellItem[]; Hoverboards?: ISellItem[];
Drones?: ISellItem[]; Drones?: ISellItem[];
KubrowPetPrints?: ISellItem[];
CrewMembers?: ISellItem[]; CrewMembers?: ISellItem[];
CrewShipWeapons?: ISellItem[]; CrewShipWeapons?: ISellItem[];
CrewShipWeaponSkins?: ISellItem[]; CrewShipWeaponSkins?: ISellItem[];

View File

@ -6,6 +6,7 @@ import { logger } from "@/src/utils/logger";
import { addMiscItems, combineInventoryChanges } from "@/src/services/inventoryService"; import { addMiscItems, combineInventoryChanges } from "@/src/services/inventoryService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService"; import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IInventoryChanges } from "../types/purchaseTypes"; import { IInventoryChanges } from "../types/purchaseTypes";
import { config } from "../services/configService";
export const crackRelic = async ( export const crackRelic = async (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
@ -35,7 +36,13 @@ export const crackRelic = async (
// Give reward // Give reward
combineInventoryChanges( combineInventoryChanges(
inventoryChanges, inventoryChanges,
(await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount)).InventoryChanges (
await handleStoreItemAcquisition(
reward.type,
inventory,
reward.itemCount * (config.relicRewardItemCountMultiplier ?? 1)
)
).InventoryChanges
); );
return reward; return reward;

View File

@ -23,7 +23,9 @@ export interface IMessageDatabase extends IMessage {
export interface IMessage { export interface IMessage {
sndr: string; sndr: string;
msg: string; msg: string;
cinematic?: string;
sub: string; sub: string;
customData?: string;
icon?: string; icon?: string;
highPriority?: boolean; highPriority?: boolean;
lowPrioNewPlayers?: boolean; lowPrioNewPlayers?: boolean;
@ -102,7 +104,9 @@ const messageSchema = new Schema<IMessageDatabase>(
ownerId: Schema.Types.ObjectId, ownerId: Schema.Types.ObjectId,
sndr: String, sndr: String,
msg: String, msg: String,
cinematic: String,
sub: String, sub: String,
customData: String,
icon: String, icon: String,
highPriority: Boolean, highPriority: Boolean,
lowPrioNewPlayers: Boolean, lowPrioNewPlayers: Boolean,

View File

@ -67,6 +67,7 @@ export interface IConfig {
unlockAllSimarisResearchEntries?: boolean; unlockAllSimarisResearchEntries?: boolean;
disableDailyTribute?: boolean; disableDailyTribute?: boolean;
spoofMasteryRank?: number; spoofMasteryRank?: number;
relicRewardItemCountMultiplier?: number;
nightwaveStandingMultiplier?: number; nightwaveStandingMultiplier?: number;
unfaithfulBugFixes?: { unfaithfulBugFixes?: {
ignore1999LastRegionPlayed?: boolean; ignore1999LastRegionPlayed?: boolean;

View File

@ -550,6 +550,19 @@ export const processFundedGuildTechProject = (
guild.XP += recipe.guildXpValue; guild.XP += recipe.guildXpValue;
} }
setGuildTechLogState(guild, techProject.ItemType, config.noDojoResearchTime ? 4 : 3, techProject.CompletionDate); setGuildTechLogState(guild, techProject.ItemType, config.noDojoResearchTime ? 4 : 3, techProject.CompletionDate);
if (config.noDojoResearchTime) {
processCompletedGuildTechProject(guild, techProject.ItemType);
}
};
export const processCompletedGuildTechProject = (guild: TGuildDatabaseDocument, type: string): void => {
if (type.startsWith("/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/")) {
guild.VaultDecoRecipes ??= [];
guild.VaultDecoRecipes.push({
ItemType: type,
ItemCount: 1
});
}
}; };
export const setGuildTechLogState = ( export const setGuildTechLogState = (

View File

@ -83,7 +83,7 @@ import { addQuestKey, completeQuest } from "@/src/services/questService";
import { handleBundleAcqusition } from "./purchaseService"; import { handleBundleAcqusition } from "./purchaseService";
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService"; import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
import { createMessage } from "./inboxService"; import { createMessage, IMessageCreationTemplate } from "./inboxService";
import { getMaxStanding, getMinStanding } from "@/src/helpers/syndicateStandingHelper"; import { getMaxStanding, getMinStanding } from "@/src/helpers/syndicateStandingHelper";
import { getNightwaveSyndicateTag, getWorldState } from "./worldStateService"; import { getNightwaveSyndicateTag, getWorldState } from "./worldStateService";
import { ICalendarSeason } from "@/src/types/worldStateTypes"; import { ICalendarSeason } from "@/src/types/worldStateTypes";
@ -1563,7 +1563,22 @@ export const addEmailItem = async (
const meta = ExportEmailItems[typeName]; const meta = ExportEmailItems[typeName];
const emailItem = inventory.EmailItems.find(x => x.ItemType == typeName); const emailItem = inventory.EmailItems.find(x => x.ItemType == typeName);
if (!emailItem || !meta.sendOnlyOnce) { if (!emailItem || !meta.sendOnlyOnce) {
await createMessage(inventory.accountOwnerId, [convertInboxMessage(meta.message)]); const msg: IMessageCreationTemplate = convertInboxMessage(meta.message);
if (msg.cinematic == "/Lotus/Levels/1999/PlayerHomeBalconyCinematics.level") {
msg.customData = JSON.stringify({
Tag: msg.customData + "KissCin",
CinLoadout: {
Skins: inventory.AdultOperatorLoadOuts[0].Skins,
Upgrades: inventory.AdultOperatorLoadOuts[0].Upgrades,
attcol: inventory.AdultOperatorLoadOuts[0].attcol,
cloth: inventory.AdultOperatorLoadOuts[0].cloth,
eyecol: inventory.AdultOperatorLoadOuts[0].eyecol,
pricol: inventory.AdultOperatorLoadOuts[0].pricol,
syancol: inventory.AdultOperatorLoadOuts[0].syancol
}
});
}
await createMessage(inventory.accountOwnerId, [msg]);
if (emailItem) { if (emailItem) {
emailItem.ItemCount += 1; emailItem.ItemCount += 1;

View File

@ -251,7 +251,9 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
return { return {
sndr: message.sender, sndr: message.sender,
msg: message.body, msg: message.body,
cinematic: message.cinematic,
sub: message.title, sub: message.title,
customData: message.customData,
att: message.attachments.length > 0 ? message.attachments : undefined, att: message.attachments.length > 0 ? message.attachments : undefined,
countedAtt: message.countedAttachments.length > 0 ? message.countedAttachments : undefined, countedAtt: message.countedAttachments.length > 0 ? message.countedAttachments : undefined,
icon: message.icon ?? "", icon: message.icon ?? "",

View File

@ -167,8 +167,13 @@ export const handleInventoryItemConfigChange = async (
inventory.LotusCustomization = equipmentChanges.LotusCustomization; inventory.LotusCustomization = equipmentChanges.LotusCustomization;
break; break;
} }
case "ValidNewLoadoutId": {
logger.debug(`ignoring ValidNewLoadoutId (${equipmentChanges.ValidNewLoadoutId})`);
// seems always equal to the id of loadout config NORMAL[0], likely has no purpose and we're free to ignore it
break;
}
default: { default: {
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") { if (equipmentKeys.includes(equipmentName as TEquipmentKey)) {
logger.debug(`general Item config saved of type ${equipmentName}`, { logger.debug(`general Item config saved of type ${equipmentName}`, {
config: equipment config: equipment
}); });
@ -216,7 +221,7 @@ export const handleInventoryItemConfigChange = async (
} }
break; break;
} else { } else {
logger.warn(`loadout category not implemented, changes may be lost: ${equipmentName}`, { logger.error(`loadout category not implemented, changes will be lost: ${equipmentName}`, {
config: equipment config: equipment
}); });
} }

View File

@ -64,8 +64,12 @@ export const handleSetShipDecorations = async (
throw new Error(`unknown room: ${placedDecoration.Room}`); throw new Error(`unknown room: ${placedDecoration.Room}`);
} }
const [itemType, meta] = Object.entries(ExportResources).find(arr => arr[1].deco == placedDecoration.Type)!; const entry = Object.entries(ExportResources).find(arr => arr[1].deco == placedDecoration.Type);
if (!itemType || meta.capacityCost === undefined) { if (!entry) {
throw new Error(`unknown deco type: ${placedDecoration.Type}`);
}
const [itemType, meta] = entry;
if (meta.capacityCost === undefined) {
throw new Error(`unknown deco type: ${placedDecoration.Type}`); throw new Error(`unknown deco type: ${placedDecoration.Type}`);
} }

View File

@ -92,5 +92,13 @@
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophySilverRecipe", "/Lotus/Levels/ClanDojo/ComponentPropRecipes/ThumperTrophySilverRecipe",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/CorpusPlaceables/GasTurbineConeRecipe", "/Lotus/Levels/ClanDojo/ComponentPropRecipes/CorpusPlaceables/GasTurbineConeRecipe",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NaturalPlaceables/CoralChunkARecipe", "/Lotus/Levels/ClanDojo/ComponentPropRecipes/NaturalPlaceables/CoralChunkARecipe",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoPlaceables/TnoBeaconEmitterRecipe" "/Lotus/Levels/ClanDojo/ComponentPropRecipes/TennoPlaceables/TnoBeaconEmitterRecipe",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronFemaleSitting",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronFemaleStanding",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronMaleStanding",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/OstronMaleStandingTwo",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisForeman",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisHazard",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisStrikerOne",
"/Lotus/Levels/ClanDojo/ComponentPropRecipes/NpcPlaceables/SolarisStrikerThree"
] ]

View File

@ -546,6 +546,7 @@
</div> </div>
</div> </div>
<div data-route="/webui/quests" data-title="Quests | OpenWF WebUI"> <div data-route="/webui/quests" data-title="Quests | OpenWF WebUI">
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
@ -788,14 +789,21 @@
<form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;"> <form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label> <label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<div class="input-group"> <div class="input-group">
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" /> <input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" data-default="-1" />
<button class="btn btn-primary" 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-primary" type="submit" data-loc="cheats_save"></button> <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div> </div>
</form> </form>
<form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;"> <form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;">
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label> <label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
<div class="input-group"> <div class="input-group">
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" value="1" /> <input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" data-default="1" />
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button> <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div> </div>
</form> </form>
@ -919,7 +927,7 @@
<form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;"> <form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;">
<label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_darvoStockMultiplier"></label> <label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_darvoStockMultiplier"></label>
<div class="input-group"> <div class="input-group">
<input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" placeholder="1" /> <input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" data-default="1" />
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button> <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div> </div>
</form> </form>

View File

@ -1984,16 +1984,14 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
$(".config-admin-show").removeClass("d-none"); $(".config-admin-show").removeClass("d-none");
Object.entries(json).forEach(entry => { Object.entries(json).forEach(entry => {
const [key, value] = entry; const [key, value] = entry;
var x = document.getElementById(`${key}`); const elm = document.getElementById(key);
if (x != null) { if (elm.type == "checkbox") {
if (x.type == "checkbox") { elm.checked = value;
x.checked = value; } else if (elm.classList.contains("tags-input")) {
} else if (x.classList.contains("tags-input")) { elm.value = value.join(", ");
x.value = value.join(", "); elm.oninput();
x.oninput(); } else {
} else { elm.value = value ?? elm.getAttribute("data-default");
x.value = value;
}
} }
}); });
}) })

View File

@ -185,6 +185,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`, cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`, cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`,
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
cheats_save: `[UNTRANSLATED] Save`, cheats_save: `[UNTRANSLATED] Save`,
cheats_account: `Account`, cheats_account: `Account`,

View File

@ -184,6 +184,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `Disable Daily Tribute`, cheats_disableDailyTribute: `Disable Daily Tribute`,
cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`, cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
cheats_relicRewardItemCountMultiplier: `Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`,
cheats_save: `Save`, cheats_save: `Save`,
cheats_account: `Account`, cheats_account: `Account`,

View File

@ -185,6 +185,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`, cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`,
cheats_disableDailyTribute: `Desactivar tributo diario`, cheats_disableDailyTribute: `Desactivar tributo diario`,
cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`, cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`, cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
cheats_save: `Guardar`, cheats_save: `Guardar`,
cheats_account: `Cuenta`, cheats_account: `Cuenta`,

View File

@ -185,6 +185,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`, cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`, cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`, cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`, cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`,
cheats_save: `Sauvegarder`, cheats_save: `Sauvegarder`,
cheats_account: `Compte`, cheats_account: `Compte`,

View File

@ -185,6 +185,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`, cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`, cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`, cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`,
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`, cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
cheats_save: `[UNTRANSLATED] Save`, cheats_save: `[UNTRANSLATED] Save`,
cheats_account: `Аккаунт`, cheats_account: `Аккаунт`,

View File

@ -185,6 +185,7 @@ dict = {
cheats_unlockAllSimarisResearchEntries: `解锁所有Simaris研究条目`, cheats_unlockAllSimarisResearchEntries: `解锁所有Simaris研究条目`,
cheats_disableDailyTribute: `禁用每日登录奖励`, cheats_disableDailyTribute: `禁用每日登录奖励`,
cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`, cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`,
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`, cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`,
cheats_save: `保存`, cheats_save: `保存`,
cheats_account: `账户`, cheats_account: `账户`,