Compare commits

...

10 Commits

Author SHA1 Message Date
f50763ff66 feat(webui): KubrowPets support
also using `/api/modularWeaponCrafting.php` instead of  `/custom/addModularEquipment` for modular equipment
2025-04-20 12:03:36 +02:00
0f3d9f6c2c chore: provide upcoming weekly acts before week rollover (#1736)
The final piece to close #1640

Reviewed-on: OpenWF/SpaceNinjaServer#1736
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:06:49 -07:00
c2a633b549 chore: improve LiteSortie handling at week rollover (#1735)
WorldState now provides the upcoming LiteSortie if relevant and the boss is derived from the sortieId so completing it at rollover should work as expected.

Reviewed-on: OpenWF/SpaceNinjaServer#1735
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:06:38 -07:00
7040d422a2 feat: manage crew members (#1734)
Reviewed-on: OpenWF/SpaceNinjaServer#1734
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:06:20 -07:00
ba1380ec4c feat: rush repair drones (#1733)
Closes #1677

Reviewed-on: OpenWF/SpaceNinjaServer#1733
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:06:07 -07:00
26f37f58e5 chore(webui): make add mods behave more like adding items (#1732)
Reviewed-on: OpenWF/SpaceNinjaServer#1732
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:05:55 -07:00
e59bdcdfbc chore(webui): assume deleting items will always succeed (#1731)
instead of waiting for a response + then refreshing inventory, we can just delete the element right away and hope it works out

Reviewed-on: OpenWF/SpaceNinjaServer#1731
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:05:43 -07:00
c1ca303310 fix: handle mk1 armaments being salvaged (#1730)
Fixes #1729

Reviewed-on: OpenWF/SpaceNinjaServer#1730
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:05:23 -07:00
8afb515231 fix(stats): captures not being tracked for a new enemy (#1728)
Reviewed-on: OpenWF/SpaceNinjaServer#1728
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:04:22 -07:00
5eecf11b1a fix: ignore assassin mission failure if recovery is still pending (#1726)
Closes #1724

Reviewed-on: OpenWF/SpaceNinjaServer#1726
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-04-19 09:04:04 -07:00
24 changed files with 784 additions and 325 deletions

View File

@ -0,0 +1,28 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
import { Types } from "mongoose";
export const crewMembersController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "CrewMembers");
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
dbCrewMember.WeaponConfigIdx = data.crewMember.WeaponConfigIdx;
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
dbCrewMember.Configs = data.crewMember.Configs;
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
await inventory.save();
res.json({
crewMemberId: data.crewMember.ItemId.$oid,
NemesisFingerprint: data.crewMember.NemesisFingerprint
});
};
interface ICrewMembersRequest {
crewMember: ICrewMemberClient;
}

View File

@ -12,6 +12,7 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getRandomInt } from "@/src/services/rngService";
import { IFingerprintStat } from "@/src/helpers/rivenHelper";
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -42,7 +43,9 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
);
} else {
const meta = ExportRailjackWeapons[payload.ItemType];
const upgradeType = meta.defaultUpgrades![0].ItemType;
let defaultOverwrites: Partial<IEquipmentDatabase> | undefined;
if (meta.defaultUpgrades?.[0]) {
const upgradeType = meta.defaultUpgrades[0].ItemType;
const upgradeMeta = ExportUpgrades[upgradeType];
const buffs: IFingerprintStat[] = [];
for (const buff of upgradeMeta.upgradeEntries!) {
@ -51,13 +54,22 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
Value: Math.trunc(Math.random() * 0x40000000)
});
}
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, undefined, inventoryChanges, {
defaultOverwrites = {
UpgradeType: upgradeType,
UpgradeFingerprint: JSON.stringify({
compat: payload.ItemType,
buffs
} satisfies IInnateDamageFingerprint)
});
};
}
addEquipment(
inventory,
"CrewShipSalvagedWeapons",
payload.ItemType,
undefined,
inventoryChanges,
defaultOverwrites
);
}
inventoryChanges.CrewShipRawSalvage = [

View File

@ -360,6 +360,22 @@ export const guildTechController: RequestHandler = async (req, res) => {
res.json({
inventoryChanges: inventoryChanges
});
} else if (data.Action == "InstantFinish") {
if (data.TechProductCategory != "CrewShipWeapons" && data.TechProductCategory != "CrewShipWeaponSkins") {
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
}
const inventoryChanges = finishComponentRepair(inventory, data.TechProductCategory, data.CategoryItemId!);
inventoryChanges.MiscItems = [
{
ItemType: "/Lotus/Types/Items/MiscItems/InstantSalvageRepairItem",
ItemCount: -1
}
];
addMiscItems(inventory, inventoryChanges.MiscItems);
await inventory.save();
res.json({
inventoryChanges: inventoryChanges
});
} else {
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
throw new Error(`unhandled guildTech request`);
@ -372,7 +388,7 @@ type TGuildTechRequest =
| IGuildTechContributeRequest;
interface IGuildTechBasicRequest {
Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush";
Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush" | "InstantFinish";
Mode: "Guild" | "Personal";
RecipeType: string;
TechProductCategory?: string;
@ -406,11 +422,19 @@ const claimSalvagedComponent = (inventory: TInventoryDatabaseDocument, itemId: s
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
const category = personalTechProject.ProductCategory! as "CrewShipWeapons" | "CrewShipWeaponSkins";
return finishComponentRepair(inventory, category, itemId);
};
const finishComponentRepair = (
inventory: TInventoryDatabaseDocument,
category: "CrewShipWeapons" | "CrewShipWeaponSkins",
itemId: string
): IInventoryChanges => {
const salvageCategory = getSalvageCategory(category);
// find salved part & delete it
const salvageIndex = inventory[salvageCategory].findIndex(x => x._id.equals(itemId));
const salvageItem = inventory[category][salvageIndex];
const salvageItem = inventory[salvageCategory][salvageIndex];
inventory[salvageCategory].splice(salvageIndex, 1);
// add final item

View File

@ -23,6 +23,7 @@ import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
interface IModularCraftRequest {
WeaponType: string;
Parts: string[];
isWebUi?: boolean;
}
export const modularWeaponCraftingController: RequestHandler = async (req, res) => {
@ -139,20 +140,25 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
}
defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades);
addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
combineInventoryChanges(inventoryChanges, occupySlot(inventory, productCategoryToInventoryBin(category)!, false));
combineInventoryChanges(
inventoryChanges,
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
);
if (defaultUpgrades) {
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
}
// Remove credits & parts
const miscItemChanges = [];
let currencyChanges = {};
if (!data.isWebUi) {
for (const part of data.Parts) {
miscItemChanges.push({
ItemType: part,
ItemCount: -1
});
}
const currencyChanges = updateCurrency(
currencyChanges = updateCurrency(
inventory,
category == "Hoverboards" ||
category == "MoaPets" ||
@ -164,8 +170,9 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
false
);
addMiscItems(inventory, miscItemChanges);
await inventory.save();
}
await inventory.save();
// Tell client what we did
res.json({
InventoryChanges: {

View File

@ -8,7 +8,11 @@ export const releasePetController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId, "RegularCredits KubrowPets");
const payload = getJSONfromString<IReleasePetRequest>(String(req.body));
const inventoryChanges = updateCurrency(inventory, 25000, false);
const inventoryChanges = updateCurrency(
inventory,
payload.recipeName == "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" ? 25000 : 0,
false
);
inventoryChanges.RemovedIdItems = [{ ItemId: { $oid: payload.petId } }];
inventory.KubrowPets.pull({ _id: payload.petId });
@ -18,6 +22,6 @@ export const releasePetController: RequestHandler = async (req, res) => {
};
interface IReleasePetRequest {
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe";
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" | "webui";
petId: string;
}

View File

@ -1,98 +0,0 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import {
getInventory,
addEquipment,
occupySlot,
productCategoryToInventoryBin,
applyDefaultUpgrades
} from "@/src/services/inventoryService";
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
import { getDefaultUpgrades } from "@/src/services/itemDataService";
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { ExportWeapons } from "warframe-public-export-plus";
import { RequestHandler } from "express";
export const addModularEquipmentController: RequestHandler = async (req, res) => {
const requiredFields = new Set();
const accountId = await getAccountIdForRequest(req);
const request = req.body as IAddModularEquipmentRequest;
const category = modularWeaponTypes[request.ItemType];
const inventoryBin = productCategoryToInventoryBin(category)!;
requiredFields.add(category);
requiredFields.add(inventoryBin);
request.ModularParts.forEach(part => {
if (ExportWeapons[part].gunType) {
if (category == "LongGuns") {
request.ItemType = {
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam"
}[ExportWeapons[part].gunType];
} else {
request.ItemType = {
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
}[ExportWeapons[part].gunType];
}
} else if (request.ItemType == "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit") {
if (part.includes("ZanukaPetPartHead")) {
request.ItemType = {
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
}[part]!;
}
}
});
const defaultUpgrades = getDefaultUpgrades(request.ModularParts);
if (defaultUpgrades) {
requiredFields.add("RawUpgrades");
}
const defaultWeaponsMap: Record<string, string[]> = {
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit": [
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIP"
],
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit": [
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIS"
],
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit": [
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponPS"
]
};
const defaultWeapons = defaultWeaponsMap[request.ItemType] as string[] | undefined;
if (defaultWeapons) {
for (const defaultWeapon of defaultWeapons) {
const category = ExportWeapons[defaultWeapon].productCategory;
requiredFields.add(category);
requiredFields.add(productCategoryToInventoryBin(category));
}
}
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
if (defaultWeapons) {
for (const defaultWeapon of defaultWeapons) {
const category = ExportWeapons[defaultWeapon].productCategory;
addEquipment(inventory, category, defaultWeapon);
occupySlot(inventory, productCategoryToInventoryBin(category)!, true);
}
}
const defaultOverwrites: Partial<IEquipmentDatabase> = {
Configs: applyDefaultUpgrades(inventory, defaultUpgrades)
};
addEquipment(inventory, category, request.ItemType, request.ModularParts, undefined, defaultOverwrites);
occupySlot(inventory, inventoryBin, true);
await inventory.save();
res.end();
};
interface IAddModularEquipmentRequest {
ItemType: string;
ModularParts: string[];
}

View File

@ -56,6 +56,7 @@ const getItemListsController: RequestHandler = (req, response) => {
res.Syndicates = [];
res.OperatorAmps = [];
res.QuestKeys = [];
res.KubrowPets = [];
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
res[item.productCategory].push({
uniqueName,
@ -64,7 +65,7 @@ const getItemListsController: RequestHandler = (req, response) => {
});
}
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
if (item.productCategory == "Sentinels") {
if (item.productCategory != "SpecialItems") {
res[item.productCategory].push({
uniqueName,
name: getString(item.name, lang)
@ -73,11 +74,13 @@ const getItemListsController: RequestHandler = (req, response) => {
}
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
if (item.partType) {
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
res.ModularParts.push({
uniqueName,
name: getString(item.name, lang),
partType: item.partType
});
}
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
res.miscitems.push({
uniqueName: uniqueName,

View File

@ -1,5 +1,6 @@
import { IMongoDate, IOid } from "@/src/types/commonTypes";
import { Types } from "mongoose";
import { TRarity } from "warframe-public-export-plus";
export const toOid = (objectId: Types.ObjectId): IOid => {
return { $oid: objectId.toString() } satisfies IOid;
@ -8,3 +9,144 @@ export const toOid = (objectId: Types.ObjectId): IOid => {
export const toMongoDate = (date: Date): IMongoDate => {
return { $date: { $numberLong: date.getTime().toString() } };
};
export const kubrowWeights: Record<TRarity, number> = {
COMMON: 6,
UNCOMMON: 4,
RARE: 2,
LEGENDARY: 1
};
export const kubrowFurPatternsWeights: Record<TRarity, number> = {
COMMON: 6,
UNCOMMON: 5,
RARE: 2,
LEGENDARY: 1
};
export const catbrowDetails = {
Colors: [
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA", rarity: "COMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseB", rarity: "COMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseC", rarity: "COMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseD", rarity: "COMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryA", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryB", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryC", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryD", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryA", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryB", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryC", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryD", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsD", rarity: "LEGENDARY" as TRarity }
],
EyeColors: [
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesI", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesJ", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesK", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesL", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesM", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesN", rarity: "LEGENDARY" as TRarity }
],
FurPatterns: [{ type: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternA", rarity: "COMMON" as TRarity }],
BodyTypes: [
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "LEGENDARY" as TRarity }
],
Heads: [
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadD", rarity: "LEGENDARY" as TRarity }
],
Tails: [
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailD", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailE", rarity: "LEGENDARY" as TRarity }
]
};
export const kubrowDetails = {
Colors: [
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneA", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneB", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneC", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneD", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneE", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneF", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneG", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneH", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidA", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidB", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidC", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidD", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidE", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidF", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidG", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidH", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantD", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantE", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantF", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantG", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantH", rarity: "LEGENDARY" as TRarity }
],
EyeColors: [
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesI", rarity: "LEGENDARY" as TRarity }
],
FurPatterns: [
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternB", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternA", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternC", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternD", rarity: "RARE" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternE", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternF", rarity: "LEGENDARY" as TRarity }
],
BodyTypes: [
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetHeavyBodyType", rarity: "LEGENDARY" as TRarity },
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetThinBodyType", rarity: "LEGENDARY" as TRarity }
],
Heads: [],
Tails: []
};

View File

@ -28,6 +28,7 @@ import { contributeToVaultController } from "@/src/controllers/api/contributeToV
import { createAllianceController } from "@/src/controllers/api/createAllianceController";
import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController";
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
@ -222,6 +223,7 @@ apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentContro
apiRouter.post("/contributeToVault.php", contributeToVaultController);
apiRouter.post("/createAlliance.php", createAllianceController);
apiRouter.post("/createGuild.php", createGuildController);
apiRouter.post("/crewMembers.php", crewMembersController);
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);

View File

@ -15,7 +15,6 @@ import { createAccountController } from "@/src/controllers/custom/createAccountC
import { createMessageController } from "@/src/controllers/custom/createMessageController";
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
import { addItemsController } from "@/src/controllers/custom/addItemsController";
import { addModularEquipmentController } from "@/src/controllers/custom/addModularEquipmentController";
import { addXpController } from "@/src/controllers/custom/addXpController";
import { importController } from "@/src/controllers/custom/importController";
@ -40,7 +39,6 @@ customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController);
customRouter.post("/addCurrency", addCurrencyController);
customRouter.post("/addItems", addItemsController);
customRouter.post("/addModularEquipment", addModularEquipmentController);
customRouter.post("/addXp", addXpController);
customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController);

View File

@ -23,7 +23,10 @@ import {
IUpgradeClient,
TPartialStartingGear,
ILoreFragmentScan,
ICrewMemberClient
ICrewMemberClient,
Status,
IKubrowPetDetailsDatabase,
ITraits
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
@ -58,14 +61,21 @@ import {
ExportWeapons,
IDefaultUpgrade,
IPowersuit,
ISentinel,
TStandingLimitBin
} from "warframe-public-export-plus";
import { createShip } from "./shipService";
import { toOid } from "../helpers/inventoryHelpers";
import {
catbrowDetails,
kubrowDetails,
kubrowFurPatternsWeights,
kubrowWeights,
toOid
} from "../helpers/inventoryHelpers";
import { addQuestKey, completeQuest } from "@/src/services/questService";
import { handleBundleAcqusition } from "./purchaseService";
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
import { getRandomElement, getRandomInt, SRng } from "./rngService";
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
import { createMessage } from "./inboxService";
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
@ -714,6 +724,11 @@ export const addItem = async (
return {
MiscItems: miscItemChanges
};
} else if (
typeName.substr(1).split("/")[3] == "CatbrowPet" ||
typeName.substr(1).split("/")[3] == "KubrowPet"
) {
return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
} else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
if (!seed) {
throw new Error(`Expected crew member to have a seed`);
@ -932,6 +947,89 @@ export const addSpaceSuit = (
return inventoryChanges;
};
export const addKubrowPet = (
inventory: TInventoryDatabaseDocument,
kubrowPetName: string,
details: IKubrowPetDetailsDatabase | undefined,
premiumPurchase: boolean,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
combineInventoryChanges(inventoryChanges, occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase));
const kubrowPet = ExportSentinels[kubrowPetName] as ISentinel | undefined;
const exalted = kubrowPet?.exalted ?? [];
for (const specialItem of exalted) {
addSpecialItem(inventory, specialItem, inventoryChanges);
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
if (!details) {
let traits: ITraits;
if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
traits = {
BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
SecondaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryVampire",
TertiaryColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryVampire",
AccentColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsVampire",
EyeColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA",
FurPattern: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternVampire",
Personality: kubrowPetName,
BodyType: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetVampireBodyType",
Head: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadVampire",
Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
};
} else {
const isCatbrow = [
"/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
"/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit"
].includes(kubrowPetName);
const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
traits = {
BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
TertiaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
AccentColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
EyeColor: getRandomWeightedReward(traitsPool.EyeColors, kubrowWeights)!.type,
FurPattern: getRandomWeightedReward(traitsPool.FurPatterns, kubrowFurPatternsWeights)!.type,
Personality: kubrowPetName,
BodyType: getRandomWeightedReward(traitsPool.BodyTypes, kubrowWeights)!.type,
Head: isCatbrow ? getRandomWeightedReward(traitsPool.Heads, kubrowWeights)!.type : undefined,
Tail: isCatbrow ? getRandomWeightedReward(traitsPool.Tails, kubrowWeights)!.type : undefined
};
}
details = {
Name: "",
IsPuppy: false,
HasCollar: true,
PrintsRemaining: 2,
Status: Status.StatusStasis,
HatchDate: new Date(Math.trunc(Date.now() / 86400000) * 86400000),
IsMale: !!getRandomInt(0, 1),
Size: getRandomInt(70, 100) / 100,
DominantTraits: traits,
RecessiveTraits: traits
};
}
const kubrowPetIndex =
inventory.KubrowPets.push({
ItemType: kubrowPetName,
Configs: configs,
XP: 0,
Details: details,
IsNew: true
}) - 1;
inventoryChanges.KubrowPets ??= [];
inventoryChanges.KubrowPets.push(inventory.KubrowPets[kubrowPetIndex].toJSON<IEquipmentClient>());
return inventoryChanges;
};
export const updateSlots = (
inventory: TInventoryDatabaseDocument,
slotName: SlotNames,

View File

@ -53,7 +53,7 @@ import conservationAnimals from "@/static/fixed_responses/conservationAnimals.js
import { getInfNodes } from "@/src/helpers/nemesisHelpers";
import { Loadout } from "../models/inventoryModels/loadoutModel";
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
import { getWorldState } from "./worldStateService";
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
import { config } from "./configService";
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
@ -132,11 +132,13 @@ export const addMissionInventoryUpdates = async (
// Somewhat heuristically detect G3 capture:
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1365
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1694
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1724
if (
inventoryUpdates.MissionFailed &&
inventoryUpdates.MissionStatus == "GS_FAILURE" &&
inventoryUpdates.ObjectiveReached &&
!inventoryUpdates.LockedWeaponGroup &&
!inventory.LockedWeaponGroup &&
!inventoryUpdates.LevelKeyName
) {
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!;
@ -988,11 +990,7 @@ function getRandomMissionDrops(
if (sortieId == "Lite") {
sortieId = arr[2];
// TODO: Some way to get from sortieId to reward to make this faster + more reliable at week rollover.
const boss = getWorldState().LiteSorties[0].Boss as
| "SORTIE_BOSS_AMAR"
| "SORTIE_BOSS_NIRA"
| "SORTIE_BOSS_BOREAL";
const boss = getLiteSortie(idToWeek(sortieId)).Boss;
let crystalType = {
SORTIE_BOSS_AMAR: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar",
SORTIE_BOSS_NIRA: "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira",

View File

@ -1,6 +1,5 @@
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
import {
IEnemy,
IStatsAdd,
IStatsMax,
IStatsSet,
@ -137,16 +136,21 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
case "HEADSHOT":
case "KILL_ASSIST": {
playerStats.Enemies ??= [];
const enemyStatKey = {
const enemyStatKey = (
{
KILL_ENEMY: "kills",
EXECUTE_ENEMY: "executions",
HEADSHOT: "headshots",
KILL_ASSIST: "assists"
}[category] as "kills" | "executions" | "headshots" | "assists";
} as const
)[category];
for (const [type, count] of Object.entries(data as IUploadEntry)) {
const enemy = playerStats.Enemies.find(element => element.type === type);
if (enemy) {
let enemy = playerStats.Enemies.find(element => element.type === type);
if (!enemy) {
enemy = { type: type };
playerStats.Enemies.push(enemy);
}
if (category === "KILL_ENEMY") {
enemy.kills ??= 0;
const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type];
@ -161,11 +165,6 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
enemy[enemyStatKey] ??= 0;
enemy[enemyStatKey] += count;
}
} else {
const newEnemy: IEnemy = { type: type };
newEnemy[enemyStatKey] = count;
playerStats.Enemies.push(newEnemy);
}
}
break;
}

View File

@ -4,7 +4,14 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
import { config } from "@/src/services/configService";
import { CRng } from "@/src/services/rngService";
import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus";
import { ICalendarDay, ICalendarSeason, ISeasonChallenge, ISortie, IWorldState } from "../types/worldStateTypes";
import {
ICalendarDay,
ICalendarSeason,
ILiteSortie,
ISeasonChallenge,
ISortie,
IWorldState
} from "../types/worldStateTypes";
const sortieBosses = [
"SORTIE_BOSS_HYENA",
@ -348,6 +355,34 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
};
};
const pushWeeklyActs = (worldState: IWorldState, week: number): void => {
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
});
};
const birthdays: number[] = [
1, // Kaya
45, // Lettie
@ -604,29 +639,10 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) {
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day + 1));
}
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
});
worldState.SeasonInfo.ActiveChallenges.push({
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
});
// TODO: Provide upcoming weekly acts if rollover is imminent
pushWeeklyActs(worldState, week);
if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
pushWeeklyActs(worldState, week + 1);
}
// Elite Sanctuary Onslaught cycling every week
worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
@ -941,65 +957,9 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
pushSortieIfRelevant(worldState.Sorties, day);
// Archon Hunt cycling every week
// TODO: Handle imminent rollover
{
const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
const nodes: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.systemIndex === systemIndex &&
value.factionIndex !== undefined &&
value.factionIndex < 2 &&
value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 // Exclude MT_ASSASSINATION
) {
nodes.push(key);
}
}
const rng = new CRng(week);
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
const firstNode = nodes[firstNodeIndex];
nodes.splice(firstNodeIndex, 1);
worldState.LiteSorties.push({
_id: {
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
},
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
Seed: week,
Boss: boss,
Missions: [
{
missionType: rng.randomElement([
"MT_INTEL",
"MT_MOBILE_DEFENSE",
"MT_EXTERMINATION",
"MT_SABOTAGE",
"MT_RESCUE"
]),
node: firstNode
},
{
missionType: rng.randomElement([
"MT_DEFENSE",
"MT_TERRITORY",
"MT_ARTIFACT",
"MT_EXCAVATE",
"MT_SURVIVAL"
]),
node: rng.randomElement(nodes)
},
{
missionType: "MT_ASSASSINATION",
node: showdownNode
}
]
});
worldState.LiteSorties.push(getLiteSortie(week));
if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
worldState.LiteSorties.push(getLiteSortie(week + 1));
}
// Circuit choices cycling every week
@ -1071,3 +1031,70 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
return worldState;
};
export const idToWeek = (id: string): number => {
return (parseInt(id.substring(0, 8), 16) * 1000 - EPOCH) / 604800000;
};
export const getLiteSortie = (week: number): ILiteSortie => {
const boss = (["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"] as const)[week % 3];
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
const nodes: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.systemIndex === systemIndex &&
value.factionIndex !== undefined &&
value.factionIndex < 2 &&
value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 // Exclude MT_ASSASSINATION
) {
nodes.push(key);
}
}
const rng = new CRng(week);
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
const firstNode = nodes[firstNodeIndex];
nodes.splice(firstNodeIndex, 1);
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
return {
_id: {
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
},
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
Seed: week,
Boss: boss,
Missions: [
{
missionType: rng.randomElement([
"MT_INTEL",
"MT_MOBILE_DEFENSE",
"MT_EXTERMINATION",
"MT_SABOTAGE",
"MT_RESCUE"
]),
node: firstNode
},
{
missionType: rng.randomElement([
"MT_DEFENSE",
"MT_TERRITORY",
"MT_ARTIFACT",
"MT_EXCAVATE",
"MT_SURVIVAL"
]),
node: rng.randomElement(nodes)
},
{
missionType: "MT_ASSASSINATION",
node: showdownNode
}
]
};
};

View File

@ -489,7 +489,7 @@ export interface ICrewMemberClient {
XP: number;
PowersuitType: string;
Configs: IItemConfig[];
SecondInCommand: boolean;
SecondInCommand: boolean; // on call
ItemId: IOid;
}

View File

@ -103,7 +103,7 @@ export interface ILiteSortie {
Expiry: IMongoDate;
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards";
Seed: number;
Boss: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"
Boss: "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL";
Missions: {
missionType: string;
node: string;

View File

@ -305,7 +305,7 @@
<input class="form-control" id="acquire-type-MoaPets" list="datalist-MoaPets" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<form class="input-group mb-3" id="modular-MoaPets" style="display: none;">
<form class="input-group mb-3" id="modular-MoaPets-Moa" style="display: none;">
<input class="form-control" id="acquire-type-MoaPets-MOA_ENGINE" list="datalist-ModularParts-MOA_ENGINE" />
<input class="form-control" id="acquire-type-MoaPets-MOA_PAYLOAD" list="datalist-ModularParts-MOA_PAYLOAD" />
<input class="form-control" id="acquire-type-MoaPets-MOA_HEAD" list="datalist-ModularParts-MOA_HEAD" />
@ -325,6 +325,28 @@
</div>
</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_kubrowPets"></h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="handleModularSelection('KubrowPets');return false;">
<input class="form-control" id="acquire-type-KubrowPets" list="datalist-KubrowPets" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<form class="input-group mb-3" id="modular-KubrowPets-Catbrow" style="display: none;">
<input class="form-control" id="acquire-type-KubrowPets-CATBROW_ANTIGEN" list="datalist-ModularParts-CATBROW_ANTIGEN" />
<input class="form-control" id="acquire-type-KubrowPets-CATBROW_MUTAGEN" list="datalist-ModularParts-CATBROW_MUTAGEN" />
</form>
<form class="input-group mb-3" id="modular-KubrowPets-Kubrow" style="display: none;">
<input class="form-control" id="acquire-type-KubrowPets-KUBROW_ANTIGEN" list="datalist-ModularParts-KUBROW_ANTIGEN" />
<input class="form-control" id="acquire-type-KubrowPets-KUBROW_MUTAGEN" list="datalist-ModularParts-KUBROW_MUTAGEN" />
</form>
<table class="table table-hover w-100">
<tbody id="KubrowPets-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_sentinelWeapons"></h5>
@ -700,6 +722,7 @@
<datalist id="datalist-Sentinels"></datalist>
<datalist id="datalist-MechSuits"></datalist>
<datalist id="datalist-MoaPets"></datalist>
<datalist id="datalist-KubrowPets"></datalist>
<datalist id="datalist-QuestKeys"></datalist>
<datalist id="datalist-miscitems"></datalist>
<datalist id="datalist-mods">
@ -731,6 +754,10 @@
<datalist id="datalist-ModularParts-ZANUKA_HEAD"></datalist>
<datalist id="datalist-ModularParts-ZANUKA_LEG"></datalist>
<datalist id="datalist-ModularParts-ZANUKA_TAIL"></datalist>
<datalist id="datalist-ModularParts-CATBROW_ANTIGEN"></datalist>
<datalist id="datalist-ModularParts-CATBROW_MUTAGEN"></datalist>
<datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
<script src="/webui/libs/jquery-3.6.0.min.js"></script>
<script src="/webui/libs/whirlpool-js.min.js"></script>
<script src="/webui/libs/single.js"></script>

View File

@ -163,7 +163,13 @@ const webUiModularWeapons = [
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
"/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit"
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
];
let uniqueLevelCaps = {};
@ -308,7 +314,11 @@ function fetchItemList() {
"LWPT_ZANUKA_BODY",
"LWPT_ZANUKA_HEAD",
"LWPT_ZANUKA_LEG",
"LWPT_ZANUKA_TAIL"
"LWPT_ZANUKA_TAIL",
"LWPT_CATBROW_ANTIGEN",
"LWPT_CATBROW_MUTAGEN",
"LWPT_KUBROW_ANTIGEN",
"LWPT_KUBROW_MUTAGEN"
];
if (supportedModularParts.includes(item.partType)) {
const option = document.createElement("option");
@ -361,7 +371,13 @@ function updateInventory() {
"/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
];
// Populate inventory route
@ -384,7 +400,8 @@ function updateInventory() {
"Hoverboards",
"OperatorAmps",
"MechSuits",
"MoaPets"
"MoaPets",
"KubrowPets"
].forEach(category => {
document.getElementById(category + "-list").innerHTML = "";
data[category].forEach(item => {
@ -462,6 +479,23 @@ function updateInventory() {
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>`;
td.appendChild(a);
}
if (category == "KubrowPets") {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
maturePet(item.ItemId.$oid, !item.Details.IsPuppy);
};
if (item.Details.IsPuppy) {
a.title = loc("code_mature");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M112 48a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm40 304l0 128c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-223.1L59.4 304.5c-9.1 15.1-28.8 20-43.9 10.9s-20-28.8-10.9-43.9l58.3-97c17.4-28.9 48.6-46.6 82.3-46.6l29.7 0c33.7 0 64.9 17.7 82.3 46.6l58.3 97c9.1 15.1 4.2 34.8-10.9 43.9s-34.8 4.2-43.9-10.9L232 256.9 232 480c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-128-16 0z"/></svg>`;
} else {
a.title = loc("code_unmature");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 64A64 64 0 1 0 128 64a64 64 0 1 0 128 0zM152.9 169.3c-23.7-8.4-44.5-24.3-58.8-45.8L74.6 94.2C64.8 79.5 45 75.6 30.2 85.4s-18.7 29.7-8.9 44.4L40.9 159c18.1 27.1 42.8 48.4 71.1 62.4L112 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96 32 0 0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-258.4c29.1-14.2 54.4-36.2 72.7-64.2l18.2-27.9c9.6-14.8 5.4-34.6-9.4-44.3s-34.6-5.5-44.3 9.4L291 122.4c-21.8 33.4-58.9 53.6-98.8 53.6c-12.6 0-24.9-2-36.6-5.8c-.9-.3-1.8-.7-2.7-.9z"/></svg>`;
}
td.appendChild(a);
}
if (category == "Suits") {
const a = document.createElement("a");
a.href = "/webui/powersuit/" + item.ItemId.$oid;
@ -487,6 +521,7 @@ function updateInventory() {
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
document.getElementById(category + "-list").removeChild(tr);
disposeOfGear(category, item.ItemId.$oid);
};
a.title = loc("code_remove");
@ -683,6 +718,7 @@ function updateInventory() {
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
document.getElementById("riven-list").removeChild(tr);
disposeOfGear("Upgrades", item.ItemId.$oid);
};
a.title = loc("code_remove");
@ -723,6 +759,7 @@ function updateInventory() {
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
document.getElementById("mods-list").removeChild(tr);
disposeOfGear("Upgrades", item.ItemId.$oid);
};
a.title = loc("code_remove");
@ -765,6 +802,7 @@ function updateInventory() {
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
document.getElementById("mods-list").removeChild(tr);
disposeOfItems("Upgrades", item.ItemType, item.ItemCount);
};
a.title = loc("code_remove");
@ -866,12 +904,12 @@ function doAcquireEquipment(category) {
});
}
function doAcquireModularEquipment(category, ItemType) {
function doAcquireModularEquipment(category, WeaponType) {
let requiredParts;
let ModularParts = [];
let Parts = [];
switch (category) {
case "HoverBoards":
ItemType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
requiredParts = ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
break;
case "OperatorAmps":
@ -887,20 +925,33 @@ function doAcquireModularEquipment(category, ItemType) {
requiredParts = ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
break;
case "MoaPets":
if (ItemType == "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
if (WeaponType == "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
requiredParts = ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"];
} else {
requiredParts = ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
}
break;
case "KubrowPets":
if (
[
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
].includes(WeaponType)
) {
requiredParts = ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"];
} else {
requiredParts = ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
}
break;
}
requiredParts.forEach(part => {
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
if (partName) {
ModularParts.push(partName);
Parts.push(partName);
}
});
if (ModularParts.length != requiredParts.length) {
if (Parts.length != requiredParts.length) {
let isFirstPart = true;
requiredParts.forEach(part => {
const partSelector = document.getElementById("acquire-type-" + category + "-" + part);
@ -916,20 +967,80 @@ function doAcquireModularEquipment(category, ItemType) {
}
});
} else {
const mapping = {
LongGuns: {
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelAPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelEgg/InfModularBarrelEggPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelCPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelDPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelBeam/InfModularBarrelBeamPart":
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam"
},
Pistols: {
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelAPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelEgg/InfModularBarrelEggPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelCPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelDPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam",
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelBeam/InfModularBarrelBeamPart":
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
},
MoaPets: {
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
}
};
Parts.forEach(part => {
const categoryMap = mapping[category];
if (categoryMap && categoryMap[part]) {
WeaponType = categoryMap[part];
}
});
if (category == "KubrowPets") Parts.unshift(WeaponType);
revalidateAuthz(() => {
const req = $.post({
url: "/custom/addModularEquipment?" + window.authz,
contentType: "application/json",
url: "/api/modularWeaponCrafting.php?" + window.authz,
contentType: "application/octet-stream",
data: JSON.stringify({
ItemType,
ModularParts
WeaponType,
Parts,
isWebUi: true
})
});
req.done(() => {
const mainInput = document.getElementById("acquire-type-" + category);
if (mainInput) {
mainInput.value = "";
document.getElementById("modular-" + category).style.display = "none";
if (category === "MoaPets") {
const modularFieldsMoa = document.getElementById("modular-MoaPets-Moa");
const modularFieldsZanuka = document.getElementById("modular-MoaPets-Zanuka");
modularFieldsZanuka.style.display = "none";
modularFieldsMoa.style.display = "none";
} else if (category === "KubrowPets") {
const modularFieldsCatbrow = document.getElementById("modular-KubrowPets-Catbrow");
const modularFieldsKubrow = document.getElementById("modular-KubrowPets-Kubrow");
modularFieldsCatbrow.style.display = "none";
modularFieldsKubrow.style.display = "none";
} else {
const modularFields = document.getElementById("modular-" + category);
modularFields.style.display = "none";
}
}
requiredParts.forEach(part => {
document.getElementById("acquire-type-" + category + "-" + part).value = "";
@ -1081,6 +1192,18 @@ function renameGear(category, oid, name) {
}
function disposeOfGear(category, oid) {
if (category == "KubrowPets") {
revalidateAuthz(() => {
$.post({
url: "/api/releasePet.php?" + window.authz,
contentType: "application/octet-stream",
data: JSON.stringify({
Recipe: "webui",
petId: oid
})
});
});
} else {
const data = {
SellCurrency: "SC_RegularCredits",
SellPrice: 0,
@ -1097,11 +1220,10 @@ function disposeOfGear(category, oid) {
url: "/api/sell.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify(data)
}).done(function () {
updateInventory();
});
});
}
}
function disposeOfItems(category, type, count) {
const data = {
@ -1120,8 +1242,6 @@ function disposeOfItems(category, type, count) {
url: "/api/sell.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify(data)
}).done(function () {
updateInventory();
});
});
}
@ -1140,6 +1260,21 @@ function gildEquipment(category, oid) {
});
}
function maturePet(oid, revert) {
revalidateAuthz(() => {
$.post({
url: "/api/maturePet.php?" + window.authz,
contentType: "application/octet-stream",
data: JSON.stringify({
petId: oid,
revert
})
}).done(function () {
updateInventory();
});
});
}
function doAcquireMiscItems() {
const uniqueName = getKey(document.getElementById("miscitem-type"));
if (!uniqueName) {
@ -1260,6 +1395,8 @@ function doAcquireMod() {
$("#mod-to-acquire").addClass("is-invalid").focus();
return;
}
const count = parseInt($("#mod-count").val());
if (count != 0) {
revalidateAuthz(() => {
$.post({
url: "/custom/addItems?" + window.authz,
@ -1267,15 +1404,20 @@ function doAcquireMod() {
data: JSON.stringify([
{
ItemType: uniqueName,
ItemCount: parseInt($("#mod-count").val())
ItemCount: count
}
])
}).done(function () {
document.getElementById("mod-to-acquire").value = "";
if (count > 0) {
toast(loc("code_succAdded"));
} else {
toast(loc("code_succRemoved"));
}
updateInventory();
});
});
}
}
const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id);
@ -1582,32 +1724,60 @@ function handleModularSelection(category) {
}
}
{
const supportedModularInventoryCategory = ["OperatorAmps", "Melee", "LongGuns", "Pistols", "MoaPets"];
const supportedModularInventoryCategory = ["OperatorAmps", "Melee", "LongGuns", "Pistols", "MoaPets", "KubrowPets"];
supportedModularInventoryCategory.forEach(inventoryCategory => {
document.getElementById("acquire-type-" + inventoryCategory).addEventListener("input", function () {
const modularFields = document.getElementById("modular-" + inventoryCategory);
const modularFieldsZanuka =
inventoryCategory === "MoaPets"
? document.getElementById("modular-" + inventoryCategory + "-Zanuka")
: null;
const modularFieldsMoa = document.getElementById("modular-MoaPets-Moa");
const modularFieldsZanuka = document.getElementById("modular-MoaPets-Zanuka");
const modularFieldsCatbrow = document.getElementById("modular-KubrowPets-Catbrow");
const modularFieldsKubrow = document.getElementById("modular-KubrowPets-Kubrow");
const key = getKey(this);
if (webUiModularWeapons.includes(key)) {
if (key === "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit" && modularFieldsZanuka) {
modularFields.style.display = "none";
if (inventoryCategory === "MoaPets") {
if (key === "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit") {
modularFieldsMoa.style.display = "none";
modularFieldsZanuka.style.display = "";
} else if (key === "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
modularFields.style.display = "";
if (modularFieldsZanuka) {
modularFieldsMoa.style.display = "";
modularFieldsZanuka.style.display = "none";
}
} else if (inventoryCategory === "KubrowPets") {
if (
[
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
].includes(key)
) {
modularFieldsCatbrow.style.display = "";
modularFieldsKubrow.style.display = "none";
} else if (
[
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
].includes(key)
) {
modularFieldsCatbrow.style.display = "none";
modularFieldsKubrow.style.display = "";
} else {
modularFieldsCatbrow.style.display = "none";
modularFieldsKubrow.style.display = "none";
}
} else {
modularFields.style.display = "";
}
} else {
if (inventoryCategory === "MoaPets") {
modularFieldsZanuka.style.display = "none";
modularFieldsMoa.style.display = "none";
} else if (inventoryCategory === "KubrowPets") {
modularFieldsCatbrow.style.display = "none";
modularFieldsKubrow.style.display = "none";
} else {
modularFields.style.display = "none";
if (modularFieldsZanuka) {
modularFieldsZanuka.style.display = "none";
}
}
});

View File

@ -54,6 +54,8 @@ dict = {
code_completed: `Abgeschlossen`,
code_active: `Aktiv`,
code_pigment: `Pigment`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
login_emailLabel: `E-Mail-Adresse`,
login_passwordLabel: `Passwort`,
@ -80,6 +82,7 @@ dict = {
inventory_operatorAmps: `Verstärker`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`,
inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`,

View File

@ -53,6 +53,8 @@ dict = {
code_completed: `Completed`,
code_active: `Active`,
code_pigment: `Pigment`,
code_mature: `Mature for combat`,
code_unmature: `Regress genetic aging`,
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
login_emailLabel: `Email address`,
login_passwordLabel: `Password`,
@ -79,6 +81,7 @@ dict = {
inventory_operatorAmps: `Amps`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `Beasts`,
inventory_bulkAddSuits: `Add Missing Warframes`,
inventory_bulkAddWeapons: `Add Missing Weapons`,
inventory_bulkAddSpaceSuits: `Add Missing Archwings`,

View File

@ -54,6 +54,8 @@ dict = {
code_completed: `Completada`,
code_active: `Activa`,
code_pigment: `Pigmento`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
login_emailLabel: `Dirección de correo electrónico`,
login_passwordLabel: `Contraseña`,
@ -80,6 +82,7 @@ dict = {
inventory_operatorAmps: `Amps`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_bulkAddSuits: `Agregar Warframes faltantes`,
inventory_bulkAddWeapons: `Agregar armas faltantes`,
inventory_bulkAddSpaceSuits: `Agregar Archwings faltantes`,

View File

@ -54,6 +54,8 @@ dict = {
code_completed: `[UNTRANSLATED] Completed`,
code_active: `[UNTRANSLATED] Active`,
code_pigment: `Pigment`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
login_description: `Connexion avec les informations de connexion OpenWF.`,
login_emailLabel: `Email`,
login_passwordLabel: `Mot de passe`,
@ -80,6 +82,7 @@ dict = {
inventory_operatorAmps: `Amplificateurs`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
inventory_bulkAddWeapons: `Ajouter les armes manquantes`,
inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`,

View File

@ -54,6 +54,8 @@ dict = {
code_completed: `Завершено`,
code_active: `Активный`,
code_pigment: `Пигмент`,
code_mature: `Подготовить к сражениям`,
code_unmature: `Регрессия генетического старения`,
login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
login_emailLabel: `Адрес электронной почты`,
login_passwordLabel: `Пароль`,
@ -80,6 +82,7 @@ dict = {
inventory_operatorAmps: `Усилители`,
inventory_hoverboards: `К-Драйвы`,
inventory_moaPets: `МОА`,
inventory_kubrowPets: `Звери`,
inventory_bulkAddSuits: `Добавить отсутствующие варфреймы`,
inventory_bulkAddWeapons: `Добавить отсутствующее оружие`,
inventory_bulkAddSpaceSuits: `Добавить отсутствующие арчвинги`,

View File

@ -54,6 +54,8 @@ dict = {
code_completed: `[UNTRANSLATED] Completed`,
code_active: `[UNTRANSLATED] Active`,
code_pigment: `颜料`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)。`,
login_emailLabel: `电子邮箱`,
login_passwordLabel: `密码`,
@ -80,6 +82,7 @@ dict = {
inventory_operatorAmps: `增幅器`,
inventory_hoverboards: `K式悬浮板`,
inventory_moaPets: `恐鸟`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_bulkAddSuits: `添加缺失战甲`,
inventory_bulkAddWeapons: `添加缺失武器`,
inventory_bulkAddSpaceSuits: `添加缺失Archwing`,