import {inject, Injectable} from '@angular/core'; import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object'; import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object'; import {ChatService} from "./chat.service"; @Injectable({providedIn: 'root'}) export class NameFormatService { private readonly chatService: ChatService = inject(ChatService); public getDisplayName(channel: ChatChannel): ColoredNameSegment[] { return this.parseColoredName(this.getNameFromType(channel)); } private readonly LEGACY_COLOR_MAP: Record = { '4': '#AA0000', 'c': '#FF5555', '6': '#FFAA00', 'e': '#FFFF55', '2': '#00AA00', 'a': '#55FF55', 'b': '#55FFFF', '3': '#00AAAA', '1': '#0000AA', '9': '#5555FF', 'd': '#FF55FF', '5': '#AA00AA', 'f': '#FFFFFF', '7': '#AAAAAA', '8': '#555555', '0': '#000000' }; private getNameFromType(channel: ChatChannel): string { if (channel.type === 'PARTY') { return this.chatService.partieMap().get(channel.name) ?? channel.name; } if (channel.type === 'DM' || channel.type === 'SPY') { return this.chatService.userNameMap().get(channel.name) ?? channel.name; } return channel.name; } private parseColoredName(name: string): ColoredNameSegment[] { const markerRegex = /\{#([0-9a-fA-F]{6})(<>|[<>]?)\}|&([0-9a-fA-F])/g; const segments: ColoredNameSegment[] = []; let currentColor: string | undefined; let gradientStartColor: string | undefined; let gradientStartSegmentIndex: number | undefined; let lastIndex = 0; let match: RegExpExecArray | null; while ((match = markerRegex.exec(name)) !== null) { this.addColoredTextSegment(segments, name.slice(lastIndex, match.index), currentColor); const isLegacy = !!match[3]; const markerColor = isLegacy ? this.LEGACY_COLOR_MAP[match[3]] : `#${match[1]}`; const markerType = isLegacy ? undefined : match[2]; if (markerType === '>') { currentColor = markerColor; gradientStartColor = markerColor; gradientStartSegmentIndex = segments.length; } else if (markerType === '<') { if (gradientStartColor !== undefined && gradientStartSegmentIndex !== undefined) { this.applyGradient(segments, gradientStartSegmentIndex, gradientStartColor, markerColor); } currentColor = markerColor; gradientStartColor = undefined; gradientStartSegmentIndex = undefined; } else if (markerType === '<>') { if (gradientStartColor !== undefined && gradientStartSegmentIndex !== undefined) { this.applyGradient(segments, gradientStartSegmentIndex, gradientStartColor, markerColor); } currentColor = markerColor; gradientStartColor = markerColor; gradientStartSegmentIndex = segments.length; } else { currentColor = markerColor; gradientStartColor = undefined; gradientStartSegmentIndex = undefined; } lastIndex = markerRegex.lastIndex; } this.addColoredTextSegment(segments, name.slice(lastIndex), currentColor); return segments.length > 0 ? segments : [{text: name}]; } private addColoredTextSegment(segments: ColoredNameSegment[], text: string, color?: string): void { if (text.length === 0) { return; } segments.push({text, color}); } private applyGradient( segments: ColoredNameSegment[], startSegmentIndex: number, startColor: string, endColor: string ): void { const text = segments.slice(startSegmentIndex).map((segment) => segment.text).join(''); const length = text.length; const start = this.hexToRgb(startColor); const end = this.hexToRgb(endColor); if (length === 0 || start === null || end === null) { return; } const gradientSegments: ColoredNameSegment[] = []; for (let i = 0; i < length; i++) { const ratio = length === 1 ? 0 : i / (length - 1); gradientSegments.push({ text: text[i], color: this.rgbToHex( start.r + (end.r - start.r) * ratio, start.g + (end.g - start.g) * ratio, start.b + (end.b - start.b) * ratio ) }); } segments.splice(startSegmentIndex, segments.length - startSegmentIndex, ...gradientSegments); } private hexToRgb(hex: string): { r: number; g: number; b: number } | null { const match = /^#?([0-9a-fA-F]{6})$/.exec(hex); if (!match) { return null; } const value = Number.parseInt(match[1], 16); return { r: (value >> 16) & 255, g: (value >> 8) & 255, b: value & 255 }; } private rgbToHex(r: number, g: number, b: number): string { return `#${[r, g, b] .map((value) => Math.round(value).toString(16).padStart(2, '0')) .join('')}`; } }