added achievements for new steal logic

This commit is contained in:
DeSqBlocki
2026-09-13 21:56:18 +02:00
parent b441de286c
commit dd4d6e4817
2 changed files with 73 additions and 31 deletions
+56 -29
View File
@@ -15,7 +15,9 @@ const {
} = require("../../utils/cookieEconomy"); } = require("../../utils/cookieEconomy");
const { const {
trackProfileEvent trackProfileEvent,
getProfileCollections,
getDefaultStats
} = require("../../utils/profileSystem"); } = require("../../utils/profileSystem");
const { const {
@@ -235,12 +237,8 @@ async function steal(interaction) {
}); });
} }
const { const { cookies } = getCookieCommandCollections();
cookies, const { stats: statsProfile } = getProfileCollections();
statsProfile
} = getCookieCommandCollections({
requireStatsProfile: true
});
const now = new Date(); const now = new Date();
const stealWindowCutoff = new Date( const stealWindowCutoff = new Date(
@@ -398,6 +396,27 @@ async function steal(interaction) {
) )
: null; : null;
/*
* Ensure the victim has a complete profile document before heat state is
* written. This avoids creating a sparse stats_profiles document when a
* player is targeted before any other profile event has initialized them.
*/
await statsProfile.updateOne(
{
userId: target.id,
guildId: interaction.guildId
},
{
$setOnInsert: getDefaultStats(
target.id,
interaction.guildId
)
},
{
upsert: true
}
);
/* /*
* Rolling heat protection. * Rolling heat protection.
* *
@@ -407,7 +426,8 @@ async function steal(interaction) {
*/ */
await statsProfile.updateOne( await statsProfile.updateOne(
{ {
userId: target.id userId: target.id,
guildId: interaction.guildId
}, },
{ {
$pull: { $pull: {
@@ -428,7 +448,8 @@ async function steal(interaction) {
*/ */
const heatUpdateResult = await statsProfile.findOneAndUpdate( const heatUpdateResult = await statsProfile.findOneAndUpdate(
{ {
userId: target.id userId: target.id,
guildId: interaction.guildId
}, },
{ {
$push: { $push: {
@@ -439,7 +460,6 @@ async function steal(interaction) {
} }
}, },
{ {
upsert: true,
returnDocument: "before" returnDocument: "before"
} }
); );
@@ -451,6 +471,30 @@ async function steal(interaction) {
const activeAttempts = const activeAttempts =
targetProfile?.stealProtection?.attempts ?? []; targetProfile?.stealProtection?.attempts ?? [];
const uniqueAttackers = new Set([
...activeAttempts.map(
attempt => attempt.userId
),
user.id
]).size;
/*
* Achievement/event tracking for the victim. The current attempt is
* included, while repeated attempts by the same user only count once
* toward the unique-attacker total.
*/
await trackProfileEvent(
interaction,
target.id,
{
type: "steal_targeted",
attackerUserId: user.id,
uniqueAttackers,
attemptsInWindow: activeAttempts.length + 1,
windowSeconds: heatWindowMs / 1000
}
);
const heat = activeAttempts.reduce( const heat = activeAttempts.reduce(
(total, attempt) => { (total, attempt) => {
const timestamp = new Date( const timestamp = new Date(
@@ -1111,9 +1155,7 @@ async function leaderboard(interaction) {
* collection names for the two collections used by this command. * collection names for the two collections used by this command.
*/ */
function getCookieCommandCollections({ function getCookieCommandCollections() {
requireStatsProfile = false
} = {}) {
const collections = getCollections(); const collections = getCollections();
const database = const database =
collections.db ?? collections.db ??
@@ -1125,28 +1167,13 @@ function getCookieCommandCollections({
collections.items_cookies ?? collections.items_cookies ??
database?.collection?.("items_cookies"); database?.collection?.("items_cookies");
const statsProfile =
collections.statsProfile ??
collections.statsProfiles ??
collections.stats_profile ??
database?.collection?.("stats_profile");
if (!cookies) { if (!cookies) {
throw new Error( throw new Error(
"getCollections() must expose the items_cookies collection as cookies/itemsCookies/items_cookies, or expose a db handle." "getCollections() must expose the items_cookies collection as cookies/itemsCookies/items_cookies, or expose a db handle."
); );
} }
if (requireStatsProfile && !statsProfile) { return { cookies };
throw new Error(
"getCollections() must expose the stats_profile collection as statsProfile/statsProfiles/stats_profile, or expose a db handle."
);
}
return {
cookies,
statsProfile
};
} }
+17 -2
View File
@@ -636,7 +636,7 @@ function getDefaultStats(userId, guildId) {
allInRunItBack: 0, allInRunItBack: 0,
bankruptcyCount: 0, bankruptcyCount: 0,
bankruptcyRecoveryBest: 0, bankruptcyRecoveryBest: 0,
maxUniqueStealAttackers: 0,
etTuCrumb: 0, etTuCrumb: 0,
noHardFeelings: 0, noHardFeelings: 0,
cookieLaundering: 0 cookieLaundering: 0
@@ -846,7 +846,22 @@ async function trackProfileEvent(
} }
); );
break; break;
case "steal_targeted":
await stats.updateOne(
{
userId,
guildId
},
{
$max: {
"special.maxUniqueStealAttackers":
Number(
event.uniqueAttackers ?? 0
)
}
}
);
break;
case "steal_success": case "steal_success":
await stats.updateOne( await stats.updateOne(
{ {