diff --git a/demos/stefan/untitled-board-game/ubg-card.js b/demos/stefan/untitled-board-game/ubg-card.js index 8c7584c78..63320aeef 100644 --- a/demos/stefan/untitled-board-game/ubg-card.js +++ b/demos/stefan/untitled-board-game/ubg-card.js @@ -54,6 +54,20 @@ export default class Card { this.versions.last.cost = cost; } + getBaseVP() { + return this.versions.last.baseVP; + } + + setBaseVP(baseVP) { + this.ensureUnprintedVersion(); + + if (!baseVP) { + delete this.versions.last.baseVP; + } else { + this.versions.last.baseVP = baseVP; + } + } + getText() { return this.versions.last.text; } diff --git a/src/components/widgets/ubg-cards-editor.html b/src/components/widgets/ubg-cards-editor.html index a85d8462a..12e286c11 100644 --- a/src/components/widgets/ubg-cards-editor.html +++ b/src/components/widgets/ubg-cards-editor.html @@ -128,7 +128,7 @@ #form-layout { display: grid; grid-template-columns: min-content auto 2.5in; - grid-template-rows: repeat(6, auto) 1fr .5fr auto; + grid-template-rows: repeat(7, auto) 1fr .5fr auto; grid-template-areas: "isPrinted-key isPrinted-value preview" "id-key id-value preview" @@ -136,6 +136,7 @@ "type-key type-value preview" "element-key element-value preview" "cost-key cost-value preview" + "vp-key vp-value preview" "text-key text-value preview" "notes-key notes-value preview" "art-key art-value preview" @@ -216,6 +217,8 @@ cost + base vp + text notes diff --git a/src/components/widgets/ubg-cards-editor.js b/src/components/widgets/ubg-cards-editor.js index bd35677f8..4d3eef893 100644 --- a/src/components/widgets/ubg-cards-editor.js +++ b/src/components/widgets/ubg-cards-editor.js @@ -22,6 +22,7 @@ export default class UBGCardsEditor extends Morph { this.$type.addEventListener(eventName, evt => this.modify$type(evt), false); this.$element.addEventListener(eventName, evt => this.modify$element(evt), false); this.$cost.addEventListener(eventName, evt => this.modify$cost(evt), false); + this.$vp.addEventListener(eventName, evt => this.modify$vp(evt), false); this.$text.addEventListener(eventName, evt => this.modify$text(evt), false); this.$notes.addEventListener(eventName, evt => this.modify$notes(evt), false); this.$art.addEventListener(eventName, evt => this.modify$art(evt), false); @@ -212,6 +213,9 @@ export default class UBGCardsEditor extends Morph { get $cost() { return this.get('#cost'); } + get $vp() { + return this.get('#vp'); + } get $text() { return this.get('#text'); } @@ -349,6 +353,35 @@ export default class UBGCardsEditor extends Morph { this.$cost.value = cost; } + modify$vp(evt) { + const vp = this.$vp.value; + + if (vp === '') { + this.card.setBaseVP(); + } else if ('*+-'.split('').some(char => vp.endsWith(char))) { + this.card.setBaseVP(vp); + } else { + const intCost = parseInt(vp); + if (_.isNaN(intCost)) { + this.card.setBaseVP(); + } else { + this.card.setBaseVP(intCost); + } + } + + this.propagateChange() + } + display$vp() { + const vp = this.card.getBaseVP(); + + if (vp === undefined) { + this.$vp.value = ''; + return; + } + + this.$vp.value = vp; + } + modify$text(evt) { const text = this.$text.value; if (text === '') { @@ -444,6 +477,7 @@ export default class UBGCardsEditor extends Morph { this.display$type(); this.display$element(); this.display$cost(); + this.display$vp(); this.display$text(); this.display$notes(); this.display$art(); diff --git a/src/components/widgets/ubg-cards-entry.html b/src/components/widgets/ubg-cards-entry.html index f6e428eef..47b549e26 100644 --- a/src/components/widgets/ubg-cards-entry.html +++ b/src/components/widgets/ubg-cards-entry.html @@ -135,6 +135,9 @@ #cost { color: goldenrod; } + #vp { + color: violet; + } #name { font-weight: 600; } @@ -152,6 +155,7 @@ + diff --git a/src/components/widgets/ubg-cards-entry.js b/src/components/widgets/ubg-cards-entry.js index 3c1f0cd44..6e5e5830a 100644 --- a/src/components/widgets/ubg-cards-entry.js +++ b/src/components/widgets/ubg-cards-entry.js @@ -129,6 +129,7 @@ export default class UBGCardEntry extends Morph { this.renderElement(v); this.get('#cost').innerHTML = v.cost || '/'; + this.get('#vp').innerHTML = card.getBaseVP() || '-'; this.get('#name').innerHTML = card.versions.last.name || 'no name yet'; this.get('#text').innerHTML = card.versions.last.text || 'no text'; diff --git a/src/components/widgets/ubg-cards.html b/src/components/widgets/ubg-cards.html index ea26266e5..e84ae0b65 100644 --- a/src/components/widgets/ubg-cards.html +++ b/src/components/widgets/ubg-cards.html @@ -139,6 +139,7 @@ + diff --git a/src/components/widgets/ubg-cards.js b/src/components/widgets/ubg-cards.js index 8f14f9364..ca532e7c7 100644 --- a/src/components/widgets/ubg-cards.js +++ b/src/components/widgets/ubg-cards.js @@ -6,6 +6,7 @@ import ContextMenu from 'src/client/contextmenu.js'; import { Paper } from 'src/client/literature.js'; import Bibliography from "src/client/bibliography.js"; import pdf from "src/external/pdf.js"; +import { shake } from 'utils'; import { serialize, deserialize } from 'src/client/serialize.js'; import Card from 'demos/stefan/untitled-board-game/ubg-card.js'; @@ -196,6 +197,9 @@ const SORT_BY = { NAME: 'name' }; +const VP_FILL = 'violet'; +const VP_STROKE = '#9400d3'; // darkviolet + export default class Cards extends Morph { async initialize() { @@ -585,6 +589,10 @@ export default class Cards extends Morph { return this.buildCards(doc, cards); // .slice(0,12) } + async fetchAssetsInfo() { + return (await this.assetsFolder.fetchStats()).contents; + } + async buildCards(doc, cardsToPrint) { const GAP = lively.pt(.2, .2); @@ -600,8 +608,7 @@ export default class Cards extends Morph { const progress = await lively.showProgress(progressLabel(0)); try { - const ASSET_FOLDER = this.assetsFolder; - const assetsInfo = (await ASSET_FOLDER.fetchStats()).contents; + const assetsInfo = await this.fetchAssetsInfo(); let i = 0; let currentPage = 0; @@ -1172,10 +1179,13 @@ export default class Cards extends Morph { // cost const coinCenter = coinLeftCenter.addXY(costCoinRadius, 0); this.renderCost(doc, cardDesc, coinCenter, costCoinRadius) + const vpCenter = coinCenter.addY(costCoinRadius * 2.75); + this.renderBaseVP(doc, cardDesc, vpCenter, costCoinRadius) } renderCost(doc, cardDesc, pos, coinRadius) { - const COST_SIZE = coinRadius / 4; + const costSize = coinRadius / 4; + const costDesc = cardDesc.getCost(); const cost = Array.isArray(costDesc) ? costDesc.first : costDesc; @@ -1184,17 +1194,54 @@ export default class Cards extends Morph { doc.setGState(new doc.GState({ opacity: 0.9 })); doc.setFillColor('#b8942d'); doc.setDrawColor(148, 0, 211); - doc.setLineWidth(0.2 * COST_SIZE) + doc.setLineWidth(0.2 * costSize) doc.circle(...coinCenter.toPair(), coinRadius, 'DF'); }); - if (cost !== undefined) { - doc::withGraphicsState(() => { - doc.setFontSize(12 * COST_SIZE); - doc.setTextColor('#000000'); - doc.text('' + cost, ...coinCenter.toPair(), { align: 'center', baseline: 'middle' }); - }); + this.renderIconText(doc, coinCenter, costSize, cost) + } + + renderBaseVP(doc, cardDesc, pos, coinRadius) { + const costSize = coinRadius / 4; + + const vp = cardDesc.getBaseVP(); + if (!vp) { + return; + } + + const iconCenter = pos; + doc::withGraphicsState(() => { + doc.setGState(new doc.GState({ opacity: 0.9 })) + // doc.setFillColor('#b8942d'); + doc.setDrawColor(VP_STROKE) + doc.setLineWidth(0.2 * costSize) + // doc.circle(...coinCenter.toPair(), coinRadius, 'DF'); + doc.setFillColor(VP_FILL) + // doc.rect(coinCenter.x - coinRadius, coinCenter.y - coinRadius, 2 * coinRadius, 2 * coinRadius, 'DF'); + + // diamond shape + const diagonal = coinRadius * .9 * Math.sqrt(2) + const rightAbsolute = iconCenter.addX(diagonal).toPair() + const down = lively.pt(-diagonal, diagonal).toPair() + const left = lively.pt(-diagonal, -diagonal).toPair() + const up = lively.pt(diagonal, -diagonal).toPair() + const rightAgain = lively.pt(diagonal, diagonal).toPair() + doc.lines([down, left, up, rightAgain], ...rightAbsolute, [1,1], 'DF', true) + }); + + this.renderIconText(doc, iconCenter, costSize, vp) + } + + renderIconText(doc, centerPos, size, text) { + if (text === undefined) { + return } + + doc::withGraphicsState(() => { + doc.setFontSize(12 * size); + doc.setTextColor('#000000'); + doc.text('' + text, ...centerPos.toPair(), { align: 'center', baseline: 'middle' }); + }); } // #important @@ -1240,6 +1287,13 @@ ${smallElementIcon(others[2], lively.pt(11, 7))} `; } + function printVP(vp) { + return ` + +${vp} +`; + } + let printedRules = rulesText; printedRules = printedRules.replace(/t3x(fire|water|earth|wind|gray)/gmi, 'tap 3x$1'); printedRules = printedRules.replace(/(^|\n)tap 3x(fire|water|earth|wind|gray)([^\n]*)/gi, function replacer(match, p1, pElement, pText, offset, string, groups) { @@ -1259,6 +1313,9 @@ ${smallElementIcon(others[2], lively.pt(11, 7))} printedRules = printedRules.replace(/(fire|water|earth|wind|gray)/gmi, function replacer(match, pElement, offset, string, groups) { return element(pElement); }); + printedRules = printedRules.replace(/(\d+|\*|d+\*|\d+x|x|\b)VP\b/gmi, function replacer(match, vp, offset, string, groups) { + return printVP(vp); + }); printedRules = printedRules.replace(/\(([*0-9x+-]*)\)/gmi, function replacer(match, p1, offset, string, groups) { return coin(p1); }); @@ -1510,7 +1567,7 @@ width: ${ruleTextBox.width}mm; min-height: ${ruleTextBox.height}mm;`}>; if (that && that.localName === 'lively-code-mirror' && document.contains(that)) { lively.showElement(that) - const matches = that.value.matchAll(/^([^0-9]+)?\s([0-9]+)?\s?([a-zA-Z ]+)?\s?(?:\(([0-9,]+)\))?\.\s(.*)?$/gmi); + const matches = that.value.matchAll(/^([^0-9]+)?\s([0-9]+)?\s?([a-zA-Z ]+)?\s?(?:\(([0-9,]+)\))?(?:\s?([0-9*+-]+))?\.\s(.*)?$/gmi); const newCards = [...matches].map(match => { const card = new Card(); @@ -1525,7 +1582,7 @@ width: ${ruleTextBox.width}mm; min-height: ${ruleTextBox.height}mm;`}>; card.setName(match[1]) card.setType(match[3]) - card.setText(match[5]) + card.setText(match[6]) let type = '' let element; @@ -1562,6 +1619,16 @@ width: ${ruleTextBox.width}mm; min-height: ${ruleTextBox.height}mm;`}>; } } + const baseVP = match[5]; + const intBaseVP = parseInt(baseVP); + if (!_.isNaN(intBaseVP)) { + card.setBaseVP(intBaseVP) + } else { + if (baseVP) { + card.setBaseVP(baseVP) + } + } + return card; }); @@ -1582,6 +1649,52 @@ width: ${ruleTextBox.width}mm; min-height: ${ruleTextBox.height}mm;`}>; } } + async onArtDesc(evt) { + const text = do { + const assetsInfo = await this.fetchAssetsInfo(); + let ids = [] + for (let entry of assetsInfo) { + if (entry.type !== 'file') { + continue + } + + const match = entry.name.match(/^(.+)\.jpg$/) + if (!match) { + continue + } + + // const id = parseInt(match[1]) + // if (_.isInteger(id)) { + // ids.push(id) + // } + ids.push(match[1]) + } + + let cards = this.cards; + cards = cards + .filter(c => !c.getIsBad()) + // we just use a string match for now + .filter(c => !ids.includes(c.getId() + '')).sortBy('id') + cards.map(c => { + const artDesc = c.getArtDirection() || c.getName(); + return `[${c.getId()}, '${artDesc}'],` + }).join('\n') + }; + + const type = "text/plain"; + const blob = new Blob([text], { type }); + // evt.clipboardData.setData('text/html', html); + const data = [new ClipboardItem({ [type]: blob })]; + + try { + await navigator.clipboard.write(data); + lively.success('copied art description!'); + } catch (e) { + shake(this.get('#artDesc')); + lively.error('copying failed', e.message); + } + } + async onPrintAll(evt) { if (!this.cards) { return;