mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Add slash command for setting sticky/cooldown. Normalize naming: timed event => timed effect
This commit is contained in:
@@ -102,14 +102,14 @@ const MAX_SCAN_DEPTH = 1000;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} WITimedEvent Timed event for world info
|
* @typedef {object} WITimedEffect Timed effect for world info
|
||||||
* @property {number} hash Hash of the entry that triggered the event
|
* @property {number} hash Hash of the entry that triggered the effect
|
||||||
* @property {number} start The chat index where the event starts
|
* @property {number} start The chat index where the effect starts
|
||||||
* @property {number} end The chat index where the event ends
|
* @property {number} end The chat index where the effect ends
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef TimedEventType Type of timed event
|
* @typedef TimedEffectType Type of timed effect
|
||||||
* @type {'sticky'|'cooldown'}
|
* @type {'sticky'|'cooldown'}
|
||||||
*/
|
*/
|
||||||
// End typedef area
|
// End typedef area
|
||||||
@@ -342,9 +342,9 @@ class WorldInfoBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a timed events manager for World Info.
|
* Represents a timed effects manager for World Info.
|
||||||
*/
|
*/
|
||||||
class WorldInfoTimedEvents {
|
class WorldInfoTimedEffects {
|
||||||
/**
|
/**
|
||||||
* Cache for entry hashes. Uses weak map to avoid memory leaks.
|
* Cache for entry hashes. Uses weak map to avoid memory leaks.
|
||||||
* @type {WeakMap<WIScanEntry, number>}
|
* @type {WeakMap<WIScanEntry, number>}
|
||||||
@@ -364,8 +364,8 @@ class WorldInfoTimedEvents {
|
|||||||
#entries = [];
|
#entries = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer for active timed events
|
* Buffer for active timed effects
|
||||||
* @type {Record<TimedEventType, WIScanEntry[]>}
|
* @type {Record<TimedEffectType, WIScanEntry[]>}
|
||||||
*/
|
*/
|
||||||
#buffer = {
|
#buffer = {
|
||||||
'sticky': [],
|
'sticky': [],
|
||||||
@@ -373,7 +373,7 @@ class WorldInfoTimedEvents {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the timed events with the given messages.
|
* Initialize the timed effects with the given messages.
|
||||||
* @param {string[]} chat Array of chat messages
|
* @param {string[]} chat Array of chat messages
|
||||||
* @param {WIScanEntry[]} entries Array of entries
|
* @param {WIScanEntry[]} entries Array of entries
|
||||||
*/
|
*/
|
||||||
@@ -431,12 +431,12 @@ class WorldInfoTimedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a timed event for a WI entry.
|
* Gets a timed effect for a WI entry.
|
||||||
* @param {WIScanEntry} entry WI entry
|
* @param {WIScanEntry} entry WI entry
|
||||||
* @param {TimedEventType} type Type of timed event
|
* @param {TimedEffectType} type Type of timed effect
|
||||||
* @returns {WITimedEvent} Timed event for the entry
|
* @returns {WITimedEffect} Timed effect for the entry
|
||||||
*/
|
*/
|
||||||
#getEntryTimedEvent(entry, type) {
|
#getEntryTimedEffect(entry, type) {
|
||||||
return {
|
return {
|
||||||
hash: this.#getEntryHash(entry),
|
hash: this.#getEntryHash(entry),
|
||||||
start: this.#chat.length,
|
start: this.#chat.length,
|
||||||
@@ -455,9 +455,9 @@ class WorldInfoTimedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const key = this.#getEntryKey(entry);
|
const key = this.#getEntryKey(entry);
|
||||||
const event = this.#getEntryTimedEvent(entry, 'cooldown');
|
const effect = this.#getEntryTimedEffect(entry, 'cooldown');
|
||||||
chat_metadata.timedWorldInfo.cooldown[key] = event;
|
chat_metadata.timedWorldInfo.cooldown[key] = effect;
|
||||||
console.log(`Adding cooldown entry ${key} on ended sticky: target release @ message ID ${event.end}`);
|
console.log(`Adding cooldown entry ${key} on ended sticky: start=${effect.start}, end=${effect.end}`);
|
||||||
// Set the cooldown immediately for this evaluation
|
// Set the cooldown immediately for this evaluation
|
||||||
this.#buffer['cooldown'].push(entry);
|
this.#buffer['cooldown'].push(entry);
|
||||||
}
|
}
|
||||||
@@ -472,15 +472,15 @@ class WorldInfoTimedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes entries for a given type of timed event.
|
* Processes entries for a given type of timed effect.
|
||||||
* @param {TimedEventType} type Identifier for the type of timed event
|
* @param {TimedEffectType} type Identifier for the type of timed effect
|
||||||
* @param {WIScanEntry[]} buffer Buffer to store the entries
|
* @param {WIScanEntry[]} buffer Buffer to store the entries
|
||||||
* @param {(entry: WIScanEntry) => void} onEnded Callback for when a timed event ends
|
* @param {(entry: WIScanEntry) => void} onEnded Callback for when a timed effect ends
|
||||||
*/
|
*/
|
||||||
#checkTimedEventOfType(type, buffer, onEnded) {
|
#checkTimedEffectOfType(type, buffer, onEnded) {
|
||||||
/** @type {[string, WITimedEvent][]} */
|
/** @type {[string, WITimedEffect][]} */
|
||||||
const events = Object.entries(chat_metadata.timedWorldInfo[type]);
|
const effects = Object.entries(chat_metadata.timedWorldInfo[type]);
|
||||||
for (const [key, value] of events) {
|
for (const [key, value] of effects) {
|
||||||
console.log(`Processing ${type} entry ${key}`, value);
|
console.log(`Processing ${type} entry ${key}`, value);
|
||||||
const entry = this.#entries.find(x => String(this.#getEntryHash(x)) === String(value.hash));
|
const entry = this.#entries.find(x => String(this.#getEntryHash(x)) === String(value.hash));
|
||||||
|
|
||||||
@@ -523,17 +523,17 @@ class WorldInfoTimedEvents {
|
|||||||
/**
|
/**
|
||||||
* Checks for timed effects on chat messages.
|
* Checks for timed effects on chat messages.
|
||||||
*/
|
*/
|
||||||
checkTimedEvents() {
|
checkTimedEffects() {
|
||||||
this.#checkTimedEventOfType('sticky', this.#buffer['sticky'], this.#onStickyEndedCallback.bind(this));
|
this.#checkTimedEffectOfType('sticky', this.#buffer['sticky'], this.#onStickyEndedCallback.bind(this));
|
||||||
this.#checkTimedEventOfType('cooldown', this.#buffer['cooldown'], this.#onCooldownEndedCallback.bind(this));
|
this.#checkTimedEffectOfType('cooldown', this.#buffer['cooldown'], this.#onCooldownEndedCallback.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a timed event for a WI entry.
|
* Sets a timed effect for a WI entry.
|
||||||
* @param {TimedEventType} type Type of timed event
|
* @param {TimedEffectType} type Type of timed effect
|
||||||
* @param {WIScanEntry} entry WI entry to check
|
* @param {WIScanEntry} entry WI entry to check
|
||||||
*/
|
*/
|
||||||
#setTimedEventOfType(type, entry) {
|
#setTimedEffectOfType(type, entry) {
|
||||||
// Skip if entry does not have the type (sticky or cooldown)
|
// Skip if entry does not have the type (sticky or cooldown)
|
||||||
if (!entry[type]) {
|
if (!entry[type]) {
|
||||||
return;
|
return;
|
||||||
@@ -542,10 +542,10 @@ class WorldInfoTimedEvents {
|
|||||||
const key = this.#getEntryKey(entry);
|
const key = this.#getEntryKey(entry);
|
||||||
|
|
||||||
if (!chat_metadata.timedWorldInfo[type][key]) {
|
if (!chat_metadata.timedWorldInfo[type][key]) {
|
||||||
const event = this.#getEntryTimedEvent(entry, type);
|
const effect = this.#getEntryTimedEffect(entry, type);
|
||||||
chat_metadata.timedWorldInfo[type][key] = event;
|
chat_metadata.timedWorldInfo[type][key] = effect;
|
||||||
|
|
||||||
console.log(`Adding ${type} entry ${key}: target release @ message ID ${event.end}`);
|
console.log(`Adding ${type} entry ${key}: start=${effect.start}, end=${effect.end}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,16 +553,43 @@ class WorldInfoTimedEvents {
|
|||||||
* Sets timed effects on chat messages.
|
* Sets timed effects on chat messages.
|
||||||
* @param {WIScanEntry[]} activatedEntries Entries that were activated
|
* @param {WIScanEntry[]} activatedEntries Entries that were activated
|
||||||
*/
|
*/
|
||||||
setTimedEvents(activatedEntries) {
|
setTimedEffects(activatedEntries) {
|
||||||
for (const entry of activatedEntries) {
|
for (const entry of activatedEntries) {
|
||||||
this.#setTimedEventOfType('sticky', entry);
|
this.#setTimedEffectOfType('sticky', entry);
|
||||||
this.#setTimedEventOfType('cooldown', entry);
|
this.#setTimedEffectOfType('cooldown', entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the string is a valid timed event type.
|
* Force set a timed effect for a WI entry.
|
||||||
* @param {string} type Name of the timed event
|
* @param {TimedEffectType} type Type of timed effect
|
||||||
|
* @param {WIScanEntry} entry WI entry
|
||||||
|
* @param {boolean} newState The state of the effect
|
||||||
|
*/
|
||||||
|
setTimedEffect(type, entry, newState) {
|
||||||
|
if (!this.isValidEffectType(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = this.isEffectActive(type, entry);
|
||||||
|
|
||||||
|
if (currentState === newState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = this.#getEntryKey(entry);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
|
||||||
|
if (newState) {
|
||||||
|
const effect = this.#getEntryTimedEffect(entry, type);
|
||||||
|
chat_metadata.timedWorldInfo[type][key] = effect;
|
||||||
|
console.log(`Adding ${type} entry ${key}: start=${effect.start}, end=${effect.end}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the string is a valid timed effect type.
|
||||||
|
* @param {string} type Name of the timed effect
|
||||||
* @returns {boolean} Is recognized type
|
* @returns {boolean} Is recognized type
|
||||||
*/
|
*/
|
||||||
isValidEffectType(type) {
|
isValidEffectType(type) {
|
||||||
@@ -571,7 +598,7 @@ class WorldInfoTimedEvents {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current entry is sticky activated.
|
* Check if the current entry is sticky activated.
|
||||||
* @param {TimedEventType} type Type of timed event
|
* @param {TimedEffectType} type Type of timed effect
|
||||||
* @param {WIScanEntry} entry WI entry to check
|
* @param {WIScanEntry} entry WI entry to check
|
||||||
* @returns {boolean} True if the entry is active
|
* @returns {boolean} True if the entry is active
|
||||||
*/
|
*/
|
||||||
@@ -584,7 +611,7 @@ class WorldInfoTimedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean-up previously set timed events.
|
* Clean-up previously set timed effects.
|
||||||
*/
|
*/
|
||||||
cleanUp() {
|
cleanUp() {
|
||||||
for (const buffer of Object.values(this.#buffer)) {
|
for (const buffer of Object.values(this.#buffer)) {
|
||||||
@@ -971,6 +998,76 @@ function registerWorldInfoSlashCommands() {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setTimedEffectCallback(args, value) {
|
||||||
|
if (!getCurrentChatId()) {
|
||||||
|
throw new Error('This command can only be used in chat');
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = resolveVariable(args.file);
|
||||||
|
const uid = resolveVariable(args.uid);
|
||||||
|
const effect = args.effect;
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
toastr.warning('New state is required');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = await getEntriesFromFile(file);
|
||||||
|
|
||||||
|
if (!entries) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {WIScanEntry} */
|
||||||
|
const entry = structuredClone(entries.find(x => String(x.uid) === String(uid)));
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
toastr.warning('Valid UID is required');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.world = file; // Required by the timed effects manager
|
||||||
|
const chat = getContext().chat.filter(x => !x.is_system).map(x => x.mes);
|
||||||
|
const timedEffects = new WorldInfoTimedEffects(chat, entries);
|
||||||
|
|
||||||
|
if (!timedEffects.isValidEffectType(effect)) {
|
||||||
|
toastr.warning('Valid effect type is required');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry[effect]) {
|
||||||
|
toastr.warning('This entry does not have the selected effect. Configure it in the editor first.');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNewEffectState = () => {
|
||||||
|
const currentState = timedEffects.isEffectActive(effect, entry);
|
||||||
|
|
||||||
|
if (['toggle', 't', ''].includes(value.trim().toLowerCase())) {
|
||||||
|
return !currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTrueBoolean(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFalseBoolean(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentState;
|
||||||
|
};
|
||||||
|
|
||||||
|
timedEffects.checkTimedEffects();
|
||||||
|
const newEffectState = getNewEffectState();
|
||||||
|
timedEffects.setTimedEffect(effect, entry, newEffectState);
|
||||||
|
|
||||||
|
await saveMetadata();
|
||||||
|
toastr.success(`Timed effect "${effect}" for entry ${entry.uid} is now ${newEffectState ? 'active' : 'inactive'}`);
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
/** A collection of local enum providers for this context of world info */
|
/** A collection of local enum providers for this context of world info */
|
||||||
const localEnumProviders = {
|
const localEnumProviders = {
|
||||||
/** All possible fields that can be set in a WI entry */
|
/** All possible fields that can be set in a WI entry */
|
||||||
@@ -990,6 +1087,11 @@ function registerWorldInfoSlashCommands() {
|
|||||||
new SlashCommandEnumValue(uid, `${data.comment ? `${data.comment}: ` : ''}${data.key.join(', ')}${data.keysecondary?.length ? ` [${Object.entries(world_info_logic).find(([_, value]) => value == data.selectiveLogic)[0]}] ${data.keysecondary.join(', ')}` : ''} [${getWiPositionString(data)}]`,
|
new SlashCommandEnumValue(uid, `${data.comment ? `${data.comment}: ` : ''}${data.key.join(', ')}${data.keysecondary?.length ? ` [${Object.entries(world_info_logic).find(([_, value]) => value == data.selectiveLogic)[0]}] ${data.keysecondary.join(', ')}` : ''} [${getWiPositionString(data)}]`,
|
||||||
enumTypes.enum, enumIcons.getWiStatusIcon(data)));
|
enumTypes.enum, enumIcons.getWiStatusIcon(data)));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
timedEffects: () => [
|
||||||
|
new SlashCommandEnumValue('sticky', 'Stays active for N messages', enumTypes.enum, '📌'),
|
||||||
|
new SlashCommandEnumValue('cooldown', 'Cooldown for N messages', enumTypes.enum, '⌛'),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
function getWiPositionString(entry) {
|
function getWiPositionString(entry) {
|
||||||
@@ -1203,7 +1305,57 @@ function registerWorldInfoSlashCommands() {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'wi-set-timed-effect',
|
||||||
|
callback: setTimedEffectCallback,
|
||||||
|
namedArgumentList: [
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'file',
|
||||||
|
description: 'book name',
|
||||||
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
|
isRequired: true,
|
||||||
|
enumProvider: commonEnumProviders.worlds,
|
||||||
|
}),
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'uid',
|
||||||
|
description: 'record UID',
|
||||||
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
|
isRequired: true,
|
||||||
|
enumProvider: localEnumProviders.wiUids,
|
||||||
|
}),
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'effect',
|
||||||
|
description: 'effect name',
|
||||||
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
|
isRequired: true,
|
||||||
|
enumProvider: localEnumProviders.timedEffects,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
unnamedArgumentList: [
|
||||||
|
SlashCommandArgument.fromProps({
|
||||||
|
description: 'new state of the effect',
|
||||||
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
|
isRequired: true,
|
||||||
|
acceptsMultiple: false,
|
||||||
|
enumList: commonEnumProviders.boolean('onOffToggle')(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
helpString: `
|
||||||
|
<div>
|
||||||
|
Set a timed effect for the record with the UID from the specified book. The duration must be set in the entry itself.
|
||||||
|
Will only be applied for the current chat. Enabling an effect that was already active refreshes the duration.
|
||||||
|
If the last chat message is swiped or deleted, the effect will be removed.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Example:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<pre><code>/wi-set-timed-effect file=chatLore uid=123 effect=sticky on</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// World Info Editor
|
// World Info Editor
|
||||||
@@ -3214,9 +3366,9 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
|
|
||||||
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
|
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
|
||||||
const sortedEntries = await getSortedEntries();
|
const sortedEntries = await getSortedEntries();
|
||||||
const timedEvents = new WorldInfoTimedEvents(chat, sortedEntries);
|
const timedEffects = new WorldInfoTimedEffects(chat, sortedEntries);
|
||||||
|
|
||||||
!isDryRun && timedEvents.checkTimedEvents();
|
!isDryRun && timedEffects.checkTimedEffects();
|
||||||
|
|
||||||
if (sortedEntries.length === 0) {
|
if (sortedEntries.length === 0) {
|
||||||
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
||||||
@@ -3259,8 +3411,8 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSticky = timedEvents.isEffectActive('sticky', entry);
|
const isSticky = timedEffects.isEffectActive('sticky', entry);
|
||||||
const isCooldown = timedEvents.isEffectActive('cooldown', entry);
|
const isCooldown = timedEffects.isEffectActive('cooldown', entry);
|
||||||
|
|
||||||
if (isCooldown && !isSticky) {
|
if (isCooldown && !isSticky) {
|
||||||
console.debug(`WI entry ${entry.uid} suppressed by cooldown`);
|
console.debug(`WI entry ${entry.uid} suppressed by cooldown`);
|
||||||
@@ -3363,7 +3515,7 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
const rollValue = Math.random() * 100;
|
const rollValue = Math.random() * 100;
|
||||||
|
|
||||||
if (entry.useProbability && rollValue > entry.probability) {
|
if (entry.useProbability && rollValue > entry.probability) {
|
||||||
const isSticky = timedEvents.isEffectActive('sticky', entry);
|
const isSticky = timedEffects.isEffectActive('sticky', entry);
|
||||||
if (!isSticky) {
|
if (!isSticky) {
|
||||||
console.debug(`WI entry ${entry.uid} ${entry.key} failed probability check, skipping`);
|
console.debug(`WI entry ${entry.uid} ${entry.key} failed probability check, skipping`);
|
||||||
failedProbabilityChecks.add(entry);
|
failedProbabilityChecks.add(entry);
|
||||||
@@ -3496,9 +3648,9 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);
|
context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);
|
||||||
}
|
}
|
||||||
|
|
||||||
!isDryRun && timedEvents.setTimedEvents(Array.from(allActivatedEntries));
|
!isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries));
|
||||||
buffer.resetExternalEffects();
|
buffer.resetExternalEffects();
|
||||||
timedEvents.cleanUp();
|
timedEffects.cleanUp();
|
||||||
|
|
||||||
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
|
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user