add some cards and fix some coloring
This commit is contained in:
		
							
								
								
									
										207
									
								
								src/cards.ts
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								src/cards.ts
									
									
									
									
									
								
							| @@ -1,8 +1,29 @@ | |||||||
| import { DominionCard, TYPE_TREASURE, TYPE_VICTORY } from "./types.ts"; | import { | ||||||
|  | 	DominionCard, | ||||||
|  | 	TYPE_ACTION, | ||||||
|  | 	TYPE_DURATION, | ||||||
|  | 	TYPE_NIGHT, | ||||||
|  | 	TYPE_TREASURE, | ||||||
|  | 	TYPE_VICTORY, | ||||||
|  | } from "./types.ts"; | ||||||
|  |  | ||||||
| const expansionIcon = ""; | const expansionIcon = ""; | ||||||
| const author = "Dylan"; | const author = "Dylan"; | ||||||
|  |  | ||||||
|  | const sampleCard = { | ||||||
|  | 	orientation: "card", | ||||||
|  | 	title: "Sample", | ||||||
|  | 	description: "", | ||||||
|  | 	types: [TYPE_ACTION], | ||||||
|  | 	image: "", | ||||||
|  | 	artist: "", | ||||||
|  | 	author, | ||||||
|  | 	version: "0.1", | ||||||
|  | 	cost: "$", | ||||||
|  | 	preview: "", | ||||||
|  | 	expansionIcon, | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const cards: DominionCard[] = [ | export const cards: DominionCard[] = [ | ||||||
| 	{ | 	{ | ||||||
| 		orientation: "card", | 		orientation: "card", | ||||||
| @@ -21,14 +42,194 @@ export const cards: DominionCard[] = [ | |||||||
| 	{ | 	{ | ||||||
| 		orientation: "card", | 		orientation: "card", | ||||||
| 		title: "Promising Land", | 		title: "Promising Land", | ||||||
| 		description: "Worth 1% per 3 cards you have that cost $4 or $5.", | 		description: "Worth 1% per 4 cards you have that cost $4 or $5.", | ||||||
| 		types: [TYPE_VICTORY], | 		types: [TYPE_VICTORY], | ||||||
| 		image: "", | 		image: "", | ||||||
| 		artist: "", | 		artist: "", | ||||||
| 		author, | 		author, | ||||||
| 		version: "", | 		version: "0.1", | ||||||
|  | 		cost: "$4", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Steelworker", | ||||||
|  | 		description: | ||||||
|  | 			"If it's your Action phase, +3 Cards.\n\nIf it's your Buy phase, +1 Buy, and +$1.", | ||||||
|  | 		types: [TYPE_ACTION, TYPE_TREASURE], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.2", | ||||||
|  | 		cost: "$5", | ||||||
|  | 		preview: "$?", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Shovel", | ||||||
|  | 		description: | ||||||
|  | 			"Play a Treasure card from your hand. Then trash it from play to gain a Treasure card costing up to $3 more than it.", | ||||||
|  | 		types: [TYPE_TREASURE], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$6", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "High Council", | ||||||
|  | 		description: | ||||||
|  | 			"+2 Cards\n+1 Action\n+1 Buy\n\nEach player (including you) may choose one: +1 Card, or trash a card from their hand.", | ||||||
|  | 		types: [TYPE_ACTION], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Lou + Dylan", | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$7", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Productive Village", | ||||||
|  | 		description: | ||||||
|  | 			"If it's your Action phase, +3 Actions.\n\nIf it's your Buy phase, +$1 per unused Action you have (Action, not Action card). +$1 if you have no Actions.", | ||||||
|  | 		types: [TYPE_ACTION, TYPE_TREASURE], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Dylan", | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$3", | ||||||
|  | 		preview: "$?", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Secret Society", | ||||||
|  | 		description: | ||||||
|  | 			"+1 Action\n\nIf you have at least 3 copies of Secret Society in play, trash all of them to gain any number of cards costing at least $2, whose total combined cost is at most $50.\n\n-\n\nOn your turn, this costs $3 plus $2 per Secret Society you've gained this game.", | ||||||
|  | 		types: [TYPE_ACTION], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Dylan", | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$?", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Eclipse", | ||||||
|  | 		description: | ||||||
|  | 			"+1 Card\n\nIf you have no Actions, +1 Action. If you have no Buys, +1 Buy. Return to your Action phase.", | ||||||
|  | 		types: [TYPE_NIGHT], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Dylan", | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$5", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Moonlit Scheme", | ||||||
|  | 		description: "You may play an Action card from your hand.", | ||||||
|  | 		types: [TYPE_NIGHT], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Dylan", | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$2", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Beaver", | ||||||
|  | 		description: | ||||||
|  | 			"Pay $1. If you did, gain a card costing up to the amount of $ you have.", | ||||||
|  | 		types: [TYPE_NIGHT], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author: "Dylan", | ||||||
|  | 		version: "0.1", | ||||||
| 		cost: "$3", | 		cost: "$3", | ||||||
| 		preview: "", | 		preview: "", | ||||||
| 		expansionIcon, | 		expansionIcon, | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Silk", | ||||||
|  | 		description: "Choose one: +$2, or gain a Silver.", | ||||||
|  | 		types: [TYPE_TREASURE], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$4", | ||||||
|  | 		preview: "$?", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Foundry", | ||||||
|  | 		description: | ||||||
|  | 			"Choose one: +1 Card, +1 Action and +$1; or trash a card from your hand to gain a card that costs up to $2 more than it.", | ||||||
|  | 		types: [TYPE_ACTION], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$5", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Vendor", | ||||||
|  | 		description: | ||||||
|  | 			"Choose three different options: +1 Card, +1 Action, +1 Buy, +$1, trash a card from your hand.", | ||||||
|  | 		types: [TYPE_ACTION], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.2", | ||||||
|  | 		cost: "$5", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Chateau", | ||||||
|  | 		description: | ||||||
|  | 			"1%\n\n-\n\nWhen you gain this, choose one: gain an Estate; or +1 Card, +1 Action, +1 Buy, +$1, and if it's your Buy phase, return to your Action phase.", | ||||||
|  | 		types: [TYPE_VICTORY], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.1", | ||||||
|  | 		cost: "$3", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		orientation: "card", | ||||||
|  | 		title: "Retainer", | ||||||
|  | 		description: | ||||||
|  | 			"Set aside a card from your hand (under this).\n\nAt any time during any of your turns, you may take +1 Action, and add the set aside card to your hand, discarding this from play.\n\nAt the start of each of your Buy phases, if the card is still set aside, +@1.", | ||||||
|  | 		types: [TYPE_ACTION, TYPE_DURATION], | ||||||
|  | 		image: "", | ||||||
|  | 		artist: "", | ||||||
|  | 		author, | ||||||
|  | 		version: "0.2", | ||||||
|  | 		cost: "$2", | ||||||
|  | 		preview: "", | ||||||
|  | 		expansionIcon, | ||||||
|  | 	}, | ||||||
| ]; | ]; | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								src/colorhelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/colorhelper.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import parseColor1 from "npm:parse-color"; | ||||||
|  |  | ||||||
|  | export const parseColor = (c: string): { rgb: [number, number, number] } => { | ||||||
|  | 	return parseColor1(c); | ||||||
|  | }; | ||||||
| @@ -391,7 +391,7 @@ export const parse = ( | |||||||
| 			pieces.push({ type: "break" }); | 			pieces.push({ type: "break" }); | ||||||
| 		} else if (char in symbolMap) { | 		} else if (char in symbolMap) { | ||||||
| 			const c = char as keyof typeof symbolMap; | 			const c = char as keyof typeof symbolMap; | ||||||
| 			const end = text.slice(i).match(new RegExp(`\\${c}\\w*`))![0] | 			const end = text.slice(i).match(new RegExp(`\\${c}[^ \n.,;]*`))![0] | ||||||
| 				.length; | 				.length; | ||||||
| 			const isBig = | 			const isBig = | ||||||
| 				isDescription && | 				isDescription && | ||||||
|   | |||||||
							
								
								
									
										97
									
								
								src/draw.ts
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								src/draw.ts
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  | import { parseColor } from "./colorhelper.ts"; | ||||||
| import { | import { | ||||||
| 	measureDominionText, | 	measureDominionText, | ||||||
| 	parse, | 	parse, | ||||||
| @@ -39,6 +40,10 @@ const imageList = [ | |||||||
| 		key: "card-color-2", | 		key: "card-color-2", | ||||||
| 		src: "/static/assets/CardColorTwo.png", | 		src: "/static/assets/CardColorTwo.png", | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		key: "card-color-2-night", | ||||||
|  | 		src: "/static/assets/CardColorTwoNight.png", | ||||||
|  | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		key: "card-brown", | 		key: "card-brown", | ||||||
| 		src: "/static/assets/CardBrown.png", | 		src: "/static/assets/CardBrown.png", | ||||||
| @@ -151,25 +156,74 @@ export const drawCard = ( | |||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | const _rgbCache: Record<string, { r: number; g: number; b: number }> = {}; | ||||||
|  | const getColorRgb = (c: string): { r: number; g: number; b: number } => { | ||||||
|  | 	const { rgb } = parseColor(c); | ||||||
|  | 	const [r, g, b] = rgb; | ||||||
|  | 	return { r, g, b }; | ||||||
|  | 	// if (c in _rgbCache) { | ||||||
|  | 	// 	return _rgbCache[c]!; | ||||||
|  | 	// } | ||||||
|  | 	// const canvas = document.createElement("canvas"); | ||||||
|  | 	// canvas.width = 10; | ||||||
|  | 	// canvas.height = 10; | ||||||
|  | 	// const context = canvas.getContext("2d")!; | ||||||
|  | 	// context.fillRect(0, 0, 10, 10); | ||||||
|  | 	// const data = context.getImageData(5, 5, 1, 1).data; | ||||||
|  | 	// console.log(data); | ||||||
|  | 	// const [r, g, b] = data; | ||||||
|  | 	// const rgb = { r: r!, g: g!, b: b! }; | ||||||
|  | 	// _rgbCache[c] = rgb; | ||||||
|  | 	// return rgb; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const getTextColorForBackground = (c: string): string => { | ||||||
|  | 	// return "black"; | ||||||
|  | 	const { r, g, b } = getColorRgb(c); | ||||||
|  | 	const avg = (r + g + b) / 3 / 255; | ||||||
|  | 	console.log([r, g, b], avg); | ||||||
|  | 	return avg > 0.5 ? "black" : "white"; | ||||||
|  | }; | ||||||
|  |  | ||||||
| const getColors = ( | const getColors = ( | ||||||
| 	types: DominionCardType[] | 	types: DominionCardType[] | ||||||
| ): { primary: string; secondary: string | null } => { | ): { | ||||||
|  | 	primary: string; | ||||||
|  | 	secondary: string | null; | ||||||
|  | 	description: string | null; | ||||||
|  | 	descriptionText: string; | ||||||
|  | 	titleText: string; | ||||||
|  | } => { | ||||||
|  | 	const descriptionType = | ||||||
|  | 		types.find((t) => t.color?.onConflictDescriptionOnly) ?? null; | ||||||
| 	const byPriority = [...types] | 	const byPriority = [...types] | ||||||
| 		.filter((type) => type.color) | 		.filter((type) => type.color && type !== descriptionType) | ||||||
| 		.sort((a, b) => b.color!.priority - a.color!.priority); | 		.sort((a, b) => b.color!.priority - a.color!.priority); | ||||||
| 	const priority1 = byPriority[0]!; | 	const priority1 = byPriority[0]!; | ||||||
| 	let primary = priority1.color?.value ?? "white"; | 	let primaryType: DominionCardType | null = priority1 ?? null; | ||||||
| 	let secondary = byPriority[1]?.color?.value ?? null; | 	let secondaryType = byPriority[1] ?? null; | ||||||
| 	if (priority1 === TYPE_ACTION) { | 	if (priority1 === TYPE_ACTION) { | ||||||
| 		const overriders = byPriority.filter((t) => t.color!.overridesAction); | 		const overriders = byPriority.filter((t) => t.color!.overridesAction); | ||||||
| 		if (overriders.length) { | 		if (overriders.length) { | ||||||
| 			primary = overriders[0]!.color!.value; | 			primaryType = overriders[0] ?? null; | ||||||
| 		} | 		} | ||||||
| 		if (primary === secondary) { | 		if (primaryType === secondaryType) { | ||||||
| 			secondary = byPriority[2]?.color?.value ?? null; | 			secondaryType = byPriority[2] ?? null; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return { primary, secondary }; | 	primaryType = primaryType ?? descriptionType; | ||||||
|  | 	const primary = primaryType?.color?.value ?? "white"; | ||||||
|  | 	const secondary = secondaryType?.color?.value ?? null; | ||||||
|  | 	const description = descriptionType?.color?.value ?? null; | ||||||
|  | 	const descriptionText = getTextColorForBackground(description ?? primary); | ||||||
|  | 	const titleText = getTextColorForBackground(primary); | ||||||
|  | 	return { | ||||||
|  | 		primary, | ||||||
|  | 		secondary, | ||||||
|  | 		description, | ||||||
|  | 		descriptionText, | ||||||
|  | 		titleText, | ||||||
|  | 	}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const drawStandardCard = async ( | const drawStandardCard = async ( | ||||||
| @@ -213,6 +267,17 @@ const drawStandardCard = async ( | |||||||
| 			0, | 			0, | ||||||
| 			0 | 			0 | ||||||
| 		); | 		); | ||||||
|  | 	} else if (colors.description) { | ||||||
|  | 		context.drawImage( | ||||||
|  | 			colorImage(getImage("card-color-1"), colors.description), | ||||||
|  | 			0, | ||||||
|  | 			0 | ||||||
|  | 		); | ||||||
|  | 		context.drawImage( | ||||||
|  | 			colorImage(getImage("card-color-2-night"), colors.primary), | ||||||
|  | 			0, | ||||||
|  | 			0 | ||||||
|  | 		); | ||||||
| 	} else { | 	} else { | ||||||
| 		context.drawImage( | 		context.drawImage( | ||||||
| 			colorImage(getImage("card-color-1"), colors.primary), | 			colorImage(getImage("card-color-1"), colors.primary), | ||||||
| @@ -224,6 +289,7 @@ const drawStandardCard = async ( | |||||||
| 	context.drawImage(getImage("card-gray"), 0, 0); | 	context.drawImage(getImage("card-gray"), 0, 0); | ||||||
| 	context.drawImage(colorImage(getImage("card-brown"), "#ff9911"), 0, 0); | 	context.drawImage(colorImage(getImage("card-brown"), "#ff9911"), 0, 0); | ||||||
| 	// Draw the name | 	// Draw the name | ||||||
|  | 	context.fillStyle = colors.titleText; | ||||||
| 	size = 78; | 	size = 78; | ||||||
| 	context.font = `${size}pt DominionTitle`; | 	context.font = `${size}pt DominionTitle`; | ||||||
| 	while ( | 	while ( | ||||||
| @@ -234,7 +300,16 @@ const drawStandardCard = async ( | |||||||
| 	} | 	} | ||||||
| 	await renderDominionText(context, parse(card.title), w / 2, 220); | 	await renderDominionText(context, parse(card.title), w / 2, 220); | ||||||
| 	// Draw the description | 	// Draw the description | ||||||
| 	context.font = "60pt DominionText"; | 	context.fillStyle = colors.descriptionText; | ||||||
|  | 	size = 60; | ||||||
|  | 	context.font = `${size}pt DominionText`; | ||||||
|  | 	while ( | ||||||
|  | 		(await measureDominionText(context, parse(card.description), 1000)) | ||||||
|  | 			.height > 650 | ||||||
|  | 	) { | ||||||
|  | 		size -= 1; | ||||||
|  | 		context.font = `${size}pt DominionText`; | ||||||
|  | 	} | ||||||
| 	await renderDominionText( | 	await renderDominionText( | ||||||
| 		context, | 		context, | ||||||
| 		parse(card.description, { isDescription: true }), | 		parse(card.description, { isDescription: true }), | ||||||
| @@ -243,6 +318,7 @@ const drawStandardCard = async ( | |||||||
| 		1000 | 		1000 | ||||||
| 	); | 	); | ||||||
| 	// Draw the types | 	// Draw the types | ||||||
|  | 	context.fillStyle = colors.titleText; | ||||||
| 	size = 65; | 	size = 65; | ||||||
| 	context.font = `${size}pt DominionTitle`; | 	context.font = `${size}pt DominionTitle`; | ||||||
| 	while ( | 	while ( | ||||||
| @@ -264,6 +340,7 @@ const drawStandardCard = async ( | |||||||
| 		800 | 		800 | ||||||
| 	); | 	); | ||||||
| 	// Draw the cost | 	// Draw the cost | ||||||
|  | 	context.fillStyle = colors.titleText; | ||||||
| 	context.font = "90pt DominionText"; | 	context.font = "90pt DominionText"; | ||||||
| 	const costMeasure = await measureDominionText(context, parse(card.cost)); | 	const costMeasure = await measureDominionText(context, parse(card.cost)); | ||||||
| 	await renderDominionText( | 	await renderDominionText( | ||||||
| @@ -273,6 +350,7 @@ const drawStandardCard = async ( | |||||||
| 		1940 | 		1940 | ||||||
| 	); | 	); | ||||||
| 	// Draw the preview | 	// Draw the preview | ||||||
|  | 	context.fillStyle = colors.titleText; | ||||||
| 	if (card.preview) { | 	if (card.preview) { | ||||||
| 		context.font = "90pt DominionText"; | 		context.font = "90pt DominionText"; | ||||||
| 		await renderDominionText(context, parse(card.preview), 200, 210); | 		await renderDominionText(context, parse(card.preview), 200, 210); | ||||||
| @@ -293,6 +371,7 @@ const drawStandardCard = async ( | |||||||
| 		2035 | 		2035 | ||||||
| 	); | 	); | ||||||
| 	// Draw the artist credit | 	// Draw the artist credit | ||||||
|  | 	context.fillStyle = "white"; | ||||||
| 	const artistMeasure = await measureDominionText( | 	const artistMeasure = await measureDominionText( | ||||||
| 		context, | 		context, | ||||||
| 		parse(card.artist) | 		parse(card.artist) | ||||||
|   | |||||||
| @@ -142,7 +142,7 @@ export const TYPE_NIGHT: DominionBasicCardType = { | |||||||
| 	typeType: "basic", | 	typeType: "basic", | ||||||
| 	name: "Night", | 	name: "Night", | ||||||
| 	color: { | 	color: { | ||||||
| 		value: "black", | 		value: "#485058", | ||||||
| 		priority: 6, | 		priority: 6, | ||||||
| 		onConflictDescriptionOnly: true, | 		onConflictDescriptionOnly: true, | ||||||
| 	}, | 	}, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user