feat: tactical alerts (#2607)
Some checks failed
Build Docker image / docker-arm64 (push) Waiting to run
Build / build (push) Has been cancelled
Build Docker image / docker-amd64 (push) Has been cancelled

Includes all `Tactical Alerts` since Star Chart 3.0 with exception:
`Snowday Showdown`
`Wolf Hunt (2019)` (couldn't find corresponded `EventNode` for that)
`Void Corruption` (that's goes into `Alerts`)
All `Warframe's Anniversary`

Re #1103

Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: #2607
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:
AMelonInsideLemon 2025-08-13 07:13:05 -07:00 committed by Sainan
parent 9cc0c76ef5
commit fd2ec696a0
17 changed files with 1100 additions and 23 deletions

View File

@ -71,10 +71,19 @@
"affinityBoost": false,
"resourceBoost": false,
"tennoLiveRelay": false,
"wolfHunt": false,
"longShadow": false,
"hallowedFlame": false,
"hallowedNightmares": false,
"hallowedNightmaresRewardsOverride": 0,
"proxyRebellion": false,
"proxyRebellionRewardsOverride": 0,
"galleonOfGhouls": 0,
"ghoulEmergenceOverride": null,
"plagueStarOverride": null,
"starDaysOverride": null,
"dogDaysOverride": null,
"dogDaysRewardsOverride": null,
"eidolonOverride": "",
"vallisOverride": "",
"duviriOverride": "",

View File

@ -13,7 +13,8 @@ import {
addItems,
combineInventoryChanges,
getEffectiveAvatarImageType,
getInventory
getInventory,
updateCurrency
} from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger";
import { ExportFlavour } from "warframe-public-export-plus";
@ -100,6 +101,9 @@ export const inboxController: RequestHandler = async (req, res) => {
}
}
}
if (message.RegularCredits) {
updateCurrency(inventory, -message.RegularCredits, false, inventoryChanges);
}
await inventory.save();
res.json({ InventoryChanges: inventoryChanges });
} else if (latestClientMessageId) {

View File

@ -47,6 +47,7 @@ export interface IMessage {
acceptAction?: string;
declineAction?: string;
hasAccountAction?: boolean;
RegularCredits?: number;
}
export interface Arg {
@ -139,7 +140,8 @@ const messageSchema = new Schema<IMessageDatabase>(
contextInfo: String,
acceptAction: String,
declineAction: String,
hasAccountAction: Boolean
hasAccountAction: Boolean,
RegularCredits: Number
},
{ id: false }
);

View File

@ -83,10 +83,19 @@ export interface IConfig {
resourceBoost?: boolean;
tennoLiveRelay?: boolean;
baroTennoConRelay?: boolean;
wolfHunt?: boolean;
longShadow?: boolean;
hallowedFlame?: boolean;
hallowedNightmares?: boolean;
hallowedNightmaresRewardsOverride?: number;
proxyRebellion?: boolean;
proxyRebellionRewardsOverride?: number;
galleonOfGhouls?: number;
ghoulEmergenceOverride?: boolean;
plagueStarOverride?: boolean;
starDaysOverride?: boolean;
dogDaysOverride?: boolean;
dogDaysRewardsOverride?: number;
eidolonOverride?: string;
vallisOverride?: string;
duviriOverride?: string;

View File

@ -50,7 +50,7 @@ import { getEntriesUnsafe } from "@/src/utils/ts-utils";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IMissionCredits, IMissionReward } from "@/src/types/missionTypes";
import { crackRelic } from "@/src/helpers/relicHelper";
import { createMessage } from "@/src/services/inboxService";
import { createMessage, IMessageCreationTemplate } from "@/src/services/inboxService";
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
@ -624,37 +624,93 @@ export const addMissionInventoryUpdates = async (
if (goal && goal.Personal) {
inventory.PersonalGoalProgress ??= [];
const goalProgress = inventory.PersonalGoalProgress.find(x => x.goalId.equals(goal._id.$oid));
if (goalProgress) {
goalProgress.Best = Math.max(goalProgress.Best, uploadProgress.Best);
goalProgress.Count += uploadProgress.Count;
} else {
if (!goalProgress) {
inventory.PersonalGoalProgress.push({
Best: uploadProgress.Best,
Count: uploadProgress.Count,
Tag: goal.Tag,
goalId: new Types.ObjectId(goal._id.$oid)
});
}
const currentNode = inventoryUpdates.RewardInfo!.node;
let currentMissionKey;
if (currentNode == goal.Node) {
currentMissionKey = goal.MissionKeyName;
} else if (goal.ConcurrentNodes && goal.ConcurrentMissionKeyNames) {
for (let i = 0; i < goal.ConcurrentNodes.length; i++) {
if (currentNode == goal.ConcurrentNodes[i]) {
currentMissionKey = goal.ConcurrentMissionKeyNames[i];
break;
}
}
}
if (currentMissionKey && currentMissionKey in goalMessagesByKey) {
const totalCount = (goalProgress?.Count ?? 0) + uploadProgress.Count;
let reward;
if (goal.InterimGoals && goal.InterimRewards) {
for (let i = 0; i < goal.InterimGoals.length; i++) {
if (
goal.InterimGoals[i] &&
goal.InterimGoals[i] <= totalCount &&
(!goalProgress || goalProgress.Count < goal.InterimGoals[i]) &&
goal.InterimRewards[i]
) {
reward = goal.InterimRewards[i];
break;
}
}
}
if (
goal.Reward &&
goal.Reward.items &&
goal.MissionKeyName &&
goal.MissionKeyName in goalMessagesByKey
!reward &&
goal.Goal &&
goal.Goal <= totalCount &&
(!goalProgress || goalProgress.Count < goal.Goal) &&
goal.Reward
) {
// Send reward via inbox
const info = goalMessagesByKey[goal.MissionKeyName];
await createMessage(inventory.accountOwnerId, [
{
reward = goal.Reward;
}
if (
!reward &&
goal.BonusGoal &&
goal.BonusGoal <= totalCount &&
(!goalProgress || goalProgress.Count < goal.BonusGoal) &&
goal.BonusReward
) {
reward = goal.BonusReward;
}
if (reward) {
if (currentMissionKey in goalMessagesByKey) {
// Send reward via inbox
const info = goalMessagesByKey[currentMissionKey];
const message: IMessageCreationTemplate = {
sndr: info.sndr,
msg: info.msg,
att: goal.Reward.items.map(x => (isStoreItem(x) ? fromStoreItem(x) : x)),
sub: info.sub,
icon: info.icon,
highPriority: true
};
if (reward.items) {
message.att = reward.items.map(x => (isStoreItem(x) ? fromStoreItem(x) : x));
}
]);
if (reward.countedItems) {
message.countedAtt = reward.countedItems;
}
if (reward.credits) {
message.RegularCredits = reward.credits;
}
await createMessage(inventory.accountOwnerId, [message]);
}
}
}
if (goalProgress) {
goalProgress.Best = Math.max(goalProgress.Best, uploadProgress.Best);
goalProgress.Count += uploadProgress.Count;
}
}
}
break;
@ -1011,8 +1067,16 @@ export const addMissionRewards = async (
if (rewardInfo.goalId) {
const goal = getWorldState().Goals.find(x => x._id.$oid == rewardInfo.goalId);
if (goal?.MissionKeyName) {
levelKeyName = goal.MissionKeyName;
if (goal) {
if (rewardInfo.node == goal.Node && goal.MissionKeyName) levelKeyName = goal.MissionKeyName;
if (goal.ConcurrentNodes && goal.ConcurrentMissionKeyNames) {
for (let i = 0; i < goal.ConcurrentNodes.length && i < goal.ConcurrentMissionKeyNames.length; i++) {
if (rewardInfo.node == goal.ConcurrentNodes[i]) {
levelKeyName = goal.ConcurrentMissionKeyNames[i];
break;
}
}
}
}
}
@ -2149,5 +2213,143 @@ const goalMessagesByKey: Record<string, { sndr: string; msg: string; sub: string
msg: "/Lotus/Language/Messages/GalleonRobbery2025RewardMsgC",
sub: "/Lotus/Language/Messages/GalleonRobbery2025MissionTitleC",
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png"
},
"/Lotus/Types/Keys/TacAlertKeyWaterFightA": {
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgA",
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectA",
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
},
"/Lotus/Types/Keys/TacAlertKeyWaterFightB": {
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgB",
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectB",
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
},
"/Lotus/Types/Keys/TacAlertKeyWaterFightC": {
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgC",
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectC",
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
},
"/Lotus/Types/Keys/TacAlertKeyWaterFightD": {
sndr: "/Lotus/Language/Bosses/BossKelaDeThaym",
msg: "/Lotus/Language/Inbox/WaterFightRewardMsgD",
sub: "/Lotus/Language/Inbox/WaterFightRewardSubjectD",
icon: "/Lotus/Interface/Icons/Npcs/Grineer/KelaDeThaym.png"
},
"/Lotus/Types/Keys/WolfTacAlertReduxA": {
sndr: "/Lotus/Language/Bosses/NoraNight",
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
},
"/Lotus/Types/Keys/WolfTacAlertReduxB": {
sndr: "/Lotus/Language/Bosses/NoraNight",
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
},
"/Lotus/Types/Keys/WolfTacAlertReduxD": {
sndr: "/Lotus/Language/Bosses/NoraNight",
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
},
"/Lotus/Types/Keys/WolfTacAlertReduxC": {
sndr: "/Lotus/Language/Bosses/NoraNight",
msg: "/Lotus/Language/Inbox/WolfTacAlertBody",
sub: "/Lotus/Language/Inbox/WolfTacAlertTitle",
icon: "/Lotus/Interface/Icons/Npcs/Seasonal/NoraNight.png"
},
"/Lotus/Types/Keys/LanternEndlessEventKeyA": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/LanternEndlessEventKeyB": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/LanternEndlessEventKeyD": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/LanternEndlessEventKeyC": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/GenericEventRewardMsgDesc",
sub: "/Lotus/Language/G1Quests/GenericTacAlertRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyHalloween": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBonusBody",
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBonusTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyHalloweenBonus": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBody",
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyHalloweenTimeAttack": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsBody",
sub: "/Lotus/Language/G1Quests/TacAlertHalloweenRewardsTitle",
icon: "/Lotus/Interface/Icons/Npcs/LotusVamp_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionOne": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionTwo": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionThree": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/RazorbackArmadaRewardBody",
sub: "/Lotus/Language/G1Quests/GenericTacAlertSmallRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionFour": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/GenericTacAlertBadgeRewardMsgDesc",
sub: "/Lotus/Language/G1Quests/GenericTacAlertBadgeRewardMsgTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchEasy": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/ProjectNightwatchRewardMsgA",
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionOneTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatch": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionTwoTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchHard": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionThreeTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
},
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchBonus": {
sndr: "/Lotus/Language/Bosses/Lotus",
msg: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionRewardBody",
sub: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertMissionFourTitle",
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png"
}
};

View File

@ -1392,6 +1392,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
Sorties: [],
LiteSorties: [],
ActiveMissions: [],
FlashSales: [],
GlobalUpgrades: [],
Invasions: [],
VoidTraders: [],
@ -1401,7 +1402,15 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
EndlessXpChoices: [],
KnownCalendarSeasons: [],
...staticWorldState,
SyndicateMissions: [...staticWorldState.SyndicateMissions]
SyndicateMissions: [...staticWorldState.SyndicateMissions],
InGameMarket: {
LandingPage: {
Categories: staticWorldState.InGameMarket.LandingPage.Categories.map(c => ({
...c,
Items: [...c.Items]
}))
}
}
};
// Old versions seem to really get hung up on not being able to load these.
@ -1526,7 +1535,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
Personal: true,
Bounty: true,
ClampNodeScores: true,
Node: "EventNode28",
Node: "EventNode28", // Incompatible with Wolf Hunt (2025)
MissionKeyName: "/Lotus/Types/Keys/GalleonRobberyAlertB",
Desc: "/Lotus/Language/Events/GalleonRobberyEventMissionTitle",
Icon: "/Lotus/Interface/Icons/Player/GalleonRobberiesEvent.png",
@ -1629,6 +1638,634 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
});
}
const firstAugustWeekday = new Date(Date.UTC(date.getUTCFullYear(), 7, 1)).getUTCDay();
const firstAugustWednesdayOffset = (3 - firstAugustWeekday + 7) % 7;
const dogDaysStart = Date.UTC(date.getUTCFullYear(), 7, 1 + firstAugustWednesdayOffset, 15);
const firstSeptemberWeekday = new Date(Date.UTC(date.getUTCFullYear(), 8, 1)).getUTCDay();
const firstSeptemberWednesdayOffset = (3 - firstSeptemberWeekday + 7) % 7;
const dogDaysEnd = Date.UTC(date.getUTCFullYear(), 8, 1 + firstSeptemberWednesdayOffset, 15);
const isDogDaysActive = timeMs >= dogDaysStart && timeMs < dogDaysEnd;
if (config.worldState?.dogDaysOverride ?? isDogDaysActive) {
const activationTimeStamp = config.worldState?.dogDaysOverride ? "1699372800000" : dogDaysStart.toString();
const expiryTimeStamp = config.worldState?.dogDaysOverride ? "2000000000000" : dogDaysEnd.toString();
const rewards = [
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Upgrades/Skins/Weapons/Redeemer/RedeemerRelayWaterSkin"]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileHydroidRelay"]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/RelayHydroidBobbleHead"]
},
{
items: [
"/Lotus/StoreItems/Types/Items/MiscItems/OrokinReactor",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
}
],
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Upgrades/Skins/Sigils/DogDays2023ASigil"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 25
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyBeachKavat"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 50
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyRucksackKubrow"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 75
}
]
},
{
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCleaningDroneBeachcomber"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 100
}
]
}
],
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/AvatarImageDogDays2024Glyph"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 25
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/DogDays2024Poster"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 50
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/DogDaysKubrowBadgeItem"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 75
}
]
},
{
items: ["/Lotus/StoreItems/Types/Items/ShipDecos/DogDays2024LisetPropCleaningDroneBeachcomber"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 100
}
]
}
],
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageDogDaysHydroidGlyph"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 25
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageDogDaysLokiGlyph"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 50
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageDogDaysNovaGlyph"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 75
}
]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageDogDaysValkyrGlyph"],
countedItems: [
{
ItemType: "/Lotus/Types/Items/MiscItems/WaterFightBucks",
ItemCount: 100
}
]
}
]
];
const year = config.worldState?.dogDaysRewardsOverride ?? 3;
worldState.Goals.push({
_id: {
$oid: ((dogDaysStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "c57487c3768936df"
},
Activation: { $date: { $numberLong: activationTimeStamp } },
Expiry: { $date: { $numberLong: expiryTimeStamp } },
Count: 0,
Goal: 100,
InterimGoals: [25, 50],
BonusGoal: 200,
Success: 0,
Personal: true,
Bounty: true,
ClampNodeScores: true,
Node: "EventNode25", // Incompatible with Hallowed Flame, Hallowed Nightmares
ConcurrentMissionKeyNames: [
"/Lotus/Types/Keys/TacAlertKeyWaterFightB",
"/Lotus/Types/Keys/TacAlertKeyWaterFightC",
"/Lotus/Types/Keys/TacAlertKeyWaterFightD"
],
ConcurrentNodeReqs: [25, 50, 100],
ConcurrentNodes: ["EventNode24", "EventNode34", "EventNode35"], // Incompatible with Hallowed Flame, Hallowed Nightmares
MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyWaterFightA",
Faction: "FC_CORPUS",
Desc: "/Lotus/Language/Alerts/TacAlertWaterFight",
Icon: "/Lotus/Interface/Icons/StoreIcons/Emblems/SplashEventIcon.png",
Tag: "WaterFight",
InterimRewards: rewards[year].slice(0, 2),
Reward: rewards[year][2],
BonusReward: rewards[year][3],
ScoreVar: "Team1Score",
NightLevel: "/Lotus/Levels/GrineerBeach/GrineerBeachEventNight.level"
});
const baseStoreItem = {
ShowInMarket: true,
HideFromMarket: false,
SupporterPack: false,
Discount: 0,
BogoBuy: 0,
BogoGet: 0,
StartDate: { $date: { $numberLong: activationTimeStamp } },
EndDate: { $date: { $numberLong: expiryTimeStamp } },
ProductExpiryOverride: { $date: { $numberLong: expiryTimeStamp } }
};
const storeItems = [
{
TypeName: "/Lotus/Types/StoreItems/Packages/WaterFightNoggleBundle",
PremiumOverride: 240,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFBeastMasterBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFChargerBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFEngineerBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFGruntBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/StoreItems/AvatarImages/ImagePopsicleGrineerPurple",
PremiumOverride: 0,
RegularOverride: 1
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFHealerBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFHeavyBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFHellionBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFSniperBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/Items/ShipDecos/Events/WFTankBobbleHead",
PremiumOverride: 35,
RegularOverride: 0
},
{
TypeName: "/Lotus/Types/StoreItems/SuitCustomizations/ColourPickerRollers",
PremiumOverride: 75,
RegularOverride: 0
}
];
worldState.FlashSales.push(...storeItems.map(item => ({ ...baseStoreItem, ...item })));
const seasonalItems = storeItems.map(item => item.TypeName);
const seasonalCategory = worldState.InGameMarket.LandingPage.Categories.find(c => c.CategoryName == "SEASONAL");
if (seasonalCategory) {
seasonalCategory.Items ??= [];
seasonalCategory.Items.push(...seasonalItems);
} else {
worldState.InGameMarket.LandingPage.Categories.push({
CategoryName: "SEASONAL",
Name: "/Lotus/Language/Store/SeasonalCategoryTitle",
Icon: "seasonal",
AddToMenu: true,
Items: seasonalItems
});
}
}
if (config.worldState?.wolfHunt) {
worldState.Goals.push({
_id: {
$oid: "67ed7672798d6466172e3b9d"
},
Activation: {
$date: {
$numberLong: "1743616800000"
}
},
Expiry: {
$date: {
$numberLong: "2000000000000"
}
},
Count: 0,
Goal: 3,
InterimGoals: [1, 2],
BonusGoal: 4,
Success: 0,
Personal: true,
Bounty: true,
ClampNodeScores: true,
Node: "EventNode29",
ConcurrentMissionKeyNames: [
"/Lotus/Types/Keys/WolfTacAlertReduxB",
"/Lotus/Types/Keys/WolfTacAlertReduxC",
"/Lotus/Types/Keys/WolfTacAlertReduxD"
],
ConcurrentNodeReqs: [1, 2, 3],
ConcurrentNodes: ["EventNode28", "EventNode39", "EventNode40"], // Incompatible with Galleon Of Ghouls
MissionKeyName: "/Lotus/Types/Keys/WolfTacAlertReduxA",
Faction: "FC_GRINEER",
Desc: "/Lotus/Language/Alerts/WolfAlert",
Icon: "/Lotus/Interface/Icons/Npcs/Seasonal/WolfStalker.png",
Tag: "WolfHuntRedux",
InterimRewards: [
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/ThrowingHammerHandle"]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/ThrowingHammerHead"]
}
],
Reward: {
credits: 50000,
items: ["/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/ThrowingHammerMotor"]
},
BonusReward: {
credits: 50000,
items: [
"/Lotus/StoreItems/Types/Recipes/Weapons/ThrowingHammerBlueprint",
"/Lotus/StoreItems/Types/Items/MiscItems/OrokinCatalyst",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
}
});
}
if (config.worldState?.hallowedFlame) {
worldState.Goals.push(
{
_id: { $oid: "5db305403d34b5158873519a" },
Activation: { $date: { $numberLong: "1699372800000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
Goal: 3,
InterimGoals: [1, 2],
Success: 0,
Personal: true,
Bounty: true,
ClampNodeScores: true,
Node: "EventNode24", // Incompatible with Hallowed Nightmares, Dog Days
ConcurrentMissionKeyNames: [
"/Lotus/Types/Keys/LanternEndlessEventKeyB",
"/Lotus/Types/Keys/LanternEndlessEventKeyC"
],
ConcurrentNodeReqs: [1, 2],
ConcurrentNodes: ["EventNode25", "EventNode34"], // Incompatible with Hallowed Nightmares, Dog Days
MissionKeyName: "/Lotus/Types/Keys/LanternEndlessEventKeyA",
Faction: "FC_INFESTATION",
Desc: "/Lotus/Language/Events/TacAlertHalloweenLantern",
Icon: "/Lotus/Interface/Icons/JackOLanternColour.png",
Tag: "Halloween19",
InterimRewards: [
{ items: ["/Lotus/StoreItems/Types/Items/MiscItems/OrokinCatalyst"] },
{ items: ["/Lotus/StoreItems/Types/Items/MiscItems/Forma"] }
],
Reward: {
items: ["/Lotus/StoreItems/Types/Items/MiscItems/FormaAura"]
}
},
{
_id: { $oid: "5db3054a3d34b5158873519c" },
Activation: { $date: { $numberLong: "1699372800000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
Goal: 900,
Success: 0,
Personal: true,
Bounty: true,
Best: true,
ClampNodeScores: true,
Node: "EventNode35",
MissionKeyName: "/Lotus/Types/Keys/LanternEndlessEventKeyD",
Faction: "FC_INFESTATION",
Desc: "/Lotus/Language/Events/TacAlertHalloweenLanternEndless",
Icon: "/Lotus/Interface/Icons/JackOLanternColour.png",
Tag: "Halloween19Endless",
PrereqGoalTags: ["Halloween19"],
Reward: {
items: [
"/Lotus/StoreItems/Upgrades/Skins/Effects/BatsEphemera",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
},
ScoreVar: "EndlessMissionTimeElapsed",
ScoreMaxTag: "Halloween19ScoreMax"
}
);
}
if (config.worldState?.hallowedNightmares) {
const rewards = [
// 2018
[
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Sigils/DotD2016Sigil"]
},
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Halloween/HalloweenDread"]
},
{
items: ["/Lotus/StoreItems/Types/Items/MiscItems/OrokinReactor"]
}
],
// 2016
[
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Sigils/OrokinCatalyst"]
},
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Sigils/DotD2016Sigil"]
},
{
items: [
"/Lotus/StoreItems/Types/Items/MiscItems/OrokinReactor",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
}
],
// 2015
[
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Sigils/OrokinCatalyst"]
},
{
items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"]
}
]
];
const year = config.worldState.hallowedNightmaresRewardsOverride ?? 0;
worldState.Goals.push({
_id: { $oid: "5bc9e8f7972d7d184c8398c9" },
Activation: { $date: { $numberLong: "1539972000000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
InterimGoals: [1],
Goal: 2,
Success: 0,
Personal: true,
Bounty: true,
Tag: "Halloween",
Faction: "FC_INFESTATION",
Desc: "/Lotus/Language/G1Quests/TacAlertHalloweenTitle",
ToolTip: "/Lotus/Language/G1Quests/TacAlertHalloweenToolTip",
Icon: "/Lotus/Interface/Icons/JackOLanternColour.png",
ClampNodeScores: true,
Node: "EventNode2",
MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyHalloween",
ConcurrentMissionKeyNames: ["/Lotus/Types/Keys/TacAlertKeyHalloweenBonus"],
ConcurrentNodeReqs: [1],
ConcurrentNodes: ["EventNode24"], // Incompatible with Hallowed Flame, Dog Days
InterimRewards: [rewards[year][0]],
Reward: rewards[year][1]
});
if (year != 2) {
worldState.Goals.push({
_id: { $oid: "5bca18b1e12d9e14a0b6ad27" },
Activation: { $date: { $numberLong: "1539972000000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
Goal: 666,
Success: 0,
Personal: true,
Bounty: true,
Best: true,
Tag: "Halloween",
PrereqGoalTags: ["Halloween"],
Faction: "FC_INFESTATION",
Desc: "Hallowed Nightmares - Time Attack",
ToolTip: "/Lotus/Language/G1Quests/TacAlertHalloweenToolTip",
Icon: "/Lotus/Interface/Icons/JackOLanternColour.png",
ClampNodeScores: true,
Node: "EventNode25", // Incompatible with Hallowed Flame, Dog Days
MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyHalloweenTimeAttack",
ScoreVar: "TimeAttackScore",
ScoreMaxTag: "Halloween16",
Reward: rewards[year][2]
});
}
}
if (config.worldState?.proxyRebellion) {
const rewards = [
// 2019
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/MiscItems/UtilityUnlocker"]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Upgrades/Mods/Randomized/RawPistolRandomMod"]
},
{
credits: 50000,
items: ["/Lotus/Types/StoreItems/Packages/EventCatalystReactorBundle"]
},
{
items: [
"/Lotus/StoreItems/Upgrades/Skins/Scarves/HornSkullScarf",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
}
],
// 2018
[
{
credits: 50000,
items: ["/Lotus/StoreItems/Upgrades/Mods/FusionBundles/NightwatchFusionBundle"]
},
{
credits: 50000,
items: ["/Lotus/StoreItems/Types/Items/MiscItems/UtilityUnlocker"]
},
{
credits: 50000,
items: ["/Lotus/Types/StoreItems/Packages/EventCatalystReactorBundle"]
},
{
items: [
"/Lotus/StoreItems/Upgrades/Skins/Sigils/EnergySigilA",
"/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
]
}
]
];
const year = config.worldState.proxyRebellionRewardsOverride ?? 0;
worldState.Goals.push({
_id: { $oid: "5b5743ac972d7d3ed0517b0d" },
Activation: { $date: { $numberLong: "1532714400000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
Goal: 3,
InterimGoals: [1, 2],
BonusGoal: 4,
Success: 0,
Personal: true,
Bounty: true,
ClampNodeScores: true,
Node: "EventNode18",
ConcurrentMissionKeyNames: [
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionTwo",
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionThree",
"/Lotus/Types/Keys/TacAlertKeyProxyRebellionFour"
],
ConcurrentNodeReqs: [1, 2, 3],
ConcurrentNodes: ["EventNode7", "EventNode4", "EventNode17"],
MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyProxyRebellionOne",
Faction: "FC_CORPUS",
Desc: "/Lotus/Language/Alerts/TacAlertProxyRebellion",
Icon: "/Lotus/Materials/Emblems/BountyBadge_e.png",
Tag: "ProxyRebellion",
InterimRewards: rewards[year].slice(0, 2),
Reward: rewards[year][2],
BonusReward: rewards[year][3]
});
}
if (config.worldState?.longShadow) {
worldState.Goals.push({
_id: { $oid: "5bc9e8f7272d5d184c8398c9" },
Activation: { $date: { $numberLong: "1539972000000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Count: 0,
InterimGoals: [1, 2],
Goal: 3,
BonusGoal: 4,
Success: 0,
Personal: true,
Bounty: true,
Tag: "NightwatchTacAlert",
Faction: "FC_GRINEER",
Desc: "/Lotus/Language/G1Quests/ProjectNightwatchTacAlertTitle",
Icon: "/Lotus/Materials/Emblems/BountyBadge_e.png",
ClampNodeScores: true,
Node: "EventNode9",
MissionKeyName: "/Lotus/Types/Keys/TacAlertKeyProjectNightwatchEasy",
ConcurrentMissionKeyNames: [
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatch",
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchHard",
"/Lotus/Types/Keys/TacAlertKeyProjectNightwatchBonus"
],
ConcurrentNodeReqs: [1, 2, 3],
ConcurrentNodes: ["SolNode136", "EventNode3", "EventNode0"],
InterimRewards: [
{
credits: 50000,
countedItems: [
{ ItemType: "/Lotus/Upgrades/Mods/FusionBundles/RareFusionBundle", ItemCount: 10 } // Not sure about that
]
},
{
items: ["/Lotus/StoreItems/Types/Items/MiscItems/UtilityUnlocker"]
}
],
Reward: {
items: ["/Lotus/Types/StoreItems/Packages/EventCatalystReactorBundle"]
},
BonusReward: { items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"] }
});
}
// Nightwave Challenges
const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
if (nightwaveSyndicateTag) {

View File

@ -5,12 +5,14 @@ export interface IWorldState {
Version: number; // for goals
BuildLabel: string;
Time: number;
InGameMarket: IInGameMarket;
Goals: IGoal[];
Alerts: [];
Sorties: ISortie[];
LiteSorties: ILiteSortie[];
SyndicateMissions: ISyndicateMissionInfo[];
ActiveMissions: IFissure[];
FlashSales: IFlashSale[];
GlobalUpgrades: IGlobalUpgrade[];
Invasions: IInvasion[];
NodeOverrides: INodeOverride[];
@ -39,10 +41,13 @@ export interface IGoal {
Expiry: IMongoDate;
Count?: number;
Goal?: number;
InterimGoals?: number[];
BonusGoal?: number;
HealthPct?: number;
Success?: number;
Personal?: boolean;
Bounty?: boolean;
Best?: boolean;
Bounty?: boolean; // Tactical Alert
Faction?: string;
ClampNodeScores?: boolean;
Desc: string;
@ -51,18 +56,28 @@ export interface IGoal {
InstructionalItem?: string;
Icon: string;
Tag: string;
PrereqGoalTags?: string[];
Node?: string;
VictimNode?: string;
ConcurrentMissionKeyNames?: string[];
ConcurrentNodeReqs?: number[];
ConcurrentNodes?: string[];
RegionIdx?: number;
Regions?: number[];
MissionKeyName?: string;
Reward?: IMissionReward;
InterimRewards?: IMissionReward[];
BonusReward?: IMissionReward;
JobAffiliationTag?: string;
Jobs?: ISyndicateJob[];
PreviousJobs?: ISyndicateJob[];
JobCurrentVersion?: IOid;
JobPreviousVersion?: IOid;
ScoreVar?: string;
ScoreMaxTag?: string;
NightLevel?: string;
}
export interface ISyndicateJob {
@ -321,6 +336,37 @@ export type TCircuitGameMode =
| "Assassination"
| "Alchemy";
export interface IFlashSale {
TypeName: string;
ShowInMarket: boolean;
HideFromMarket: boolean;
SupporterPack: boolean;
Discount: number;
BogoBuy: number;
BogoGet: number;
PremiumOverride: number;
RegularOverride: number;
ProductExpiryOverride?: IMongoDate;
StartDate: IMongoDate;
EndDate: IMongoDate;
}
export interface IInGameMarket {
LandingPage: ILandingPage;
}
export interface ILandingPage {
Categories: IGameMarketCategory[];
}
export interface IGameMarketCategory {
CategoryName: string;
Name: string;
Icon: string;
AddToMenu?: boolean;
Items?: string[];
}
export interface ITmp {
cavabegin: string;
PurchasePlatformLockEnabled: boolean; // Seems unused

View File

@ -937,8 +937,57 @@
<input class="form-check-input" type="checkbox" id="worldState.varziaFullyStocked" />
<label class="form-check-label" for="worldState.varziaFullyStocked" data-loc="worldState_varziaFullyStocked"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="worldState.wolfHunt" />
<label class="form-check-label" for="worldState.wolfHunt" data-loc="worldState_wolfHunt"></label>
<abbr data-loc-inc="worldState_galleonOfGhouls"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="worldState.longShadow" />
<label class="form-check-label" for="worldState.longShadow" data-loc="worldState_longShadow"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="worldState.hallowedFlame" />
<label class="form-check-label" for="worldState.hallowedFlame" data-loc="worldState_hallowedFlame"></label>
<abbr data-loc-inc="worldState_hallowedNightmares|worldState_dogDays"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
</div>
<div class="form-group mt-2 d-flex gap-2">
<div class="flex-fill">
<label class="form-label" for="worldState.hallowedNightmares" data-loc="worldState_hallowedNightmares"></label>
<abbr data-loc-inc="worldState_hallowedFlame|worldState_dogDays"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
<select class="form-control" id="worldState.hallowedNightmares" data-default="false">
<option value="true" data-loc="enabled"></option>
<option value="false" data-loc="disabled"></option>
</select>
</div>
<div class="flex-fill">
<label class="form-label" for="worldState.hallowedNightmaresRewardsOverride" data-loc="worldState_hallowedNightmaresRewards"></label>
<select class="form-control" id="worldState.hallowedNightmaresRewardsOverride" data-default="0">
<option value="0" data-loc="worldState_from_year" data-loc-year="2018"></option>
<option value="1" data-loc="worldState_from_year" data-loc-year="2016"></option>
<option value="2" data-loc="worldState_from_year" data-loc-year="2015"></option>
</select>
</div>
</div>
<div class="form-group mt-2 d-flex gap-2">
<div class="flex-fill">
<label class="form-label" for="worldState.proxyRebellion" data-loc="worldState_proxyRebellion"></label>
<select class="form-control" id="worldState.proxyRebellion" data-default="false">
<option value="true" data-loc="enabled"></option>
<option value="false" data-loc="disabled"></option>
</select>
</div>
<div class="flex-fill">
<label class="form-label" for="worldState.proxyRebellionRewardsOverride" data-loc="worldState_proxyRebellionRewards"></label>
<select class="form-control" id="worldState.proxyRebellionRewardsOverride" data-default="0">
<option value="0" data-loc="worldState_from_year" data-loc-year="2019"></option>
<option value="1" data-loc="worldState_from_year" data-loc-year="2018"></option>
</select>
</div>
</div>
<div class="form-group mt-2">
<label class="form-label" for="worldState.galleonOfGhouls" data-loc="worldState_galleonOfGhouls"></label>
<abbr data-loc-inc="worldState_wolfHunt"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
<select class="form-control" id="worldState.galleonOfGhouls" data-default="0">
<option value="0" data-loc="disabled"></option>
<option value="1" data-loc="worldState_we1"></option>
@ -970,6 +1019,27 @@
<option value="false" data-loc="disabled"></option>
</select>
</div>
<div class="form-group mt-2 d-flex gap-2">
<div class="flex-fill">
<label class="form-label" for="worldState.dogDaysOverride" data-loc="worldState_dogDays"></label>
<abbr data-loc-inc="worldState_hallowedFlame|worldState_hallowedNightmares"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM320 200C333.3 200 344 210.7 344 224L344 336C344 349.3 333.3 360 320 360C306.7 360 296 349.3 296 336L296 224C296 210.7 306.7 200 320 200zM293.3 416C292.7 406.1 297.6 396.7 306.1 391.5C314.6 386.4 325.3 386.4 333.8 391.5C342.3 396.7 347.2 406.1 346.6 416C347.2 425.9 342.3 435.3 333.8 440.5C325.3 445.6 314.6 445.6 306.1 440.5C297.6 435.3 292.7 425.9 293.3 416z"/></svg></abbr>
<select class="form-control" id="worldState.dogDaysOverride" 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="flex-fill">
<label class="form-label" for="worldState.dogDaysRewardsOverride" data-loc="worldState_dogDaysRewards"></label>
<select class="form-control" id="worldState.dogDaysRewardsOverride" data-default="null">
<option value="null" data-loc="normal"></option>
<option value="3" data-loc="worldState_from_year" data-loc-year="2025"></option>
<option value="2" data-loc="worldState_from_year" data-loc-year="2024"></option>
<option value="1" data-loc="worldState_from_year" data-loc-year="2023"></option>
<option value="0" data-loc="worldState_pre_year" data-loc-year="2023"></option>
</select>
</div>
</div>
<div class="form-group mt-2">
<label class="form-label" for="worldState.eidolonOverride" data-loc="worldState_eidolonOverride"></label>
<select class="form-control" id="worldState.eidolonOverride" data-default="">

View File

@ -202,6 +202,17 @@ function updateLocElements() {
document.querySelectorAll("[data-loc-placeholder]").forEach(elm => {
elm.placeholder = loc(elm.getAttribute("data-loc-placeholder"));
});
document.querySelectorAll("[data-loc-inc]").forEach(elm => {
const incWith = elm
.getAttribute("data-loc-inc")
.split("|")
.map(key => loc(key))
.join(", ");
elm.title = `${loc("worldState_incompatibleWith")} ${incWith}`;
});
document.querySelectorAll("[data-loc-year]").forEach(elm => {
elm.innerHTML = elm.innerHTML.replace("|YEAR|", elm.getAttribute("data-loc-year"));
});
}
function setActiveLanguage(lang) {

View File

@ -29,9 +29,12 @@ body:not(.logged-in) .user-dropdown {
display: none;
}
td.text-end > a > svg {
/* font awesome icons */
svg {
fill: currentColor;
height: 1em;
}
td.text-end > a > svg {
margin-left: 0.5em;
margin-bottom: 4px; /* to centre the icon */
}

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `Galeone der Ghule`,
worldState_ghoulEmergence: `Ghul Ausrottung`,
worldState_plagueStar: `Plagenstern`,
worldState_dogDays: `Hitzefrei`,
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
worldState_wolfHunt: `Wolfsjagd (2025)`,
worldState_longShadow: `Lange Schatten`,
worldState_hallowedFlame: `Geweihte Flamme`,
worldState_hallowedNightmares: `Geweihte Albträume`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `Proxy-Rebellion`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_from_year: `[UNTRANSLATED] from |YEAR|`,
worldState_pre_year: `[UNTRANSLATED] pre |YEAR|`,
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
enabled: `Aktiviert`,
disabled: `Deaktiviert`,
worldState_we1: `Wochenende 1`,

View File

@ -247,6 +247,18 @@ dict = {
worldState_galleonOfGhouls: `Galleon of Ghouls`,
worldState_ghoulEmergence: `Ghoul Purge`,
worldState_plagueStar: `Plague Star`,
worldState_dogDays: `Dog Days`,
worldState_dogDaysRewards: `Dog Days Rewards`,
worldState_wolfHunt: `Wolf Hunt (2025)`,
worldState_longShadow: `Long Shadow`,
worldState_hallowedFlame: `Hallowed Flame`,
worldState_hallowedNightmares: `Hallowed Nightmares`,
worldState_hallowedNightmaresRewards: `Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `Proxy Rebellion`,
worldState_proxyRebellionRewards: `Proxy Rebellion Rewards`,
worldState_from_year: `from |YEAR|`,
worldState_pre_year: `pre |YEAR|`,
worldState_incompatibleWith: `Incompatible with:`,
enabled: `Enabled`,
disabled: `Disabled`,
worldState_we1: `Weekend 1`,

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `Galeón de Gules`,
worldState_ghoulEmergence: `Purga de Gules`,
worldState_plagueStar: `Estrella Infestada`,
worldState_dogDays: `Canícula`,
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
worldState_wolfHunt: `Cacería del Lobo (2025)`,
worldState_longShadow: `Sombra Prolongada`,
worldState_hallowedFlame: `Llama Sagrada`,
worldState_hallowedNightmares: `Pesadillas sagradas`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `Rebelión Proxy`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_from_year: `[UNTRANSLATED] from |YEAR|`,
worldState_pre_year: `[UNTRANSLATED] pre |YEAR|`,
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
enabled: `Activado`,
disabled: `Desactivado`,
worldState_we1: `Semana 1`,

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `Galion des Goules`,
worldState_ghoulEmergence: `Purge des Goules`,
worldState_plagueStar: `Fléau Céleste`,
worldState_dogDays: `Bataille d'Eau`,
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
worldState_wolfHunt: `Chasse au Loup (2025)`,
worldState_longShadow: `La Propagation des Ombres`,
worldState_hallowedFlame: `Flamme Hantée`,
worldState_hallowedNightmares: `Cauchemars Hantés`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `Rébellion Proxy`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_from_year: `[UNTRANSLATED] from |YEAR|`,
worldState_pre_year: `[UNTRANSLATED] pre |YEAR|`,
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
enabled: `Activé`,
disabled: `Désactivé`,
worldState_we1: `Weekend 1`,

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `Галеон Гулей`,
worldState_ghoulEmergence: `Избавление от гулей`,
worldState_plagueStar: `Чумная звезда`,
worldState_dogDays: `Знойные дни`,
worldState_dogDaysRewards: `Награды Знойных дней`,
worldState_wolfHunt: `Волчья Охота (2025)`,
worldState_longShadow: `Длинная Тень`,
worldState_hallowedFlame: `Священное пламя`,
worldState_hallowedNightmares: `Священные Кошмары`,
worldState_hallowedNightmaresRewards: `Награды Священных Кошмаров`,
worldState_proxyRebellion: `Восстание Роботов`,
worldState_proxyRebellionRewards: `Награды Восстания Роботов`,
worldState_from_year: `из |YEAR|`,
worldState_pre_year: `до |YEAR|`,
worldState_incompatibleWith: `Несовместимо с:`,
enabled: `Включено`,
disabled: `Отключено`,
worldState_we1: `Выходные 1`,

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `Гульський Галеон`,
worldState_ghoulEmergence: `Зачищення від гулів`,
worldState_plagueStar: `Морова зірка`,
worldState_dogDays: `Спекотні дні`,
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
worldState_wolfHunt: `Полювання на Вовка (2025)`,
worldState_longShadow: `Довга тінь`,
worldState_hallowedFlame: `Священне полум'я`,
worldState_hallowedNightmares: `Священні жахіття`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `Повстання роботів`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_from_year: `[UNTRANSLATED] from |YEAR|`,
worldState_pre_year: `[UNTRANSLATED] pre |YEAR|`,
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
enabled: `Увімкнено`,
disabled: `Вимкнено`,
worldState_we1: `Вихідні 1`,

View File

@ -248,6 +248,18 @@ dict = {
worldState_galleonOfGhouls: `战术警报:尸鬼的帆船战舰`,
worldState_ghoulEmergence: `尸鬼净化`,
worldState_plagueStar: `瘟疫之星`,
worldState_dogDays: `三伏天`,
worldState_dogDaysRewards: `[UNTRANSLATED] Dog Days Rewards`,
worldState_wolfHunt: `恶狼狩猎 (2025)`,
worldState_longShadow: `暗夜长影`,
worldState_hallowedFlame: `万圣之焰`,
worldState_hallowedNightmares: `万圣噩梦`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_proxyRebellion: `机械叛乱`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_from_year: `[UNTRANSLATED] from |YEAR|`,
worldState_pre_year: `[UNTRANSLATED] pre |YEAR|`,
worldState_incompatibleWith: `[UNTRANSLATED] Incompatible with:`,
enabled: `启用`,
disabled: `关闭/取消配置`,
worldState_we1: `活动阶段:第一周`,