Add support for hero and villain decks
Should definitely have been smaller commits, but oh well, too lazy to split them up now important note: switched editor from XMLHttpRequest to fetch
This commit is contained in:
parent
550811adfd
commit
8bc7e43732
112
js/editor.js
112
js/editor.js
@ -10,10 +10,13 @@ document.title = "Editor|" + deckName;
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
// load deck input json
|
||||
getJSON("deck.input.json", json => {
|
||||
deckJSON = json;
|
||||
makeSVGs(deckJSON);
|
||||
});
|
||||
fetch("deck.input.json")
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
deckJSON = json;
|
||||
makeSVGs(deckJSON);
|
||||
})
|
||||
.catch(error => console.error(error));
|
||||
|
||||
// deck JSON uploader
|
||||
document.querySelector('#jsonUpload').addEventListener('change', event => {
|
||||
@ -96,55 +99,54 @@ function downloadFile(file, name) {
|
||||
document.body.removeChild(dl);
|
||||
}
|
||||
|
||||
function getJSON(filename, callback) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener("load", () => {
|
||||
if (xhr.status === 200) {
|
||||
callback(JSON.parse(xhr.responseText));
|
||||
}
|
||||
});
|
||||
xhr.open("GET", filename);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function getSVGTemplate(name, callback) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener("load", () => {
|
||||
let respSVG = xhr.responseXML.children[0];
|
||||
callback(respSVG);
|
||||
});
|
||||
xhr.open("GET", "/template/" + name + ".svg");
|
||||
xhr.send();
|
||||
return fetch("/template/" + name + ".svg")
|
||||
.then(response => response.text())
|
||||
.then(str => (new window.DOMParser()).parseFromString(str, "text/xml").activeElement);
|
||||
}
|
||||
|
||||
function makeSVGs(deckJSON) {
|
||||
async function makeSVGs(deckJSON) {
|
||||
document.querySelector('#deckName').value = deckJSON.name || "";
|
||||
document.querySelector('#deckType').value = deckJSON.type || "";
|
||||
|
||||
let deck = document.querySelector('#deck');
|
||||
deck.innerHTML = "";
|
||||
|
||||
setDeckTemplate(deckJSON.type, () => {
|
||||
Object.entries(template.cardTypes).forEach(cardType => {
|
||||
getSVGTemplate(deckJSON.type + "/" + cardType[0], templateSVG => {
|
||||
deck.style.width = Math.ceil(Math.sqrt(deckJSON.deck.length)) *
|
||||
parseInt(templateSVG.getAttribute("width")) + "pt";
|
||||
deck.style.height = Math.ceil(Math.sqrt(deckJSON.deck.length)) *
|
||||
parseInt(templateSVG.getAttribute("height")) + "pt";
|
||||
let template = await fetch(`/template/${deckJSON.type}/input.json`)
|
||||
.then(data => data.json());
|
||||
|
||||
// build card SVGs
|
||||
deckJSON[cardType[0]].forEach(
|
||||
card => makeCardSVG(deck, cardType[1], templateSVG, card));
|
||||
});
|
||||
let cardCount = Object.entries(template.cardTypes)
|
||||
.map(ct => deckJSON[ct[0]].length * (ct[1].back ? 2 : 1))
|
||||
.reduce((sum, current) => sum + current, 0);
|
||||
|
||||
// note: needs to be a for loop because it needs to be synchronous
|
||||
// and also have await
|
||||
// Although I suppose I could prefetch the SVGs and then do the rest...
|
||||
for (let cardType of Object.entries(template.cardTypes)) {
|
||||
let backSVG;
|
||||
if (cardType[1].back) {
|
||||
let backTemplate = cardType[1].back.template || (cardType[0] + "-back");
|
||||
backSVG = await getSVGTemplate(deckJSON.type + "/" + backTemplate);
|
||||
}
|
||||
let templateSVG = await getSVGTemplate(deckJSON.type + "/" + cardType[0]);
|
||||
console.log(templateSVG);
|
||||
|
||||
// build card SVGs
|
||||
deckJSON[cardType[0]].forEach(card => {
|
||||
makeCardSVG(deck, cardType[1], templateSVG, card);
|
||||
|
||||
// if there is a back, build it too
|
||||
if (cardType[1].back) {
|
||||
makeCardSVG(deck, cardType[1].back, backSVG, card, back=true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setDeckTemplate(type, callback) {
|
||||
getJSON("/template/" + type + "/input.json", json => {
|
||||
template = json;
|
||||
callback();
|
||||
});
|
||||
// set div width/height based on number of cards
|
||||
deck.style.width = Math.ceil(Math.sqrt(cardCount)) *
|
||||
parseInt(templateSVG.getAttribute("width")) + "pt";
|
||||
deck.style.height = Math.ceil(Math.sqrt(cardCount)) *
|
||||
parseInt(templateSVG.getAttribute("height")) + "pt";
|
||||
};
|
||||
}
|
||||
|
||||
function setForm(cardTemplate, card) {
|
||||
@ -170,21 +172,20 @@ function setForm(cardTemplate, card) {
|
||||
});
|
||||
}
|
||||
|
||||
function makeCardSVG(deck, cardInputTemplate, templateSVG, card) {
|
||||
function makeCardSVG(deck, cardInputTemplate, templateSVG, card, back=false) {
|
||||
let propSource = (back && card.back) ? card.back : card;
|
||||
let cardSVG = deck.appendChild(templateSVG.cloneNode(true));
|
||||
cardSVG.addEventListener('click', () => {
|
||||
selected = {svg: cardSVG, json: card};
|
||||
setForm(cardInputTemplate, card);
|
||||
}, true);
|
||||
Object.keys(cardInputTemplate.inputs).forEach(
|
||||
prop => wrapSVGText(cardSVG.querySelector('#' + prop), String(card[prop] || "")));
|
||||
Object.entries(cardInputTemplate.hide).forEach(hidable => {
|
||||
if (hidable[1] in card) {
|
||||
cardSVG.querySelector('#' + hidable[0]).setAttribute('display', '');
|
||||
}
|
||||
else {
|
||||
cardSVG.querySelector('#' + hidable[0]).setAttribute('display', 'none');
|
||||
}
|
||||
Object.keys(cardInputTemplate.inputs).forEach(prop => {
|
||||
let inputProp = propSource[prop] || card[prop] || "";
|
||||
wrapSVGText(cardSVG.querySelector('#' + prop), String(inputProp));
|
||||
});
|
||||
Object.entries(cardInputTemplate.hide || []).forEach(hidable => {
|
||||
cardSVG.querySelector('#' + hidable[0])
|
||||
.setAttribute('display', hidable[1] in propSource ? '' : 'none');
|
||||
});
|
||||
}
|
||||
|
||||
@ -193,10 +194,11 @@ function upload() {
|
||||
|
||||
// POST the generated SVGs to the server
|
||||
let data = (new XMLSerializer()).serializeToString(deck);
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', "upload");
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.send(JSON.stringify({body: data, json: deckJSON}));
|
||||
fetch('upload', {
|
||||
method: 'post',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({body: data, json: deckJSON})
|
||||
});
|
||||
}
|
||||
|
||||
function wrapSVGText(e, string) {
|
||||
|
@ -50,12 +50,14 @@ const server = http.createServer((req, res) => {
|
||||
case "card.json":
|
||||
case "deck.json":
|
||||
case "environment/input.json":
|
||||
case "hero/input.json":
|
||||
case "villain/input.json":
|
||||
sendFile(res, "template/" + item, 'application/json');
|
||||
break;
|
||||
case "environment/deck.svg":
|
||||
case "hero/deck.svg":
|
||||
case "hero/character-back.svg":
|
||||
case "hero/character-front.svg":
|
||||
case "hero/character.svg":
|
||||
case "villain/deck.svg":
|
||||
case "villain/character.svg":
|
||||
case "villain/instructions.svg":
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"cardTypes": {
|
||||
"deck": {
|
||||
"meta": ["count"],
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
"keywords": "text",
|
||||
|
@ -4,7 +4,7 @@
|
||||
<path d="m9.498 243.5h162v-234h-162z" fill="#fff" stroke="#000" stroke-miterlimit="10"/>
|
||||
<path d="m9.5 243.5h162v-234h-162z" fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path d="m168.08 239.87h-154.91v-52.371h154.91z" fill="#cfd1d4" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<text id="incapacitatedPowers" x="15.184497" y="195.27484" fill="#000000" font-family="'RedStateBlueState BB'" font-size="8px">
|
||||
<text id="incapacitated" x="15.184497" y="195.27484" fill="#000000" font-family="'RedStateBlueState BB'" font-size="8px">
|
||||
<tspan x="15.184497" y="195.27484">{ Text Here</tspan>
|
||||
<tspan x="15.184497" y="214.02484">{ Text Here</tspan>
|
||||
<tspan x="15.184497" y="232.77484">{ Text Here</tspan>
|
||||
|
Before Width: | Height: | Size: 851 B After Width: | Height: | Size: 845 B |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
@ -1,5 +1,17 @@
|
||||
{
|
||||
"cardTypes": {
|
||||
"character": {
|
||||
"back": {
|
||||
"inputs": {
|
||||
"incapacitated": "text"
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"hp": "number",
|
||||
"power": "text",
|
||||
"powerText": "textarea"
|
||||
}
|
||||
},
|
||||
"deck": {
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
|
@ -1,5 +1,40 @@
|
||||
{
|
||||
"cardTypes": {
|
||||
"character": {
|
||||
"back": {
|
||||
"template": "character",
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
"hp": "number",
|
||||
"title": "text"
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
"hp": "number",
|
||||
"title": "text"
|
||||
}
|
||||
},
|
||||
"instructions": {
|
||||
"back": {
|
||||
"template": "instructions",
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
"title": "text",
|
||||
"gameplay": "textarea"
|
||||
},
|
||||
"hide": {
|
||||
"setupBox": "setup"
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
"title": "text",
|
||||
"setup": "textarea",
|
||||
"gameplay": "textarea",
|
||||
"advanced": "textarea"
|
||||
}
|
||||
},
|
||||
"deck": {
|
||||
"inputs": {
|
||||
"name": "text",
|
||||
|
@ -9,16 +9,18 @@
|
||||
<path d="m99.873 223.92h-81.75v-8.5h81.75z" fill="#ebe130" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<path d="m99.873 128.92h-81.75v-8.5h81.75z" fill="#ebe130" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<text id="name" transform="scale(.97038 1.0305)" x="14.445003" y="44.008652" fill="#ffffff" font-family="'Armor Piercing'" font-size="29.11px" stroke="#000000" stroke-width=".72998px" xml:space="preserve">Villain Name</text>
|
||||
<text id="title" x="65.489799" y="52.567066" font-family="'RedStateBlueState BB'" font-size="11px" font-style="italic">Villain Title</text>
|
||||
<text id="artist" x="168.59041" y="247.55547" fill="#ffffff" font-family="'RedStateBlueState BB'" font-size="5.3px" text-align="end" text-anchor="end">Art By</text>
|
||||
<text x="16.214796" y="134.85202" font-family="'RedStateBlueState BB'" font-size="8.7997px" stroke-width=".75" style="line-height:5.25px" xml:space="preserve"><tspan x="16.214796" y="134.85202" stroke-width=".75"><tspan x="16.214796" y="134.85202" stroke-width=".75">Text Here</tspan></tspan></text>
|
||||
<g id="setupBox">
|
||||
<path d="m167.79 117.46h-154.58v-50.539h154.58z" fill="#cfd1d4" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<path d="m99.873 68.92h-81.75v-8.5h81.75z" fill="#ebe130" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<text x="16.214796" y="74.85202" font-family="'RedStateBlueState BB'" font-size="8.7997px" letter-spacing="0px" stroke-width=".75" word-spacing="0px" style="line-height:5.25px" xml:space="preserve"><tspan x="16.214796" y="74.85202" stroke-width=".75"><tspan x="16.214796" y="74.85202" stroke-width=".75">Text Here</tspan></tspan></text>
|
||||
<text x="47.487053" y="67.280876" font-family="'RedStateBlueState BB'" font-size="10px" font-style="italic">SetUp</text>
|
||||
<g font-family="'RedStateBlueState BB'">
|
||||
<text id="title" x="65.489799" y="52.567066" font-size="11px" font-style="italic">Villain Title</text>
|
||||
<g id="setupBox">
|
||||
<path d="m167.79 117.46h-154.58v-50.539h154.58z" fill="#cfd1d4" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<path d="m99.873 68.92h-81.75v-8.5h81.75z" fill="#ebe130" stroke="#000" stroke-linejoin="round" stroke-miterlimit="10"/>
|
||||
<text id="setup" x="16.214796" y="74.85202" width="152" height="50" font-size="9px">Text Here</text>
|
||||
<text x="47.487053" y="67.280876" font-size="10px" font-style="italic">SetUp</text>
|
||||
</g>
|
||||
<text x="37.503998" y="127.27358" font-size="10px" font-style="italic">Game Play</text>
|
||||
<text id="gameplay" x="16.214796" y="134.85202" width="150" height="80" font-size="9px">Text Here</text>
|
||||
<text x="39.672401" y="222.4875" font-size="10px" font-style="italic">Advanced</text>
|
||||
<text id="advanced" x="16.214796" y="229.85202" width="152" height="10" font-size="9px">Text Here</text>
|
||||
<text id="artist" x="168.59041" y="247.55547" fill="#ffffff" font-size="5.3px" text-align="end" text-anchor="end">Art By</text>
|
||||
</g>
|
||||
<text x="37.503998" y="127.27358" font-family="'RedStateBlueState BB'" font-size="10px" font-style="italic">Game Play</text>
|
||||
<text x="39.672401" y="222.4875" font-family="'RedStateBlueState BB'" font-size="10px" font-style="italic">Advanced</text>
|
||||
<text x="16.214796" y="229.85202" font-family="'RedStateBlueState BB'" font-size="8.7997px" stroke-width=".75" style="line-height:5.25px" xml:space="preserve"><tspan x="16.214796" y="229.85202" stroke-width=".75"><tspan x="16.214796" y="229.85202" stroke-width=".75">Text Here</tspan></tspan></text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in New Issue
Block a user