Compare commits
10 Commits
e9d6f4581d
...
fc7c8ccd25
Author | SHA1 | Date | |
---|---|---|---|
fc7c8ccd25 | |||
143b358a03 | |||
0b75757277 | |||
fd7f4c9e92 | |||
fa6fac494b | |||
6b3f524574 | |||
506365f97e | |||
70646160c3 | |||
3ffa4a7fd3 | |||
826a09a473 |
41
src/controllers/api/completeCalendarEventController.ts
Normal file
41
src/controllers/api/completeCalendarEventController.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { getCalendarProgress, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||||
|
import { getWorldState } from "@/src/services/worldStateService";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER
|
||||||
|
export const completeCalendarEventController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
const calendarProgress = getCalendarProgress(inventory);
|
||||||
|
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||||
|
let inventoryChanges: IInventoryChanges = {};
|
||||||
|
let dayIndex = 0;
|
||||||
|
for (const day of currentSeason.Days) {
|
||||||
|
if (day.events.length == 0 || day.events[0].type != "CET_CHALLENGE") {
|
||||||
|
if (dayIndex == calendarProgress.SeasonProgress.LastCompletedDayIdx) {
|
||||||
|
if (day.events.length != 0) {
|
||||||
|
const selection = day.events[parseInt(req.query.CompletedEventIdx as string)];
|
||||||
|
if (selection.type == "CET_REWARD") {
|
||||||
|
inventoryChanges = (await handleStoreItemAcquisition(selection.reward!, inventory))
|
||||||
|
.InventoryChanges;
|
||||||
|
} else if (selection.type == "CET_UPGRADE") {
|
||||||
|
calendarProgress.YearProgress.Upgrades.push(selection.upgrade!);
|
||||||
|
} else if (selection.type != "CET_PLOT") {
|
||||||
|
throw new Error(`unexpected selection type: ${selection.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++dayIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
CalendarProgress: inventory.CalendarProgress
|
||||||
|
});
|
||||||
|
};
|
@ -3,7 +3,7 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||||
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { generateRewardSeed, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getInventoryResponse } from "./inventoryController";
|
import { getInventoryResponse } from "./inventoryController";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||||
@ -63,6 +63,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
||||||
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
||||||
) {
|
) {
|
||||||
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||||
res.json({
|
res.json({
|
||||||
@ -81,6 +82,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
ConquestCompletedMissionsCount
|
ConquestCompletedMissionsCount
|
||||||
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||||
|
|
||||||
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export const setShipFavouriteLoadoutController: RequestHandler = async (req, res
|
|||||||
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
||||||
}
|
}
|
||||||
await personalRooms.save();
|
await personalRooms.save();
|
||||||
res.json({});
|
res.json(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ISetShipFavouriteLoadoutRequest {
|
interface ISetShipFavouriteLoadoutRequest {
|
||||||
|
@ -390,7 +390,7 @@ MailboxSchema.set("toJSON", {
|
|||||||
|
|
||||||
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||||
{
|
{
|
||||||
Seed: Number,
|
Seed: BigInt,
|
||||||
NumCompletions: { type: Number, default: 0 }
|
NumCompletions: { type: Number, default: 0 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1125,15 +1125,15 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
|
|||||||
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
||||||
{
|
{
|
||||||
Version: { type: Number, default: 19 },
|
Version: { type: Number, default: 19 },
|
||||||
Iteration: { type: Number, default: 2 },
|
Iteration: { type: Number, required: true },
|
||||||
YearProgress: {
|
YearProgress: {
|
||||||
Upgrades: { type: [] }
|
Upgrades: { type: [String], default: [] }
|
||||||
},
|
},
|
||||||
SeasonProgress: {
|
SeasonProgress: {
|
||||||
SeasonType: String,
|
SeasonType: { type: String, required: true },
|
||||||
LastCompletedDayIdx: { type: Number, default: -1 },
|
LastCompletedDayIdx: { type: Number, default: 0 },
|
||||||
LastCompletedChallengeDayIdx: { type: Number, default: -1 },
|
LastCompletedChallengeDayIdx: { type: Number, default: 0 },
|
||||||
ActivatedChallenges: []
|
ActivatedChallenges: { type: [String], default: [] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
|
@ -19,6 +19,7 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
|
|||||||
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
|
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
|
||||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
|
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
|
||||||
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
|
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
|
||||||
|
import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController";
|
||||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
||||||
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
||||||
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
||||||
@ -158,6 +159,7 @@ apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
|||||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||||
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
||||||
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
||||||
|
apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController);
|
||||||
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
||||||
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
||||||
apiRouter.get("/credits.php", creditsController);
|
apiRouter.get("/credits.php", creditsController);
|
||||||
|
@ -18,7 +18,6 @@ import {
|
|||||||
IKubrowPetEggDatabase,
|
IKubrowPetEggDatabase,
|
||||||
IKubrowPetEggClient,
|
IKubrowPetEggClient,
|
||||||
ILibraryDailyTaskInfo,
|
ILibraryDailyTaskInfo,
|
||||||
ICalendarProgress,
|
|
||||||
IDroneClient,
|
IDroneClient,
|
||||||
IUpgradeClient,
|
IUpgradeClient,
|
||||||
TPartialStartingGear,
|
TPartialStartingGear,
|
||||||
@ -26,7 +25,8 @@ import {
|
|||||||
ICrewMemberClient,
|
ICrewMemberClient,
|
||||||
Status,
|
Status,
|
||||||
IKubrowPetDetailsDatabase,
|
IKubrowPetDetailsDatabase,
|
||||||
ITraits
|
ITraits,
|
||||||
|
ICalendarProgress
|
||||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||||
@ -78,6 +78,7 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
|||||||
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
|
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
|
||||||
import { createMessage } from "./inboxService";
|
import { createMessage } from "./inboxService";
|
||||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||||
|
import { getWorldState } from "./worldStateService";
|
||||||
|
|
||||||
export const createInventory = async (
|
export const createInventory = async (
|
||||||
accountOwnerId: Types.ObjectId,
|
accountOwnerId: Types.ObjectId,
|
||||||
@ -91,7 +92,6 @@ export const createInventory = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
|
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
|
||||||
inventory.CalendarProgress = createCalendar();
|
|
||||||
inventory.RewardSeed = generateRewardSeed();
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
inventory.DuviriInfo = {
|
inventory.DuviriInfo = {
|
||||||
Seed: generateRewardSeed(),
|
Seed: generateRewardSeed(),
|
||||||
@ -120,10 +120,15 @@ export const createInventory = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateRewardSeed = (): number => {
|
export const generateRewardSeed = (): bigint => {
|
||||||
const min = -Number.MAX_SAFE_INTEGER;
|
const hiDword = getRandomInt(0, 0x7fffffff);
|
||||||
const max = Number.MAX_SAFE_INTEGER;
|
const loDword = getRandomInt(0, 0xffffffff);
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
let seed = (BigInt(hiDword) << 32n) | BigInt(loDword);
|
||||||
|
if (Math.random() < 0.5) {
|
||||||
|
seed *= -1n;
|
||||||
|
seed -= 1n;
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: RawUpgrades might need to return a LastAdded
|
//TODO: RawUpgrades might need to return a LastAdded
|
||||||
@ -1756,20 +1761,6 @@ export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCalendar = (): ICalendarProgress => {
|
|
||||||
return {
|
|
||||||
Version: 19,
|
|
||||||
Iteration: 2,
|
|
||||||
YearProgress: { Upgrades: [] },
|
|
||||||
SeasonProgress: {
|
|
||||||
SeasonType: "CST_SPRING",
|
|
||||||
LastCompletedDayIdx: -1,
|
|
||||||
LastCompletedChallengeDayIdx: -1,
|
|
||||||
ActivatedChallenges: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => {
|
export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => {
|
||||||
inventory.Affiliations.push({
|
inventory.Affiliations.push({
|
||||||
Title: 1,
|
Title: 1,
|
||||||
@ -1806,3 +1797,37 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
|||||||
LibrarySyndicate.FreeFavorsEarned = undefined;
|
LibrarySyndicate.FreeFavorsEarned = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
||||||
|
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||||
|
|
||||||
|
if (!inventory.CalendarProgress) {
|
||||||
|
inventory.CalendarProgress = {
|
||||||
|
Version: 19,
|
||||||
|
Iteration: currentSeason.YearIteration,
|
||||||
|
YearProgress: {
|
||||||
|
Upgrades: []
|
||||||
|
},
|
||||||
|
SeasonProgress: {
|
||||||
|
SeasonType: currentSeason.Season,
|
||||||
|
LastCompletedDayIdx: 0,
|
||||||
|
LastCompletedChallengeDayIdx: 0,
|
||||||
|
ActivatedChallenges: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const yearRolledOver = inventory.CalendarProgress.Iteration != currentSeason.YearIteration;
|
||||||
|
if (yearRolledOver) {
|
||||||
|
inventory.CalendarProgress.Iteration = currentSeason.YearIteration;
|
||||||
|
inventory.CalendarProgress.YearProgress.Upgrades = [];
|
||||||
|
}
|
||||||
|
if (yearRolledOver || inventory.CalendarProgress.SeasonProgress.SeasonType != currentSeason.Season) {
|
||||||
|
inventory.CalendarProgress.SeasonProgress.SeasonType = currentSeason.Season;
|
||||||
|
inventory.CalendarProgress.SeasonProgress.LastCompletedDayIdx = -1;
|
||||||
|
inventory.CalendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = -1;
|
||||||
|
inventory.CalendarProgress.SeasonProgress.ActivatedChallenges = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return inventory.CalendarProgress;
|
||||||
|
};
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
addStanding,
|
addStanding,
|
||||||
combineInventoryChanges,
|
combineInventoryChanges,
|
||||||
generateRewardSeed,
|
generateRewardSeed,
|
||||||
|
getCalendarProgress,
|
||||||
updateCurrency,
|
updateCurrency,
|
||||||
updateSyndicate
|
updateSyndicate
|
||||||
} from "@/src/services/inventoryService";
|
} from "@/src/services/inventoryService";
|
||||||
@ -73,13 +74,11 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
|||||||
return [rewardInfo.rewardTier];
|
return [rewardInfo.rewardTier];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aborting a railjack mission should not give any rewards (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
const rotationCount = rewardInfo.rewardQualifications?.length || 0;
|
||||||
if (rewardInfo.rewardQualifications === undefined) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const rotationCount = rewardInfo.rewardQualifications.length || 0;
|
// Empty or absent rewardQualifications should not give rewards:
|
||||||
if (rotationCount === 0) return [0];
|
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||||
|
// - Completing only 1 zone of (E)SO (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||||
|
|
||||||
const rotationPattern =
|
const rotationPattern =
|
||||||
tierOverride === undefined
|
tierOverride === undefined
|
||||||
@ -562,6 +561,15 @@ export const addMissionInventoryUpdates = async (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "CalendarProgress": {
|
||||||
|
const calendarProgress = getCalendarProgress(inventory);
|
||||||
|
for (const progress of value) {
|
||||||
|
const challengeName = progress.challenge.substring(progress.challenge.lastIndexOf("/") + 1);
|
||||||
|
calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx++;
|
||||||
|
calendarProgress.SeasonProgress.ActivatedChallenges.push(challengeName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "duviriCaveOffers": {
|
case "duviriCaveOffers": {
|
||||||
// Duviri cave offers (generated with the duviri seed) change after completing one of its game modes (not when aborting).
|
// Duviri cave offers (generated with the duviri seed) change after completing one of its game modes (not when aborting).
|
||||||
if (inventoryUpdates.MissionStatus != "GS_QUIT") {
|
if (inventoryUpdates.MissionStatus != "GS_QUIT") {
|
||||||
@ -749,11 +757,6 @@ export const addMissionRewards = async (
|
|||||||
return { MissionRewards: [] };
|
return { MissionRewards: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rewardInfo.rewardSeed) {
|
|
||||||
// We're using a reward seed, so give the client a new one in the response. On live, missionInventoryUpdate seems to always provide a fresh one in the response.
|
|
||||||
inventory.RewardSeed = generateRewardSeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: check double reward merging
|
//TODO: check double reward merging
|
||||||
const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
|
const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
|
||||||
logger.debug("random mission drops:", MissionRewards);
|
logger.debug("random mission drops:", MissionRewards);
|
||||||
@ -799,7 +802,8 @@ export const addMissionRewards = async (
|
|||||||
missions.Tag != "SolNode761" && // the index
|
missions.Tag != "SolNode761" && // the index
|
||||||
missions.Tag != "SolNode762" && // the index
|
missions.Tag != "SolNode762" && // the index
|
||||||
missions.Tag != "SolNode763" && // the index
|
missions.Tag != "SolNode763" && // the index
|
||||||
missions.Tag != "CrewBattleNode556" // free flight
|
missions.Tag != "CrewBattleNode556" && // free flight
|
||||||
|
getRotations(rewardInfo).length > 0 // (E)SO should not give credits for only completing zone 1, in which case it has no rewardQualifications (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||||
) {
|
) {
|
||||||
const levelCreditReward = getLevelCreditRewards(node);
|
const levelCreditReward = getLevelCreditRewards(node);
|
||||||
missionCompletionCredits += levelCreditReward;
|
missionCompletionCredits += levelCreditReward;
|
||||||
@ -811,6 +815,14 @@ export const addMissionRewards = async (
|
|||||||
missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
|
missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rewardInfo.sortieTag == "Mission1") {
|
||||||
|
missionCompletionCredits += 20_000;
|
||||||
|
} else if (rewardInfo.sortieTag == "Mission2") {
|
||||||
|
missionCompletionCredits += 30_000;
|
||||||
|
} else if (rewardInfo.sortieTag == "Final") {
|
||||||
|
missionCompletionCredits += 50_000;
|
||||||
|
}
|
||||||
|
|
||||||
if (missions.Tag == "PlutoToErisJunction") {
|
if (missions.Tag == "PlutoToErisJunction") {
|
||||||
await createMessage(inventory.accountOwnerId, [
|
await createMessage(inventory.accountOwnerId, [
|
||||||
{
|
{
|
||||||
@ -1413,6 +1425,11 @@ function getRandomMissionDrops(
|
|||||||
if (rewardManifests.length != 0) {
|
if (rewardManifests.length != 0) {
|
||||||
logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
|
logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
|
||||||
}
|
}
|
||||||
|
if (RewardInfo.rewardSeed) {
|
||||||
|
if (RewardInfo.rewardSeed != inventory.RewardSeed) {
|
||||||
|
logger.warn(`RewardSeed mismatch:`, { client: RewardInfo.rewardSeed, database: inventory.RewardSeed });
|
||||||
|
}
|
||||||
|
}
|
||||||
const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn);
|
const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn);
|
||||||
rewardManifests.forEach(name => {
|
rewardManifests.forEach(name => {
|
||||||
const table = ExportRewards[name];
|
const table = ExportRewards[name];
|
||||||
|
@ -97,9 +97,17 @@ export class CRng {
|
|||||||
}
|
}
|
||||||
|
|
||||||
randomInt(min: number, max: number): number {
|
randomInt(min: number, max: number): number {
|
||||||
min = Math.ceil(min);
|
const diff = max - min;
|
||||||
max = Math.floor(max);
|
if (diff != 0) {
|
||||||
return Math.floor(this.random() * (max - min + 1)) + min;
|
if (diff < 0) {
|
||||||
|
throw new Error(`max must be greater than min`);
|
||||||
|
}
|
||||||
|
if (diff > 0x3fffffff) {
|
||||||
|
throw new Error(`insufficient entropy`);
|
||||||
|
}
|
||||||
|
min += Math.floor(this.random() * (diff + 1));
|
||||||
|
}
|
||||||
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomElement<T>(arr: T[]): T {
|
randomElement<T>(arr: T[]): T {
|
||||||
|
@ -5,9 +5,10 @@ import {
|
|||||||
IItemManifestPreprocessed,
|
IItemManifestPreprocessed,
|
||||||
IRawVendorManifest,
|
IRawVendorManifest,
|
||||||
IVendorInfo,
|
IVendorInfo,
|
||||||
|
IVendorInfoPreprocessed,
|
||||||
IVendorManifestPreprocessed
|
IVendorManifestPreprocessed
|
||||||
} from "@/src/types/vendorTypes";
|
} from "@/src/types/vendorTypes";
|
||||||
import { ExportVendors } from "warframe-public-export-plus";
|
import { ExportVendors, IRange } from "warframe-public-export-plus";
|
||||||
|
|
||||||
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
|
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
|
||||||
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
|
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
|
||||||
@ -32,7 +33,6 @@ import OstronFishmongerVendorManifest from "@/static/fixed_responses/getVendorIn
|
|||||||
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
||||||
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
|
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
|
||||||
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
|
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
|
||||||
import SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json";
|
|
||||||
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
||||||
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json";
|
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json";
|
||||||
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
|
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
|
||||||
@ -63,7 +63,6 @@ const rawVendorManifests: IRawVendorManifest[] = [
|
|||||||
OstronPetVendorManifest,
|
OstronPetVendorManifest,
|
||||||
OstronProspectorVendorManifest,
|
OstronProspectorVendorManifest,
|
||||||
RadioLegionIntermission12VendorManifest,
|
RadioLegionIntermission12VendorManifest,
|
||||||
SolarisDebtTokenVendorManifest,
|
|
||||||
SolarisDebtTokenVendorRepossessionsManifest,
|
SolarisDebtTokenVendorRepossessionsManifest,
|
||||||
SolarisFishmongerVendorManifest,
|
SolarisFishmongerVendorManifest,
|
||||||
SolarisProspectorVendorManifest,
|
SolarisProspectorVendorManifest,
|
||||||
@ -72,7 +71,7 @@ const rawVendorManifests: IRawVendorManifest[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
|
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
|
||||||
cycleStart: number;
|
cycleOffset: number;
|
||||||
cycleDuration: number;
|
cycleDuration: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ const generatableVendors: IGeneratableVendorInfo[] = [
|
|||||||
RandomSeedType: "VRST_WEAPON",
|
RandomSeedType: "VRST_WEAPON",
|
||||||
RequiredGoalTag: "",
|
RequiredGoalTag: "",
|
||||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||||
cycleStart: 1740960000_000,
|
cycleOffset: 1740960000_000,
|
||||||
cycleDuration: 4 * unixTimesInMs.day
|
cycleDuration: 4 * unixTimesInMs.day
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -93,8 +92,16 @@ const generatableVendors: IGeneratableVendorInfo[] = [
|
|||||||
PropertyTextHash: "34F8CF1DFF745F0D67433A5EF0A03E70",
|
PropertyTextHash: "34F8CF1DFF745F0D67433A5EF0A03E70",
|
||||||
RandomSeedType: "VRST_WEAPON",
|
RandomSeedType: "VRST_WEAPON",
|
||||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||||
cycleStart: 1744934400_000,
|
cycleOffset: 1744934400_000,
|
||||||
cycleDuration: 4 * unixTimesInMs.day
|
cycleDuration: 4 * unixTimesInMs.day
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: { $oid: "5be4a159b144f3cdf1c22efa" },
|
||||||
|
TypeName: "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorManifest",
|
||||||
|
PropertyTextHash: "A39621049CA3CA13761028CD21C239EF",
|
||||||
|
RandomSeedType: "VRST_FLAVOUR_TEXT",
|
||||||
|
cycleOffset: 1734307200_000,
|
||||||
|
cycleDuration: unixTimesInMs.hour
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
|
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
|
||||||
@ -166,60 +173,128 @@ const refreshExpiry = (expiry: IMongoDate): number => {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toRange = (value: IRange | number): IRange => {
|
||||||
|
if (typeof value == "number") {
|
||||||
|
return { minValue: value, maxValue: value };
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const vendorInfoCache: Record<string, IVendorInfoPreprocessed> = {};
|
||||||
|
|
||||||
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifestPreprocessed => {
|
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifestPreprocessed => {
|
||||||
const EPOCH = vendorInfo.cycleStart;
|
if (!(vendorInfo.TypeName in vendorInfoCache)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
|
||||||
|
vendorInfoCache[vendorInfo.TypeName] = {
|
||||||
|
...clientVendorInfo,
|
||||||
|
ItemManifest: [],
|
||||||
|
Expiry: { $date: { $numberLong: "0" } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const processed = vendorInfoCache[vendorInfo.TypeName];
|
||||||
|
if (Date.now() >= parseInt(processed.Expiry.$date.$numberLong)) {
|
||||||
|
// Remove expired offers
|
||||||
|
for (let i = 0; i != processed.ItemManifest.length; ) {
|
||||||
|
if (Date.now() >= parseInt(processed.ItemManifest[i].Expiry.$date.$numberLong)) {
|
||||||
|
processed.ItemManifest.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new offers
|
||||||
|
const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
|
||||||
|
const cycleIndex = Math.trunc((Date.now() - vendorInfo.cycleOffset) / vendorInfo.cycleDuration);
|
||||||
|
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
|
||||||
const manifest = ExportVendors[vendorInfo.TypeName];
|
const manifest = ExportVendors[vendorInfo.TypeName];
|
||||||
|
const offersToAdd = [];
|
||||||
|
if (manifest.numItems && manifest.numItems.minValue != manifest.numItems.maxValue) {
|
||||||
|
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
|
||||||
|
while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) {
|
||||||
|
// TODO: Consider per-bin item limits
|
||||||
|
// TODO: Consider item probability weightings
|
||||||
|
offersToAdd.push(rng.randomElement(manifest.items));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let binThisCycle;
|
let binThisCycle;
|
||||||
if (manifest.isOneBinPerCycle) {
|
if (manifest.isOneBinPerCycle) {
|
||||||
const cycleDuration = vendorInfo.cycleDuration; // manifest.items[0].durationHours! * 3600_000;
|
|
||||||
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
|
|
||||||
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
||||||
}
|
}
|
||||||
const items: IItemManifestPreprocessed[] = [];
|
for (const rawItem of manifest.items) {
|
||||||
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
|
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) {
|
||||||
for (let i = 0; i != manifest.items.length; ++i) {
|
offersToAdd.push(rawItem);
|
||||||
const rawItem = manifest.items[i];
|
|
||||||
if (manifest.isOneBinPerCycle && rawItem.bin != binThisCycle) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
const cycleDuration = vendorInfo.cycleDuration; // rawItem.durationHours! * 3600_000;
|
|
||||||
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
|
|
||||||
const cycleStart = EPOCH + cycleIndex * cycleDuration;
|
|
||||||
const cycleEnd = cycleStart + cycleDuration;
|
|
||||||
if (soonestOfferExpiry > cycleEnd) {
|
|
||||||
soonestOfferExpiry = cycleEnd;
|
|
||||||
}
|
}
|
||||||
const rng = new CRng(cycleIndex);
|
}
|
||||||
rng.churnSeed(i);
|
const cycleStart = vendorInfo.cycleOffset + cycleIndex * vendorInfo.cycleDuration;
|
||||||
/*for (let j = -1; j != rawItem.duplicates; ++j)*/ {
|
for (const rawItem of offersToAdd) {
|
||||||
|
const durationHoursRange = toRange(rawItem.durationHours);
|
||||||
|
const expiry =
|
||||||
|
cycleStart +
|
||||||
|
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
|
||||||
const item: IItemManifestPreprocessed = {
|
const item: IItemManifestPreprocessed = {
|
||||||
StoreItem: rawItem.storeItem,
|
StoreItem: rawItem.storeItem,
|
||||||
ItemPrices: rawItem.itemPrices!.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||||
Bin: "BIN_" + rawItem.bin,
|
Bin: "BIN_" + rawItem.bin,
|
||||||
QuantityMultiplier: 1,
|
QuantityMultiplier: 1,
|
||||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } },
|
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||||
AllowMultipurchase: false,
|
AllowMultipurchase: false,
|
||||||
Id: {
|
Id: {
|
||||||
$oid:
|
$oid:
|
||||||
i.toString(16).padStart(8, "0") +
|
Math.trunc(cycleStart / 1000)
|
||||||
|
.toString(16)
|
||||||
|
.padStart(8, "0") +
|
||||||
vendorInfo._id.$oid.substring(8, 16) +
|
vendorInfo._id.$oid.substring(8, 16) +
|
||||||
rng.randomInt(0, 0xffffffff).toString(16).padStart(8, "0")
|
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0") +
|
||||||
|
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (rawItem.numRandomItemPrices) {
|
||||||
|
item.ItemPrices = [];
|
||||||
|
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
|
||||||
|
let itemPrice: { type: string; count: IRange };
|
||||||
|
do {
|
||||||
|
itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin]);
|
||||||
|
} while (item.ItemPrices.find(x => x.ItemType == itemPrice.type));
|
||||||
|
item.ItemPrices.push({
|
||||||
|
ItemType: itemPrice.type,
|
||||||
|
ItemCount: rng.randomInt(itemPrice.count.minValue, itemPrice.count.maxValue),
|
||||||
|
ProductCategory: "MiscItems"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rawItem.credits) {
|
||||||
|
const value =
|
||||||
|
typeof rawItem.credits == "number"
|
||||||
|
? rawItem.credits
|
||||||
|
: rng.randomInt(
|
||||||
|
rawItem.credits.minValue / rawItem.credits.step,
|
||||||
|
rawItem.credits.maxValue / rawItem.credits.step
|
||||||
|
) * rawItem.credits.step;
|
||||||
|
item.RegularPrice = [value, value];
|
||||||
|
}
|
||||||
if (vendorInfo.RandomSeedType) {
|
if (vendorInfo.RandomSeedType) {
|
||||||
item.LocTagRandSeed =
|
item.LocTagRandSeed = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||||
(BigInt(rng.randomInt(0, 0xffffffff)) << 32n) | BigInt(rng.randomInt(0, 0xffffffff));
|
if (vendorInfo.RandomSeedType == "VRST_WEAPON") {
|
||||||
}
|
const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||||
items.push(item);
|
item.LocTagRandSeed = (BigInt(highDword) << 32n) | BigInt(item.LocTagRandSeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
processed.ItemManifest.push(item);
|
||||||
const { cycleStart, cycleDuration, ...clientVendorInfo } = vendorInfo;
|
}
|
||||||
|
|
||||||
|
// Update vendor expiry
|
||||||
|
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
|
||||||
|
for (const offer of processed.ItemManifest) {
|
||||||
|
const offerExpiry = parseInt(offer.Expiry.$date.$numberLong);
|
||||||
|
if (soonestOfferExpiry > offerExpiry) {
|
||||||
|
soonestOfferExpiry = offerExpiry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed.Expiry.$date.$numberLong = soonestOfferExpiry.toString();
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
VendorInfo: {
|
VendorInfo: processed
|
||||||
...clientVendorInfo,
|
|
||||||
ItemManifest: items,
|
|
||||||
Expiry: { $date: { $numberLong: soonestOfferExpiry.toString() } }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ import { CRng } from "@/src/services/rngService";
|
|||||||
import { eMissionType, ExportNightwave, ExportRegions, IRegion } from "warframe-public-export-plus";
|
import { eMissionType, ExportNightwave, ExportRegions, IRegion } from "warframe-public-export-plus";
|
||||||
import {
|
import {
|
||||||
ICalendarDay,
|
ICalendarDay,
|
||||||
|
ICalendarEvent,
|
||||||
ICalendarSeason,
|
ICalendarSeason,
|
||||||
ILiteSortie,
|
ILiteSortie,
|
||||||
ISeasonChallenge,
|
ISeasonChallenge,
|
||||||
@ -186,10 +187,12 @@ const pushSyndicateMissions = (
|
|||||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
for (const [key, value] of Object.entries(ExportRegions)) {
|
||||||
if (
|
if (
|
||||||
!isArchwingMission(value) &&
|
!isArchwingMission(value) &&
|
||||||
value.systemIndex != 23 && // no 1999 stuff
|
!value.questReq && // Exclude zariman, murmor, and 1999 stuff
|
||||||
|
!value.hidden && // Exclude the index
|
||||||
|
!value.darkSectorData && // Exclude dark sectors
|
||||||
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
|
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
|
||||||
value.missionIndex != 23 && // no junctions
|
value.missionIndex != 23 && // no junctions
|
||||||
value.missionIndex <= 28 // no railjack or some such
|
value.missionIndex < 28 // no open worlds, railjack, etc
|
||||||
) {
|
) {
|
||||||
nodeOptions.push(key);
|
nodeOptions.push(key);
|
||||||
}
|
}
|
||||||
@ -464,8 +467,8 @@ const birthdays: number[] = [
|
|||||||
|
|
||||||
const getCalendarSeason = (week: number): ICalendarSeason => {
|
const getCalendarSeason = (week: number): ICalendarSeason => {
|
||||||
const seasonIndex = week % 4;
|
const seasonIndex = week % 4;
|
||||||
const seasonDay1 = seasonIndex * 90 + 1;
|
const seasonDay1 = [1, 91, 182, 274][seasonIndex];
|
||||||
const seasonDay91 = seasonIndex * 90 + 91;
|
const seasonDay91 = seasonDay1 + 90;
|
||||||
const eventDays: ICalendarDay[] = [];
|
const eventDays: ICalendarDay[] = [];
|
||||||
for (const day of birthdays) {
|
for (const day of birthdays) {
|
||||||
if (day < seasonDay1) {
|
if (day < seasonDay1) {
|
||||||
@ -524,8 +527,12 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
|
|||||||
challengeDay = rng.randomInt(chunkDay1, chunkDay13);
|
challengeDay = rng.randomInt(chunkDay1, chunkDay13);
|
||||||
} while (birthdays.indexOf(challengeDay) != -1);
|
} while (birthdays.indexOf(challengeDay) != -1);
|
||||||
|
|
||||||
const challengeIndex = rng.randomInt(0, challenges.length - 1);
|
let challengeIndex;
|
||||||
const challenge = challenges[challengeIndex];
|
let challenge;
|
||||||
|
do {
|
||||||
|
challengeIndex = rng.randomInt(0, challenges.length - 1);
|
||||||
|
challenge = challenges[challengeIndex];
|
||||||
|
} while (i < 2 && !challenge.endsWith("Easy")); // First 2 challenges should be easy
|
||||||
challenges.splice(challengeIndex, 1);
|
challenges.splice(challengeIndex, 1);
|
||||||
|
|
||||||
//logger.debug(`challenge on day ${challengeDay}`);
|
//logger.debug(`challenge on day ${challengeDay}`);
|
||||||
@ -572,69 +579,100 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
|
|||||||
"/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalViolet"
|
"/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalViolet"
|
||||||
];
|
];
|
||||||
for (let i = 0; i != rewardRanges.length - 1; ++i) {
|
for (let i = 0; i != rewardRanges.length - 1; ++i) {
|
||||||
|
const events: ICalendarEvent[] = [];
|
||||||
|
for (let j = 0; j != 2; ++j) {
|
||||||
const rewardIndex = rng.randomInt(0, rewards.length - 1);
|
const rewardIndex = rng.randomInt(0, rewards.length - 1);
|
||||||
const reward = rewards[rewardIndex];
|
events.push({ type: "CET_REWARD", reward: rewards[rewardIndex] });
|
||||||
rewards.splice(rewardIndex, 1);
|
rewards.splice(rewardIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
//logger.debug(`trying to fit a reward between day ${rewardRanges[i]} and ${rewardRanges[i + 1]}`);
|
//logger.debug(`trying to fit rewards between day ${rewardRanges[i]} and ${rewardRanges[i + 1]}`);
|
||||||
let day: number;
|
let day: number;
|
||||||
do {
|
do {
|
||||||
day = rng.randomInt(rewardRanges[i] + 1, rewardRanges[i + 1] - 1);
|
day = rng.randomInt(rewardRanges[i] + 1, rewardRanges[i + 1] - 1);
|
||||||
} while (eventDays.find(x => x.day == day));
|
} while (eventDays.find(x => x.day == day));
|
||||||
eventDays.push({ day, events: [{ type: "CET_REWARD", reward }] });
|
eventDays.push({ day, events });
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgrades = [
|
const upgradesByHexMember = [
|
||||||
"/Lotus/Upgrades/Calendar/MeleeCritChance",
|
[
|
||||||
"/Lotus/Upgrades/Calendar/MeleeAttackSpeed",
|
"/Lotus/Upgrades/Calendar/AttackAndMovementSpeedOnCritMelee",
|
||||||
"/Lotus/Upgrades/Calendar/EnergyOrbToAbilityRange",
|
"/Lotus/Upgrades/Calendar/ElectricalDamageOnBulletJump",
|
||||||
|
"/Lotus/Upgrades/Calendar/ElectricDamagePerDistance",
|
||||||
|
"/Lotus/Upgrades/Calendar/ElectricStatusDamageAndChance",
|
||||||
|
"/Lotus/Upgrades/Calendar/OvershieldCap",
|
||||||
|
"/Lotus/Upgrades/Calendar/SpeedBuffsWhenAirborne"
|
||||||
|
],
|
||||||
|
[
|
||||||
"/Lotus/Upgrades/Calendar/AbilityStrength",
|
"/Lotus/Upgrades/Calendar/AbilityStrength",
|
||||||
|
"/Lotus/Upgrades/Calendar/EnergyOrbToAbilityRange",
|
||||||
|
"/Lotus/Upgrades/Calendar/MagnetStatusPull",
|
||||||
|
"/Lotus/Upgrades/Calendar/MagnitizeWithinRangeEveryXCasts",
|
||||||
|
"/Lotus/Upgrades/Calendar/PowerStrengthAndEfficiencyPerEnergySpent",
|
||||||
|
"/Lotus/Upgrades/Calendar/SharedFreeAbilityEveryXCasts"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Calendar/EnergyWavesOnCombo",
|
||||||
|
"/Lotus/Upgrades/Calendar/FinisherChancePerComboMultiplier",
|
||||||
|
"/Lotus/Upgrades/Calendar/MeleeAttackSpeed",
|
||||||
|
"/Lotus/Upgrades/Calendar/MeleeCritChance",
|
||||||
|
"/Lotus/Upgrades/Calendar/MeleeSlideFowardMomentumOnEnemyHit",
|
||||||
|
"/Lotus/Upgrades/Calendar/RadialJavelinOnHeavy"
|
||||||
|
],
|
||||||
|
[
|
||||||
"/Lotus/Upgrades/Calendar/Armor",
|
"/Lotus/Upgrades/Calendar/Armor",
|
||||||
"/Lotus/Upgrades/Calendar/RadiationProcOnTakeDamage",
|
"/Lotus/Upgrades/Calendar/CloneActiveCompanionForEnergySpent",
|
||||||
"/Lotus/Upgrades/Calendar/CompanionDamage",
|
"/Lotus/Upgrades/Calendar/CompanionDamage",
|
||||||
|
"/Lotus/Upgrades/Calendar/CompanionsBuffNearbyPlayer",
|
||||||
|
"/Lotus/Upgrades/Calendar/CompanionsRadiationChance",
|
||||||
|
"/Lotus/Upgrades/Calendar/RadiationProcOnTakeDamage",
|
||||||
|
"/Lotus/Upgrades/Calendar/ReviveEnemyAsSpectreOnKill"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Calendar/EnergyOrbsGrantShield",
|
||||||
|
"/Lotus/Upgrades/Calendar/EnergyRestoration",
|
||||||
|
"/Lotus/Upgrades/Calendar/ExplodingHealthOrbs",
|
||||||
|
"/Lotus/Upgrades/Calendar/GenerateOmniOrbsOnWeakKill",
|
||||||
|
"/Lotus/Upgrades/Calendar/HealingEffects",
|
||||||
|
"/Lotus/Upgrades/Calendar/OrbsDuplicateOnPickup"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Calendar/BlastEveryXShots",
|
||||||
"/Lotus/Upgrades/Calendar/GasChanceToPrimaryAndSecondary",
|
"/Lotus/Upgrades/Calendar/GasChanceToPrimaryAndSecondary",
|
||||||
|
"/Lotus/Upgrades/Calendar/GuidingMissilesChance",
|
||||||
"/Lotus/Upgrades/Calendar/MagazineCapacity",
|
"/Lotus/Upgrades/Calendar/MagazineCapacity",
|
||||||
"/Lotus/Upgrades/Calendar/PunchToPrimary",
|
"/Lotus/Upgrades/Calendar/PunchToPrimary",
|
||||||
"/Lotus/Upgrades/Calendar/HealingEffects",
|
|
||||||
"/Lotus/Upgrades/Calendar/EnergyRestoration",
|
|
||||||
"/Lotus/Upgrades/Calendar/OvershieldCap",
|
|
||||||
"/Lotus/Upgrades/Calendar/ElectricStatusDamageAndChance",
|
|
||||||
"/Lotus/Upgrades/Calendar/FinisherChancePerComboMultiplier",
|
|
||||||
"/Lotus/Upgrades/Calendar/MagnetStatusPull",
|
|
||||||
"/Lotus/Upgrades/Calendar/CompanionsBuffNearbyPlayer",
|
|
||||||
"/Lotus/Upgrades/Calendar/StatusChancePerAmmoSpent",
|
|
||||||
"/Lotus/Upgrades/Calendar/OrbsDuplicateOnPickup",
|
|
||||||
"/Lotus/Upgrades/Calendar/AttackAndMovementSpeedOnCritMelee",
|
|
||||||
"/Lotus/Upgrades/Calendar/RadialJavelinOnHeavy",
|
|
||||||
"/Lotus/Upgrades/Calendar/MagnitizeWithinRangeEveryXCasts",
|
|
||||||
"/Lotus/Upgrades/Calendar/CompanionsRadiationChance",
|
|
||||||
"/Lotus/Upgrades/Calendar/BlastEveryXShots",
|
|
||||||
"/Lotus/Upgrades/Calendar/GenerateOmniOrbsOnWeakKill",
|
|
||||||
"/Lotus/Upgrades/Calendar/ElectricDamagePerDistance",
|
|
||||||
"/Lotus/Upgrades/Calendar/MeleeSlideFowardMomentumOnEnemyHit",
|
|
||||||
"/Lotus/Upgrades/Calendar/SharedFreeAbilityEveryXCasts",
|
|
||||||
"/Lotus/Upgrades/Calendar/ReviveEnemyAsSpectreOnKill",
|
|
||||||
"/Lotus/Upgrades/Calendar/RefundBulletOnStatusProc",
|
"/Lotus/Upgrades/Calendar/RefundBulletOnStatusProc",
|
||||||
"/Lotus/Upgrades/Calendar/ExplodingHealthOrbs",
|
"/Lotus/Upgrades/Calendar/StatusChancePerAmmoSpent"
|
||||||
"/Lotus/Upgrades/Calendar/SpeedBuffsWhenAirborne",
|
]
|
||||||
"/Lotus/Upgrades/Calendar/EnergyWavesOnCombo",
|
|
||||||
"/Lotus/Upgrades/Calendar/PowerStrengthAndEfficiencyPerEnergySpent",
|
|
||||||
"/Lotus/Upgrades/Calendar/CloneActiveCompanionForEnergySpent",
|
|
||||||
"/Lotus/Upgrades/Calendar/GuidingMissilesChance",
|
|
||||||
"/Lotus/Upgrades/Calendar/EnergyOrbsGrantShield",
|
|
||||||
"/Lotus/Upgrades/Calendar/ElectricalDamageOnBulletJump"
|
|
||||||
];
|
];
|
||||||
for (let i = 0; i != upgradeRanges.length - 1; ++i) {
|
for (let i = 0; i != upgradeRanges.length - 1; ++i) {
|
||||||
const upgradeIndex = rng.randomInt(0, upgrades.length - 1);
|
// Pick 3 unique hex members
|
||||||
const upgrade = upgrades[upgradeIndex];
|
const hexMembersPickedForThisDay: number[] = [];
|
||||||
upgrades.splice(upgradeIndex, 1);
|
for (let j = 0; j != 3; ++j) {
|
||||||
|
let hexMemberIndex: number;
|
||||||
|
do {
|
||||||
|
hexMemberIndex = rng.randomInt(0, upgradesByHexMember.length - 1);
|
||||||
|
} while (hexMembersPickedForThisDay.indexOf(hexMemberIndex) != -1);
|
||||||
|
hexMembersPickedForThisDay.push(hexMemberIndex);
|
||||||
|
}
|
||||||
|
hexMembersPickedForThisDay.sort(); // Always present them in the same order
|
||||||
|
|
||||||
//logger.debug(`trying to fit an upgrade between day ${upgradeRanges[i]} and ${upgradeRanges[i + 1]}`);
|
// For each hex member, pick an upgrade that was not yet picked this season.
|
||||||
|
const events: ICalendarEvent[] = [];
|
||||||
|
for (const hexMemberIndex of hexMembersPickedForThisDay) {
|
||||||
|
const upgrades = upgradesByHexMember[hexMemberIndex];
|
||||||
|
const upgradeIndex = rng.randomInt(0, upgrades.length - 1);
|
||||||
|
events.push({ type: "CET_UPGRADE", upgrade: upgrades[upgradeIndex] });
|
||||||
|
upgrades.splice(upgradeIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//logger.debug(`trying to fit upgrades between day ${upgradeRanges[i]} and ${upgradeRanges[i + 1]}`);
|
||||||
let day: number;
|
let day: number;
|
||||||
do {
|
do {
|
||||||
day = rng.randomInt(upgradeRanges[i] + 1, upgradeRanges[i + 1] - 1);
|
day = rng.randomInt(upgradeRanges[i] + 1, upgradeRanges[i + 1] - 1);
|
||||||
} while (eventDays.find(x => x.day == day));
|
} while (eventDays.find(x => x.day == day));
|
||||||
eventDays.push({ day, events: [{ type: "CET_UPGRADE", upgrade }] });
|
eventDays.push({ day, events });
|
||||||
}
|
}
|
||||||
|
|
||||||
eventDays.sort((a, b) => a.day - b.day);
|
eventDays.sort((a, b) => a.day - b.day);
|
||||||
@ -645,7 +683,7 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
|
|||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
Activation: { $date: { $numberLong: weekStart.toString() } },
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
||||||
Days: eventDays,
|
Days: eventDays,
|
||||||
Season: ["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"][seasonIndex],
|
Season: (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[seasonIndex],
|
||||||
YearIteration: Math.trunc(week / 4),
|
YearIteration: Math.trunc(week / 4),
|
||||||
Version: 19,
|
Version: 19,
|
||||||
UpgradeAvaliabilityRequirements: ["/Lotus/Upgrades/Calendar/1999UpgradeApplicationRequirement"]
|
UpgradeAvaliabilityRequirements: ["/Lotus/Upgrades/Calendar/1999UpgradeApplicationRequirement"]
|
||||||
|
@ -134,7 +134,7 @@ export const equipmentKeys = [
|
|||||||
export type TEquipmentKey = (typeof equipmentKeys)[number];
|
export type TEquipmentKey = (typeof equipmentKeys)[number];
|
||||||
|
|
||||||
export interface IDuviriInfo {
|
export interface IDuviriInfo {
|
||||||
Seed: number;
|
Seed: bigint;
|
||||||
NumCompletions: number;
|
NumCompletions: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +202,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
|||||||
Mailbox?: IMailboxClient;
|
Mailbox?: IMailboxClient;
|
||||||
SubscribedToEmails: number;
|
SubscribedToEmails: number;
|
||||||
Created: IMongoDate;
|
Created: IMongoDate;
|
||||||
RewardSeed: number | bigint;
|
RewardSeed: bigint;
|
||||||
RegularCredits: number;
|
RegularCredits: number;
|
||||||
PremiumCredits: number;
|
PremiumCredits: number;
|
||||||
PremiumCreditsFree: number;
|
PremiumCreditsFree: number;
|
||||||
@ -353,7 +353,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
|||||||
DeathSquadable: boolean;
|
DeathSquadable: boolean;
|
||||||
EndlessXP?: IEndlessXpProgress[];
|
EndlessXP?: IEndlessXpProgress[];
|
||||||
DialogueHistory?: IDialogueHistoryClient;
|
DialogueHistory?: IDialogueHistoryClient;
|
||||||
CalendarProgress: ICalendarProgress;
|
CalendarProgress?: ICalendarProgress;
|
||||||
SongChallenges?: ISongChallenge[];
|
SongChallenges?: ISongChallenge[];
|
||||||
EntratiVaultCountLastPeriod?: number;
|
EntratiVaultCountLastPeriod?: number;
|
||||||
EntratiVaultCountResetDate?: IMongoDate;
|
EntratiVaultCountResetDate?: IMongoDate;
|
||||||
@ -1193,17 +1193,18 @@ export interface IMarker {
|
|||||||
z: number;
|
z: number;
|
||||||
showInHud: boolean;
|
showInHud: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISeasonProgress {
|
export interface ISeasonProgress {
|
||||||
SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||||
LastCompletedDayIdx: number;
|
LastCompletedDayIdx: number;
|
||||||
LastCompletedChallengeDayIdx: number;
|
LastCompletedChallengeDayIdx: number;
|
||||||
ActivatedChallenges: unknown[];
|
ActivatedChallenges: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICalendarProgress {
|
export interface ICalendarProgress {
|
||||||
Version: number;
|
Version: number;
|
||||||
Iteration: number;
|
Iteration: number;
|
||||||
YearProgress: { Upgrades: unknown[] };
|
YearProgress: { Upgrades: string[] };
|
||||||
SeasonProgress: ISeasonProgress;
|
SeasonProgress: ISeasonProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ export type IMissionInventoryUpdateRequest = {
|
|||||||
|
|
||||||
SyndicateId?: string;
|
SyndicateId?: string;
|
||||||
SortieId?: string;
|
SortieId?: string;
|
||||||
|
CalendarProgress?: { challenge: string }[];
|
||||||
SeasonChallengeCompletions?: ISeasonChallenge[];
|
SeasonChallengeCompletions?: ISeasonChallenge[];
|
||||||
AffiliationChanges?: IAffiliationChange[];
|
AffiliationChanges?: IAffiliationChange[];
|
||||||
crossPlaySetting?: string;
|
crossPlaySetting?: string;
|
||||||
@ -144,7 +145,7 @@ export interface IRewardInfo {
|
|||||||
invasionId?: string;
|
invasionId?: string;
|
||||||
invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
|
invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
|
||||||
sortieId?: string;
|
sortieId?: string;
|
||||||
sortieTag?: string;
|
sortieTag?: "Mission1" | "Mission2" | "Final";
|
||||||
sortiePrereqs?: string[];
|
sortiePrereqs?: string[];
|
||||||
VaultsCracked?: number; // for Spy missions
|
VaultsCracked?: number; // for Spy missions
|
||||||
rewardTier?: number;
|
rewardTier?: number;
|
||||||
|
@ -13,6 +13,7 @@ export interface IItemPricePreprocessed extends Omit<IItemPrice, "ItemType"> {
|
|||||||
export interface IItemManifest {
|
export interface IItemManifest {
|
||||||
StoreItem: string;
|
StoreItem: string;
|
||||||
ItemPrices?: IItemPrice[];
|
ItemPrices?: IItemPrice[];
|
||||||
|
RegularPrice?: number[];
|
||||||
Bin: string;
|
Bin: string;
|
||||||
QuantityMultiplier: number;
|
QuantityMultiplier: number;
|
||||||
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
|
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
|
||||||
|
@ -133,7 +133,7 @@ export interface ISeasonChallenge {
|
|||||||
export interface ICalendarSeason {
|
export interface ICalendarSeason {
|
||||||
Activation: IMongoDate;
|
Activation: IMongoDate;
|
||||||
Expiry: IMongoDate;
|
Expiry: IMongoDate;
|
||||||
Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"
|
Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||||
Days: ICalendarDay[];
|
Days: ICalendarDay[];
|
||||||
YearIteration: number;
|
YearIteration: number;
|
||||||
Version: number;
|
Version: number;
|
||||||
|
@ -1,248 +0,0 @@
|
|||||||
{
|
|
||||||
"VendorInfo": {
|
|
||||||
"_id": {
|
|
||||||
"$oid": "5be4a159b144f3cdf1c22efa"
|
|
||||||
},
|
|
||||||
"TypeName": "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorManifest",
|
|
||||||
"ItemManifest": [
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonD",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Gameplay/Venus/Resources/VenusCoconutItem",
|
|
||||||
"ItemCount": 5,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/Circuits",
|
|
||||||
"ItemCount": 3664,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [87300, 87300],
|
|
||||||
"Bin": "BIN_1",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 1881404827,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "670daf92d21f34757a5e73b4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleRareC",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/NeuralSensor",
|
|
||||||
"ItemCount": 1,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [53300, 53300],
|
|
||||||
"Bin": "BIN_2",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 1943984533,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "6710b5029e1a3080a65e73a7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonG",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/Salvage",
|
|
||||||
"ItemCount": 11540,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [27300, 27300],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 744199559,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "67112582cc115756985e73a4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonB",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishThermalLaserItem",
|
|
||||||
"ItemCount": 9,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [75800, 75800],
|
|
||||||
"Bin": "BIN_1",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 3744711432,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "670de7d28a6ec82cd25e73a2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonB",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/Rubedo",
|
|
||||||
"ItemCount": 3343,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [52200, 52200],
|
|
||||||
"Bin": "BIN_1",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 1579000687,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "670e58526171148e125e73ad"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonA",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Gameplay/Venus/Resources/CoolantItem",
|
|
||||||
"ItemCount": 9,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishAnoscopicSensorItem",
|
|
||||||
"ItemCount": 5,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [12400, 12400],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 3589081466,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "67112582cc115756985e73a5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonC",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Gems/Solaris/SolarisCommonOreBAlloyItem",
|
|
||||||
"ItemCount": 13,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [77500, 77500],
|
|
||||||
"Bin": "BIN_1",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 1510234814,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "670f0f21250ad046c35e73ee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleUncommonD",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Fish/Solaris/FishParts/CorpusFishParralelBiodeItem",
|
|
||||||
"ItemCount": 7,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/Gems/Solaris/SolarisCommonGemBCutItem",
|
|
||||||
"ItemCount": 12,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [94600, 94600],
|
|
||||||
"Bin": "BIN_1",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 4222095721,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "670f63827be40254f95e739d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"StoreItem": "/Lotus/Types/StoreItems/Packages/DebtTokenBundles/DebtTokenBundleCommonJ",
|
|
||||||
"ItemPrices": [
|
|
||||||
{
|
|
||||||
"ItemType": "/Lotus/Types/Items/MiscItems/Nanospores",
|
|
||||||
"ItemCount": 14830,
|
|
||||||
"ProductCategory": "MiscItems"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"RegularPrice": [25600, 25600],
|
|
||||||
"Bin": "BIN_0",
|
|
||||||
"QuantityMultiplier": 1,
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseQuantityLimit": 1,
|
|
||||||
"AllowMultipurchase": true,
|
|
||||||
"LocTagRandSeed": 2694388669,
|
|
||||||
"Id": {
|
|
||||||
"$oid": "67112582cc115756985e73a6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"PropertyTextHash": "A39621049CA3CA13761028CD21C239EF",
|
|
||||||
"RandomSeedType": "VRST_FLAVOUR_TEXT",
|
|
||||||
"Expiry": {
|
|
||||||
"$date": {
|
|
||||||
"$numberLong": "9999999000000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user