diff options
Diffstat (limited to 'html/cards')
-rw-r--r-- | html/cards/cards.js | 441 | ||||
-rw-r--r-- | html/cards/fonts/DotGothic16-Regular.ttf | bin | 0 -> 2027048 bytes | |||
-rw-r--r-- | html/cards/fonts/OFL copy.txt | 93 | ||||
-rw-r--r-- | html/cards/fonts/OFL.txt | 93 | ||||
-rw-r--r-- | html/cards/fonts/PressStart2P-Regular.ttf | bin | 0 -> 116008 bytes | |||
-rw-r--r-- | html/cards/index.html | 36 | ||||
m--------- | html/cards/pokemon-font | 0 |
7 files changed, 663 insertions, 0 deletions
diff --git a/html/cards/cards.js b/html/cards/cards.js new file mode 100644 index 0000000..98aa0e1 --- /dev/null +++ b/html/cards/cards.js @@ -0,0 +1,441 @@ +/** + * @fileOverview Trying to make an easily extensible bit of code to handle + * creating and drawing any number of decks of cards. + * + * @author: eli_oat + * @license: no gods, no masters + */ + +/** + * @typedef {Object} Card + * @property {number} x + * @property {number} y + * @property {CardData} card + * @property {boolean} isFaceUp + */ + +/** + * @typedef {Object} CardData + * @property {string} suit + * @property {string} value + */ + +/** + * @typedef {Object} GameState + * @property {Card[]} cards + * @property {Card|null} draggingCard + * @property {CardData[]} deck + * @property {{x: number, y: number}} stackPosition + */ + +const CARD_WIDTH = 100; +const CARD_HEIGHT = 150; +const PADDING = 10; +const SUITS = ['❤️', '♦️', '♣️', '♠️']; +const VALUES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; +const PATTERN_SIZE = 10; +const INITIAL_CARD_X = 20; +const INITIAL_CARD_Y = 20; +const FONT_SIZE = '34px "pokemon-font", monospace'; +const CARD_BORDER_COLOR = '#000000'; +const CARD_FACE_COLOR = '#FFFFFF'; +const DECK_COUNT = 4; // Can be changed to any number +const BASE_COLORS = [ + { primary: '#FF9900', secondary: '#FFCC00' }, // Original orange deck + { primary: '#6B8E23', secondary: '#9ACD32' }, // Olive green deck + { primary: '#4169E1', secondary: '#87CEEB' }, // Royal blue deck + { primary: '#8B008B', secondary: '#DA70D6' }, // Purple deck + { primary: '#CD853F', secondary: '#DEB887' } // Brown deck +]; + + +// Pile layout +const PILE_SPACING = CARD_WIDTH + PADDING * 4; // Space between piles +const PILE_OFFSET = 5; // Vertical offset for stacked cards + + +// Setting up the canvas +const canvas = document.getElementById('cards'); +const ctx = canvas.getContext('2d'); +canvas.width = window.innerWidth; +canvas.height = window.innerHeight; + + +/** + * Shuffles an array in place and returns a new shuffled array. + * @param {Array} array - The array to shuffle. + * @returns {Array} A new array containing the shuffled elements. + */ +const shuffle = array => { + const result = [...array]; + for (let i = result.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [result[i], result[j]] = [result[j], result[i]]; + } + return result; +}; + + +/** + * Creates a deck of cards for a given deck index. + * @param {number} deckIndex - The index of the deck being created. + * @returns {Array} An array of card objects, each containing suit, value, and deckId. + */ +const createDeck = (deckIndex) => SUITS.flatMap(suit => + VALUES.map(value => ({ + suit, + value, + deckId: deckIndex // Add deckId to track which deck a card belongs to + })) +); + +/** + * Creates multiple decks of cards based on the specified count. + * If the count exceeds the number of unique deck colors defined, + * some decks will repeat colors. + * + * @param {number} count - The number of decks to create. + * @returns {Array} An array of card objects, each containing suit, value, and deckId. + */ +const createDecks = (count) => { + if (count > BASE_COLORS.length) { + console.warn(`Only ${BASE_COLORS.length} unique deck colors are defined. Some decks will repeat colors.`); + } + return Array.from({ length: count }, (_, i) => createDeck(i)).flat(); +}; + +/** + * Creates a card object with a known position and some card data. + * @param {number} x - The x-coordinate of the card. + * @param {number} y - The y-coordinate of the card. + * @param {CardData} cardData - The data for the card, including suit and value. + * @returns {Card} A new card object. + */ +const createCard = (x, y, cardData) => Object.freeze({ + x: x + PADDING, + y: y + PADDING, + card: Object.freeze({ ...cardData }), + isFaceUp: false +}); + +/** + * Determines if a point is within a card. + * Used to determine where to hold a card when dragging. + * @param {number} x - The x-coordinate of the point. + * @param {number} y - The y-coordinate of the point. + * @param {Card} card - The card to check by card object reference. + * @returns {boolean} True if the point is within the card. + */ +const isPointInCard = (x, y, card) => + x >= card.x && x <= card.x + CARD_WIDTH && y >= card.y && y <= card.y + CARD_HEIGHT; + +const clearCanvas = () => { + ctx.fillStyle = 'beige'; + ctx.fillRect(0, 0, canvas.width, canvas.height); +}; + +const drawCardBack = card => { + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + drawRetroPattern(card); + ctx.strokeStyle = CARD_BORDER_COLOR; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); +}; + +const drawRetroPattern = card => { + const checkeredSize = 10; + const deckColors = BASE_COLORS[card.card.deckId % BASE_COLORS.length]; + + for (let i = 0; i < CARD_WIDTH; i += checkeredSize) { + for (let j = 0; j < CARD_HEIGHT; j += checkeredSize) { + ctx.fillStyle = (Math.floor(i / checkeredSize) + Math.floor(j / checkeredSize)) % 2 === 0 + ? deckColors.primary + : deckColors.secondary; + ctx.fillRect(card.x + i, card.y + j, checkeredSize, checkeredSize); + } + } +}; + +const drawCardFront = card => { + ctx.fillStyle = CARD_FACE_COLOR; + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + ctx.fillStyle = CARD_BORDER_COLOR; + ctx.font = FONT_SIZE; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + + drawCardValue(card.card.value, card.x + 12, card.y + 42, 'left'); + drawCardSuit(card.card.suit, card.x + CARD_WIDTH / 2, card.y + CARD_HEIGHT / 2 + 20); +}; + +const drawCardValue = (value, x, y, alignment) => { + ctx.textAlign = alignment; + ctx.fillStyle = CARD_BORDER_COLOR; + ctx.fillText(value, x, y); +}; + +const drawCardSuit = (suit, x, y) => { + ctx.textAlign = 'center'; + ctx.fillStyle = CARD_BORDER_COLOR; + ctx.fillText(suit, x, y); +}; + +/** + * Renders a card, determining which side to draw based on its face-up state. + * @param {Card} card - The card to render by card object reference. + */ +const renderCard = card => { + card.isFaceUp ? drawCardFront(card) : drawCardBack(card); +}; + +const renderAllCards = cards => { + clearCanvas(); + cards.forEach(renderCard); +}; + +let gameState; + +const initializeGameState = () => ({ + cards: [], + draggingCard: null, + deck: shuffle(createDecks(DECK_COUNT)), + stackPosition: { x: 0, y: 0 } +}); + +const initializeGame = () => { + try { + gameState = initializeGameState(); + + // Group cards by deck + const cardsByDeck = gameState.deck.reduce((acc, cardData) => { + const deckId = cardData.deckId; + if (!acc[deckId]) acc[deckId] = []; + acc[deckId].push(cardData); + return acc; + }, {}); + + // Calculate starting X position to center all piles + // FIXME: How can I make the deck position be dynamic? + const totalWidth = PILE_SPACING * DECK_COUNT; + const startX = (canvas.width - totalWidth) / 2; + + // Create cards for each deck in its own pile + gameState.cards = Object.entries(cardsByDeck).flatMap(([deckId, deckCards]) => { + const pileX = startX + (parseInt(deckId) * PILE_SPACING); + + return deckCards.map((cardData, indexInDeck) => + createCard( + pileX, + INITIAL_CARD_Y + (indexInDeck * PILE_OFFSET), + cardData + ) + ); + }); + + // TODO: Consider adding another level Box > Deck > Pile > Card + + clearCanvas(); + renderAllCards(gameState.cards); + setupEventListeners(); + + } catch (error) { + console.error('Failed to initialize game:', error); + alert('Failed to initialize game. Please refresh the page.'); + } +}; + +const setupEventListeners = () => { + canvas.addEventListener('mousedown', handleMouseDown); + canvas.addEventListener('contextmenu', e => e.preventDefault()); + document.addEventListener('keydown', e => { + if (e.key === 'q') handleResetGame(); + }); +}; + +const handleMouseMove = e => { + if (!gameState.draggingCard) return; + + const rect = canvas.getBoundingClientRect(); + const newX = e.clientX - rect.left - dragOffset.x; + const newY = e.clientY - rect.top - dragOffset.y; + + const updatedCard = moveCard(gameState.draggingCard, newX, newY); + gameState.cards = gameState.cards.map(card => + card === gameState.draggingCard ? updatedCard : card + ); + gameState.draggingCard = updatedCard; + + renderAllCards(gameState.cards); +}; + +const handleMouseUp = e => { + if (!gameState.draggingCard) { + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + // Was the card clicked? + const clickedCard = gameState.cards.slice().reverse().find(card => isPointInCard(x, y, card)); + if (clickedCard) { + // Move the clicked card to the top of the stack + gameState.cards = gameState.cards.filter(card => card !== clickedCard); + gameState.cards.push(clickedCard); + renderAllCards(gameState.cards); // Re-render all cards + } + } + + gameState.draggingCard = null; + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); +}; + +let dragOffset = { x: 0, y: 0 }; // To store the offset of the click position + +/** + * Finds the card that was clicked. + * @param {number} x - The x-coordinate of the click. + * @param {number} y - The y-coordinate of the click. + * @param {Card[]} cards - The list of cards to search through by card object reference. + * @returns {Card|null} The card that was clicked, or null if no card was clicked. + */ +const findClickedCard = (x, y, cards) => + cards.slice().reverse().find(card => isPointInCard(x, y, card)); + +/** + * Moves a card to the top of the stack. + * @param {Card} targetCard - The card to move to the top. + * @param {Card[]} cards - The list of cards to search through by card object reference. + * @returns {Card[]} A new array with the target card moved to the top. + */ +const moveCardToTop = (targetCard, cards) => [ + ...cards.filter(card => card !== targetCard), + targetCard +]; + +const handleMouseDown = e => { + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + if (e.button === 2) { + e.preventDefault(); + const clickedCard = findClickedCard(x, y, gameState.cards); + if (clickedCard) { + const updatedCard = toggleCardFace(clickedCard); + gameState.cards = gameState.cards.map(card => + card === clickedCard ? updatedCard : card + ); + renderAllCards(gameState.cards); + } + return; + } + + const clickedCard = findClickedCard(x, y, gameState.cards); + if (clickedCard) { + gameState.draggingCard = clickedCard; + dragOffset = { + x: x - clickedCard.x, + y: y - clickedCard.y + }; + gameState.cards = moveCardToTop(clickedCard, gameState.cards); + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + } +}; + +const handleResetGame = () => { + if (confirm("Would you like to reset the cards?")) { + resetCardsToOriginalPiles(); + } +}; + +/** + * Moves a card to a new position. + * @param {Card} card - The card to move. + * @param {number} newX - The new x-coordinate for the card. + * @param {number} newY - The new y-coordinate for the card. + * @returns {Card} A new card object with updated position. + */ +const moveCard = (card, newX, newY) => ({ + ...card, + x: newX, + y: newY +}); + +const toggleCardFace = card => ({ + ...card, + isFaceUp: !card.isFaceUp +}); + +/** + * Calculates some stats for each deck, including total and face-up counts. + * @returns {Map} A map containing the total and face-up counts for each deck. + * Useful for debugging information to the canvas. + */ +const getDeckStats = () => { + const stats = new Map(); + gameState.cards.forEach(card => { + const deckId = card.card.deckId; + const current = stats.get(deckId) || { total: 0, faceUp: 0 }; + stats.set(deckId, { + total: current.total + 1, + faceUp: current.faceUp + (card.isFaceUp ? 1 : 0) + }); + }); + return stats; +}; + +const renderDeckStats = () => { + const stats = getDeckStats(); + ctx.font = '16px "pokemon-font", monospace'; + + // Calculate the same starting X position as the piles + const totalWidth = PILE_SPACING * DECK_COUNT; + const startX = (canvas.width - totalWidth) / 2; + + stats.forEach((stat, deckId) => { + const colors = BASE_COLORS[deckId % BASE_COLORS.length]; + const pileX = startX + (deckId * PILE_SPACING); + + ctx.fillStyle = colors.primary; + ctx.textAlign = 'center'; + ctx.fillText( + `Deck ${deckId + 1}: ${stat.faceUp}/${stat.total}`, + pileX + CARD_WIDTH / 2, + INITIAL_CARD_Y - 10 + ); + }); +}; + +// FIXME: this is too complicated, and would probably work better if I had a better way of handling state. +const resetCardsToOriginalPiles = () => { + const totalWidth = PILE_SPACING * DECK_COUNT; + const startX = (canvas.width - totalWidth) / 2; + + // Group cards by deck + const cardsByDeck = gameState.cards.reduce((acc, card) => { + const deckId = card.card.deckId; + if (!acc[deckId]) acc[deckId] = []; + acc[deckId].push(card); + return acc; + }, {}); + + // Reset position for each deck + Object.entries(cardsByDeck).forEach(([deckId, deckCards]) => { + const pileX = startX + (parseInt(deckId) * PILE_SPACING); + + deckCards.forEach((card, index) => { + card.x = pileX; + card.y = INITIAL_CARD_Y + (index * PILE_OFFSET); + card.isFaceUp = false; + }); + }); + + renderAllCards(gameState.cards); +}; + +initializeGame(); + +window.addEventListener('unload', () => { + canvas.removeEventListener('mousedown', handleMouseDown); + canvas.removeEventListener('contextmenu', e => e.preventDefault()); +}); \ No newline at end of file diff --git a/html/cards/fonts/DotGothic16-Regular.ttf b/html/cards/fonts/DotGothic16-Regular.ttf new file mode 100644 index 0000000..6634bc1 --- /dev/null +++ b/html/cards/fonts/DotGothic16-Regular.ttf Binary files differdiff --git a/html/cards/fonts/OFL copy.txt b/html/cards/fonts/OFL copy.txt new file mode 100644 index 0000000..7c6649c --- /dev/null +++ b/html/cards/fonts/OFL copy.txt @@ -0,0 +1,93 @@ +Copyright 2020 The DotGothic16 Project Authors (https://github.com/fontworks-fonts/DotGothic16) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/html/cards/fonts/OFL.txt b/html/cards/fonts/OFL.txt new file mode 100644 index 0000000..70041e1 --- /dev/null +++ b/html/cards/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/html/cards/fonts/PressStart2P-Regular.ttf b/html/cards/fonts/PressStart2P-Regular.ttf new file mode 100644 index 0000000..2442aff --- /dev/null +++ b/html/cards/fonts/PressStart2P-Regular.ttf Binary files differdiff --git a/html/cards/index.html b/html/cards/index.html new file mode 100644 index 0000000..6c6c25e --- /dev/null +++ b/html/cards/index.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="description" content="One should always play fairly when one has the winning cards - Oscar Wilde"> + <title>🃏 cards 🃏</title> + <style> + @font-face { + font-family: 'pokemon-font'; + src: url('./pokemon-font/fonts/pokemon-font.ttf') format('ttf'); /* IE9 Compat Modes */ + src: url('./pokemon-font/fonts/pokemon-font.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('./pokemon-font/fonts/pokemon-font.woff2') format('woff2'), /* Super Modern Browsers */ + url('./pokemon-font/fonts/pokemon-font.woff') format('woff'), /* Pretty Modern Browsers */ + url('./pokemon-font/fonts/pokemon-font.ttf') format('truetype') /* Safari, Android, iOS */ + } + + body { + margin: 0; + padding: 0; + font-smooth: never; + -webkit-font-smoothing: none; + font-family: "pokemon-font", monospace; + font-size: 16px; + } + + canvas { + display: block; + } + </style> +</head> +<body> + <canvas id="cards"></canvas> + <script src="./cards.js"></script> +</body> +</html> diff --git a/html/cards/pokemon-font b/html/cards/pokemon-font new file mode 160000 +Subproject 81b60805150c75ebfdfcec6d8352c67e491f8a6 |