TableturfBattleApp/TableturfBattleClient/src/CardDisplay.ts
2024-03-08 12:45:03 +11:00

209 lines
8.2 KiB
TypeScript

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 = <Element> 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', `
<filter id="ink-${this.idNumber}" class="inkFilter" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="${dr} 0 0 0 ${r1} 0 ${dg} 0 0 ${g1} 0 0 ${db} 0 ${b1} 0 0 0 0.88 0"/></filter>
<image href="assets/external/CardInk_00.webp" width="100%" height="100%" clip-path="url(#cardBorder)" filter="url(#ink-${this.idNumber})"/>
<image href="assets/external/CardFrame_01.webp" width="100%" height="100%" clip-path="url(#cardBorder)"/>
`);
}
// 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', `<image class='cardSizeBackground' href='assets/external/CardCost_0${card.rarity}.webp' width='74.1' height='74.1' transform='translate(8.4 555) rotate(-45)'/>`);
svg.insertAdjacentHTML('beforeend', `<text fill='white' stroke='${card.rarity == Rarity.Common ? '#482BB4' : card.rarity == Rarity.Rare ? '#8B7E25' : '#481EF9'}' paint-order='stroke' stroke-width='5' font-size='33.4' x='13.7%' y='92.2%' text-anchor='middle'>${card.size}</text>`);
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();
}
}