diff --git a/src/components/widgets/ubg-cards.js b/src/components/widgets/ubg-cards.js
index 74bc59084..2a2d7f9f0 100644
--- a/src/components/widgets/ubg-cards.js
+++ b/src/components/widgets/ubg-cards.js
@@ -16,18 +16,85 @@ import Card from 'demos/stefan/untitled-board-game/ubg-card.js';
const POKER_CARD_SIZE_INCHES = lively.pt(2.5, 3.5);
const POKER_CARD_SIZE_MM = POKER_CARD_SIZE_INCHES.scaleBy(25.4);
-import BeaufortforLOLJaBold from 'https://lively-kernel.org/lively4/ubg-assets/fonts/runeterra/fonts/BeaufortforLOLJa-Bold-normal.js'
-import BeaufortforLOLJaRegular from 'https://lively-kernel.org/lively4/ubg-assets/fonts/runeterra/fonts/BeaufortforLOLJa-Regular-normal.js'
-import Univers59UltraCondensed from 'https://lively-kernel.org/lively4/ubg-assets/fonts/runeterra/fonts/Univers 59 Ultra Condensed-normal.js'
-import univers_55 from 'https://lively-kernel.org/lively4/ubg-assets/fonts/runeterra/fonts/univers_55-normal.js'
+class FontCache {
+
+ constructor() {
+ this.fonts = {};
+ }
+
+ async getFile(path) {
+ if (this.fonts[path]) {
+ // lively.notify('cache hit')
+ } else {
+ // lively.notify('cache miss')
+ this.fonts[path] = this.getBase64Font(path)
+ }
+
+ return this.fonts[path];
+ }
+
+ async getBase64Font(url) {
+ async function blobToBase64String(blob) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ // Extract the Base64 encoded string
+ const base64String = reader.result.split(',')[1];
+ resolve(base64String);
+ };
+ reader.readAsDataURL(blob);
+ })
+ }
+
+ const response = await fetch(url)
+ if (!response.ok) {
+ throw new Error('Network response was not ok.');
+ }
+ const blob = await response.blob();
+ return blobToBase64String(blob)
+ }
+
+ async getRuneterraFont(fileName) {
+ const RUNETERRA_FONT_FOLDER = 'https://lively-kernel.org/lively4/ubg-assets/fonts/runeterra/fonts/';
+ return this.getBase64Font(RUNETERRA_FONT_FOLDER + fileName)
+ }
+
+ async BASE64_BeaufortforLOLJaBold() {
+ return this.getRuneterraFont('BeaufortforLOLJa-Bold.ttf')
+ }
+
+ async BASE64_BeaufortforLOLJaRegular() {
+ return this.getRuneterraFont('BeaufortforLOLJa-Regular.ttf')
+ }
+
+ async BASE64_Univers59UltraCondensed() {
+ return this.getRuneterraFont('Univers 59 Ultra Condensed.ttf')
+ }
+
+ async BASE64_Univers_55() {
+ return this.getRuneterraFont('univers_55.ttf')
+ }
+
+}
+
+if (globalThis.__ubg_font_cache__) {
+ globalThis.__ubg_font_cache__.migrateTo(FontCache);
+} else {
+ globalThis.__ubg_font_cache__ = new FontCache();
+}
+
+const BASE64_BeaufortforLOLJaBold = await globalThis.__ubg_font_cache__.BASE64_BeaufortforLOLJaBold()
+const BASE64_BeaufortforLOLJaRegular = await globalThis.__ubg_font_cache__.BASE64_BeaufortforLOLJaRegular()
+const BASE64_Univers59UltraCondensed = await globalThis.__ubg_font_cache__.BASE64_Univers59UltraCondensed()
+const BASE64_Univers_55 = await globalThis.__ubg_font_cache__.BASE64_Univers_55()
const FONT_NAME_BEAUFORT_FOR_LOL_BOLD = 'BeaufortforLOLJa-Bold'
const FONT_NAME_BEAUFORT_FOR_LOL_REGULAR = 'BeaufortforLOLJa-Regular'
-const FONT_NAME_UNIVERS_59 = 'Univers 59 Ultra Condensed'
+const FONT_NAME_UNIVERS_59 = 'Univers 59 Ultra Condensed' // #BROKEN?? #TODO
const FONT_NAME_UNIVERS_55 = 'univers_55'
-// Card group name (ELITE, SPIDER, YETI, etc.) -- Univers 59
-const FONT_NAME_CARD_TYPE = FONT_NAME_UNIVERS_59
+// Card group name (ELITE, SPIDER, YETI, etc.) -- Univers 59 // #BROKEN?? #TODO
+const FONT_NAME_CARD_TYPE = FONT_NAME_UNIVERS_55
// Card name, card cost, card stats -- Beaufort for LOL Bold
const FONT_NAME_CARD_NAME = FONT_NAME_BEAUFORT_FOR_LOL_BOLD
@@ -314,6 +381,51 @@ const hedronSVG = do {
document.body.insertAdjacentHTML("afterbegin", hedronSVG.outerHTML)
}
+const upgradeSVG = do {
+ function point(pt) {
+ return `${pt.x} ${pt.y}`;
+ }
+
+ const topB = lively.pt(11.5 / 2.3, 14.401 / 2.3);
+ const topL = topB.addXY(-11.5 / 2.3, -14.758 / 2.3);
+ const topT = topL.addXY(11.5 / 2.3, -9.66 / 2.3);
+ const topR = topT.addXY(11.5 / 2.3, 9.66 / 2.3);
+ const topB2 = topR.addXY(-11.5 / 2.3, 4.758 / 2.3);
+ const topLeftData = `M${point(topB)} L ${point(topL)} ${point(topT)} z`;
+ const topRightData = `M${point(topB)} L ${point(topT)} ${point(topR)} z`;
+
+ const bottomB = lively.pt(11.5 / 2.3, 16.036 / 2.3);
+ const bottomL = bottomB.addXY(-11.5 / 2.3, -5.050 / 2.3);
+ const bottomT = bottomL.addXY(11.5 / 2.3, 12.030 / 2.3);
+ const bottomR = bottomT.addXY(11.5 / 2.3, -12.030 / 2.3);
+ const bottomB2 = bottomR.addXY(-11.5 / 2.3, 5.050 / 2.3);
+ const bottomLeftData = `M${point(bottomB)} L ${point(bottomL)} ${point(bottomT)} z`;
+ const bottomRightData = `M${point(bottomB)} L ${point(bottomT)} ${point(bottomR)} ${point(bottomB2)} z`;
+
+ const greenHedron = true;
+ ;
+};
+
+{
+ const hedronTemp = document.getElementById('hedron2')
+ if (hedronTemp) {
+ hedronTemp.remove()
+ }
+ document.body.insertAdjacentHTML("afterbegin", upgradeSVG.outerHTML)
+}
+
class FileCache {
@@ -469,7 +581,7 @@ ${SVG.elementSymbol(others[2], lively.pt(12.5, 8.5), 1.5)}`, lively.rect(0, 0, 1
return `${text}`
}
- return printedRules.replace(/\bremind(?:er)?(\w+(?:\-(\w|\(|\))+)*)\b/gmi, (match, myMatch, offset, string, groups) => {
+ return printedRules.replace(/\bremind(?:er)?(\w+(?:\-(\w|\(|\))*)*)\b/gmi, (match, myMatch, offset, string, groups) => {
const keywords = {
actionquest: () => {
return 'You may play this when you perform the action.'
@@ -539,14 +651,21 @@ ${SVG.elementSymbol(others[2], lively.pt(12.5, 8.5), 1.5)}`, lively.rect(0, 0, 1
return 'If you fulfill its condition (track with []), as a free action you may trash this to create an Achievement Token.'
},
- cycle: (...args) => {
- return 'To cycle a card, trash it to play a card of equal or lower cost.'
+ cycle: (cost) => {
+ if (cost) {
+ return `To cycle (${cost}), pay (${cost}) and trash the card to play a card of equal or lower cost.`
+ }
+ return `To cycle, trash the card to play a card of equal or lower cost.`
},
cycling: (cost, who) => {
let whoToPrint = 'this'
if (who === 'acard') {
whoToPrint = 'a card'
+ } else if (who === 'one') {
+ whoToPrint = 'the card'
+ } else if (who === 'all') {
+ whoToPrint = 'a card'
}
if (cost) {
@@ -567,6 +686,13 @@ ${SVG.elementSymbol(others[2], lively.pt(12.5, 8.5), 1.5)}`, lively.rect(0, 0, 1
return `Pay (${cost}) to play ${thatCard}, but trash ${it} at end of turn.`
},
+
+ delirium: () => {
+ // alternative: if you have cards of four different elements in trash.
+ return `Only activate delirium abilities if you have fire, water, earth and wind cards in trash.`
+ },
+
+
discover: (howMany) => {
return `To discover ${howMany}, reveal top ${howMany} cards of any piles. Add 1 to your hand, trash the rest.`
},
@@ -679,6 +805,21 @@ ${SVG.elementSymbol(others[2], lively.pt(12.5, 8.5), 1.5)}`, lively.rect(0, 0, 1
return 'Tiny cards do not count for triggering the game end.'
},
+ trade: (...args) => {
+ return 'To trade, trash the card from hand to draw a card.'
+ },
+
+ trading: (who) => {
+ let whoText = 'this'
+ if (who === 'one') {
+ whoText = 'the card'
+ } else if (who === 'all') {
+ whoText = 'a card'
+ }
+
+ return `Passive Once per turn as a free action, you may trash ${whoText} from hand to draw a card.`
+ },
+
upgrade: (diff, who) => {
let whoText = 'this'
if (who === 'one') {
@@ -702,14 +843,49 @@ ${SVG.elementSymbol(others[2], lively.pt(12.5, 8.5), 1.5)}`, lively.rect(0, 0, 1
}
static renderKeywords(printedRules) {
- function makeBold(text) {
+ function makeBold(text, color) {
// light highlight on dark background rgb(250 214 90)
'rgb(226 175 0)'
- return `${text}`
+ return `${text}`
}
- printedRules = printedRules.replace(/manaburst:?/gmi, (match, pElement, offset, string, groups) => makeBold(match));
- printedRules = printedRules.replace(/\b(un)?meld(ed)?\b/gmi, (match, pElement, offset, string, groups) => makeBold(match));
+ function highlightKeyword(pattern, color='rgb(180 140 0)') {
+ printedRules = printedRules.replace(pattern, (match, pElement, offset, string, groups) => makeBold(match, color));
+ }
+
+ const C_HEADER = '#00f'; /*MD MD*/
+ const C_QUOTE = '#090';
+ const C_NEGATIVE = '#d44';
+ const C_POSITIVE = '#292';
+ const C_KEYWORD = '#708';
+ const C_ATOM = '#219';
+ const C_NUMBER = '#164';
+ const C_DEF = '#00f';
+ const C_VARIABLE = '#05a';
+ const C_VARIABLE2 = '#085';
+ const C_COMMENT = '#a50';
+ const C_STRING = '#a11';
+ const C_STRING2 = '#f50';
+ const C_META = '#555';
+ const C_QUALIFIER = '#555';
+ const C_BUILTIN = '#30a';
+ const C_BRACKET = '#997';
+ const C_TAG = '#170';
+ const C_ATTRIBUTE = '#00c';
+ const C_HR = '#999';
+ const C_LINK = '#00c';
+ const C_ERROR = '#f00';
+
+ highlightKeyword(/cycl(ed?|ing)\b/gmi, C_STRING2);
+ highlightKeyword(/delirium:?\b/gmi, C_STRING2);
+ highlightKeyword(/manaburst\b:?/gmi, C_STRING2);
+ highlightKeyword(/\b(un)?meld(ed)?\b/gmi, C_STRING2);
+ highlightKeyword(/quickcast\b/gmi, C_STRING2);
+ highlightKeyword(/resonance\b/gmi, C_STRING2);
+ highlightKeyword(/trad(ed?|ing)\b/gmi, C_STRING2);
+ highlightKeyword(/upgraded?\b/gmi, C_STRING2);
return printedRules
}
@@ -1351,14 +1527,15 @@ export default class Cards extends Morph {
/*MD #### Fonts MD*/
// convert fonts to jspdf-compatible format at https://peckconsulting.s3.amazonaws.com/fontconverter/fontconverter.html
addFonts(doc) {
- this.addFont(doc, 'BeaufortforLOLJa-Bold-normal.ttf', 'BeaufortforLOLJa-Bold', BeaufortforLOLJaBold)
- this.addFont(doc, 'BeaufortforLOLJa-Regular-normal.ttf', 'BeaufortforLOLJa-Regular', BeaufortforLOLJaRegular)
- this.addFont(doc, 'Univers 59 Ultra Condensed-normal.ttf', 'Univers 59 Ultra Condensed', Univers59UltraCondensed)
- this.addFont(doc, 'univers_55-normal.ttf', 'univers_55', univers_55);
+ this.addFont(doc, 'BeaufortforLOLJa-Bold-normal.ttf', 'BeaufortforLOLJa-Bold', BASE64_BeaufortforLOLJaBold)
+ this.addFont(doc, 'BeaufortforLOLJa-Regular-normal.ttf', 'BeaufortforLOLJa-Regular', BASE64_BeaufortforLOLJaRegular)
+ this.addFont(doc, 'Univers 59 Ultra Condensed-normal.ttf', 'Univers 59 Ultra Condensed', BASE64_Univers59UltraCondensed)
+ this.addFont(doc, 'univers_55-normal.ttf', 'univers_55', BASE64_Univers_55);
}
addFont(doc, vfsName, fontName, fontDataBase64) {
- const fontBase64 = "data:font/ttf;base64," + fontDataBase64;
+ // "data:font/ttf;base64," +
+ const fontBase64 = fontDataBase64;
doc.addFileToVFS(vfsName, fontBase64);
doc.addFont(vfsName, fontName, 'normal');
}
@@ -1942,7 +2119,7 @@ export default class Cards extends Morph {
// card name
doc::withGraphicsState(() => {
- // doc.setFont(FONT_NAME_CARD_NAME, "normal");
+ doc.setFont(FONT_NAME_CARD_NAME, "normal");
doc.setFontSize(.6 * titleBar.height::mmToPoint());
doc.setTextColor('#000000');
doc.text(this.getNameFromCard(cardDesc), ...titleBar.leftCenter().addX(2).toPair(), {
@@ -1986,7 +2163,7 @@ export default class Cards extends Morph {
}
renderCost(doc, cardDesc, pos, coinRadius) {
- const costSize = coinRadius / 4;
+ const costSize = coinRadius / 3;
const costDesc = cardDesc.getCost();
const cost = Array.isArray(costDesc) ? costDesc.first : costDesc;
@@ -2000,11 +2177,11 @@ export default class Cards extends Morph {
doc.circle(...coinCenter.toPair(), coinRadius, 'DF');
});
- this.renderIconText(doc, coinCenter, costSize, cost)
+ this.renderIconText(doc, coinCenter, costSize, cost, FONT_NAME_CARD_COST)
}
renderBaseVP(doc, cardDesc, pos, coinRadius) {
- const costSize = coinRadius / 4;
+ const costSize = coinRadius / 3;
const vp = cardDesc.getBaseVP() || 0;
@@ -2028,15 +2205,16 @@ export default class Cards extends Morph {
doc.lines([down, left, up, rightAgain], ...rightAbsolute, [1,1], 'DF', true)
});
- this.renderIconText(doc, iconCenter, costSize, vp)
+ this.renderIconText(doc, iconCenter, costSize, vp, FONT_NAME_CARD_VP)
}
- renderIconText(doc, centerPos, size, text) {
+ renderIconText(doc, centerPos, size, text, font) {
if (text === undefined) {
return
}
doc::withGraphicsState(() => {
+ doc.setFont(font, "normal");
doc.setFontSize(12 * size);
doc.setTextColor('#000000');
doc.text('' + text, ...centerPos.toPair(), { align: 'center', baseline: 'middle' });
@@ -2070,8 +2248,8 @@ export default class Cards extends Morph {
// } else if (element) {
// fullText = fullText::prepend(element::curate())
// }
+ doc.setFont(FONT_NAME_CARD_TYPE, "normal");
doc.setFontSize(7);
- // doc.setFont(FONT_NAME_UNIVERS_59, "normal");
const { w, h: textHeight } = doc.getTextDimensions(fullText);
@@ -2476,6 +2654,29 @@ export default class Cards extends Morph {
return await navigator.clipboard.write(data);
}
+
+ filterCardsForPrinting(cards) {
+ return cards.filter(card => {
+ if (card.getRating() === 'remove') {
+ return false;
+ }
+
+ if (card.hasTag('duplicate')) {
+ return false;
+ }
+ if (card.hasTag('unfinished')) {
+ return false;
+ }
+ if (card.hasTag('bad')) {
+ return false;
+ }
+ if (card.hasTag('deprecated')) {
+ return false;
+ }
+
+ return true
+ })
+ }
async onPrintSelected(evt) {
if (!this.cards) {
@@ -2483,7 +2684,7 @@ export default class Cards extends Morph {
}
const filteredEntries = this.allEntries.filter(entry => entry.isVisible())
- const cardsToPrint = filteredEntries.map(entry => entry.card)
+ const cardsToPrint = this.filterCardsForPrinting(filteredEntries.map(entry => entry.card))
if (await this.checkForLargePrinting(cardsToPrint)) {
await this.printForExport(cardsToPrint, evt.shiftKey);
@@ -2495,7 +2696,7 @@ export default class Cards extends Morph {
return;
}
- const cardsToPrint = this.cards.filter(card => !card.getIsPrinted());
+ const cardsToPrint = this.filterCardsForPrinting(this.cards.filter(card => !card.getIsPrinted()));
if (await this.checkForLargePrinting(cardsToPrint)) {
await this.printForExport(cardsToPrint, evt.shiftKey);