Filter inclusion groups by timed effects (#2765) * Filter inclusion groups by timed effects Closes #2762 * Skip group scoring check if sticky entries are present * Optimize sticky checks

75c6bee350c65b4f1824cc399edff8feb9d532d1

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +71 -4Showing whitespace changes
public/scripts/world-info.js+71 -4
@@ -3953,7 +3953,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
39533953 let newContent = '';
39543954 const textToScanTokens = await getTokenCountAsync(allActivatedText);
39553955
39563956 filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects);
39573957
39583958 console.debug('[WI] --- PROBABILITY CHECKS ---');
39593959 for (const entry of newEntries) {
@@ -4143,8 +4143,9 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
41434143 * @param {WorldInfoBuffer} buffer The buffer to use for scoring
41444144 * @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry
41454145 * @param {number} scanState The current scan state
4146+ * @param {Map<string, boolean>} hasStickyMap The sticky entries map
41464147 */
41474148function filterGroupsByScoring(groups, buffer, removeEntry, scanState, hasStickyMap) {
41484149 for (const [key, group] of Object.entries(groups)) {
41494150 // Group scoring is disabled both globally and for the group entries
41504151 if (!world_info_use_group_scoring && !group.some(x => x.useGroupScoring)) {
@@ -4152,6 +4153,13 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
41524153 continue;
41534154 }
41544155
4156+ // If the group has any sticky entries, the rest are already removed by the timed effects filter
4157+ const hasAnySticky = hasStickyMap.get(key);
4158+ if (hasAnySticky) {
4159+ console.debug(`[WI] Skipping group scoring check, group '${key}' has sticky entries`);
4160+ continue;
4161+ }
4162+
41554163 const scores = group.map(entry => buffer.getScore(entry, scanState));
41564164 const maxScore = Math.max(...scores);
41574165 console.debug(`[WI] Group '${key}' max score:`, maxScore);
@@ -4176,13 +4184,64 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
41764184}
41774185
41784186/**
4187+ * Removes entries on cooldown and forces sticky entries as winners.
4188+ * @param {Record<string, WIScanEntry[]>} groups The groups to filter
4189+ * @param {WorldInfoTimedEffects} timedEffects The timed effects to use
4190+ * @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry
4191+ * @returns {Map<string, boolean>} If any sticky entries were found
4192+ */
4193+function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) {
4194+ /** @type {Map<string, boolean>} */
4195+ const hasStickyMap = new Map();
4196+
4197+ for (const [key, group] of Object.entries(groups)) {
4198+ hasStickyMap.set(key, false);
4199+
4200+ // If the group has any sticky entries, leave only the sticky entries
4201+ const stickyEntries = group.filter(x => timedEffects.isEffectActive('sticky', x));
4202+ if (stickyEntries.length) {
4203+ for (const entry of group) {
4204+ if (stickyEntries.includes(entry)) {
4205+ continue;
4206+ }
4207+
4208+ console.debug(`[WI] Entry ${entry.uid}`, `removed as a non-sticky loser from inclusion group '${key}'`, entry);
4209+ removeEntry(entry);
4210+ }
4211+
4212+ hasStickyMap.set(key, true);
4213+ }
4214+
4215+ // It should not be possible for an entry on cooldown/delay to event get into the grouping phase but @Wolfsblvt told me to leave it here.
4216+ const cooldownEntries = group.filter(x => timedEffects.isEffectActive('cooldown', x));
4217+ if (cooldownEntries.length) {
4218+ console.debug(`[WI] Inclusion group '${key}' has entries on cooldown. They will be removed.`, cooldownEntries);
4219+ for (const entry of cooldownEntries) {
4220+ removeEntry(entry);
4221+ }
4222+ }
4223+
4224+ const delayEntries = group.filter(x => timedEffects.isEffectActive('delay', x));
4225+ if (delayEntries.length) {
4226+ console.debug(`[WI] Inclusion group '${key}' has entries with delay. They will be removed.`, delayEntries);
4227+ for (const entry of delayEntries) {
4228+ removeEntry(entry);
4229+ }
4230+ }
4231+ }
4232+
4233+ return hasStickyMap;
4234+}
4235+
4236+/**
41794237 * Filters entries by inclusion groups.
41804238 * @param {object[]} newEntries Entries activated on current recursion level
41814239 * @param {Set<object>} allActivatedEntries Set of all activated entries
41824240 * @param {WorldInfoBuffer} buffer The buffer to use for scanning
41834241 * @param {number} scanState The current scan state
4242+ * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
41844243 */
41854244function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects) {
41864245 console.debug('[WI] --- INCLUSION GROUP CHECKS ---');
41874246
41884247 const grouped = newEntries.filter(x => x.group).reduce((acc, item) => {
@@ -4212,11 +4271,19 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt
42124271 }
42134272 }
42144273
42154274 filterGroupsByScoringconst hasStickyMap = filterGroupsByTimedEffects(grouped, buffertimedEffects, removeEntry, scanState);
4275+ filterGroupsByScoring(grouped, buffer, removeEntry, scanState, hasStickyMap);
42164276
42174277 for (const [key, group] of Object.entries(grouped)) {
42184278 console.debug(`[WI] Checking inclusion group '${key}' with ${group.length} entries`, group);
42194279
4280+ // If the group has any sticky entries, the rest are already removed by the timed effects filter
4281+ const hasAnySticky = hasStickyMap.get(key);
4282+ if (hasAnySticky) {
4283+ console.debug(`[WI] Skipping inclusion group check, group '${key}' has sticky entries`);
4284+ continue;
4285+ }
4286+
42204287 if (Array.from(allActivatedEntries).some(x => x.group === key)) {
42214288 console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
42224289 // We need to forcefully deactivate all other entries in the group