1
Fork 0
mirror of https://github.com/Steffo99/patched-porobot.git synced 2025-01-07 01:09:42 +00:00

Compare commits

..

1 commit

Author SHA1 Message Date
cb10ae5a4b
Merge 8cc1c2b8f5 into 8c1db44f7f 2023-06-25 02:19:32 +02:00
35 changed files with 1403 additions and 10116 deletions

34
Cargo.lock generated
View file

@ -313,7 +313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"hashbrown 0.12.3", "hashbrown",
"lock_api", "lock_api",
"once_cell", "once_cell",
"parking_lot_core", "parking_lot_core",
@ -393,12 +393,6 @@ dependencies = [
"termcolor", "termcolor",
] ]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "erasable" name = "erasable"
version = "1.2.1" version = "1.2.1"
@ -636,9 +630,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.24" version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -662,12 +656,6 @@ dependencies = [
"ahash", "ahash",
] ]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.19" version = "0.1.19"
@ -811,12 +799,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.1.0" version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [ dependencies = [
"equivalent", "autocfg",
"hashbrown 0.14.3", "hashbrown",
] ]
[[package]] [[package]]
@ -944,7 +932,7 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
dependencies = [ dependencies = [
"hashbrown 0.12.3", "hashbrown",
] ]
[[package]] [[package]]
@ -1157,7 +1145,7 @@ dependencies = [
[[package]] [[package]]
name = "patched_porobot" name = "patched_porobot"
version = "0.15.0" version = "0.13.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.21.0", "base64 0.21.0",
@ -2373,9 +2361,9 @@ dependencies = [
[[package]] [[package]]
name = "webpki" name = "webpki"
version = "0.22.2" version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [ dependencies = [
"ring", "ring",
"untrusted", "untrusted",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "patched_porobot" name = "patched_porobot"
version = "0.15.0" version = "0.13.0"
authors = ["Stefano Pigozzi <me@steffo.eu>"] authors = ["Stefano Pigozzi <me@steffo.eu>"]
edition = "2021" edition = "2021"
description = "Legends of Runeterra card database utilities and bots" description = "Legends of Runeterra card database utilities and bots"

View file

@ -1,13 +1,7 @@
<div align="center"> # ![](icon.png) Patched Porobot
![](icon.png)
# Patched Porobot
Legends of Runeterra game data crate and chat bots Legends of Runeterra game data crate and chat bots
</div>
## Links ## Links
[![Telegram Bot](https://img.shields.io/badge/telegram%20bot-done-success)](https://t.me/patchedporobot) [![Telegram Bot](https://img.shields.io/badge/telegram%20bot-done-success)](https://t.me/patchedporobot)
@ -15,12 +9,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) [![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) ![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) [![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/latest/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)
## Screenshots ## Screenshots
@ -33,8 +27,6 @@ Legends of Runeterra game data crate and chat bots
</details> </details>
---
<details> <details>
<summary>The message the bot sends when it detects an interaction from the user, such as the default /start command.</summary> <summary>The message the bot sends when it detects an interaction from the user, such as the default /start command.</summary>
@ -42,8 +34,6 @@ Legends of Runeterra game data crate and chat bots
</details> </details>
---
<details> <details>
<summary>The card search prompt that appears when attempting to use the bot in a chat.</summary> <summary>The card search prompt that appears when attempting to use the bot in a chat.</summary>
@ -51,8 +41,6 @@ Legends of Runeterra game data crate and chat bots
</details> </details>
---
<details> <details>
<summary>A search for "poro". Many poros are displayed, and also Braum Level 2, since it contains "poro" in its description.</summary> <summary>A search for "poro". Many poros are displayed, and also Braum Level 2, since it contains "poro" in its description.</summary>
@ -60,8 +48,6 @@ Legends of Runeterra game data crate and chat bots
</details> </details>
---
<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> <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>
@ -69,62 +55,12 @@ Legends of Runeterra game data crate and chat bots
</details> </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 ## 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. 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> <details>
<summary>List of licenses as output by <code>cargo license</code></summary> <summary>List of licenses as output by cargo license</summary>
- **(Apache-2.0 OR MIT) AND BSD-3-Clause** (1): encoding_rs - **(Apache-2.0 OR MIT) AND BSD-3-Clause** (1): encoding_rs
- **(MIT OR Apache-2.0) AND Unicode-DFS-2016** (1): unicode-ident - **(MIT OR Apache-2.0) AND Unicode-DFS-2016** (1): unicode-ident
@ -147,4 +83,4 @@ Patched Porobot isn't endorsed by Riot Games and doesn't reflect the views or op
- **Unlicense** (1): measure_time - **Unlicense** (1): measure_time
- **zlib-acknowledgement** (1): fastdivide - **zlib-acknowledgement** (1): fastdivide
</details> </details>

View file

@ -1,7 +1,7 @@
{ {
"vocabTerms": [ "vocabTerms": [
{ {
"description": "When you summon this unit, it gets its allegiance bonus if the top card of your deck matches its region.", "description": "When you summon this, it gets its allegiance bonus if the top card of your deck matches its region.",
"name": "Allegiance", "name": "Allegiance",
"nameRef": "Allegiance" "nameRef": "Allegiance"
}, },
@ -20,11 +20,6 @@
"name": "Reforge", "name": "Reforge",
"nameRef": "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.", "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", "name": "Restore the Sun Disc",
@ -70,21 +65,11 @@
"name": "Round End", "name": "Round End",
"nameRef": "RoundEnd" "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.", "description": "Completely removed from the game. Doesn't cause Last Breath and can't be revived.",
"name": "Obliterate", "name": "Obliterate",
"nameRef": "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.", "description": "This is how much damage the unit can withstand. If it reaches zero, the unit dies.",
"name": "Health", "name": "Health",
@ -150,11 +135,6 @@
"name": "Round Start", "name": "Round Start",
"nameRef": "RoundStart" "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.", "description": "The opponent in The Path of Champions.",
"name": "Foe", "name": "Foe",
@ -205,11 +185,6 @@
"name": "Manifest", "name": "Manifest",
"nameRef": "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.", "description": "Transform allies Equipped with Darkin Equipment into their Darkin unit forms. If they are Champions, they Level Up.",
"name": "Assimilate", "name": "Assimilate",
@ -277,11 +252,6 @@
"name": "Fleeting", "name": "Fleeting",
"nameRef": "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", "description": "Missing Translation",
"name": "Missing Translation", "name": "Missing Translation",
@ -453,7 +423,7 @@
"nameRef": "Skill" "nameRef": "Skill"
}, },
{ {
"description": "A card activates its plunder ability when played if you damaged the enemy Nexus this round.", "description": "A card triggers its plunder ability when played if you damaged the enemy Nexus this round.",
"name": "Plunder", "name": "Plunder",
"nameRef": "Plunder" "nameRef": "Plunder"
}, },
@ -606,127 +576,115 @@
"regions": [ "regions": [
{ {
"abbreviation": "NX", "abbreviation": "NX",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-noxus.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-noxus.png",
"name": "Noxus", "name": "Noxus",
"nameRef": "Noxus" "nameRef": "Noxus"
}, },
{ {
"abbreviation": "RYZE", "abbreviation": "RYZE",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-ryze.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-ryze.png",
"name": "Ryze", "name": "Ryze",
"nameRef": "Ryze" "nameRef": "Ryze"
}, },
{ {
"abbreviation": "Jhin", "abbreviation": "Jhin",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-jhin.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-jhin.png",
"name": "Jhin", "name": "Jhin",
"nameRef": "Jhin" "nameRef": "Jhin"
}, },
{ {
"abbreviation": "Varus", "abbreviation": "Varus",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-varus.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-varus.png",
"name": "Varus", "name": "Varus",
"nameRef": "Varus" "nameRef": "Varus"
}, },
{ {
"abbreviation": "Aatrox", "abbreviation": "Aatrox",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-aatrox.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-aatrox.png",
"name": "Aatrox", "name": "Aatrox",
"nameRef": "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", "abbreviation": "Jax",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-jax.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-jax.png",
"name": "Jax", "name": "Jax",
"nameRef": "Jax" "nameRef": "Jax"
}, },
{ {
"abbreviation": "Kayn", "abbreviation": "Kayn",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-kayn.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-kayn.png",
"name": "Kayn", "name": "Kayn",
"nameRef": "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", "abbreviation": "Evelynn",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-evelynn.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-evelynn.png",
"name": "Evelynn", "name": "Evelynn",
"nameRef": "Evelynn" "nameRef": "Evelynn"
}, },
{ {
"abbreviation": "Bard", "abbreviation": "Bard",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bard.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bard.png",
"name": "Bard", "name": "Bard",
"nameRef": "Bard" "nameRef": "Bard"
}, },
{ {
"abbreviation": "DE", "abbreviation": "DE",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-demacia.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-demacia.png",
"name": "Demacia", "name": "Demacia",
"nameRef": "Demacia" "nameRef": "Demacia"
}, },
{ {
"abbreviation": "RU", "abbreviation": "RU",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-runeterra.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-runeterra.png",
"name": "Runeterra", "name": "Runeterra",
"nameRef": "Runeterra" "nameRef": "Runeterra"
}, },
{ {
"abbreviation": "FR", "abbreviation": "FR",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-freljord.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-freljord.png",
"name": "Freljord", "name": "Freljord",
"nameRef": "Freljord" "nameRef": "Freljord"
}, },
{ {
"abbreviation": "SI", "abbreviation": "SI",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-shadowisles.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-shadowisles.png",
"name": "Shadow Isles", "name": "Shadow Isles",
"nameRef": "ShadowIsles" "nameRef": "ShadowIsles"
}, },
{ {
"abbreviation": "MT", "abbreviation": "MT",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-targon.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-targon.png",
"name": "Targon", "name": "Targon",
"nameRef": "Targon" "nameRef": "Targon"
}, },
{ {
"abbreviation": "IO", "abbreviation": "IO",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-ionia.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-ionia.png",
"name": "Ionia", "name": "Ionia",
"nameRef": "Ionia" "nameRef": "Ionia"
}, },
{ {
"abbreviation": "SH", "abbreviation": "SH",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-shurima.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-shurima.png",
"name": "Shurima", "name": "Shurima",
"nameRef": "Shurima" "nameRef": "Shurima"
}, },
{ {
"abbreviation": "BW", "abbreviation": "BW",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bilgewater.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bilgewater.png",
"name": "Bilgewater", "name": "Bilgewater",
"nameRef": "Bilgewater" "nameRef": "Bilgewater"
}, },
{ {
"abbreviation": "PZ", "abbreviation": "PZ",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-piltoverzaun.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-piltoverzaun.png",
"name": "Piltover & Zaun", "name": "Piltover & Zaun",
"nameRef": "PiltoverZaun" "nameRef": "PiltoverZaun"
}, },
{ {
"abbreviation": "BC", "abbreviation": "BC",
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/regions/icon-bandlecity.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/regions/icon-bandlecity.png",
"name": "Bandle City", "name": "Bandle City",
"nameRef": "BandleCity" "nameRef": "BandleCity"
} }
@ -769,64 +727,54 @@
], ],
"sets": [ "sets": [
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set3_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set3_crispmip.png",
"name": "Call of the Mountain", "name": "Call of the Mountain",
"nameRef": "Set3" "nameRef": "Set3"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set5_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set5_crispmip.png",
"name": "Beyond the Bandlewood", "name": "Beyond the Bandlewood",
"nameRef": "Set5" "nameRef": "Set5"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set1_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set1_crispmip.png",
"name": "Foundations", "name": "Foundations",
"nameRef": "Set1" "nameRef": "Set1"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set2_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set2_crispmip.png",
"name": "Rising Tides", "name": "Rising Tides",
"nameRef": "Set2" "nameRef": "Set2"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set6ab_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set6ab_crispmip.png",
"name": "Worldwalker", "name": "Worldwalker",
"nameRef": "Set6" "nameRef": "Set6"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set4_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set4_crispmip.png",
"name": "Empires of the Ascended", "name": "Empires of the Ascended",
"nameRef": "Set4" "nameRef": "Set4"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set7_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set7_crispmip.png",
"name": "Glory in Navori", "name": "Glory in Navori",
"nameRef": "Set7" "nameRef": "Set7"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set8_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/setevent_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", "name": "Events",
"nameRef": "SetEvent" "nameRef": "SetEvent"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/sets/set6cde_crispmip.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/sets/set6cde_crispmip.png",
"name": "The Darkin Saga", "name": "The Darkin Saga",
"nameRef": "Set6cde" "nameRef": "Set6cde"
} }
], ],
"formats": [ "formats": [
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/formats/queue_select_standard_toggle_active.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/formats/queue_select_standard_toggle_active.png",
"name": "Standard", "name": "Standard",
"nameRef": "client_Formats_Standard_name" "nameRef": "client_Formats_Standard_name"
}, },
@ -836,7 +784,7 @@
"nameRef": "client_Deckbuilder_RulesFilters_Singleton" "nameRef": "client_Deckbuilder_RulesFilters_Singleton"
}, },
{ {
"iconAbsolutePath": "http://dd.b.pvp.net/4_10_0/core/en_us/img/formats/queue_select_eternal_toggle_active.png", "iconAbsolutePath": "http://dd.b.pvp.net/4_3_0/core/en_us/img/formats/queue_select_eternal_toggle_active.png",
"name": "Eternal", "name": "Eternal",
"nameRef": "client_Formats_Eternal_name" "nameRef": "client_Formats_Eternal_name"
}, },
@ -850,27 +798,5 @@
"name": "Even Cost Cards", "name": "Even Cost Cards",
"nameRef": "client_Formats_EvenCostCards_name" "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"
}
] ]
} }

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
Copyright Riot Games, Inc. 2019

View file

@ -1,40 +0,0 @@
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"
}
},
{...}
]

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
{
"locales": [
"en_us"
]
}

View file

@ -1 +0,0 @@
Copyright Riot Games, Inc. 2019

View file

@ -1,40 +0,0 @@
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"
}
},
{...}
]

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
{
"locales": [
"en_us"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View file

@ -101,12 +101,6 @@ pub async fn create_globalindexes_from_dd_latest(locale: &str) -> globals::Local
#[cfg(test)] #[cfg(test)]
mod tests { 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 { macro_rules! test_fetch {
( $id:ident, $version:literal, $locale:literal ) => { ( $id:ident, $version:literal, $locale:literal ) => {
#[tokio::test] #[tokio::test]
@ -118,29 +112,7 @@ mod tests {
}; };
} }
test_fetch!(test_fetch_4_9_0_en_us, "4_9_0", "en_us"); test_fetch!(test_fetch_4_5_0_en_us, "4_5_0", "en_us");
test_fetch!(test_fetch_4_9_0_it_it, "4_9_0", "it_it"); test_fetch!(test_fetch_4_5_0_it_it, "4_5_0", "it_it");
test_fetch!(test_fetch_latest_en_us, "latest", "en_us"); 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");
}

View file

@ -520,7 +520,9 @@ impl Deck {
/// ///
pub fn cards_are_allowed_in(&self, cards: &CardIndex, format: CardFormat) -> bool { pub fn cards_are_allowed_in(&self, cards: &CardIndex, format: CardFormat) -> bool {
self.contents.keys() self.contents.keys()
.filter_map(|cc| cc.to_card(cards)) .map(|cc| cc.to_card(&cards))
.filter(|o| o.is_some())
.map(|o| o.unwrap())
.all(|c| c.formats.contains(&format)) .all(|c| c.formats.contains(&format))
} }

View file

@ -196,7 +196,6 @@ pub enum CardKeyword {
/// Double Attack. /// Double Attack.
/// ///
/// > While attacking, it strikes both before AND at the same time as its blocker. /// > While attacking, it strikes both before AND at the same time as its blocker.
#[serde(rename = "DoubleStrike")]
DoubleAttack, DoubleAttack,
/// Vulnerable. /// Vulnerable.
@ -348,30 +347,6 @@ 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. /// > 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, 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. /// Unsupported card keyword.
#[serde(other)] #[serde(other)]
Unsupported, Unsupported,
@ -466,11 +441,6 @@ impl CardKeyword {
CardKeyword::Plunder => "", CardKeyword::Plunder => "",
CardKeyword::BlockElusive => "", CardKeyword::BlockElusive => "",
CardKeyword::Flow => "", CardKeyword::Flow => "",
CardKeyword::Capture => "<:capture:1056024295190577153>",
CardKeyword::Attack => "",
CardKeyword::ElementalSkill => "<:elementalskill:1165762476974026814>",
CardKeyword::LevelUp => "",
CardKeyword::Freljord => "<:freljord:1056024331437735936>",
CardKeyword::Unsupported => "<:invaliddeck:1056022952396730438>", CardKeyword::Unsupported => "<:invaliddeck:1056022952396730438>",
} }
} }
@ -533,7 +503,7 @@ mod tests {
test_deserialization!(deserialize_silenceindividualkeyword, r#""SilenceIndividualKeyword""#, CardKeyword::SilenceIndividualKeyword); test_deserialization!(deserialize_silenceindividualkeyword, r#""SilenceIndividualKeyword""#, CardKeyword::SilenceIndividualKeyword);
test_deserialization!(deserialize_skill, r#""Skill""#, CardKeyword::Skill); test_deserialization!(deserialize_skill, r#""Skill""#, CardKeyword::Skill);
test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder); test_deserialization!(deserialize_plunder, r#""Plunder""#, CardKeyword::Plunder);
test_deserialization!(deserialize_doubleattack, r#""DoubleStrike""#, CardKeyword::DoubleAttack); test_deserialization!(deserialize_doubleattack, r#""DoubleAttack""#, CardKeyword::DoubleAttack);
test_deserialization!(deserialize_vulnerable, r#""Vulnerable""#, CardKeyword::Vulnerable); test_deserialization!(deserialize_vulnerable, r#""Vulnerable""#, CardKeyword::Vulnerable);
test_deserialization!(deserialize_elusive, r#""Elusive""#, CardKeyword::Elusive); test_deserialization!(deserialize_elusive, r#""Elusive""#, CardKeyword::Elusive);
test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun); test_deserialization!(deserialize_stun, r#""Stun""#, CardKeyword::Stun);
@ -565,10 +535,5 @@ mod tests {
test_deserialization!(deserialize_deep, r#""Deep""#, CardKeyword::Deep); test_deserialization!(deserialize_deep, r#""Deep""#, CardKeyword::Deep);
test_deserialization!(deserialize_flow, r#""Flow""#, CardKeyword::Flow); test_deserialization!(deserialize_flow, r#""Flow""#, CardKeyword::Flow);
test_deserialization!(deserialize_equipment, r#""Equipment""#, CardKeyword::Equipment); 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); test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardKeyword::Unsupported);
} }

View file

@ -96,27 +96,25 @@ mod tests {
}; };
} }
test_fetch!(test_fetch_4_9_0_en_us_set1, "4_9_0", "en_us", "set1"); test_fetch!(test_fetch_4_5_0_en_us_set1, "4_5_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_5_0_en_us_set2, "4_5_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_5_0_en_us_set3, "4_5_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_5_0_en_us_set4, "4_5_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_5_0_en_us_set5, "4_5_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_5_0_en_us_set6, "4_5_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_5_0_en_us_set6cde, "4_5_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_5_0_en_us_set7, "4_5_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_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_set8, "4_9_0", "en_us", "set8");
test_fetch!(test_fetch_4_9_0_it_it_set1, "4_9_0", "it_it", "set1"); test_fetch!(test_fetch_4_5_0_it_it_set1, "4_5_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_5_0_it_it_set2, "4_5_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_5_0_it_it_set3, "4_5_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_5_0_it_it_set4, "4_5_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_5_0_it_it_set5, "4_5_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_5_0_it_it_set6, "4_5_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_5_0_it_it_set6cde, "4_5_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_5_0_it_it_set7, "4_5_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_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_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_set1, "latest", "en_us", "set1");
test_fetch!(test_fetch_latest_en_us_set2, "latest", "en_us", "set2"); test_fetch!(test_fetch_latest_en_us_set2, "latest", "en_us", "set2");
@ -127,7 +125,6 @@ mod tests {
test_fetch!(test_fetch_latest_en_us_set6cde, "latest", "en_us", "set6cde"); 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_set7, "latest", "en_us", "set7");
test_fetch!(test_fetch_latest_en_us_set7b, "latest", "en_us", "set7b"); test_fetch!(test_fetch_latest_en_us_set7b, "latest", "en_us", "set7b");
test_fetch!(test_fetch_latest_en_us_set8, "latest", "en_us", "set8");
} }

View file

@ -3,7 +3,6 @@
use crate::data::corebundle::rarity::{LocalizedCardRarity, LocalizedCardRarityIndex}; use crate::data::corebundle::rarity::{LocalizedCardRarity, LocalizedCardRarityIndex};
/// A possible [Card](super::card::Card) rarity. /// A possible [Card](super::card::Card) rarity.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum CardRarity { pub enum CardRarity {
/// The card has no rarity, as it probably is not [collectible](super::card::Card::collectible). /// The card has no rarity, as it probably is not [collectible](super::card::Card::collectible).
@ -20,10 +19,6 @@ pub enum CardRarity {
/// A champion (orange hexagon) card, sometimes referred to as *Legendary*. /// A champion (orange hexagon) card, sometimes referred to as *Legendary*.
Champion, Champion,
/// Unsupported rarity.
#[serde(other)]
Unsupported,
} }
impl CardRarity { impl CardRarity {
@ -38,32 +33,6 @@ impl CardRarity {
) -> Option<&'hm LocalizedCardRarity> { ) -> Option<&'hm LocalizedCardRarity> {
hm.get(self) 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)] #[cfg(test)]
@ -87,5 +56,9 @@ mod tests {
test_deserialization!(deserialize_rare, r#""Rare""#, CardRarity::Rare); test_deserialization!(deserialize_rare, r#""Rare""#, CardRarity::Rare);
test_deserialization!(deserialize_epic, r#""Epic""#, CardRarity::Epic); test_deserialization!(deserialize_epic, r#""Epic""#, CardRarity::Epic);
test_deserialization!(deserialize_champion, r#""Champion""#, CardRarity::Champion); test_deserialization!(deserialize_champion, r#""Champion""#, CardRarity::Champion);
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, CardRarity::Unsupported);
#[test]
fn deserialize_fallback() {
assert!(serde_json::de::from_str::<'static, CardRarity>("Xyzzy").is_err());
}
} }

View file

@ -32,28 +32,6 @@ pub enum CardRegion {
/// Runeterra. /// Runeterra.
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. /// Unsupported region.
#[serde(other)] #[serde(other)]
Unsupported, Unsupported,
@ -112,30 +90,6 @@ 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`]. /// Get the Discord emoji code associated with this [`CardRegion`].
pub fn discord_emoji(&self) -> &'static str { pub fn discord_emoji(&self) -> &'static str {
match self { match self {
@ -150,16 +104,6 @@ impl CardRegion {
CardRegion::PiltoverZaun => "<:piltoverzaun:1056022918959734835>", CardRegion::PiltoverZaun => "<:piltoverzaun:1056022918959734835>",
CardRegion::BandleCity => "<:bandlecity:1056024280493735976>", CardRegion::BandleCity => "<:bandlecity:1056024280493735976>",
CardRegion::Runeterra => "<:runeterra:1056022895031238727>", 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>", CardRegion::Unsupported => "<:invaliddeck:1056022952396730438>",
} }
} }
@ -236,15 +180,5 @@ mod tests {
test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardRegion::PiltoverZaun); test_deserialization!(deserialize_piltoverzaun, r#""PiltoverZaun""#, CardRegion::PiltoverZaun);
test_deserialization!(deserialize_bandlecity, r#""BandleCity""#, CardRegion::BandleCity); test_deserialization!(deserialize_bandlecity, r#""BandleCity""#, CardRegion::BandleCity);
test_deserialization!(deserialize_runeterra, r#""Runeterra""#, CardRegion::Runeterra); 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); test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardRegion::Unsupported);
} }

View file

@ -40,14 +40,6 @@ pub enum CardSet {
#[serde(rename = "Set7")] #[serde(rename = "Set7")]
GloryInNavori, GloryInNavori,
/// Heart of the Huntress.
#[serde(rename = "Set7b")]
HeartOfTheHuntress,
/// Fate's Voyage.
#[serde(rename = "Set8")]
FatesVoyage,
/// Events, cards released "outside" a set. /// Events, cards released "outside" a set.
#[serde(rename = "SetEvent")] #[serde(rename = "SetEvent")]
Events, Events,
@ -102,24 +94,6 @@ impl CardSet {
_ => None, _ => 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. /// Get the [`CardSet`] from its internal id.
@ -183,9 +157,6 @@ mod tests {
test_deserialization!(deserialize_set5, r#""Set5""#, CardSet::BeyondTheBandlewood); test_deserialization!(deserialize_set5, r#""Set5""#, CardSet::BeyondTheBandlewood);
test_deserialization!(deserialize_set6, r#""Set6""#, CardSet::Worldwalker); test_deserialization!(deserialize_set6, r#""Set6""#, CardSet::Worldwalker);
test_deserialization!(deserialize_set6cde, r#""Set6cde""#, CardSet::TheDarkinSaga); 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_setevent, r#""SetEvent""#, CardSet::Events);
test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardSet::Unsupported); test_deserialization!(deserialize_fallback, r#""Xyzzy""#, CardSet::Unsupported);
} }

View file

@ -3,7 +3,6 @@
use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex}; use crate::data::corebundle::speed::{LocalizedSpellSpeed, LocalizedSpellSpeedIndex};
/// A possible [`Spell`](super::type::CardType::Spell) speed. /// A possible [`Spell`](super::type::CardType::Spell) speed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum SpellSpeed { pub enum SpellSpeed {
/// Non-spell cards have this speed. /// Non-spell cards have this speed.
@ -15,9 +14,6 @@ pub enum SpellSpeed {
Fast, Fast,
/// Either a Burst or a Focus spell; to disambiguate between the two, check for the `Focus` keyword. /// Either a Burst or a Focus spell; to disambiguate between the two, check for the `Focus` keyword.
Burst, Burst,
/// Unsupported spell speed.
#[serde(other)]
Unsupported,
} }
impl SpellSpeed { impl SpellSpeed {
@ -54,5 +50,9 @@ mod tests {
test_deserialization!(deserialize_slow, r#""Slow""#, SpellSpeed::Slow); test_deserialization!(deserialize_slow, r#""Slow""#, SpellSpeed::Slow);
test_deserialization!(deserialize_fast, r#""Fast""#, SpellSpeed::Fast); test_deserialization!(deserialize_fast, r#""Fast""#, SpellSpeed::Fast);
test_deserialization!(deserialize_burst, r#""Burst""#, SpellSpeed::Burst); test_deserialization!(deserialize_burst, r#""Burst""#, SpellSpeed::Burst);
test_deserialization!(deserialize_unsupported, r#""Xyzzy""#, SpellSpeed::Unsupported);
#[test]
fn deserialize_fallback() {
assert!(serde_json::de::from_str::<'static, SpellSpeed>("Xyzzy").is_err());
}
} }

View file

@ -9,9 +9,6 @@ pub enum CardType {
/// A spell. /// A spell.
Spell, Spell,
/// A equipment.
Equipment,
/// An unit: either a minion, or a champion. /// 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. /// 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.
@ -36,7 +33,6 @@ pub enum CardType {
impl From<&CardType> for &'static str { impl From<&CardType> for &'static str {
fn from(r#type: &CardType) -> Self { fn from(r#type: &CardType) -> Self {
match r#type { match r#type {
CardType::Equipment => "Equipment",
CardType::Spell => "Spell", CardType::Spell => "Spell",
CardType::Unit => "Unit", CardType::Unit => "Unit",
CardType::Ability => "Ability", CardType::Ability => "Ability",

View file

@ -12,6 +12,8 @@ use crate::data::deckcode::deck::Deck;
use crate::data::deckcode::format::DeckCodeFormat; use crate::data::deckcode::format::DeckCodeFormat;
use crate::data::setbundle::r#type::CardType; use crate::data::setbundle::r#type::CardType;
use crate::data::setbundle::rarity::CardRarity; 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::data::setbundle::supertype::CardSupertype;
use crate::search::cardsearch::CardSearchEngine; use crate::search::cardsearch::CardSearchEngine;
@ -92,11 +94,13 @@ impl EventHandler {
} }
if !card.keywords.is_empty() { if !card.keywords.is_empty() {
e.field("Keywords", card.keywords.iter().map(|r| { e.field("Keywords", card.keywords.iter().map(|r|
let icon = r.discord_emoji(); format!(
let text = r.localized(&engine.globals.keywords).map_or_else(|| String::from("Unknown"), |l| l.name.clone()); "{} {}",
format!("{icon} {text}") r.discord_emoji(),
}).join(", "), true); r.localized(&engine.globals.keywords).map_or_else(|| String::from("Missing translation"), |l| l.name.clone())
)
).join(", "), true);
} }
e.field("Mana cost", format!("{} mana", card.cost), true); e.field("Mana cost", format!("{} mana", card.cost), true);
@ -117,30 +121,56 @@ impl EventHandler {
vec vec
}.join(", "), true); }.join(", "), true);
e.field("Regions", card.regions.iter().map(|r| { e.field("Regions", card.regions.iter().map(|r| match r {
let icon = r.discord_emoji(); CardRegion::Noxus => "<:noxus:1056022924169064498> Noxus",
let text = r.localized(&engine.globals.regions).map_or_else(|| String::from("Unknown"), |r| r.name.clone()); CardRegion::Demacia => "<:demacia:1056023014128484412> Demacia",
format!("{icon} {text}") 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",
}).join(", "), false); }).join(", "), false);
e.field("Set", { e.field("Set", match card.set {
let icon = card.set.discord_emoji(); CardSet::Foundations => "<:foundations:1071644734667366410> Foundations",
let text = card.set.localized(&engine.globals.sets).map_or_else(|| String::from("Unknown"), |r| r.name.clone()); CardSet::RisingTides => "<:rising_tides:1071644736126976160> Rising Tides",
format!("{icon} {text}") 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",
}, true); }, true);
let actual_rarity = match card.supertype { e.field("Rarity", match card.supertype {
CardSupertype::Champion => CardRarity::Champion, CardSupertype::Champion => "<:champion:1056024303856001034> Champion",
_ => card.rarity _ => 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);
e.field("Rarity", { e.color(match card.supertype {
let icon = actual_rarity.discord_emoji(); CardSupertype::Champion => 0x81541f,
let text = actual_rarity.localized(&engine.globals.rarities).map_or_else(|| String::from("Unknown"), |r| r.name.clone()); _ => match card.rarity {
format!("{icon} {text}") CardRarity::None => 0x202225,
} , true); CardRarity::Common => 0x1e6a49,
CardRarity::Rare => 0x244778,
e.color(actual_rarity.color()); CardRarity::Epic => 0x502970,
CardRarity::Champion => 0x81541f,
}
});
if !card.localized_flavor_text.is_empty() { if !card.localized_flavor_text.is_empty() {
e.footer(|f| f.text(card.localized_flavor_text.clone())); e.footer(|f| f.text(card.localized_flavor_text.clone()));

View file

@ -35,7 +35,7 @@ pub struct CardSearchEngine {
} }
impl CardSearchEngine { impl CardSearchEngine {
/// Create the [TextAnalyzer] for card text. /// Create the [tantivy::tokenizer::TextAnalyzer] for card text.
/// ///
/// It should not alter text significantly, as it may contain important game vocabulary terms. /// It should not alter text significantly, as it may contain important game vocabulary terms.
fn tokenizer() -> TextAnalyzer { fn tokenizer() -> TextAnalyzer {
@ -44,7 +44,7 @@ impl CardSearchEngine {
TextAnalyzer::from(SimpleTokenizer).filter(LowerCaser) TextAnalyzer::from(SimpleTokenizer).filter(LowerCaser)
} }
/// Create the [TextOptions] for card codes. /// Create the [tantivy::schema::TextOptions] for card codes.
/// ///
/// Card codes should: /// Card codes should:
/// - TODO: be tokenized without alterations; /// - TODO: be tokenized without alterations;
@ -63,7 +63,7 @@ impl CardSearchEngine {
.set_fast() .set_fast()
} }
/// Create the [TextOptions] for card keywords. /// Create the [tantivy::schema::TextOptions] for card keywords.
/// ///
/// Card keywords should: /// Card keywords should:
/// - be tokenized with the [CardSearchEngine::tokenizer]; /// - be tokenized with the [CardSearchEngine::tokenizer];
@ -78,7 +78,7 @@ impl CardSearchEngine {
) )
} }
/// Create the [TextOptions] for card text fields. /// Create the [tantivy::schema::TextOptions] for card text fields.
/// ///
/// Card text should: /// Card text should:
/// - TODO: be tokenized with the tokenizer for the locale language; /// - TODO: be tokenized with the tokenizer for the locale language;
@ -93,7 +93,7 @@ impl CardSearchEngine {
) )
} }
/// Create the [NumericOptions] for card numeric fields. /// Create the [tantivy::schema::NumericOptions] for card numeric fields.
/// ///
/// Card numbers should: /// Card numbers should:
/// - be indexed. /// - be indexed.

View file

@ -23,162 +23,118 @@ use teloxide::utils::html::escape;
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String { pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
let title = format!("<b><u>{}</u></b>\n", escape(&card.name),);
let title: String = display_title(&card.name); let stats = match &card.r#type {
let r#type: String = display_type(globals, card); CardType::Spell => format!("{} mana\n\n", escape(&card.cost.to_string()),),
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, &regions, &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!( CardType::Unit => format!(
"{} mana · {}|{}", "{} mana {}|{}\n\n",
escape(&card.cost.to_string()), escape(&card.cost.to_string()),
escape(&card.attack.to_string()), escape(&card.attack.to_string()),
escape(&card.health.to_string()), escape(&card.health.to_string()),
), ),
CardType::Landmark => format!("{} mana", &card.cost), CardType::Landmark => format!("{} mana\n\n", &card.cost),
_ => "".to_string(), _ => "".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, &regions, &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 the [CardSubtype]s in [Telegram Bot HTML]. /// Render a [CardSet] in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_subtypes(subtypes: &[CardSubtype]) -> String { fn display_set(set: &CardSet, hm: &LocalizedCardSetIndex) -> String {
let result = subtypes format!(
.iter() "<i>{}</i>",
.map(|s| titlecase(s)) set.localized(hm)
.map(|s| escape(&s)) .map(|o| format!("<i>{}</i>", escape(&o.name)))
.map(|s| format!("<i>{s}</i>")) .unwrap_or_else(|| "Unknown".to_string())
.join(", "); )
}
if result.is_empty() { /// Render a slice of [CardRegion]s in [Telegram Bot HTML].
result ///
} else { /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
result + "\n" 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 a slice of [CardKeyword]s in [Telegram Bot HTML]. /// Render a slice of [CardKeyword]s in [Telegram Bot HTML].
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
fn display_keywords(keywords: &[CardKeyword], hm: &LocalizedCardKeywordIndex) -> String { fn display_keywords(keywords: &[CardKeyword], hm: &LocalizedCardKeywordIndex) -> String {
let result = keywords format!(
.iter() "{}\n",
.filter(|keyword| !matches!(keyword, keywords
CardKeyword::Countdown | .iter()
CardKeyword::OnPlay | .map(|keyword| keyword
CardKeyword::Landmark | .localized(hm)
CardKeyword::Shurima | .map(|o| format!("[<b>{}</b>]", escape(&o.name)))
CardKeyword::Noxus | .unwrap_or_else(|| "Unknown".to_string()))
CardKeyword::ClobberNoEmptySlotRequirement | .join(" ")
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]. /// Render a [Card::localized_description_text] in [Telegram Bot HTML].
@ -203,46 +159,6 @@ 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`. /// Render a [Deck] in [Telegram Bot HTML], with an optional `name`.
/// ///
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style /// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
@ -286,19 +202,19 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
}) })
.join("\n"); .join("\n");
let mut tags: Vec<String> = vec![]; let mut tags: Vec<&'static str> = vec![];
let regions = if let Some(regions) = deck.standard(index) { let regions = if let Some(regions) = deck.standard(index) {
tags.push("#Standard_4_5".to_string()); tags.push("#Standard_4_3");
regions regions
} else if let Some(regions) = deck.eternal(index) { } else if let Some(regions) = deck.eternal(index) {
tags.push("#Eternal".to_string()); tags.push("#Eternal");
regions regions
} else if let Some(regions) = deck.unlimited_champions(index) { } else if let Some(regions) = deck.unlimited_champions(index) {
tags.push("#UnlimitedChampions".to_string()); tags.push("#UnlimitedChampions");
regions regions
} else if let Some(regions) = deck.singleton(index) { } else if let Some(regions) = deck.singleton(index) {
tags.push("#Singleton".to_string()); tags.push("#Singleton");
regions regions
} else { } else {
HashSet::new() HashSet::new()
@ -306,8 +222,18 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
for region in regions { for region in regions {
tags.push(match region { tags.push(match region {
CardRegion::Unsupported => "<i>Unknown</i>".to_string(), CardRegion::Noxus => "#Noxus",
_ => format!("#{}", region.to_tag().map_or_else(|| "<i>Unknown</i>", |r| r)), 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>",
}) })
} }
@ -319,14 +245,3 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s
None => format!("<code>{}</code>\n{}\n{}", &code, &tags, &cards), 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(),
}
}