all the things

This commit is contained in:
Emma 2023-07-07 17:40:59 -06:00
commit bd35804bc4
93 changed files with 2693 additions and 0 deletions

25
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#af666c",
"activityBar.background": "#af666c",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#2f542c",
"activityBarBadge.foreground": "#e7e7e7",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#af666c",
"statusBar.background": "#944e53",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#af666c",
"statusBarItem.remoteBackground": "#944e53",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#944e53",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#944e5399",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.remoteColor": "#de088c",
"deno.enable": true,
"deno.unstable": true,
"peacock.color": "#944e53"
}

14
attackDefs.ts Normal file
View File

@ -0,0 +1,14 @@
export const attackRanges = {
airToAir: {
min: 0,
max: 2
},
airToGround: {
min: 0,
max: 2
},
groundToAir: {
min: 0,
max: 2
}
}

BIN
bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

1
camelToSpace.ts Normal file
View File

@ -0,0 +1 @@
export const camelToSpace = (value: string) => value.replace(/[A-Z0-9]/g, e => ` ${e.toLowerCase()}`);

BIN
card-images/series1-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-21.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-22.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-23.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-25.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-26.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-27.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-28.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-29.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-30.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-31.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-33.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-34.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-35.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-37.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-38.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-39.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-41.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-42.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-43.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-44.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-45.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-46.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-47.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-49.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-50.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-51.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-52.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-53.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-54.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-55.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-56.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-57.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-58.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-59.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
card-images/series1-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

1
cardGen/consts.ts Normal file
View File

@ -0,0 +1 @@
export const textSmall = '24px'

62
cardGen/rows.ts Normal file
View File

@ -0,0 +1,62 @@
import { CanvasRenderingContext2D } from "https://deno.land/x/skia_canvas@0.4.1/mod.ts";
import { styleTypeAccessor, TextStyle } from "./textStyle.ts";
import { wordWrap } from "./wordWrap.ts";
type line = { value: string, style: TextStyle, type: styleTypeAccessor, newLine?: boolean, combine?: boolean }
export const flexRow = (ctx: CanvasRenderingContext2D, sections: string[], maxWidth: number) => {
const totalWidth = sections.map(s => ctx.measureText(s).width).reduce((a, b) => a + b, 0);
const gap = (maxWidth - totalWidth) / (sections.length - 1);
ctx.save()
for (const section of sections) {
ctx.fillText(section, 0, 0);
ctx.translate(ctx.measureText(section).width + gap, 0);
}
ctx.restore();
}
export const inlineRow = (ctx: CanvasRenderingContext2D, sections: line[], maxWidth: number) => {
const lines: line[] = [];
for (const [i, section] of sections.entries()) {
if (!section.newLine) {
const last = lines[i - 1];
if (last) {
ctx.font = last.style[last.type];
const lastWidth = ctx.measureText(last.value).width;
ctx.font = section.style[section.type];
lines.push(...wordWrap(ctx, section.value, maxWidth, maxWidth - lastWidth).map((e, i): line => ({
...section,
value: e,
combine: i === 0
})));
continue;
}
}
ctx.font = section.style[section.type];
lines.push(...wordWrap(ctx, section.value, maxWidth).map((e): line => ({
...section,
value: e
})));
}
return lines;
}
export const drawRow = (ctx: CanvasRenderingContext2D, lines: line[]) => {
for (const [i, line] of lines.entries()) {
const last = lines[i - 1];
if (line.combine && last) {
ctx.save();
ctx.font = last.style[last.type];
ctx.translate(ctx.measureText(last.value).width, 0);
ctx.font = line.style[line.type];
ctx.fillText(line.value, 0, 0);
ctx.restore();
} else {
ctx.translate(0, line.style.lineHeight);
ctx.font = line.style[line.type];
ctx.fillText(line.value, 0, 0);
}
}
}

22
cardGen/textStyle.ts Normal file
View File

@ -0,0 +1,22 @@
export type styleTypeAccessor = 'default' | 'bold' | 'italic' | 'boldItalic';
export class TextStyle {
fontSize: number;
lineHeight: number;
constructor(fontSize: number, lineHeight?: number) {
this.fontSize = fontSize;
this.lineHeight = lineHeight || fontSize;
}
get default() {
return `${this.fontSize}px Chakra Petch`
}
get bold() {
return `bold ${this.fontSize}px Chakra Petch`
}
get italic() {
return `italic ${this.fontSize}px Chakra Petch`
}
get boldItalic() {
return `italic bold ${this.fontSize}px Chakra Petch`
}
}

24
cardGen/wordWrap.ts Normal file
View File

@ -0,0 +1,24 @@
import { CanvasRenderingContext2D } from "https://deno.land/x/skia_canvas@0.4.1/mod.ts";
export const wordWrap = (ctx: CanvasRenderingContext2D, text: string, maxWidth: number, lineWidth?: number) => {
const words = text.split(' ');
let testLine = '';
const lines: string[] = [];
for (const word of words) {
const previous = testLine;
testLine += ` ${word}`;
testLine = testLine.replace(/^\s/, '');
const { width } = ctx.measureText(testLine);
const calcedWidth = lineWidth && lines.length === 0 ? lineWidth : maxWidth;
if (width > calcedWidth) {
lines.push(previous);
testLine = word;
}
}
if (testLine) lines.push(testLine);
return lines;
}

BIN
fonts/ChakraPetch-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
fonts/ChakraPetch-Light.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
fonts/Rajdhani-Bold.ttf Normal file

Binary file not shown.

BIN
fonts/Rajdhani-Light.ttf Normal file

Binary file not shown.

BIN
fonts/Rajdhani-Medium.ttf Normal file

Binary file not shown.

BIN
fonts/Rajdhani-Regular.ttf Normal file

Binary file not shown.

BIN
fonts/Rajdhani-SemiBold.ttf Normal file

Binary file not shown.

176
generateCardImages.ts Normal file
View File

@ -0,0 +1,176 @@
import { createCanvas, Fonts, Image } from "https://deno.land/x/skia_canvas@0.4.1/mod.ts";
import { drawRow, flexRow, inlineRow } from "./cardGen/rows.ts";
import { TextStyle } from "./cardGen/textStyle.ts";
import { wordWrap } from "./cardGen/wordWrap.ts";
import { card } from './types.ts';
import { camelToSpace } from './camelToSpace.ts';
const bg = Image.loadSync('./bg.jpg');
Fonts.registerDir('./fonts');
// Fonts.setAlias('Petch', 'Chakra Petch')
const xsText = new TextStyle(32, 36);
const mdText = new TextStyle(40, 46);
const smText = new TextStyle(34, 40);
const lgText = new TextStyle(60, 66)
const width = Math.floor(2.74 * 300);
const height = Math.floor(3.74 * 300);
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
const cutAreaWidth = 750;
const cutAreaHeight = 1050;
const _cutArea = {
wStart: (canvas.width - cutAreaWidth) / 2,
wEnd: canvas.width - ((canvas.width - cutAreaWidth) / 2),
hStart: (canvas.height - cutAreaHeight) / 2,
hEnd: canvas.height - ((canvas.height - cutAreaHeight) / 2),
}
const safeAreaWidth = 690;
const safeAreaHeight = 990;
const safeArea = {
wStart: (canvas.width - safeAreaWidth) / 2,
wEnd: canvas.width - ((canvas.width - safeAreaWidth) / 2),
hStart: (canvas.height - safeAreaHeight) / 2,
hEnd: canvas.height - ((canvas.height - safeAreaHeight) / 2),
}
const cards: card[] = JSON.parse(Deno.readTextFileSync('./series1.json'));
// const card = cards[12];
// Safe Area
// ctx.strokeStyle = 'green'
// ctx.beginPath()
// ctx.roundRect(safeArea.wStart, safeArea.hStart, safeAreaWidth, safeAreaHeight, 24)
// ctx.stroke()
// Cut Area
// ctx.strokeStyle = 'red'
// ctx.beginPath()
// ctx.roundRect(cutArea.wStart, cutArea.hStart, cutAreaWidth, cutAreaHeight, 40)
// ctx.stroke()
for (const [i, card] of cards.entries()) {
ctx.drawImage(bg, 0, 0)
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.textBaseline = "top"
ctx.fillStyle = 'black'
ctx.translate(safeArea.wStart, safeArea.hStart)
ctx.font = lgText.bold;
ctx.fillText(card.type.toLocaleUpperCase(), 0, 0)
ctx.translate(0, lgText.lineHeight + 40);
const blurbs = {
bad: `Bad luck! You must take one of the following penalties`,
good: `Select either one maneuver or one attack to make. If you can't complete any action from this card, you must take one penalty listed. If there are no penalties listed, discard this card.`
}
const blurb = card.attacks.length || card.maneuvers.length ? blurbs.good : blurbs.bad;
ctx.font = xsText.italic
const blurbLines = wordWrap(ctx, blurb, safeAreaWidth);
for (const line of blurbLines) {
ctx.fillText(line, 0, 0);
ctx.translate(0, smText.lineHeight);
}
ctx.font = mdText.default
if (card.attacks.length) {
ctx.translate(0, -20)
const attackLines = inlineRow(
ctx,
[
{
value: 'Attacks: ',
style: mdText,
type: 'bold',
},
{
value: card.attacks.map(a => camelToSpace(a)).join(', '),
style: mdText,
type: 'default'
}
],
safeAreaWidth
);
drawRow(ctx, attackLines);
}
if (card.maneuvers.length) {
ctx.translate(0, 20)
const maneuverLines = inlineRow(
ctx,
[
{
value: 'Maneuvers: ',
style: mdText,
type: 'bold',
},
{
value: card.maneuvers.map(m => camelToSpace(m.name)).join(', '),
style: mdText,
type: 'default'
}
],
safeAreaWidth
);
drawRow(ctx, maneuverLines);
}
if (card.penalties.length) {
ctx.translate(0, 80);
ctx.strokeStyle = 'purple';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(safeAreaWidth, 0);
ctx.stroke();
ctx.translate(0, -20);
const penaltyLines = inlineRow(
ctx,
[
{
value: card.penalties.length === 1 ? 'Penalty: ' : 'Penalties: ',
style: mdText,
type: 'bold',
},
{
value: card.penalties.join(', '),
style: mdText,
type: 'default'
}
],
safeAreaWidth
);
drawRow(ctx, penaltyLines);
}
ctx.restore();
ctx.fillStyle = 'grey'
ctx.font = xsText.default;
ctx.save();
ctx.translate(safeArea.wStart, safeArea.hEnd);
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, -xsText.lineHeight);
ctx.lineTo(safeAreaWidth, -xsText.lineHeight);
ctx.stroke();
ctx.textAlign = 'center';
ctx.fillText('Nightfall: Blood Red Skies', safeAreaWidth / 2, -xsText.lineHeight - 12);
ctx.textAlign = 'left'
flexRow(ctx, ['Series 1', `${i + 1}/60`, `© ${new Date().getFullYear()} Cyborg Grizzly Games`], safeAreaWidth);
ctx.restore();
try {
canvas.save(`./card-images/series1-${i + 1}.png`, "png")
} catch (error) {
console.log(error);
console.log(i);
}
// canvas.save(`./testImg.png`, "png")
}

94
generateHtml.ts Normal file
View File

@ -0,0 +1,94 @@
import { camelToSpace } from "./camelToSpace.ts";
import { card } from "./types.ts";
const series = 1;
const template = (cards: string[]) => `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blood Red Skies Cards Series ${series}</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
fontFamily: {
sans: [
'Chakra Petch'
]
},
extend: {
aspectRatio: {
card: '2.5 / 3.5'
},
container: {
center: true
},
colors: {
gunmetal: '#0a2e36',
green: {
10: '#09a129',
100: '#036d19'
},
violet: {
10: '#8a7090',
50: '#8d3b72'
}
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer base {
* {
@apply dark:text-white text-black
}
hr {
@apply !border-violet-10
}
}
</style>
<style>
@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Rajdhani:wght@300;400;500;600;700&display=swap');
</style>
</head>
<body class="dark:bg-gunmetal">
<div class="container p-8 grid grid-cols-4 gap-8 bg-purple">
${cards.join('\n')}
</div>
</body>
</html>
`
const decoder = new TextDecoder();
const cards: card[] = JSON.parse(decoder.decode(Deno.readFileSync(`./series${series}.json`)));
const html = template(cards.map((c, i) => `
<div class="rounded-lg aspect-card p-8 shadow-lg flex flex-wrap dark:bg-green-700">
<div class="basis-full">
<p class="mb-4 font-extrabold uppercase text-xl">${c.type}</p>
${c.attacks.length || c.maneuvers.length ?
`<p class="mb-8 text-sm">Select either one maneuver or one attack to make. If you can't complete one action from this card, you must take one penalty listed. If there are no penalties listed, discard this card.</p>` :
`<p class="mb-8 text-sm">Bad luck! You must take one of the following penalties</p>`
}
${c.maneuvers.length > 0 ? `<p class="capitalize my-8"><span class="font-bold">Maneuvers</span>: ${c.maneuvers.map(a => camelToSpace(a.name)).join(', ')}</p>` : ''}
${c.attacks.length > 0 ? `<p class="capitalize my-8"><span class="font-bold">Attacks</span>: ${c.attacks.map(a => camelToSpace(a)).join(', ')}</p>` : ''}
${c.penalties.length > 0 ? `<hr/><p class="capitalize my-8"><span class="font-bold">Penalties</span>: ${c.penalties.join(', ')}</p>` : ''}
</div>
<div class="text-xs self-end flex justify-between w-full text-white/50">
<p class="dark:text-white/50 text-black/50">Series ${series}</p>
<p class="dark:text-white/50 text-black/50">${i + 1}/${cards.length}</p>
<p class="dark:text-white/50 text-black/50">&copy; ${new Date().getFullYear()} Cyborg Grizzly Games</p>
</div>
</div>
`))
const encoder = new TextEncoder();
Deno.writeFile(`./series${series}.html`, encoder.encode(html));

15
index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blood Red Skies Cards</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="container grid grid-cols-4">
</div>
</body>
</html>

67
index.ts Normal file
View File

@ -0,0 +1,67 @@
import { attackRanges } from "./attackDefs.ts";
import { move, moves } from "./moveDefs.ts";
import { penaltyDefs } from "./penaltyDefs.ts";
import { card } from "./types.ts";
const series = Deno.args.find(a => a.includes('series'))?.split('=')[1];
console.log(`Generating cards for series ${series}`);
const cardCount = 60;
const cards: card[] = [];
while (cards.length < cardCount) {
const maneuvers: move[] = [];
const maneuverCount = Math.floor(Math.random() * 3);
while (maneuvers.length < maneuverCount) {
const move = moves[Math.floor(Math.random() * moves.length)];
if (!maneuvers.includes(move)) maneuvers.push(move);
}
const attacks: string[] = [];
for (const [name, attack] of Object.entries(attackRanges)) {
const rand = Math.random() * 10;
if (rand < attack.max) attacks.push(name);
}
const penalties = penaltyDefs.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value).splice(0, Math.min(3, Math.floor(Math.random() * (penaltyDefs.length + 1))))
.sort((a,b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
const card: card = {
maneuvers,
attacks,
penalties,
type: 'action'
};
if (card.penalties.length === 0 && card.attacks.includes('groundToAir')) card.penalties.push('Land Battle');
// TODO: optimize this. Cards should be stored in a map with the key being the stringification of a normalized concatenation of all properties
for (const existing of cards) {
const everyAttack = existing.attacks.every(a => card.attacks.includes(a)) && card.attacks.every(a => existing.attacks.includes(a))
const everyManeuver = existing.maneuvers.every(a => card.maneuvers.find(e => e.name === a.name)) && card.maneuvers.every(a => existing.maneuvers.find(e => e.name === a.name));
const everyPenalty = existing.penalties.every(a => card.penalties.includes(a)) && card.penalties.every(a => existing.penalties.includes(a))
if (everyAttack && everyManeuver && everyPenalty) {
card.needsEffect = true;
break;
}
}
(card.maneuvers.length || card.attacks.length || card.penalties.length) && cards.push(card);
}
const encoder = new TextEncoder();
Deno.writeFile(`./series${series}.json`, encoder.encode(JSON.stringify(cards, null, 2)));
console.log('Done');

46
moveDefs.ts Normal file
View File

@ -0,0 +1,46 @@
export interface move {
complexity: 'simple' | 'complex';
length?: number;
facing?: number;
horizontalOffset?: number;
evasiveness?: number;
name: string
}
export const moves: move[] = [
{
name: 'immelmann',
complexity: 'complex',
length: 10
},
{
name: 'bankedTurn',
complexity: 'simple',
length: 6
},
{
name: 'barrelRoll',
complexity: 'complex',
length: 8
},
{
name: 'straight',
complexity: 'simple',
length: 4
},
{
name: 'tightBank',
complexity: 'complex',
length: 4
},
{
name: 'turn',
complexity: 'simple',
length: 4
},
{
name: 'sBend',
complexity: 'simple',
length: 4
},
]

5
penaltyDefs.ts Normal file
View File

@ -0,0 +1,5 @@
export const penaltyDefs = [
'Stall',
'Damage',
'Ground Battle'
]

1198
series1.html Normal file

File diff suppressed because it is too large Load Diff

934
series1.json Normal file
View File

@ -0,0 +1,934 @@
[
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
}
],
"attacks": [
"airToAir"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
}
],
"attacks": [],
"penalties": [
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
}
],
"attacks": [],
"penalties": [
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "turn",
"complexity": "simple",
"length": 4
}
],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
},
{
"name": "straight",
"complexity": "simple",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [
"airToAir",
"airToGround"
],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "sBend",
"complexity": "simple",
"length": 4
},
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"airToGround",
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
},
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [
"airToAir"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
},
{
"name": "turn",
"complexity": "simple",
"length": 4
}
],
"attacks": [
"airToGround",
"groundToAir"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [],
"attacks": [
"airToAir",
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
},
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
}
],
"attacks": [
"airToAir"
],
"penalties": [
"Damage"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "straight",
"complexity": "simple",
"length": 4
}
],
"attacks": [],
"penalties": [
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"airToAir"
],
"penalties": [
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"airToAir"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"airToAir"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [
"airToAir"
],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "straight",
"complexity": "simple",
"length": 4
},
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "turn",
"complexity": "simple",
"length": 4
}
],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
},
{
"name": "sBend",
"complexity": "simple",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"groundToAir"
],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"airToGround"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "sBend",
"complexity": "simple",
"length": 4
}
],
"attacks": [],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
},
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "tightBank",
"complexity": "complex",
"length": 4
},
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "sBend",
"complexity": "simple",
"length": 4
},
{
"name": "bankedTurn",
"complexity": "simple",
"length": 6
}
],
"attacks": [
"airToAir"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [],
"attacks": [
"airToAir"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [
"airToAir"
],
"penalties": [],
"type": "action"
},
{
"maneuvers": [],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action",
"needsEffect": true
},
{
"maneuvers": [
{
"name": "immelmann",
"complexity": "complex",
"length": 10
},
{
"name": "barrelRoll",
"complexity": "complex",
"length": 8
}
],
"attacks": [
"groundToAir"
],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "sBend",
"complexity": "simple",
"length": 4
},
{
"name": "immelmann",
"complexity": "complex",
"length": 10
}
],
"attacks": [
"airToGround"
],
"penalties": [
"Damage",
"Land Battle"
],
"type": "action"
},
{
"maneuvers": [
{
"name": "sBend",
"complexity": "simple",
"length": 4
},
{
"name": "tightBank",
"complexity": "complex",
"length": 4
}
],
"attacks": [],
"penalties": [
"Damage",
"Land Battle",
"Stall"
],
"type": "action"
}
]

BIN
testImg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

9
types.ts Normal file
View File

@ -0,0 +1,9 @@
import { move } from "./moveDefs.ts";
export interface card {
maneuvers: move[];
attacks: string[];
penalties: string[];
type: 'action' | 'win condition' | 'wild card' | 'sudden death';
needsEffect?: boolean;
}