-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loot Probability Command #483
Changes from 8 commits
05f32d5
559cbc1
84da696
5a2fb88
5d346df
8176c1d
0d5aff5
8092cb1
494ad53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { | ||
type ChatInputCommandInteraction, | ||
type CommandInteraction, | ||
SlashCommandBuilder, | ||
SlashCommandSubcommandBuilder, | ||
} from "discord.js"; | ||
|
||
import type { BotContext } from "@/context.js"; | ||
import type { ApplicationCommand } from "@/commands/command.js"; | ||
import { ensureChatInputCommand } from "@/utils/interactionUtils.js"; | ||
|
||
import * as lootDataService from "@/service/lootData.js"; | ||
import { adjustLootWithBuffsFromUser } from "@/service/lootDrop.js"; | ||
|
||
export default class LootCommand implements ApplicationCommand { | ||
name = "loot"; | ||
description = "Geht's um Loot?"; | ||
|
||
applicationCommand = new SlashCommandBuilder() | ||
.setName(this.name) | ||
.setDescription(this.description) | ||
.addSubcommand( | ||
new SlashCommandSubcommandBuilder() | ||
.setName("wahrscheinlichkeiten") | ||
.setDescription("Zeige die Wahrscheinlichkeiten für Loot Gegenstände an"), | ||
); | ||
|
||
async handleInteraction(interaction: CommandInteraction, context: BotContext) { | ||
const command = ensureChatInputCommand(interaction); | ||
const subCommand = command.options.getSubcommand(); | ||
switch (subCommand) { | ||
case "wahrscheinlichkeiten": | ||
await this.#showLootProbability(interaction, context); | ||
break; | ||
default: | ||
throw new Error(`Unknown subcommand: "${subCommand}"`); | ||
} | ||
} | ||
|
||
async #showLootProbability(interaction: CommandInteraction, context: BotContext) { | ||
if (!interaction.isChatInputCommand()) { | ||
throw new Error("Interaction is not a chat input command"); | ||
} | ||
if (!interaction.guild || !interaction.channel) { | ||
return; | ||
} | ||
|
||
// TODO: Lowperformer solution. A diagram with graphviz or something would be cooler | ||
const loot = ( | ||
await adjustLootWithBuffsFromUser(interaction.user, lootDataService.lootTemplates) | ||
).loot.filter(l => l.weight > 0); | ||
const totalWeight = loot.reduce((acc, curr) => acc + curr.weight, 0); | ||
const lootWithProbabilitiy = loot | ||
.map(l => ({ | ||
...l, // Oh no, please optimize for webscale. No need to copy the whole data :cry: | ||
probability: Number(l.weight / totalWeight), | ||
})) | ||
.sort((a, b) => b.probability - a.probability); | ||
|
||
const textRepresentation = lootWithProbabilitiy | ||
.map( | ||
l => | ||
`${l.displayName}: ${l.probability.toLocaleString(undefined, { style: "percent", maximumFractionDigits: 2 })}`, | ||
) | ||
.join("\n"); | ||
|
||
await interaction.reply( | ||
`Deine persönlichen Loot Wahrscheinlichkeiten:\n\n${textRepresentation}`, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
import * as fs from "node:fs/promises"; | ||
|
||
import { | ||
ActionRowBuilder, | ||
ButtonBuilder, | ||
ButtonStyle, | ||
ChannelType, | ||
|
@@ -28,6 +27,7 @@ import { | |
} from "@/service/lootData.js"; | ||
|
||
import log from "@log"; | ||
import type { LootTemplate } from "@/storage/loot.js"; | ||
|
||
const lootTimeoutMs = 60 * 1000; | ||
|
||
|
@@ -152,13 +152,15 @@ export async function postLootDrop( | |
return; | ||
} | ||
|
||
const defaultWeights = lootTemplates.map(t => t.weight); | ||
const { messages, weights } = await getDropWeightAdjustments(interaction.user, defaultWeights); | ||
const { messages, loot } = await adjustLootWithBuffsFromUser(interaction.user, lootTemplates); | ||
|
||
const rarityWeights = lootAttributeTemplates.map(a => a.initialDropWeight ?? 0); | ||
const initialAttribute = randomEntryWeighted(lootAttributeTemplates, rarityWeights); | ||
|
||
const template = randomEntryWeighted(lootTemplates, weights); | ||
const template = randomEntryWeighted( | ||
lootTemplates, | ||
loot.map(l => l.weight), | ||
); | ||
const claimedLoot = await lootService.createLoot( | ||
template, | ||
interaction.user, | ||
|
@@ -228,12 +230,12 @@ export async function postLootDrop( | |
|
||
type AdjustmentResult = { | ||
messages: string[]; | ||
weights: number[]; | ||
loot: LootTemplate[]; | ||
}; | ||
|
||
async function getDropWeightAdjustments( | ||
export async function adjustLootWithBuffsFromUser( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Der Purpose der Funktion sind generell Adjustments in der Situation des Klickens. Das können Buffs, aber auch Debufffs durch aktuelle Items sein. Später war gedacht, die aktulle Uhrzeit des Klickens oder den schenkenden User ("Schenker X hat Item Y, deshalb ist Warhscheinlichkeit Z höher/geringer") etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mit "Buffs" meinte ich tatsächlich auch beides. Vielleicht git's da eine bessere Bezeichnung There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weight Adjustments |
||
user: User, | ||
weights: readonly number[], | ||
loot: readonly LootTemplate[], | ||
): Promise<AdjustmentResult> { | ||
const waste = await lootService.getUserLootCountById(user.id, LootKindId.RADIOACTIVE_WASTE); | ||
const messages = []; | ||
|
@@ -254,13 +256,19 @@ async function getDropWeightAdjustments( | |
messages.push("Da du privat versichert bist, hast du die doppelte Chance auf eine AU."); | ||
} | ||
|
||
const newWeights = [...weights]; | ||
newWeights[LootKindId.NICHTS] = Math.ceil(weights[LootKindId.NICHTS] * wasteFactor) | 0; | ||
newWeights[LootKindId.KRANKSCHREIBUNG] = (weights[LootKindId.KRANKSCHREIBUNG] * pkvFactor) | 0; | ||
const weights = loot.map(t => t.weight); | ||
const newLoot = [...loot]; | ||
const updateEntry = (id: LootKindId, newValue: number) => { | ||
const idx = newLoot.findIndex(l => l.id === id); | ||
console.assert(idx !== -1); | ||
newLoot[idx].weight = newValue; | ||
}; | ||
updateEntry(LootKindId.NICHTS, Math.ceil(weights[LootKindId.NICHTS] * wasteFactor) | 0); | ||
updateEntry(LootKindId.KRANKSCHREIBUNG, (weights[LootKindId.KRANKSCHREIBUNG] * pkvFactor) | 0); | ||
|
||
return { | ||
messages, | ||
weights: newWeights, | ||
loot: newLoot, | ||
Comment on lines
-263
to
+274
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Früher war das so und es hatte irgendeinen Grund, warum ich das umgebaut habe. Kann dir nicht genau sagen, welchen |
||
}; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mach mal ephemeral.