Skip to content

Commit db0d98c

Browse files
authored
Fix Cockatrice export and add MTGA/MTGO options (#1419)
* fix export of text for double-sided cards * style deck + draft log export * start writing es6 tests for export deck helpers * complete cocacktrice + mtgo export (with tests) * refactor + add first pass Arena export * add text export, final tidy up * fix land export * export PR feedback -> tidyups * New line to stimulate test rerun
1 parent a62e82a commit db0d98c

27 files changed

+1351
-420
lines changed

.mocharc.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require:
2+
- 'core-js'
3+
- 'regenerator-runtime'
4+
- '@babel/register'
5+

backend/api/sets.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const sanitize = (json) => {
6262
subtypes = [],
6363
manaCost,
6464
url,
65-
identifiers = [],
65+
identifiers = {},
6666
rarity,
6767
power,
6868
toughness,

backend/import/toBoosterCard.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const toBoosterCard = (setCode) => (mtgjsonCard, index, rawCards) => {
1717
subtypes = [],
1818
manaCost,
1919
url,
20-
identifiers = [],
20+
identifiers = {},
2121
rarity,
2222
power,
2323
toughness,
@@ -31,7 +31,6 @@ const toBoosterCard = (setCode) => (mtgjsonCard, index, rawCards) => {
3131

3232
const {isDoubleFaced, flippedCardURL, flippedIsBack, flippedNumber} = getDoubleFacedProps(mtgjsonCard, rawCards);
3333
const color = upperFirst(getColor(mtgjsonCard, rawCards));
34-
const identifiersMini = {"scryfallId": identifiers.scryfallId};
3534

3635
return {
3736
uuid,
@@ -48,7 +47,12 @@ const toBoosterCard = (setCode) => (mtgjsonCard, index, rawCards) => {
4847
manaCost: manaCost || "",
4948
rarity: upperFirst(rarity),
5049
url: url || `https://api.scryfall.com/cards/${identifiers.scryfallId}?format=image`,
51-
identifiers: identifiersMini,
50+
identifiers: {
51+
scryfallId: identifiers.scryfallId,
52+
mtgoId: identifiers.mtgoId
53+
// needed for exporting cards to MTGO .dek
54+
// NOTE: not all cards seem to have this identifier..?
55+
},
5256
layout,
5357
isDoubleFaced,
5458
flippedCardURL,

frontend/src/app.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@ let App = {
5858
cols: false,
5959
hidepicks: false,
6060
deckSize: 40,
61-
filename: "filename",
62-
filetype: "txt",
61+
62+
// export deck
63+
exportDeckFormat: "cockatrice",
64+
exportDeckFilename: "filename",
65+
6366
side: false,
6467
sort: "rarity",
6568
log: {},
@@ -227,9 +230,8 @@ let App = {
227230
const savename = type === "draft" ? sets[0] + "-draft" : type;
228231
const date = new Date();
229232
const currentTime = date.toISOString().slice(0, 10).replace("T", " ") + "_" + date.toString().slice(16, 21).replace(":", "-");
230-
const filename = `${savename.replace(/\W/, "-")}_${currentTime}`;
231233
App.set({
232-
filename,
234+
exportDeckFilename: `${savename.replace(/\W/, "-")}_${currentTime}`,
233235
game: { type, sets, packsInfo, burnsPerPack },
234236
picksPerPack,
235237
});

frontend/src/basiclands.js

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/* The below script can be used to generate a different set of lands
2+
* The data used here is for MTGO + MTGA exports, which prefer to know extra details beyond card name.
3+
*
4+
* ## Use
5+
* 1. uncomment below lines
6+
* 2. change the "set" code (keeping in mind must be > Ixalan for Arena)
7+
* 3. run $ node frontend/src/basiclands.js
8+
* 4. copy the output in to replace the current lands
9+
*/
10+
11+
// const { getCardByName } = require('../../backend/data')
12+
// const set = 'm20' // must be lower case
13+
// const lands = {
14+
// W: getCardByName(`plains (${set})`),
15+
// U: getCardByName(`island (${set})`),
16+
// B: getCardByName(`swamp (${set})`),
17+
// R: getCardByName(`mountain (${set})`),
18+
// G: getCardByName(`forest (${set})`)
19+
// }
20+
21+
// console.log(JSON.stringify(lands, null, 2))
22+
23+
export default {
24+
"W": {
25+
"uuid": "4fefe700-3104-50e8-812e-2e305bc8813d",
26+
"name": "Plains",
27+
"color": "Colorless",
28+
"colors": [],
29+
"colorIdentity": [
30+
"W"
31+
],
32+
"setCode": "M20",
33+
"cmc": 0,
34+
"number": "261",
35+
"type": "Land",
36+
"manaCost": "",
37+
"rarity": "Basic",
38+
"url": "https://api.scryfall.com/cards/73fb4a34-c11e-45e9-a986-43c0a0ae5424?format=image",
39+
"identifiers": {
40+
"scryfallId": "73fb4a34-c11e-45e9-a986-43c0a0ae5424",
41+
"mtgoId": "73421"
42+
},
43+
"layout": "normal",
44+
"isDoubleFaced": false,
45+
"flippedCardURL": "",
46+
"flippedIsBack": false,
47+
"flippedNumber": "",
48+
"supertypes": [
49+
"Basic"
50+
],
51+
"subtypes": [
52+
"Plains"
53+
],
54+
"text": "({T}: Add {W}.)"
55+
},
56+
"U": {
57+
"uuid": "db340b60-f2fb-5a74-9cc3-cc8cc33b9374",
58+
"name": "Island",
59+
"color": "Colorless",
60+
"colors": [],
61+
"colorIdentity": [
62+
"U"
63+
],
64+
"setCode": "M20",
65+
"cmc": 0,
66+
"number": "265",
67+
"type": "Land",
68+
"manaCost": "",
69+
"rarity": "Basic",
70+
"url": "https://api.scryfall.com/cards/fe7f4393-38f9-43b5-873b-58246183b874?format=image",
71+
"identifiers": {
72+
"scryfallId": "fe7f4393-38f9-43b5-873b-58246183b874",
73+
"mtgoId": "73429"
74+
},
75+
"layout": "normal",
76+
"isDoubleFaced": false,
77+
"flippedCardURL": "",
78+
"flippedIsBack": false,
79+
"flippedNumber": "",
80+
"supertypes": [
81+
"Basic"
82+
],
83+
"subtypes": [
84+
"Island"
85+
],
86+
"text": "({T}: Add {U}.)"
87+
},
88+
"B": {
89+
"uuid": "328ba861-c227-521a-aff4-f93a86e6db06",
90+
"name": "Swamp",
91+
"color": "Colorless",
92+
"colors": [],
93+
"colorIdentity": [
94+
"B"
95+
],
96+
"setCode": "M20",
97+
"cmc": 0,
98+
"number": "269",
99+
"type": "Land",
100+
"manaCost": "",
101+
"rarity": "Basic",
102+
"url": "https://api.scryfall.com/cards/184a196e-8604-49d2-a66a-6f7c0eafd5de?format=image",
103+
"identifiers": {
104+
"scryfallId": "184a196e-8604-49d2-a66a-6f7c0eafd5de",
105+
"mtgoId": "73437"
106+
},
107+
"layout": "normal",
108+
"isDoubleFaced": false,
109+
"flippedCardURL": "",
110+
"flippedIsBack": false,
111+
"flippedNumber": "",
112+
"supertypes": [
113+
"Basic"
114+
],
115+
"subtypes": [
116+
"Swamp"
117+
],
118+
"text": "({T}: Add {B}.)"
119+
},
120+
"R": {
121+
"uuid": "4066857f-e1f2-5095-9a60-ec713874fc20",
122+
"name": "Mountain",
123+
"color": "Colorless",
124+
"colors": [],
125+
"colorIdentity": [
126+
"R"
127+
],
128+
"setCode": "M20",
129+
"cmc": 0,
130+
"number": "273",
131+
"type": "Land",
132+
"manaCost": "",
133+
"rarity": "Basic",
134+
"url": "https://api.scryfall.com/cards/399f7531-e137-463b-bec3-e86756b6ed71?format=image",
135+
"identifiers": {
136+
"scryfallId": "399f7531-e137-463b-bec3-e86756b6ed71",
137+
"mtgoId": "73445"
138+
},
139+
"layout": "normal",
140+
"isDoubleFaced": false,
141+
"flippedCardURL": "",
142+
"flippedIsBack": false,
143+
"flippedNumber": "",
144+
"supertypes": [
145+
"Basic"
146+
],
147+
"subtypes": [
148+
"Mountain"
149+
],
150+
"text": "({T}: Add {R}.)"
151+
},
152+
"G": {
153+
"uuid": "c4198bae-2c2b-51bc-8d57-5b9640232e6e",
154+
"name": "Forest",
155+
"color": "Colorless",
156+
"colors": [],
157+
"colorIdentity": [
158+
"G"
159+
],
160+
"setCode": "M20",
161+
"cmc": 0,
162+
"number": "277",
163+
"type": "Land",
164+
"manaCost": "",
165+
"rarity": "Basic",
166+
"url": "https://api.scryfall.com/cards/42352899-f2f2-4dea-863b-8d685e63b454?format=image",
167+
"identifiers": {
168+
"scryfallId": "42352899-f2f2-4dea-863b-8d685e63b454",
169+
"mtgoId": "73453"
170+
},
171+
"layout": "normal",
172+
"isDoubleFaced": false,
173+
"flippedCardURL": "",
174+
"flippedIsBack": false,
175+
"flippedNumber": "",
176+
"supertypes": [
177+
"Basic"
178+
],
179+
"subtypes": [
180+
"Forest"
181+
],
182+
"text": "({T}: Add {G}.)"
183+
}
184+
};

frontend/src/components/Select.jsx

+21-15
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@ import PropTypes from "prop-types";
33

44
import App from "../app";
55

6-
const Select = ({
7-
link,
8-
opts,
9-
onChange = (e) => { App.save(link, e.currentTarget.value); },
10-
value = App.state[link],
11-
...rest}) => (
12-
<select
13-
onChange={onChange}
14-
value={value}
15-
{...rest}>
16-
{opts.map((opt, index) =>
17-
<option key={index}>{opt}</option>
18-
)}
19-
</select>
20-
);
6+
const Select = props => {
7+
const {
8+
link,
9+
opts,
10+
value = App.state[link],
11+
onChange = (e) => App.save(link, e.currentTarget.value),
12+
...rest
13+
} = props
14+
15+
return (
16+
<select
17+
onChange={onChange}
18+
value={value}
19+
{...rest}
20+
>
21+
{opts.map((opt, index) =>
22+
<option value={opt} key={index}>{opt}</option>
23+
)}
24+
</select>
25+
);
26+
}
2127

2228
Select.propTypes = {
2329
link: PropTypes.string,

0 commit comments

Comments
 (0)