Compare commits

...

9 Commits

Author SHA1 Message Date
b21bca7a6d fix: AffiliationChanges disapears from EOM screen when bounty stage is completed (#2560)
Reviewed-on: OpenWF/SpaceNinjaServer#2560
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-07-29 00:31:48 -07:00
d30d450311 chore: add rewards for NewbieJob (#2559)
Closes #2536

Reviewed-on: OpenWF/SpaceNinjaServer#2559
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-07-29 00:31:37 -07:00
b62e326920 feat(webui): ability overrides (#2558)
Closes #851

Reviewed-on: OpenWF/SpaceNinjaServer#2558
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-07-29 00:31:29 -07:00
8b4bc114f6 chore: add logging for bounty medallion rewards (#2557)
Reviewed-on: OpenWF/SpaceNinjaServer#2557
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-07-29 00:31:19 -07:00
564aa06762 fix: correctly apply riven cipher (#2554)
The completeRandomModChallenge endpoint is only supposed to complete the challenge, what a shocker. Because we directly set a unveiled fingerprint, the game was not showing the expected UI.

Reviewed-on: OpenWF/SpaceNinjaServer#2554
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-07-27 06:29:12 -07:00
2e84f71af8 chore: faithful handling when ki'teer signa was rolled (#2553)
Reviewed-on: OpenWF/SpaceNinjaServer#2553
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-07-27 06:29:02 -07:00
ddfa98e0b2 chore: update baro.json (#2550)
Co-authored-by: BanLanGen <banlangen@noreply.localhost>
Reviewed-on: OpenWF/SpaceNinjaServer#2550
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-07-27 06:28:51 -07:00
bb3c3e01b0 chore: add GEAR loadout slot (#2545)
added missing GEAR loadout slot (was causing issues with saving loadout in U29)

Reviewed-on: OpenWF/SpaceNinjaServer#2545
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: azdful <mischzaripov@yandex.ru>
Co-committed-by: azdful <mischzaripov@yandex.ru>
2025-07-25 01:51:13 -07:00
695dcf98e0 chore: handle sale of fusion treasures (#2542)
Closes #2541

Reviewed-on: OpenWF/SpaceNinjaServer#2542
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-07-24 05:30:55 -07:00
21 changed files with 300 additions and 23 deletions

View File

@ -4,8 +4,7 @@ import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inven
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { createUnveiledRivenFingerprint } from "@/src/helpers/rivenHelper";
import { ExportUpgrades } from "warframe-public-export-plus";
import { IVeiledRivenFingerprint } from "@/src/helpers/rivenHelper";
export const completeRandomModChallengeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -27,10 +26,11 @@ export const completeRandomModChallengeController: RequestHandler = async (req,
inventoryChanges.MiscItems = miscItemChanges;
}
// Update riven fingerprint to a randomised unveiled state
// Complete the riven challenge
const upgrade = inventory.Upgrades.id(request.ItemId)!;
const meta = ExportUpgrades[upgrade.ItemType];
upgrade.UpgradeFingerprint = JSON.stringify(createUnveiledRivenFingerprint(meta));
const fp = JSON.parse(upgrade.UpgradeFingerprint!) as IVeiledRivenFingerprint;
fp.challenge.Progress = fp.challenge.Required;
upgrade.UpgradeFingerprint = JSON.stringify(fp);
await inventory.save();

View File

@ -2,22 +2,14 @@ import { RequestHandler } from "express";
import { ExportResources } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addFusionTreasures, addMiscItems, getInventory } from "@/src/services/inventoryService";
import { IFusionTreasure, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
interface IFusionTreasureRequest {
oldTreasureName: string;
newTreasureName: string;
}
const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
const arr = name.split("_");
return {
ItemType: arr[0],
Sockets: parseInt(arr[1], 16),
ItemCount: count
};
};
export const fusionTreasuresController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);

View File

@ -10,13 +10,15 @@ import {
combineInventoryChanges,
addCrewShipRawSalvage,
addFusionPoints,
addCrewShipFusionPoints
addCrewShipFusionPoints,
addFusionTreasures
} from "@/src/services/inventoryService";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportDojoRecipes } from "warframe-public-export-plus";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { sendWsBroadcastEx } from "@/src/services/wsService";
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
export const sellController: RequestHandler = async (req, res) => {
const payload = JSON.parse(String(req.body)) as ISellRequest;
@ -295,6 +297,11 @@ export const sellController: RequestHandler = async (req, res) => {
]);
});
}
if (payload.Items.FusionTreasures) {
payload.Items.FusionTreasures.forEach(sellItem => {
addFusionTreasures(inventory, [parseFusionTreasure(sellItem.String, sellItem.Count * -1)]);
});
}
await inventory.save();
res.json({
@ -327,6 +334,7 @@ interface ISellRequest {
CrewMembers?: ISellItem[];
CrewShipWeapons?: ISellItem[];
CrewShipWeaponSkins?: ISellItem[];
FusionTreasures?: ISellItem[];
};
SellPrice: number;
SellCurrency:

View File

@ -0,0 +1,33 @@
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
export const abilityOverrideController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const request = req.body as IAbilityOverrideRequest;
if (request.category === "Suits") {
const inventory = await getInventory(accountId, request.category);
const item = inventory[request.category].id(request.oid);
if (item) {
if (request.action == "set") {
item.Configs[request.configIndex].AbilityOverride = request.AbilityOverride;
} else {
item.Configs[request.configIndex].AbilityOverride = undefined;
}
await inventory.save();
}
}
res.end();
};
interface IAbilityOverrideRequest {
category: TEquipmentKey;
oid: string;
action: "set" | "remove";
configIndex: number;
AbilityOverride: {
Ability: string;
Index: number;
};
}

View File

@ -1,6 +1,7 @@
import { RequestHandler } from "express";
import { getDict, getItemName, getString } from "@/src/services/itemDataService";
import {
ExportAbilities,
ExportArcanes,
ExportAvionics,
ExportBoosters,
@ -57,6 +58,7 @@ interface ItemLists {
mods: ListedItem[];
Boosters: ListedItem[];
VarziaOffers: ListedItem[];
Abilities: ListedItem[];
//circuitGameModes: ListedItem[];
}
@ -94,7 +96,8 @@ const getItemListsController: RequestHandler = (req, response) => {
EvolutionProgress: [],
mods: [],
Boosters: [],
VarziaOffers: []
VarziaOffers: [],
Abilities: []
/*circuitGameModes: [
{
uniqueName: "Survival",
@ -132,6 +135,12 @@ const getItemListsController: RequestHandler = (req, response) => {
name: getString(item.name, lang),
exalted: item.exalted
});
item.abilities.forEach(ability => {
res.Abilities.push({
uniqueName: ability.uniqueName,
name: getString(ability.name || uniqueName, lang)
});
});
}
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
@ -348,6 +357,13 @@ const getItemListsController: RequestHandler = (req, response) => {
});
}
for (const [uniqueName, ability] of Object.entries(ExportAbilities)) {
res.Abilities.push({
uniqueName,
name: getString(ability.name || uniqueName, lang)
});
}
response.json(res);
};

View File

@ -1,6 +1,7 @@
import { IMongoDate, IOid, IOidWithLegacySupport } from "@/src/types/commonTypes";
import { Types } from "mongoose";
import { TRarity } from "warframe-public-export-plus";
import { IFusionTreasure } from "@/src/types/inventoryTypes/inventoryTypes";
export const version_compare = (a: string, b: string): number => {
const a_digits = a
@ -51,6 +52,15 @@ export const fromMongoDate = (date: IMongoDate): Date => {
return new Date(parseInt(date.$date.$numberLong));
};
export const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
const arr = name.split("_");
return {
ItemType: arr[0],
Sockets: parseInt(arr[1], 16),
ItemCount: count
};
};
export type TTraitsPool = Record<
"Colors" | "EyeColors" | "FurPatterns" | "BodyTypes" | "Heads" | "Tails",
{ type: string; rarity: TRarity }[]

View File

@ -62,6 +62,7 @@ export const loadoutSchema = new Schema<ILoadoutDatabase, loadoutModelType>({
NORMAL_PVP: [loadoutConfigSchema],
LUNARO: [loadoutConfigSchema],
OPERATOR: [loadoutConfigSchema],
GEAR: [loadoutConfigSchema],
KDRIVE: [loadoutConfigSchema],
DATAKNIFE: [loadoutConfigSchema],
MECH: [loadoutConfigSchema],
@ -88,6 +89,7 @@ type loadoutDocumentProps = {
NORMAL_PVP: Types.DocumentArray<ILoadoutConfigDatabase>;
LUNARO: Types.DocumentArray<ILoadoutConfigDatabase>;
OPERATOR: Types.DocumentArray<ILoadoutConfigDatabase>;
GEAR: Types.DocumentArray<ILoadoutConfigDatabase>;
KDRIVE: Types.DocumentArray<ILoadoutConfigDatabase>;
DATAKNIFE: Types.DocumentArray<ILoadoutConfigDatabase>;
MECH: Types.DocumentArray<ILoadoutConfigDatabase>;

View File

@ -15,6 +15,7 @@ import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webu
import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
import { addMissingHelminthBlueprintsController } from "@/src/controllers/custom/addMissingHelminthBlueprintsController";
import { abilityOverrideController } from "@/src/controllers/custom/abilityOverrideController";
import { createAccountController } from "@/src/controllers/custom/createAccountController";
import { createMessageController } from "@/src/controllers/custom/createMessageController";
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
@ -47,6 +48,7 @@ customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
customRouter.get("/completeAllMissions", completeAllMissionsController);
customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
customRouter.post("/abilityOverride", abilityOverrideController);
customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController);
customRouter.post("/addCurrency", addCurrencyController);

View File

@ -422,6 +422,7 @@ export const importLoadOutPresets = (db: ILoadoutDatabase, client: ILoadOutPrese
db.NORMAL_PVP = client.NORMAL_PVP.map(convertLoadOutConfig);
db.LUNARO = client.LUNARO.map(convertLoadOutConfig);
db.OPERATOR = client.OPERATOR.map(convertLoadOutConfig);
db.GEAR = client.GEAR.map(convertLoadOutConfig);
db.KDRIVE = client.KDRIVE.map(convertLoadOutConfig);
db.DATAKNIFE = client.DATAKNIFE.map(convertLoadOutConfig);
db.MECH = client.MECH.map(convertLoadOutConfig);

View File

@ -972,7 +972,8 @@ export const addMissionRewards = async (
Missions: missions,
RegularCredits: creditDrops,
VoidTearParticipantsCurrWave: voidTearWave,
StrippedItems: strippedItems
StrippedItems: strippedItems,
AffiliationChanges: AffiliationMods
}: IMissionInventoryUpdateRequest,
firstCompletion: boolean
): Promise<AddMissionRewardsReturnType> => {
@ -992,7 +993,6 @@ export const addMissionRewards = async (
);
logger.debug("random mission drops:", MissionRewards);
const inventoryChanges: IInventoryChanges = {};
const AffiliationMods: IAffiliationMods[] = [];
let SyndicateXPItemReward;
let ConquestCompletedMissionsCount;
@ -1302,6 +1302,9 @@ export const addMissionRewards = async (
ItemCount: medallionAmount
});
SyndicateXPItemReward = medallionAmount;
logger.debug(
`Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
);
} else {
if (rewardInfo.JobTier! >= 0) {
addStanding(
@ -1331,6 +1334,9 @@ export const addMissionRewards = async (
}
}
}
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
addStanding(inventory, "CetusSyndicate", Math.floor(200 / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
}
}
if (rewardInfo.challengeMissionId) {
@ -1347,6 +1353,7 @@ export const addMissionRewards = async (
ItemCount: medallionAmount
});
SyndicateXPItemReward = medallionAmount;
logger.debug(`Giving ${medallionAmount} medallions for the ${tier} tier bounty`);
} else {
let standingAmount = (tier + 1) * 1000;
if (tier > 5) standingAmount = 7500; // InfestedLichBounty
@ -1772,6 +1779,11 @@ function getRandomMissionDrops(
}
}
}
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
rewardManifests = ["/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableARewards"];
rotations = [3];
if (RewardInfo.Q) rotations.push(3);
}
}
} else if (RewardInfo.challengeMissionId) {
const rewardTables: Record<string, string[]> = {

View File

@ -581,7 +581,7 @@ const handleBoosterPackPurchase = async (
purchaseResponse.InventoryChanges,
await addItem(inventory, specialItemReward.Item)
);
// TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
atten.Atten = 0;
} else {
atten.Atten += specialItemReward.PityIncreaseRate!;
}

View File

@ -79,6 +79,7 @@ export interface ILoadoutDatabase {
NORMAL_PVP: ILoadoutConfigDatabase[];
LUNARO: ILoadoutConfigDatabase[];
OPERATOR: ILoadoutConfigDatabase[];
GEAR: ILoadoutConfigDatabase[];
KDRIVE: ILoadoutConfigDatabase[];
DATAKNIFE: ILoadoutConfigDatabase[];
MECH: ILoadoutConfigDatabase[];

View File

@ -304,7 +304,6 @@
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/AvatarImageGlyphCookieKubrow", "PrimePrice": 80, "RegularPrice": 50000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/LisetScarf", "PrimePrice": 600, "RegularPrice": 400000 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/SuitCustomizations/ColourPickerTwitchBItemA", "PrimePrice": 220, "RegularPrice": 220000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/BaroKavatBadgeItem", "PrimePrice": 50, "RegularPrice": 50000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/BaroKavatSigil", "PrimePrice": 55, "RegularPrice": 45000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/WraithTurbinesScarf", "PrimePrice": 400, "RegularPrice": 500000 },
@ -363,7 +362,6 @@
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileInarosTomb", "PrimePrice": 325, "RegularPrice": 175000 },
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/BaroFireWorksCrate", "PrimePrice": 50, "RegularPrice": 100000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileOrokinExtraction", "PrimePrice": 325, "RegularPrice": 175000 },
{ "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBait", "PrimePrice": 200, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBaitB", "PrimePrice": 200, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationB", "PrimePrice": 100, "RegularPrice": 100000 },
@ -401,7 +399,39 @@
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationC", "PrimePrice": 100, "RegularPrice": 100000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/PedistalPrime", "PrimePrice": 0, "RegularPrice": 1000000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/Emotes/BaroEmote", "PrimePrice": 0, "RegularPrice": 1000000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 }
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageAvaClemCommunityGlyph", "PrimePrice": 20, "RegularPrice": 33333 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TennoconConcert2025Display", "PrimePrice": 90, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/SummerGameFestPoster", "PrimePrice": 90, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/RathuumEventPoster", "PrimePrice": 90, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionCorpus", "PrimePrice": 70, "RegularPrice": 55000 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionEntrati", "PrimePrice": 99, "RegularPrice": 1900 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionScaldra", "PrimePrice": 93, "RegularPrice": 1906 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionTechrot", "PrimePrice": 98, "RegularPrice": 1901 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Warframes/VorunaActionGlyph", "PrimePrice": 75, "RegularPrice": 60000 },
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageVoidAngelBaro", "PrimePrice": 80, "RegularPrice": 50000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/1999DrippySigil", "PrimePrice": 50, "RegularPrice": 45000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Weapons/Rapier/CrpRapierSkin", "PrimePrice": 375, "RegularPrice": 400000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Events/OgrisOldSchool", "PrimePrice": 350, "RegularPrice": 325000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrismaLotusFlamesSigil", "PrimePrice": 55, "RegularPrice": 60000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/TenthAnniversaryLoginSongItem", "PrimePrice": 145, "RegularPrice": 165000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/AbyssofDagathSongItem", "PrimePrice": 150, "RegularPrice": 155000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/ZarimanLoginSongItem", "PrimePrice": 160, "RegularPrice": 180000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DanteUnboundLoginSongItem", "PrimePrice": 150, "RegularPrice": 150000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/EmpyreanSongItem", "PrimePrice": 160, "RegularPrice": 155000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DeimosLoginSongItem", "PrimePrice": 155, "RegularPrice": 160000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/JadeShadowsLoginSongItem", "PrimePrice": 150, "RegularPrice": 170000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/WhispersInTheWallLoginSongItem", "PrimePrice": 165, "RegularPrice": 170000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/CorpusRailjackLoginSongItem", "PrimePrice": 150, "RegularPrice": 165000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/LotusEatersSongItem", "PrimePrice": 165, "RegularPrice": 150000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/KuvaLichLoginSongItem", "PrimePrice": 140, "RegularPrice": 170000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyBaro", "PrimePrice": 100, "RegularPrice": 125000 },
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyInaros", "PrimePrice": 120, "RegularPrice": 90000 },
{ "ItemType": "/Lotus/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 }
],
"allIfAny": [
[

View File

@ -460,6 +460,13 @@
<h3 id="detailedView-loading" class="mb-0" data-loc="general_loading"></h3>
<h3 id="detailedView-title" class="mb-0"></h3>
<p class="text-body-secondary"></p>
<div id="loadout-card" class="card mb-3 d-none">
<h5 class="card-header" data-loc="detailedView_loadoutLabel"></h5>
<div class="card-body">
<ul class="nav nav-tabs" id="loadoutTabs"></ul>
<div class="tab-content mt-3" id="loadoutTabsContent"></div>
</div>
</div>
<div id="archonShards-card" class="card mb-3 d-none">
<h5 class="card-header" data-loc="detailedView_archonShardsLabel"></h5>
<div class="card-body">
@ -1073,6 +1080,7 @@
<datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
<datalist id="datalist-Boosters"></datalist>
<datalist id="datalist-Abilities"></datalist>
<datalist id="datalist-circuitGameModes">
<option>Survival</option>
<option>VoidFlood</option>

View File

@ -1241,6 +1241,117 @@ function updateInventory() {
document.getElementById("dv-invigoration-offensive").value = OffensiveUpgrade;
document.getElementById("dv-invigoration-defensive").value = DefensiveUpgrade;
document.getElementById("dv-invigoration-expiry").value = UpgradesExpiry;
{
document.getElementById("loadout-card").classList.remove("d-none");
const maxModConfigNum = Math.min(2 + (item.ModSlotPurchases ?? 0), 5);
const configs = item.Configs ?? [];
const loadoutTabs = document.getElementById("loadoutTabs");
const loadoutTabsContent = document.getElementById("loadoutTabsContent");
loadoutTabs.innerHTML = "";
loadoutTabsContent.innerHTML = "";
for (let i = 0; i <= maxModConfigNum; i++) {
const config = configs[i] ?? {};
{
const li = document.createElement("li");
li.classList.add("nav-item");
const button = document.createElement("button");
button.classList.add("nav-link");
if (i === 0) button.classList.add("active");
button.id = `config${i}-tab`;
button.setAttribute("data-bs-toggle", "tab");
button.setAttribute("data-bs-target", `#config${i}`);
button.innerHTML = config.Name?.trim() || String.fromCharCode(65 + i);
li.appendChild(button);
loadoutTabs.appendChild(li);
}
{
const tabDiv = document.createElement("div");
tabDiv.classList = "tab-pane";
if (i === 0) tabDiv.classList.add("show", "active");
tabDiv.id = `config${i}`;
{
const abilityOverrideForm = document.createElement("form");
abilityOverrideForm.classList = "form-group mt-2";
abilityOverrideForm.setAttribute(
"onsubmit",
`handleAbilityOverride(event, ${i});return false;`
);
const abilityOverrideFormLabel = document.createElement("label");
abilityOverrideFormLabel.setAttribute("data-loc", "abilityOverride_label");
abilityOverrideFormLabel.innerHTML = loc("abilityOverride_label");
abilityOverrideFormLabel.classList = "form-label";
abilityOverrideFormLabel.setAttribute("for", "abilityOverride-ability");
abilityOverrideForm.appendChild(abilityOverrideFormLabel);
const abilityOverrideInputGroup = document.createElement("div");
abilityOverrideInputGroup.classList = "input-group";
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
const abilityOverrideInput = document.createElement("input");
abilityOverrideInput.id = "abilityOverride-ability";
abilityOverrideInput.classList = "form-control";
abilityOverrideInput.setAttribute("list", "datalist-Abilities");
if (config.AbilityOverride) {
const datalist = document.getElementById("datalist-Abilities");
const options = Array.from(datalist.options);
abilityOverrideInput.value = options.find(
option =>
config.AbilityOverride.Ability == option.getAttribute("data-key")
).value;
}
abilityOverrideInputGroup.appendChild(abilityOverrideInput);
const abilityOverrideOnSlot = document.createElement("span");
abilityOverrideOnSlot.classList = "input-group-text";
abilityOverrideOnSlot.setAttribute("data-loc", "abilityOverride_onSlot");
abilityOverrideOnSlot.innerHTML = loc("abilityOverride_onSlot");
abilityOverrideInputGroup.appendChild(abilityOverrideOnSlot);
const abilityOverrideSecondInput = document.createElement("input");
abilityOverrideSecondInput.id = "abilityOverride-ability-index";
abilityOverrideSecondInput.classList = "form-control";
abilityOverrideSecondInput.setAttribute("type", "number");
abilityOverrideSecondInput.setAttribute("min", "0");
abilityOverrideSecondInput.setAttribute("max", "3");
if (config.AbilityOverride)
abilityOverrideSecondInput.value = config.AbilityOverride.Index;
abilityOverrideInputGroup.appendChild(abilityOverrideSecondInput);
const abilityOverrideSetButton = document.createElement("button");
abilityOverrideSetButton.classList = "btn btn-primary";
abilityOverrideSetButton.setAttribute("type", "submit");
abilityOverrideSetButton.setAttribute("value", "set");
abilityOverrideSetButton.setAttribute("data-loc", "general_setButton");
abilityOverrideSetButton.innerHTML = loc("general_setButton");
abilityOverrideInputGroup.appendChild(abilityOverrideSetButton);
const abilityOverrideRemoveButton = document.createElement("button");
abilityOverrideRemoveButton.classList = "btn btn-danger";
abilityOverrideRemoveButton.setAttribute("type", "submit");
abilityOverrideRemoveButton.setAttribute("value", "remove");
abilityOverrideRemoveButton.setAttribute("data-loc", "code_remove");
abilityOverrideRemoveButton.innerHTML = loc("code_remove");
abilityOverrideInputGroup.appendChild(abilityOverrideRemoveButton);
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
tabDiv.appendChild(abilityOverrideForm);
}
loadoutTabsContent.appendChild(tabDiv);
}
}
}
} else if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
document.getElementById("valenceBonus-card").classList.remove("d-none");
document.getElementById("valenceBonus-innateDamage").value = "";
@ -2248,6 +2359,7 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
document.getElementById("detailedView-loading").classList.remove("d-none");
document.getElementById("detailedView-title").textContent = "";
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
document.getElementById("loadout-card").classList.add("d-none");
document.getElementById("archonShards-card").classList.add("d-none");
document.getElementById("edit-suit-invigorations-card").classList.add("d-none");
document.getElementById("modularParts-card").classList.add("d-none");
@ -2963,3 +3075,29 @@ async function editSuitInvigorationUpgrade(oid, data) {
updateInventory();
});
}
function handleAbilityOverride(event, configIndex) {
event.preventDefault();
const urlParams = new URLSearchParams(window.location.search);
const action = event.submitter.value;
const Ability = getKey(document.getElementById("abilityOverride-ability"));
const Index = document.getElementById("abilityOverride-ability-index").value;
revalidateAuthz().then(() => {
$.post({
url: "/custom/abilityOverride?" + window.authz,
contentType: "application/json",
data: JSON.stringify({
category: urlParams.get("productCategory"),
oid: urlParams.get("itemId"),
configIndex,
action,
AbilityOverride: {
Ability,
Index
}
})
}).done(function () {
updateInventory();
});
});
}

View File

@ -128,6 +128,7 @@ dict = {
detailedView_valenceBonusDescription: `Du kannst den Valenz-Bonus deiner Waffe festlegen oder entfernen.`,
detailedView_modularPartsLabel: `Modulare Teile ändern`,
detailedView_suitInvigorationLabel: `Warframe-Kräftigung`,
detailedView_loadoutLabel: `Loadouts`,
invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
invigorations_offensive_AbilityRange: `+100% Fähigkeitsreichweite`,
@ -155,6 +156,9 @@ dict = {
invigorations_defensiveLabel: `Defensives Upgrade`,
invigorations_expiryLabel: `Upgrades Ablaufdatum (optional)`,
abilityOverride_label: `[UNTRANSLATED] Ability Override`,
abilityOverride_onSlot: `[UNTRANSLATED] on slot`,
mods_addRiven: `Riven hinzufügen`,
mods_fingerprint: `Fingerabdruck`,
mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,

View File

@ -127,6 +127,7 @@ dict = {
detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
detailedView_modularPartsLabel: `Change Modular Parts`,
detailedView_suitInvigorationLabel: `Warframe Invigoration`,
detailedView_loadoutLabel: `Loadouts`,
invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
invigorations_offensive_AbilityRange: `+100% Ability Range`,
@ -154,6 +155,9 @@ dict = {
invigorations_defensiveLabel: `Defensive Upgrade`,
invigorations_expiryLabel: `Upgrades Expiry (optional)`,
abilityOverride_label: `Ability Override`,
abilityOverride_onSlot: `on slot`,
mods_addRiven: `Add Riven`,
mods_fingerprint: `Fingerprint`,
mods_fingerprintHelp: `Need help with the fingerprint?`,

View File

@ -128,6 +128,7 @@ dict = {
detailedView_valenceBonusDescription: `Puedes establecer o quitar el bono de valencia de tu arma.`,
detailedView_modularPartsLabel: `Cambiar partes modulares`,
detailedView_suitInvigorationLabel: `Vigorización de Warframe`,
detailedView_loadoutLabel: `Equipamientos`,
invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
invigorations_offensive_AbilityRange: `+100% Alcance de Habilidad`,
@ -155,6 +156,9 @@ dict = {
invigorations_defensiveLabel: `Mejora Defensiva`,
invigorations_expiryLabel: `Caducidad de Mejoras (opcional)`,
abilityOverride_label: `[UNTRANSLATED] Ability Override`,
abilityOverride_onSlot: `[UNTRANSLATED] on slot`,
mods_addRiven: `Agregar Agrietado`,
mods_fingerprint: `Huella digital`,
mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,

View File

@ -128,6 +128,7 @@ dict = {
detailedView_valenceBonusDescription: `Définir le Bonus Valence de l'arme.`,
detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
detailedView_suitInvigorationLabel: `Invigoration de Warframe`,
detailedView_loadoutLabel: `Équipements`,
invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
invigorations_offensive_AbilityRange: `+100% de portée de pouvoir`,
@ -155,6 +156,9 @@ dict = {
invigorations_defensiveLabel: `Amélioration défensive`,
invigorations_expiryLabel: `Expiration de l'invigoration (optionnel)`,
abilityOverride_label: `[UNTRANSLATED] Ability Override`,
abilityOverride_onSlot: `[UNTRANSLATED] on slot`,
mods_addRiven: `Ajouter un riven`,
mods_fingerprint: `Empreinte`,
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,

View File

@ -128,6 +128,7 @@ dict = {
detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус валентности с вашего оружия.`,
detailedView_modularPartsLabel: `Изменить Модульные Части`,
detailedView_suitInvigorationLabel: `[UNTRANSLATED] Warframe Invigoration`,
detailedView_loadoutLabel: `Конфигурации`,
invigorations_offensive_AbilityStrength: `[UNTRANSLATED] +200% Ability Strength`,
invigorations_offensive_AbilityRange: `[UNTRANSLATED] +100% Ability Range`,
@ -155,6 +156,9 @@ dict = {
invigorations_defensiveLabel: `[UNTRANSLATED] Defensive Upgrade`,
invigorations_expiryLabel: `[UNTRANSLATED] Upgrades Expiry (optional)`,
abilityOverride_label: `Переопределение способности`,
abilityOverride_onSlot: `в ячейке`,
mods_addRiven: `Добавить Мод Разлома`,
mods_fingerprint: `Отпечаток`,
mods_fingerprintHelp: `Нужна помощь с отпечатком?`,

View File

@ -128,6 +128,7 @@ dict = {
detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
detailedView_modularPartsLabel: `更换部件`,
detailedView_suitInvigorationLabel: `编辑战甲活化属性`,
detailedView_loadoutLabel: `配置`,
invigorations_offensive_AbilityStrength: `+200%技能强度`,
invigorations_offensive_AbilityRange: `+100%技能范围`,
@ -155,6 +156,9 @@ dict = {
invigorations_defensiveLabel: `功能型属性`,
invigorations_expiryLabel: `活化时效(可选)`,
abilityOverride_label: `[UNTRANSLATED] Ability Override`,
abilityOverride_onSlot: `[UNTRANSLATED] on slot`,
mods_addRiven: `添加裂罅MOD`,
mods_fingerprint: `印记`,
mods_fingerprintHelp: `需要印记相关的帮助?`,