From 1aaf3530669293c55c50c80386173e3574c29b57 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Wed, 1 Apr 2026 14:45:53 -0400 Subject: [PATCH] renamed the Popes/0-card trumps from Earthman deck (feat. new apps.epic migrations to reseed); fixes to card deck horizontal scroll speed, game_kit.html, to make scrolling feel more natural --- .../migrations/0018_alter_tarotcard_suit.py | 18 +++++ .../0019_rename_earthman_schiz_and_popes.py | 70 +++++++++++++++++++ src/apps/epic/models.py | 3 - .../static/apps/gameboard/game-kit.js | 60 ++++++++++++++-- src/static_src/scss/_game-kit.scss | 8 +++ 5 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 src/apps/epic/migrations/0018_alter_tarotcard_suit.py create mode 100644 src/apps/epic/migrations/0019_rename_earthman_schiz_and_popes.py diff --git a/src/apps/epic/migrations/0018_alter_tarotcard_suit.py b/src/apps/epic/migrations/0018_alter_tarotcard_suit.py new file mode 100644 index 0000000..5760b54 --- /dev/null +++ b/src/apps/epic/migrations/0018_alter_tarotcard_suit.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0 on 2026-04-01 17:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('epic', '0017_tableseat_significator_fk'), + ] + + operations = [ + migrations.AlterField( + model_name='tarotcard', + name='suit', + field=models.CharField(blank=True, choices=[('WANDS', 'Wands'), ('CUPS', 'Cups'), ('SWORDS', 'Swords'), ('PENTACLES', 'Pentacles')], max_length=10, null=True), + ), + ] diff --git a/src/apps/epic/migrations/0019_rename_earthman_schiz_and_popes.py b/src/apps/epic/migrations/0019_rename_earthman_schiz_and_popes.py new file mode 100644 index 0000000..2cbaae7 --- /dev/null +++ b/src/apps/epic/migrations/0019_rename_earthman_schiz_and_popes.py @@ -0,0 +1,70 @@ +""" +Data migration: rename The Schiz (card 0) and the five Pope cards (cards 1–5) +in the Earthman deck. + + 0: "The Schiz" → "The Nomad" + 1: "Pope 1: Chancellor" → "Pope 1: The Schizo" + 2: "Pope 2: President" → "Pope 2: The Despot" + 3: "Pope 3: Tsar" → "Pope 3: The Capitalist" + 4: "Pope 4: Chairman" → "Pope 4: The Fascist" + 5: "Pope 5: Emperor" → "Pope 5: The War Machine" +""" +from django.db import migrations + + +NEW_NAMES = { + 0: ("The Nomad", "the-nomad"), + 1: ("Pope 1: The Schizo", "pope-1-the-schizo"), + 2: ("Pope 2: The Despot", "pope-2-the-despot"), + 3: ("Pope 3: The Capitalist", "pope-3-the-capitalist"), + 4: ("Pope 4: The Fascist", "pope-4-the-fascist"), + 5: ("Pope 5: The War Machine","pope-5-the-war-machine"), +} + +OLD_NAMES = { + 0: ("The Schiz", "the-schiz"), + 1: ("Pope 1: Chancellor", "pope-1-chancellor"), + 2: ("Pope 2: President", "pope-2-president"), + 3: ("Pope 3: Tsar", "pope-3-tsar"), + 4: ("Pope 4: Chairman", "pope-4-chairman"), + 5: ("Pope 5: Emperor", "pope-5-emperor"), +} + + +def rename_forward(apps, schema_editor): + TarotCard = apps.get_model("epic", "TarotCard") + DeckVariant = apps.get_model("epic", "DeckVariant") + + earthman = DeckVariant.objects.filter(slug="earthman").first() + if not earthman: + return + + for number, (new_name, new_slug) in NEW_NAMES.items(): + TarotCard.objects.filter( + deck_variant=earthman, arcana="MAJOR", number=number + ).update(name=new_name, slug=new_slug) + + +def rename_reverse(apps, schema_editor): + TarotCard = apps.get_model("epic", "TarotCard") + DeckVariant = apps.get_model("epic", "DeckVariant") + + earthman = DeckVariant.objects.filter(slug="earthman").first() + if not earthman: + return + + for number, (old_name, old_slug) in OLD_NAMES.items(): + TarotCard.objects.filter( + deck_variant=earthman, arcana="MAJOR", number=number + ).update(name=old_name, slug=old_slug) + + +class Migration(migrations.Migration): + + dependencies = [ + ("epic", "0018_alter_tarotcard_suit"), + ] + + operations = [ + migrations.RunPython(rename_forward, reverse_code=rename_reverse), + ] diff --git a/src/apps/epic/models.py b/src/apps/epic/models.py index fb39d57..c60cf4f 100644 --- a/src/apps/epic/models.py +++ b/src/apps/epic/models.py @@ -213,13 +213,11 @@ class TarotCard(models.Model): CUPS = "CUPS" SWORDS = "SWORDS" PENTACLES = "PENTACLES" # Fiorentine 4th suit - COINS = "COINS" # Earthman 4th suit (Ossum / Stone) SUIT_CHOICES = [ (WANDS, "Wands"), (CUPS, "Cups"), (SWORDS, "Swords"), (PENTACLES, "Pentacles"), - (COINS, "Coins"), ] deck_variant = models.ForeignKey( @@ -282,7 +280,6 @@ class TarotCard(models.Model): self.WANDS: 'fa-wand-sparkles', self.CUPS: 'fa-trophy', self.SWORDS: 'fa-gun', - self.COINS: 'fa-star', self.PENTACLES: 'fa-star', }.get(self.suit, '') diff --git a/src/apps/gameboard/static/apps/gameboard/game-kit.js b/src/apps/gameboard/static/apps/gameboard/game-kit.js index 5e34d9f..5ad9742 100644 --- a/src/apps/gameboard/static/apps/gameboard/game-kit.js +++ b/src/apps/gameboard/static/apps/gameboard/game-kit.js @@ -67,6 +67,9 @@ function initGameKitPage() { fanContent.innerHTML = html; cards = Array.from(fanContent.querySelectorAll('.fan-card')); if (currentIndex >= cards.length) currentIndex = 0; + cards.forEach(function(c) { + c.style.transition = 'transform 0.18s ease-out, opacity 0.18s ease-out'; + }); updateFan(); dialog.showModal(); }); @@ -84,6 +87,21 @@ function initGameKitPage() { updateFan(); } + // Step through multiple cards one at a time so intermediate cards are visible + var _navTimer = null; + function navigateAnimated(steps) { + if (!cards.length || steps === 0) return; + clearTimeout(_navTimer); + var sign = steps > 0 ? 1 : -1; + var remaining = Math.abs(steps); + function tick() { + navigate(sign); + remaining--; + if (remaining > 0) _navTimer = setTimeout(tick, 60); + } + tick(); + } + // Click on the dark backdrop (the dialog or fan-wrap itself, not on any card child) closes var fanWrap = dialog.querySelector('.tarot-fan-wrap'); dialog.addEventListener('click', function(e) { @@ -96,16 +114,46 @@ function initGameKitPage() { if (e.key === 'ArrowLeft') navigate(-1); }); - // Mousewheel navigation — throttled so each detent advances one card - var lastWheel = 0; + // Mousewheel navigation — accumulate delta, cap at 3 cards per event so fast + // spins don't overshoot; CSS transitions handle the visual smoothness. + var wheelAccum = 0; + var wheelDecayTimer = null; + var WHEEL_STEP = 150; dialog.addEventListener('wheel', function(e) { e.preventDefault(); - var now = Date.now(); - if (now - lastWheel < 150) return; - lastWheel = now; - navigate(e.deltaY > 0 ? 1 : -1); + clearTimeout(wheelDecayTimer); + wheelAccum += e.deltaY; + var steps = Math.trunc(wheelAccum / WHEEL_STEP); + if (steps !== 0) { + steps = Math.sign(steps) * Math.min(Math.abs(steps), 3); + wheelAccum -= steps * WHEEL_STEP; + navigate(steps); + } + wheelDecayTimer = setTimeout(function() { wheelAccum = 0; }, 200); }, { passive: false }); + // Touch/swipe navigation — uses navigateAnimated so intermediate cards are visible + var touchStartX = 0; + var touchStartY = 0; + var touchStartTime = 0; + dialog.addEventListener('touchstart', function(e) { + touchStartX = e.touches[0].clientX; + touchStartY = e.touches[0].clientY; + touchStartTime = Date.now(); + }, { passive: true }); + dialog.addEventListener('touchend', function(e) { + var dx = e.changedTouches[0].clientX - touchStartX; + var dy = e.changedTouches[0].clientY - touchStartY; + if (Math.abs(dy) > Math.abs(dx)) return; // vertical swipe — ignore + if (Math.abs(dx) < 60) return; // dead zone — raise to 40–60 for more deliberate swipe required + var elapsed = Math.max(1, Date.now() - touchStartTime); + var velocity = Math.abs(dx) / elapsed; // px/ms + var steps = velocity > 0.8 // flick threshold — raise (e.g. 0.6) so more swipes use drag formula + ? Math.max(1, Math.round(velocity * 4)) // flick multiplier — lower (e.g. 4–5) to reduce cards per fast flick + : Math.round(Math.abs(dx) / 150); // slow-drag divisor — raise (e.g. 120–150) for fewer cards per short drag + navigateAnimated(dx < 0 ? steps : -steps); + }, { passive: true }); + prevBtn.addEventListener('click', function() { navigate(-1); }); nextBtn.addEventListener('click', function() { navigate(1); }); diff --git a/src/static_src/scss/_game-kit.scss b/src/static_src/scss/_game-kit.scss index 4a2c014..4b7dd0c 100644 --- a/src/static_src/scss/_game-kit.scss +++ b/src/static_src/scss/_game-kit.scss @@ -227,6 +227,14 @@ align-items: center; justify-content: center; perspective: 900px; + + button { + box-shadow: none; + + &:hover, &.active { + box-shadow: none; + } + } } .tarot-fan {