2025-08-24 21:41:20 -07:00
|
|
|
import type { RequestHandler } from "express";
|
2025-10-16 00:48:01 -07:00
|
|
|
import { getAccountForRequest } from "../../services/loginService.ts";
|
2025-08-25 13:37:14 -07:00
|
|
|
import { getInventory, addMiscItems, addEquipment, occupySlot } from "../../services/inventoryService.ts";
|
|
|
|
|
import type { IMiscItem, TFocusPolarity, TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
|
|
|
|
|
import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
|
|
|
|
|
import { logger } from "../../utils/logger.ts";
|
2024-06-22 02:39:51 +02:00
|
|
|
import { ExportFocusUpgrades } from "warframe-public-export-plus";
|
2025-08-25 13:37:14 -07:00
|
|
|
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
|
2025-10-16 00:48:01 -07:00
|
|
|
import { version_compare } from "../../helpers/inventoryHelpers.ts";
|
2023-12-14 17:34:15 +01:00
|
|
|
|
2024-06-01 18:22:02 +02:00
|
|
|
export const focusController: RequestHandler = async (req, res) => {
|
2025-10-16 00:48:01 -07:00
|
|
|
const account = await getAccountForRequest(req);
|
|
|
|
|
|
|
|
|
|
let op = req.query.op as string;
|
|
|
|
|
const focus2 = account.BuildLabel && version_compare(account.BuildLabel, "2022.04.29.12.53") < 0;
|
|
|
|
|
if (focus2) {
|
|
|
|
|
// Focus 2.0
|
|
|
|
|
switch (req.query.op) {
|
|
|
|
|
case Focus2Operation.InstallLens:
|
|
|
|
|
op = "InstallLens";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.UnlockWay:
|
|
|
|
|
op = "UnlockWay";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.UnlockUpgrade:
|
|
|
|
|
op = "UnlockUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.IncreasePool:
|
|
|
|
|
op = "IncreasePool";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.LevelUpUpgrade:
|
|
|
|
|
op = "LevelUpUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.ActivateWay:
|
|
|
|
|
op = "ActivateWay";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.UpdateUpgrade:
|
|
|
|
|
op = "UpdateUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.SentTrainingAmplifier:
|
|
|
|
|
op = "SentTrainingAmplifier";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.UnbindUpgrade:
|
|
|
|
|
op = "UnbindUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus2Operation.ConvertShard:
|
|
|
|
|
op = "ConvertShard";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Focus 3.0
|
|
|
|
|
switch (req.query.op) {
|
|
|
|
|
case Focus3Operation.InstallLens:
|
|
|
|
|
op = "InstallLens";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.UnlockWay:
|
|
|
|
|
op = "UnlockWay";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.UnlockUpgrade:
|
|
|
|
|
op = "UnlockUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.LevelUpUpgrade:
|
|
|
|
|
op = "LevelUpUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.ActivateWay:
|
|
|
|
|
op = "ActivateWay";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.SentTrainingAmplifier:
|
|
|
|
|
op = "SentTrainingAmplifier";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.UnbindUpgrade:
|
|
|
|
|
op = "UnbindUpgrade";
|
|
|
|
|
break;
|
|
|
|
|
case Focus3Operation.ConvertShard:
|
|
|
|
|
op = "ConvertShard";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (op) {
|
2024-06-01 18:22:02 +02:00
|
|
|
default:
|
2024-12-30 00:07:53 +01:00
|
|
|
logger.error("Unhandled focus op type: " + String(req.query.op));
|
2024-12-29 21:41:56 +01:00
|
|
|
logger.debug(String(req.body));
|
2024-06-01 18:22:02 +02:00
|
|
|
res.end();
|
|
|
|
|
break;
|
2025-10-16 00:48:01 -07:00
|
|
|
case "InstallLens": {
|
2024-10-15 16:27:11 +02:00
|
|
|
const request = JSON.parse(String(req.body)) as ILensInstallRequest;
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2025-03-31 04:15:32 -07:00
|
|
|
const item = inventory[request.Category].id(request.WeaponId);
|
|
|
|
|
if (item) {
|
|
|
|
|
item.FocusLens = request.LensType;
|
|
|
|
|
addMiscItems(inventory, [
|
|
|
|
|
{
|
|
|
|
|
ItemType: request.LensType,
|
|
|
|
|
ItemCount: -1
|
|
|
|
|
} satisfies IMiscItem
|
|
|
|
|
]);
|
2024-10-15 16:27:11 +02:00
|
|
|
}
|
|
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
weaponId: request.WeaponId,
|
|
|
|
|
lensType: request.LensType
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "UnlockWay": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const focusType = (JSON.parse(String(req.body)) as IWayRequest).FocusType;
|
2024-06-01 18:22:02 +02:00
|
|
|
const focusPolarity = focusTypeToPolarity(focusType);
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString(), "FocusAbility FocusUpgrades FocusXP");
|
2024-06-01 18:22:02 +02:00
|
|
|
const cost = inventory.FocusAbility ? 50_000 : 0;
|
|
|
|
|
inventory.FocusAbility ??= focusType;
|
|
|
|
|
inventory.FocusUpgrades.push({ ItemType: focusType });
|
2025-08-31 18:47:14 -07:00
|
|
|
if (cost) {
|
|
|
|
|
inventory.FocusXP![focusPolarity]! -= cost;
|
2024-06-01 18:22:02 +02:00
|
|
|
}
|
|
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
FocusUpgrade: { ItemType: focusType },
|
|
|
|
|
FocusPointCosts: { [focusPolarity]: cost }
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "IncreasePool": {
|
|
|
|
|
const request = JSON.parse(String(req.body)) as IIncreasePoolRequest;
|
|
|
|
|
const focusPolarity = focusTypeToPolarity(request.FocusType);
|
|
|
|
|
const inventory = await getInventory(account._id.toString(), "FocusXP FocusCapacity");
|
|
|
|
|
let cost = 0;
|
|
|
|
|
for (let capacity = request.CurrentTotalCapacity; capacity != request.NewTotalCapacity; ++capacity) {
|
|
|
|
|
cost += increasePoolCost[capacity - 5];
|
|
|
|
|
}
|
|
|
|
|
inventory.FocusXP![focusPolarity]! -= cost;
|
|
|
|
|
inventory.FocusCapacity = request.NewTotalCapacity;
|
|
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
TotalCapacity: request.NewTotalCapacity,
|
|
|
|
|
FocusPointCosts: { [focusPolarity]: cost }
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "ActivateWay": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const focusType = (JSON.parse(String(req.body)) as IWayRequest).FocusType;
|
2025-03-16 16:41:39 +01:00
|
|
|
|
|
|
|
|
await Inventory.updateOne(
|
|
|
|
|
{
|
2025-10-16 00:48:01 -07:00
|
|
|
accountOwnerId: account._id.toString()
|
2025-03-16 16:41:39 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
FocusAbility: focusType
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-05-21 04:51:36 -07:00
|
|
|
res.json({
|
|
|
|
|
FocusUpgrade: { ItemType: focusType }
|
|
|
|
|
});
|
2024-06-01 18:22:02 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "UnlockUpgrade": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const request = JSON.parse(String(req.body)) as IUnlockUpgradeRequest;
|
2024-06-01 18:22:02 +02:00
|
|
|
const focusPolarity = focusTypeToPolarity(request.FocusTypes[0]);
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2024-06-01 18:22:02 +02:00
|
|
|
let cost = 0;
|
|
|
|
|
for (const focusType of request.FocusTypes) {
|
2025-10-16 00:48:01 -07:00
|
|
|
if (focusType in ExportFocusUpgrades) {
|
|
|
|
|
cost += ExportFocusUpgrades[focusType].baseFocusPointCost;
|
|
|
|
|
} else if (focusType == "/Lotus/Upgrades/Focus/Power/Residual/ChannelEfficiencyFocusUpgrade") {
|
|
|
|
|
// Zenurik's Inner Might (Focus 2.0)
|
|
|
|
|
cost += 50_000;
|
|
|
|
|
} else {
|
|
|
|
|
logger.warn(`unknown focus upgrade ${focusType}, will unlock it for free`);
|
|
|
|
|
}
|
2024-06-01 18:22:02 +02:00
|
|
|
inventory.FocusUpgrades.push({ ItemType: focusType, Level: 0 });
|
|
|
|
|
}
|
2025-06-17 05:02:58 -07:00
|
|
|
inventory.FocusXP![focusPolarity]! -= cost;
|
2024-06-01 18:22:02 +02:00
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
FocusTypes: request.FocusTypes,
|
|
|
|
|
FocusPointCosts: { [focusPolarity]: cost }
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "LevelUpUpgrade":
|
|
|
|
|
case "UpdateUpgrade": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const request = JSON.parse(String(req.body)) as ILevelUpUpgradeRequest;
|
2024-06-01 18:22:02 +02:00
|
|
|
const focusPolarity = focusTypeToPolarity(request.FocusInfos[0].ItemType);
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2024-06-01 18:22:02 +02:00
|
|
|
let cost = 0;
|
|
|
|
|
for (const focusUpgrade of request.FocusInfos) {
|
|
|
|
|
cost += focusUpgrade.FocusXpCost;
|
|
|
|
|
const focusUpgradeDb = inventory.FocusUpgrades.find(entry => entry.ItemType == focusUpgrade.ItemType)!;
|
2025-10-16 00:48:01 -07:00
|
|
|
if (op == "UpdateUpgrade") {
|
|
|
|
|
focusUpgradeDb.IsActive = focusUpgrade.IsActive;
|
|
|
|
|
} else {
|
|
|
|
|
focusUpgradeDb.Level = focusUpgrade.Level;
|
|
|
|
|
}
|
2024-06-01 18:22:02 +02:00
|
|
|
}
|
2025-06-17 05:02:58 -07:00
|
|
|
inventory.FocusXP![focusPolarity]! -= cost;
|
2024-06-01 18:22:02 +02:00
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
FocusInfos: request.FocusInfos,
|
|
|
|
|
FocusPointCosts: { [focusPolarity]: cost }
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "SentTrainingAmplifier": {
|
2024-06-29 14:49:40 +03:00
|
|
|
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2025-04-26 11:56:06 -07:00
|
|
|
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
|
|
|
|
|
ModularParts: [
|
|
|
|
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
|
|
|
|
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
|
|
|
|
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
|
|
|
|
|
]
|
|
|
|
|
});
|
2025-03-16 04:33:48 -07:00
|
|
|
occupySlot(inventory, InventorySlot.AMPS, false);
|
2025-01-04 00:25:09 +01:00
|
|
|
await inventory.save();
|
2025-07-04 16:49:25 -07:00
|
|
|
res.json(inventoryChanges.OperatorAmps![0]);
|
2024-06-29 14:49:40 +03:00
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "UnbindUpgrade": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const request = JSON.parse(String(req.body)) as IUnbindUpgradeRequest;
|
2024-06-22 03:01:05 +02:00
|
|
|
const focusPolarity = focusTypeToPolarity(request.FocusTypes[0]);
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2025-06-17 05:02:58 -07:00
|
|
|
inventory.FocusXP![focusPolarity]! -= 750_000 * request.FocusTypes.length;
|
2024-06-22 03:01:05 +02:00
|
|
|
addMiscItems(inventory, [
|
|
|
|
|
{
|
|
|
|
|
ItemType: "/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantItem",
|
|
|
|
|
ItemCount: request.FocusTypes.length * -1
|
|
|
|
|
}
|
|
|
|
|
]);
|
|
|
|
|
request.FocusTypes.forEach(type => {
|
|
|
|
|
const focusUpgradeDb = inventory.FocusUpgrades.find(entry => entry.ItemType == type)!;
|
|
|
|
|
focusUpgradeDb.IsUniversal = true;
|
|
|
|
|
});
|
|
|
|
|
await inventory.save();
|
|
|
|
|
res.json({
|
|
|
|
|
FocusTypes: request.FocusTypes,
|
|
|
|
|
FocusPointCosts: {
|
2024-06-22 17:55:30 +02:00
|
|
|
[focusPolarity]: 750_000 * request.FocusTypes.length
|
2024-06-22 03:01:05 +02:00
|
|
|
},
|
|
|
|
|
MiscItemCosts: [
|
|
|
|
|
{
|
|
|
|
|
ItemType: "/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantItem",
|
|
|
|
|
ItemCount: request.FocusTypes.length
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
case "ConvertShard": {
|
2024-06-24 12:37:28 +02:00
|
|
|
const request = JSON.parse(String(req.body)) as IConvertShardRequest;
|
2024-06-01 18:22:02 +02:00
|
|
|
// Tally XP
|
|
|
|
|
let xp = 0;
|
|
|
|
|
for (const shard of request.Shards) {
|
|
|
|
|
xp += shardValues[shard.ItemType as keyof typeof shardValues] * shard.ItemCount;
|
|
|
|
|
}
|
|
|
|
|
// Send response
|
|
|
|
|
res.json({
|
|
|
|
|
FocusPointGains: {
|
|
|
|
|
[request.Polarity]: xp
|
|
|
|
|
},
|
|
|
|
|
MiscItemCosts: request.Shards
|
|
|
|
|
});
|
|
|
|
|
// Commit added XP and removed shards to DB
|
|
|
|
|
for (const shard of request.Shards) {
|
|
|
|
|
shard.ItemCount *= -1;
|
|
|
|
|
}
|
2025-10-16 00:48:01 -07:00
|
|
|
const inventory = await getInventory(account._id.toString());
|
2025-06-17 05:02:58 -07:00
|
|
|
const polarity = request.Polarity;
|
|
|
|
|
inventory.FocusXP ??= {};
|
|
|
|
|
inventory.FocusXP[polarity] ??= 0;
|
|
|
|
|
inventory.FocusXP[polarity] += xp;
|
2024-06-01 18:22:02 +02:00
|
|
|
addMiscItems(inventory, request.Shards);
|
|
|
|
|
await inventory.save();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-16 00:48:01 -07:00
|
|
|
// Focus 3.0
|
|
|
|
|
enum Focus3Operation {
|
2024-10-15 16:27:11 +02:00
|
|
|
InstallLens = "1",
|
2024-06-01 18:22:02 +02:00
|
|
|
UnlockWay = "2",
|
|
|
|
|
UnlockUpgrade = "3",
|
|
|
|
|
LevelUpUpgrade = "4",
|
|
|
|
|
ActivateWay = "5",
|
2024-06-29 14:49:40 +03:00
|
|
|
SentTrainingAmplifier = "7",
|
2024-06-22 03:01:05 +02:00
|
|
|
UnbindUpgrade = "8",
|
2024-06-01 18:22:02 +02:00
|
|
|
ConvertShard = "9"
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 00:48:01 -07:00
|
|
|
// Focus 2.0
|
|
|
|
|
enum Focus2Operation {
|
|
|
|
|
InstallLens = "1",
|
|
|
|
|
UnlockWay = "2",
|
|
|
|
|
UnlockUpgrade = "3",
|
|
|
|
|
IncreasePool = "4",
|
|
|
|
|
LevelUpUpgrade = "5",
|
|
|
|
|
ActivateWay = "6",
|
|
|
|
|
UpdateUpgrade = "7", // used to change the IsActive state, same format as ILevelUpUpgradeRequest
|
|
|
|
|
SentTrainingAmplifier = "9",
|
|
|
|
|
UnbindUpgrade = "10",
|
|
|
|
|
ConvertShard = "11"
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-01 18:22:02 +02:00
|
|
|
// For UnlockWay & ActivateWay
|
|
|
|
|
interface IWayRequest {
|
|
|
|
|
FocusType: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IUnlockUpgradeRequest {
|
|
|
|
|
FocusTypes: string[];
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 00:48:01 -07:00
|
|
|
// Focus 2.0
|
|
|
|
|
interface IIncreasePoolRequest {
|
|
|
|
|
FocusType: string;
|
|
|
|
|
CurrentTotalCapacity: number;
|
|
|
|
|
NewTotalCapacity: number;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-01 18:22:02 +02:00
|
|
|
interface ILevelUpUpgradeRequest {
|
|
|
|
|
FocusInfos: {
|
|
|
|
|
ItemType: string;
|
|
|
|
|
FocusXpCost: number;
|
|
|
|
|
IsUniversal: boolean;
|
|
|
|
|
Level: number;
|
|
|
|
|
IsActiveAbility: boolean;
|
2025-10-16 00:48:01 -07:00
|
|
|
IsActive?: number; // Focus 2.0
|
2024-06-01 18:22:02 +02:00
|
|
|
}[];
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-22 03:01:05 +02:00
|
|
|
interface IUnbindUpgradeRequest {
|
|
|
|
|
ShardTypes: string[];
|
|
|
|
|
FocusTypes: string[];
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-01 18:22:02 +02:00
|
|
|
interface IConvertShardRequest {
|
|
|
|
|
Shards: IMiscItem[];
|
|
|
|
|
Polarity: TFocusPolarity;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-29 14:49:40 +03:00
|
|
|
interface ISentTrainingAmplifierRequest {
|
|
|
|
|
StartingWeaponType: string;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-15 16:27:11 +02:00
|
|
|
interface ILensInstallRequest {
|
|
|
|
|
LensType: string;
|
|
|
|
|
Category: TEquipmentKey;
|
|
|
|
|
WeaponId: string;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-01 18:22:02 +02:00
|
|
|
// Works for ways & upgrades
|
|
|
|
|
const focusTypeToPolarity = (type: string): TFocusPolarity => {
|
2025-09-08 20:43:31 -07:00
|
|
|
return ("AP_" + type.substring(1).split("/")[3].toUpperCase()) as TFocusPolarity;
|
2024-06-01 18:22:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const shardValues = {
|
|
|
|
|
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardCommonItem": 2_500,
|
|
|
|
|
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardSynthesizedItem": 5_000,
|
|
|
|
|
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantItem": 25_000,
|
|
|
|
|
"/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantTierTwoItem": 40_000
|
2023-12-14 17:34:15 +01:00
|
|
|
};
|
2025-10-16 00:48:01 -07:00
|
|
|
|
|
|
|
|
// Starting at a capacity of 5 (Source: https://wiki.warframe.com/w/Focus_2.0)
|
|
|
|
|
const increasePoolCost = [
|
|
|
|
|
2576, 3099, 3638, 4190, 4755, 5331, 5918, 6514, 7120, 7734, 8357, 8988, 9626, 10271, 10923, 11582, 12247, 12918,
|
|
|
|
|
13595, 14277, 14965, 15659, 16357, 17061, 17769, 18482, 19200, 19922, 20649, 21380, 22115, 22854, 23597, 24344,
|
|
|
|
|
25095, 25850, 26609, 27371, 28136, 28905, 29678, 30454, 31233, 32015, 32801, 33590, 34382, 35176, 35974, 36775,
|
|
|
|
|
37579, 38386, 39195, 40008, 40823, 41641, 42461, 43284, 44110, 44938, 45769, 46603, 47439, 48277, 49118, 49961,
|
|
|
|
|
50807, 51655, 52505, 53357, 54212, 55069, 55929, 56790, 57654, 58520, 59388, 60258, 61130, 62005, 62881, 63759,
|
|
|
|
|
64640, 65522, 66407, 67293, 68182, 69072, 69964, 70858, 71754, 72652, 73552, 74453, 75357, 76262, 77169, 78078,
|
|
|
|
|
78988, 79900, 80814, 81730, 82648, 83567, 84488, 85410, 86334, 87260, 88188, 89117, 90047, 90980, 91914, 92849,
|
|
|
|
|
93786, 94725, 95665, 96607, 97550, 98495, 99441, 100389, 101338, 102289, 103241, 104195, 105150, 106107, 107065,
|
|
|
|
|
108024, 108985, 109948, 110911, 111877, 112843, 113811, 114780, 115751, 116723, 117696, 118671, 119647, 120624,
|
|
|
|
|
121603, 122583, 123564, 124547, 125531, 126516, 127503, 128490, 129479, 130470, 131461, 132454, 133448, 134443,
|
|
|
|
|
135440, 136438, 137437, 138437, 139438, 140441, 141444, 142449, 143455, 144463, 145471, 146481, 147492, 148503,
|
|
|
|
|
149517
|
|
|
|
|
];
|