From da68928f125cc020c8712245cc968f5d7b1f0f51 Mon Sep 17 00:00:00 2001 From: w Date: Sat, 19 Jul 2025 23:24:19 -0300 Subject: [PATCH 1/8] requirements hotfix --- requirements.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/requirements.txt b/requirements.txt index d747e71..44138e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,8 +15,20 @@ # along with this program. If not, see https://www.gnu.org/licenses/. blinker==1.9.0 +blurhash==1.1.4 +cairocffi==1.7.1 +CairoSVG==2.8.2 +certifi==2025.4.26 +cffi==1.17.1 +charset-normalizer==3.4.2 click==8.1.8 +cssselect2==0.8.0 +decorator==5.2.1 +defusedxml==0.7.1 + Flask==3.1.0 +gunicorn==23.0.0 +idna==3.10 itsdangerous==2.2.0 Jinja2==3.1.6 MarkupSafe==3.0.2 @@ -24,3 +36,14 @@ Werkzeug==3.1.3 Misskey.py==4.1.0 Mastodon.py==1.8.1 filetype==1.2.0 +packaging==25.0 +pillow==11.2.1 +pycparser==2.22 +python-dateutil==2.9.0.post0 +python-magic==0.4.27 +requests==2.32.3 +six==1.17.0 +tinycss2==1.4.0 +urllib3==2.4.0 +watchdog==6.0.0 +webencodings==0.5.1 From da27513334d21b8d0f2a1d0f4e7744cad835c584 Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 00:17:28 -0300 Subject: [PATCH 2/8] Enhance card creator UI with instructions and clarifications for users --- web/templates/card_creator.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index 5977472..256963c 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -34,13 +34,18 @@

Card Creator

- +
+

Use this tool to create custom Kemoverse cards! Fill in the details below, upload your art, and generate a card image.

+

Note: The card ID is generated based on the details, so make sure to fill them out!

+ + +
-
+



@@ -55,6 +60,7 @@ +

All the processing is done in your browser, no data is sent to the server.

From 8f1dda579f20ed2bc3383db2df834217aafd69cb Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 00:43:41 -0300 Subject: [PATCH 3/8] Add character card standards documentation including dimensions, layout, rarity tiers, and stat allocation rules --- docs/design.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/design.md diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..ea05f08 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,89 @@ +# ðŸŽī Character Card Standards + +This page explains the standard format and rules used for character cards. + +--- + +## 📐 Card Dimensions + +- **Canvas Size:** `800x1120px` +- **Print Equivalent:** 2.5 x 3.5 inches (at 300dpi, classic card format) + +--- + +## ðŸ§Đ Layout Regions + +Each card is composed of the following sections: + +- **Top Name Bar** – Displays character name +- **Series Text** – Indicates the collection or set the card belongs to +- **Main Art Zone** – Character illustration +- **Stats Row** – Shows 3 core stats: + - **Power** + - **Charm** + - **Wit** +- **Flavor Text Box** – Optional quote or description +- **Rarity Marker** – Icon, stars, or color code +- **Artist name** - The creator of the Character illustration +- **Kemoverse ID** - Unique SHA-256 code generated for the card, based on the information of the card (Ommiting art) + + +--- + +## 🌟 Rarity Tiers + +Each card belongs to a **rarity tier** which affects both appearance and stat limits. + +| Rarity | Total Stats Cap | Visual Features Recommended | +|--------------|------------------|-------------------------------------| +| **Common** | 100 | Plain template, minimal shine | +| **Uncommon** | 140 | Slightly enhanced frame | +| **Rare** | 180 | Decorative borders, mild shine | +| **Super Rare** | 220 | Unique frame, vibrant shine | +| **Legendary** | 250 | Custom art frames, animated effects | + +--- + +## ðŸŽē Rarity Distribution Guidelines + +The following table shows an **example** of how many cards you might include *in a 20-card pack*, but the actual number of cards per rarity is flexible. + +What **matters most** is that rarities follow the **percentage limits** defined below. +These limits ensure balance across the entire card pool — regardless of total size. + +| Rarity | Max % Allowed | Example (20 cards) | Weight | Notes | +|--------------|----------------|--------------------|---------|----------------------------------------| +| **Common** | No upper limit | 10 cards (50%) | 0.7 | Can exceed 50% of total if needed | +| **Uncommon** | 20% | 4 cards | 0.2 | Should not exceed 20% of total | +| **Rare** | 15% | 3 cards | 0.08 | Should not exceed 15% of total | +| **Super Rare** | 10% | 2 cards | 0.015 | Should not exceed 10% of total | +| **Legendary** | 5% | 1 card | 0.005 | Should not exceed 5% of total | + +> ✅ You can include more cards overall (e.g. 40, 100, etc.), +> 🔒 But rarities from **Uncommon to Legendary** must **not exceed their percentage caps**. +> ðŸŠķ **Common** cards are the only exception — they can scale freely and fill the remaining space. + + +--- + +## ðŸ§Ū Stat Allocation Rules + +Cards have **three core stats**: `Power`, `Charm`, and `Wit`. + +- The **sum** of these three stats **must not exceed** the max for the rarity tier. +- Stats can be distributed however you want, as long as the total stays within the cap. +- Minimum stat per field: `0` +- Maximum per stat: Not enforced, but try to keep cards balanced visually and mechanically. + +--- + +## âœĻ Rarity Visual Guide + +The standard cards have the following : + +- ðŸŒą Common – Gray +- ðŸ”ļ Uncommon – Green +- 🔷 Rare – Blue +- ðŸ”Ū Epic – Gold +- 🌈 Legendary – Purple + From 01df661b6079b4c7cf5b832e6e6aa88e997775cf Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 00:48:53 -0300 Subject: [PATCH 4/8] Add stat restriction feature based on rarity caps in card creator --- web/templates/card_creator.html | 60 +++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index 256963c..e048df1 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -41,8 +41,10 @@

Use this tool to create custom Kemoverse cards! Fill in the details below, upload your art, and generate a card image.

Note: The card ID is generated based on the details, so make sure to fill them out!

- - +
+ + +


@@ -86,9 +88,19 @@ const artistInput = document.getElementById("artistInput"); const downloadBtn = document.getElementById("downloadBtn"); const uploadArt = document.getElementById("uploadArt"); const frameSelect = document.getElementById("frameSelect"); +const restrictStatsToggle = document.getElementById("restrictStatsToggle"); let frameImage = new Image(); let uploadedImage = null; +// Rarity caps for stats +const rarityCaps = { + "v1_common.png": 100, + "v1_uncommon.png": 140, + "v1_rare.png": 180, + "v1_epic.png": 220, + "v1_legendary.png": 250 +}; + // Generate a unique ID for the card based on pack and card name async function generateCardId(...args) { const encoder = new TextEncoder(); @@ -226,6 +238,50 @@ function wrapText(text, x, y, maxWidth, lineHeight) { input.addEventListener("input", drawCard); }); +// Enforce stat caps based on rarity and frame +function enforceStatCap(changedInput) { + if (!restrictStatsToggle.checked) return; + const cap = rarityCaps[frameSelect.value] || 9999; + let power = parseInt(powerInput.value) || 0; + let charm = parseInt(charmInput.value) || 0; + let wit = parseInt(witInput.value) || 0; + let total = power + charm + wit; + if (total > cap) { + // Reduce the changed input to fit the cap + const excess = total - cap; + if (changedInput === powerInput) { + power = Math.max(0, power - excess); + powerInput.value = power; + } else if (changedInput === charmInput) { + charm = Math.max(0, charm - excess); + charmInput.value = charm; + } else if (changedInput === witInput) { + wit = Math.max(0, wit - excess); + witInput.value = wit; + } + } +} + +[powerInput, charmInput, witInput].forEach(input => { + input.addEventListener("input", function() { + enforceStatCap(this); + drawCard(); + }); +}); + +frameSelect.addEventListener("change", function() { + if (restrictStatsToggle.checked) { + enforceStatCap(); + } + updateFrame(); +}); + +restrictStatsToggle.addEventListener("change", function() { + if (this.checked) { + enforceStatCap(); + } +}); + // Download button downloadBtn.addEventListener("click", () => { // Sanitize file name From 7ae5c8f62a64454d82ffaa97dc5bab66e723e2dc Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 00:49:46 -0300 Subject: [PATCH 5/8] Update frame options in card creator to include stat values for clarity --- web/templates/card_creator.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index e048df1..00fcc76 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -55,11 +55,11 @@

All the processing is done in your browser, no data is sent to the server.

From 21005d311c252806907242de28beea8986aca0a5 Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 00:58:58 -0300 Subject: [PATCH 6/8] Add white background to card drawing for improved visibility --- web/templates/card_creator.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index 00fcc76..26d6819 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -142,6 +142,10 @@ frameSelect.addEventListener("change", updateFrame); function drawCard() { ctx.clearRect(0, 0, canvas.width, canvas.height); + // Place a white background + ctx.fillStyle = "#FFFFFF"; // white background + // make it smaller than the size of the canvas + ctx.fillRect(30, 30, canvas.width - 60, canvas.height - 60); // Draw uploaded art as cropped and centered background if present if (uploadedImage) { From 98c36f913f62be1a5829052a6dc00873a6bedf16 Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 01:01:54 -0300 Subject: [PATCH 7/8] Fix ZIP file naming in download functionality for card creator --- web/templates/card_creator.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index 26d6819..ae7b5d5 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -347,6 +347,7 @@ zipBtn.onclick = async () => { const safePack = (packInput.value || "pack").replace(/[^a-z0-9_\-]/gi, "_"); const imgFileName = `kemoverse_${safePack}_${safeName}.webp`; const jsonFileName = `kemoverse_${safePack}_${safeName}.json`; + const zipFileName = `kemoverse_${safePack}_${safeName}.zip`; // Create ZIP const zip = new JSZip(); @@ -360,7 +361,7 @@ zipBtn.onclick = async () => { const content = await zip.generateAsync({type: "blob"}); const link = document.createElement("a"); link.href = URL.createObjectURL(content); - link.download = `kemoverse_${safePack}_${safeName}.zip`; + link.download = zipFileName; link.click(); }; From a248eb48642988271d7fb44f0dfb4107e5e7f0ad Mon Sep 17 00:00:00 2001 From: w Date: Sun, 20 Jul 2025 01:39:01 -0300 Subject: [PATCH 8/8] Add Pack Index input and update card ID generation in card creator --- web/templates/card_creator.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/web/templates/card_creator.html b/web/templates/card_creator.html index ae7b5d5..e36325a 100644 --- a/web/templates/card_creator.html +++ b/web/templates/card_creator.html @@ -40,6 +40,8 @@

Use this tool to create custom Kemoverse cards! Fill in the details below, upload your art, and generate a card image.

Note: The card ID is generated based on the details, so make sure to fill them out!

+ +
@@ -51,6 +53,7 @@


+



@@ -62,6 +65,8 @@ +

PI: Pack Index is used to identify the card within a pack.

+

SHA: Secure Hash Algorithm is used to generate a unique ID for the card based on its attributes.

All the processing is done in your browser, no data is sent to the server.

@@ -209,9 +214,9 @@ function drawCard() { } async function drawCardId() { - const id = await generateCardId(packInput.value, nameInput.value, powerInput.value, charmInput.value, witInput.value, artistInput.value); + const id = await generateCardId(packInput.value, nameInput.value, powerInput.value, charmInput.value, witInput.value, artistInput.value,packIdInput.value); ctx.font = "15px sans-serif"; - ctx.fillText("Kemoverse - " + id, canvas.width/2, canvas.height - 30); + ctx.fillText("Kemoverse - SHA: " + id + " - PI:" + packIdInput.value, canvas.width/2, canvas.height - 30); } @@ -238,7 +243,7 @@ function wrapText(text, x, y, maxWidth, lineHeight) { } // Update card live when inputs change -[nameInput, powerInput, charmInput, witInput, flavorInput,packInput,artistInput].forEach(input => { +[nameInput, powerInput, charmInput, witInput, flavorInput, packInput, artistInput, packIdInput].forEach(input => { input.addEventListener("input", drawCard); }); @@ -306,13 +311,14 @@ jsonBtn.onclick = () => { const cardData = { name: nameInput.value, pack: packInput.value, + pack_id: packIdInput.value, power: powerInput.value, charm: charmInput.value, wit: witInput.value, flavor: flavorInput.value, artist: artistInput.value, frame: frameSelect.value, - id: generateCardId(packInput.value, nameInput.value, powerInput.value, charmInput.value, witInput.value, artistInput.value) + id: generateCardId(packInput.value, nameInput.value, powerInput.value, charmInput.value, witInput.value, artistInput.value, packIdInput.value) }; const safeName = (nameInput.value || "card").replace(/[^a-z0-9_\-]/gi, "_"); const safePack = (packInput.value || "pack").replace(/[^a-z0-9_\-]/gi, "_"); @@ -335,6 +341,7 @@ zipBtn.onclick = async () => { const cardData = { name: nameInput.value, pack: packInput.value, + pack_id: packIdInput.value, power: powerInput.value, charm: charmInput.value, wit: witInput.value,