diff --git a/TableturfBattleClient/src/Card.ts b/TableturfBattleClient/src/Card.ts index 6fc2bd4..c28d749 100644 --- a/TableturfBattleClient/src/Card.ts +++ b/TableturfBattleClient/src/Card.ts @@ -63,7 +63,8 @@ class Card { return card; } - get isUpcoming() { return this.number < 0; } + get isCustom() { return this.number <= CUSTOM_CARD_START; } + get isUpcoming() { return this.number < 0 && !this.isCustom; } get isSpecialWeapon() { return this.specialCost == 3 && this.size == 12 }; getSpace(x: number, y: number, rotation: number) { diff --git a/TableturfBattleClient/src/CardButton.ts b/TableturfBattleClient/src/CardButton.ts index 2bb9996..3c339e9 100644 --- a/TableturfBattleClient/src/CardButton.ts +++ b/TableturfBattleClient/src/CardButton.ts @@ -11,8 +11,8 @@ class CardButton extends CheckButton implements ICardElement { button.type = 'button'; button.classList.add('cardButton'); button.classList.add([ 'common', 'rare', 'fresh' ][card.rarity]); - if (card.number <= CUSTOM_CARD_START) button.classList.add('custom'); - else if (card.number < 0) button.classList.add('upcoming'); + if (card.isCustom) button.classList.add('custom'); + else if (card.isUpcoming) button.classList.add('upcoming'); button.dataset.cardNumber = card.number.toString(); super(button); this.element = button; @@ -37,7 +37,7 @@ class CardButton extends CheckButton implements ICardElement { let el2 = document.createElement('div'); el2.classList.add('cardNumber'); - el2.innerText = card.number >= 0 ? `No. ${card.number}` : card.number <= CUSTOM_CARD_START ? 'Custom' : 'Upcoming'; + el2.innerText = card.number >= 0 ? `No. ${card.number}` : card.isCustom ? 'Custom' : 'Upcoming'; row.appendChild(el2); el2 = document.createElement('div'); diff --git a/TableturfBattleClient/src/CardDatabase.ts b/TableturfBattleClient/src/CardDatabase.ts index daa06ef..ffb42c9 100644 --- a/TableturfBattleClient/src/CardDatabase.ts +++ b/TableturfBattleClient/src/CardDatabase.ts @@ -1,6 +1,14 @@ const cardDatabase = { + /** The list of official cards, or null if the database has not yet been loaded. */ cards: null as Card[] | null, + /** The list of custom cards added by the user. */ + customCards: [ ] as Card[], + /** The list of custom cards used in the current game that were not added by the user. */ + receivedCustomCards: [ ] as Card[], + customCardsModified: false, + /** The number of official cards, and the highest card number among official cards. */ lastOfficialCardNumber: 0, + _byAltNumber: [ ] as Card[], // Upcoming cards are identified with a negative number, as their actual numbers aren't known until their release. @@ -10,8 +18,11 @@ const cardDatabase = { if (number > 0) { number--; if (number < cardDatabase.lastOfficialCardNumber) return cardDatabase.cards[number]; + } else if (number <= RECEIVED_CUSTOM_CARD_START) { + const card = cardDatabase.receivedCustomCards[RECEIVED_CUSTOM_CARD_START - number]; + if (card) return card; } else if (number <= CUSTOM_CARD_START) { - const card = customCards[CUSTOM_CARD_START - number]; + const card = cardDatabase.customCards[CUSTOM_CARD_START - number]; if (card) return card; } else if (number < 0) { const card = cardDatabase._byAltNumber[-number]; @@ -19,9 +30,15 @@ const cardDatabase = { } throw new RangeError(`No card with number ${number}`); }, - isValidCardNumber(number: number) { + isValidOfficialCardNumber(number: number) { return number > 0 ? number <= cardDatabase.lastOfficialCardNumber : cardDatabase._byAltNumber[-number] != undefined; }, + isValidCardNumber(number: number) { + return number > 0 ? number <= cardDatabase.lastOfficialCardNumber + : number <= RECEIVED_CUSTOM_CARD_START ? RECEIVED_CUSTOM_CARD_START - number < cardDatabase.receivedCustomCards.length + : number <= CUSTOM_CARD_START ? CUSTOM_CARD_START - number < cardDatabase.customCards.length + : cardDatabase._byAltNumber[-number] != undefined; + }, loadAsync() { return new Promise((resolve, reject) => { if (cardDatabase.cards != null) { diff --git a/TableturfBattleClient/src/CardDisplay.ts b/TableturfBattleClient/src/CardDisplay.ts index 7f12070..24e0307 100644 --- a/TableturfBattleClient/src/CardDisplay.ts +++ b/TableturfBattleClient/src/CardDisplay.ts @@ -21,8 +21,8 @@ class CardDisplay implements ICardElement { this.svg = svg; element.appendChild(svg); - if (card.number <= CUSTOM_CARD_START) svg.classList.add('custom'); - else if (card.number < 0) svg.classList.add('upcoming'); + 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()); diff --git a/TableturfBattleClient/src/CardList.ts b/TableturfBattleClient/src/CardList.ts index ed1a4fa..f511c82 100644 --- a/TableturfBattleClient/src/CardList.ts +++ b/TableturfBattleClient/src/CardList.ts @@ -90,7 +90,7 @@ class CardList { removeAllCustomCards() { for (let i = this.cardButtons.length - 1; i >= 0; i--) { const button = this.cardButtons[i]; - if (button.card.number <= CUSTOM_CARD_START) { + if (button.card.isCustom) { this.listElement.removeChild(button.element); this.cardButtons.splice(i, 1); } diff --git a/TableturfBattleClient/src/Config.ts b/TableturfBattleClient/src/Config.ts index 225df7f..67e7c53 100644 --- a/TableturfBattleClient/src/Config.ts +++ b/TableturfBattleClient/src/Config.ts @@ -58,5 +58,5 @@ function saveChecklist() { } function saveCustomCards() { - localStorage.setItem('customCards', JSON.stringify(customCards)); + localStorage.setItem('customCards', JSON.stringify(cardDatabase.customCards)); } diff --git a/TableturfBattleClient/src/Pages/DeckEditPage.ts b/TableturfBattleClient/src/Pages/DeckEditPage.ts index a3e5b77..eb91c7a 100644 --- a/TableturfBattleClient/src/Pages/DeckEditPage.ts +++ b/TableturfBattleClient/src/Pages/DeckEditPage.ts @@ -124,10 +124,10 @@ function editDeck() { } function reloadCustomCards() { - if (!customCardsModified) return; + if (!cardDatabase.customCardsModified) return; cardList.removeAllCustomCards(); testAllCardsList.removeAllCustomCards(); - for (const card of customCards) { + for (const card of cardDatabase.customCards) { addCardToDeckEditor(card); addTestCard(card); } diff --git a/TableturfBattleClient/src/Pages/GalleryPage.ts b/TableturfBattleClient/src/Pages/GalleryPage.ts index fe2b002..896fa85 100644 --- a/TableturfBattleClient/src/Pages/GalleryPage.ts +++ b/TableturfBattleClient/src/Pages/GalleryPage.ts @@ -31,7 +31,7 @@ function showCardList() { } function galleryInitCardDatabase(cards: Card[]) { - for (const card of cards.concat(customCards)) { + for (const card of cards.concat(cardDatabase.customCards)) { addCardToGallery(card); } updateBitsToComplete(); @@ -47,7 +47,7 @@ function createGalleryCardDisplay(card: Card) { const cardNumber = document.createElement('div'); cardNumber.className = 'cardNumber'; - cardNumber.innerText = card.number >= 0 ? `No. ${card.number}` : card.number <= CUSTOM_CARD_START ? 'Custom' : 'Upcoming'; + cardNumber.innerText = card.number >= 0 ? `No. ${card.number}` : card.isCustom ? 'Custom' : 'Upcoming'; display.element.insertBefore(cardNumber, display.element.firstChild); display.element.addEventListener('click', () => { @@ -88,8 +88,8 @@ function openGalleryCardView(card: Card) { display.element.appendChild(galleryCardEditor); galleryCardEditor.hidden = true; display.element.classList.remove('editing'); - galleryCardEditorEditButton.hidden = card.number > CUSTOM_CARD_START; - galleryCardEditorDeleteButton.hidden = card.number > CUSTOM_CARD_START; + galleryCardEditorEditButton.hidden = !card.isCustom; + galleryCardEditorDeleteButton.hidden = !card.isCustom; galleryCardEditorSubmitButton.hidden = true; galleryCardEditorCancelButton.innerText = 'Close'; @@ -214,9 +214,9 @@ function updateBitsToComplete() { const customCardsString = localStorage.getItem('customCards'); if (customCardsString) { for (const cardJson of JSON.parse(customCardsString)) { - customCards.push(Card.fromJson(cardJson)); + cardDatabase.customCards.push(Card.fromJson(cardJson)); } - customCardsModified = customCards.length > 0; + cardDatabase.customCardsModified = cardDatabase.customCards.length > 0; } } @@ -296,13 +296,13 @@ galleryCardEditorSubmitButton.addEventListener('click', () => { card.size = customCardSize; card.specialCost = customCardSpecialCost; if (card.number == UNSAVED_CUSTOM_CARD_INDEX) { - card.number = CUSTOM_CARD_START - customCards.length; - customCards.push(card); + card.number = CUSTOM_CARD_START - cardDatabase.customCards.length; + cardDatabase.customCards.push(card); addCardToGallery(card); } else { updateCardInGallery(card); } - customCardsModified = true; + cardDatabase.customCardsModified = true; saveCustomCards(); }); @@ -316,6 +316,25 @@ galleryCardEditorDeleteYesButton.addEventListener('click', () => { const card = galleryCardDisplay!.card; galleryCardList.remove(card); galleryCardDialog.close(); - customCards.splice(customCards.indexOf(card), 1); + + let i = cardDatabase.customCards.indexOf(card); + if (i < 0) return; + + // Remove the card from decks and update other custom card numbers. + for (const deck of decks) { + for (let i = 0; i < deck.cards.length; i++) { + if (deck.cards[i] == card.number) + deck.cards[i] = 0; + else if (deck.cards[i] < card.number) + deck.cards[i]++; + } + } + + // Update the custom cards list. + cardDatabase.customCards.splice(i, 1); + for (; i < cardDatabase.customCards.length; i++) + cardDatabase.customCards[i].number++; + saveCustomCards(); + saveDecks(); }); diff --git a/TableturfBattleClient/src/app.ts b/TableturfBattleClient/src/app.ts index 579df60..9366cdc 100644 --- a/TableturfBattleClient/src/app.ts +++ b/TableturfBattleClient/src/app.ts @@ -1,6 +1,7 @@ declare var baseUrl: string; -const CUSTOM_CARD_START = -1000; // TODO: Card numbers in replays shall be expanded to 2 bytes. +const CUSTOM_CARD_START = -10000; // TODO: Card numbers in replays shall be expanded to 2 bytes. +const RECEIVED_CUSTOM_CARD_START = -20000; const UNSAVED_CUSTOM_CARD_INDEX = CUSTOM_CARD_START + 1; const defaultColours = [ [ { r: 236, g: 249, b: 1 }, { r: 250, g: 158, b: 0 }, { r: 249, g: 249, b: 31 } ], @@ -20,8 +21,6 @@ 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 customCardsModified = false; let selectedDeck: SavedDeck | null = null; let editingDeck = false; let deckModified = false;