Slash command to set sprite / emote. Also allow to do it per click even in online mode
This commit is contained in:
parent
f2cae64b0d
commit
bbe52886da
|
@ -2,6 +2,7 @@ import { callPopup, eventSource, event_types, getRequestHeaders, saveSettingsDeb
|
||||||
import { dragElement, isMobile } from "../../RossAscends-mods.js";
|
import { dragElement, isMobile } from "../../RossAscends-mods.js";
|
||||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js";
|
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js";
|
||||||
import { loadMovingUIState, power_user } from "../../power-user.js";
|
import { loadMovingUIState, power_user } from "../../power-user.js";
|
||||||
|
import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
|
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
|
@ -125,15 +126,7 @@ async function visualNovelSetCharacterSprites(container, name, expression) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let spriteFolderName = character.name;
|
const spriteFolderName = getSpriteFolderName({ original_avatar: character.avatar }, character.name);
|
||||||
const avatarFileName = getSpriteFolderName({ original_avatar: character.avatar });
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// download images if not downloaded yet
|
// download images if not downloaded yet
|
||||||
if (spriteCache[spriteFolderName] === undefined) {
|
if (spriteCache[spriteFolderName] === undefined) {
|
||||||
|
@ -270,16 +263,7 @@ async function setLastMessageSprite(img, avatar, labels) {
|
||||||
|
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
const text = lastMessage.mes || '';
|
const text = lastMessage.mes || '';
|
||||||
let spriteFolderName = lastMessage.name;
|
const spriteFolderName = getSpriteFolderName(lastMessage);
|
||||||
const avatarFileName = getSpriteFolderName(lastMessage);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sprites = spriteCache[spriteFolderName] || [];
|
const sprites = spriteCache[spriteFolderName] || [];
|
||||||
const label = await getExpressionLabel(text);
|
const label = await getExpressionLabel(text);
|
||||||
const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : '';
|
const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : '';
|
||||||
|
@ -365,7 +349,7 @@ async function setImage(img, path) {
|
||||||
expressionClone.removeClass('default');
|
expressionClone.removeClass('default');
|
||||||
expressionClone.off('error');
|
expressionClone.off('error');
|
||||||
expressionClone.on('error', function () {
|
expressionClone.on('error', function () {
|
||||||
console.debug('Expression image error', sprite.path);
|
console.debug('Expression image error', path);
|
||||||
$(this).attr('src', '');
|
$(this).attr('src', '');
|
||||||
$(this).off('error');
|
$(this).off('error');
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -419,17 +403,7 @@ async function loadLiveChar() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = getContext();
|
const spriteFolderName = getSpriteFolderName();
|
||||||
let spriteFolderName = context.name2;
|
|
||||||
const message = getLastCharacterMessage();
|
|
||||||
const avatarFileName = getSpriteFolderName(message);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
|
const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
|
||||||
|
|
||||||
|
@ -468,7 +442,7 @@ async function loadLiveChar() {
|
||||||
function handleImageChange() {
|
function handleImageChange() {
|
||||||
const imgElement = document.querySelector('img#expression-image.expression');
|
const imgElement = document.querySelector('img#expression-image.expression');
|
||||||
|
|
||||||
if (!imgElement) {
|
if (!imgElement || !(imgElement instanceof HTMLImageElement)) {
|
||||||
console.log("Cannot find addExpressionImage()");
|
console.log("Cannot find addExpressionImage()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -480,7 +454,7 @@ function handleImageChange() {
|
||||||
if (imgElement.src !== talkingheadResultFeedSrc) {
|
if (imgElement.src !== talkingheadResultFeedSrc) {
|
||||||
const expressionImageElement = document.querySelector('.expression_list_image');
|
const expressionImageElement = document.querySelector('.expression_list_image');
|
||||||
|
|
||||||
if (expressionImageElement) {
|
if (expressionImageElement && expressionImageElement instanceof HTMLImageElement) {
|
||||||
doExtrasFetch(expressionImageElement.src, {
|
doExtrasFetch(expressionImageElement.src, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
})
|
})
|
||||||
|
@ -516,12 +490,14 @@ async function moduleWorker() {
|
||||||
|
|
||||||
//clear expression
|
//clear expression
|
||||||
let imgElement = document.getElementById('expression-image');
|
let imgElement = document.getElementById('expression-image');
|
||||||
|
if (imgElement && imgElement instanceof HTMLImageElement) {
|
||||||
imgElement.src = "";
|
imgElement.src = "";
|
||||||
|
}
|
||||||
|
|
||||||
//set checkbox to global var
|
//set checkbox to global var
|
||||||
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
|
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
|
||||||
if (extension_settings.expressions.talkinghead) {
|
if (extension_settings.expressions.talkinghead) {
|
||||||
settalkingheadState(extension_settings.expressions.talkinghead);
|
setTalkingHeadState(extension_settings.expressions.talkinghead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,15 +521,7 @@ async function moduleWorker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
let spriteFolderName = currentLastMessage.name;
|
let spriteFolderName = getSpriteFolderName(currentLastMessage, currentLastMessage.name);
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// character has no expressions or it is not loaded
|
// character has no expressions or it is not loaded
|
||||||
if (Object.keys(spriteCache).length === 0) {
|
if (Object.keys(spriteCache).length === 0) {
|
||||||
|
@ -627,18 +595,8 @@ async function moduleWorker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function talkingheadcheck() {
|
async function talkingHeadCheck() {
|
||||||
const context = getContext();
|
let spriteFolderName = getSpriteFolderName();
|
||||||
let spriteFolderName = context.name2;
|
|
||||||
const message = getLastCharacterMessage();
|
|
||||||
const avatarFileName = getSpriteFolderName(message);
|
|
||||||
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
|
|
||||||
e.name == avatarFileName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (expressionOverride && expressionOverride.path) {
|
|
||||||
spriteFolderName = expressionOverride.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await validateImages(spriteFolderName);
|
await validateImages(spriteFolderName);
|
||||||
|
@ -659,11 +617,25 @@ async function talkingheadcheck() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function settalkingheadState(switch_var) {
|
function getSpriteFolderName(characterMessage = null, characterName = null) {
|
||||||
|
const context = getContext();
|
||||||
|
let spriteFolderName = characterName ?? context.name2;
|
||||||
|
const message = characterMessage ?? getLastCharacterMessage();
|
||||||
|
const avatarFileName = getFolderNameByMessage(message);
|
||||||
|
const expressionOverride = extension_settings.expressionOverrides.find(e => e.name == avatarFileName);
|
||||||
|
|
||||||
|
if (expressionOverride && expressionOverride.path) {
|
||||||
|
spriteFolderName = expressionOverride.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return spriteFolderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTalkingHeadState(switch_var) {
|
||||||
extension_settings.expressions.talkinghead = switch_var; // Store setting
|
extension_settings.expressions.talkinghead = switch_var; // Store setting
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
|
||||||
talkingheadcheck().then(result => {
|
talkingHeadCheck().then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
//console.log("talkinghead exists!");
|
//console.log("talkinghead exists!");
|
||||||
|
|
||||||
|
@ -672,7 +644,7 @@ function settalkingheadState(switch_var) {
|
||||||
} else {
|
} else {
|
||||||
unloadLiveChar();
|
unloadLiveChar();
|
||||||
}
|
}
|
||||||
handleImageChange(switch_var); // Change image as needed
|
handleImageChange(); // Change image as needed
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -681,7 +653,7 @@ function settalkingheadState(switch_var) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSpriteFolderName(message) {
|
function getFolderNameByMessage(message) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
let avatarPath = '';
|
let avatarPath = '';
|
||||||
|
|
||||||
|
@ -712,6 +684,31 @@ async function sendExpressionCall(name, expression, force, vnMode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setSpriteSlashCommand(_, spriteId) {
|
||||||
|
if (!spriteId) {
|
||||||
|
console.log('No sprite id provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spriteId = spriteId.trim().toLowerCase();
|
||||||
|
|
||||||
|
const spriteFolderName = getSpriteFolderName();
|
||||||
|
await validateImages(spriteFolderName);
|
||||||
|
|
||||||
|
// Fuzzy search for sprite
|
||||||
|
const fuse = new Fuse(spriteCache[spriteFolderName], { keys: ['label'] });
|
||||||
|
const results = fuse.search(spriteId);
|
||||||
|
const spriteItem = results[0]?.item;
|
||||||
|
|
||||||
|
if (!spriteItem) {
|
||||||
|
console.log('No sprite found for search term ' + spriteId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vnMode = isVisualNovelMode();
|
||||||
|
await sendExpressionCall(spriteFolderName, spriteItem.label, true, vnMode);
|
||||||
|
}
|
||||||
|
|
||||||
async function getExpressionLabel(text) {
|
async function getExpressionLabel(text) {
|
||||||
// Return if text is undefined, saving a costly fetch request
|
// Return if text is undefined, saving a costly fetch request
|
||||||
if (!modules.includes('classify') || !text) {
|
if (!modules.includes('classify') || !text) {
|
||||||
|
@ -968,12 +965,12 @@ async function setExpression(character, expression, force) {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
||||||
talkingheadcheck().then(result => {
|
talkingHeadCheck().then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Find the <img> element with id="expression-image" and class="expression"
|
// Find the <img> element with id="expression-image" and class="expression"
|
||||||
const imgElement = document.querySelector('img#expression-image.expression');
|
const imgElement = document.querySelector('img#expression-image.expression');
|
||||||
//console.log("searching");
|
//console.log("searching");
|
||||||
if (imgElement) {
|
if (imgElement && imgElement instanceof HTMLImageElement) {
|
||||||
//console.log("setting value");
|
//console.log("setting value");
|
||||||
imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
|
imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
|
||||||
}
|
}
|
||||||
|
@ -988,18 +985,10 @@ async function setExpression(character, expression, force) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickExpressionImage() {
|
function onClickExpressionImage() {
|
||||||
// online mode doesn't need force set
|
|
||||||
if (modules.includes('classify')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expression = $(this).attr('id');
|
const expression = $(this).attr('id');
|
||||||
const name = getLastCharacterMessage().name;
|
setSpriteSlashCommand({}, expression);
|
||||||
|
}
|
||||||
|
|
||||||
if ($(this).find('.failure').length === 0) {
|
|
||||||
setExpression(name, expression, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function handleFileUpload(url, formData) {
|
async function handleFileUpload(url, formData) {
|
||||||
try {
|
try {
|
||||||
const data = await jQuery.ajax({
|
const data = await jQuery.ajax({
|
||||||
|
@ -1057,7 +1046,7 @@ async function onClickExpressionUpload(event) {
|
||||||
async function onClickExpressionOverrideButton() {
|
async function onClickExpressionOverrideButton() {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
const avatarFileName = getFolderNameByMessage(currentLastMessage);
|
||||||
|
|
||||||
// If the avatar name couldn't be found, abort.
|
// If the avatar name couldn't be found, abort.
|
||||||
if (!avatarFileName) {
|
if (!avatarFileName) {
|
||||||
|
@ -1066,7 +1055,7 @@ async function onClickExpressionOverrideButton() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const overridePath = $("#expression_override").val();
|
const overridePath = String($("#expression_override").val());
|
||||||
const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) =>
|
const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) =>
|
||||||
e.name == avatarFileName
|
e.name == avatarFileName
|
||||||
);
|
);
|
||||||
|
@ -1191,7 +1180,7 @@ async function onClickExpressionDelete(event) {
|
||||||
|
|
||||||
function setExpressionOverrideHtml(forceClear = false) {
|
function setExpressionOverrideHtml(forceClear = false) {
|
||||||
const currentLastMessage = getLastCharacterMessage();
|
const currentLastMessage = getLastCharacterMessage();
|
||||||
const avatarFileName = getSpriteFolderName(currentLastMessage);
|
const avatarFileName = getFolderNameByMessage(currentLastMessage);
|
||||||
if (!avatarFileName) {
|
if (!avatarFileName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1249,7 +1238,9 @@ function setExpressionOverrideHtml(forceClear = false) {
|
||||||
$('.expression_settings').hide();
|
$('.expression_settings').hide();
|
||||||
|
|
||||||
$('#image_type_toggle').on('click', function () {
|
$('#image_type_toggle').on('click', function () {
|
||||||
settalkingheadState(this.checked);
|
if (this instanceof HTMLInputElement) {
|
||||||
|
setTalkingHeadState(this.checked);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,4 +1261,5 @@ function setExpressionOverrideHtml(forceClear = false) {
|
||||||
});
|
});
|
||||||
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
|
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
|
||||||
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
|
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
|
||||||
|
registerSlashCommand('sprite', setSpriteSlashCommand, ['emote'], '<span class="monospace">spriteId</span> – force sets the sprite for the current character', true, true);
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in New Issue