add vp symbols

This commit is contained in:
Dylan Pizzo 2025-01-07 08:10:47 -08:00
parent 6213eda240
commit 3adf3bf76d
4 changed files with 94 additions and 154 deletions

View File

@ -1,13 +1,13 @@
import { useState } from "react"; import { useState } from "react";
import { sampleCard1, sampleCard2, sampleCard3 } from "../sampleData.ts"; import { sampleCards } from "../sampleData.ts";
import { Card } from "./Card.tsx"; import { Card } from "./Card.tsx";
export const App = () => { export const App = () => {
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
return <div> return <div>
<Card key={`1-${count}`} card={sampleCard1}/> {sampleCards.map((sampleCard) => {
<Card key={`2-${count}`} card={sampleCard2}/> return <Card key={`${sampleCard.title}-${count}`} card={sampleCard}/>
<Card key={`3-${count}`} card={sampleCard3}/> })}
<button onClick={() => {setCount(c => c+1)}}>Rerender (for fonts)</button> <button onClick={() => {setCount(c => c+1)}}>Rerender (for fonts)</button>
</div>; </div>;
}; };

View File

@ -5,9 +5,12 @@ export type Piece =
| { type: "text"; text: string; isBold?: boolean; isItalic?: boolean } | { type: "text"; text: string; isBold?: boolean; isItalic?: boolean }
| { type: "space" } | { type: "space" }
| { type: "break" } | { type: "break" }
| { type: "coin"; text: string } | {
| { type: "debt"; text: string } type: "symbol";
| { type: "potion"; text: string }; symbol: "coin" | "debt" | "potion" | "vp" | "vp-token";
text: string;
textColor: string;
};
type PromiseOr<T> = T | Promise<T>; type PromiseOr<T> = T | Promise<T>;
@ -124,14 +127,14 @@ const breakPiece = pieceDef({
render() {}, render() {},
}); });
const coinPiece = pieceDef({ const symbolPiece = pieceDef({
type: "coin", type: "symbol",
measure(context, _piece) { measure(context, piece) {
context.save(); context.save();
const metrics = context.measureText(" "); const metrics = context.measureText(" ");
const height = const height =
metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
const coinImage = getImage("coin"); const coinImage = getImage(piece.symbol);
context.restore(); context.restore();
return { return {
type: "content", type: "content",
@ -146,7 +149,7 @@ const coinPiece = pieceDef({
const height = measure.ascent + measure.descent; const height = measure.ascent + measure.descent;
// context.fillRect(x, y - measure.ascent, measure.width, height); // context.fillRect(x, y - measure.ascent, measure.width, height);
context.drawImage( context.drawImage(
getImage("coin"), getImage(piece.symbol),
x, x,
y - measure.ascent, y - measure.ascent,
measure.width, measure.width,
@ -158,103 +161,14 @@ const coinPiece = pieceDef({
fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2; fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2;
const font = stringifyFont(fontInfo); const font = stringifyFont(fontInfo);
context.font = font; context.font = font;
context.fillStyle = "black"; context.fillStyle = piece.textColor;
context.textAlign = "center"; context.textAlign = "center";
context.fillText(piece.text, x + measure.width / 2, y); context.fillText(piece.text, x + measure.width / 2, y);
context.restore(); context.restore();
}, },
}); });
const debtPiece = pieceDef({ const pieceDefs = [textPiece, spacePiece, breakPiece, symbolPiece];
type: "debt",
measure(context, _piece) {
context.save();
const metrics = context.measureText(" ");
const height =
metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
const coinImage = getImage("debt");
context.restore();
return {
type: "content",
width: coinImage.width * (height / coinImage.height),
ascent: metrics.fontBoundingBoxAscent,
descent: metrics.fontBoundingBoxDescent,
};
},
render(context, piece, x, y, measure) {
context.save();
// context.fillStyle = "yellow";
const height = measure.ascent + measure.descent;
// context.fillRect(x, y - measure.ascent, measure.width, height);
context.drawImage(
getImage("debt"),
x,
y - measure.ascent,
measure.width,
height
);
const fontInfo = parseFont(context.font);
fontInfo.family = ["DominionSpecial"];
fontInfo.weight = "bold";
fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2;
const font = stringifyFont(fontInfo);
context.font = font;
context.fillStyle = "white";
context.textAlign = "center";
context.fillText(piece.text, x + measure.width / 2, y);
context.restore();
},
});
const potionPiece = pieceDef({
type: "potion",
measure(context, _piece) {
context.save();
const metrics = context.measureText(" ");
const height =
metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
const coinImage = getImage("potion");
context.restore();
return {
type: "content",
width: coinImage.width * (height / coinImage.height),
ascent: metrics.fontBoundingBoxAscent,
descent: metrics.fontBoundingBoxDescent,
};
},
render(context, piece, x, y, measure) {
context.save();
// context.fillStyle = "yellow";
const height = measure.ascent + measure.descent;
// context.fillRect(x, y - measure.ascent, measure.width, height);
context.drawImage(
getImage("potion"),
x,
y - measure.ascent,
measure.width,
height
);
const fontInfo = parseFont(context.font);
fontInfo.family = ["DominionSpecial"];
fontInfo.weight = "bold";
fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2;
const font = stringifyFont(fontInfo);
context.font = font;
context.fillStyle = "white";
context.textAlign = "center";
context.fillText(piece.text, x + measure.width / 2, y);
context.restore();
},
});
const pieceDefs = [
textPiece,
spacePiece,
breakPiece,
coinPiece,
debtPiece,
potionPiece,
];
const tools: PieceTools = {} as any; const tools: PieceTools = {} as any;
@ -406,23 +320,28 @@ export const renderDominionText = async (
export const parse = (text: string): Piece[] => { export const parse = (text: string): Piece[] => {
const pieces: Piece[] = []; const pieces: Piece[] = [];
const symbolMap = {
"$": { symbol: "coin", textColor: "black" },
"@": { symbol: "debt", textColor: "white" },
"^": { symbol: "potion", textColor: "white" },
"%": { symbol: "vp", textColor: "white" },
"#": { symbol: "vp-token", textColor: "black" },
} as const;
for (let i = 0; i < text.length; i++) { for (let i = 0; i < text.length; i++) {
const char = text[i]; const char = text[i];
if (char === " ") { if (char === " ") {
pieces.push({ type: "space" }); pieces.push({ type: "space" });
} else if (char === "\n") { } else if (char === "\n") {
pieces.push({ type: "break" }); pieces.push({ type: "break" });
} else if (char === "$") { } else if (char && char in symbolMap) {
const end = text.slice(i).match(/\$\d*/)![0].length; const c = char as keyof typeof symbolMap;
pieces.push({ type: "coin", text: text.slice(i + 1, i + end) }); const end = text.slice(i).match(new RegExp(`\\${c}\\w*`))![0]
i += end - 1; .length;
} else if (char === "@") { pieces.push({
const end = text.slice(i).match(/@\d*/)![0].length; type: "symbol",
pieces.push({ type: "debt", text: text.slice(i + 1, i + end) }); ...symbolMap[c],
i += end - 1; text: text.slice(i + 1, i + end),
} else if (char === "^") { });
const end = text.slice(i).match(/\^\d*/)![0].length;
pieces.push({ type: "potion", text: text.slice(i + 1, i + end) });
i += end - 1; i += end - 1;
} else if (char === "+") { } else if (char === "+") {
const match = text.slice(i).match(/\+\d* \w+/); const match = text.slice(i).match(/\+\d* \w+/);

View File

@ -59,6 +59,14 @@ const imageList = [
key: "potion", key: "potion",
src: "/static/assets/Potion.png", src: "/static/assets/Potion.png",
}, },
{
key: "vp",
src: "/static/assets/VP.png",
},
{
key: "vp-token",
src: "/static/assets/VP-Token.png",
},
]; ];
export const loadImages = async () => { export const loadImages = async () => {

View File

@ -3,45 +3,58 @@ import {
TYPE_ACTION, TYPE_ACTION,
TYPE_REACTION, TYPE_REACTION,
TYPE_TREASURE, TYPE_TREASURE,
TYPE_VICTORY,
} from "./types.ts"; } from "./types.ts";
export const sampleCard1: DominionCard = { export const sampleCards: DominionCard[] = [
orientation: "card", {
title: "Title", orientation: "card",
description: title: "Title",
"+1 Action\n\nReveal the top card of your deck. If it's an Action card, +1 Action.", description:
types: [TYPE_ACTION, TYPE_REACTION], "+1 Action\n\nReveal the top card of your deck. If it's an Action card, +1 Action. If it has ^ in its cost, +1 Card.",
image: "https://wiki.dominionstrategy.com/images/7/76/AdventurerArt.jpg", types: [TYPE_ACTION, TYPE_REACTION],
artist: "Dall-E", image: "https://wiki.dominionstrategy.com/images/7/76/AdventurerArt.jpg",
author: "John Doe", artist: "Dall-E",
version: "", author: "John Doe",
cost: "@8", version: "",
preview: "", cost: "@8",
}; preview: "",
},
export const sampleCard2: DominionCard = { {
orientation: "card", orientation: "card",
title: "Market", title: "Market",
description: "+1 Card\n+1 Action\n+1 Buy\n+$1", description: "+1 Card\n+1 Action\n+1 Buy\n+$1",
types: [TYPE_ACTION], types: [TYPE_ACTION],
image: "", image: "",
artist: "Leonardo DaVinci", artist: "Leonardo DaVinci",
author: "Jane Smith", author: "Jane Smith",
version: "", version: "",
cost: "$4", cost: "$4",
preview: "", preview: "",
}; },
{
export const sampleCard3: DominionCard = { orientation: "card",
orientation: "card", title: "Flask",
title: "Flask", description:
description: "+2 Cards\n\nAt the start of your Clean-up phase, you may put a card from your hand onto your deck.",
"+2 Cards\n\nAt the start of your Clean-up phase, you may put a card from your hand onto your deck.", types: [TYPE_TREASURE],
types: [TYPE_TREASURE], image: "",
image: "", artist: "",
artist: "", author: "",
author: "", version: "",
version: "", cost: "$6",
cost: "$6", preview: "",
preview: "", },
}; {
orientation: "card",
title: "Flask",
description: "+1 #\n\n-\n\n2 %",
types: [TYPE_VICTORY],
image: "",
artist: "",
author: "",
version: "",
cost: "$6",
preview: "",
},
];