mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-03-21 17:34:28 -05:00
Add WIP gallery page and custom card editor
This commit is contained in:
parent
329993b15d
commit
f193477ce3
|
|
@ -57,6 +57,7 @@
|
|||
</div>
|
||||
<p>
|
||||
<a id="preGameDeckEditorButton" href="deckeditor">Edit decks</a> |
|
||||
<a id="preGameGalleryButton" href="cardlist">Card list</a> |
|
||||
<a id="preGameReplayButton" href="replay">Replay</a> |
|
||||
<a id="preGameSettingsButton" href="settings">Settings</a> |
|
||||
<a id="preGameHelpButton" href="help">Help</a>
|
||||
|
|
@ -584,6 +585,35 @@
|
|||
</section>
|
||||
<div id="deckEditorCardListBackdrop"></div>
|
||||
</div>
|
||||
<div id="galleryPage" hidden>
|
||||
<header>
|
||||
<a id="galleryBackButton" href=".">Back</a>
|
||||
<label for="gallerySortBox">
|
||||
Sort by
|
||||
<select id="gallerySortBox" autocomplete="off"></select>
|
||||
</label>
|
||||
<input type="text" id="galleryFilterBox" placeholder="Filter"/>
|
||||
<button id="galleryNewCustomCardButton">New custom card</button>
|
||||
<label for="galleryChecklistBox"><input type="checkbox" id="galleryChecklistBox" autocomplete="off"/>Checklist</label>
|
||||
Bits to complete: <span id="bitsToCompleteField"></span>
|
||||
</header>
|
||||
<main id="galleryCardList">
|
||||
</main>
|
||||
<dialog id="galleryCardDialog">
|
||||
<div id="galleryCardEditor" hidden>
|
||||
<textarea type="text" id="galleryCardEditorName" placeholder="Card name"></textarea>
|
||||
<div id="galleryCardEditorGrid"></div>
|
||||
<div id="galleryCardEditorSpecialCost">
|
||||
<label for="galleryCardEditorSpecialCostDefaultBox"><input type="checkbox" id="galleryCardEditorSpecialCostDefaultBox" checked/> Default</label>
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog">
|
||||
<button type="button" id="galleryCardEditorEditButton">Edit</button>
|
||||
<button type="submit" id="galleryCardEditorSubmitButton">Save</button>
|
||||
<button type="submit" id="galleryCardEditorCancelButton">Cancel</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
<dialog id="testStageSelectionDialog">
|
||||
<h3>Select a stage.</h3>
|
||||
<form id="testStageSelectionForm" method="dialog">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/// <reference path="CheckButton.ts"/>
|
||||
|
||||
class CardButton extends CheckButton {
|
||||
class CardButton extends CheckButton implements ICardElement {
|
||||
readonly element: HTMLButtonElement;
|
||||
private static idNumber = 0;
|
||||
|
||||
readonly card: Card;
|
||||
|
|
@ -13,6 +14,7 @@ class CardButton extends CheckButton {
|
|||
if (card.number < 0) button.classList.add('upcoming');
|
||||
button.dataset.cardNumber = card.number.toString();
|
||||
super(button);
|
||||
this.element = button;
|
||||
|
||||
this.card = card;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,26 @@
|
|||
class CardDisplay {
|
||||
class CardDisplay implements ICardElement {
|
||||
readonly card: Card;
|
||||
readonly element: SVGSVGElement;
|
||||
readonly element: HTMLElement;
|
||||
readonly svg: SVGSVGElement;
|
||||
private readonly sizeElement: SVGTextElement;
|
||||
private readonly specialCostGroup: SVGGElement;
|
||||
private level: number;
|
||||
|
||||
constructor(card: Card, level: number) {
|
||||
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
constructor(card: Card, level: number, elementType: string = 'div') {
|
||||
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 635 885');
|
||||
svg.setAttribute('alt', card.name);
|
||||
this.element = svg;
|
||||
this.svg = svg;
|
||||
element.appendChild(svg);
|
||||
|
||||
svg.classList.add('card');
|
||||
svg.classList.add([ 'common', 'rare', 'fresh' ][card.rarity]);
|
||||
if (card.number < 0) svg.classList.add('upcoming');
|
||||
svg.dataset.cardNumber = card.number.toString();
|
||||
svg.style.setProperty("--number", card.number.toString());
|
||||
|
|
@ -44,6 +55,7 @@ class CardDisplay {
|
|||
|
||||
// Grid
|
||||
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
g.setAttribute('class', 'cardGrid');
|
||||
g.setAttribute('transform', 'translate(380 604) rotate(6.5) scale(0.283)');
|
||||
svg.appendChild(g);
|
||||
|
||||
|
|
@ -54,6 +66,7 @@ class CardDisplay {
|
|||
text1.setAttribute('class', 'cardDisplayName');
|
||||
text1.setAttribute('x', '50%');
|
||||
text1.setAttribute('y', '168');
|
||||
text1.setAttribute('text-anchor', 'middle');
|
||||
text1.setAttribute('font-size', '76');
|
||||
text1.setAttribute('font-weight', 'bold');
|
||||
text1.setAttribute('stroke', 'black');
|
||||
|
|
@ -108,28 +121,17 @@ class CardDisplay {
|
|||
|
||||
// Size
|
||||
svg.insertAdjacentHTML('beforeend', `<image href='assets/external/Game Assets/CardCost_0${card.rarity}.png' width='80' height='80' transform='translate(12 798) rotate(-45) scale(1.33)'/>`);
|
||||
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='48' y='816' x='87'>${card.size}</text>`);
|
||||
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='48' y='816' x='87' 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(170 806) scale(0.32)');
|
||||
svg.appendChild(g2);
|
||||
|
||||
for (let i = 0; i < card.specialCost; 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.png');
|
||||
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');
|
||||
g2.appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
this.card = card;
|
||||
this.setSpecialCost(card.specialCost);
|
||||
}
|
||||
|
||||
static CreateSvgCardGrid(card: Card, parent: SVGElement) {
|
||||
|
|
@ -162,4 +164,24 @@ class CardDisplay {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.png');
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
class CardList {
|
||||
class CardList<T extends ICardElement> {
|
||||
readonly listElement: HTMLElement;
|
||||
readonly sortBox: HTMLSelectElement;
|
||||
readonly filterBox: HTMLInputElement;
|
||||
readonly cardButtons: CardButton[] = [ ];
|
||||
readonly cardButtons: T[] = [ ];
|
||||
|
||||
static readonly cardSortOrders: { [key: string]: (a: Card, b: Card) => number } = {
|
||||
'number': (a, b) => CardList.compareByNumber(a, b),
|
||||
|
|
@ -42,7 +42,7 @@ class CardList {
|
|||
filterBox.addEventListener('input', () => {
|
||||
const s = filterBox.value.toLowerCase();
|
||||
for (const button of this.cardButtons)
|
||||
button.buttonElement.hidden = s != '' && !button.card.name.toLowerCase().includes(s);
|
||||
button.element.hidden = s != '' && !button.card.name.toLowerCase().includes(s);
|
||||
});
|
||||
|
||||
for (const label in CardList.cardSortOrders) {
|
||||
|
|
@ -59,17 +59,17 @@ class CardList {
|
|||
clearChildren(this.listElement);
|
||||
this.cardButtons.sort((a, b) => sortOrder(a.card, b.card));
|
||||
for (const button of this.cardButtons)
|
||||
this.listElement.appendChild(button.buttonElement);
|
||||
this.listElement.appendChild(button.element);
|
||||
}
|
||||
}
|
||||
|
||||
static fromId(id: string, sortBoxId: string, filterBoxId: string) {
|
||||
return new CardList(document.getElementById(id)!, document.getElementById(sortBoxId) as HTMLSelectElement, document.getElementById(filterBoxId) as HTMLInputElement);
|
||||
static fromId<T extends ICardElement>(id: string, sortBoxId: string, filterBoxId: string) {
|
||||
return new CardList<T>(document.getElementById(id)!, document.getElementById(sortBoxId) as HTMLSelectElement, document.getElementById(filterBoxId) as HTMLInputElement);
|
||||
}
|
||||
|
||||
add(button: CardButton) {
|
||||
add(button: T) {
|
||||
this.cardButtons.push(button);
|
||||
this.listElement.appendChild(button.buttonElement);
|
||||
this.listElement.appendChild(button.element);
|
||||
}
|
||||
|
||||
setSortOrder(sortOrder: string) {
|
||||
|
|
@ -80,6 +80,6 @@ class CardList {
|
|||
clearFilter() {
|
||||
this.filterBox.value = '';
|
||||
for (const button of this.cardButtons)
|
||||
button.buttonElement.hidden = false;
|
||||
button.element.hidden = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,3 +52,7 @@ let userConfig = new Config();
|
|||
function saveSettings() {
|
||||
localStorage.setItem('settings', JSON.stringify(userConfig));
|
||||
}
|
||||
|
||||
function saveChecklist() {
|
||||
localStorage.setItem('checklist', JSON.stringify(ownedCards));
|
||||
}
|
||||
|
|
|
|||
4
TableturfBattleClient/src/ICardElement.ts
Normal file
4
TableturfBattleClient/src/ICardElement.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface ICardElement {
|
||||
card: Card;
|
||||
element: HTMLElement;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
const deckNameLabel2 = document.getElementById('deckName2')!;
|
||||
const deckEditSize = document.getElementById('deckEditSize')!;
|
||||
const deckCardListEdit = document.getElementById('deckCardListEdit')!;
|
||||
const cardList = CardList.fromId('cardList', 'cardListSortBox', 'cardListFilterBox');
|
||||
const cardList = CardList.fromId<CardButton>('cardList', 'cardListSortBox', 'cardListFilterBox');
|
||||
const cardListButtonGroup = new CheckButtonGroup<Card>();
|
||||
|
||||
const deckEditMenu = document.getElementById('deckEditMenu')!;
|
||||
|
|
|
|||
245
TableturfBattleClient/src/Pages/GalleryPage.ts
Normal file
245
TableturfBattleClient/src/Pages/GalleryPage.ts
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
const galleryCardList = CardList.fromId<CardDisplay>('galleryCardList', 'gallerySortBox', 'galleryFilterBox');
|
||||
const galleryBackButton = document.getElementById('galleryBackButton') as HTMLLinkElement;
|
||||
const galleryCardDialog = document.getElementById('galleryCardDialog') as HTMLDialogElement;
|
||||
|
||||
const galleryNewCustomCardButton = document.getElementById('galleryNewCustomCardButton') as HTMLButtonElement;
|
||||
const galleryChecklistBox = document.getElementById('galleryChecklistBox') as HTMLInputElement;
|
||||
const bitsToCompleteField = document.getElementById('bitsToCompleteField') as HTMLElement;
|
||||
|
||||
let galleryCardDisplay: CardDisplay | null = null;
|
||||
const galleryCardEditor = document.getElementById('galleryCardEditor') as HTMLButtonElement;
|
||||
const galleryCardEditorGridButtons: HTMLButtonElement[][] = [ ];
|
||||
const galleryCardEditorSpecialCostButtons: HTMLButtonElement[] = [ ];
|
||||
const galleryCardEditorSpecialCost = document.getElementById('galleryCardEditorSpecialCost') as HTMLElement;
|
||||
const galleryCardEditorSpecialCostDefaultBox = document.getElementById('galleryCardEditorSpecialCostDefaultBox') as HTMLInputElement;
|
||||
const galleryCardEditorEditButton = document.getElementById('galleryCardEditorEditButton') as HTMLButtonElement;
|
||||
const galleryCardEditorSubmitButton = document.getElementById('galleryCardEditorSubmitButton') as HTMLButtonElement;
|
||||
const galleryCardEditorCancelButton = document.getElementById('galleryCardEditorCancelButton') as HTMLButtonElement;
|
||||
|
||||
const ownedCards: {[key: number]: number} = { 6: 0, 34: 0, 159: 0, 13: 0, 45: 0, 137: 0, 22: 0, 52: 0, 141: 0, 28: 0, 55: 0, 103: 0, 40: 0, 56: 0, 92: 0 };
|
||||
let lastGridButton: HTMLButtonElement | null = null;
|
||||
let customCardSpecialCost = 0;
|
||||
|
||||
function showCardList() {
|
||||
showPage('gallery');
|
||||
}
|
||||
|
||||
function galleryInitCardDatabase(cards: Card[]) {
|
||||
for (const card of cards.concat(customCards)) {
|
||||
const display = new CardDisplay(card, 1, 'button');
|
||||
|
||||
const cardNumber = document.createElement('div');
|
||||
cardNumber.className = 'cardNumber';
|
||||
cardNumber.innerText = card.number >= 0 ? `No. ${card.number}` : 'Upcoming';
|
||||
display.element.insertBefore(cardNumber, display.element.firstChild);
|
||||
|
||||
galleryCardList.add(display);
|
||||
|
||||
display.element.addEventListener('click', () => {
|
||||
if (galleryChecklistBox.checked) {
|
||||
if (card.number in ownedCards) {
|
||||
delete ownedCards[card.number];
|
||||
display.element.classList.add('unowned');
|
||||
} else {
|
||||
ownedCards[card.number] = 0;
|
||||
display.element.classList.remove('unowned');
|
||||
}
|
||||
updateBitsToComplete();
|
||||
saveChecklist();
|
||||
} else {
|
||||
const existingEl = galleryCardDialog.firstElementChild;
|
||||
if (existingEl && existingEl.tagName != 'FORM')
|
||||
galleryCardDialog.removeChild(existingEl);
|
||||
const display = new CardDisplay(card, 1);
|
||||
galleryCardDisplay = display;
|
||||
galleryCardDialog.insertBefore(display.element, galleryCardDialog.firstChild);
|
||||
|
||||
galleryCardEditor.parentElement?.removeChild(galleryCardEditor);
|
||||
display.element.appendChild(galleryCardEditor);
|
||||
galleryCardEditor.hidden = true;
|
||||
display.element.classList.remove('editing');
|
||||
galleryCardEditorEditButton.hidden = false;
|
||||
galleryCardEditorSubmitButton.hidden = true;
|
||||
galleryCardEditorCancelButton.innerText = 'Close';
|
||||
|
||||
galleryCardDialog.showModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
updateBitsToComplete();
|
||||
}
|
||||
|
||||
galleryBackButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showPage('preGame');
|
||||
|
||||
if (canPushState) {
|
||||
try {
|
||||
history.pushState(null, '', '.');
|
||||
} catch {
|
||||
canPushState = false;
|
||||
}
|
||||
}
|
||||
if (location.hash)
|
||||
location.hash = '';
|
||||
});
|
||||
|
||||
galleryChecklistBox.addEventListener('change', () => {
|
||||
if (galleryChecklistBox.checked) {
|
||||
for (const cardDisplay of galleryCardList.cardButtons) {
|
||||
if (cardDisplay.card.number in ownedCards)
|
||||
cardDisplay.element.classList.remove('unowned');
|
||||
else
|
||||
cardDisplay.element.classList.add('unowned');
|
||||
}
|
||||
} else {
|
||||
for (const cardDisplay of galleryCardList.cardButtons)
|
||||
cardDisplay.element.classList.remove('unowned');
|
||||
}
|
||||
});
|
||||
|
||||
function updateBitsToComplete() {
|
||||
if (!cardDatabase.cards) throw new Error('Card database not loaded');
|
||||
let bitsRequired = 0;
|
||||
for (const card of cardDatabase.cards) {
|
||||
if (card.number in ownedCards) continue;
|
||||
switch (card.rarity) {
|
||||
case Rarity.Fresh: bitsRequired += 40; break;
|
||||
case Rarity.Rare: bitsRequired += 15; break;
|
||||
default: bitsRequired += 5; break;
|
||||
}
|
||||
}
|
||||
bitsToCompleteField.innerText = bitsRequired.toString();
|
||||
}
|
||||
|
||||
{
|
||||
const customCardsString = localStorage.getItem('customCards');
|
||||
if (customCardsString) {
|
||||
for (const card of JSON.parse(customCardsString)) {
|
||||
customCards.push(Card.fromJson(card));
|
||||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < 8; x++) {
|
||||
const row = [ ];
|
||||
for (let y = 0; y < 8; y++) {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.dataset.state = Space.Empty.toString();
|
||||
button.dataset.x = x.toString();
|
||||
button.dataset.y = y.toString();
|
||||
button.addEventListener('click', () => {
|
||||
const state = parseInt(button.dataset.state ?? '0');
|
||||
switch (state) {
|
||||
case Space.Empty:
|
||||
button.dataset.state = Space.Ink1.toString();
|
||||
break;
|
||||
case Space.Ink1:
|
||||
if (lastGridButton == button) {
|
||||
// When a space is pressed twice, move the special space there.
|
||||
for (const row of galleryCardEditorGridButtons) {
|
||||
for (const button2 of row) {
|
||||
if (button2 == button)
|
||||
button2.dataset.state = Space.SpecialInactive1.toString();
|
||||
else if (button2.dataset.state == Space.SpecialInactive1.toString())
|
||||
button2.dataset.state = Space.Ink1.toString();
|
||||
}
|
||||
}
|
||||
} else
|
||||
button.dataset.state = Space.Empty.toString();
|
||||
break;
|
||||
default:
|
||||
button.dataset.state = Space.Empty.toString();
|
||||
break;
|
||||
}
|
||||
lastGridButton = button;
|
||||
|
||||
updateCustomCardSize();
|
||||
});
|
||||
row.push(button);
|
||||
}
|
||||
galleryCardEditorGridButtons.push(row);
|
||||
}
|
||||
|
||||
const galleryCardEditorGrid = document.getElementById('galleryCardEditorGrid')!;
|
||||
for (let y = 0; y < 8; y++) {
|
||||
for (let x = 0; x < 8; x++) {
|
||||
galleryCardEditorGrid.appendChild(galleryCardEditorGridButtons[x][y]);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the saved checklist.
|
||||
const checklistString = localStorage.getItem('checklist');
|
||||
if (checklistString) {
|
||||
const cards = JSON.parse(checklistString);
|
||||
Object.assign(ownedCards, cards);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const button = document.createElement('button');
|
||||
const n = i < 5 ? i + 6 : i - 4;
|
||||
button.dataset.value = n.toString();
|
||||
galleryCardEditorSpecialCost.appendChild(button);
|
||||
galleryCardEditorSpecialCostButtons.push(button);
|
||||
button.addEventListener('click', () => {
|
||||
customCardSpecialCost = n;
|
||||
galleryCardEditorSpecialCostDefaultBox.checked = false;
|
||||
updateCustomCardSpecialCost();
|
||||
});
|
||||
}
|
||||
|
||||
function updateCustomCardSize() {
|
||||
let size = 0, hasSpecialSpace = false;
|
||||
for (const row of galleryCardEditorGridButtons) {
|
||||
for (const button2 of row) {
|
||||
switch (parseInt(button2.dataset.state!)) {
|
||||
case Space.Ink1:
|
||||
size++;
|
||||
break;
|
||||
case Space.SpecialInactive1:
|
||||
size++;
|
||||
hasSpecialSpace = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
galleryCardDisplay!.setSize(size);
|
||||
if (galleryCardEditorSpecialCostDefaultBox.checked) {
|
||||
customCardSpecialCost =
|
||||
size <= 3 ? 1
|
||||
: size <= 5 ? 2
|
||||
: size <= 8 ? 3
|
||||
: size <= 11 ? 4
|
||||
: size <= 15 ? 5
|
||||
: 6;
|
||||
if (!hasSpecialSpace && customCardSpecialCost > 3)
|
||||
customCardSpecialCost = 3;
|
||||
updateCustomCardSpecialCost();
|
||||
}
|
||||
}
|
||||
|
||||
function updateCustomCardSpecialCost() {
|
||||
galleryCardDisplay?.setSpecialCost(customCardSpecialCost);
|
||||
for (let i = 0; i < galleryCardEditorSpecialCostButtons.length; i++) {
|
||||
const button = galleryCardEditorSpecialCostButtons[i];
|
||||
if (parseInt(button.dataset.value!) <= customCardSpecialCost)
|
||||
button.classList.add('active');
|
||||
else
|
||||
button.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
galleryCardEditorSpecialCostDefaultBox.addEventListener('change', () => {
|
||||
if (galleryCardEditorSpecialCostDefaultBox.checked)
|
||||
updateCustomCardSize();
|
||||
});
|
||||
|
||||
galleryCardEditorEditButton.addEventListener('click', () => {
|
||||
galleryCardEditor.hidden = false;
|
||||
galleryCardDisplay?.element.classList.add('editing');
|
||||
galleryCardEditorEditButton.hidden = true;
|
||||
galleryCardEditorSubmitButton.hidden = false;
|
||||
galleryCardEditorCancelButton.innerText = 'Cancel';
|
||||
});
|
||||
|
|
@ -47,7 +47,7 @@ playContainers.sort((a, b) => parseInt(a.dataset.index || '0') - parseInt(b.data
|
|||
|
||||
const testControls = document.getElementById('testControls')!;
|
||||
const testDeckList = document.getElementById('testDeckList')!;
|
||||
const testAllCardsList = CardList.fromId('testAllCardsList', 'testAllCardsListSortBox', 'testAllCardsListFilterBox');
|
||||
const testAllCardsList = CardList.fromId<CardButton>('testAllCardsList', 'testAllCardsListSortBox', 'testAllCardsListFilterBox');
|
||||
const testPlacementList = document.getElementById('testPlacementList')!;
|
||||
const testDeckButton = CheckButton.fromId('testDeckButton');
|
||||
const testDeckContainer = document.getElementById('testDeckContainer')!;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const joinGameButton = document.getElementById('joinGameButton')!;
|
|||
const nameBox = document.getElementById('nameBox') as HTMLInputElement;
|
||||
const gameIDBox = document.getElementById('gameIDBox') as HTMLInputElement;
|
||||
const preGameDeckEditorButton = document.getElementById('preGameDeckEditorButton') as HTMLLinkElement;
|
||||
const preGameGalleryButton = document.getElementById('preGameGalleryButton') as HTMLLinkElement;
|
||||
const preGameLoadingSection = document.getElementById('preGameLoadingSection')!;
|
||||
const preGameLoadingLabel = document.getElementById('preGameLoadingLabel')!;
|
||||
const preGameReplayButton = document.getElementById('preGameReplayButton') as HTMLLinkElement;
|
||||
|
|
@ -301,6 +302,12 @@ preGameDeckEditorButton.addEventListener('click', e => {
|
|||
setUrl('deckeditor');
|
||||
});
|
||||
|
||||
preGameGalleryButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showCardList();
|
||||
setUrl('cardlist');
|
||||
});
|
||||
|
||||
preGameSettingsButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
optionsColourGoodBox.value = userConfig.goodColour ?? 'yellow';
|
||||
|
|
@ -419,6 +426,8 @@ window.addEventListener('popstate', () => {
|
|||
}
|
||||
}
|
||||
|
||||
if (!canPushState)
|
||||
if (!canPushState) {
|
||||
preGameDeckEditorButton.href = '#deckeditor';
|
||||
preGameGalleryButton.href = '#cardlist';
|
||||
}
|
||||
setLoadingMessage('Loading game data...');
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ let initialiseCallback: (() => void) | null = null;
|
|||
let canPushState = isSecureContext && location.protocol != 'file:';
|
||||
|
||||
const decks = [ new SavedDeck('Starter Deck', 0, [ 6, 34, 159, 13, 45, 137, 22, 52, 141, 28, 55, 103, 40, 56, 92 ], new Array(15).fill(1), true) ];
|
||||
const customCards: Card[] = [ ];
|
||||
let selectedDeck: SavedDeck | null = null;
|
||||
let editingDeck = false;
|
||||
let deckModified = false;
|
||||
|
|
@ -50,6 +51,7 @@ function onInitialise(callback: () => void) {
|
|||
|
||||
function initCardDatabase(cards: Card[]) {
|
||||
deckEditInitCardDatabase(cards);
|
||||
galleryInitCardDatabase(cards);
|
||||
if (!cards.find(c => c.number < 0)) {
|
||||
gameSetupAllowUpcomingCardsBox.parentElement!.hidden = true;
|
||||
lobbyAllowUpcomingCardsBox.parentElement!.hidden = true;
|
||||
|
|
@ -63,7 +65,7 @@ function initStageDatabase(stages: Stage[]) {
|
|||
|
||||
// Pages
|
||||
const pages = new Map<string, HTMLDivElement>();
|
||||
for (var id of [ 'noJS', 'preGame', 'lobby', 'game', 'deckList', 'deckEdit' ]) {
|
||||
for (var id of [ 'noJS', 'preGame', 'lobby', 'game', 'deckList', 'deckEdit', 'gallery' ]) {
|
||||
let el = document.getElementById(`${id}Page`) as HTMLDivElement;
|
||||
if (!el) throw new EvalError(`Element not found: ${id}Page`);
|
||||
pages.set(id, el);
|
||||
|
|
@ -484,6 +486,8 @@ function processUrl() {
|
|||
clearGame();
|
||||
if (location.pathname.endsWith('/deckeditor') || location.hash == '#deckeditor')
|
||||
onInitialise(showDeckList);
|
||||
else if (location.pathname.endsWith('/cardlist') || location.hash == '#cardlist')
|
||||
onInitialise(showCardList);
|
||||
else {
|
||||
showPage('preGame');
|
||||
if (location.pathname.endsWith('/help') || location.hash == '#help')
|
||||
|
|
|
|||
|
|
@ -501,10 +501,6 @@ dialog::backdrop {
|
|||
|
||||
.cardNumber {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cardListGrid .cardButton:hover .cardNumber {
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: grey;
|
||||
border: 1px solid black;
|
||||
|
|
@ -515,6 +511,10 @@ dialog::backdrop {
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.cardListGrid .cardButton:hover .cardNumber {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cardName {
|
||||
text-align: center;
|
||||
line-height: 1.25em;
|
||||
|
|
@ -605,6 +605,7 @@ dialog::backdrop {
|
|||
}
|
||||
.playContainer .card {
|
||||
animation: 0.1s ease-out forwards flipCardIn;
|
||||
height: 100%;
|
||||
}
|
||||
.playContainer .card.preview {
|
||||
animation: none;
|
||||
|
|
@ -1810,6 +1811,142 @@ button.dragging {
|
|||
#deckSleevesList label:nth-of-type(24) { background-position: -700% -200%; }
|
||||
#deckSleevesList label:nth-of-type(25) { background-position: 0% -300%; }
|
||||
|
||||
/* Card list */
|
||||
|
||||
#galleryPage:not([hidden]) {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
#galleryCardList {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 10em);
|
||||
grid-auto-rows: auto;
|
||||
gap: 0.5em;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
#galleryCardList .card {
|
||||
height: 14rem;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
#galleryCardList .card.unowned svg {
|
||||
opacity: 0.333;
|
||||
}
|
||||
|
||||
#galleryCardList .card:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
#galleryCardList .card:hover .cardNumber {
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
#galleryCardDialog > .card {
|
||||
height: min(75vh, 100vw);
|
||||
}
|
||||
|
||||
#galleryCardEditor {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font-size: calc(min(75vh, 100vw) * 0.08);
|
||||
}
|
||||
|
||||
#galleryCardEditorName {
|
||||
position: absolute;
|
||||
left: 5%;
|
||||
top: 8%;
|
||||
width: 90%;
|
||||
height: 20%;
|
||||
color: black;
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
line-height: 1.25em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card.editing :is(.cardDisplayName, .cardGrid) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#galleryCardEditorGrid {
|
||||
position: absolute;
|
||||
left: 20%;
|
||||
right: 20%;
|
||||
top: 30%;
|
||||
bottom: 30%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
grid-template-rows: repeat(8, 1fr);
|
||||
}
|
||||
|
||||
#galleryCardEditorGrid button {
|
||||
position: relative;
|
||||
border: none;
|
||||
}
|
||||
:is(#galleryCardEditorGrid, #galleryCardEditorSpecialCost) button:is(:hover, :focus)::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #ffffff80;
|
||||
content: '';
|
||||
}
|
||||
|
||||
#galleryCardEditorGrid button[data-state="0"] {
|
||||
border: 1px solid grey;
|
||||
background: #00000080;
|
||||
}
|
||||
#galleryCardEditorGrid button[data-state="4"] {
|
||||
border: 1px solid grey;
|
||||
background: url('assets/InkOverlay.png') center/cover, var(--primary-colour-1);
|
||||
}
|
||||
#galleryCardEditorGrid button[data-state="8"] {
|
||||
border: 1px solid grey;
|
||||
background: url('assets/SpecialOverlay.png') center/cover, var(--special-colour-1);
|
||||
}
|
||||
|
||||
#galleryCardEditorSpecialCost {
|
||||
display: none;
|
||||
position: absolute;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
left: 26.8%;
|
||||
top: 86.8%;
|
||||
gap: 0.072em;
|
||||
}
|
||||
|
||||
#galleryCardEditorSpecialCost button {
|
||||
width: 1.7em;
|
||||
height: 1.7em;
|
||||
border: none;
|
||||
background: #00000080;
|
||||
position: relative;
|
||||
}
|
||||
#galleryCardEditorSpecialCost button.active {
|
||||
background: url('assets/SpecialOverlay.png') center/cover, var(--special-colour-1);
|
||||
}
|
||||
|
||||
#galleryCardEditorSpecialCost label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 105%;
|
||||
font: 33% 'Splatoon 2', sans-serif;
|
||||
background: grey;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
/* Help */
|
||||
|
||||
#helpControls {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ internal partial class Program {
|
|||
private static void HttpServer_OnRequest(object? sender, HttpRequestEventArgs e) {
|
||||
e.Response.AppendHeader("Access-Control-Allow-Origin", "*");
|
||||
if (!e.Request.RawUrl.StartsWith("/api/")) {
|
||||
var path = e.Request.RawUrl == "/" || e.Request.RawUrl.StartsWith("/deckeditor") || e.Request.RawUrl.StartsWith("/game/") || e.Request.RawUrl.StartsWith("/replay/")
|
||||
var path = e.Request.RawUrl == "/" || e.Request.RawUrl.StartsWith("/deckeditor") || e.Request.RawUrl.StartsWith("/cardlist") || e.Request.RawUrl.StartsWith("/game/") || e.Request.RawUrl.StartsWith("/replay/")
|
||||
? "index.html"
|
||||
: HttpUtility.UrlDecode(e.Request.RawUrl[1..]);
|
||||
if (e.TryReadFile(path, out var bytes))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user