mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-10-27 03:53:53 -05:00
Card art and sleeves update
This commit is contained in:
@@ -196,7 +196,7 @@ class Board {
|
||||
if (space == Space.Wall || space == Space.OutOfBounds)
|
||||
return 'It cannot be over a wall.';
|
||||
if (space >= Space.SpecialInactive1)
|
||||
return `It cannot be over${' <div class="playHintSpecial" aria-label="Special space"> </div>'.repeat(Math.max(currentGame?.players?.length ?? 0, 2))}.`;
|
||||
return `It cannot be over${' <div class="playHintSpecial" aria-label="Special space"> </div>'.repeat(Math.max(currentGame?.game.players.length ?? 0, 2))}.`;
|
||||
if (space != Space.Empty && !isSpecialAttack)
|
||||
return 'It cannot be over inked spaces.';
|
||||
if (!isAnchored) {
|
||||
|
||||
@@ -2,6 +2,13 @@ class Card {
|
||||
number: number;
|
||||
altNumber: number | null;
|
||||
name: string;
|
||||
line1: string | null;
|
||||
line2: string | null;
|
||||
artFileName: string | null;
|
||||
imageUrl?: string;
|
||||
textScale: number;
|
||||
inkColour1: Colour;
|
||||
inkColour2: Colour;
|
||||
rarity: Rarity;
|
||||
specialCost: number;
|
||||
grid: readonly (readonly Space[])[];
|
||||
@@ -12,10 +19,19 @@ class Card {
|
||||
private maxX: number;
|
||||
private maxY: number;
|
||||
|
||||
constructor(number: number, altNumber: number | null, name: string, rarity: Rarity, specialCost: number, grid: Space[][]) {
|
||||
private static DEFAULT_INK_COLOUR_1: Colour = { r: 116, g: 96, b: 240 };
|
||||
private static DEFAULT_INK_COLOUR_2: Colour = { r: 224, g: 242, b: 104 };
|
||||
|
||||
constructor(number: number, altNumber: number | null, name: string, line1: string | null, line2: string | null, artFileName: string | null, textScale: number, inkColour1: Colour, inkColour2: Colour, rarity: Rarity, specialCost: number, grid: Space[][]) {
|
||||
this.number = number;
|
||||
this.altNumber = altNumber;
|
||||
this.name = name;
|
||||
this.line1 = line1;
|
||||
this.line2 = line2;
|
||||
this.artFileName = artFileName;
|
||||
this.textScale = textScale;
|
||||
this.inkColour1 = inkColour1;
|
||||
this.inkColour2 = inkColour2;
|
||||
this.rarity = rarity;
|
||||
this.specialCost = specialCost;
|
||||
this.grid = grid;
|
||||
@@ -40,7 +56,9 @@ class Card {
|
||||
}
|
||||
|
||||
static fromJson(obj: any) {
|
||||
return new Card(obj.number, obj.altNumber ?? null, obj.name, obj.rarity, obj.specialCost, obj.grid);
|
||||
return cardDatabase.cards && cardDatabase.isValidCardNumber(obj.number)
|
||||
? cardDatabase.get(obj.number)
|
||||
: new Card(obj.number, obj.altNumber ?? null, obj.name, obj.line1 ?? null, obj.line2 ?? null, obj.artFileName ?? null, obj.textScale ?? 1, obj.inkColour1 ?? this.DEFAULT_INK_COLOUR_1, obj.inkColour2 ?? this.DEFAULT_INK_COLOUR_2, obj.rarity, obj.specialCost, obj.grid);
|
||||
}
|
||||
|
||||
get isUpcoming() { return this.number < 0; }
|
||||
|
||||
@@ -8,7 +8,7 @@ class CardButton extends CheckButton {
|
||||
constructor(card: Card) {
|
||||
let button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.classList.add('card');
|
||||
button.classList.add('cardButton');
|
||||
button.classList.add([ 'common', 'rare', 'fresh' ][card.rarity]);
|
||||
if (card.number < 0) button.classList.add('upcoming');
|
||||
button.dataset.cardNumber = card.number.toString();
|
||||
@@ -35,6 +35,13 @@ class CardButton extends CheckButton {
|
||||
}
|
||||
}
|
||||
|
||||
if (card.imageUrl) {
|
||||
const bgDiv = document.createElement('div');
|
||||
bgDiv.setAttribute('class', 'cardArt');
|
||||
bgDiv.style.backgroundImage = `url(${card.imageUrl})`;
|
||||
button.appendChild(bgDiv);
|
||||
}
|
||||
|
||||
let row = document.createElement('div');
|
||||
row.className = 'cardHeader';
|
||||
button.appendChild(row);
|
||||
|
||||
@@ -28,7 +28,7 @@ const cardDatabase = {
|
||||
const cardListRequest = new XMLHttpRequest();
|
||||
cardListRequest.open('GET', `${config.apiBaseUrl}/cards`);
|
||||
cardListRequest.addEventListener('load', e => {
|
||||
const cards = [ ];
|
||||
const cards: Card[] = [ ];
|
||||
if (cardListRequest.status == 200) {
|
||||
const s = cardListRequest.responseText;
|
||||
const response = JSON.parse(s) as object[];
|
||||
@@ -40,7 +40,38 @@ const cardDatabase = {
|
||||
else if (card.altNumber != null && card.altNumber < 0) cardDatabase._byAltNumber[-card.altNumber] = card;
|
||||
}
|
||||
cardDatabase.cards = cards;
|
||||
resolve(cards);
|
||||
|
||||
if (window.location.protocol == 'file:') {
|
||||
// If debugging locally, just read the files from the assets directory.
|
||||
for (const card of cardDatabase.cards!) {
|
||||
card.imageUrl = `assets/external/card/${card.artFileName}.webp`
|
||||
}
|
||||
resolve(cards);
|
||||
return;
|
||||
}
|
||||
// Otherwise, download and extract card images from a .tar package.
|
||||
const imagesRequest = new XMLHttpRequest();
|
||||
imagesRequest.responseType = 'arraybuffer';
|
||||
imagesRequest.open('GET', 'assets/external/card.tar');
|
||||
imagesRequest.addEventListener('load', () => {
|
||||
if (imagesRequest.status == 200 && imagesRequest.response) {
|
||||
const buffer = imagesRequest.response as ArrayBuffer;
|
||||
untar(buffer).then(files => {
|
||||
for (const tarFile of files) {
|
||||
const card = cardDatabase.cards!.find(c => tarFile.name == `${c.artFileName}.webp`);
|
||||
if (!card) continue;
|
||||
card.imageUrl = tarFile.getBlobUrl();
|
||||
}
|
||||
resolve(cards);
|
||||
});
|
||||
} else {
|
||||
reject(new Error(`Error downloading card images: response was ${imagesRequest.status}`));
|
||||
}
|
||||
});
|
||||
imagesRequest.addEventListener('error', () => {
|
||||
reject(new Error('Error downloading card images: no further information.'))
|
||||
});
|
||||
imagesRequest.send();
|
||||
} else {
|
||||
reject(new Error(`Error downloading card database: response was ${cardListRequest.status}`));
|
||||
}
|
||||
|
||||
165
TableturfBattleClient/src/CardDisplay.ts
Normal file
165
TableturfBattleClient/src/CardDisplay.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
class CardDisplay {
|
||||
readonly card: Card;
|
||||
readonly element: SVGSVGElement;
|
||||
|
||||
constructor(card: Card, level: number) {
|
||||
let 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;
|
||||
|
||||
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());
|
||||
|
||||
// Background
|
||||
const image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
||||
image.setAttribute('href', `assets/CardBackground-${card.rarity}-${level > 0 ? '1' : '0'}.webp`);
|
||||
image.setAttribute('width', '100%');
|
||||
image.setAttribute('height', '100%');
|
||||
svg.appendChild(image);
|
||||
|
||||
if (level == 0) {
|
||||
svg.insertAdjacentHTML('beforeend', `<image href="assets/external/CardInk.webp" width="635" height="885" clip-path="url(#myClip)"/>`);
|
||||
} else {
|
||||
svg.insertAdjacentHTML('beforeend', `
|
||||
<filter id="ink1-${card.number}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="${card.inkColour1.r / 255} 0 0 0 0 0 ${card.inkColour1.g / 255} 0 0 0 0 0 ${card.inkColour1.b / 255} 0 0 0 0 0 0.88 0"/></filter>
|
||||
<image href="assets/external/CardInk-1.webp" width="635" height="885" clip-path="url(#myClip)" filter="url(#ink1-${card.number})"/>
|
||||
<filter id="ink2-${card.number}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="${card.inkColour2.r / 255} 0 0 0 0 0 ${card.inkColour2.g / 255} 0 0 0 0 0 ${card.inkColour2.b / 255} 0 0 0 0 0 0.88 0"/></filter>
|
||||
<image href="assets/external/CardInk-2.webp" width="635" height="885" clip-path="url(#myClip)" filter="url(#ink2-${card.number})"/>
|
||||
`);
|
||||
}
|
||||
|
||||
// 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('transform', 'translate(380 604) rotate(6.5) scale(0.283)');
|
||||
svg.appendChild(g);
|
||||
|
||||
let size = 0;
|
||||
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');
|
||||
g.appendChild(rect);
|
||||
} else {
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
const elements: Element[] = [ rect ];
|
||||
|
||||
size++;
|
||||
rect.classList.add(card.grid[x][y] == Space.SpecialInactive1 ? 'special' : 'ink');
|
||||
|
||||
const image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
||||
image.setAttribute('href', card.grid[x][y] == Space.SpecialInactive1 ? 'assets/SpecialOverlay.png' : 'assets/InkOverlay.png');
|
||||
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');
|
||||
g.appendChild(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Name
|
||||
const text1 = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text1.setAttribute('class', 'cardDisplayName');
|
||||
text1.setAttribute('x', '50%');
|
||||
text1.setAttribute('y', '168');
|
||||
text1.setAttribute('font-size', '76');
|
||||
text1.setAttribute('font-weight', 'bold');
|
||||
text1.setAttribute('stroke', 'black');
|
||||
text1.setAttribute('stroke-width', '15');
|
||||
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:
|
||||
svg.insertAdjacentHTML('beforeend', `
|
||||
<linearGradient id='rareGradient' y1='25%' spreadMethod='reflect'>
|
||||
<stop offset='0%' stop-color='#FEF9C6'/>
|
||||
<stop offset='50%' stop-color='#DFAF17'/>
|
||||
<stop offset='100%' stop-color='#FEF9C6'/>
|
||||
</linearGradient>
|
||||
`);
|
||||
text1.setAttribute('fill', 'url("#rareGradient")');
|
||||
break;
|
||||
case Rarity.Fresh:
|
||||
svg.insertAdjacentHTML('beforeend', `
|
||||
<linearGradient id='freshGradient' y2='25%'>
|
||||
<stop offset='0%' stop-color='#FF8EDD'/>
|
||||
<stop offset='25%' stop-color='#FFEC9F'/>
|
||||
<stop offset='50%' stop-color='#B84386'/>
|
||||
<stop offset='75%' stop-color='#2BEFC8'/>
|
||||
<stop offset='100%' stop-color='#FF8EDD'/>
|
||||
</linearGradient>
|
||||
`);
|
||||
text1.setAttribute('fill', 'url("#freshGradient")');
|
||||
break;
|
||||
}
|
||||
if (card.line1 && card.line2) {
|
||||
const tspan1 = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||
tspan1.setAttribute('y', '122');
|
||||
tspan1.appendChild(document.createTextNode(card.line1));
|
||||
text1.appendChild(tspan1);
|
||||
|
||||
const tspan2 = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||
tspan2.setAttribute('x', '50%');
|
||||
tspan2.setAttribute('y', '216');
|
||||
tspan2.appendChild(document.createTextNode(card.line2));
|
||||
text1.appendChild(tspan2);
|
||||
} else
|
||||
text1.innerHTML = card.name;
|
||||
|
||||
svg.appendChild(text1);
|
||||
|
||||
// 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'>${size}</text>`);
|
||||
|
||||
// Special cost
|
||||
const g2 = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
class Deck {
|
||||
class SavedDeck {
|
||||
name: string;
|
||||
sleeves: number;
|
||||
cards: number[];
|
||||
isReadOnly: boolean;
|
||||
upgrades: number[];
|
||||
isReadOnly?: boolean;
|
||||
|
||||
constructor(name: string, cards: number[], isReadOnly: boolean) {
|
||||
constructor(name: string, sleeves: number, cards: number[], upgrades: number[], isReadOnly: boolean) {
|
||||
this.name = name;
|
||||
this.sleeves = sleeves;
|
||||
this.cards = cards;
|
||||
this.upgrades = upgrades;
|
||||
this.isReadOnly = isReadOnly;
|
||||
}
|
||||
|
||||
@@ -19,3 +23,17 @@ class Deck {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Deck {
|
||||
name: string;
|
||||
sleeves: number;
|
||||
cards: Card[];
|
||||
upgrades: number[];
|
||||
|
||||
constructor(name: string, sleeves: number, cards: Card[], upgrades: number[]) {
|
||||
this.name = name;
|
||||
this.sleeves = sleeves;
|
||||
this.cards = cards;
|
||||
this.upgrades = upgrades;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
/** A UUID used to identify the client. */
|
||||
let clientToken = window.localStorage.getItem('clientToken') || '';
|
||||
/** The data of the current game, or null if not in a game. */
|
||||
let currentGame: {
|
||||
id: string,
|
||||
interface Game {
|
||||
state: GameState,
|
||||
/** The list of players in the current game. */
|
||||
players: Player[],
|
||||
/** The maximum number of players in the game. */
|
||||
maxPlayers: number,
|
||||
/** The current one-based turn number, or 0 if redraw decisions are being made. */
|
||||
turnNumber: number,
|
||||
/** The total turn time limit in seconds. */
|
||||
turnTimeLimit: number | null,
|
||||
/** The time remaining in the current turn in seconds. */
|
||||
turnTimeLeft: number | null,
|
||||
/** The number of game wins needed to win the set, or null if no goal win count is set. */
|
||||
goalWinCount: number | null,
|
||||
}
|
||||
|
||||
/** A UUID used to identify the client. */
|
||||
let clientToken = window.localStorage.getItem('clientToken') || '';
|
||||
/** The data of the current game, or null if not in a game. */
|
||||
let currentGame: {
|
||||
id: string,
|
||||
game: Game,
|
||||
/** The user's player data, or null if they are spectating. */
|
||||
me: PlayerData | null,
|
||||
turnNumber: number,
|
||||
turnTimeLimit: number | null,
|
||||
turnTimeLeft: number | null,
|
||||
goalWinCount: number | null,
|
||||
/** The WebSocket used for receiving game events, or null if not yet connected. */
|
||||
webSocket: WebSocket | null
|
||||
} | null = null;
|
||||
@@ -24,7 +32,7 @@ let currentReplay: {
|
||||
games: {
|
||||
stage: Stage,
|
||||
playerData: {
|
||||
deck: Card[],
|
||||
deck: Deck,
|
||||
initialDrawOrder: number[],
|
||||
drawOrder: number[],
|
||||
won: boolean
|
||||
|
||||
@@ -9,6 +9,7 @@ const deckCardListView = document.getElementById('deckCardListView')!;
|
||||
const addDeckControls = document.getElementById('addDeckControls')!;
|
||||
const newDeckButton = document.getElementById('newDeckButton') as HTMLButtonElement;
|
||||
const importDeckButton = document.getElementById('importDeckButton') as HTMLButtonElement;
|
||||
const deckSleevesButton = document.getElementById('deckSleevesButton') as HTMLButtonElement;
|
||||
const deckEditButton = document.getElementById('deckEditButton') as HTMLButtonElement;
|
||||
const deckListTestButton = document.getElementById('deckListTestButton') as HTMLButtonElement;
|
||||
const deckExportButton = document.getElementById('deckExportButton') as HTMLButtonElement;
|
||||
@@ -22,6 +23,11 @@ const deckExportTextBox = document.getElementById('deckExportTextBox') as HTMLTe
|
||||
|
||||
const deckExportAllButton = document.getElementById('deckExportAllButton') as HTMLButtonElement;
|
||||
|
||||
const deckSleevesDialog = document.getElementById('deckSleevesDialog') as HTMLDialogElement;
|
||||
const deckSleevesForm = document.getElementById('deckSleevesForm') as HTMLFormElement;
|
||||
const deckSleevesButtons = deckSleevesForm.getElementsByTagName('input');
|
||||
const deckSleevesOkButton = document.getElementById('deckSleevesOkButton') as HTMLButtonElement;
|
||||
|
||||
const deckImportDialog = document.getElementById('deckImportDialog') as HTMLDialogElement;
|
||||
const deckImportForm = document.getElementById('deckImportForm') as HTMLFormElement;
|
||||
const deckImportTextBox = document.getElementById('deckImportTextBox') as HTMLTextAreaElement;
|
||||
@@ -39,7 +45,7 @@ const deckImportFileBox = document.getElementById('deckImportFileBox') as HTMLIn
|
||||
const deckImportErrorBox = document.getElementById('deckImportErrorBox')!;
|
||||
const deckImportOkButton = document.getElementById('deckImportOkButton') as HTMLButtonElement;
|
||||
|
||||
const deckButtons = new CheckButtonGroup<Deck>(deckList);
|
||||
const deckButtons = new CheckButtonGroup<SavedDeck>(deckList);
|
||||
|
||||
let deckListTouchMode = false;
|
||||
let draggingDeckButton: Element | null = null;
|
||||
@@ -85,7 +91,7 @@ deckViewBackButton.addEventListener('click', e => {
|
||||
});
|
||||
|
||||
function saveDecks() {
|
||||
const json = JSON.stringify(decks.filter(d => !d.isReadOnly), [ 'name', 'cards' ]);
|
||||
const json = JSON.stringify(decks.filter(d => !d.isReadOnly), [ 'name', 'cards', 'sleeves' ]);
|
||||
localStorage.setItem('decks', json);
|
||||
}
|
||||
|
||||
@@ -93,13 +99,13 @@ function saveDecks() {
|
||||
const decksString = localStorage.getItem('decks');
|
||||
if (decksString) {
|
||||
for (const deck of JSON.parse(decksString)) {
|
||||
decks.push(new Deck(deck.name, deck.cards, false));
|
||||
decks.push(new SavedDeck(deck.name, deck.sleeves ?? 0, deck.cards, deck.upgrades ?? new Array(15), 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));
|
||||
decks.push(new SavedDeck('Custom Deck', 0, lastDeck, new Array(15), false));
|
||||
saveDecks();
|
||||
}
|
||||
localStorage.removeItem('lastDeck');
|
||||
@@ -110,9 +116,11 @@ function saveDecks() {
|
||||
}
|
||||
}
|
||||
|
||||
function createDeckButton(deck: Deck) {
|
||||
function createDeckButton(deck: SavedDeck) {
|
||||
const buttonElement = document.createElement('button');
|
||||
buttonElement.className = 'deckButton';
|
||||
buttonElement.type = 'button';
|
||||
buttonElement.dataset.sleeves = deck.sleeves.toString();
|
||||
const button = new CheckButton(buttonElement);
|
||||
deckButtons.add(button, deck);
|
||||
buttonElement.addEventListener('click', () => {
|
||||
@@ -199,14 +207,17 @@ function deckButton_drop(e: DragEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function importDecks(decksToImport: (Deck | number[])[]) {
|
||||
let newSelectedDeck: Deck | null = null;
|
||||
function importDecks(decksToImport: (SavedDeck | number[])[]) {
|
||||
let newSelectedDeck: SavedDeck | null = null;
|
||||
for (const el of decksToImport) {
|
||||
let deck;
|
||||
if (el instanceof Array)
|
||||
deck = new Deck(`Imported Deck ${decks.length + 1}`, el, false);
|
||||
deck = new SavedDeck(`Imported Deck ${decks.length + 1}`, 0, el, new Array(15), false);
|
||||
else {
|
||||
deck = el;
|
||||
deck.sleeves ??= 0;
|
||||
deck.upgrades ??= new Array(15);
|
||||
deck.isReadOnly = false;
|
||||
if (!deck.name) deck.name = `Imported Deck ${decks.length + 1}`;
|
||||
}
|
||||
createDeckButton(deck);
|
||||
@@ -223,7 +234,7 @@ function importDecks(decksToImport: (Deck | number[])[]) {
|
||||
}
|
||||
|
||||
newDeckButton.addEventListener('click', () => {
|
||||
selectedDeck = new Deck(`Deck ${decks.length + 1}`, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], false);
|
||||
selectedDeck = new SavedDeck(`Deck ${decks.length + 1}`, 0, new Array(15), new Array(15), false);
|
||||
createDeckButton(selectedDeck);
|
||||
decks.push(selectedDeck);
|
||||
editDeck();
|
||||
@@ -287,9 +298,29 @@ function parseDecksForImport(s: string) {
|
||||
// TODO: add support for tblturf.ink
|
||||
}
|
||||
|
||||
deckSleevesButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
deckSleevesButtons[selectedDeck.sleeves].checked = true;
|
||||
deckSleevesDialog.showModal();
|
||||
});
|
||||
deckSleevesForm.addEventListener('submit', e => {
|
||||
if (e.submitter == deckSleevesOkButton && selectedDeck != null) {
|
||||
let i = 0;
|
||||
for (const button of deckSleevesButtons) {
|
||||
if (button.checked) {
|
||||
selectedDeck.sleeves = i;
|
||||
deckButtons.entries[decks.indexOf(selectedDeck)].button.buttonElement.dataset.sleeves = i.toString();
|
||||
saveDecks();
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
deckEditButton.addEventListener('click', editDeck);
|
||||
|
||||
deckListTestButton.addEventListener('click', _ => {
|
||||
deckListTestButton.addEventListener('click', () => {
|
||||
testStageButtons.deselect();
|
||||
testStageSelectionDialog.showModal();
|
||||
});
|
||||
@@ -315,9 +346,10 @@ function selectDeck() {
|
||||
deckListTestButton.disabled = false;
|
||||
deckExportButton.disabled = false;
|
||||
deckCopyButton.disabled = false;
|
||||
deckEditButton.disabled = selectedDeck.isReadOnly;
|
||||
deckRenameButton.disabled = selectedDeck.isReadOnly;
|
||||
deckDeleteButton.disabled = selectedDeck.isReadOnly;
|
||||
deckSleevesButton.disabled = selectedDeck.isReadOnly ?? false;
|
||||
deckEditButton.disabled = selectedDeck.isReadOnly ?? false;
|
||||
deckRenameButton.disabled = selectedDeck.isReadOnly ?? false;
|
||||
deckDeleteButton.disabled = selectedDeck.isReadOnly ?? false;
|
||||
deckViewSize.innerText = size.toString();
|
||||
deckListPage.classList.add('showingDeck');
|
||||
}
|
||||
@@ -331,6 +363,7 @@ function deselectDeck() {
|
||||
deckListTestButton.disabled = true;
|
||||
deckExportButton.disabled = true;
|
||||
deckCopyButton.disabled = true;
|
||||
deckSleevesButton.disabled = true;
|
||||
deckEditButton.disabled = true;
|
||||
deckRenameButton.disabled = true;
|
||||
deckDeleteButton.disabled = true;
|
||||
@@ -362,7 +395,7 @@ deckRenameButton.addEventListener('click', () => {
|
||||
|
||||
deckCopyButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
importDecks([ new Deck(`${selectedDeck.name} - Copy`, Array.from(selectedDeck.cards), false) ]);
|
||||
importDecks([ new SavedDeck(`${selectedDeck.name} - Copy`, selectedDeck.sleeves, Array.from(selectedDeck.cards), Array.from(selectedDeck.upgrades), false) ]);
|
||||
});
|
||||
|
||||
deckDeleteButton.addEventListener('click', () => {
|
||||
|
||||
@@ -153,7 +153,7 @@ function initTest(stage: Stage) {
|
||||
clear();
|
||||
testMode = true;
|
||||
gamePage.classList.add('deckTest');
|
||||
currentGame = { id: 'test', state: GameState.Ongoing, maxPlayers: 2, players: [ ], webSocket: null, turnNumber: 1, turnTimeLimit: null, turnTimeLeft: null, goalWinCount: null, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ] } };
|
||||
currentGame = { id: 'test', game: { state: GameState.Ongoing, maxPlayers: 2, players: [ ], turnNumber: 1, turnTimeLimit: null, turnTimeLeft: null, goalWinCount: null }, me: { playerIndex: 0, move: null, deck: null, hand: null, cardsUsed: [ ] }, webSocket: null };
|
||||
board.resize(stage.copyGrid());
|
||||
const startSpaces = stage.getStartSpaces(2);
|
||||
board.startSpaces = startSpaces;
|
||||
@@ -163,6 +163,8 @@ function initTest(stage: Stage) {
|
||||
|
||||
for (var o of playerBars)
|
||||
o.element.hidden = true;
|
||||
for (var el of playContainers)
|
||||
el.hidden = true;
|
||||
testPlacements.splice(0);
|
||||
testUndoButton.enabled = false;
|
||||
clearChildren(testPlacementList);
|
||||
@@ -184,79 +186,60 @@ function initTest(stage: Stage) {
|
||||
}
|
||||
|
||||
replayNextButton.buttonElement.addEventListener('click', _ => {
|
||||
if (currentGame == null || currentReplay == null || currentGame.state == GameState.GameEnded || currentGame.state == GameState.SetEnded)
|
||||
if (currentGame == null || currentReplay == null || currentGame.game.state == GameState.GameEnded || currentGame.game.state == GameState.SetEnded)
|
||||
return;
|
||||
|
||||
if (replayAnimationAbortController) {
|
||||
replayUpdateHand();
|
||||
replayAnimationAbortController.abort();
|
||||
replayAnimationAbortController = null;
|
||||
turnNumberLabel.turnNumber = currentGame.turnNumber;
|
||||
turnNumberLabel.turnNumber = currentGame.game.turnNumber;
|
||||
board.refresh();
|
||||
const scores = board.getScores();
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
updateStats(i, scores);
|
||||
}
|
||||
}
|
||||
|
||||
clearPlayContainers();
|
||||
if (currentGame.turnNumber == 0) {
|
||||
if (currentGame.game.turnNumber == 0) {
|
||||
// Show redraw decisions.
|
||||
replayPreviousButton.enabled = true;
|
||||
currentGame.state = GameState.Ongoing;
|
||||
currentGame.turnNumber++;
|
||||
turnNumberLabel.turnNumber = currentGame.turnNumber;
|
||||
currentGame.game.state = GameState.Ongoing;
|
||||
currentGame.game.turnNumber++;
|
||||
turnNumberLabel.turnNumber = currentGame.game.turnNumber;
|
||||
replayUpdateHand();
|
||||
} else if (currentGame.turnNumber > 12) {
|
||||
currentGame.state = currentReplay.gameNumber + 1 >= currentReplay.games.length ? GameState.SetEnded : GameState.GameEnded;
|
||||
} else if (currentGame.game.turnNumber > 12) {
|
||||
currentGame.game.state = currentReplay.gameNumber + 1 >= currentReplay.games.length ? GameState.SetEnded : GameState.GameEnded;
|
||||
gameButtonsContainer.hidden = true;
|
||||
gamePage.classList.add('gameEnded');
|
||||
showResult();
|
||||
} else {
|
||||
const moves = currentReplay.turns[currentGame.turnNumber - 1];
|
||||
const moves = currentReplay.turns[currentGame.game.turnNumber - 1];
|
||||
const result = board.makePlacements(moves);
|
||||
currentReplay.placements.push(result);
|
||||
|
||||
let anySpecialAttacks = false;
|
||||
// Show the cards that were played.
|
||||
const entry = handButtons.entries.find(b => b.value.number == moves[currentReplay!.watchingPlayer].card.number);
|
||||
if (entry) entry.button.checked = true;
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
const player = currentGame.players[i];
|
||||
|
||||
const move = moves[i];
|
||||
const button = new CardButton(move.card);
|
||||
button.buttonElement.disabled = true;
|
||||
if ((move as PlayMove).isSpecialAttack) {
|
||||
anySpecialAttacks = true;
|
||||
player.specialPoints -= (move as PlayMove).card.specialCost;
|
||||
button.buttonElement.classList.add('specialAttack');
|
||||
} else if (move.isPass) {
|
||||
player.passes++;
|
||||
player.specialPoints++;
|
||||
const el = document.createElement('div');
|
||||
el.className = 'passLabel';
|
||||
el.innerText = 'Pass';
|
||||
button.buttonElement.appendChild(el);
|
||||
}
|
||||
playContainers[i].append(button.buttonElement);
|
||||
}
|
||||
|
||||
for (const p of result.specialSpacesActivated) {
|
||||
const space = board.grid[p.x][p.y];
|
||||
const player2 = currentGame.players[space & 3];
|
||||
const player2 = currentGame.game.players[space & 3];
|
||||
player2.specialPoints++;
|
||||
player2.totalSpecialPoints++;
|
||||
}
|
||||
currentGame.turnNumber++;
|
||||
currentGame.game.turnNumber++;
|
||||
|
||||
for (let i = 0; i < currentGame.game.players.length; i++)
|
||||
showReady(i);
|
||||
|
||||
replayAnimationAbortController = new AbortController();
|
||||
(async () => {
|
||||
await playInkAnimations({ game: { state: GameState.Ongoing, board: null, turnNumber: currentGame.turnNumber, players: currentGame.players }, moves, placements: result.placements, specialSpacesActivated: result.specialSpacesActivated }, anySpecialAttacks, replayAnimationAbortController.signal);
|
||||
turnNumberLabel.turnNumber = currentGame.turnNumber;
|
||||
await playInkAnimations({ game: { state: GameState.Ongoing, board: null, turnNumber: currentGame.game.turnNumber, players: currentGame.game.players }, moves, placements: result.placements, specialSpacesActivated: result.specialSpacesActivated }, replayAnimationAbortController.signal);
|
||||
turnNumberLabel.turnNumber = currentGame.game.turnNumber;
|
||||
clearPlayContainers();
|
||||
if (currentGame.turnNumber > 12) {
|
||||
currentGame.state = currentReplay.gameNumber + 1 >= currentReplay.games.length ? GameState.SetEnded : GameState.GameEnded;
|
||||
if (currentGame.game.turnNumber > 12) {
|
||||
currentGame.game.state = currentReplay.gameNumber + 1 >= currentReplay.games.length ? GameState.SetEnded : GameState.GameEnded;
|
||||
gameButtonsContainer.hidden = true;
|
||||
gamePage.classList.add('gameEnded');
|
||||
showResult();
|
||||
@@ -267,52 +250,52 @@ replayNextButton.buttonElement.addEventListener('click', _ => {
|
||||
});
|
||||
|
||||
replayPreviousButton.buttonElement.addEventListener('click', _ => {
|
||||
if (currentGame == null || currentReplay == null || currentGame.turnNumber == 0) return;
|
||||
if (currentGame == null || currentReplay == null || currentGame.game.turnNumber == 0) return;
|
||||
|
||||
replayAnimationAbortController?.abort();
|
||||
replayAnimationAbortController = null;
|
||||
|
||||
if (currentGame.state == GameState.GameEnded || currentGame.state == GameState.SetEnded) {
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
if (currentGame.game.state == GameState.GameEnded || currentGame.game.state == GameState.SetEnded) {
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
if (currentReplay.games[currentReplay.gameNumber].playerData[i].won)
|
||||
currentGame.players[i].gamesWon--;
|
||||
playerBars[i].winCounter.wins = currentGame.players[i].gamesWon;
|
||||
currentGame.game.players[i].gamesWon--;
|
||||
playerBars[i].winCounter.wins = currentGame.game.players[i].gamesWon;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGame.turnNumber > 1) {
|
||||
if (currentGame.game.turnNumber > 1) {
|
||||
const result = currentReplay.placements.pop();
|
||||
if (!result) return;
|
||||
|
||||
clearPlayContainers();
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
const el = playerBars[i].resultElement;
|
||||
el.innerText = '';
|
||||
}
|
||||
|
||||
undoTurn(result);
|
||||
}
|
||||
currentGame.turnNumber--;
|
||||
currentGame.game.turnNumber--;
|
||||
replayNextButton.enabled = true;
|
||||
gamePage.classList.remove('gameEnded');
|
||||
handContainer.hidden = false;
|
||||
gameButtonsContainer.hidden = false;
|
||||
|
||||
if (currentGame.turnNumber > 0) {
|
||||
currentGame.state = GameState.Ongoing;
|
||||
turnNumberLabel.turnNumber = currentGame.turnNumber;
|
||||
if (currentGame.game.turnNumber > 0) {
|
||||
currentGame.game.state = GameState.Ongoing;
|
||||
turnNumberLabel.turnNumber = currentGame.game.turnNumber;
|
||||
const scores = board.getScores();
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
const move = currentReplay.turns[currentGame.turnNumber - 1][i];
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
const move = currentReplay.turns[currentGame.game.turnNumber - 1][i];
|
||||
if (move.isPass) {
|
||||
currentGame.players[i].passes--;
|
||||
currentGame.players[i].specialPoints--;
|
||||
currentGame.game.players[i].passes--;
|
||||
currentGame.game.players[i].specialPoints--;
|
||||
} else if ((move as PlayMove).isSpecialAttack)
|
||||
currentGame.players[i].specialPoints += (move as PlayMove).card.specialCost;
|
||||
currentGame.game.players[i].specialPoints += (move as PlayMove).card.specialCost;
|
||||
updateStats(i, scores);
|
||||
}
|
||||
} else {
|
||||
currentGame.state = GameState.Redraw;
|
||||
currentGame.game.state = GameState.Redraw;
|
||||
replayPreviousButton.enabled = false;
|
||||
turnNumberLabel.turnNumber = null;
|
||||
}
|
||||
@@ -324,7 +307,7 @@ replayPreviousButton.buttonElement.addEventListener('click', _ => {
|
||||
function undoTurn(turn: PlacementResults) {
|
||||
for (const p of turn.specialSpacesActivated) {
|
||||
const space = board.grid[p.x][p.y];
|
||||
const player2 = currentGame!.players[space & 3];
|
||||
const player2 = currentGame!.game.players[space & 3];
|
||||
if (player2) {
|
||||
player2.specialPoints--;
|
||||
player2.totalSpecialPoints--;
|
||||
@@ -352,23 +335,23 @@ function replaySwitchGame(gameNumber: number) {
|
||||
replayAnimationAbortController?.abort();
|
||||
replayAnimationAbortController = null;
|
||||
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
currentGame.players[i].specialPoints = 0;
|
||||
currentGame.players[i].totalSpecialPoints = 0;
|
||||
currentGame.players[i].passes = 0;
|
||||
currentGame.players[i].gamesWon = 0;
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
currentGame.game.players[i].specialPoints = 0;
|
||||
currentGame.game.players[i].totalSpecialPoints = 0;
|
||||
currentGame.game.players[i].passes = 0;
|
||||
currentGame.game.players[i].gamesWon = 0;
|
||||
for (let j = 0; j < gameNumber; j++) {
|
||||
if (currentReplay.games[j].playerData[i].won)
|
||||
currentGame.players[i].gamesWon++;
|
||||
currentGame.game.players[i].gamesWon++;
|
||||
}
|
||||
playerBars[i].winCounter.wins = currentGame.players[i].gamesWon;
|
||||
playerBars[i].winCounter.wins = currentGame.game.players[i].gamesWon;
|
||||
}
|
||||
|
||||
currentReplay.gameNumber = gameNumber;
|
||||
currentReplay.turns = currentReplay.games[gameNumber].turns;
|
||||
currentReplay.placements.splice(0);
|
||||
currentGame.state = GameState.Ongoing;
|
||||
currentGame.turnNumber = 0;
|
||||
currentGame.game.state = GameState.Ongoing;
|
||||
currentGame.game.turnNumber = 0;
|
||||
clearPlayContainers();
|
||||
gamePage.classList.remove('gameEnded');
|
||||
replayPreviousGameButton.enabled = gameNumber > 0;
|
||||
@@ -381,8 +364,8 @@ function replaySwitchGame(gameNumber: number) {
|
||||
|
||||
const stage = currentReplay.games[gameNumber].stage;
|
||||
board.resize(stage.copyGrid());
|
||||
const startSpaces = stage.getStartSpaces(currentGame.players.length);
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
const startSpaces = stage.getStartSpaces(currentGame.game.players.length);
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
board.grid[startSpaces[i].x][startSpaces[i].y] = Space.SpecialInactive1 | i;
|
||||
playerBars[i].points = 1;
|
||||
playerBars[i].pointsDelta = null;
|
||||
@@ -400,18 +383,18 @@ flipButton.addEventListener('click', () => {
|
||||
replayAnimationAbortController.abort();
|
||||
replayAnimationAbortController = null;
|
||||
clearPlayContainers();
|
||||
turnNumberLabel.turnNumber = currentGame.turnNumber;
|
||||
turnNumberLabel.turnNumber = currentGame.game.turnNumber;
|
||||
const scores = board.getScores();
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
updateStats(i, scores);
|
||||
}
|
||||
}
|
||||
if (currentReplay) {
|
||||
currentReplay.watchingPlayer++;
|
||||
if (currentReplay.watchingPlayer >= currentGame.players.length)
|
||||
if (currentReplay.watchingPlayer >= currentGame.game.players.length)
|
||||
currentReplay.watchingPlayer = 0;
|
||||
gamePage.dataset.myPlayerIndex = currentReplay.watchingPlayer.toString();
|
||||
gamePage.dataset.uiBaseColourIsSpecialColour = currentGame.players[currentReplay.watchingPlayer].uiBaseColourIsSpecialColour?.toString();
|
||||
gamePage.dataset.uiBaseColourIsSpecialColour = currentGame.game.players[currentReplay.watchingPlayer].uiBaseColourIsSpecialColour?.toString();
|
||||
board.flip = currentReplay.watchingPlayer % 2 != 0;
|
||||
clearShowDeck();
|
||||
replayUpdateHand();
|
||||
@@ -510,7 +493,7 @@ function loadPlayers(players: Player[]) {
|
||||
const scores = board.getScores();
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
const player = players[i];
|
||||
currentGame!.players[i] = players[i];
|
||||
currentGame!.game.players[i] = players[i];
|
||||
playerBars[i].name = player.name;
|
||||
playerBars[i].winCounter.wins = players[i].gamesWon;
|
||||
updateStats(i, scores);
|
||||
@@ -522,6 +505,7 @@ function loadPlayers(players: Player[]) {
|
||||
}
|
||||
for (let i = 0; i < playerBars.length; i++) {
|
||||
playerBars[i].visible = i < players.length;
|
||||
playContainers[i].hidden = i >= players.length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,15 +514,15 @@ function updateStats(playerIndex: number, scores: number[]) {
|
||||
playerBars[playerIndex].points = scores[playerIndex];
|
||||
playerBars[playerIndex].pointsDelta = 0;
|
||||
playerBars[playerIndex].pointsTo = 0;
|
||||
playerBars[playerIndex].specialPoints = currentGame.players[playerIndex].specialPoints;
|
||||
playerBars[playerIndex].statSpecialPointsElement.innerText = currentGame.players[playerIndex].totalSpecialPoints.toString();
|
||||
playerBars[playerIndex].statPassesElement.innerText = currentGame.players[playerIndex].passes.toString();
|
||||
playerBars[playerIndex].specialPoints = currentGame.game.players[playerIndex].specialPoints;
|
||||
playerBars[playerIndex].statSpecialPointsElement.innerText = currentGame.game.players[playerIndex].totalSpecialPoints.toString();
|
||||
playerBars[playerIndex].statPassesElement.innerText = currentGame.game.players[playerIndex].passes.toString();
|
||||
}
|
||||
|
||||
/** Shows the waiting indication for the specified player. */
|
||||
function showWaiting(playerIndex: number) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'cardBack waiting';
|
||||
el.className = 'waiting';
|
||||
playContainers[playerIndex].appendChild(el);
|
||||
}
|
||||
|
||||
@@ -547,7 +531,7 @@ function showReady(playerIndex: number) {
|
||||
clearChildren(playContainers[playerIndex]);
|
||||
const el = document.createElement('div');
|
||||
el.className = 'cardBack';
|
||||
el.innerText = 'Ready';
|
||||
el.dataset.sleeves = (currentGame?.game.players[playerIndex].sleeves ?? 0).toString();
|
||||
playContainers[playerIndex].appendChild(el);
|
||||
}
|
||||
|
||||
@@ -566,7 +550,7 @@ function resetPlayControls() {
|
||||
if (canPlay && currentGame?.me?.hand != null) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
canPlayCard[i] = board.canPlayCard(currentGame.me.playerIndex, currentGame.me.hand[i], false);
|
||||
canPlayCardAsSpecialAttack[i] = currentGame.players[currentGame.me.playerIndex].specialPoints >= currentGame.me.hand[i].specialCost
|
||||
canPlayCardAsSpecialAttack[i] = currentGame.game.players[currentGame.me.playerIndex].specialPoints >= currentGame.me.hand[i].specialCost
|
||||
&& board.canPlayCard(currentGame.me.playerIndex, currentGame.me.hand[i], true);
|
||||
handButtons.entries[i].button.enabled = canPlayCard[i];
|
||||
}
|
||||
@@ -602,7 +586,9 @@ async function playInkAnimations(data: {
|
||||
moves: Move[],
|
||||
placements: Placement[],
|
||||
specialSpacesActivated: Point[]
|
||||
}, anySpecialAttacks: boolean, abortSignal?: AbortSignal) {
|
||||
}, abortSignal?: AbortSignal) {
|
||||
if (!currentGame) return;
|
||||
|
||||
const inkPlaced = new Set<number>();
|
||||
const placements = data.placements;
|
||||
board.clearHighlight();
|
||||
@@ -611,6 +597,46 @@ async function playInkAnimations(data: {
|
||||
board.specialAttack = false;
|
||||
canPlay = false;
|
||||
timeLabel.faded = true;
|
||||
|
||||
// Show the cards that were played.
|
||||
let anySpecialAttacks = false;
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
const player = currentGame.game.players[i];
|
||||
const move = data.moves[i];
|
||||
if ((move as PlayMove).isSpecialAttack)
|
||||
anySpecialAttacks = true;
|
||||
|
||||
function addCardDisplay() {
|
||||
clearChildren(playContainers[i]);
|
||||
const display = new CardDisplay(move.card, 1);
|
||||
playContainers[i].append(display.element);
|
||||
if ((move as PlayMove).isSpecialAttack) {
|
||||
if (currentReplay) player.specialPoints -= (move as PlayMove).card.specialCost;
|
||||
const el = document.createElement('div');
|
||||
el.className = 'specialAttackLabel';
|
||||
el.innerText = 'Special Attack!';
|
||||
playContainers[i].appendChild(el);
|
||||
display.element.classList.add('specialAttack');
|
||||
} else if (move.isPass) {
|
||||
if (currentReplay) {
|
||||
player.passes++;
|
||||
player.specialPoints++;
|
||||
}
|
||||
const el = document.createElement('div');
|
||||
el.className = 'passLabel';
|
||||
el.innerText = 'Pass';
|
||||
playContainers[i].appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
const back = playContainers[i].firstElementChild as HTMLElement;
|
||||
if (back) {
|
||||
back.style.setProperty('animation', '0.1s ease-in forwards flipCardOut');
|
||||
back.addEventListener('animationend', addCardDisplay);
|
||||
} else
|
||||
addCardDisplay();
|
||||
}
|
||||
|
||||
await delay(anySpecialAttacks ? 3000 : 1000, abortSignal);
|
||||
for (let i = 0; i < data.game.players.length; i++) {
|
||||
if ((data.moves[i] as PlayMove).isSpecialAttack)
|
||||
@@ -661,7 +687,7 @@ function showResult() {
|
||||
turnNumberLabel.turnNumber = null;
|
||||
|
||||
let winners = [ 0 ]; let maxPoints = playerBars[0].points;
|
||||
for (let i = 1; i < currentGame.players.length; i++) {
|
||||
for (let i = 1; i < currentGame.game.players.length; i++) {
|
||||
if (playerBars[i].points > maxPoints) {
|
||||
winners.splice(0);
|
||||
winners.push(i);
|
||||
@@ -670,7 +696,7 @@ function showResult() {
|
||||
winners.push(i);
|
||||
}
|
||||
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
const el = playerBars[i].resultElement;
|
||||
if (winners.includes(i)) {
|
||||
if (winners.length == 1) {
|
||||
@@ -678,8 +704,10 @@ function showResult() {
|
||||
el.classList.remove('lose');
|
||||
el.classList.remove('draw');
|
||||
el.innerText = 'Victory';
|
||||
if (currentReplay) currentGame.players[i].gamesWon++;
|
||||
playerBars[i].winCounter.wins++;
|
||||
if (currentReplay) {
|
||||
currentGame.game.players[i].gamesWon++;
|
||||
playerBars[i].winCounter.wins++;
|
||||
}
|
||||
} else {
|
||||
el.classList.remove('win');
|
||||
el.classList.remove('lose');
|
||||
@@ -704,9 +732,9 @@ function showResult() {
|
||||
replayNextButton.buttonElement.hidden = true;
|
||||
replayNextGameButton.buttonElement.hidden = true;
|
||||
flipButton.hidden = true;
|
||||
if (currentGame.state == GameState.SetEnded) {
|
||||
if (currentGame.game.state == GameState.SetEnded) {
|
||||
leaveButton.hidden = false;
|
||||
nextGameButton.buttonElement.hidden = currentGame.goalWinCount != null;
|
||||
nextGameButton.buttonElement.hidden = currentGame.game.goalWinCount != null;
|
||||
shareReplayLinkButton.hidden = false;
|
||||
canShareReplay = navigator.canShare && navigator.canShare({ url: window.location.href, title: 'Tableturf Battle Replay' });
|
||||
shareReplayLinkButton.innerText = canShareReplay ? 'Share replay link' : 'Copy replay link';
|
||||
@@ -714,7 +742,7 @@ function showResult() {
|
||||
leaveButton.hidden = currentGame.me != null;
|
||||
nextGameButton.buttonElement.hidden = currentGame.me == null;
|
||||
}
|
||||
nextGameButton.enabled = currentGame.me != null && !currentGame.players[currentGame.me.playerIndex].isReady;
|
||||
nextGameButton.enabled = currentGame.me != null && !currentGame.game.players[currentGame.me.playerIndex].isReady;
|
||||
nextGameButton.buttonElement.innerHTML = nextGameButton.enabled ? 'Next game' : '<div class="loadingSpinner"></div> Waiting for other player';
|
||||
}
|
||||
}
|
||||
@@ -726,9 +754,9 @@ function clearShowDeck() {
|
||||
showDeckButtons.splice(0);
|
||||
}
|
||||
|
||||
function populateShowDeck(deck: Card[]) {
|
||||
function populateShowDeck(deck: Deck) {
|
||||
if (showDeckButtons.length == 0) {
|
||||
for (const card of deck) {
|
||||
for (const card of deck.cards) {
|
||||
const button = new CardButton(card);
|
||||
button.buttonElement.disabled = true;
|
||||
showDeckButtons.push(button);
|
||||
@@ -764,7 +792,7 @@ function updateHandAndDeck(playerData: PlayerData) {
|
||||
handButtons.add(button, card);
|
||||
button.buttonElement.addEventListener('click', e => {
|
||||
if (!button.enabled) {
|
||||
if (specialButton.checked && currentGame!.players[currentGame!.me!.playerIndex].specialPoints < card.specialCost)
|
||||
if (specialButton.checked && currentGame!.game.players[currentGame!.me!.playerIndex].specialPoints < card.specialCost)
|
||||
cardHint.showError('Not enough special points.');
|
||||
else if (!(specialButton.checked ? canPlayCardAsSpecialAttack : canPlayCard)[i])
|
||||
cardHint.showError('No place to play this card.');
|
||||
@@ -832,13 +860,13 @@ function replayUpdateHand() {
|
||||
b.buttonElement.parentElement!.className = '';
|
||||
|
||||
let indices;
|
||||
if (currentGame.turnNumber == 0) {
|
||||
if (currentGame.game.turnNumber == 0) {
|
||||
indices = playerData.initialDrawOrder;
|
||||
} else {
|
||||
indices = playerData.drawOrder.slice(0, 4);
|
||||
for (let i = 0; i < currentGame.turnNumber - 1; i++) {
|
||||
for (let i = 0; i < currentGame.game.turnNumber - 1; i++) {
|
||||
const move = currentReplay.turns[i][currentReplay.watchingPlayer];
|
||||
let j = indices.findIndex(k => playerData.deck[k].number == move.card.number);
|
||||
let j = indices.findIndex(k => playerData.deck.cards[k].number == move.card.number);
|
||||
if (j < 0) j = indices.findIndex(k => k < 0 || k >= 15);
|
||||
if (j >= 0) {
|
||||
showDeckButtons[indices[j]].buttonElement.parentElement!.className = 'used';
|
||||
@@ -856,7 +884,7 @@ function replayUpdateHand() {
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (indices[i] >= 15) continue; // Accounts for an old bug in the server that corrupted the initialDrawOrder replay fields.
|
||||
const card = playerData.deck[indices[i]];
|
||||
const card = playerData.deck.cards[indices[i]];
|
||||
const button = new CardButton(card);
|
||||
button.buttonElement.disabled = true;
|
||||
handButtons.add(button, card);
|
||||
@@ -906,7 +934,7 @@ passButton.buttonElement.addEventListener('click', passButton_click);
|
||||
|
||||
function specialButton_click(e: Event) {
|
||||
if (!specialButton.enabled) {
|
||||
if (currentGame!.me!.hand!.every(c => currentGame!.players[currentGame!.me!.playerIndex].specialPoints < c.specialCost))
|
||||
if (currentGame!.me!.hand!.every(c => currentGame!.game.players[currentGame!.me!.playerIndex].specialPoints < c.specialCost))
|
||||
cardHint.showError('Not enough special points.');
|
||||
else if (!canPlayCardAsSpecialAttack.includes(true))
|
||||
cardHint.showError('No place to play a special attack.');
|
||||
@@ -1041,7 +1069,7 @@ board.onhighlightchange = dScores => {
|
||||
|
||||
timeLabel.ontimeout = () => {
|
||||
if (currentGame == null || !canPlay) return;
|
||||
if (currentGame.turnNumber == 0) {
|
||||
if (currentGame.game.turnNumber == 0) {
|
||||
let req = new XMLHttpRequest();
|
||||
req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/redraw`);
|
||||
req.addEventListener('error', () => communicationError());
|
||||
@@ -1263,15 +1291,15 @@ function updateRGB(playerIndex: number, colourIndex: number) {
|
||||
setColour(playerIndex, colourIndex, rgb);
|
||||
}
|
||||
function setColour(playerIndex: number, colourIndex: number, colour: Colour) {
|
||||
if (!currentGame || playerIndex >= currentGame.players.length) return;
|
||||
if (!currentGame || playerIndex >= currentGame.game.players.length) return;
|
||||
if (colourIndex == 0) {
|
||||
currentGame.players[playerIndex].colour = colour;
|
||||
currentGame.game.players[playerIndex].colour = colour;
|
||||
document.body.style.setProperty(`--primary-colour-${playerIndex + 1}`, `rgb(${colour.r}, ${colour.g}, ${colour.b})`);
|
||||
} else if (colourIndex == 1) {
|
||||
currentGame.players[playerIndex].specialColour = colour;
|
||||
currentGame.game.players[playerIndex].specialColour = colour;
|
||||
document.body.style.setProperty(`--special-colour-${playerIndex + 1}`, `rgb(${colour.r}, ${colour.g}, ${colour.b})`);
|
||||
} else {
|
||||
currentGame.players[playerIndex].specialAccentColour = colour;
|
||||
currentGame.game.players[playerIndex].specialAccentColour = colour;
|
||||
document.body.style.setProperty(`--special-accent-colour-${playerIndex + 1}`, `rgb(${colour.r}, ${colour.g}, ${colour.b})`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const lobbyStageSection = document.getElementById('lobbyStageSection')!;
|
||||
const lobbyStageSubmitButton = document.getElementById('submitStageButton') as HTMLButtonElement;
|
||||
const lobbyDeckSection = document.getElementById('lobbyDeckSection')!;
|
||||
const lobbyDeckList = document.getElementById('lobbyDeckList')!;
|
||||
const lobbyDeckButtons = new CheckButtonGroup<Deck>(lobbyDeckList);
|
||||
const lobbyDeckButtons = new CheckButtonGroup<SavedDeck>(lobbyDeckList);
|
||||
const lobbyDeckSubmitButton = document.getElementById('submitDeckButton') as HTMLButtonElement;
|
||||
|
||||
const lobbyTimeLimitBox = document.getElementById('lobbyTimeLimitBox') as HTMLInputElement;
|
||||
@@ -94,7 +94,7 @@ function lobbyResetSlots() {
|
||||
playerListItems.splice(0);
|
||||
lobbyWinCounters.splice(0);
|
||||
|
||||
for (let i = 0; i < currentGame.maxPlayers; i++) {
|
||||
for (let i = 0; i < currentGame.game.maxPlayers; i++) {
|
||||
var el = document.createElement('li');
|
||||
el.className = 'empty';
|
||||
el.innerText = 'Waiting...';
|
||||
@@ -113,8 +113,8 @@ function clearReady() {
|
||||
if (!currentGame) throw new Error('No current game');
|
||||
stageSelectionFormSubmitButton.disabled = false;
|
||||
stageSelectionFormLoadingSection.hidden = true;
|
||||
for (var i = 0; i < currentGame.players.length; i++) {
|
||||
currentGame.players[i].isReady = false;
|
||||
for (var i = 0; i < currentGame.game.players.length; i++) {
|
||||
currentGame.game.players[i].isReady = false;
|
||||
playerListItems[i].className = 'filled';
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ function clearReady() {
|
||||
function lobbyAddPlayer(playerIndex: number) {
|
||||
if (!currentGame) throw new Error('No current game');
|
||||
const listItem = playerListItems[playerIndex];
|
||||
const player = currentGame.players[playerIndex];
|
||||
const player = currentGame.game.players[playerIndex];
|
||||
listItem.innerText = player.name;
|
||||
listItem.className = player.isReady ? 'filled ready' : 'filled';
|
||||
|
||||
@@ -131,7 +131,7 @@ function lobbyAddPlayer(playerIndex: number) {
|
||||
el.title = 'Battles won';
|
||||
listItem.appendChild(el);
|
||||
const winCounter = new WinCounter(el);
|
||||
winCounter.wins = currentGame.players[playerIndex].gamesWon;
|
||||
winCounter.wins = currentGame.game.players[playerIndex].gamesWon;
|
||||
lobbyWinCounters.push(winCounter);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,9 @@ function initDeckSelection() {
|
||||
|
||||
const buttonElement = document.createElement('button');
|
||||
buttonElement.type = 'button';
|
||||
buttonElement.className = 'deckButton';
|
||||
buttonElement.innerText = deck.name;
|
||||
buttonElement.dataset.sleeves = deck.sleeves.toString();
|
||||
const button = new CheckButton(buttonElement);
|
||||
lobbyDeckButtons.add(button, deck);
|
||||
|
||||
@@ -202,6 +204,7 @@ deckSelectionForm.addEventListener('submit', e => {
|
||||
data.append('clientToken', clientToken);
|
||||
data.append('deckName', selectedDeck.name);
|
||||
data.append('deckCards', selectedDeck.cards.join('+'));
|
||||
data.append('deckSleeves', selectedDeck.sleeves.toString());
|
||||
req.send(data.toString());
|
||||
|
||||
localStorage.setItem('lastDeckName', selectedDeck.name);
|
||||
|
||||
@@ -6,6 +6,7 @@ interface Player {
|
||||
specialColour: Colour;
|
||||
specialAccentColour: Colour;
|
||||
uiBaseColourIsSpecialColour?: boolean;
|
||||
sleeves: number;
|
||||
totalSpecialPoints: number;
|
||||
passes: number;
|
||||
gamesWon: number;
|
||||
|
||||
@@ -55,7 +55,7 @@ class PlayerBar {
|
||||
|
||||
get specialPoints() { return this.specialPointsContainer.getElementsByClassName('specialPoint').length; }
|
||||
set specialPoints(value: number) {
|
||||
const oldList = this.specialPointsContainer.getElementsByClassName('specialPoint');
|
||||
const oldList = Array.from(this.specialPointsContainer.getElementsByClassName('specialPoint'));
|
||||
if (value < oldList.length) {
|
||||
for (let i = oldList.length - 1; i >= value; i--)
|
||||
this.specialPointsContainer.removeChild(oldList[i]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
interface PlayerData {
|
||||
playerIndex: number;
|
||||
hand: Card[] | null;
|
||||
deck: Card[] | null;
|
||||
deck: Deck | null;
|
||||
cardsUsed: number[];
|
||||
move: Move | null;
|
||||
}
|
||||
|
||||
@@ -28,17 +28,18 @@ function loadReplay(base64: string) {
|
||||
specialColour: { r: dataView.getUint8(pos + 3), g: dataView.getUint8(pos + 4), b: dataView.getUint8(pos + 5) },
|
||||
specialAccentColour: { r: dataView.getUint8(pos + 6), g: dataView.getUint8(pos + 7), b: dataView.getUint8(pos + 8) },
|
||||
uiBaseColourIsSpecialColour: false,
|
||||
sleeves: 0,
|
||||
totalSpecialPoints: 0,
|
||||
passes: 0,
|
||||
gamesWon: 0
|
||||
};
|
||||
players.push(player);
|
||||
|
||||
const deck = [ ];
|
||||
const cards = [ ];
|
||||
const initialDrawOrder = [ ];
|
||||
const drawOrder = [ ];
|
||||
for (let j = 0; j < 15; j++) {
|
||||
deck.push(loadCardFromReplay(dataView, pos + 9 + j));
|
||||
cards.push(loadCardFromReplay(dataView, pos + 9 + j));
|
||||
}
|
||||
for (let j = 0; j < 2; j++) {
|
||||
initialDrawOrder.push(dataView.getUint8(pos + 24 + j) & 0xF);
|
||||
@@ -51,7 +52,7 @@ function loadReplay(base64: string) {
|
||||
else
|
||||
drawOrder.push(dataView.getUint8(pos + 26 + j) >> 4 & 0xF);
|
||||
}
|
||||
playerData.push({ deck, initialDrawOrder, drawOrder, won: false });
|
||||
playerData.push({ deck: new Deck("Deck", 0, cards, new Array(15)), initialDrawOrder, drawOrder, won: false });
|
||||
pos += 35 + len;
|
||||
}
|
||||
|
||||
@@ -95,6 +96,7 @@ function loadReplay(base64: string) {
|
||||
specialColour: { r: dataView.getUint8(pos + 3), g: dataView.getUint8(pos + 4), b: dataView.getUint8(pos + 5) },
|
||||
specialAccentColour: { r: dataView.getUint8(pos + 6), g: dataView.getUint8(pos + 7), b: dataView.getUint8(pos + 8) },
|
||||
uiBaseColourIsSpecialColour: (n2 & 0x80) != 0,
|
||||
sleeves: 0,
|
||||
totalSpecialPoints: 0,
|
||||
passes: 0,
|
||||
gamesWon: 0
|
||||
@@ -108,12 +110,12 @@ function loadReplay(base64: string) {
|
||||
const playerData = [ ];
|
||||
pos++;
|
||||
for (let i = 0; i < numPlayers; i++) {
|
||||
const deck = [ ];
|
||||
const cards = [ ];
|
||||
const initialDrawOrder = [ ];
|
||||
const drawOrder = [ ];
|
||||
let won = false;
|
||||
for (let j = 0; j < 15; j++) {
|
||||
deck.push(loadCardFromReplay(dataView, pos + j));
|
||||
cards.push(loadCardFromReplay(dataView, pos + j));
|
||||
}
|
||||
for (let j = 0; j < 2; j++) {
|
||||
initialDrawOrder.push(dataView.getUint8(pos + 15 + j) & 0xF);
|
||||
@@ -126,7 +128,7 @@ function loadReplay(base64: string) {
|
||||
else
|
||||
drawOrder.push(dataView.getUint8(pos + 17 + j) >> 4 & 0xF);
|
||||
}
|
||||
playerData.push({ deck, initialDrawOrder, drawOrder, won });
|
||||
playerData.push({ deck: new Deck("Deck", 0, cards, new Array(15)), initialDrawOrder, drawOrder, won });
|
||||
pos += 25;
|
||||
}
|
||||
const turns = replayLoadTurns(dataView, numPlayers, pos);
|
||||
@@ -141,14 +143,16 @@ function loadReplay(base64: string) {
|
||||
|
||||
currentGame = {
|
||||
id: 'replay',
|
||||
state: GameState.Redraw,
|
||||
game: {
|
||||
state: GameState.Redraw,
|
||||
players: players,
|
||||
maxPlayers: players.length,
|
||||
turnNumber: 0,
|
||||
turnTimeLimit: null,
|
||||
turnTimeLeft: null,
|
||||
goalWinCount: goalWinCount
|
||||
},
|
||||
me: null,
|
||||
players: players,
|
||||
maxPlayers: players.length,
|
||||
turnNumber: 0,
|
||||
turnTimeLimit: null,
|
||||
turnTimeLeft: null,
|
||||
goalWinCount: goalWinCount,
|
||||
webSocket: null
|
||||
};
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ let initialised = false;
|
||||
let initialiseCallback: (() => void) | null = null;
|
||||
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;
|
||||
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), true) ];
|
||||
let selectedDeck: SavedDeck | null = null;
|
||||
let editingDeck = false;
|
||||
let deckModified = false;
|
||||
let shouldConfirmLeavingGame = false;
|
||||
@@ -97,15 +97,15 @@ function clearUrlFromGame() {
|
||||
|
||||
function onGameSettingsChange() {
|
||||
if (currentGame == null) return;
|
||||
if (lobbyTimeLimitBox.value != currentGame.turnTimeLimit?.toString() ?? '')
|
||||
lobbyTimeLimitBox.value = currentGame.turnTimeLimit?.toString() ?? '';
|
||||
if (lobbyTimeLimitBox.value != currentGame.game.turnTimeLimit?.toString() ?? '')
|
||||
lobbyTimeLimitBox.value = currentGame.game.turnTimeLimit?.toString() ?? '';
|
||||
}
|
||||
|
||||
function onGameStateChange(game: any, playerData: PlayerData | null) {
|
||||
if (currentGame == null)
|
||||
throw new Error('currentGame is null');
|
||||
clearPlayContainers();
|
||||
currentGame.state = game.state;
|
||||
currentGame.game.state = game.state;
|
||||
|
||||
if (game.board) {
|
||||
board.flip = playerData != null && playerData.playerIndex % 2 != 0;
|
||||
@@ -119,7 +119,7 @@ function onGameStateChange(game: any, playerData: PlayerData | null) {
|
||||
gamePage.dataset.myPlayerIndex = playerData ? playerData.playerIndex.toString() : '';
|
||||
gamePage.dataset.uiBaseColourIsSpecialColour = playerData && game.players[playerData.playerIndex].uiBaseColourIsSpecialColour ? 'true' : 'false';
|
||||
|
||||
if (game.stage != GameState.WaitingForPlayers)
|
||||
if (game.state != GameState.WaitingForPlayers)
|
||||
lobbyLockSettings(true);
|
||||
|
||||
redrawModal.hidden = true;
|
||||
@@ -159,25 +159,25 @@ function onGameStateChange(game: any, playerData: PlayerData | null) {
|
||||
} else
|
||||
initSpectator();
|
||||
|
||||
currentGame.turnNumber = game.turnNumber;
|
||||
currentGame.game.turnNumber = game.turnNumber;
|
||||
gameButtonsContainer.hidden = currentGame.me == null || game.state == GameState.GameEnded || game.state == GameState.SetEnded;
|
||||
|
||||
switch (game.state) {
|
||||
case GameState.Redraw:
|
||||
if (currentGame.me) setConfirmLeavingGame();
|
||||
redrawModal.hidden = currentGame.me == null || currentGame.players[currentGame.me.playerIndex].isReady;
|
||||
redrawModal.hidden = currentGame.me == null || currentGame.game.players[currentGame.me.playerIndex].isReady;
|
||||
turnNumberLabel.turnNumber = null;
|
||||
canPlay = false;
|
||||
timeLabel.faded = redrawModal.hidden;
|
||||
timeLabel.paused = false;
|
||||
break;
|
||||
case GameState.Ongoing:
|
||||
for (let i = 0; i < currentGame.players.length; i++)
|
||||
for (let i = 0; i < currentGame.game.players.length; i++)
|
||||
showWaiting(i);
|
||||
if (currentGame.me) setConfirmLeavingGame();
|
||||
turnNumberLabel.turnNumber = game.turnNumber;
|
||||
board.autoHighlight = true;
|
||||
canPlay = currentGame.me != null && !currentGame.players[currentGame.me.playerIndex].isReady;
|
||||
canPlay = currentGame.me != null && !currentGame.game.players[currentGame.me.playerIndex].isReady;
|
||||
timeLabel.faded = !canPlay;
|
||||
timeLabel.paused = false;
|
||||
resetPlayControls();
|
||||
@@ -214,7 +214,7 @@ errorDialog.addEventListener('close', e => {
|
||||
|
||||
function playerDataReviver(key: string, value: any) {
|
||||
return !value ? value
|
||||
: key == 'hand' || key == 'deck'
|
||||
: key == 'hand' || key == 'cards'
|
||||
? (value as (Card | number)[]).map(v => typeof v == 'number' ? cardDatabase.get(v) : Card.fromJson(v))
|
||||
: key == 'card'
|
||||
? typeof value == 'number' ? cardDatabase.get(value) : Card.fromJson(value)
|
||||
@@ -247,33 +247,35 @@ function setupWebSocket(gameID: string) {
|
||||
} else {
|
||||
currentGame = {
|
||||
id: gameID,
|
||||
state: payload.data.state,
|
||||
game: {
|
||||
state: payload.data.state,
|
||||
players: payload.data.players,
|
||||
maxPlayers: payload.data.maxPlayers,
|
||||
turnNumber: payload.data.turnNumber,
|
||||
turnTimeLimit: payload.data.turnTimeLimit,
|
||||
turnTimeLeft: payload.data.turnTimeLeft,
|
||||
goalWinCount: payload.data.goalWinCount,
|
||||
},
|
||||
me: payload.playerData,
|
||||
players: payload.data.players,
|
||||
maxPlayers: payload.data.maxPlayers,
|
||||
turnNumber: payload.data.turnNumber,
|
||||
turnTimeLimit: payload.data.turnTimeLimit,
|
||||
turnTimeLeft: payload.data.turnTimeLeft,
|
||||
goalWinCount: payload.data.goalWinCount,
|
||||
webSocket: webSocket
|
||||
};
|
||||
|
||||
lobbyResetSlots();
|
||||
for (let i = 0; i < currentGame.players.length; i++)
|
||||
for (let i = 0; i < currentGame.game.players.length; i++)
|
||||
lobbyAddPlayer(i);
|
||||
onGameSettingsChange();
|
||||
|
||||
for (let i = 0; i < playerBars.length; i++) {
|
||||
playerBars[i].visible = i < currentGame.maxPlayers;
|
||||
playerBars[i].visible = i < currentGame.game.maxPlayers;
|
||||
}
|
||||
|
||||
for (const button of stageButtons.buttons)
|
||||
(button as StageButton).setStartSpaces(currentGame.maxPlayers);
|
||||
(button as StageButton).setStartSpaces(currentGame.game.maxPlayers);
|
||||
|
||||
onGameStateChange(payload.data, payload.playerData);
|
||||
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
if (currentGame.players[i].isReady) showReady(i);
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
if (currentGame.game.players[i].isReady) showReady(i);
|
||||
}
|
||||
|
||||
if (currentGame.me) {
|
||||
@@ -294,8 +296,8 @@ function setupWebSocket(gameID: string) {
|
||||
}
|
||||
|
||||
timeLabel.paused = false;
|
||||
if (currentGame.turnTimeLeft) {
|
||||
timeLabel.setTime(currentGame.turnTimeLeft);
|
||||
if (currentGame.game.turnTimeLeft) {
|
||||
timeLabel.setTime(currentGame.game.turnTimeLeft);
|
||||
timeLabel.show();
|
||||
} else
|
||||
timeLabel.hide();
|
||||
@@ -307,19 +309,19 @@ function setupWebSocket(gameID: string) {
|
||||
}
|
||||
switch (payload.event) {
|
||||
case 'settingsChange':
|
||||
currentGame.turnTimeLimit = payload.data.turnTimeLimit;
|
||||
currentGame.game.turnTimeLimit = payload.data.turnTimeLimit;
|
||||
onGameSettingsChange();
|
||||
break;
|
||||
case 'join':
|
||||
if (payload.data.playerIndex == currentGame.players.length) {
|
||||
currentGame.players.push(payload.data.player);
|
||||
if (payload.data.playerIndex == currentGame.game.players.length) {
|
||||
currentGame.game.players.push(payload.data.player);
|
||||
lobbyAddPlayer(payload.data.playerIndex);
|
||||
}
|
||||
else
|
||||
communicationError();
|
||||
break;
|
||||
case 'playerReady':
|
||||
currentGame.players[payload.data.playerIndex].isReady = true;
|
||||
currentGame.game.players[payload.data.playerIndex].isReady = true;
|
||||
lobbySetReady(payload.data.playerIndex);
|
||||
|
||||
if (payload.data.playerIndex == currentGame.me?.playerIndex) {
|
||||
@@ -343,46 +345,28 @@ function setupWebSocket(gameID: string) {
|
||||
clearReady();
|
||||
board.autoHighlight = false;
|
||||
showPage('game');
|
||||
currentGame.turnNumber = payload.data.game.turnNumber;
|
||||
currentGame.game.turnNumber = payload.data.game.turnNumber;
|
||||
|
||||
let anySpecialAttacks = false;
|
||||
// Show the cards that were played.
|
||||
clearPlayContainers();
|
||||
for (let i = 0; i < currentGame.players.length; i++) {
|
||||
const player = currentGame.players[i];
|
||||
for (let i = 0; i < currentGame.game.players.length; i++) {
|
||||
const player = currentGame.game.players[i];
|
||||
player.specialPoints = payload.data.game.players[i].specialPoints;
|
||||
player.totalSpecialPoints = payload.data.game.players[i].totalSpecialPoints;
|
||||
player.passes = payload.data.game.players[i].passes;
|
||||
player.gamesWon = payload.data.game.players[i].gamesWon;
|
||||
player.isReady = payload.data.game.players[i].isReady;
|
||||
lobbyWinCounters[i].wins = player.gamesWon;
|
||||
|
||||
const move = payload.data.moves[i];
|
||||
const button = new CardButton(move.card);
|
||||
button.buttonElement.disabled = true;
|
||||
if (move.isSpecialAttack) {
|
||||
anySpecialAttacks = true;
|
||||
button.buttonElement.classList.add('specialAttack');
|
||||
} else if (move.isPass) {
|
||||
const el = document.createElement('div');
|
||||
el.className = move.isTimeout ? 'passLabel timeout' : 'passLabel';
|
||||
el.innerText = 'Pass';
|
||||
button.buttonElement.appendChild(el);
|
||||
}
|
||||
button.buttonElement.disabled = false;
|
||||
playContainers[i].append(button.buttonElement);
|
||||
}
|
||||
timeLabel.paused = true;
|
||||
if (payload.data.game.turnTimeLeft)
|
||||
timeLabel.setTime(payload.data.game.turnTimeLeft);
|
||||
|
||||
(async () => {
|
||||
await playInkAnimations(payload.data, anySpecialAttacks);
|
||||
await playInkAnimations(payload.data);
|
||||
if (payload.playerData) updateHandAndDeck(payload.playerData);
|
||||
turnNumberLabel.turnNumber = payload.data.game.turnNumber;
|
||||
clearPlayContainers();
|
||||
if (payload.event == 'gameEnd') {
|
||||
currentGame.state = payload.data.game.state;
|
||||
currentGame.game.state = payload.data.game.state;
|
||||
clearConfirmLeavingGame();
|
||||
gameButtonsContainer.hidden = true;
|
||||
passButton.enabled = false;
|
||||
@@ -398,7 +382,7 @@ function setupWebSocket(gameID: string) {
|
||||
timeLabel.paused = false;
|
||||
if (payload.data.game.turnTimeLeft)
|
||||
timeLabel.show();
|
||||
for (let i = 0; i < currentGame.players.length; i++)
|
||||
for (let i = 0; i < currentGame.game.players.length; i++)
|
||||
showWaiting(i);
|
||||
}
|
||||
})();
|
||||
@@ -553,7 +537,6 @@ function resetAnimation(el: HTMLElement) {
|
||||
el.style.animation = '';
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('noJSPage')!.innerText = 'Loading client...';
|
||||
|
||||
if (config.discordUrl) {
|
||||
|
||||
36
TableturfBattleClient/src/js-untar.d.ts
vendored
Normal file
36
TableturfBattleClient/src/js-untar.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
declare class TarFile {
|
||||
name: string;
|
||||
mode: string;
|
||||
uid: number;
|
||||
gid: number;
|
||||
size: number;
|
||||
mtime: number;
|
||||
checksum: number;
|
||||
type: string;
|
||||
linkname: string;
|
||||
ustarFormat: string;
|
||||
|
||||
version?: string;
|
||||
uname?: string;
|
||||
gname?: string;
|
||||
devmajor?: number;
|
||||
devminor?: number;
|
||||
namePrefix?: string;
|
||||
|
||||
buffer: ArrayBuffer;
|
||||
blob: Blob;
|
||||
getBlobUrl(): string;
|
||||
readAsString(): string;
|
||||
readAsJSON(): object;
|
||||
}
|
||||
|
||||
declare class ProgressivePromise<TResult, TProgress> extends Promise<TResult> {
|
||||
progress(cb: ((value: TProgress) => void)): ProgressivePromise<TResult, TProgress>;
|
||||
then<TResult1 = TResult, TResult2 = never>(
|
||||
onfulfilled?: ((value: TResult) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
||||
onProgress?: ((value: TProgress) => void) | undefined | null)
|
||||
: ProgressivePromise<TResult1 | TResult2, TProgress>;
|
||||
}
|
||||
|
||||
declare function untar(arrayBuffer: ArrayBuffer): ProgressivePromise<TarFile[], TarFile>;
|
||||
Reference in New Issue
Block a user