Skip to content
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

Overlay Emote Modifier #6590

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
27 changes: 27 additions & 0 deletions src/modules/chat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const EMOTE_MODIFIERS = {
'c!': 'bttv-emote-modifier-cursed',
'l!': 'bttv-emote-modifier-rotate-left',
'r!': 'bttv-emote-modifier-rotate-right',
'o!': 'bttv-emote-modifier-overlay',
ffzW: 'bttv-emote-modifier-wide',
ffzX: 'bttv-emote-modifier-flip-horizontal',
ffzY: 'bttv-emote-modifier-flip-vertical',
Expand Down Expand Up @@ -288,6 +289,7 @@ class ChatModule {

const parts = data.split(' ');
const partMetadata = [];
const isOverlay = [];
let hasModifiers = false;
let modified = false;
for (let j = 0; j < parts.length; j++) {
Expand Down Expand Up @@ -325,6 +327,7 @@ class ChatModule {
let modifiers = [];
if (hasModifiers && isEmoteOrSuffixModifier) {
let detectedEmote = false;
let predecessor = null;
// we search backwards to find the emote and any modifiers
for (let k = j; k >= 0; k--) {
const partMetadataItem = partMetadata[k];
Expand All @@ -341,11 +344,35 @@ class ChatModule {
(!detectedEmote && partMetadataItem.type === 'prefix') ||
(detectedEmote && partMetadataItem.type === 'suffix')
) {
predecessor = k;
break;
}
modifiers.push(partMetadataItem);
parts[k] = null;
}

const overlayModifierPredicate = ({modifier}) => modifier === 'o!';
const negate = (pred) => (x) => !pred(x);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we pull these function definitions out of the hot path? they look re-usable

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved these out and near the definitions of the modifiers.


// An emote may be an overlay if it satisfies the following conditions:
// 1. It has a "predecessor" part.
// 2. That predecessor part is an emote.
// 3. That predecessor emote is not an overlay.
// 4. The user has applied the overlay modifier.
// This ensures that overlays do not stack and they only stack on another emote.
if (
predecessor != null &&
partMetadata[predecessor]?.emote &&
!isOverlay[predecessor] &&
modifiers.some(overlayModifierPredicate)
) {
// Keep track of this part being an overlay.
isOverlay[j] = true;
} else {
// Strip the overlay modifier from the part.
modifiers = modifiers.filter(negate(overlayModifierPredicate));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stripping it works, but it also results in the modifier going missing from the output. for other modifiers, we do not show the modified emote but we show the modifier was sent when used incorrectly. is there a way to replicate this behavior here?

image

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as an aside, do you have an image asset for this modifier? if not, we can probably have one made

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to replicate this behavior here?

I've modified it so that if an overlay modifier is removed, it replaces the corresponding parts in the part array.
Screenshot 2024-02-17 094835

do you have an image asset for this modifier?

I'm no artist, so feel free to use or not use this.
overlay2

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dclstn any ideas on the icon for this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like so perhaps:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's so much better 👏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the image is small in emote form, it kind of just looks like 1 square. Maybe make it so that the top square doesn't overlap the bottom one quite so completely?

Copy link
Owner

@night night Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with Dallas' feedback. you can't tell what this is when smaller. it also resembles a copy button, which is not the intention.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How's this?

downsized for reference:


Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks better

}

// if the emote is only a suffix modifier, render it without its effect
if (modifiers.length === 1 && modifiers[0].type === 'suffix' && emoteIndex === j) {
modifiers = [];
Expand Down
4 changes: 4 additions & 0 deletions src/modules/chat/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@
.bttv-emote-modifier-cursed img {
filter: grayscale(1) brightness(0.7) contrast(2.5);
}

.bttv-emote-modifier-overlay {
margin-left: -32px;
}
2 changes: 1 addition & 1 deletion src/modules/settings/components/settings/global/Emotes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function EmotesModule() {
{formatMessage(
{
defaultMessage:
'Emote modifiers allow you to transform emotes in realtime. Wide: <code>w! emoteName</code>, Horizontal Flip: <code>h! emoteName</code>, Vertical Flip: <code>v! emoteName</code>, Zero-Width: <code>z! emoteName</code>, Rotate Left: <code>l! emoteName</code>, Rotate Right: <code>r! emoteName</code>, Cursed: <code>c! emoteName</code>',
'Emote modifiers allow you to transform emotes in realtime. Wide: <code>w! emoteName</code>, Horizontal Flip: <code>h! emoteName</code>, Vertical Flip: <code>v! emoteName</code>, Zero-Width: <code>z! emoteName</code>, Rotate Left: <code>l! emoteName</code>, Rotate Right: <code>r! emoteName</code>, Cursed: <code>c! emoteName</code>, Overlay: <code>o! emoteName</code>',
},
{
// eslint-disable-next-line react/no-unstable-nested-components
Expand Down