mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-03-21 17:34:28 -05:00
Add a basic deck editor page. #1
This commit is contained in:
parent
e3fd48b45e
commit
191a2982f7
|
|
@ -42,6 +42,9 @@
|
|||
<p><button type="submit" id="joinGameButton2">Join game</button></p>
|
||||
<a id="preGameBackButton" href="../..">Create or join a different room</a>
|
||||
</div>
|
||||
<p>
|
||||
<a id="preGameDeckEditorButton" href="deckeditor">Edit decks</a>
|
||||
</p>
|
||||
</form>
|
||||
<footer>
|
||||
<p>This website is not affiliated with Nintendo. All product names, logos, and brands are property of their respective owners.</p>
|
||||
|
|
@ -70,10 +73,8 @@
|
|||
</section>
|
||||
<div id="lobbyDeckSection" hidden>
|
||||
<h3>Choose your deck.</h3>
|
||||
<p><span id="countLabel">0</span>/15 cards chosen</p>
|
||||
<p><button type="button" id="submitDeckButton" disabled>Submit</button></p>
|
||||
<p id="cardListLoadingSection">Loading cards...</p>
|
||||
<div id="cardList"></div>
|
||||
<div id="lobbyDeckList"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gameSection" hidden>
|
||||
|
|
@ -185,6 +186,52 @@
|
|||
<div id="errorModal" hidden>
|
||||
<div id="errorModalBox">A communication error has occurred.</div>
|
||||
</div>
|
||||
<div id="deckListSection" hidden>
|
||||
<section id="deckEditorDeckListSection">
|
||||
<a id="deckListBackButton" href="..">Back</a>
|
||||
<h3>Deck list</h3>
|
||||
<div id="deckList">
|
||||
<button id="newDeckButton">New deck</label>
|
||||
</div>
|
||||
</section>
|
||||
<section id="deckEditorDeckViewSection" hidden>
|
||||
<h3 id="deckName">Deck</h3>
|
||||
<div>
|
||||
<button type="button" id="deckEditButton">Edit</button>
|
||||
<button type="button" id="deckRenameButton">Rename</button>
|
||||
<button type="button" id="deckCopyButton">Copy</button>
|
||||
<button type="button" id="deckDeleteButton" class="danger">Delete</button>
|
||||
</div>
|
||||
<div class="deckSizeContainer">Total: <div id="deckViewSize">0</div></div>
|
||||
<div id="deckCardListView">
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div id="deckEditSection" hidden>
|
||||
<section id="deckEditorDeckEditSection">
|
||||
<h3 id="deckName2">Deck</h3>
|
||||
<div>
|
||||
<button type="button" id="deckTestButton">Test</button>
|
||||
<button type="button" id="deckSaveButton">Save</button>
|
||||
<button type="button" id="deckCancelButton">Cancel</button>
|
||||
</div>
|
||||
<div class="deckSizeContainer">Total: <div id="deckEditSize">0</div></div>
|
||||
<div id="deckCardListEdit">
|
||||
</div>
|
||||
</section>
|
||||
<section id="deckEditorCardListSection">
|
||||
<label for="cardListSortBox">
|
||||
Sort by
|
||||
<select id="cardListSortBox" autocomplete="off">
|
||||
<option value="number">number</option>
|
||||
<option value="name">name</option>
|
||||
<option value="size">size</option>
|
||||
<option value="rarity">rarity</option>
|
||||
</select>
|
||||
</label>
|
||||
<div id="cardList"></div>
|
||||
</section>
|
||||
</div>
|
||||
<script src="build/tsbuild.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
21
TableturfBattleClient/src/Deck.ts
Normal file
21
TableturfBattleClient/src/Deck.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class Deck {
|
||||
name: string;
|
||||
cards: number[];
|
||||
isReadOnly: boolean;
|
||||
|
||||
constructor(name: string, cards: number[], isReadOnly: boolean) {
|
||||
this.name = name;
|
||||
this.cards = cards;
|
||||
this.isReadOnly = isReadOnly;
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
if (!cardDatabase.cards) throw new Error('Card database must be loaded to validate decks.');
|
||||
if (this.cards.length != 15) return false;
|
||||
for (let i = 0; i < 15; i++) {
|
||||
if (this.cards[i] <= 0 || this.cards[i] > cardDatabase.cards.length) return false;
|
||||
if (this.cards.indexOf(this.cards[i], i + 1) >= 0) return false; // Duplicate cards
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
161
TableturfBattleClient/src/Pages/DeckEditPage.ts
Normal file
161
TableturfBattleClient/src/Pages/DeckEditPage.ts
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
const deckNameLabel2 = document.getElementById('deckName2')!;
|
||||
const deckEditSize = document.getElementById('deckEditSize')!;
|
||||
const deckCardListEdit = document.getElementById('deckCardListEdit')!;
|
||||
const cardList = document.getElementById('cardList')!;
|
||||
const deckTestButton = document.getElementById('deckTestButton') as HTMLButtonElement;
|
||||
const deckSaveButton = document.getElementById('deckSaveButton') as HTMLButtonElement;
|
||||
const deckCancelButton = document.getElementById('deckCancelButton') as HTMLButtonElement;
|
||||
const cardListSortBox = document.getElementById('cardListSortBox') as HTMLSelectElement;
|
||||
|
||||
const cardButtons: CardButton[] = [ ];
|
||||
const deckEditCardButtons: (CardButton | HTMLLabelElement)[] = [ ];
|
||||
|
||||
let selectedDeckCardIndex: number | null = null;
|
||||
|
||||
function editDeck() {
|
||||
if (selectedDeck == null) return;
|
||||
|
||||
deckNameLabel2.innerText = selectedDeck.name;
|
||||
|
||||
clearChildren(deckCardListEdit);
|
||||
deckEditCardButtons.splice(0);
|
||||
selectedDeckCardIndex = null;
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
if (selectedDeck.cards[i]) {
|
||||
const button = createDeckEditCardButton(i, selectedDeck.cards[i]);
|
||||
deckCardListEdit.appendChild(button.element);
|
||||
deckEditCardButtons.push(button);
|
||||
} else {
|
||||
const element = createDeckEditEmptySlotButton(i);
|
||||
deckCardListEdit.appendChild(element);
|
||||
deckEditCardButtons.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
deckEditUpdateSize();
|
||||
showSection('deckEdit');
|
||||
}
|
||||
|
||||
function createDeckEditCardButton(index: number, card: number) {
|
||||
const button = new CardButton('radio', cardDatabase.cards![card - 1]);
|
||||
button.inputElement.name = 'deckEditorSelectedCard'
|
||||
deckCardListEdit.appendChild(button.element);
|
||||
button.inputElement.addEventListener('input', () => {
|
||||
if (button.inputElement.checked) {
|
||||
for (const o of deckEditCardButtons) {
|
||||
if (o != button) {
|
||||
if ((o as CardButton).card)
|
||||
(o as CardButton).element.classList.remove('checked');
|
||||
else
|
||||
(o as HTMLElement).classList.remove('checked');
|
||||
}
|
||||
}
|
||||
|
||||
selectedDeckCardIndex = index;
|
||||
for (const button2 of cardButtons) {
|
||||
button2.checked = button2.card.number == card;
|
||||
}
|
||||
}
|
||||
});
|
||||
return button;
|
||||
}
|
||||
|
||||
function createDeckEditEmptySlotButton(index: number) {
|
||||
const element = document.createElement('label');
|
||||
element.className = 'card emptySlot';
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'radio';
|
||||
input.name = 'deckEditorSelectedCard'
|
||||
input.addEventListener('input', () => {
|
||||
if (input.checked) {
|
||||
for (const o of deckEditCardButtons) {
|
||||
if (o != element) {
|
||||
if ((o as CardButton).card)
|
||||
(o as CardButton).element.classList.remove('checked');
|
||||
else
|
||||
(o as HTMLElement).classList.remove('checked');
|
||||
}
|
||||
}
|
||||
|
||||
selectedDeckCardIndex = index;
|
||||
for (const button2 of cardButtons) {
|
||||
button2.checked = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
element.appendChild(input);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
deckSaveButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
selectedDeck.cards = deckEditCardButtons.map(o => (o as CardButton).card?.number ?? 0);
|
||||
saveDecks();
|
||||
selectDeck();
|
||||
showSection('deckList');
|
||||
});
|
||||
|
||||
deckCancelButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
if (!confirm('Are you sure you want to stop editing this deck without saving?')) return;
|
||||
showSection('deckList');
|
||||
});
|
||||
|
||||
function deckEditUpdateSize() {
|
||||
let size = 0;
|
||||
for (const o of deckEditCardButtons) {
|
||||
const card = (o as CardButton).card;
|
||||
if (card) size += card.size;
|
||||
}
|
||||
deckEditSize.innerText = size.toString();
|
||||
}
|
||||
|
||||
function initCardDatabase(cards: Card[]) {
|
||||
for (const card of cards) {
|
||||
const button = new CardButton('radio', card);
|
||||
button.inputElement.name = 'deckEditorCardList';
|
||||
cardButtons.push(button);
|
||||
button.inputElement.addEventListener('input', () => {
|
||||
if (button.inputElement.checked) {
|
||||
for (const button2 of cardButtons) {
|
||||
if (button2 != button)
|
||||
button2.checked = false;
|
||||
}
|
||||
|
||||
if (selectedDeckCardIndex == null) return;
|
||||
const oldButton = deckEditCardButtons[selectedDeckCardIndex];
|
||||
|
||||
const button3 = createDeckEditCardButton(selectedDeckCardIndex, card.number);
|
||||
button3.checked = true;
|
||||
|
||||
const oldElement = (oldButton as CardButton).element ?? (oldButton as Element);
|
||||
deckCardListEdit.insertBefore(button3.element, oldElement);
|
||||
deckCardListEdit.removeChild(oldElement);
|
||||
|
||||
deckEditCardButtons[selectedDeckCardIndex] = button3;
|
||||
deckEditUpdateSize();
|
||||
}
|
||||
});
|
||||
cardList.appendChild(button.element);
|
||||
}
|
||||
}
|
||||
|
||||
const cardSortOrders: { [key: string]: ((a: Card, b: Card) => number) | undefined } = {
|
||||
'number': (a, b) => a.number - b.number,
|
||||
'name': (a, b) => a.name.localeCompare(b.name),
|
||||
'size': (a, b) => a.size != b.size ? a.size - b.size : a.number - b.number,
|
||||
'rarity': (a, b) => a.rarity != b.rarity ? a.rarity - b.rarity : a.number - b.number,
|
||||
}
|
||||
|
||||
cardListSortBox.addEventListener('change', () => {
|
||||
const sortOrder = cardSortOrders[cardListSortBox.value];
|
||||
if (sortOrder) {
|
||||
clearChildren(cardList);
|
||||
cardButtons.sort((a, b) => sortOrder(a.card, b.card));
|
||||
for (const button of cardButtons)
|
||||
cardList.appendChild(button.element);
|
||||
}
|
||||
});
|
||||
162
TableturfBattleClient/src/Pages/DeckListPage.ts
Normal file
162
TableturfBattleClient/src/Pages/DeckListPage.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
const deckListBackButton = document.getElementById('deckListBackButton') as HTMLLinkElement;
|
||||
const deckEditorDeckViewSection = document.getElementById('deckEditorDeckViewSection')!;
|
||||
const deckNameLabel = document.getElementById('deckName')!;
|
||||
const deckViewSize = document.getElementById('deckViewSize')!;
|
||||
const deckList = document.getElementById('deckList')!;
|
||||
const deckCardListView = document.getElementById('deckCardListView')!;
|
||||
const newDeckButton = document.getElementById('newDeckButton') as HTMLButtonElement;
|
||||
const deckEditButton = document.getElementById('deckEditButton') as HTMLButtonElement;
|
||||
const deckRenameButton = document.getElementById('deckRenameButton') as HTMLButtonElement;
|
||||
const deckCopyButton = document.getElementById('deckCopyButton') as HTMLButtonElement;
|
||||
const deckDeleteButton = document.getElementById('deckDeleteButton') as HTMLButtonElement;
|
||||
|
||||
function showDeckList() {
|
||||
showSection('deckList');
|
||||
selectedDeck = null;
|
||||
for (const el of deckList.getElementsByTagName('input')) {
|
||||
(el as HTMLInputElement).checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
deckListBackButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showSection('preGame');
|
||||
|
||||
if (canPushState) {
|
||||
try {
|
||||
history.pushState(null, '', '..');
|
||||
} catch {
|
||||
canPushState = false;
|
||||
}
|
||||
}
|
||||
if (location.hash)
|
||||
location.hash = '';
|
||||
});
|
||||
|
||||
function saveDecks() {
|
||||
const json = JSON.stringify(decks.filter(d => !d.isReadOnly), [ 'name', 'cards' ]);
|
||||
localStorage.setItem('decks', json);
|
||||
}
|
||||
|
||||
{
|
||||
const decksString = localStorage.getItem('decks');
|
||||
if (decksString) {
|
||||
for (const deck of JSON.parse(decksString)) {
|
||||
decks.push(new Deck(deck.name, deck.cards, false));
|
||||
}
|
||||
} else {
|
||||
const lastDeckString = localStorage.getItem('lastDeck');
|
||||
const lastDeck = lastDeckString?.split(/\+/)?.map(s => parseInt(s));
|
||||
if (lastDeck && lastDeck.length == 15) {
|
||||
decks.push(new Deck('Custom Deck', lastDeck, false));
|
||||
saveDecks();
|
||||
}
|
||||
localStorage.removeItem('lastDeck');
|
||||
}
|
||||
|
||||
for (let i = 0; i < decks.length; i++) {
|
||||
createDeckButton(i, decks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function createDeckButton(index: number, deck: Deck) {
|
||||
const label = document.createElement('label');
|
||||
|
||||
const button = document.createElement('input');
|
||||
button.name = 'selectedDeck';
|
||||
button.type = 'radio';
|
||||
button.dataset.index = index.toString();
|
||||
button.addEventListener('click', e => {
|
||||
selectedDeck = decks[parseInt((e.target as HTMLInputElement).dataset.index!)];
|
||||
selectDeck();
|
||||
});
|
||||
label.appendChild(button);
|
||||
|
||||
label.appendChild(document.createTextNode(deck.name));
|
||||
|
||||
deckList.insertBefore(label, newDeckButton);
|
||||
return label;
|
||||
}
|
||||
|
||||
newDeckButton.addEventListener('click', () => {
|
||||
selectedDeck = new Deck(`Deck ${decks.length}`, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], false);
|
||||
createDeckButton(decks.length, selectedDeck);
|
||||
decks.push(selectedDeck);
|
||||
editDeck();
|
||||
});
|
||||
|
||||
deckEditButton.addEventListener('click', editDeck);
|
||||
|
||||
function selectDeck() {
|
||||
clearChildren(deckCardListView);
|
||||
if (selectedDeck == null) return;
|
||||
|
||||
let size = 0;
|
||||
|
||||
deckNameLabel.innerText = selectedDeck.name;
|
||||
for (const cardNumber of selectedDeck.cards) {
|
||||
if (cardNumber) {
|
||||
const card = cardDatabase.cards![cardNumber - 1];
|
||||
size += card.size;
|
||||
|
||||
const button = new CardButton('radio', card);
|
||||
button.inputElement.disabled = true;
|
||||
button.inputElement.hidden = true;
|
||||
deckCardListView.appendChild(button.element);
|
||||
}
|
||||
}
|
||||
|
||||
deckEditButton.disabled = selectedDeck.isReadOnly;
|
||||
deckRenameButton.disabled = selectedDeck.isReadOnly;
|
||||
deckCopyButton.disabled = false;
|
||||
deckDeleteButton.disabled = selectedDeck.isReadOnly;
|
||||
deckViewSize.innerText = size.toString();
|
||||
deckEditorDeckViewSection.hidden = false;
|
||||
}
|
||||
|
||||
deckRenameButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
const name = prompt(`What will you rename ${selectedDeck.name} to?`, selectedDeck.name)?.trim();
|
||||
if (name) {
|
||||
selectedDeck.name = name;
|
||||
deckNameLabel.innerText = selectedDeck.name;
|
||||
saveDecks();
|
||||
}
|
||||
});
|
||||
|
||||
deckCopyButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
selectedDeck = new Deck(selectedDeck.name, Array.from(selectedDeck.cards), false);
|
||||
const button = createDeckButton(decks.length, selectedDeck);
|
||||
decks.push(selectedDeck);
|
||||
(button.getElementsByTagName('input')[0] as HTMLInputElement).checked = true;
|
||||
selectDeck();
|
||||
saveDecks();
|
||||
});
|
||||
|
||||
deckDeleteButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
const index = decks.indexOf(selectedDeck);
|
||||
if (index < 0) return;
|
||||
if (!confirm(`Are you sure you want to delete ${selectedDeck.name}?`)) return;
|
||||
|
||||
decks.splice(index, 1);
|
||||
|
||||
let removed = false;
|
||||
for (const el of Array.from(deckList.getElementsByTagName('label'))) {
|
||||
const input = el.getElementsByTagName('input')[0];
|
||||
if (removed) {
|
||||
input.dataset.index = (parseInt(input.dataset.index!) - 1).toString();
|
||||
} else if (parseInt(input.dataset.index!) == index) {
|
||||
deckList.removeChild(el);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
selectedDeck = null;
|
||||
deckEditorDeckViewSection.hidden = true;
|
||||
saveDecks();
|
||||
});
|
||||
|
||||
if (!canPushState)
|
||||
deckListBackButton.href = '#';
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
/// <reference path="../StageDatabase.ts"/>
|
||||
|
||||
const stageButtons: StageButton[] = [ ];
|
||||
const cardButtons: CardButton[] = [ ];
|
||||
const shareLinkButton = document.getElementById('shareLinkButton') as HTMLButtonElement;
|
||||
const submitDeckButton = document.getElementById('submitDeckButton') as HTMLButtonElement;
|
||||
let lobbyShareData: ShareData | null;
|
||||
|
|
@ -13,6 +12,7 @@ const stageRandomButton = document.getElementById('stageRandomButton') as HTMLIn
|
|||
const lobbySelectedStageSection = document.getElementById('lobbySelectedStageSection')!;
|
||||
const lobbyStageSection = document.getElementById('lobbyStageSection')!;
|
||||
const lobbyDeckSection = document.getElementById('lobbyDeckSection')!;
|
||||
const lobbyDeckList = document.getElementById('lobbyDeckList')!;
|
||||
|
||||
let selectedStageButton = null as StageButton | null;
|
||||
|
||||
|
|
@ -64,7 +64,50 @@ function updateDeckCount() {
|
|||
submitDeckButton.disabled = (count != 15);
|
||||
}
|
||||
|
||||
submitDeckButton.addEventListener('click', e => {
|
||||
function initDeckSelection() {
|
||||
const lastDeckName = localStorage.getItem('lastDeckName');
|
||||
|
||||
selectedDeck = null;
|
||||
if (currentGame?.me) {
|
||||
clearChildren(lobbyDeckList);
|
||||
|
||||
for (let i = 0; i < decks.length; i++) {
|
||||
const deck = decks[i];
|
||||
|
||||
const label = document.createElement('label');
|
||||
|
||||
const button = document.createElement('input');
|
||||
button.name = 'gameSelectedDeck';
|
||||
button.type = 'radio';
|
||||
button.dataset.index = i.toString();
|
||||
button.addEventListener('click', () => {
|
||||
selectedDeck = deck;
|
||||
submitDeckButton.disabled = false;
|
||||
});
|
||||
label.appendChild(button);
|
||||
|
||||
label.appendChild(document.createTextNode(deck.name));
|
||||
|
||||
if (!deck.isValid) {
|
||||
label.classList.add('disabled');
|
||||
button.disabled = true;
|
||||
} else if (deck.name == lastDeckName) {
|
||||
selectedDeck = deck;
|
||||
button.checked = true;
|
||||
}
|
||||
|
||||
lobbyDeckList.appendChild(label);
|
||||
}
|
||||
submitDeckButton.disabled = selectedDeck == null;
|
||||
lobbyDeckSection.hidden = false;
|
||||
} else {
|
||||
lobbyDeckSection.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
submitDeckButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/chooseDeck`);
|
||||
req.addEventListener('load', e => {
|
||||
|
|
@ -73,37 +116,13 @@ submitDeckButton.addEventListener('click', e => {
|
|||
}
|
||||
});
|
||||
let data = new URLSearchParams();
|
||||
let cardsString = '';
|
||||
for (var el of cardButtons) {
|
||||
if (el.inputElement.checked) {
|
||||
if (cardsString != '') cardsString += '+';
|
||||
cardsString += el.card.number.toString();
|
||||
}
|
||||
}
|
||||
data.append('clientToken', clientToken);
|
||||
data.append('deckName', 'Deck');
|
||||
data.append('deckCards', cardsString);
|
||||
data.append('deckName', selectedDeck.name);
|
||||
data.append('deckCards', selectedDeck.cards.join('+'));
|
||||
req.send(data.toString());
|
||||
localStorage.setItem('lastDeck', cardsString);
|
||||
localStorage.setItem('lastDeckName', selectedDeck.name);
|
||||
});
|
||||
|
||||
const starterDeck = [ 6, 34, 159, 13, 45, 137, 22, 52, 141, 28, 55, 103, 40, 56, 92 ];
|
||||
const lastDeckString = localStorage.getItem('lastDeck');
|
||||
const lastDeck = lastDeckString?.split(/\+/)?.map(s => parseInt(s)) || starterDeck;
|
||||
|
||||
cardDatabase.loadAsync().then(cards => {
|
||||
const cardList = document.getElementById('cardList')!;
|
||||
for (const card of cards) {
|
||||
const button = new CardButton('checkbox', card);
|
||||
cardButtons.push(button);
|
||||
button.checked = lastDeck != null && lastDeck.includes(card.number);
|
||||
button.inputElement.addEventListener('input', updateDeckCount);
|
||||
cardList.appendChild(button.element);
|
||||
}
|
||||
updateDeckCount();
|
||||
document.getElementById('cardListLoadingSection')!.hidden = true;
|
||||
}).catch(() => communicationError);
|
||||
|
||||
stageDatabase.loadAsync().then(stages => {
|
||||
const stageList = document.getElementById('stageList')!;
|
||||
for (const stage of stages) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const joinGameButton = document.getElementById('joinGameButton')!;
|
|||
const nameBox = document.getElementById('nameBox') as HTMLInputElement;
|
||||
const gameIDBox = document.getElementById('gameIDBox') as HTMLInputElement;
|
||||
const maxPlayersBox = document.getElementById('maxPlayersBox') as HTMLSelectElement;
|
||||
const preGameDeckEditorButton = document.getElementById('preGameDeckEditorButton') as HTMLLinkElement;
|
||||
|
||||
let shownMaxPlayersWarning = false;
|
||||
|
||||
|
|
@ -47,17 +48,18 @@ maxPlayersBox.addEventListener('change', () => {
|
|||
}
|
||||
});
|
||||
|
||||
function setGameUrl(gameID: string | null) {
|
||||
function setUrl(path: string) {
|
||||
if (canPushState) {
|
||||
try {
|
||||
history.pushState(null, '', `game/${gameID}`);
|
||||
history.pushState(null, '', path);
|
||||
} catch {
|
||||
canPushState = false;
|
||||
location.hash = `#game/${gameID}`;
|
||||
location.hash = `#${path}`;
|
||||
}
|
||||
} else
|
||||
location.hash = `#game/${gameID}`;
|
||||
location.hash = `#${path}`;
|
||||
}
|
||||
function setGameUrl(gameID: string) { setUrl(`game/${gameID}`); }
|
||||
|
||||
function tryJoinGame(name: string, idOrUrl: string, fromInitialLoad: boolean) {
|
||||
const m = /(?:^|[#/])([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i.exec(idOrUrl);
|
||||
|
|
@ -147,19 +149,29 @@ function presetGameID(url: string) {
|
|||
tryJoinGame(playerName, url, true);
|
||||
}
|
||||
|
||||
preGameDeckEditorButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showDeckList();
|
||||
setUrl('deckeditor');
|
||||
});
|
||||
|
||||
const playerName = localStorage.getItem('name');
|
||||
(document.getElementById('nameBox') as HTMLInputElement).value = playerName || '';
|
||||
|
||||
function processUrl() {
|
||||
const m = /^(.*)\/game\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/.exec(location.toString());
|
||||
if (m)
|
||||
presetGameID(m[2]);
|
||||
else if (location.hash) {
|
||||
canPushState = false;
|
||||
presetGameID(location.hash);
|
||||
} else {
|
||||
clearPreGameForm(false);
|
||||
showSection('preGame');
|
||||
if (location.pathname.endsWith('/deckeditor') || location.hash == '#deckeditor')
|
||||
showDeckList();
|
||||
else {
|
||||
const m = /^(.*)\/game\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/.exec(location.toString());
|
||||
if (m)
|
||||
presetGameID(m[2]);
|
||||
else if (location.hash) {
|
||||
canPushState = false;
|
||||
presetGameID(location.hash);
|
||||
} else {
|
||||
clearPreGameForm(false);
|
||||
showSection('preGame');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,3 +179,6 @@ window.addEventListener('popstate', () => {
|
|||
processUrl();
|
||||
});
|
||||
processUrl();
|
||||
|
||||
if (!canPushState)
|
||||
preGameDeckEditorButton.href = '#deckeditor';
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
let canPushState = isSecureContext && location.protocol != 'file:';
|
||||
|
||||
const decks: Deck[] = [ new Deck('Starter Deck', [ 6, 34, 159, 13, 45, 137, 22, 52, 141, 28, 55, 103, 40, 56, 92 ], true) ];
|
||||
let selectedDeck: Deck | null = null;
|
||||
|
||||
function delay(ms: number) { return new Promise(resolve => setTimeout(() => resolve(null), ms)); }
|
||||
|
||||
// Sections
|
||||
const sections = new Map<string, HTMLDivElement>();
|
||||
for (var id of [ 'noJS', 'preGame', 'lobby', 'game' ]) {
|
||||
for (var id of [ 'noJS', 'preGame', 'lobby', 'game', 'deckList', 'deckEdit' ]) {
|
||||
let el = document.getElementById(`${id}Section`) as HTMLDivElement;
|
||||
if (!el) throw new EvalError(`Element not found: ${id}Section`);
|
||||
sections.set(id, el);
|
||||
|
|
@ -50,7 +53,7 @@ function onGameStateChange(game: any, playerData: any) {
|
|||
lobbySelectedStageSection.appendChild(selectedStageButton.element);
|
||||
|
||||
lobbySelectedStageSection.hidden = false;
|
||||
lobbyDeckSection.hidden = !playerData || game.players[playerData.playerIndex]?.isReady;
|
||||
initDeckSelection();
|
||||
break;
|
||||
case GameState.Redraw:
|
||||
case GameState.Ongoing:
|
||||
|
|
@ -239,3 +242,11 @@ function isInternetExplorer() {
|
|||
if (isInternetExplorer()) {
|
||||
alert("You seem to be using an unsupported browser. Some layout or features of this app may not work correctly.");
|
||||
}
|
||||
|
||||
function clearChildren(el: Element) {
|
||||
let el2;
|
||||
while (el2 = el.firstChild)
|
||||
el.removeChild(el2);
|
||||
}
|
||||
|
||||
cardDatabase.loadAsync().then(initCardDatabase).catch(() => communicationError);
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@
|
|||
src: url('assets/splatoon2.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Body */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
background: black;
|
||||
font-family: 'Splatoon 2', sans-serif;
|
||||
|
|
@ -75,16 +74,16 @@ footer {
|
|||
}
|
||||
|
||||
#playerList {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#playerList li {
|
||||
width: calc(100% - 3em);
|
||||
margin: 0.5em 1em;
|
||||
background: #111;
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
width: calc(100% - 3em);
|
||||
margin: 0.5em 1em;
|
||||
background: #111;
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
text-shadow: 1px 1px black;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +94,7 @@ footer {
|
|||
}
|
||||
|
||||
#playerList .ready::after {
|
||||
content: '\2714';
|
||||
content: '\2714';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0.5em;
|
||||
|
|
@ -145,7 +144,7 @@ footer {
|
|||
}
|
||||
|
||||
.stageGrid td {
|
||||
width: 0.5em;
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
|
@ -183,6 +182,7 @@ footer {
|
|||
border: 1px solid var(--colour);
|
||||
border-radius: 0.5em;
|
||||
width: 10em;
|
||||
height: 12em;
|
||||
margin: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -360,6 +360,7 @@ footer {
|
|||
#cardList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* Game UI */
|
||||
|
|
@ -649,7 +650,74 @@ footer {
|
|||
font-size: large;
|
||||
}
|
||||
|
||||
/* Deck editor */
|
||||
|
||||
:is(#deckListSection, #deckEditSection):not([hidden]) {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
#deckList :is(label, button) {
|
||||
width: 20rem;
|
||||
height: 3rem;
|
||||
background: dimgrey;
|
||||
margin: 0.5em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
border: inherit;
|
||||
}
|
||||
|
||||
/* Score section */
|
||||
#deckList {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
#deckList input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card.emptySlot {
|
||||
--colour: dimgrey;
|
||||
}
|
||||
|
||||
#deckCardListView, #deckCardListEdit {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
justify-content: center;
|
||||
grid-column: 2;
|
||||
grid-row: 1 / 5;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#deckEditorDeckViewSection:not([hidden]), #deckEditorDeckEditSection {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto auto auto 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#deckName, #deckName2 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
:is(#deckEditorDeckViewSection, #deckEditorDeckEditSection) > h3 + div {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.deckSizeContainer {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
#deckEditorCardListSection {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
"strict": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
|
||||
"noImplicitReturns": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user