forked from OpenWF/SpaceNinjaServer
		
	feat(goals): cetus events (#2598)
Includes `Plague Star` and `Ghoul Purge`. Translation for webUI taken from game files. Re #1103 Reviewed-on: OpenWF/SpaceNinjaServer#2598 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									9129bdb5fc
								
							
						
					
					
						commit
						51c0ddda38
					
				@ -72,6 +72,8 @@
 | 
				
			|||||||
    "resourceBoost": false,
 | 
					    "resourceBoost": false,
 | 
				
			||||||
    "tennoLiveRelay": false,
 | 
					    "tennoLiveRelay": false,
 | 
				
			||||||
    "galleonOfGhouls": 0,
 | 
					    "galleonOfGhouls": 0,
 | 
				
			||||||
 | 
					    "ghoulEmergenceOverride": null,
 | 
				
			||||||
 | 
					    "plagueStarOverride": null,
 | 
				
			||||||
    "starDaysOverride": null,
 | 
					    "starDaysOverride": null,
 | 
				
			||||||
    "eidolonOverride": "",
 | 
					    "eidolonOverride": "",
 | 
				
			||||||
    "vallisOverride": "",
 | 
					    "vallisOverride": "",
 | 
				
			||||||
 | 
				
			|||||||
@ -84,6 +84,8 @@ export interface IConfig {
 | 
				
			|||||||
        tennoLiveRelay?: boolean;
 | 
					        tennoLiveRelay?: boolean;
 | 
				
			||||||
        baroTennoConRelay?: boolean;
 | 
					        baroTennoConRelay?: boolean;
 | 
				
			||||||
        galleonOfGhouls?: number;
 | 
					        galleonOfGhouls?: number;
 | 
				
			||||||
 | 
					        ghoulEmergenceOverride?: boolean;
 | 
				
			||||||
 | 
					        plagueStarOverride?: boolean;
 | 
				
			||||||
        starDaysOverride?: boolean;
 | 
					        starDaysOverride?: boolean;
 | 
				
			||||||
        eidolonOverride?: string;
 | 
					        eidolonOverride?: string;
 | 
				
			||||||
        vallisOverride?: string;
 | 
					        vallisOverride?: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -76,7 +76,7 @@ import {
 | 
				
			|||||||
} from "@/src/services/worldStateService";
 | 
					} from "@/src/services/worldStateService";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
 | 
					import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
 | 
				
			||||||
import { ISyndicateMissionInfo } from "@/src/types/worldStateTypes";
 | 
					import { IGoal, ISyndicateMissionInfo } from "@/src/types/worldStateTypes";
 | 
				
			||||||
import { fromOid } from "@/src/helpers/inventoryHelpers";
 | 
					import { fromOid } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
import { TAccountDocument } from "@/src/services/loginService";
 | 
					import { TAccountDocument } from "@/src/services/loginService";
 | 
				
			||||||
import { ITypeCount } from "@/src/types/commonTypes";
 | 
					import { ITypeCount } from "@/src/types/commonTypes";
 | 
				
			||||||
@ -1259,6 +1259,8 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AffiliationMods ??= [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
 | 
					    if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
 | 
				
			||||||
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
        const [jobType, unkIndex, hubNode, syndicateMissionId] = rewardInfo.jobId.split("_");
 | 
					        const [jobType, unkIndex, hubNode, syndicateMissionId] = rewardInfo.jobId.split("_");
 | 
				
			||||||
@ -1266,9 +1268,29 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        if (syndicateMissionId) {
 | 
					        if (syndicateMissionId) {
 | 
				
			||||||
            pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
					            pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
 | 
					        let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
 | 
				
			||||||
 | 
					            m => m._id.$oid === syndicateMissionId
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
 | 
				
			||||||
 | 
					                "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
 | 
				
			||||||
 | 
					            ].some(prefix => jobType.startsWith(prefix))
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            const { Goals } = getWorldState(undefined);
 | 
				
			||||||
 | 
					            syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
 | 
				
			||||||
 | 
					            if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (syndicateEntry && syndicateEntry.Jobs) {
 | 
					        if (syndicateEntry && syndicateEntry.Jobs) {
 | 
				
			||||||
            let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
 | 
					            let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
 | 
				
			||||||
 | 
					                    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
 | 
				
			||||||
 | 
					                ].some(prefix => jobType.startsWith(prefix))
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                currentJob = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
					            if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
				
			||||||
                if (
 | 
					                if (
 | 
				
			||||||
                    [
 | 
					                    [
 | 
				
			||||||
@ -1311,31 +1333,35 @@ export const addMissionRewards = async (
 | 
				
			|||||||
                    `Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
 | 
					                    `Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (rewardInfo.JobTier! >= 0) {
 | 
					                const specialCase = [
 | 
				
			||||||
 | 
					                    { endings: ["Heists/HeistProfitTakerBountyOne"], stage: 2, amount: 1000 },
 | 
				
			||||||
 | 
					                    { endings: ["Hunts/AllTeralystsHunt"], stage: 2, amount: 5000 },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        endings: [
 | 
				
			||||||
 | 
					                            "Hunts/TeralystHunt",
 | 
				
			||||||
 | 
					                            "Heists/HeistProfitTakerBountyTwo",
 | 
				
			||||||
 | 
					                            "Heists/HeistProfitTakerBountyThree",
 | 
				
			||||||
 | 
					                            "Heists/HeistProfitTakerBountyFour",
 | 
				
			||||||
 | 
					                            "Heists/HeistExploiterBountyOne"
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                        amount: 1000
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					                const specialCaseReward = specialCase.find(
 | 
				
			||||||
 | 
					                    rule =>
 | 
				
			||||||
 | 
					                        rule.endings.some(e => jobType.endsWith(e)) &&
 | 
				
			||||||
 | 
					                        (rule.stage === undefined || rewardInfo.JobStage === rule.stage)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (specialCaseReward) {
 | 
				
			||||||
 | 
					                    addStanding(inventory, syndicateEntry.Tag, specialCaseReward.amount, AffiliationMods);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
                    addStanding(
 | 
					                    addStanding(
 | 
				
			||||||
                        inventory,
 | 
					                        inventory,
 | 
				
			||||||
                        syndicateEntry.Tag,
 | 
					                        syndicateEntry.Tag,
 | 
				
			||||||
                        Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
 | 
					                        Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
 | 
				
			||||||
                        AffiliationMods
 | 
					                        AffiliationMods
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
 | 
					 | 
				
			||||||
                        addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (jobType.endsWith("Hunts/AllTeralystsHunt") && rewardInfo.JobStage === 2) {
 | 
					 | 
				
			||||||
                        addStanding(inventory, syndicateEntry.Tag, 5000, AffiliationMods);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            "Hunts/TeralystHunt",
 | 
					 | 
				
			||||||
                            "Heists/HeistProfitTakerBountyTwo",
 | 
					 | 
				
			||||||
                            "Heists/HeistProfitTakerBountyThree",
 | 
					 | 
				
			||||||
                            "Heists/HeistProfitTakerBountyFour",
 | 
					 | 
				
			||||||
                            "Heists/HeistExploiterBountyOne"
 | 
					 | 
				
			||||||
                        ].some(ending => jobType.endsWith(ending))
 | 
					 | 
				
			||||||
                    ) {
 | 
					 | 
				
			||||||
                        addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1672,7 +1698,19 @@ function getRandomMissionDrops(
 | 
				
			|||||||
                    if (syndicateMissionId) {
 | 
					                    if (syndicateMissionId) {
 | 
				
			||||||
                        pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
					                        pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
 | 
					                    let syndicateEntry: ISyndicateMissionInfo | IGoal | undefined = syndicateMissions.find(
 | 
				
			||||||
 | 
					                        m => m._id.$oid === syndicateMissionId
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    if (
 | 
				
			||||||
 | 
					                        [
 | 
				
			||||||
 | 
					                            "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
 | 
				
			||||||
 | 
					                            "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
 | 
				
			||||||
 | 
					                        ].some(prefix => jobType.startsWith(prefix))
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        const { Goals } = getWorldState(undefined);
 | 
				
			||||||
 | 
					                        syndicateEntry = Goals.find(m => m._id.$oid === syndicateMissionId);
 | 
				
			||||||
 | 
					                        if (syndicateEntry) syndicateEntry.Tag = syndicateEntry.JobAffiliationTag!;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    if (syndicateEntry && syndicateEntry.Jobs) {
 | 
					                    if (syndicateEntry && syndicateEntry.Jobs) {
 | 
				
			||||||
                        let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
 | 
					                        let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1757,6 +1795,14 @@ function getRandomMissionDrops(
 | 
				
			|||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (
 | 
				
			||||||
 | 
					                            [
 | 
				
			||||||
 | 
					                                "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
 | 
				
			||||||
 | 
					                                "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBounty"
 | 
				
			||||||
 | 
					                            ].some(prefix => jobType.startsWith(prefix))
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            job = syndicateEntry.Jobs.find(j => j.jobType === jobType)!;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        rewardManifests = [job.rewards];
 | 
					                        rewardManifests = [job.rewards];
 | 
				
			||||||
                        if (job.xpAmounts.length > 1) {
 | 
					                        if (job.xpAmounts.length > 1) {
 | 
				
			||||||
                            const curentStage = RewardInfo.JobStage! + 1;
 | 
					                            const curentStage = RewardInfo.JobStage! + 1;
 | 
				
			||||||
 | 
				
			|||||||
@ -131,6 +131,13 @@ const eidolonNarmerJobs: readonly string[] = [
 | 
				
			|||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyLib"
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyLib"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eidolonGhoulJobs: readonly string[] = [
 | 
				
			||||||
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyAss",
 | 
				
			||||||
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyExt",
 | 
				
			||||||
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyHunt",
 | 
				
			||||||
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/GhoulAlertBountyRes"
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const venusJobs: readonly string[] = [
 | 
					const venusJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobAmbush",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobAmbush",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobExcavation",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobExcavation",
 | 
				
			||||||
@ -1556,6 +1563,72 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const firstNovemberWeekday = new Date(Date.UTC(date.getUTCFullYear(), 10, 1)).getUTCDay();
 | 
				
			||||||
 | 
					    const firstNovemberMondayOffset = (8 - firstNovemberWeekday) % 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const plagueStarStart = Date.UTC(date.getUTCFullYear(), 10, firstNovemberMondayOffset + 1, 16);
 | 
				
			||||||
 | 
					    const plagueStarEnd = Date.UTC(date.getUTCFullYear(), 10, firstNovemberMondayOffset + 15, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const isPlagueStarActive = timeMs >= plagueStarStart && timeMs < plagueStarEnd;
 | 
				
			||||||
 | 
					    if (config.worldState?.plagueStarOverride ?? isPlagueStarActive) {
 | 
				
			||||||
 | 
					        worldState.Goals.push({
 | 
				
			||||||
 | 
					            _id: { $oid: "654a5058c757487cdb11824f" },
 | 
				
			||||||
 | 
					            Activation: {
 | 
				
			||||||
 | 
					                $date: {
 | 
				
			||||||
 | 
					                    $numberLong: config.worldState?.plagueStarOverride ? "1699372800000" : plagueStarStart.toString()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Expiry: {
 | 
				
			||||||
 | 
					                $date: {
 | 
				
			||||||
 | 
					                    $numberLong: config.worldState?.plagueStarOverride ? "2000000000000" : plagueStarEnd.toString()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Tag: "InfestedPlains",
 | 
				
			||||||
 | 
					            RegionIdx: 2,
 | 
				
			||||||
 | 
					            Faction: "FC_INFESTATION",
 | 
				
			||||||
 | 
					            Desc: "/Lotus/Language/InfestedPlainsEvent/InfestedPlainsBountyName",
 | 
				
			||||||
 | 
					            ToolTip: "/Lotus/Language/InfestedPlainsEvent/InfestedPlainsBountyDesc",
 | 
				
			||||||
 | 
					            Icon: "/Lotus/Materials/Emblems/PlagueStarEventBadge_e.png",
 | 
				
			||||||
 | 
					            JobAffiliationTag: "EventSyndicate",
 | 
				
			||||||
 | 
					            Jobs: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBounty",
 | 
				
			||||||
 | 
					                    rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/PlagueStarTableRewards",
 | 
				
			||||||
 | 
					                    minEnemyLevel: 15,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 25,
 | 
				
			||||||
 | 
					                    xpAmounts: [50, 300, 100, 575]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBountyAdvanced",
 | 
				
			||||||
 | 
					                    rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/PlagueStarTableRewards",
 | 
				
			||||||
 | 
					                    minEnemyLevel: 55,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 65,
 | 
				
			||||||
 | 
					                    xpAmounts: [200, 1000, 300, 1700],
 | 
				
			||||||
 | 
					                    requiredItems: [
 | 
				
			||||||
 | 
					                        "/Lotus/StoreItems/Types/Items/Eidolon/InfestedEventIngredient",
 | 
				
			||||||
 | 
					                        "/Lotus/StoreItems/Types/Items/Eidolon/InfestedEventClanIngredient"
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    useRequiredItemsAsMiscItemFee: true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: "/Lotus/Types/Gameplay/Eidolon/Jobs/Events/InfestedPlainsBountySteelPath",
 | 
				
			||||||
 | 
					                    rewards: "/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/PlagueStarTableSteelPathRewards",
 | 
				
			||||||
 | 
					                    minEnemyLevel: 100,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 110,
 | 
				
			||||||
 | 
					                    xpAmounts: [200, 1100, 400, 2100],
 | 
				
			||||||
 | 
					                    masteryReq: 10,
 | 
				
			||||||
 | 
					                    requiredItems: [
 | 
				
			||||||
 | 
					                        "/Lotus/StoreItems/Types/Items/Eidolon/InfestedEventIngredient",
 | 
				
			||||||
 | 
					                        "/Lotus/StoreItems/Types/Items/Eidolon/InfestedEventClanIngredient"
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    useRequiredItemsAsMiscItemFee: true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            Transmission: "/Lotus/Sounds/Dialog/PlainsMeteorLeadUp/LeadUp/DLeadUp0021Lotus",
 | 
				
			||||||
 | 
					            InstructionalItem: "/Lotus/Types/StoreItems/Packages/PlagueStarEventStoreItem"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Nightwave Challenges
 | 
					    // Nightwave Challenges
 | 
				
			||||||
    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
					    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
				
			||||||
    if (nightwaveSyndicateTag) {
 | 
					    if (nightwaveSyndicateTag) {
 | 
				
			||||||
@ -1626,6 +1699,103 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        pushClassicBounties(worldState.SyndicateMissions, bountyCycle);
 | 
					        pushClassicBounties(worldState.SyndicateMissions, bountyCycle);
 | 
				
			||||||
    } while (isBeforeNextExpectedWorldStateRefresh(timeMs, bountyCycleEnd) && ++bountyCycle);
 | 
					    } while (isBeforeNextExpectedWorldStateRefresh(timeMs, bountyCycleEnd) && ++bountyCycle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const ghoulsCycleDay = day % 21;
 | 
				
			||||||
 | 
					    const isGhoulEmergenceActive = ghoulsCycleDay >= 17 && ghoulsCycleDay <= 20; // 4 days for event and 17 days for break
 | 
				
			||||||
 | 
					    if (config.worldState?.ghoulEmergenceOverride ?? isGhoulEmergenceActive) {
 | 
				
			||||||
 | 
					        const ghoulPool = [...eidolonGhoulJobs];
 | 
				
			||||||
 | 
					        const pastGhoulPool = [...eidolonGhoulJobs];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const seed = new SRng(bountyCycle).randomInt(0, 100_000);
 | 
				
			||||||
 | 
					        const pastSeed = new SRng(bountyCycle - 1).randomInt(0, 100_000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const rng = new SRng(seed);
 | 
				
			||||||
 | 
					        const pastRng = new SRng(pastSeed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const activeStartDay = day - ghoulsCycleDay + 17;
 | 
				
			||||||
 | 
					        const activeEndDay = activeStartDay + 5;
 | 
				
			||||||
 | 
					        const dayWithFraction = (timeMs - EPOCH) / 86400000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const progress = (dayWithFraction - activeStartDay) / (activeEndDay - activeStartDay);
 | 
				
			||||||
 | 
					        const healthPct = 1 - Math.min(Math.max(progress, 0), 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        worldState.Goals.push({
 | 
				
			||||||
 | 
					            _id: { $oid: "687ebbe6d1d17841c9c59f38" },
 | 
				
			||||||
 | 
					            Activation: {
 | 
				
			||||||
 | 
					                $date: {
 | 
				
			||||||
 | 
					                    $numberLong: config.worldState?.ghoulEmergenceOverride
 | 
				
			||||||
 | 
					                        ? "1753204900185"
 | 
				
			||||||
 | 
					                        : Date.UTC(
 | 
				
			||||||
 | 
					                              date.getUTCFullYear(),
 | 
				
			||||||
 | 
					                              date.getUTCMonth(),
 | 
				
			||||||
 | 
					                              date.getUTCDate() + (day - ghoulsCycleDay + 17)
 | 
				
			||||||
 | 
					                          ).toString()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Expiry: {
 | 
				
			||||||
 | 
					                $date: {
 | 
				
			||||||
 | 
					                    $numberLong: config.worldState?.ghoulEmergenceOverride
 | 
				
			||||||
 | 
					                        ? "2000000000000"
 | 
				
			||||||
 | 
					                        : Date.UTC(
 | 
				
			||||||
 | 
					                              date.getUTCFullYear(),
 | 
				
			||||||
 | 
					                              date.getUTCMonth(),
 | 
				
			||||||
 | 
					                              date.getUTCDate() + (day - ghoulsCycleDay + 21)
 | 
				
			||||||
 | 
					                          ).toString()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HealthPct: config.worldState?.ghoulEmergenceOverride ? 1 : healthPct,
 | 
				
			||||||
 | 
					            VictimNode: "SolNode228",
 | 
				
			||||||
 | 
					            Regions: [2],
 | 
				
			||||||
 | 
					            Success: 0,
 | 
				
			||||||
 | 
					            Desc: "/Lotus/Language/GameModes/RecurringGhoulAlert",
 | 
				
			||||||
 | 
					            ToolTip: "/Lotus/Language/GameModes/RecurringGhoulAlertDesc",
 | 
				
			||||||
 | 
					            Icon: "/Lotus/Interface/Icons/Categories/IconGhouls256.png",
 | 
				
			||||||
 | 
					            Tag: "GhoulEmergence",
 | 
				
			||||||
 | 
					            JobAffiliationTag: "CetusSyndicate",
 | 
				
			||||||
 | 
					            JobCurrentVersion: {
 | 
				
			||||||
 | 
					                $oid: ((bountyCycle * 9000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Jobs: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: rng.randomElementPop(ghoulPool),
 | 
				
			||||||
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/GhoulBountyTableARewards`,
 | 
				
			||||||
 | 
					                    masteryReq: 1,
 | 
				
			||||||
 | 
					                    minEnemyLevel: 15,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 25,
 | 
				
			||||||
 | 
					                    xpAmounts: [270, 270, 270, 400] // not faithful
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: rng.randomElementPop(ghoulPool),
 | 
				
			||||||
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/GhoulBountyTableBRewards`,
 | 
				
			||||||
 | 
					                    masteryReq: 3,
 | 
				
			||||||
 | 
					                    minEnemyLevel: 40,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 50,
 | 
				
			||||||
 | 
					                    xpAmounts: [480, 480, 480, 710] // not faithful
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            JobPreviousVersion: {
 | 
				
			||||||
 | 
					                $oid: (((bountyCycle - 1) * 9000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            PreviousJobs: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: pastRng.randomElementPop(pastGhoulPool),
 | 
				
			||||||
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/GhoulBountyTableARewards`,
 | 
				
			||||||
 | 
					                    masteryReq: 1,
 | 
				
			||||||
 | 
					                    minEnemyLevel: 15,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 25,
 | 
				
			||||||
 | 
					                    xpAmounts: [270, 270, 270, 400] // not faithful
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    jobType: pastRng.randomElementPop(pastGhoulPool),
 | 
				
			||||||
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/GhoulBountyTableBRewards`,
 | 
				
			||||||
 | 
					                    masteryReq: 3,
 | 
				
			||||||
 | 
					                    minEnemyLevel: 40,
 | 
				
			||||||
 | 
					                    maxEnemyLevel: 50,
 | 
				
			||||||
 | 
					                    xpAmounts: [480, 480, 480, 710] // not faithful
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (config.worldState?.creditBoost) {
 | 
					    if (config.worldState?.creditBoost) {
 | 
				
			||||||
        worldState.GlobalUpgrades.push({
 | 
					        worldState.GlobalUpgrades.push({
 | 
				
			||||||
            _id: { $oid: "5b23106f283a555109666672" },
 | 
					            _id: { $oid: "5b23106f283a555109666672" },
 | 
				
			||||||
 | 
				
			|||||||
@ -37,19 +37,46 @@ export interface IGoal {
 | 
				
			|||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    Activation: IMongoDate;
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
    Expiry: IMongoDate;
 | 
					    Expiry: IMongoDate;
 | 
				
			||||||
    Count: number;
 | 
					    Count?: number;
 | 
				
			||||||
    Goal: number;
 | 
					    Goal?: number;
 | 
				
			||||||
    Success: number;
 | 
					    HealthPct?: number;
 | 
				
			||||||
    Personal: boolean;
 | 
					    Success?: number;
 | 
				
			||||||
 | 
					    Personal?: boolean;
 | 
				
			||||||
    Bounty?: boolean;
 | 
					    Bounty?: boolean;
 | 
				
			||||||
 | 
					    Faction?: string;
 | 
				
			||||||
    ClampNodeScores?: boolean;
 | 
					    ClampNodeScores?: boolean;
 | 
				
			||||||
    Desc: string;
 | 
					    Desc: string;
 | 
				
			||||||
    ToolTip?: string;
 | 
					    ToolTip?: string;
 | 
				
			||||||
 | 
					    Transmission?: string;
 | 
				
			||||||
 | 
					    InstructionalItem?: string;
 | 
				
			||||||
    Icon: string;
 | 
					    Icon: string;
 | 
				
			||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Node: string;
 | 
					    Node?: string;
 | 
				
			||||||
 | 
					    VictimNode?: string;
 | 
				
			||||||
 | 
					    RegionIdx?: number;
 | 
				
			||||||
 | 
					    Regions?: number[];
 | 
				
			||||||
    MissionKeyName?: string;
 | 
					    MissionKeyName?: string;
 | 
				
			||||||
    Reward?: IMissionReward;
 | 
					    Reward?: IMissionReward;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JobAffiliationTag?: string;
 | 
				
			||||||
 | 
					    Jobs?: ISyndicateJob[];
 | 
				
			||||||
 | 
					    PreviousJobs?: ISyndicateJob[];
 | 
				
			||||||
 | 
					    JobCurrentVersion?: IOid;
 | 
				
			||||||
 | 
					    JobPreviousVersion?: IOid;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ISyndicateJob {
 | 
				
			||||||
 | 
					    jobType?: string;
 | 
				
			||||||
 | 
					    rewards: string;
 | 
				
			||||||
 | 
					    masteryReq?: number;
 | 
				
			||||||
 | 
					    minEnemyLevel: number;
 | 
				
			||||||
 | 
					    maxEnemyLevel: number;
 | 
				
			||||||
 | 
					    xpAmounts: number[];
 | 
				
			||||||
 | 
					    endless?: boolean;
 | 
				
			||||||
 | 
					    locationTag?: string;
 | 
				
			||||||
 | 
					    isVault?: boolean;
 | 
				
			||||||
 | 
					    requiredItems?: string[];
 | 
				
			||||||
 | 
					    useRequiredItemsAsMiscItemFee?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ISyndicateMissionInfo {
 | 
					export interface ISyndicateMissionInfo {
 | 
				
			||||||
@ -59,17 +86,7 @@ export interface ISyndicateMissionInfo {
 | 
				
			|||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Seed: number;
 | 
					    Seed: number;
 | 
				
			||||||
    Nodes: string[];
 | 
					    Nodes: string[];
 | 
				
			||||||
    Jobs?: {
 | 
					    Jobs?: ISyndicateJob[];
 | 
				
			||||||
        jobType?: string;
 | 
					 | 
				
			||||||
        rewards: string;
 | 
					 | 
				
			||||||
        masteryReq: number;
 | 
					 | 
				
			||||||
        minEnemyLevel: number;
 | 
					 | 
				
			||||||
        maxEnemyLevel: number;
 | 
					 | 
				
			||||||
        xpAmounts: number[];
 | 
					 | 
				
			||||||
        endless?: boolean;
 | 
					 | 
				
			||||||
        locationTag?: string;
 | 
					 | 
				
			||||||
        isVault?: boolean;
 | 
					 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IGlobalUpgrade {
 | 
					export interface IGlobalUpgrade {
 | 
				
			||||||
 | 
				
			|||||||
@ -946,6 +946,22 @@
 | 
				
			|||||||
                                        <option value="3" data-loc="worldState_we3"></option>
 | 
					                                        <option value="3" data-loc="worldState_we3"></option>
 | 
				
			||||||
                                    </select>
 | 
					                                    </select>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="worldState.ghoulEmergenceOverride" data-loc="worldState_ghoulEmergence"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.ghoulEmergenceOverride" data-default="null">
 | 
				
			||||||
 | 
					                                        <option value="null" data-loc="normal"></option>
 | 
				
			||||||
 | 
					                                        <option value="true" data-loc="enabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="false" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="worldState.plagueStarOverride" data-loc="worldState_plagueStar"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.plagueStarOverride" data-default="null">
 | 
				
			||||||
 | 
					                                        <option value="null" data-loc="normal"></option>
 | 
				
			||||||
 | 
					                                        <option value="true" data-loc="enabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="false" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
                                <div class="form-group mt-2">
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
                                    <label class="form-label" for="worldState.starDaysOverride" data-loc="worldState_starDays"></label>
 | 
					                                    <label class="form-label" for="worldState.starDaysOverride" data-loc="worldState_starDays"></label>
 | 
				
			||||||
                                    <select class="form-control" id="worldState.starDaysOverride" data-default="null">
 | 
					                                    <select class="form-control" id="worldState.starDaysOverride" data-default="null">
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Baros TennoCon Relais`,
 | 
					    worldState_baroTennoConRelay: `Baros TennoCon Relais`,
 | 
				
			||||||
    worldState_starDays: `Sternen-Tage`,
 | 
					    worldState_starDays: `Sternen-Tage`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Galeone der Ghule`,
 | 
					    worldState_galleonOfGhouls: `Galeone der Ghule`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Ghul Ausrottung`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Plagenstern`,
 | 
				
			||||||
    enabled: `Aktiviert`,
 | 
					    enabled: `Aktiviert`,
 | 
				
			||||||
    disabled: `Deaktiviert`,
 | 
					    disabled: `Deaktiviert`,
 | 
				
			||||||
    worldState_we1: `Wochenende 1`,
 | 
					    worldState_we1: `Wochenende 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -245,6 +245,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Baro's TennoCon Relay`,
 | 
					    worldState_baroTennoConRelay: `Baro's TennoCon Relay`,
 | 
				
			||||||
    worldState_starDays: `Star Days`,
 | 
					    worldState_starDays: `Star Days`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Galleon of Ghouls`,
 | 
					    worldState_galleonOfGhouls: `Galleon of Ghouls`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Ghoul Purge`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Plague Star`,
 | 
				
			||||||
    enabled: `Enabled`,
 | 
					    enabled: `Enabled`,
 | 
				
			||||||
    disabled: `Disabled`,
 | 
					    disabled: `Disabled`,
 | 
				
			||||||
    worldState_we1: `Weekend 1`,
 | 
					    worldState_we1: `Weekend 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Repetidor de Baro de la TennoCon`,
 | 
					    worldState_baroTennoConRelay: `Repetidor de Baro de la TennoCon`,
 | 
				
			||||||
    worldState_starDays: `Días estelares`,
 | 
					    worldState_starDays: `Días estelares`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Galeón de Gules`,
 | 
					    worldState_galleonOfGhouls: `Galeón de Gules`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Purga de Gules`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Estrella Infestada`,
 | 
				
			||||||
    enabled: `Activado`,
 | 
					    enabled: `Activado`,
 | 
				
			||||||
    disabled: `Desactivado`,
 | 
					    disabled: `Desactivado`,
 | 
				
			||||||
    worldState_we1: `Semana 1`,
 | 
					    worldState_we1: `Semana 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `[UNTRANSLATED] Baro's TennoCon Relay`,
 | 
					    worldState_baroTennoConRelay: `[UNTRANSLATED] Baro's TennoCon Relay`,
 | 
				
			||||||
    worldState_starDays: `Jours Stellaires`,
 | 
					    worldState_starDays: `Jours Stellaires`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Galion des Goules`,
 | 
					    worldState_galleonOfGhouls: `Galion des Goules`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Purge des Goules`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Fléau Céleste`,
 | 
				
			||||||
    enabled: `[UNTRANSLATED] Enabled`,
 | 
					    enabled: `[UNTRANSLATED] Enabled`,
 | 
				
			||||||
    disabled: `Désactivé`,
 | 
					    disabled: `Désactivé`,
 | 
				
			||||||
    worldState_we1: `Weekend 1`,
 | 
					    worldState_we1: `Weekend 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Реле Баро TennoCon`,
 | 
					    worldState_baroTennoConRelay: `Реле Баро TennoCon`,
 | 
				
			||||||
    worldState_starDays: `Звёздные дни`,
 | 
					    worldState_starDays: `Звёздные дни`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Галеон Гулей`,
 | 
					    worldState_galleonOfGhouls: `Галеон Гулей`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Избавление от гулей`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Чумная звезда`,
 | 
				
			||||||
    enabled: `Включено`,
 | 
					    enabled: `Включено`,
 | 
				
			||||||
    disabled: `Отключено`,
 | 
					    disabled: `Отключено`,
 | 
				
			||||||
    worldState_we1: `Выходные 1`,
 | 
					    worldState_we1: `Выходные 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Реле Баро TennoCon`,
 | 
					    worldState_baroTennoConRelay: `Реле Баро TennoCon`,
 | 
				
			||||||
    worldState_starDays: `Зоряні дні`,
 | 
					    worldState_starDays: `Зоряні дні`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `Гульський Галеон`,
 | 
					    worldState_galleonOfGhouls: `Гульський Галеон`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `Зачищення від гулів`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `Морова зірка`,
 | 
				
			||||||
    enabled: `Увімкнено`,
 | 
					    enabled: `Увімкнено`,
 | 
				
			||||||
    disabled: `Вимкнено`,
 | 
					    disabled: `Вимкнено`,
 | 
				
			||||||
    worldState_we1: `Вихідні 1`,
 | 
					    worldState_we1: `Вихідні 1`,
 | 
				
			||||||
 | 
				
			|||||||
@ -246,6 +246,8 @@ dict = {
 | 
				
			|||||||
    worldState_baroTennoConRelay: `Baro的TennoCon中继站`,
 | 
					    worldState_baroTennoConRelay: `Baro的TennoCon中继站`,
 | 
				
			||||||
    worldState_starDays: `活动:星日`,
 | 
					    worldState_starDays: `活动:星日`,
 | 
				
			||||||
    worldState_galleonOfGhouls: `战术警报:尸鬼的帆船战舰`,
 | 
					    worldState_galleonOfGhouls: `战术警报:尸鬼的帆船战舰`,
 | 
				
			||||||
 | 
					    worldState_ghoulEmergence: `尸鬼净化`,
 | 
				
			||||||
 | 
					    worldState_plagueStar: `瘟疫之星`,
 | 
				
			||||||
    enabled: `启用`,
 | 
					    enabled: `启用`,
 | 
				
			||||||
    disabled: `关闭/取消配置`,
 | 
					    disabled: `关闭/取消配置`,
 | 
				
			||||||
    worldState_we1: `活动阶段:第一周`,
 | 
					    worldState_we1: `活动阶段:第一周`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user