feat: add ring emoji for proposals and partner bond display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import type { EntityState } from '@dflike/shared';
|
||||
|
||||
const EMOJI_POSITIVE = '\u{1F60A}';
|
||||
const EMOJI_NEGATIVE = '\u{1F620}';
|
||||
const EMOJI_PROPOSAL = '\u{1F48D}';
|
||||
|
||||
export class InteractionEmojiManager {
|
||||
private activeEmojis: Map<number, HTMLDivElement> = new Map();
|
||||
@@ -18,10 +19,10 @@ export class InteractionEmojiManager {
|
||||
const emoting = new Set<number>();
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.socialState?.phase === 'emoting' && entity.socialState.outcome) {
|
||||
if ((entity.socialState?.phase === 'emoting' || entity.socialState?.phase === 'proposing') && entity.socialState.outcome) {
|
||||
emoting.add(entity.id);
|
||||
if (!this.activeEmojis.has(entity.id)) {
|
||||
this.createEmoji(entity.id, entity.socialState.outcome);
|
||||
this.createEmoji(entity.id, entity.socialState.outcome, entity.socialState.phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +38,7 @@ export class InteractionEmojiManager {
|
||||
}
|
||||
}
|
||||
|
||||
private createEmoji(entityId: number, outcome: string): void {
|
||||
private createEmoji(entityId: number, outcome: string, phase?: string): void {
|
||||
const div = document.createElement('div');
|
||||
div.style.cssText = `
|
||||
position: fixed;
|
||||
@@ -47,7 +48,13 @@ export class InteractionEmojiManager {
|
||||
transform: translate(-50%, -100%);
|
||||
`;
|
||||
div.style.animation = 'emoji-float 2s ease-out forwards';
|
||||
div.textContent = outcome === 'positive' ? EMOJI_POSITIVE : EMOJI_NEGATIVE;
|
||||
let emoji: string;
|
||||
if (phase === 'proposing') {
|
||||
emoji = EMOJI_PROPOSAL;
|
||||
} else {
|
||||
emoji = outcome === 'positive' ? EMOJI_POSITIVE : EMOJI_NEGATIVE;
|
||||
}
|
||||
div.textContent = emoji;
|
||||
|
||||
// Self-cleanup when animation completes
|
||||
div.addEventListener('animationend', () => {
|
||||
|
||||
@@ -483,7 +483,7 @@ export class NpcInfoPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
const tierOrder = ['Partner', 'Close Friend', 'Friend', 'Acquaintance', 'Stranger', 'Wary', 'Rival', 'Enemy', 'Nemesis'];
|
||||
const tierOrder = ['Partner', 'Devoted', 'Close Friend', 'Friend', 'Acquaintance', 'Stranger', 'Wary', 'Rival', 'Enemy', 'Nemesis'];
|
||||
const groups = new Map<string, typeof relationships>();
|
||||
const memories: typeof relationships = [];
|
||||
|
||||
@@ -500,7 +500,7 @@ export class NpcInfoPanel {
|
||||
this.relationshipsContent.innerHTML = '';
|
||||
|
||||
const tierIcons: Record<string, string> = {
|
||||
'Partner': '\u2665', 'Close Friend': '\u2605', 'Friend': '\u25C6',
|
||||
'Partner': '\u2665', 'Devoted': '\u2764', 'Close Friend': '\u2605', 'Friend': '\u25C6',
|
||||
'Acquaintance': '\u25CB', 'Stranger': '\u00B7',
|
||||
'Wary': '\u25B2', 'Rival': '\u2716', 'Enemy': '\u2620', 'Nemesis': '\u2620',
|
||||
};
|
||||
@@ -525,7 +525,7 @@ export class NpcInfoPanel {
|
||||
tierEl.appendChild(tierLabel);
|
||||
|
||||
for (const rel of shown) {
|
||||
tierEl.appendChild(this.createRelSlider(rel.name, rel.value, tier));
|
||||
tierEl.appendChild(this.createRelSlider(rel.name, rel.value, tier, rel.bond));
|
||||
}
|
||||
|
||||
if (hidden.length > 0) {
|
||||
@@ -533,7 +533,7 @@ export class NpcInfoPanel {
|
||||
const overflow = document.createElement('div');
|
||||
overflow.style.cssText = isExpanded ? '' : 'display: none;';
|
||||
for (const rel of hidden) {
|
||||
overflow.appendChild(this.createRelSlider(rel.name, rel.value, tier));
|
||||
overflow.appendChild(this.createRelSlider(rel.name, rel.value, tier, rel.bond));
|
||||
}
|
||||
tierEl.appendChild(overflow);
|
||||
|
||||
@@ -579,7 +579,7 @@ export class NpcInfoPanel {
|
||||
const memHidden = memories.slice(2);
|
||||
|
||||
for (const rel of memShown) {
|
||||
memEl.appendChild(this.createRelSlider(rel.name + ' \u2020', rel.value, 'memory'));
|
||||
memEl.appendChild(this.createRelSlider(rel.name + ' \u2020', rel.value, 'memory', rel.bond));
|
||||
}
|
||||
|
||||
if (memHidden.length > 0) {
|
||||
@@ -588,7 +588,7 @@ export class NpcInfoPanel {
|
||||
const overflow = document.createElement('div');
|
||||
overflow.style.cssText = isExpanded ? '' : 'display: none;';
|
||||
for (const rel of memHidden) {
|
||||
overflow.appendChild(this.createRelSlider(rel.name + ' \u2020', rel.value, 'memory'));
|
||||
overflow.appendChild(this.createRelSlider(rel.name + ' \u2020', rel.value, 'memory', rel.bond));
|
||||
}
|
||||
memEl.appendChild(overflow);
|
||||
|
||||
@@ -623,7 +623,7 @@ export class NpcInfoPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private createRelSlider(name: string, value: number, tier: string): HTMLDivElement {
|
||||
private createRelSlider(name: string, value: number, tier: string, bond?: string | null): HTMLDivElement {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = `
|
||||
display: flex;
|
||||
@@ -635,7 +635,8 @@ export class NpcInfoPanel {
|
||||
|
||||
const nameEl = document.createElement('span');
|
||||
nameEl.style.cssText = `color: ${EB.textPrimary}; min-width: 70px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`;
|
||||
nameEl.textContent = name;
|
||||
const bondIcon = bond === 'partner' ? ' \u{1F48D}' : bond === 'former_partner' ? ' \u{1F48D}\u{FE0E}' : '';
|
||||
nameEl.textContent = name + bondIcon;
|
||||
|
||||
const slider = document.createElement('div');
|
||||
slider.style.cssText = `
|
||||
@@ -682,6 +683,7 @@ export class NpcInfoPanel {
|
||||
private getRelColor(tier: string): string {
|
||||
switch (tier) {
|
||||
case 'Partner': return '#ff69b4';
|
||||
case 'Devoted': return '#ff69b4';
|
||||
case 'Close Friend': return '#58d858';
|
||||
case 'Friend': return '#40b840';
|
||||
case 'Acquaintance': return '#6868a8';
|
||||
|
||||
Reference in New Issue
Block a user