diff --git a/src/service/cron.ts b/src/service/cron.ts index 630bd11c6..b8069f2f4 100644 --- a/src/service/cron.ts +++ b/src/service/cron.ts @@ -16,7 +16,7 @@ import { checkBirthdays } from "@/service/birthday.js"; import { handleFadingMessages } from "@/service/fadingMessage.js"; import { checkExpiredShifts } from "@/service/lootRoles.js"; import { getTrichterUnserEmbed } from "@/service/trichterUnser.js"; -import { degradeItems, exposeWithRadiation } from "@/service/lootDegradation.js"; +import { degradeItems, exposeWithRadiation, runHalfLife } from "@/service/lootDegradation.js"; import * as poll from "@/commands/poll.js"; import * as ehre from "@/storage/ehre.js"; @@ -47,6 +47,7 @@ export async function schedule(context: BotContext) { cron("0 20 * * FRI", () => getTrichterUnserEmbed(context)); cron("0 * * * *", () => degradeItems(context)); cron("26 18,19 * * *", () => exposeWithRadiation(context)); + cron("15 18,19 * * *", () => runHalfLife(context)); const loot = context.commandConfig.loot; if (loot.enabled) { diff --git a/src/service/lootDegradation.ts b/src/service/lootDegradation.ts index fd65da86b..1b7f32e6d 100644 --- a/src/service/lootDegradation.ts +++ b/src/service/lootDegradation.ts @@ -1,8 +1,9 @@ +import { type Snowflake, userMention } from "discord.js"; import type { BotContext } from "@/context.js"; import * as time from "@/utils/time.js"; import * as lootService from "@/service/loot.js"; -import { LootAttributeKindId, LootKindId } from "@/service/lootData.js"; +import { LootAttributeKindId, LootKindId, resolveLootTemplate } from "@/service/lootData.js"; import log from "@log"; import { randomEntry } from "@/service/random.js"; @@ -73,7 +74,7 @@ export async function exposeWithRadiation(context: BotContext) { await context.textChannels.hauptchat.send({ embeds: [ { - description: `:radioactive: ${targetLoot.displayName} von <@${targetLoot.winnerId}> wurde verstrahlt. :radioactive:`, + description: `:radioactive: ${targetLoot.displayName} von ${userMention(targetLoot.winnerId)} wurde verstrahlt. :radioactive:`, footer: { text: "Du solltest deinen Müll besser entsorgen", }, @@ -81,3 +82,70 @@ export async function exposeWithRadiation(context: BotContext) { ], }); } + +export async function runHalfLife(context: BotContext) { + log.info("Running half life"); + + const allWaste = await lootService.getLootsByKindId(LootKindId.RADIOACTIVE_WASTE); + + // See: https://github.com/NullDev/CSZ-Bot/issues/470 + // We don't do /2 straigt away, so we can roll this out more slowly initially. We can increase this to 2 once we got this number down in general + // Also, consider aligning this with the drop rate of radioactive waste, so we have that balanced + const targetWasteCount = Math.ceil(allWaste.length / 1.1); + + if (targetWasteCount >= allWaste.length) { + return; + } + + const wasteToRemove = allWaste.sort((a, b) => Math.random()).slice(targetWasteCount); + if (wasteToRemove.length === 0) { + return; + } + + const leadTemplate = resolveLootTemplate(LootKindId.BLEI); + if (!leadTemplate) { + log.error("Could not resolve loot template for lead."); + return; + } + + const replacedStats = new Map(); + + for (const l of wasteToRemove) { + const replaced = await lootService.replaceLoot( + l.id, + { + displayName: leadTemplate.displayName, + description: leadTemplate.infoDescription ?? leadTemplate.dropDescription, + lootKindId: leadTemplate.id, + usedImage: leadTemplate.asset, + winnerId: l.winnerId, + claimedAt: l.claimedAt, + guildId: l.guildId, + channelId: l.channelId, + messageId: l.messageId, + origin: "replacement", + }, + true, + ); + + replacedStats.set(replaced.winnerId, replacedStats.getOrInsert(replaced.winnerId, 0) + 1); + } + + const listFormatter = new Intl.ListFormat("de", { + style: "short", + type: "conjunction", + }); + + const decayStats = replacedStats + .entries() + .toArray() + .map(([user, count]) => `${count}x von ${userMention(user)}`); + + await context.textChannels.hauptchat.send({ + embeds: [ + { + description: `:radioactive: Der Müll ${decayStats.length === 1 ? "eines Users" : "einiger User"} ist zu einem Stück Blei zerfallen: ${listFormatter.format(decayStats)}. :radioactive:`, + }, + ], + }); +}