class CardDisplay implements ICardElement { readonly card: Card; level: number; readonly element: HTMLElement; readonly svg: SVGSVGElement; private readonly sizeElement: SVGTextElement; private readonly specialCostGroup: SVGGElement; private idNumber: number; private static nextIdNumber = 0; private static getGradientId(baseId: string, scale: number) { if (scale >= 20) return baseId; const roundedScale = Math.round(scale * 20); if (roundedScale >= 20) return baseId; const id = `${baseId}${roundedScale}`; if (document.getElementById(id)) return id; const baseElement = document.getElementById(baseId); if (!baseElement) throw new Error(`Base gradient element '${baseId}' not found.`); const el = baseElement!.cloneNode(true); baseElement.insertAdjacentElement('afterend', el); el.setAttribute('id', id); el.setAttribute('gradientTransform', `translate(${-6350 / roundedScale + 317.5} 0) scale(${20 / roundedScale} 1)`); return id; } constructor(card: Card, level: number, elementType: string = 'div') { this.idNumber = CardDisplay.nextIdNumber++; this.card = card; this.level = level; const element = document.createElement(elementType); element.classList.add('card'); element.classList.add([ 'common', 'rare', 'fresh' ][card.rarity]); this.element = element; const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('viewBox', '0 0 442 616'); svg.setAttribute('alt', card.name); this.svg = svg; element.appendChild(svg); if (card.isCustom) svg.classList.add('custom'); else if (card.isUpcoming) svg.classList.add('upcoming'); svg.dataset.cardNumber = card.number.toString(); svg.style.setProperty("--number", card.number.toString()); // Background const image = document.createElementNS('http://www.w3.org/2000/svg', 'image'); image.setAttribute('class', 'cardDisplayBackground'); image.setAttribute('href', `assets/external/CardBackground${card.isCustom ? '-custom' : ''}-${card.rarity}-${level > 0 ? '1' : '0'}.webp`); image.setAttribute('width', '100%'); image.setAttribute('height', '100%'); svg.appendChild(image); if (level > 0) { const r1 = card.inkColour1.r / 255; const g1 = card.inkColour1.g / 255; const b1 = card.inkColour1.b / 255; const dr = (card.inkColour2.r - card.inkColour1.r) / 255; const dg = (card.inkColour2.g - card.inkColour1.g) / 255; const db = (card.inkColour2.b - card.inkColour1.b) / 255; svg.insertAdjacentHTML('beforeend', ` `); } // Art if (card.imageUrl) { const image2 = document.createElementNS('http://www.w3.org/2000/svg', 'image'); image2.setAttribute('class', 'cardArt'); image2.setAttribute('href', card.imageUrl); image2.setAttribute('width', '100%'); image2.setAttribute('height', '100%'); svg.appendChild(image2); } // Grid const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); g.setAttribute('class', 'cardGrid'); g.setAttribute('transform', 'translate(264 420) rotate(6.5) scale(0.197)'); svg.appendChild(g); CardDisplay.CreateSvgCardGrid(card, g); // Name const text1 = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text1.setAttribute('class', 'cardDisplayName'); text1.setAttribute('x', '50%'); text1.setAttribute('y', '19%'); text1.setAttribute('text-anchor', 'middle'); text1.setAttribute('font-size', '53'); text1.setAttribute('font-weight', 'bold'); text1.setAttribute('stroke', 'black'); text1.setAttribute('stroke-width', '10.5'); text1.setAttribute('stroke-linejoin', 'round'); text1.setAttribute('paint-order', 'stroke'); text1.setAttribute('word-spacing', '-10'); text1.setAttribute('transform-origin', 'center'); text1.setAttribute('transform', `scale(${card.textScale} 1)`); switch (card.rarity) { case Rarity.Common: text1.setAttribute('fill', '#6038FF'); break; case Rarity.Rare: text1.setAttribute('fill', `url("#${CardDisplay.getGradientId('rareGradient', card.textScale)}")`); break; case Rarity.Fresh: text1.setAttribute('fill', `url("#${CardDisplay.getGradientId('freshGradient', card.textScale)}")`); break; } if (card.line1 != null && card.line2 != null) { const tspan1 = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); tspan1.setAttribute('y', '13.8%'); tspan1.appendChild(document.createTextNode(card.line1)); text1.appendChild(tspan1); if (!card.line1.endsWith('-') && !card.line2.startsWith('-')) { // Add a space in the middle, to be included when copying the card name. const tspanBr = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); tspanBr.setAttribute('x', '50%'); tspanBr.setAttribute('y', '19%'); tspanBr.appendChild(document.createTextNode(' ')); text1.appendChild(tspanBr); } const tspan2 = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); tspan2.setAttribute('x', '50%'); tspan2.setAttribute('y', '24.4%'); tspan2.appendChild(document.createTextNode(card.line2)); text1.appendChild(tspan2); } else text1.innerHTML = card.name; svg.appendChild(text1); // Size svg.insertAdjacentHTML('beforeend', ``); svg.insertAdjacentHTML('beforeend', `${card.size}`); this.sizeElement = svg.lastElementChild as SVGTextElement; // Special cost const g2 = document.createElementNS('http://www.w3.org/2000/svg', 'g'); this.specialCostGroup = g2; g2.setAttribute('class', 'specialCost'); g2.setAttribute('transform', 'translate(118 561) scale(0.222)'); svg.appendChild(g2); this.setSpecialCost(card.specialCost); } static CreateSvgCardGrid(card: Card, parent: SVGElement) { for (var y = 0; y < 8; y++) { for (var x = 0; x < 8; x++) { if (card.grid[x][y] == Space.Empty) { const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.classList.add('empty'); rect.setAttribute('x', (100 * x + 3).toString()); rect.setAttribute('y', (100 * y + 3).toString()); rect.setAttribute('width', '94'); rect.setAttribute('height', '94'); parent.appendChild(rect); } else { const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.classList.add(card.grid[x][y] == Space.SpecialInactive1 ? 'special' : 'ink'); const elements: Element[] = [rect]; const image = document.createElementNS('http://www.w3.org/2000/svg', 'image'); image.setAttribute('href', card.grid[x][y] == Space.SpecialInactive1 ? 'assets/SpecialOverlay.webp' : 'assets/InkOverlay.webp'); elements.push(image); for (const el of elements) { el.setAttribute('x', (100 * x).toString()); el.setAttribute('y', (100 * y).toString()); el.setAttribute('width', '100'); el.setAttribute('height', '100'); parent.appendChild(el); } } } } } setSpecialCost(value: number) { clearChildren(this.specialCostGroup); for (let i = 0; i < value; i++) { let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); const image = document.createElementNS('http://www.w3.org/2000/svg', 'image'); image.setAttribute('href', 'assets/SpecialOverlay.webp'); for (const el of [ rect, image ]) { el.setAttribute('x', (110 * (i % 5)).toString()); el.setAttribute('y', (-125 * Math.floor(i / 5)).toString()); el.setAttribute('width', '95'); el.setAttribute('height', '95'); this.specialCostGroup.appendChild(el); } } } setSize(value: number) { this.sizeElement.innerHTML = value.toString(); } }