mirror of
https://github.com/Cockatrice/Magic-Token.git
synced 2026-03-22 01:54:39 -05:00
Compare commits
286 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cb896878f | ||
|
|
de2fcd79fa | ||
|
|
a593821c44 | ||
|
|
fcbf92a759 | ||
|
|
2d1dfa72d3 | ||
|
|
63f74cb0c3 | ||
|
|
7ee51750fc | ||
|
|
445f2e3f06 | ||
|
|
1fe94adf84 | ||
|
|
d200e25935 | ||
|
|
56ea368bdb | ||
|
|
e203c9d2b5 | ||
|
|
b9c9a87de4 | ||
|
|
0d3cfae7ff | ||
|
|
739e66e110 | ||
|
|
3a3b5f5600 | ||
|
|
5030e56cb5 | ||
|
|
e81f9fa565 | ||
|
|
8f9adfc495 | ||
|
|
5c763f11f0 | ||
|
|
f9c8e0e1c3 | ||
|
|
a7e1070587 | ||
|
|
5fa59474b7 | ||
|
|
041207a347 | ||
|
|
e57b24391c | ||
|
|
c2029ad871 | ||
|
|
38c3ea136d | ||
|
|
7b0b79f268 | ||
|
|
192a6ef65b | ||
|
|
31ee92a458 | ||
|
|
afd8a457df | ||
|
|
2c089f447f | ||
|
|
739c53db22 | ||
|
|
3a615a5150 | ||
|
|
8e36b80a07 | ||
|
|
8a1c5edff1 | ||
|
|
83ed31dedd | ||
|
|
6f7c0ce3a6 | ||
|
|
c32eebe48e | ||
|
|
d5d87219b8 | ||
|
|
57ad272560 | ||
|
|
8fd18aa081 | ||
|
|
b8d38ac90c | ||
|
|
d33f642973 | ||
|
|
e0db15f3e2 | ||
|
|
0259e75b22 | ||
|
|
ea08838a4e | ||
|
|
ba2b95676a | ||
|
|
685818f394 | ||
|
|
f0ac15f3d7 | ||
|
|
f02baa2b96 | ||
|
|
3250c5d245 | ||
|
|
848edb9d29 | ||
|
|
1fcbdd8ce2 | ||
|
|
482da119b3 | ||
|
|
60d55aad21 | ||
|
|
6caed9d976 | ||
|
|
e87fb25abc | ||
|
|
921926c1a1 | ||
|
|
26b21bb048 | ||
|
|
4134cf11d9 | ||
|
|
f064e1dc8c | ||
|
|
6748cbc534 | ||
|
|
4bda2cf57e | ||
|
|
7d04d7fb4b | ||
|
|
6b062afc99 | ||
|
|
ecc1713d2c | ||
|
|
b559e493bd | ||
|
|
6dd15fee0b | ||
|
|
908e370dd4 | ||
|
|
b16be347a8 | ||
|
|
aa71e2e906 | ||
|
|
2f74f5a6ff | ||
|
|
45bb857f63 | ||
|
|
6805bf2198 | ||
|
|
0e02452664 | ||
|
|
9b2169bec0 | ||
|
|
437672b234 | ||
|
|
4d1bb79e18 | ||
|
|
c9e635da50 | ||
|
|
9ec72a63c6 | ||
|
|
8e95c26356 | ||
|
|
8ebe3ba0c1 | ||
|
|
219a99e008 | ||
|
|
81b8d4607b | ||
|
|
67305374e4 | ||
|
|
d3064e07d6 | ||
|
|
1a44ac3b22 | ||
|
|
32893b6ef5 | ||
|
|
3dfb1dc049 | ||
|
|
e90589b373 | ||
|
|
35fba691ba | ||
|
|
848e13a416 | ||
|
|
e7f23650ae | ||
|
|
1686e08f9b | ||
|
|
aaaa441486 | ||
|
|
9f81570bad | ||
|
|
f0cfabe15a | ||
|
|
c498726503 | ||
|
|
9ba8eee958 | ||
|
|
8f5c8b1878 | ||
|
|
6d0fab910c | ||
|
|
d3af97a464 | ||
|
|
af577d5560 | ||
|
|
e9fac70938 | ||
|
|
af3bef59c6 | ||
|
|
05df7a59dd | ||
|
|
239a647e7f | ||
|
|
418cb4a577 | ||
|
|
e2d2ce39ea | ||
|
|
34c67e792c | ||
|
|
575e2e2860 | ||
|
|
564835a7ae | ||
|
|
121948b338 | ||
|
|
28bd50b64c | ||
|
|
01075f3a0c | ||
|
|
86940b005e | ||
|
|
c7c9ec3009 | ||
|
|
d0ddf18acb | ||
|
|
52a667b3b7 | ||
|
|
f06eadf45c | ||
|
|
638a4e21e7 | ||
|
|
625139b109 | ||
|
|
462af4cbbb | ||
|
|
93e03fc0eb | ||
|
|
3f0b9a6e25 | ||
|
|
d857c53b0a | ||
|
|
e5570c0448 | ||
|
|
59e63bb760 | ||
|
|
5e27247b43 | ||
|
|
525fdbc3d6 | ||
|
|
9269679ecf | ||
|
|
64183aced6 | ||
|
|
e42d19e721 | ||
|
|
a24fbec07b | ||
|
|
8ac11cdeb1 | ||
|
|
afc416882e | ||
|
|
2934391ec8 | ||
|
|
3157694184 | ||
|
|
3b135c4e35 | ||
|
|
ecb3b5f092 | ||
|
|
1a138d00d8 | ||
|
|
39dd2ab15e | ||
|
|
096ceac80a | ||
|
|
37060f024f | ||
|
|
0149a59692 | ||
|
|
cb66449845 | ||
|
|
605b2f15a2 | ||
|
|
b2d086567e | ||
|
|
dccbca0dd1 | ||
|
|
e060bbd6a4 | ||
|
|
4246755bb6 | ||
|
|
65dd9fe4a2 | ||
|
|
73caa470ea | ||
|
|
2e2b5c38e7 | ||
|
|
42aa45af66 | ||
|
|
4e5f6db3f5 | ||
|
|
d1f5ef88d9 | ||
|
|
15dcb60bd9 | ||
|
|
204ec559d8 | ||
|
|
410a91bd82 | ||
|
|
49132caca5 | ||
|
|
947fdc191e | ||
|
|
59988113f5 | ||
|
|
ce93b81ec4 | ||
|
|
8c4f1f7646 | ||
|
|
441ba75304 | ||
|
|
30a868bf91 | ||
|
|
17804764ea | ||
|
|
33dddfaccd | ||
|
|
b8181c9464 | ||
|
|
5c0fae6db2 | ||
|
|
b17e62a989 | ||
|
|
d3b3cab24d | ||
|
|
93fabd0297 | ||
|
|
79e1b61dde | ||
|
|
013a775783 | ||
|
|
f5f9e4eeb0 | ||
|
|
0b6b093a1c | ||
|
|
c25b023747 | ||
|
|
3438b24bd5 | ||
|
|
dab8a27687 | ||
|
|
49a23f1361 | ||
|
|
65290d481b | ||
|
|
5c421387e0 | ||
|
|
f7b5ebe82c | ||
|
|
5167e4e89d | ||
|
|
7e880863ce | ||
|
|
b1f189229a | ||
|
|
12e37ef8d3 | ||
|
|
c0cf131b7d | ||
|
|
451482d8f6 | ||
|
|
99d37f4dd6 | ||
|
|
c8e55a0035 | ||
|
|
ee0e316fe3 | ||
|
|
c65cd01679 | ||
|
|
3f8566d63b | ||
|
|
212e597d17 | ||
|
|
7c64b2c3df | ||
|
|
add4502925 | ||
|
|
8b5f675a3c | ||
|
|
92d4ef9801 | ||
|
|
e317aa255a | ||
|
|
7fe56fbdf8 | ||
|
|
bf8a88ccb0 | ||
|
|
6e1ce596bb | ||
|
|
f9ec0f4bde | ||
|
|
7dafd41d28 | ||
|
|
593d95ceff | ||
|
|
6282826daf | ||
|
|
6824561b28 | ||
|
|
2ec8c81950 | ||
|
|
cf9290cc02 | ||
|
|
a6a74cdc5b | ||
|
|
7b8142318e | ||
|
|
015f65a133 | ||
|
|
86fd956aa1 | ||
|
|
7c1b4aca2e | ||
|
|
f3d956033d | ||
|
|
d56eddda0c | ||
|
|
9afb95f720 | ||
|
|
757b619c0c | ||
|
|
711dcec236 | ||
|
|
328b73c20d | ||
|
|
af4f9b2af0 | ||
|
|
f406b1abcb | ||
|
|
3e64f0d0f4 | ||
|
|
265a474130 | ||
|
|
cb37bdf540 | ||
|
|
6ce8f96a55 | ||
|
|
dede5505a5 | ||
|
|
77e04724cb | ||
|
|
5ead6f9148 | ||
|
|
0ba6681d1c | ||
|
|
5903f6ea17 | ||
|
|
beecf68502 | ||
|
|
3ccf8b40fd | ||
|
|
6228a90011 | ||
|
|
a150a9c000 | ||
|
|
589b2788bf | ||
|
|
664c60f987 | ||
|
|
18c28f80a1 | ||
|
|
82fedcad01 | ||
|
|
96553e567a | ||
|
|
ff9ed4169c | ||
|
|
b150f580c5 | ||
|
|
6a6c9b9f45 | ||
|
|
3ac22d8413 | ||
|
|
e54b3aa136 | ||
|
|
591392e9c4 | ||
|
|
690097be50 | ||
|
|
17736838bd | ||
|
|
9441cca678 | ||
|
|
09b759f64e | ||
|
|
7773be02f1 | ||
|
|
5e676f8477 | ||
|
|
d73e66c21e | ||
|
|
22a15ace59 | ||
|
|
084cb7a3db | ||
|
|
03ee2b8c8b | ||
|
|
2666cee318 | ||
|
|
c69a6d4115 | ||
|
|
4afcfebb13 | ||
|
|
3b56dc1824 | ||
|
|
f3074c4dba | ||
|
|
b44a2bdd2a | ||
|
|
cfead3340e | ||
|
|
3aef4bd4a0 | ||
|
|
d8354ece0c | ||
|
|
e7dd78f9c8 | ||
|
|
38fb711bec | ||
|
|
9d98508a50 | ||
|
|
a0d3f87753 | ||
|
|
947a024528 | ||
|
|
fc18c78f1e | ||
|
|
9042752335 | ||
|
|
6c7462b127 | ||
|
|
21b0c02850 | ||
|
|
4f45c32ccd | ||
|
|
c39bc30882 | ||
|
|
a71c452e10 | ||
|
|
0aa71e3d53 | ||
|
|
a6d31f8b74 | ||
|
|
703a6f0ce1 | ||
|
|
4da88da373 | ||
|
|
ad978f02ea |
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Configuration options: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Enable version updates for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
# Directory must be set to "/" to check for workflow files in .github/workflows
|
||||
directory: "/"
|
||||
# Check for updates to GitHub Actions once a week
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
open-pull-requests-limit: 2
|
||||
104
.github/workflows/check_links.yml
vendored
Normal file
104
.github/workflows/check_links.yml
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
name: Picture Health
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '.github/workflows/check_links.yml'
|
||||
- '**.xml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '.github/workflows/check_links.yml'
|
||||
- '**.xml'
|
||||
pull_request_review:
|
||||
types: [ submitted ]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Runs at the start of each month (UTC)
|
||||
- cron: '0 0 1 * *'
|
||||
|
||||
jobs:
|
||||
check_urls:
|
||||
# Do not run the scheduled workflow on forks
|
||||
if: ( github.event_name != 'schedule' || github.repository_owner == 'Cockatrice' )
|
||||
|
||||
name: Check image links
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Restore cache
|
||||
- name: Restore lychee cache
|
||||
id: restore-cache
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
# Extract picture URLs from tokens.xml
|
||||
- name: Extract URLs
|
||||
id: tokens_pic_urls
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
args: --dump -- tokens.xml
|
||||
output: url_list.md
|
||||
jobSummary: false
|
||||
|
||||
# Check dumped URLs
|
||||
- name: Check token art URLs
|
||||
if: steps.tokens_pic_urls.outcome == 'success'
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
args: --no-progress --require-https --cache --max-cache-age 8h -- url_list.md
|
||||
jobSummary: true
|
||||
|
||||
# List helpful stats on missing pictures
|
||||
- name: List missing image link counts
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
echo "❌ **Missing Image Links (<kbd>master</kbd> Branch)**"
|
||||
echo "| Count | Description |"
|
||||
echo "|------:|:------------|"
|
||||
echo "| [](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml) | Tokens with missing \`set\` element |"
|
||||
echo "| [](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml) | Set elements with missing picURL attribute (\`<set>\`) |"
|
||||
echo "| [](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml) | Set elements with empty picURL value (\`<set picURL=\"\">\`) |"
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Analyse extracted links (1/2)
|
||||
- name: List duplicated image links
|
||||
if: steps.tokens_pic_urls.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
echo "🪞 **Duplicated Image Links**"
|
||||
echo "| Count | Image URL |"
|
||||
echo "|------:|:----------|"
|
||||
# Remove empty lines | trim trailing integers (Scryfall) | sort | count and list only duplicates with numbers | sort descending | print md table
|
||||
grep . url_list.md | sed 's/\.jpg?.*/.jpg/' | sort | uniq -cd | sort -nr | awk '{printf "| %5s | %s |\n", $1, $2}'
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Analyse extracted links (2/2)
|
||||
- name: List image hosting sources
|
||||
if: steps.tokens_pic_urls.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
echo "📶 **Image Hosting Statistics**"
|
||||
echo "| Count | Domain |"
|
||||
echo "|------:|:-------|"
|
||||
# Extract 3rd field (domain name) | remove empty lines | sort | count and list with numbers | sort descending | print md table
|
||||
awk -F/ '{print $3}' url_list.md | grep . | sort | uniq -c | sort -nr | awk '{printf "| %5s | %s |\n", $1, $2}'
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Always save cache
|
||||
- name: Save lychee cache
|
||||
uses: actions/cache/save@v5
|
||||
if: always()
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||
66
.github/workflows/update_links.yml
vendored
Normal file
66
.github/workflows/update_links.yml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
name: Image URLs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# runs in the middle of each month
|
||||
- cron: '0 0 15 * *'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '.github/workflows/update_links.yml'
|
||||
- 'scripts/update_image_links.py'
|
||||
|
||||
jobs:
|
||||
update_links:
|
||||
# Do not run the scheduled workflow on forks
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||
|
||||
name: Update links
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Query Scryfall API and update picURLs
|
||||
shell: bash
|
||||
working-directory: ./scripts
|
||||
run: python3 update_image_links.py ../tokens.xml --inplace
|
||||
|
||||
- name: Create pull request
|
||||
if: github.event_name != 'pull_request'
|
||||
id: create_pr
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
add-paths: |
|
||||
tokens.xml
|
||||
commit-message: Update image links
|
||||
# author is the owner of the commit
|
||||
author: github-actions <github-actions@github.com>
|
||||
branch: ci-update_image_links
|
||||
delete-branch: true
|
||||
title: 'Update image links'
|
||||
body: |
|
||||
Update all picURLs in the `tokens.xml` file via Scryfall API.
|
||||
|
||||
---
|
||||
*This PR is automatically generated and updated by the workflow at `.github/workflows/update_links.yml`. Review [action runs][1].*<br>
|
||||
|
||||
[1]: https://github.com/Cockatrice/Magic-Token/actions/workflows/update_links.yml?query=branch%3Amaster
|
||||
labels: |
|
||||
CI
|
||||
draft: false
|
||||
|
||||
- name: PR Status
|
||||
if: github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
env:
|
||||
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }}
|
||||
run: |
|
||||
if [[ "$STATUS" == "none" ]]; then
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY
|
||||
84
.github/workflows/update_version.yml
vendored
Normal file
84
.github/workflows/update_version.yml
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
name: Update version on tokens.xml changes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'tokens.xml'
|
||||
- 'version.txt'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains(github.ref, '/master') }}
|
||||
|
||||
jobs:
|
||||
update_version:
|
||||
if: "!startsWith(github.event.head_commit.message, 'Update version to ')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get previous version
|
||||
id: get_versions
|
||||
shell: bash
|
||||
run: |
|
||||
old_version="$(<version.txt)"
|
||||
current_date="$(date --utc +%Y%m%d)"
|
||||
echo "old_version=$old_version" >> $GITHUB_OUTPUT
|
||||
echo "current_date=$current_date" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Compute new version
|
||||
id: compute_version
|
||||
shell: python
|
||||
run: |
|
||||
import os, re
|
||||
old_version = "${{steps.get_versions.outputs.old_version}}"
|
||||
current_date = "${{steps.get_versions.outputs.current_date}}"
|
||||
new_version = current_date
|
||||
if old_version.startswith(current_date):
|
||||
match = re.search(r"[a-z]+", old_version)
|
||||
if match is None:
|
||||
suffix = "a" # the first a is actually hidden
|
||||
else:
|
||||
suffix = match[0]
|
||||
charlist = []
|
||||
rev = reversed(suffix)
|
||||
for char in rev:
|
||||
if char == "z":
|
||||
charlist.append("a")
|
||||
else:
|
||||
nextchar = chr(ord(char) + 1)
|
||||
charlist.append(nextchar)
|
||||
charlist.extend(rev)
|
||||
break
|
||||
else:
|
||||
charlist.append("a")
|
||||
new_version += "".join(reversed(charlist))
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as envFile:
|
||||
print(f"new_version={new_version}", file=envFile)
|
||||
|
||||
- name: Update version in files
|
||||
shell: bash
|
||||
env:
|
||||
VERSION_OLD: ${{steps.get_versions.outputs.old_version}}
|
||||
VERSION_NEW: ${{steps.compute_version.outputs.new_version}}
|
||||
run: |
|
||||
echo "Updating version from $VERSION_OLD to $VERSION_NEW"
|
||||
tag="sourceVersion"
|
||||
sed -i "s?<$tag>.*</$tag>?<$tag>$VERSION_NEW</$tag>?" tokens.xml
|
||||
echo "$VERSION_NEW" >version.txt
|
||||
|
||||
- name: Commit and push changes
|
||||
shell: bash
|
||||
env:
|
||||
VERSION_NEW: ${{steps.compute_version.outputs.new_version}}
|
||||
run: |
|
||||
echo "::notice::Triggering commit: https://github.com/${GITHUB_REPOSITORY}/commit/$GITHUB_SHA"
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git add tokens.xml version.txt
|
||||
git commit -m "Update version to $VERSION_NEW" -m "Bump triggered by commit $GITHUB_SHA"
|
||||
git push
|
||||
commit_hash="$(git rev-parse HEAD)"
|
||||
echo "::notice::Pushed commit: https://github.com/${GITHUB_REPOSITORY}/commit/$commit_hash"
|
||||
11
README.md
11
README.md
|
|
@ -1,10 +1,9 @@
|
|||
[](https://discord.gg/3Z9yzmA) [](https://gitter.im/Cockatrice/Magic-Spoiler)
|
||||
[](https://discord.gg/3Z9yzmA)
|
||||
|
||||
Magic-Token [](https://github.com/Cockatrice/Magic-Token/blob/master/tokens.xml)
|
||||
Magic-Token [](https://github.com/Cockatrice/Magic-Token/blob/master/tokens.xml) [](https://github.com/Cockatrice/Magic-Token/actions/workflows/check_links.yml?query=branch%3Amaster)
|
||||
=================
|
||||
|
||||
This repo contains tokens information in [Cockatrice](https://github.com/cockatrice/cockatrice)'s XML card database format for Magic: The Gathering
|
||||
This repo contains token information in [Cockatrice](https://github.com/cockatrice/cockatrice)'s [XML card database format](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Cards-&-Sets#to-add-your-own-custom-cards-follow-these-steps) for Magic: The Gathering.<br />
|
||||
It describes [)](https://github.com/Cockatrice/Magic-Token/blob/master/tokens.xml) individual tokens with linked art and the relation to their creators.
|
||||
|
||||
For normal tokens, save the file found here: [**tokens.xml**](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml)
|
||||
|
||||
For tokens from the Theros block challege decks, save the file found here: [**challenge_tokens.xml**](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/challenge_tokens.xml)
|
||||
Cockatrice pulls its copy of the token file from here: [**tokens.xml**](https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml)
|
||||
|
|
|
|||
|
|
@ -1,707 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cockatrice_carddatabase version="3">
|
||||
<sets>
|
||||
<set>
|
||||
<name>CTH</name>
|
||||
<longname>Challenge the Horde</longname>
|
||||
<settype>Challenge</settype>
|
||||
<releasedate>2011-09-05</releasedate>
|
||||
</set>
|
||||
</sets>
|
||||
<cards>
|
||||
<card>
|
||||
<name>Strike the Weak Spot</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66612-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Destroy target Head. If that Head was elite, the Hydra take an extra turn after this one.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Mogis's Chosen</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66703-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Minotaur</type>
|
||||
<pt>5/4</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Mogis's Chosen enters the battlefield tapped. Mogis's Chosen attacks each turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Rip to Pieces</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66809-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>At the beginning of combat this turn, each Reveler deals 1 damage to each player and each creature those players control.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Descend on the Prey</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66707-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Whenever a Minotaur attacks this turn, it gains first strike until end of turn and must be blocked this turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Vanquisher</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66786-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Your starting hand size is increased by one. Your maximum hand size is increased by one.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The General</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66724-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Exile The General: Creature you control get +1/+1 until end of turn. Untap them.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Minotaur Younghorn</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66702-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Minotaur</type>
|
||||
<pt>2/2</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Haste, Minotaur Younghorn attacks each turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Dance of Flame</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66812-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Enchantment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Whenever a Reveler attacks, Dance of Flame deals 1 damage to each player.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Provider</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66764-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Exile The Provider: Put two +1/+1 counters on target creature you control. You gain life equal to that creature's toughness.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Refreshing Elixir</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66714-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Artifact</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>At the beginning of the Horde's precombat main phase, reveal an additional card from the top of the Horde's library. The Horde casts that card.
|
||||
Hero's Reward — When Refreshing Elixir is put into a graveyard from anywhere, each player gains 5 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Rollicking Throng</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66803-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Human Reveler</type>
|
||||
<pt>1/3</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>When Rollicking Throng enters the battlefield, reveal the top card of Xenagos's library and Xenagos casts that card. (This ability doesn't trigger as the game begins.)</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Impulsive Destruction</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66807-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Each player may sacrifice an artifact or enchantment. Impulsive Destruction deals 3 damage to each player who didn't sacrifice a permanent this way.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Unquenchable Fury</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66710-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Each Minotaur can't be blocked this turn except by two or more creatures.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Maddened Oread</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66814-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Enchantment Creature — Nymph Reveler</type>
|
||||
<pt>4/2</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>As long as five or more Revelers are on the battlefield, Maddened Oread attacks each turn if able. Hero's Reward - When Maddened Oread leaves the battlefield, each player gains 3 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Xenagos's Scorn</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66810-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Xenagos Ascended gains trample until end of turn and attacks this turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Shrieking Titan Head</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66604-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Elite Creature — Head</type>
|
||||
<pt>0/8</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>At the beginning of the Hydra's end step, each player discards a card.
|
||||
Hero's Reward — When Savage Vigor Head leaves the battlefield, each player gains 4 life and draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Xenagos's Strike</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66811-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Xenagos's Strike deals 4 damage to each player.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Snapping Fang Head</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66605-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Elite Creature — Head</type>
|
||||
<pt>0/8</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>At the beginning of the Hydra's end step, Snapping Fang Head deals 1 damage to each player.
|
||||
Hero's Reward — When Savage Vigor Head leaves the battlefield, each player gains 4 life and draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Massacre Totem</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66712-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Artifact</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>At the beginning of the Horde's precombat main phase, reveal an additional card from the top of the Horde's library. The Horde casts that card. Hero's Reward — When Altar of Mogis is put into a graveyard from anywhere, the Horde sacrifies two Minotaurs.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Spear of the General</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66827-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero Artifact — Equipment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Equipped creature gets +2/+0 and has first strike. Equip {2}</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Minotaur Goreseeker</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66701-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Minotaur</type>
|
||||
<pt>3/2</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Haste, Minotaur Goreseeker attacks each turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Swallow the Hero Whole</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66613-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Each player exiles a creature he or she controls. Until the Hydra's next turn, when a Head leaves the battlefield, return the exiled cards to the battlefield under their owners' control.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Philosopher</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66631-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{2}, {T}: Tap target creature.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Disorienting Glower</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66606-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Player's can't cast spells until the Hydra's next turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Destined</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66878-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Spells you cast that target a creature you control cost {2} less to cast</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Altar of Mogis</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66711-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Artifact</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>At the beginning of the Horde's precombat main phase, reveal an additional card from the top of the Horde's library. The Horde casts that card. Hero's Reward — When Massacre Totem is put into a graveyard from anywhere, put the top seven cards of the Horde's library into its graveyard.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Warrior</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66651-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{T}: Target creature you control gains haste until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Touch of the Horned God</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66709-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Whenever a Minotaur attacks this turn, it gains deathtouch until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Ravenous Brute Head</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66602-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Elite Creature — Head</type>
|
||||
<pt>0/6</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Hero's Reward — When Ravenous Brute Head leaves the battlefield, each player gains 2 life and draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Savant</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66734-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Exile The Savant: Tap all creatures your opponent control. Those creatures don't untap during their controllers' next untap steps.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Dance of Panic</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66813-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Enchantment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>As long as five or more Revelers are on the battlefield, all Revelers have haste and attack each turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Savage Vigor Head</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66603-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Elite Creature — Head</type>
|
||||
<pt>0/8</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>At the beginning of the Hydra's end step, reveal the top card of the Hydra's library and the Hydra casts that card. Hero's Reward — When Savage Vigor Head leaves the battlefield, each player gains 4 life and draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Intervention of Keranos</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66708-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>At the beginning of combat this turn, Intervention of Keranos deals 3 damage to each creature.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Distract the Hydra</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66607-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Each player may sacrifice a creature. Each player who sacrificed a creature this way chooses a Head and taps it. Each player who didn't sacrifice a creature loses 3 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Slayer</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66683-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>You start the game with an additional 7 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Impulsive Return</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66808-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Return two cards named Ecstatic Piper from Xenagos's graveyard to the battlefield. At the beginning of combat this turn, Impulsive Return deals damage to each player equal to the number of Revelers on the battlefield.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Phoberos Reaver</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66704-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Minotaur</type>
|
||||
<pt>2/3</pt>
|
||||
<tablerow>0</tablerow>
|
||||
<text>Haste, Phoberos Reaver attacks each turn if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Bow of the Hunter</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66867-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero Artifact — Equipment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Equipped creature has "{T}: This creature deals 2 damage to target creature of player." Equip {2}</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Torn Between Heads</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66614-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Tap up to two Heads. They don't untap during the Hydra's next untap step. Torn Between Heads deals 5 damage to each player.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Noxious Hydra Breath</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66611-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Choose one — Noxious Hydra Breath deals 5 damage to each player; or destroy each tapped non-Head creature.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Vitality Salve</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66715-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Artifact</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>At the beginning of the Horde's precombat main phase, reveal an additional card from the top of the Horde's library. The Horde casts that card. Hero's Reward — When Vitality Salve is put into a graveyard from anywhere, each player returns a creature card from his or her graveyard to the battlefield.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Lash of the Tyrant</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66847-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero Artifact — Equipment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Equipped creature gets +1/+2 and has deathtouch. Equip {2}</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Reckless Minotaur</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66705-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Minotaur</type>
|
||||
<pt>4/1</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Haste, Reckless Minotaur attacks each turn if able. At the beginning of the end step, destroy Reckless Minotaur.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Hydra's Impenetrable Hide</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66609-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Each Head gains indestructible until the end of the Hydra's next turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Axe of the Warmonger</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66857-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero Artifact — Equipment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Equipped creature gets +2/+1 and has haste. Equip {2}</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Harvester</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66672-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{T}: Draw a card, then discard a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Hydra Head</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66601-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Head</type>
|
||||
<pt>0/3</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Hero's Reward — When Hydra Head leaves the battlefield, each player gains 2 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Explorer</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66775-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>You may play an additional land on each of your turns.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Grown from the Stump</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66608-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Put exactly two cards named Hydra Head onto the battlefield from the Hydra's graveyard. If you can't, reveal cards from the top of the Hydra's library until you reveal a Head card. Put that card onto the battlefield and the rest into the Hydra's graveyard.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Warmonger</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66754-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Exile The Warmonger: Creature you control get +2/+0 and gain haste until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Unified Lunge</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66615-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Unified Lunge deals X damage to each player, where X is the number of Heads on the battlefield.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Tyrant</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66744-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>0</tablerow>
|
||||
<text>Exile The Tyrant: Creature your opponents control get -1/-1 until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Champion</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66889-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{2}, {T}, Exile The Champion: Search your library for a legendary artifact card that isn't a creature, reveal it, and put it onto the battlefield. Then shuffle your library.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Plundered Statue</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66713-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Artifact</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>At the beginning of the Horde's precombat main phase, reveal an additional card from the top of the Horde's library. The Horde casts that card. Hero's Reward — When Plundered Statue is put into a graveyard from anywhere, each player draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Protector</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66621-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{T}: Prevent the next 1 damage that would be dealt to target creature or player this turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Impulsive Charge</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66806-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>At the beginning of combat this turn, all Revelers gain haste until end of turn and attack this combat if able.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Pheres-Band Revelers</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66802-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Centaur Reveler</type>
|
||||
<pt>4/4</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Hero's Reward - When Pheres-Band Revelers leaves the battlefield, each player draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Hunter</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66661-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{T}: Target creature you control gets +1/+1 until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Ecstatic Piper</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66801-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Satyr Reveler</type>
|
||||
<pt>2/2</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>When Ecstatic Piper enters the battlefield, Xenagos Ascended attacks this turn if able Hero's Reward - When Ecstatic Piper leaves the battlefield, each player gains 2 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Consuming Rage</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/BNG/-66706-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>Whenever a Minotaur attacks this turn, it gets +2/+0 until end of turn. Destroy that creature at end of combat.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Xenagos Ascended</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66815-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Legendary Enchantment Creature — God</type>
|
||||
<pt>6/5</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Xenagos Ascended can't leave the battlefield as long as a Reveler is on the battlefield. When Xenagos Ascended leaves the battlefield, each player wins the game.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Wild Maenads</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66805-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Human Reveler</type>
|
||||
<pt>3/1</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>First strike, Hero's Reward - When Wild Maenads leaves the battlefield, each player gains 3 life.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Serpent Dancers</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/-66804-hr.jpg">TK</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Creature — Human Reveler</type>
|
||||
<pt>2/4</pt>
|
||||
<tablerow>2</tablerow>
|
||||
<text>Deathtouch, Hero's Reward - When Serpent Dancers leaves the battlefield, each player draws a card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Neck Tangle</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66610-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Sorcery</type>
|
||||
<tablerow>3</tablerow>
|
||||
<text>If there are five or more Heads on the battlefield, tap two of them and they don't untap during the Hydra's next untap step. Otherwise, reveal the top card of the Hydra's library and the Hydra casts that card.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>The Avenger</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/THS/-66641-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>{3}, {T}: Target creature you control gains deathtouch until end of turn.</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Cloak of the Philosopher</name>
|
||||
<set picURL="http://deckmaster.info/images/cards/JOU/-66837-hr.jpg">CTH</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Hero Artifact — Equipment</type>
|
||||
<tablerow>1</tablerow>
|
||||
<text>Equipped creature gets +1/+1. {2}: Untap equipped creature. Equip {2}</text>
|
||||
</card>
|
||||
<card>
|
||||
<name>Garruk the Slayer</name>
|
||||
<set picURL="http://s3.gatheringmagic.com.s3.amazonaws.com/uploads/2014/06/23/garruk-the-slayer.png">M15</set>
|
||||
<color></color>
|
||||
<manacost></manacost>
|
||||
<cmc>0</cmc>
|
||||
<type>Planeswalker - Garruk</type>
|
||||
<tablerow>2</tablerow>
|
||||
<text>0: Put a 2/2 green Wolf creature token onto the battlefield.
|
||||
+4: Target Wolf creature gets +1/+0 and gains deathtouch until end of turn.
|
||||
-10: Destroy target creature. Put loyalty counters on Garruk the Slayer equal to that creature's toughness.
|
||||
-25: Destroy all creatures Garruk the Slayer doesn't control.</text>
|
||||
<loyalty>20</loyalty>
|
||||
</card>
|
||||
</cards>
|
||||
</cockatrice_carddatabase>
|
||||
13
scripts/README.md
Normal file
13
scripts/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
## Scripts
|
||||
|
||||
[`update_image_links`](https://github.com/Cockatrice/Magic-Token/blob/master/scripts/update_image_links.py)
|
||||
This script changes the links from the old CDN on c1.scryfall.com to the new CDN on cards.scryfall.io.
|
||||
It reads a tokens.xml file containing CDN picURLs of either version, makes requests to the Scryfall API
|
||||
to receive the most up-to-date URLs for each token, and generates an output file.
|
||||
`--inplace / -i` will update the input file
|
||||
`--output / -o` will create a new file of your choice
|
||||
|
||||
Basic call to run the script:
|
||||
```
|
||||
python3 update_image_links.py tokens.xml -i
|
||||
```
|
||||
185
scripts/update_image_links.py
Normal file
185
scripts/update_image_links.py
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
"""
|
||||
This script parses a token.xml file, collects the picURLs of cards within, and replaces
|
||||
the links to Scryfall images with up-to-date URLs by querying Scryfall's API.
|
||||
"""
|
||||
|
||||
from xml.sax import saxutils, make_parser, handler
|
||||
from urllib.parse import urlsplit
|
||||
from urllib.request import Request, urlopen
|
||||
import itertools
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import tempfile
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
SCRYFALL_MAX_LIST_SIZE = 75
|
||||
|
||||
def cards_collection(identifiers):
|
||||
"""
|
||||
Get information about a set of cards using the Scryfall API.
|
||||
|
||||
This simply returns a list of dictionaries representing the Card objects as
|
||||
returned by the /card/collection Scryfall API.
|
||||
|
||||
If the list of identifiers is larger than Scryfall's API limit,
|
||||
cards_collection automatically splits the list into smaller chunks and
|
||||
makes multiple requests.
|
||||
"""
|
||||
|
||||
start_time = 0
|
||||
n = 0
|
||||
while n < len(identifiers):
|
||||
chunk = identifiers[n:n + SCRYFALL_MAX_LIST_SIZE]
|
||||
print("Requesting chunk {}-{}/{}...".format(n, n + len(chunk), len(identifiers)))
|
||||
n += SCRYFALL_MAX_LIST_SIZE
|
||||
|
||||
payload = json.dumps({'identifiers': chunk}).encode('utf-8')
|
||||
req = Request('https://api.scryfall.com/cards/collection', payload,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
# Rate limiting
|
||||
cur_time = time.time()
|
||||
delta_time = cur_time - start_time
|
||||
if delta_time < 0.1:
|
||||
time.sleep(0.1 - delta_time)
|
||||
start_time = time.time()
|
||||
|
||||
with urlopen(req) as f:
|
||||
list_obj = json.load(f)
|
||||
assert not list_obj.get('has_more', False)
|
||||
assert 'warnings' not in list_obj
|
||||
|
||||
yield from list_obj['data']
|
||||
|
||||
def parse_picurl(picurl):
|
||||
"""
|
||||
Parse a Scryfall picURL into its components.
|
||||
|
||||
The Scryfall picURL must be in one of those forms:
|
||||
|
||||
- https://c1.scryfall.com/file/scryfall-cards/<version>/<face>/*/*/<uuid>.jpg
|
||||
- https://cards.scryfall.io/<version>/<face>/*/*/<uuid>.jpg
|
||||
|
||||
If it is, a dictionary with keys 'uuid', 'version' and 'face' is returned.
|
||||
Otherwise, an empty dictionary is returned.
|
||||
"""
|
||||
|
||||
obj = {}
|
||||
urlinfo = urlsplit(picurl)
|
||||
if urlinfo.netloc == 'c1.scryfall.com':
|
||||
parts = urlinfo.path.split('/')
|
||||
obj['version'] = parts[3]
|
||||
obj['face'] = parts[4]
|
||||
obj['uuid'] = parts[-1].rsplit('.')[0]
|
||||
elif urlinfo.netloc == 'cards.scryfall.io':
|
||||
parts = urlinfo.path.split('/')
|
||||
obj['version'] = parts[1]
|
||||
obj['face'] = parts[2]
|
||||
obj['uuid'] = parts[-1].rsplit('.')[0]
|
||||
return obj
|
||||
|
||||
class URLCollector(handler.ContentHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.urls = []
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
if name == 'set' and 'picURL' in attrs:
|
||||
obj = parse_picurl(attrs['picURL'])
|
||||
if obj:
|
||||
assert 'uuid' in obj
|
||||
self.urls.append(obj)
|
||||
|
||||
class URLRewriter(handler.LexicalHandler, saxutils.XMLGenerator):
|
||||
def __init__(self, images, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._images = images
|
||||
self.started = False
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
self.started = True
|
||||
|
||||
if name == 'set' and 'picURL' in attrs:
|
||||
obj = parse_picurl(attrs['picURL'])
|
||||
if 'uuid' in obj and obj['uuid'] in self._images:
|
||||
new_url = self._images[obj['uuid']][obj['face']][obj['version']]
|
||||
if parse_picurl(new_url) != obj:
|
||||
raise RuntimeError(
|
||||
"URL `{}` was rewritten to `{}` that no longer resolves to the same card (hint: update parse_picurl).".format(
|
||||
attrs['picURL'], new_url))
|
||||
attrs = dict(attrs, picURL=new_url)
|
||||
|
||||
super().startElement(name, attrs)
|
||||
|
||||
def endDocument(self):
|
||||
super().endDocument()
|
||||
self._write('\n')
|
||||
|
||||
def comment(self, content):
|
||||
self.ignorableWhitespace('<!--{}-->{}'.format(content, '' if self.started else '\n'))
|
||||
|
||||
def collect_urls(fname):
|
||||
parser = make_parser()
|
||||
uc = URLCollector()
|
||||
parser.setContentHandler(uc)
|
||||
parser.parse(fname)
|
||||
return uc.urls
|
||||
|
||||
def rewrite_urls(fname, images, *, out=None):
|
||||
parser = make_parser()
|
||||
ur = URLRewriter(images, out=out, encoding='UTF-8')
|
||||
parser.setContentHandler(ur)
|
||||
parser.setProperty(handler.property_lexical_handler, ur)
|
||||
parser.parse(fname)
|
||||
|
||||
def main(fname, *, out=None):
|
||||
urls = collect_urls(fname)
|
||||
identifiers = {obj['uuid'] for obj in urls}
|
||||
identifiers = [{'id': uuid} for uuid in identifiers]
|
||||
|
||||
images = {}
|
||||
for card in cards_collection(identifiers):
|
||||
assert card['id'] not in images
|
||||
|
||||
if 'image_uris' in card:
|
||||
images[card['id']] = {'front': card['image_uris']}
|
||||
|
||||
else:
|
||||
assert 'card_faces' in card and len(card['card_faces']) == 2
|
||||
images[card['id']] = {
|
||||
'front': card['card_faces'][0]['image_uris'],
|
||||
'back': card['card_faces'][1]['image_uris'],
|
||||
}
|
||||
|
||||
rewrite_urls(fname, images, out=out)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Helper script to refresh scryfall image URLs')
|
||||
parser.add_argument('filename', nargs='?', default='tokens.xml')
|
||||
|
||||
output_group = parser.add_mutually_exclusive_group(required=True)
|
||||
output_group.add_argument('--output', '-o')
|
||||
output_group.add_argument('--inplace', '-i', action='store_true')
|
||||
|
||||
ns = parser.parse_args()
|
||||
|
||||
if ns.inplace:
|
||||
outpath = pathlib.Path(ns.filename)
|
||||
else:
|
||||
outpath = pathlib.Path(ns.output)
|
||||
fd, temppath = tempfile.mkstemp(dir=outpath.parent)
|
||||
|
||||
try:
|
||||
main(ns.filename, out=os.fdopen(fd, mode='w+b'))
|
||||
os.replace(temppath, outpath)
|
||||
except:
|
||||
os.unlink(temppath)
|
||||
raise
|
||||
14776
tokens.xml
14776
tokens.xml
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
|||
20210713
|
||||
20260307
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user