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
This commit is contained in:
18
src/apps/epic/migrations/0018_alter_tarotcard_suit.py
Normal file
18
src/apps/epic/migrations/0018_alter_tarotcard_suit.py
Normal file
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
]
|
||||
@@ -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, '')
|
||||
|
||||
|
||||
@@ -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); });
|
||||
|
||||
|
||||
@@ -227,6 +227,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
perspective: 900px;
|
||||
|
||||
button {
|
||||
box-shadow: none;
|
||||
|
||||
&:hover, &.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tarot-fan {
|
||||
|
||||
Reference in New Issue
Block a user