Compare commits
40 commits
cb10ae5a4b
...
4bdb01eb61
Author | SHA1 | Date | |
---|---|---|---|
4bdb01eb61 | |||
8ae11125ed | |||
|
0c81b64030 | ||
96aabc5552 | |||
9c7bf31e57 | |||
6472da3432 | |||
bbadb3b5ae | |||
115af23e4b | |||
75aab93b57 | |||
85aac42dd2 | |||
97899f3ca2 | |||
c8c389c0b1 | |||
e56fce556e | |||
a3dbd8b76f | |||
3a1821846d | |||
9a930724de | |||
8b3ca83c65 | |||
24a67cf445 | |||
9ed2220be9 | |||
e1edda3041 | |||
7041b75d2f | |||
e9e051b72e | |||
0aee8e76dc | |||
9de0b24931 | |||
c778b5cc82 | |||
c321ff6b15 | |||
|
fb67ee7907 | ||
c94b325b7e | |||
ad66b29a87 | |||
9eec837632 | |||
929a71b1ee | |||
140a35e6a4 | |||
933907cd5f | |||
96613c6626 | |||
618ebb4d50 | |||
f549bf5fef | |||
0a820667b3 | |||
394397bed3 | |||
8cc1c2b8f5 | |||
29e3853867 |
34
Cargo.lock
generated
|
@ -313,7 +313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
|
@ -393,6 +393,12 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "erasable"
|
||||
version = "1.2.1"
|
||||
|
@ -630,9 +636,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.17"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f"
|
||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
|
@ -656,6 +662,12 @@ dependencies = [
|
|||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -799,12 +811,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -932,7 +944,7 @@ version = "0.7.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1145,7 +1157,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "patched_porobot"
|
||||
version = "0.13.0"
|
||||
version = "0.15.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.0",
|
||||
|
@ -2361,9 +2373,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "patched_porobot"
|
||||
version = "0.13.0"
|
||||
version = "0.15.0"
|
||||
authors = ["Stefano Pigozzi <me@steffo.eu>"]
|
||||
edition = "2021"
|
||||
description = "Legends of Runeterra card database utilities and bots"
|
||||
|
|
|
@ -69,6 +69,7 @@ COPY --from=builder \
|
|||
/usr/src/patched_porobot/target/*/release/patched_porobot_matrix \
|
||||
/usr/bin/
|
||||
|
||||
USER ${UID:-1000}:${GID:-1000}
|
||||
ENTRYPOINT []
|
||||
CMD []
|
||||
|
||||
|
|
74
README.md
|
@ -1,7 +1,13 @@
|
|||
# ![](icon.png) Patched Porobot
|
||||
<div align="center">
|
||||
|
||||
![](icon.png)
|
||||
|
||||
# Patched Porobot
|
||||
|
||||
Legends of Runeterra game data crate and chat bots
|
||||
|
||||
</div>
|
||||
|
||||
## Links
|
||||
|
||||
[![Telegram Bot](https://img.shields.io/badge/telegram%20bot-done-success)](https://t.me/patchedporobot)
|
||||
|
@ -9,12 +15,12 @@ Legends of Runeterra game data crate and chat bots
|
|||
[![Discord Bot](https://img.shields.io/badge/discord%20bot-done-success)](https://discord.com/api/oauth2/authorize?client_id=1071989978743193672&scope=applications.commands)
|
||||
|
||||
![Matrix Bot](https://img.shields.io/badge/matrix%20bot-to%20do-inactive)
|
||||
|
||||
![Fediverse Bot](https://img.shields.io/badge/fediverse%20bot-to%20do-inactive)
|
||||
|
||||
[![Crates.io](https://img.shields.io/crates/v/patched_porobot)](https://crates.io/crates/patched_porobot)
|
||||
|
||||
[![Documentation](https://img.shields.io/docsrs/patched_porobot)](https://docs.rs/patched_porobot/0.9.2/patched_porobot/)
|
||||
|
||||
[![Chat](https://img.shields.io/matrix/patched_porobot:ryg.one?server_fqdn=matrix.ryg.one)](https://matrix.to/#/#patched_porobot:ryg.one)
|
||||
[![Documentation](https://img.shields.io/docsrs/patched_porobot)](https://docs.rs/patched_porobot/latest/patched_porobot/)
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
@ -27,6 +33,8 @@ Legends of Runeterra game data crate and chat bots
|
|||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The message the bot sends when it detects an interaction from the user, such as the default /start command.</summary>
|
||||
|
||||
|
@ -34,6 +42,8 @@ Legends of Runeterra game data crate and chat bots
|
|||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The card search prompt that appears when attempting to use the bot in a chat.</summary>
|
||||
|
||||
|
@ -41,6 +51,8 @@ Legends of Runeterra game data crate and chat bots
|
|||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>A search for "poro". Many poros are displayed, and also Braum Level 2, since it contains "poro" in its description.</summary>
|
||||
|
||||
|
@ -48,6 +60,8 @@ Legends of Runeterra game data crate and chat bots
|
|||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The message sent when a card is clicked from the menu. It contains both the card image and a plain text render of the card (for accessibility). Additionally, the flavor text, the artist name, and a link to the full illustration are provided.</summary>
|
||||
|
||||
|
@ -55,12 +69,62 @@ Legends of Runeterra game data crate and chat bots
|
|||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>A search for a deck code, followed by "My new deck". It returns a button saying «Deck "My new deck" with 14 cards»</summary>
|
||||
|
||||
![](media/td-deck.png)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The message sent when the Deck button is clicked from the menu. It contains the name of the deck, followed by the formats it's playable in, its regions, and the cards that it contains. Champions are underlined.</summary>
|
||||
|
||||
![](media/td-eternal.png)
|
||||
|
||||
</details>
|
||||
|
||||
### Discord bot
|
||||
|
||||
<details>
|
||||
<summary>The message the bot sends when an user sends <code>/help</code> command.</summary>
|
||||
|
||||
![](media/ds-help.png)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The message the bot sends when an user sends the <code>/card query: patched porobot</code> command.</summary>
|
||||
|
||||
![](media/ds-card.png)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>The message the sends when an user sends the <code>/deck code: CECQCAQCA4AQIAYKAIAQGLRWAQAQECAPEUXAIAQDAEBQOCIBAIAQEMJYAA name: My new deck</code> command.</summary>
|
||||
|
||||
![](media/ds-deck.png)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## Licenses
|
||||
|
||||
### Riot Games
|
||||
|
||||
Patched Porobot isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.
|
||||
|
||||
### Open Source Licenses
|
||||
|
||||
<details>
|
||||
<summary>List of licenses as output by cargo license</summary>
|
||||
<summary>List of licenses as output by <code>cargo license</code></summary>
|
||||
|
||||
- **(Apache-2.0 OR MIT) AND BSD-3-Clause** (1): encoding_rs
|
||||
- **(MIT OR Apache-2.0) AND Unicode-DFS-2016** (1): unicode-ident
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"vocabTerms": [
|
||||
{
|
||||
"description": "When you summon this, it gets its allegiance bonus if the top card of your deck matches its region.",
|
||||
"description": "When you summon this unit, it gets its allegiance bonus if the top card of your deck matches its region.",
|
||||
"name": "Allegiance",
|
||||
"nameRef": "Allegiance"
|
||||
},
|
||||
|
@ -20,6 +20,11 @@
|
|||
"name": "Reforge",
|
||||
"nameRef": "Reforge"
|
||||
},
|
||||
{
|
||||
"description": "Secretly transforms into another unit before entering play. They won't reveal their true identity to your opponent until they leave play or level up.",
|
||||
"name": "Disguise",
|
||||
"nameRef": "Disguise"
|
||||
},
|
||||
{
|
||||
"description": "Immediately draw 1 of each Ascended ally. For the rest of the game, level 2 Ascended allies are level 3.",
|
||||
"name": "Restore the Sun Disc",
|
||||
|
@ -65,11 +70,21 @@
|
|||
"name": "Round End",
|
||||
"nameRef": "RoundEnd"
|
||||
},
|
||||
{
|
||||
"description": "Units with 8+ power or health",
|
||||
"name": "Titanic",
|
||||
"nameRef": "Titanic"
|
||||
},
|
||||
{
|
||||
"description": "Completely removed from the game. Doesn't cause Last Breath and can't be revived.",
|
||||
"name": "Obliterate",
|
||||
"nameRef": "Obliterate"
|
||||
},
|
||||
{
|
||||
"description": "Shuffle a card into your deck and reduce its cost by 1",
|
||||
"name": "Updraft",
|
||||
"nameRef": "Updraft"
|
||||
},
|
||||
{
|
||||
"description": "This is how much damage the unit can withstand. If it reaches zero, the unit dies.",
|
||||
"name": "Health",
|
||||
|
@ -135,6 +150,11 @@
|
|||
"name": "Round Start",
|
||||
"nameRef": "RoundStart"
|
||||
},
|
||||
{
|
||||
"description": "A unit's spell-like effect that allows enemy reactions. It's elemental!",
|
||||
"name": "Elemental Skill",
|
||||
"nameRef": "ElementalSkill"
|
||||
},
|
||||
{
|
||||
"description": "The opponent in The Path of Champions.",
|
||||
"name": "Foe",
|
||||
|
@ -185,6 +205,11 @@
|
|||
"name": "Manifest",
|
||||
"nameRef": "Manifest"
|
||||
},
|
||||
{
|
||||
"description": "Can be played hidden if you have no other hidden Ambush allies. Hidden allies are 2 cost 2|2s. You may pay a hidden ally's Ambush cost to transform it into its base card.",
|
||||
"name": "Ambush",
|
||||
"nameRef": "Ambush"
|
||||
},
|
||||
{
|
||||
"description": "Transform allies Equipped with Darkin Equipment into their Darkin unit forms. If they are Champions, they Level Up.",
|
||||
"name": "Assimilate",
|
||||
|
@ -252,6 +277,11 @@
|
|||
"name": "Fleeting",
|
||||
"nameRef": "Fleeting"
|
||||
},
|
||||
{
|
||||
"description": "A unit's spell-like effect that allows enemy reactions. It's elemental!",
|
||||
"name": "Elemental Skill",
|
||||
"nameRef": "ElementalSkill"
|
||||
},
|
||||
{
|
||||
"description": "Missing Translation",
|
||||
"name": "Missing Translation",
|
||||
|
@ -423,7 +453,7 @@
|
|||
"nameRef": "Skill"
|
||||
},
|
||||
{
|
||||
"description": "A card triggers its plunder ability when played if you damaged the enemy Nexus this round.",
|
||||
"description": "A card activates its plunder ability when played if you damaged the enemy Nexus this round.",
|
||||
"name": "Plunder",
|
||||
"nameRef": "Plunder"
|
||||
},
|
||||
|
@ -576,115 +606,127 @@
|
|||
"regions": [
|
||||
{
|
||||
"abbreviation": "NX",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-noxus.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-noxus.png",
|
||||
"name": "Noxus",
|
||||
"nameRef": "Noxus"
|
||||
},
|
||||
{
|
||||
"abbreviation": "RYZE",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-ryze.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-ryze.png",
|
||||
"name": "Ryze",
|
||||
"nameRef": "Ryze"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Jhin",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-jhin.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-jhin.png",
|
||||
"name": "Jhin",
|
||||
"nameRef": "Jhin"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Varus",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-varus.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-varus.png",
|
||||
"name": "Varus",
|
||||
"nameRef": "Varus"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Aatrox",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-aatrox.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-aatrox.png",
|
||||
"name": "Aatrox",
|
||||
"nameRef": "Aatrox"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Neeko",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-neeko.png",
|
||||
"name": "Neeko",
|
||||
"nameRef": "Neeko"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Jax",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-jax.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-jax.png",
|
||||
"name": "Jax",
|
||||
"nameRef": "Jax"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Kayn",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-kayn.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-kayn.png",
|
||||
"name": "Kayn",
|
||||
"nameRef": "Kayn"
|
||||
},
|
||||
{
|
||||
"abbreviation": "POROKING",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-poroking.png",
|
||||
"name": "Poro King",
|
||||
"nameRef": "PoroKing"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Evelynn",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-evelynn.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-evelynn.png",
|
||||
"name": "Evelynn",
|
||||
"nameRef": "Evelynn"
|
||||
},
|
||||
{
|
||||
"abbreviation": "Bard",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bard.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bard.png",
|
||||
"name": "Bard",
|
||||
"nameRef": "Bard"
|
||||
},
|
||||
{
|
||||
"abbreviation": "DE",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-demacia.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-demacia.png",
|
||||
"name": "Demacia",
|
||||
"nameRef": "Demacia"
|
||||
},
|
||||
{
|
||||
"abbreviation": "RU",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-runeterra.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-runeterra.png",
|
||||
"name": "Runeterra",
|
||||
"nameRef": "Runeterra"
|
||||
},
|
||||
{
|
||||
"abbreviation": "FR",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-freljord.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-freljord.png",
|
||||
"name": "Freljord",
|
||||
"nameRef": "Freljord"
|
||||
},
|
||||
{
|
||||
"abbreviation": "SI",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-shadowisles.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-shadowisles.png",
|
||||
"name": "Shadow Isles",
|
||||
"nameRef": "ShadowIsles"
|
||||
},
|
||||
{
|
||||
"abbreviation": "MT",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-targon.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-targon.png",
|
||||
"name": "Targon",
|
||||
"nameRef": "Targon"
|
||||
},
|
||||
{
|
||||
"abbreviation": "IO",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-ionia.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-ionia.png",
|
||||
"name": "Ionia",
|
||||
"nameRef": "Ionia"
|
||||
},
|
||||
{
|
||||
"abbreviation": "SH",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-shurima.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-shurima.png",
|
||||
"name": "Shurima",
|
||||
"nameRef": "Shurima"
|
||||
},
|
||||
{
|
||||
"abbreviation": "BW",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bilgewater.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bilgewater.png",
|
||||
"name": "Bilgewater",
|
||||
"nameRef": "Bilgewater"
|
||||
},
|
||||
{
|
||||
"abbreviation": "PZ",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-piltoverzaun.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-piltoverzaun.png",
|
||||
"name": "Piltover & Zaun",
|
||||
"nameRef": "PiltoverZaun"
|
||||
},
|
||||
{
|
||||
"abbreviation": "BC",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bandlecity.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bandlecity.png",
|
||||
"name": "Bandle City",
|
||||
"nameRef": "BandleCity"
|
||||
}
|
||||
|
@ -727,54 +769,64 @@
|
|||
],
|
||||
"sets": [
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set3_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set3_crispmip.png",
|
||||
"name": "Call of the Mountain",
|
||||
"nameRef": "Set3"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set5_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set5_crispmip.png",
|
||||
"name": "Beyond the Bandlewood",
|
||||
"nameRef": "Set5"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set1_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set1_crispmip.png",
|
||||
"name": "Foundations",
|
||||
"nameRef": "Set1"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set2_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set2_crispmip.png",
|
||||
"name": "Rising Tides",
|
||||
"nameRef": "Set2"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set6ab_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set6ab_crispmip.png",
|
||||
"name": "Worldwalker",
|
||||
"nameRef": "Set6"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set4_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set4_crispmip.png",
|
||||
"name": "Empires of the Ascended",
|
||||
"nameRef": "Set4"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set7_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set7_crispmip.png",
|
||||
"name": "Glory in Navori",
|
||||
"nameRef": "Set7"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/setevent_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set8_crispmip.png",
|
||||
"name": "Fate's Voyage",
|
||||
"nameRef": "Set8"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set7b_crispmip.png",
|
||||
"name": "Heart of the Huntress",
|
||||
"nameRef": "Set7b"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/setevent_crispmip.png",
|
||||
"name": "Events",
|
||||
"nameRef": "SetEvent"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set6cde_crispmip.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set6cde_crispmip.png",
|
||||
"name": "The Darkin Saga",
|
||||
"nameRef": "Set6cde"
|
||||
}
|
||||
],
|
||||
"formats": [
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/formats/queue_select_standard_toggle_active.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/formats/queue_select_standard_toggle_active.png",
|
||||
"name": "Standard",
|
||||
"nameRef": "client_Formats_Standard_name"
|
||||
},
|
||||
|
@ -784,7 +836,7 @@
|
|||
"nameRef": "client_Deckbuilder_RulesFilters_Singleton"
|
||||
},
|
||||
{
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/formats/queue_select_eternal_toggle_active.png",
|
||||
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/formats/queue_select_eternal_toggle_active.png",
|
||||
"name": "Eternal",
|
||||
"nameRef": "client_Formats_Eternal_name"
|
||||
},
|
||||
|
@ -798,5 +850,27 @@
|
|||
"name": "Even Cost Cards",
|
||||
"nameRef": "client_Formats_EvenCostCards_name"
|
||||
}
|
||||
],
|
||||
"adventureRarities": [
|
||||
{
|
||||
"name": "COMMON",
|
||||
"nameRef": "Common"
|
||||
},
|
||||
{
|
||||
"name": "RARE",
|
||||
"nameRef": "Rare"
|
||||
},
|
||||
{
|
||||
"name": "EPIC",
|
||||
"nameRef": "Epic"
|
||||
},
|
||||
{
|
||||
"name": "LEGENDARY",
|
||||
"nameRef": "Legendary"
|
||||
},
|
||||
{
|
||||
"name": "SPECIAL",
|
||||
"nameRef": "Special"
|
||||
}
|
||||
]
|
||||
}
|
1
data/set7b-en_us/COPYRIGHT
Normal file
|
@ -0,0 +1 @@
|
|||
Copyright Riot Games, Inc. 2019
|
40
data/set7b-en_us/README.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
metadata.json
|
||||
{
|
||||
"locales": ["{string}", ...],
|
||||
"clientHash": "{string}"
|
||||
"gameplayDataHash": "{string}",
|
||||
"timestamp": "{YYYYMMDDhhmm}",
|
||||
"patchlineRef": "{string}"
|
||||
}
|
||||
|
||||
cards.json
|
||||
[
|
||||
{
|
||||
"id": "{cardCode}",
|
||||
"idComponents":
|
||||
{
|
||||
"set": "setNumber",
|
||||
"region":
|
||||
{
|
||||
"id": "{shortRegionCode}",
|
||||
"name": "{regionName}"
|
||||
}
|
||||
}
|
||||
"name": "{name}",
|
||||
"type": "{type}",
|
||||
"subType": "{subType}",
|
||||
"superType": "{superType}",
|
||||
"description": "{description}",
|
||||
"keywords": [],
|
||||
"associatedCards": [{cardCode}, ...]
|
||||
"health": "{health}",
|
||||
"attack": "{attack}",
|
||||
"cost": "{cost}",
|
||||
"assets":
|
||||
{
|
||||
"gameAbsolutePath": "http://{cdn}/{bundleName}/set1/en_us/img/card/game/{cardCode}.png"
|
||||
}
|
||||
|
||||
},
|
||||
{...}
|
||||
]
|
4462
data/set7b-en_us/en_us/data/set7b-en_us.json
Normal file
5
data/set7b-en_us/metadata.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"locales": [
|
||||
"en_us"
|
||||
]
|
||||
}
|
1
data/set8-en_us/COPYRIGHT
Normal file
|
@ -0,0 +1 @@
|
|||
Copyright Riot Games, Inc. 2019
|
40
data/set8-en_us/README.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
metadata.json
|
||||
{
|
||||
"locales": ["{string}", ...],
|
||||
"clientHash": "{string}"
|
||||
"gameplayDataHash": "{string}",
|
||||
"timestamp": "{YYYYMMDDhhmm}",
|
||||
"patchlineRef": "{string}"
|
||||
}
|
||||
|
||||
cards.json
|
||||
[
|
||||
{
|
||||
"id": "{cardCode}",
|
||||
"idComponents":
|
||||
{
|
||||
"set": "setNumber",
|
||||
"region":
|
||||
{
|
||||
"id": "{shortRegionCode}",
|
||||
"name": "{regionName}"
|
||||
}
|
||||
}
|
||||
"name": "{name}",
|
||||
"type": "{type}",
|
||||
"subType": "{subType}",
|
||||
"superType": "{superType}",
|
||||
"description": "{description}",
|
||||
"keywords": [],
|
||||
"associatedCards": [{cardCode}, ...]
|
||||
"health": "{health}",
|
||||
"attack": "{attack}",
|
||||
"cost": "{cost}",
|
||||
"assets":
|
||||
{
|
||||
"gameAbsolutePath": "http://{cdn}/{bundleName}/set1/en_us/img/card/game/{cardCode}.png"
|
||||
}
|
||||
|
||||
},
|
||||
{...}
|
||||
]
|
3199
data/set8-en_us/en_us/data/set8-en_us.json
Normal file
5
data/set8-en_us/metadata.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"locales": [
|
||||
"en_us"
|
||||
]
|
||||
}
|
BIN
media/ds-card.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
media/ds-deck.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
media/ds-help.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
media/td-deck.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
media/td-eternal.png
Normal file
After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 256 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 411 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 151 KiB |
|
@ -101,6 +101,12 @@ pub async fn create_globalindexes_from_dd_latest(locale: &str) -> globals::Local
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::data::setbundle::format::CardFormat;
|
||||
use crate::data::setbundle::keyword::CardKeyword;
|
||||
use crate::data::setbundle::rarity::CardRarity;
|
||||
use crate::data::setbundle::region::CardRegion;
|
||||
use crate::data::setbundle::set::CardSet;
|
||||
use crate::data::setbundle::speed::SpellSpeed;
|
||||
macro_rules! test_fetch {
|
||||
( $id:ident, $version:literal, $locale:literal ) => {
|
||||
#[tokio::test]
|
||||
|
@ -112,7 +118,29 @@ mod tests {
|
|||
};
|
||||
}
|
||||
|
||||
test_fetch!(test_fetch_4_5_0_en_us, "4_5_0", "en_us");
|
||||
test_fetch!(test_fetch_4_5_0_it_it, "4_5_0", "it_it");
|
||||
test_fetch!(test_fetch_4_9_0_en_us, "4_9_0", "en_us");
|
||||
test_fetch!(test_fetch_4_9_0_it_it, "4_9_0", "it_it");
|
||||
test_fetch!(test_fetch_latest_en_us, "latest", "en_us");
|
||||
|
||||
macro_rules! test_supported {
|
||||
( $id:ident, $version:literal, $locale:literal ) => {
|
||||
#[tokio::test]
|
||||
async fn $id() {
|
||||
let client = reqwest::Client::new();
|
||||
let result = crate::data::corebundle::CoreBundle::fetch(&client, &format!("https://dd.b.pvp.net/{}", $version), $locale).await;
|
||||
let result = result.expect("fetch request to be successful");
|
||||
|
||||
result.globals.keywords.iter().for_each(|o| assert_ne!(o.keyword, CardKeyword::Unsupported, "{:?} is unsupported", o));
|
||||
result.globals.regions.iter().for_each(|o| assert_ne!(o.region, CardRegion::Unsupported, "{:?} is unsupported", o));
|
||||
result.globals.spell_speeds.iter().for_each(|o| assert_ne!(o.spell_speed, SpellSpeed::Unsupported, "{:?} is unsupported", o));
|
||||
result.globals.rarities.iter().for_each(|o| assert_ne!(o.rarity, CardRarity::Unsupported, "{:?} is unsupported", o));
|
||||
result.globals.sets.iter().for_each(|o| assert_ne!(o.set, CardSet::Unsupported, "{:?} is unsupported", o));
|
||||
result.globals.formats.iter().for_each(|o| assert_ne!(o.format, CardFormat::Unsupported, "{:?} is unsupported", o));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_supported!(test_supported_4_9_0_en_us, "4_9_0", "en_us");
|
||||
test_supported!(test_supported_4_9_0_it_it, "4_9_0", "it_it");
|
||||
test_supported!(test_supported_latest_en_us, "latest", "en_us");
|
||||
}
|
|
@ -520,9 +520,7 @@ impl Deck {
|
|||
///
|
||||
pub fn cards_are_allowed_in(&self, cards: &CardIndex, format: CardFormat) -> bool {
|
||||
self.contents.keys()
|
||||
.map(|cc| cc.to_card(&cards))
|
||||
.filter(|o| o.is_some())
|
||||
.map(|o| o.unwrap())
|
||||
.filter_map(|cc| cc.to_card(cards))
|
||||
.all(|c| c.formats.contains(&format))
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ pub enum CardKeyword {
|
|||
/// Double Attack.
|
||||
///
|
||||
/// > While attacking, it strikes both before AND at the same time as its blocker.
|
||||
#[serde(rename = "DoubleStrike")]
|
||||
DoubleAttack,
|
||||
|
||||
/// Vulnerable.
|
||||
|
@ -347,6 +348,30 @@ pub enum CardKeyword {
|
|||
/// > Equip to a unit to grant the listed bonuses. If the unit leaves play, the equipment will return to your hand. You may play each equipment at most once per round.
|
||||
Equipment,
|
||||
|
||||
/// Capture.
|
||||
///
|
||||
/// > A Captured card is removed from the game. It returns when the Capturing unit leaves play.
|
||||
Capture,
|
||||
|
||||
/// Attack.
|
||||
///
|
||||
/// > Get this effect when this unit attacks.
|
||||
#[serde(rename = "AttackSkillMark")]
|
||||
Attack,
|
||||
|
||||
/// Skill with the elemental marker.
|
||||
///
|
||||
/// > A unit's spell-like effect that allows enemy reactions. It's elemental!
|
||||
ElementalSkill,
|
||||
|
||||
/// Level up!
|
||||
///
|
||||
/// > A champion in play levels up everywhere once this condition is met. Some champions must be in play to see their condition progress.
|
||||
LevelUp,
|
||||
|
||||
/// ???
|
||||
Freljord,
|
||||
|
||||
/// Unsupported card keyword.
|
||||
#[serde(other)]
|
||||
Unsupported,
|
||||
|
@ -441,6 +466,11 @@ impl CardKeyword {
|
|||
CardKeyword::Plunder => "",
|
||||
CardKeyword::BlockElusive => "",
|
||||
CardKeyword::Flow => "",
|
||||
CardKeyword::Capture => "<:capture:1056024295190577153>",
|
||||
CardKeyword::Attack => "",
|
||||
CardKeyword::ElementalSkill => "<:elementalskill:1165762476974026814>",
|
||||
CardKeyword::LevelUp => "",
|
||||
CardKeyword::Freljord => "<:freljord:1056024331437735936>",
|
||||
CardKeyword::Unsupported => "<:invaliddeck:1056022952396730438>",
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +533,7 @@ mod tests {
|
|||
test_deserialization!(deserialize_silenceindividualkeyword, r#""SilenceIndividualKeyword""#, CardKeyword::SilenceIndividualKeyword);
|
||||
test_deserialization!(deserialize_skill, r#""Skill""#, CardKeyword::Skill);
|
||||
test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder);
|
||||
test_deserialization!(deserialize_doubleattack, r#""DoubleAttack""#, CardKeyword::DoubleAttack);
|
||||
test_deserialization!(deserialize_doubleattack, r#""DoubleStrike""#, CardKeyword::DoubleAttack);
|
||||
test_deserialization!(deserialize_vulnerable, r#""Vulnerable""#, CardKeyword::Vulnerable);
|
||||
test_deserialization!(deserialize_elusive, r#""Elusive""#, CardKeyword::Elusive);
|
||||
test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun);
|
||||
|
@ -535,5 +565,10 @@ mod tests {
|
|||
test_deserialization!(deserialize_deep, r#""Deep""#, CardKeyword::Deep);
|
||||
test_deserialization!(deserialize_flow, r#""Flow""#, CardKeyword::Flow);
|
||||
test_deserialization!(deserialize_equipment, r#""Equipment""#, CardKeyword::Equipment);
|
||||
test_deserialization!(deserialize_capture, r#""Capture""#, CardKeyword::Capture);
|
||||
test_deserialization!(deserialize_attack, r#""AttackSkillMark""#, CardKeyword::Attack);
|
||||
test_deserialization!(deserialize_elementalskill, r#""ElementalSkill""#, CardKeyword::ElementalSkill);
|
||||
test_deserialization!(deserialize_levelup, r#""LevelUp""#, CardKeyword::LevelUp);
|
||||
test_deserialization!(deserialize_freljord, r#""Freljord""#, CardKeyword::Freljord);
|
||||
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardKeyword::Unsupported);
|
||||
}
|
||||
|
|
|
@ -96,25 +96,27 @@ mod tests {
|
|||
};
|
||||
}
|
||||
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set1, "4_5_0", "en_us", "set1");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set2, "4_5_0", "en_us", "set2");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set3, "4_5_0", "en_us", "set3");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set4, "4_5_0", "en_us", "set4");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set5, "4_5_0", "en_us", "set5");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set6, "4_5_0", "en_us", "set6");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set6cde, "4_5_0", "en_us", "set6cde");
|
||||
test_fetch!(test_fetch_4_5_0_en_us_set7, "4_5_0", "en_us", "set7");
|
||||
// test_fetch!(test_fetch_4_5_0_en_us_set7b, "4_5_0", "en_us", "set7b"); // TODO: Unignore me when set releases
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set1, "4_9_0", "en_us", "set1");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set2, "4_9_0", "en_us", "set2");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set3, "4_9_0", "en_us", "set3");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set4, "4_9_0", "en_us", "set4");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set5, "4_9_0", "en_us", "set5");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set6, "4_9_0", "en_us", "set6");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set6cde, "4_9_0", "en_us", "set6cde");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set7, "4_9_0", "en_us", "set7");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set7b, "4_9_0", "en_us", "set7b");
|
||||
test_fetch!(test_fetch_4_9_0_en_us_set8, "4_9_0", "en_us", "set8");
|
||||
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set1, "4_5_0", "it_it", "set1");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set2, "4_5_0", "it_it", "set2");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set3, "4_5_0", "it_it", "set3");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set4, "4_5_0", "it_it", "set4");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set5, "4_5_0", "it_it", "set5");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set6, "4_5_0", "it_it", "set6");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set6cde, "4_5_0", "it_it", "set6cde");
|
||||
test_fetch!(test_fetch_4_5_0_it_it_set7, "4_5_0", "it_it", "set7");
|
||||
// test_fetch!(test_fetch_4_5_0_it_it_set7b, "4_5_0", "it_it", "set7b"); // TODO: Unignore me when set releases
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set1, "4_9_0", "it_it", "set1");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set2, "4_9_0", "it_it", "set2");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set3, "4_9_0", "it_it", "set3");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set4, "4_9_0", "it_it", "set4");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set5, "4_9_0", "it_it", "set5");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set6, "4_9_0", "it_it", "set6");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set6cde, "4_9_0", "it_it", "set6cde");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set7, "4_9_0", "it_it", "set7");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set7b, "4_9_0", "it_it", "set7b");
|
||||
test_fetch!(test_fetch_4_9_0_it_it_set8, "4_9_0", "it_it", "set8");
|
||||
|
||||
test_fetch!(test_fetch_latest_en_us_set1, "latest", "en_us", "set1");
|
||||
test_fetch!(test_fetch_latest_en_us_set2, "latest", "en_us", "set2");
|
||||
|
@ -125,6 +127,7 @@ mod tests {
|
|||
test_fetch!(test_fetch_latest_en_us_set6cde, "latest", "en_us", "set6cde");
|
||||
test_fetch!(test_fetch_latest_en_us_set7, "latest", "en_us", "set7");
|
||||
test_fetch!(test_fetch_latest_en_us_set7b, "latest", "en_us", "set7b");
|
||||
test_fetch!(test_fetch_latest_en_us_set8, "latest", "en_us", "set8");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::data::corebundle::rarity::{LocalizedCardRarity, LocalizedCardRarityIndex};
|
||||
|
||||
/// A possible [Card](super::card::Card) rarity.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum CardRarity {
|
||||
/// The card has no rarity, as it probably is not [collectible](super::card::Card::collectible).
|
||||
|
@ -19,6 +20,10 @@ pub enum CardRarity {
|
|||
|
||||
/// A champion (orange hexagon) card, sometimes referred to as *Legendary*.
|
||||
Champion,
|
||||
|
||||
/// Unsupported rarity.
|
||||
#[serde(other)]
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl CardRarity {
|
||||
|
@ -33,6 +38,32 @@ impl CardRarity {
|
|||
) -> Option<&'hm LocalizedCardRarity> {
|
||||
hm.get(self)
|
||||
}
|
||||
|
||||
/// Get the Discord emoji code associated with this [`CardRarity`].
|
||||
pub fn discord_emoji(&self) -> &'static str {
|
||||
match self {
|
||||
CardRarity::None => "",
|
||||
CardRarity::Common => "<:common:1056024315046412358>",
|
||||
CardRarity::Rare => "<:rare:1056022907433799690>",
|
||||
CardRarity::Epic => "<:epic:1056023004028608622>",
|
||||
CardRarity::Champion => "<:champion:1056024303856001034>",
|
||||
CardRarity::Unsupported => "",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the color associated with this [`CardRarity`].
|
||||
///
|
||||
/// Used for example to determine the color of the Discord embed.
|
||||
pub fn color(&self) -> u32 {
|
||||
match self {
|
||||
CardRarity::None => 0x202225,
|
||||
CardRarity::Common => 0x1e6a49,
|
||||
CardRarity::Rare => 0x244778,
|
||||
CardRarity::Epic => 0x502970,
|
||||
CardRarity::Champion => 0x81541f,
|
||||
CardRarity::Unsupported => 0xff0000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -56,9 +87,5 @@ mod tests {
|
|||
test_deserialization!(deserialize_rare, r#""Rare""#, CardRarity::Rare);
|
||||
test_deserialization!(deserialize_epic, r#""Epic""#, CardRarity::Epic);
|
||||
test_deserialization!(deserialize_champion, r#""Champion""#, CardRarity::Champion);
|
||||
|
||||
#[test]
|
||||
fn deserialize_fallback() {
|
||||
assert!(serde_json::de::from_str::<'static, CardRarity>("Xyzzy").is_err());
|
||||
}
|
||||
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardRarity::Unsupported);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,28 @@ pub enum CardRegion {
|
|||
/// Runeterra.
|
||||
Runeterra,
|
||||
|
||||
/// Runeterra: Ryze.
|
||||
Ryze,
|
||||
/// Runeterra: Jhin.
|
||||
Jhin,
|
||||
/// Runeterra: Varus.
|
||||
Varus,
|
||||
/// Runeterra: Aatrox.
|
||||
Aatrox,
|
||||
/// Runeterra: Neeko.
|
||||
Neeko,
|
||||
/// Runeterra: Jax.
|
||||
Jax,
|
||||
/// Runeterra: Kayn.
|
||||
Kayn,
|
||||
/// Runeterra: Poro King.
|
||||
PoroKing,
|
||||
/// Runeterra: Evelynn.
|
||||
Evelynn,
|
||||
/// Runeterra: Bard.
|
||||
Bard,
|
||||
|
||||
|
||||
/// Unsupported region.
|
||||
#[serde(other)]
|
||||
Unsupported,
|
||||
|
@ -90,6 +112,30 @@ impl CardRegion {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the long code of this [`CardRegion`].
|
||||
///
|
||||
/// If the region has no short code, it will return [`None`].
|
||||
///
|
||||
/// Used for deck hashtags in the Telegram bot.
|
||||
pub fn to_tag(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Demacia => Some("Demacia"),
|
||||
Self::Freljord => Some("Freljord"),
|
||||
Self::Ionia => Some("Ionia"),
|
||||
Self::Noxus => Some("Noxus"),
|
||||
Self::PiltoverZaun => Some("PiltoverZaun"),
|
||||
Self::ShadowIsles => Some("ShadowIsles"),
|
||||
Self::Bilgewater => Some("Bilgewater"),
|
||||
Self::Shurima => Some("Shurima"),
|
||||
Self::Targon => Some("Targon"),
|
||||
Self::BandleCity => Some("BandleCity"),
|
||||
Self::Runeterra => Some("Runeterra"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the human friendly
|
||||
|
||||
/// Get the Discord emoji code associated with this [`CardRegion`].
|
||||
pub fn discord_emoji(&self) -> &'static str {
|
||||
match self {
|
||||
|
@ -104,6 +150,16 @@ impl CardRegion {
|
|||
CardRegion::PiltoverZaun => "<:piltoverzaun:1056022918959734835>",
|
||||
CardRegion::BandleCity => "<:bandlecity:1056024280493735976>",
|
||||
CardRegion::Runeterra => "<:runeterra:1056022895031238727>",
|
||||
CardRegion::Ryze => "",
|
||||
CardRegion::Jhin => "",
|
||||
CardRegion::Varus => "",
|
||||
CardRegion::Aatrox => "",
|
||||
CardRegion::Neeko => "",
|
||||
CardRegion::Jax => "",
|
||||
CardRegion::Kayn => "",
|
||||
CardRegion::PoroKing => "",
|
||||
CardRegion::Evelynn => "",
|
||||
CardRegion::Bard => "",
|
||||
CardRegion::Unsupported => "<:invaliddeck:1056022952396730438>",
|
||||
}
|
||||
}
|
||||
|
@ -180,5 +236,15 @@ mod tests {
|
|||
test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardRegion::PiltoverZaun);
|
||||
test_deserialization!(deserialize_bandlecity, r#""BandleCity""#, CardRegion::BandleCity);
|
||||
test_deserialization!(deserialize_runeterra, r#""Runeterra""#, CardRegion::Runeterra);
|
||||
test_deserialization!(deserialize_runeterra_ryze, r#""Ryze""#, CardRegion::Ryze);
|
||||
test_deserialization!(deserialize_runeterra_jhin, r#""Jhin""#, CardRegion::Jhin);
|
||||
test_deserialization!(deserialize_runeterra_varus, r#""Varus""#, CardRegion::Varus);
|
||||
test_deserialization!(deserialize_runeterra_aatrox, r#""Aatrox""#, CardRegion::Aatrox);
|
||||
test_deserialization!(deserialize_runeterra_neeko, r#""Neeko""#, CardRegion::Neeko);
|
||||
test_deserialization!(deserialize_runeterra_jax, r#""Jax""#, CardRegion::Jax);
|
||||
test_deserialization!(deserialize_runeterra_kayn, r#""Kayn""#, CardRegion::Kayn);
|
||||
test_deserialization!(deserialize_runeterra_poroking, r#""PoroKing""#, CardRegion::PoroKing);
|
||||
test_deserialization!(deserialize_runeterra_evelynn, r#""Evelynn""#, CardRegion::Evelynn);
|
||||
test_deserialization!(deserialize_runeterra_bard, r#""Bard""#, CardRegion::Bard);
|
||||
test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardRegion::Unsupported);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,14 @@ pub enum CardSet {
|
|||
#[serde(rename = "Set7")]
|
||||
GloryInNavori,
|
||||
|
||||
/// Heart of the Huntress.
|
||||
#[serde(rename = "Set7b")]
|
||||
HeartOfTheHuntress,
|
||||
|
||||
/// Fate's Voyage.
|
||||
#[serde(rename = "Set8")]
|
||||
FatesVoyage,
|
||||
|
||||
/// Events, cards released "outside" a set.
|
||||
#[serde(rename = "SetEvent")]
|
||||
Events,
|
||||
|
@ -94,6 +102,24 @@ impl CardSet {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Discord emoji code associated with this [`CardSet`].
|
||||
pub fn discord_emoji(&self) -> &'static str {
|
||||
match self {
|
||||
CardSet::Foundations => "<:foundations:1071644734667366410>",
|
||||
CardSet::RisingTides => "<:rising_tides:1071644736126976160>",
|
||||
CardSet::CallOfTheMountain => "<:call_of_the_mountain:1071644738555478076>",
|
||||
CardSet::EmpiresOfTheAscended => "<:empires_of_the_ascended:1071644740342255616>",
|
||||
CardSet::BeyondTheBandlewood => "<:beyond_the_bandlewood:1071644742640750734>",
|
||||
CardSet::Worldwalker => "<:worldwalker:1071644743798370315>",
|
||||
CardSet::TheDarkinSaga => "<:the_darkin_saga:1071644746411417610>",
|
||||
CardSet::GloryInNavori => "<:glory_in_navori:1095363395890458756>",
|
||||
CardSet::HeartOfTheHuntress => "<:heart_of_the_huntress:1165769749922320494>",
|
||||
CardSet::FatesVoyage => "<:fates_voyage:1165769932995317851>",
|
||||
CardSet::Events => "",
|
||||
CardSet::Unsupported => "<:invaliddeck:1056022952396730438>",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the [`CardSet`] from its internal id.
|
||||
|
@ -157,6 +183,9 @@ mod tests {
|
|||
test_deserialization!(deserialize_set5, r#""Set5""#, CardSet::BeyondTheBandlewood);
|
||||
test_deserialization!(deserialize_set6, r#""Set6""#, CardSet::Worldwalker);
|
||||
test_deserialization!(deserialize_set6cde, r#""Set6cde""#, CardSet::TheDarkinSaga);
|
||||
test_deserialization!(deserialize_set7, r#""Set7""#, CardSet::GloryInNavori);
|
||||
test_deserialization!(deserialize_set7b, r#""Set7b""#, CardSet::HeartOfTheHuntress);
|
||||
test_deserialization!(deserialize_set8, r#""Set8""#, CardSet::FatesVoyage);
|
||||
test_deserialization!(deserialize_setevent, r#""SetEvent""#, CardSet::Events);
|
||||
test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardSet::Unsupported);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex};
|
||||
|
||||
/// A possible [`Spell`](super::type::CardType::Spell) speed.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SpellSpeed {
|
||||
/// Non-spell cards have this speed.
|
||||
|
@ -14,6 +15,9 @@ pub enum SpellSpeed {
|
|||
Fast,
|
||||
/// Either a Burst or a Focus spell; to disambiguate between the two, check for the `Focus` keyword.
|
||||
Burst,
|
||||
/// Unsupported spell speed.
|
||||
#[serde(other)]
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl SpellSpeed {
|
||||
|
@ -50,9 +54,5 @@ mod tests {
|
|||
test_deserialization!(deserialize_slow, r#""Slow""#, SpellSpeed::Slow);
|
||||
test_deserialization!(deserialize_fast, r#""Fast""#, SpellSpeed::Fast);
|
||||
test_deserialization!(deserialize_burst, r#""Burst""#, SpellSpeed::Burst);
|
||||
|
||||
#[test]
|
||||
fn deserialize_fallback() {
|
||||
assert!(serde_json::de::from_str::<'static, SpellSpeed>("Xyzzy").is_err());
|
||||
}
|
||||
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, SpellSpeed::Unsupported);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ pub enum CardType {
|
|||
/// A spell.
|
||||
Spell,
|
||||
|
||||
/// A equipment.
|
||||
Equipment,
|
||||
|
||||
/// An unit: either a minion, or a champion.
|
||||
///
|
||||
/// Champions have their [supertype](super::card::Card::supertype) set to `Champion`, and their [rarity](super::card::Card::rarity) set to [CardRarity::Champion](super::rarity::CardRarity::Champion) as well.
|
||||
|
@ -33,6 +36,7 @@ pub enum CardType {
|
|||
impl From<&CardType> for &'static str {
|
||||
fn from(r#type: &CardType) -> Self {
|
||||
match r#type {
|
||||
CardType::Equipment => "Equipment",
|
||||
CardType::Spell => "Spell",
|
||||
CardType::Unit => "Unit",
|
||||
CardType::Ability => "Ability",
|
||||
|
|
|
@ -12,8 +12,6 @@ use crate::data::deckcode::deck::Deck;
|
|||
use crate::data::deckcode::format::DeckCodeFormat;
|
||||
use crate::data::setbundle::r#type::CardType;
|
||||
use crate::data::setbundle::rarity::CardRarity;
|
||||
use crate::data::setbundle::region::CardRegion;
|
||||
use crate::data::setbundle::set::CardSet;
|
||||
use crate::data::setbundle::supertype::CardSupertype;
|
||||
use crate::search::cardsearch::CardSearchEngine;
|
||||
|
||||
|
@ -94,13 +92,11 @@ impl EventHandler {
|
|||
}
|
||||
|
||||
if !card.keywords.is_empty() {
|
||||
e.field("Keywords", card.keywords.iter().map(|r|
|
||||
format!(
|
||||
"{} {}",
|
||||
r.discord_emoji(),
|
||||
r.localized(&engine.globals.keywords).map_or_else(|| String::from("Missing translation"), |l| l.name.clone())
|
||||
)
|
||||
).join(", "), true);
|
||||
e.field("Keywords", card.keywords.iter().map(|r| {
|
||||
let icon = r.discord_emoji();
|
||||
let text = r.localized(&engine.globals.keywords).map_or_else(|| String::from("Unknown"), |l| l.name.clone());
|
||||
format!("{icon} {text}")
|
||||
}).join(", "), true);
|
||||
}
|
||||
|
||||
e.field("Mana cost", format!("{} mana", card.cost), true);
|
||||
|
@ -121,56 +117,30 @@ impl EventHandler {
|
|||
vec
|
||||
}.join(", "), true);
|
||||
|
||||
e.field("Regions", card.regions.iter().map(|r| match r {
|
||||
CardRegion::Noxus => "<:noxus:1056022924169064498> Noxus",
|
||||
CardRegion::Demacia => "<:demacia:1056023014128484412> Demacia",
|
||||
CardRegion::Freljord => "<:freljord:1056024331437735936> Freljord",
|
||||
CardRegion::ShadowIsles => "<:shadowisles:1056022886848135292> Shadow Isles",
|
||||
CardRegion::Targon => "<:targon:1056022866174418944> Targon",
|
||||
CardRegion::Ionia => "<:ionia:1056022949569777708> Ionia",
|
||||
CardRegion::Bilgewater => "<:bilgewater:1056024288215437484> Bilgewater",
|
||||
CardRegion::Shurima => "<:shurima:1056022884616765500> Shurima",
|
||||
CardRegion::PiltoverZaun => "<:piltoverzaun:1056022918959734835> Piltover & Zaun",
|
||||
CardRegion::BandleCity => "<:bandlecity:1056024280493735976> Bandle City",
|
||||
CardRegion::Runeterra => "<:runeterra:1056022895031238727> Runeterra",
|
||||
CardRegion::Unsupported => "<:invaliddeck:1056022952396730438> Unknown",
|
||||
e.field("Regions", card.regions.iter().map(|r| {
|
||||
let icon = r.discord_emoji();
|
||||
let text = r.localized(&engine.globals.regions).map_or_else(|| String::from("Unknown"), |r| r.name.clone());
|
||||
format!("{icon} {text}")
|
||||
}).join(", "), false);
|
||||
|
||||
e.field("Set", match card.set {
|
||||
CardSet::Foundations => "<:foundations:1071644734667366410> Foundations",
|
||||
CardSet::RisingTides => "<:rising_tides:1071644736126976160> Rising Tides",
|
||||
CardSet::CallOfTheMountain => "<:call_of_the_mountain:1071644738555478076> Call of the Mountain",
|
||||
CardSet::EmpiresOfTheAscended => "<:empires_of_the_ascended:1071644740342255616> Empires of the Ascended",
|
||||
CardSet::BeyondTheBandlewood => "<:beyond_the_bandlewood:1071644742640750734> Beyond the Bandlewood",
|
||||
CardSet::Worldwalker => "<:worldwalker:1071644743798370315> Worldwalker",
|
||||
CardSet::TheDarkinSaga => "<:the_darkin_saga:1071644746411417610> The Darkin Saga",
|
||||
CardSet::GloryInNavori => "<:glory_in_navori:1095363395890458756>
|
||||
Glory in Navori",
|
||||
CardSet::Events => "Events", // TODO: Add icon
|
||||
CardSet::Unsupported => "<:invaliddeck:1056022952396730438> Unknown",
|
||||
e.field("Set", {
|
||||
let icon = card.set.discord_emoji();
|
||||
let text = card.set.localized(&engine.globals.sets).map_or_else(|| String::from("Unknown"), |r| r.name.clone());
|
||||
format!("{icon} {text}")
|
||||
}, true);
|
||||
|
||||
e.field("Rarity", match card.supertype {
|
||||
CardSupertype::Champion => "<:champion:1056024303856001034> Champion",
|
||||
_ => match card.rarity {
|
||||
CardRarity::None => "None",
|
||||
CardRarity::Common => "<:common:1056024315046412358> Common",
|
||||
CardRarity::Rare => "<:rare:1056022907433799690> Rare",
|
||||
CardRarity::Epic => "<:epic:1056023004028608622> Epic",
|
||||
CardRarity::Champion => "<:champion:1056024303856001034> Champion",
|
||||
}
|
||||
}, true);
|
||||
let actual_rarity = match card.supertype {
|
||||
CardSupertype::Champion => CardRarity::Champion,
|
||||
_ => card.rarity
|
||||
};
|
||||
|
||||
e.color(match card.supertype {
|
||||
CardSupertype::Champion => 0x81541f,
|
||||
_ => match card.rarity {
|
||||
CardRarity::None => 0x202225,
|
||||
CardRarity::Common => 0x1e6a49,
|
||||
CardRarity::Rare => 0x244778,
|
||||
CardRarity::Epic => 0x502970,
|
||||
CardRarity::Champion => 0x81541f,
|
||||
}
|
||||
});
|
||||
e.field("Rarity", {
|
||||
let icon = actual_rarity.discord_emoji();
|
||||
let text = actual_rarity.localized(&engine.globals.rarities).map_or_else(|| String::from("Unknown"), |r| r.name.clone());
|
||||
format!("{icon} {text}")
|
||||
} , true);
|
||||
|
||||
e.color(actual_rarity.color());
|
||||
|
||||
if !card.localized_flavor_text.is_empty() {
|
||||
e.footer(|f| f.text(card.localized_flavor_text.clone()));
|
||||
|
|
|
@ -35,7 +35,7 @@ pub struct CardSearchEngine {
|
|||
}
|
||||
|
||||
impl CardSearchEngine {
|
||||
/// Create the [tantivy::tokenizer::TextAnalyzer] for card text.
|
||||
/// Create the [TextAnalyzer] for card text.
|
||||
///
|
||||
/// It should not alter text significantly, as it may contain important game vocabulary terms.
|
||||
fn tokenizer() -> TextAnalyzer {
|
||||
|
@ -44,7 +44,7 @@ impl CardSearchEngine {
|
|||
TextAnalyzer::from(SimpleTokenizer).filter(LowerCaser)
|
||||
}
|
||||
|
||||
/// Create the [tantivy::schema::TextOptions] for card codes.
|
||||
/// Create the [TextOptions] for card codes.
|
||||
///
|
||||
/// Card codes should:
|
||||
/// - TODO: be tokenized without alterations;
|
||||
|
@ -63,7 +63,7 @@ impl CardSearchEngine {
|
|||
.set_fast()
|
||||
}
|
||||
|
||||
/// Create the [tantivy::schema::TextOptions] for card keywords.
|
||||
/// Create the [TextOptions] for card keywords.
|
||||
///
|
||||
/// Card keywords should:
|
||||
/// - be tokenized with the [CardSearchEngine::tokenizer];
|
||||
|
@ -78,7 +78,7 @@ impl CardSearchEngine {
|
|||
)
|
||||
}
|
||||
|
||||
/// Create the [tantivy::schema::TextOptions] for card text fields.
|
||||
/// Create the [TextOptions] for card text fields.
|
||||
///
|
||||
/// Card text should:
|
||||
/// - TODO: be tokenized with the tokenizer for the locale language;
|
||||
|
@ -93,7 +93,7 @@ impl CardSearchEngine {
|
|||
)
|
||||
}
|
||||
|
||||
/// Create the [tantivy::schema::NumericOptions] for card numeric fields.
|
||||
/// Create the [NumericOptions] for card numeric fields.
|
||||
///
|
||||
/// Card numbers should:
|
||||
/// - be indexed.
|
||||
|
|
|
@ -23,118 +23,162 @@ use teloxide::utils::html::escape;
|
|||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
|
||||
let title = format!("<b><u>{}</u></b>\n", escape(&card.name),);
|
||||
|
||||
let stats = match &card.r#type {
|
||||
CardType::Spell => format!("{} mana\n\n", escape(&card.cost.to_string()),),
|
||||
let title: String = display_title(&card.name);
|
||||
let r#type: String = display_type(globals, card);
|
||||
let stats = display_stats(card);
|
||||
let subtypes: String = display_subtypes(&card.subtypes);
|
||||
|
||||
let header = format!("{} ({})\n{}\n{}\n", &title, &r#type, &stats, &subtypes);
|
||||
|
||||
let keywords = display_keywords(&card.keywords, &globals.keywords);
|
||||
let description = display_description(&card.localized_description_text);
|
||||
let levelup = display_levelup(&card.localized_levelup_text);
|
||||
|
||||
let body = format!("{}{}{}", &keywords, &description, &levelup);
|
||||
|
||||
let set = display_set(&card.set, &globals.sets);
|
||||
let regions = display_regions(&card.regions, &globals.regions);
|
||||
let flavor = display_flavor(&card.main_art().expect("Card to have at least one illustration").full_png, &card.localized_flavor_text);
|
||||
|
||||
let footer = format!("{}{}\n{}", &set, ®ions, &flavor);
|
||||
|
||||
format!("{}⸻\n\n{}⸻\n\n{}", &header, &body, &footer)
|
||||
}
|
||||
|
||||
/// Render a [Card]'s title in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_title(name: &str) -> String {
|
||||
format!("<b><u>{}</u></b>", escape(name))
|
||||
}
|
||||
|
||||
/// Render the [Card]'s in-game type in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_type(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
|
||||
match card.r#type {
|
||||
CardType::Spell => {
|
||||
"Spell".to_string()
|
||||
}
|
||||
CardType::Equipment => {
|
||||
CardKeyword::Equipment.localized(&globals.keywords).map(|l| l.name.clone()).unwrap_or_else(|| "EQUIPMENT".to_string())
|
||||
}
|
||||
CardType::Unit => {
|
||||
if card.supertype.eq(&CardSupertype::Champion) {
|
||||
"Champion".to_string()
|
||||
}
|
||||
else {
|
||||
"Unit".to_string()
|
||||
}
|
||||
}
|
||||
CardType::Ability => {
|
||||
if card.keywords.contains(&CardKeyword::Skill) {
|
||||
CardKeyword::Skill.localized(&globals.keywords).map(|l| l.name.clone()).unwrap_or_else(|| "SKILL".to_string())
|
||||
}
|
||||
else {
|
||||
"Ability".to_string() // Does this exist?
|
||||
}
|
||||
}
|
||||
CardType::Landmark => {
|
||||
CardKeyword::Landmark.localized(&globals.keywords).map(|l| l.name.clone()).unwrap_or_else(|| "LANDMARK".to_string())
|
||||
}
|
||||
CardType::Trap => {
|
||||
if card.keywords.contains(&CardKeyword::Trap) {
|
||||
CardKeyword::Trap.localized(&globals.keywords).map(|l| l.name.clone()).unwrap_or_else(|| "TRAP".to_string())
|
||||
}
|
||||
else if card.keywords.contains(&CardKeyword::Boon) {
|
||||
CardKeyword::Boon.localized(&globals.keywords).map(|l| l.name.clone()).unwrap_or_else(|| "Boon".to_string())
|
||||
}
|
||||
else {
|
||||
"Trigger".to_string() // Does this exist?
|
||||
}
|
||||
}
|
||||
CardType::Unsupported => {
|
||||
"UNKNOWN?".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display_stats(card: &Card) -> String {
|
||||
match &card.r#type {
|
||||
CardType::Spell => format!("{} mana", escape(&card.cost.to_string())),
|
||||
CardType::Unit => format!(
|
||||
"{} mana {}|{}\n\n",
|
||||
"{} mana · {}|{}",
|
||||
escape(&card.cost.to_string()),
|
||||
escape(&card.attack.to_string()),
|
||||
escape(&card.health.to_string()),
|
||||
),
|
||||
CardType::Landmark => format!("{} mana\n\n", &card.cost),
|
||||
CardType::Landmark => format!("{} mana", &card.cost),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
let set = display_set(&card.set, &globals.sets);
|
||||
let regions = display_regions(&card.regions, &globals.regions);
|
||||
let r#type = display_types(&card.r#type, &card.supertype, &card.subtypes);
|
||||
|
||||
let breadcrumbs = format!("{} › {} › {}\n\n", &set, ®ions, &r#type);
|
||||
|
||||
let keywords = display_keywords(&card.keywords, &globals.keywords);
|
||||
|
||||
let description = display_description(&card.localized_description_text);
|
||||
let levelup = display_levelup(&card.localized_levelup_text);
|
||||
|
||||
let flavor = format!("<i>{}</i>\n", escape(&card.localized_flavor_text));
|
||||
|
||||
let artist = format!(
|
||||
r#"<a href="{}">Illustration</a> by {}"#,
|
||||
&card
|
||||
.main_art()
|
||||
.expect("Card to have at least one illustration")
|
||||
.full_png,
|
||||
escape(&card.artist_name)
|
||||
);
|
||||
|
||||
format!(
|
||||
"{}{}{}{}{}{}-----\n{}{}",
|
||||
&title, &breadcrumbs, &keywords, &stats, &description, &levelup, &flavor, &artist,
|
||||
)
|
||||
}
|
||||
|
||||
/// Render a [CardSet] in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String {
|
||||
format!(
|
||||
"<i>{}</i>",
|
||||
set.localized(hm)
|
||||
.map(|o| format!("<i>{}</i>", escape(&o.name)))
|
||||
.unwrap_or_else(|| "Unknown".to_string())
|
||||
)
|
||||
}
|
||||
|
||||
/// Render a slice of [CardRegion]s in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_regions(regions: &[CardRegion], hm: &LocalizedCardRegionIndex) -> String {
|
||||
regions
|
||||
.iter()
|
||||
.map(|region| {
|
||||
region
|
||||
.localized(hm)
|
||||
.map(|o| format!("<i>{}</i>", escape(&o.name)))
|
||||
.unwrap_or_else(|| "Unknown".to_string())
|
||||
})
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
/// Render the [CardType], the [CardSupertype] and the [CardSubtype]s in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_types(r#type: &CardType, supertype: &CardSupertype, subtypes: &[CardSubtype]) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
result.push_str(
|
||||
match supertype {
|
||||
CardSupertype::Champion => "<i>Champion</i> › ",
|
||||
CardSupertype::Unsupported => "<i>Unknown</i> › ",
|
||||
_ => "",
|
||||
}
|
||||
);
|
||||
|
||||
result.push_str(&format!("<i>{}</i>", escape(&String::from(r#type)),));
|
||||
|
||||
if !subtypes.is_empty() {
|
||||
result.push_str(&format!(
|
||||
" › {}",
|
||||
subtypes
|
||||
.iter()
|
||||
.map(|subtype| format!("<i>{}</i>", escape(subtype)))
|
||||
.join(", ")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
/// Render the [CardSubtype]s in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_subtypes(subtypes: &[CardSubtype]) -> String {
|
||||
let result = subtypes
|
||||
.iter()
|
||||
.map(|s| titlecase(s))
|
||||
.map(|s| escape(&s))
|
||||
.map(|s| format!("<i>{s}</i>"))
|
||||
.join(", ");
|
||||
|
||||
if result.is_empty() {
|
||||
result
|
||||
} else {
|
||||
result + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a slice of [CardKeyword]s in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_keywords(keywords: &[CardKeyword], hm: &LocalizedCardKeywordIndex) -> String {
|
||||
format!(
|
||||
"{}\n",
|
||||
keywords
|
||||
.iter()
|
||||
.map(|keyword| keyword
|
||||
.localized(hm)
|
||||
.map(|o| format!("[<b>{}</b>]", escape(&o.name)))
|
||||
.unwrap_or_else(|| "Unknown".to_string()))
|
||||
.join(" ")
|
||||
)
|
||||
let result = keywords
|
||||
.iter()
|
||||
.filter(|keyword| !matches!(keyword,
|
||||
CardKeyword::Countdown |
|
||||
CardKeyword::OnPlay |
|
||||
CardKeyword::Landmark |
|
||||
CardKeyword::Shurima |
|
||||
CardKeyword::Noxus |
|
||||
CardKeyword::ClobberNoEmptySlotRequirement |
|
||||
CardKeyword::Nab |
|
||||
CardKeyword::Enlightened |
|
||||
CardKeyword::Invoke |
|
||||
CardKeyword::Drain |
|
||||
CardKeyword::LastBreath |
|
||||
CardKeyword::Demacia |
|
||||
CardKeyword::BandleCity |
|
||||
CardKeyword::Bilgewater |
|
||||
CardKeyword::Runeterra |
|
||||
CardKeyword::Recall |
|
||||
CardKeyword::Weakest |
|
||||
CardKeyword::Support |
|
||||
CardKeyword::Obliterate |
|
||||
CardKeyword::Imbue |
|
||||
CardKeyword::Targon |
|
||||
CardKeyword::ShadowIsles |
|
||||
CardKeyword::AuraVisualFakeKeyword |
|
||||
CardKeyword::Ionia |
|
||||
CardKeyword::PiltoverZaun |
|
||||
CardKeyword::SilenceIndividualKeyword |
|
||||
CardKeyword::Plunder |
|
||||
CardKeyword::Silenced
|
||||
))
|
||||
.map(|keyword| keyword
|
||||
.localized(hm)
|
||||
.map(|o| format!("[<b>{}</b>: {}]\n", escape(&o.name), escape(&o.description)))
|
||||
.unwrap_or_else(|| "[<b>UNKNOWN?</b>]\n".to_string()))
|
||||
.join("");
|
||||
|
||||
if result.is_empty() {
|
||||
result
|
||||
} else {
|
||||
result + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a [Card::localized_description_text] in [Telegram Bot HTML].
|
||||
|
@ -159,6 +203,46 @@ fn display_levelup(levelup: &String) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Render a [CardSet] in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String {
|
||||
format!(
|
||||
"<i>{}</i>",
|
||||
set.localized(hm)
|
||||
.map(|o| format!("<i>{}</i>", escape(&o.name)))
|
||||
.unwrap_or_else(|| "UNKNOWN?".to_string())
|
||||
)
|
||||
}
|
||||
|
||||
/// Render a slice of [CardRegion]s in [Telegram Bot HTML].
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_regions(regions: &[CardRegion], hm: &LocalizedCardRegionIndex) -> String {
|
||||
let result = regions
|
||||
.iter()
|
||||
.map(|region| {
|
||||
region
|
||||
.localized(hm)
|
||||
.map(|o| format!("<u>{}</u>", escape(&o.name)))
|
||||
.unwrap_or_else(|| "UNKNOWN?".to_string())
|
||||
})
|
||||
.join(", ");
|
||||
|
||||
if result.is_empty() {
|
||||
result
|
||||
} else {
|
||||
format!(" · {}", &result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the flavor text of a [Card] in [Telegram Bot HTML], linking to the given art.
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_flavor(art_url: &str, localized_flavor_text: &str) -> String {
|
||||
format!("<a href=\"{}\"><i>{}</i></a>", &art_url, escape(localized_flavor_text))
|
||||
}
|
||||
|
||||
/// Render a [Deck] in [Telegram Bot HTML], with an optional `name`.
|
||||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
|
@ -202,19 +286,19 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
|
|||
})
|
||||
.join("\n");
|
||||
|
||||
let mut tags: Vec<&'static str> = vec![];
|
||||
let mut tags: Vec<String> = vec![];
|
||||
|
||||
let regions = if let Some(regions) = deck.standard(index) {
|
||||
tags.push("#Standard_4_3");
|
||||
tags.push("#Standard_4_5".to_string());
|
||||
regions
|
||||
} else if let Some(regions) = deck.eternal(index) {
|
||||
tags.push("#Eternal");
|
||||
tags.push("#Eternal".to_string());
|
||||
regions
|
||||
} else if let Some(regions) = deck.unlimited_champions(index) {
|
||||
tags.push("#UnlimitedChampions");
|
||||
tags.push("#UnlimitedChampions".to_string());
|
||||
regions
|
||||
} else if let Some(regions) = deck.singleton(index) {
|
||||
tags.push("#Singleton");
|
||||
tags.push("#Singleton".to_string());
|
||||
regions
|
||||
} else {
|
||||
HashSet::new()
|
||||
|
@ -222,18 +306,8 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
|
|||
|
||||
for region in regions {
|
||||
tags.push(match region {
|
||||
CardRegion::Noxus => "#Noxus",
|
||||
CardRegion::Demacia => "#Demacia",
|
||||
CardRegion::Freljord => "#Freljord",
|
||||
CardRegion::ShadowIsles => "#ShadowIsles",
|
||||
CardRegion::Targon => "#Targon",
|
||||
CardRegion::Ionia => "#Ionia",
|
||||
CardRegion::Bilgewater => "#Bilgewater",
|
||||
CardRegion::Shurima => "#Shurima",
|
||||
CardRegion::PiltoverZaun => "#PiltoverZaun",
|
||||
CardRegion::BandleCity => "#BandleCity",
|
||||
CardRegion::Runeterra => "#Runeterra",
|
||||
CardRegion::Unsupported => "<i>Unknown</i>",
|
||||
CardRegion::Unsupported => "<i>Unknown</i>".to_string(),
|
||||
_ => format!("#{}", region.to_tag().map_or_else(|| "<i>Unknown</i>", |r| r)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -245,3 +319,14 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
|
|||
None => format!("<code>{}</code>\n{}\n{}", &code, &tags, &cards),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/38406885/4334568
|
||||
fn titlecase(s: &str) -> String {
|
||||
let s = s.to_lowercase();
|
||||
let mut c = s.chars();
|
||||
match c.next() {
|
||||
None => String::new(),
|
||||
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
|
||||
}
|
||||
}
|