SIG SELECT: Nomad/Schizo locked by default; Note-unlock gate — TDD
- _filter_major_unlocks(cards, user): strips Major 0 (Nomad) and Major 1 (Schizo) unless user has matching 'nomad'/'schizo' Note; unauthenticated users see 0 majors - levity_sig_cards(room, user) / gravity_sig_cards(room, user): accept user param; default 16 court cards, up to 18 with both Note unlocks - View wires user into both calls; _sig_unique_cards / sig_deck_cards unchanged (game-table deck still includes all 18 unique) - _full_sig_setUp: seats now carry deck_variant=earthman - SigCardHelperTest: 4 new ITs (default 16, nomad +1, schizo +1); empty-deck test updated to clear seats + owner - SigSelectRenderingTest: 18-card test updated to 16-default + 3 Note-unlock ITs Pending: superusers auto-granted nomad + schizo Notes on creation (ask user) Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -491,14 +491,27 @@ def _sig_unique_cards(room):
|
||||
return wands_crowns + swords_cups + major
|
||||
|
||||
|
||||
def levity_sig_cards(room):
|
||||
"""The 18 cards available to the levity group (PC/NC/SC)."""
|
||||
return _sig_unique_cards(room)
|
||||
def _filter_major_unlocks(cards, user):
|
||||
"""Remove Nomad (0) and Schizo (1) unless the user has the matching Note unlock."""
|
||||
if user is None or not user.is_authenticated:
|
||||
return [c for c in cards if c.arcana != TarotCard.MAJOR]
|
||||
earned = set(user.notes.values_list("slug", flat=True))
|
||||
return [
|
||||
c for c in cards
|
||||
if c.arcana != TarotCard.MAJOR
|
||||
or (c.number == 0 and "nomad" in earned)
|
||||
or (c.number == 1 and "schizo" in earned)
|
||||
]
|
||||
|
||||
|
||||
def gravity_sig_cards(room):
|
||||
"""The 18 cards available to the gravity group (BC/EC/AC)."""
|
||||
return _sig_unique_cards(room)
|
||||
def levity_sig_cards(room, user=None):
|
||||
"""Cards available to the levity group (PC/NC/SC), filtered by user's Note unlocks."""
|
||||
return _filter_major_unlocks(_sig_unique_cards(room), user)
|
||||
|
||||
|
||||
def gravity_sig_cards(room, user=None):
|
||||
"""Cards available to the gravity group (BC/EC/AC), filtered by user's Note unlocks."""
|
||||
return _filter_major_unlocks(_sig_unique_cards(room), user)
|
||||
|
||||
|
||||
def sig_seat_order(room):
|
||||
|
||||
@@ -445,25 +445,44 @@ class SigReservationModelTest(TestCase):
|
||||
|
||||
|
||||
class SigCardHelperTest(TestCase):
|
||||
"""levity_sig_cards() and gravity_sig_cards() return 18 cards each.
|
||||
"""levity_sig_cards() and gravity_sig_cards() return 16 courts by default;
|
||||
Nomad/Schizo added when the user has the matching Note unlock.
|
||||
Relies on the Earthman deck seeded by migrations (no manual card creation).
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
# Earthman deck is already seeded by migrations
|
||||
from django.utils import timezone
|
||||
self.earthman = DeckVariant.objects.get(slug="earthman")
|
||||
self.owner = User.objects.create(email="founder@test.io")
|
||||
self.owner.equipped_deck = self.earthman
|
||||
self.owner.save()
|
||||
self.room = Room.objects.create(name="Card Test", owner=self.owner)
|
||||
TableSeat.objects.create(
|
||||
room=Room.objects.create(name="Card Test", owner=self.owner),
|
||||
gamer=self.owner, slot_number=1, role="PC",
|
||||
deck_variant=self.earthman,
|
||||
)
|
||||
self.room = self.owner.table_seats.first().room
|
||||
self._tz = timezone
|
||||
|
||||
def test_levity_sig_cards_returns_18(self):
|
||||
cards = levity_sig_cards(self.room)
|
||||
self.assertEqual(len(cards), 18)
|
||||
def test_levity_sig_cards_returns_16_without_notes(self):
|
||||
cards = levity_sig_cards(self.room, self.owner)
|
||||
self.assertEqual(len(cards), 16)
|
||||
|
||||
def test_gravity_sig_cards_returns_18(self):
|
||||
cards = gravity_sig_cards(self.room)
|
||||
self.assertEqual(len(cards), 18)
|
||||
def test_gravity_sig_cards_returns_16_without_notes(self):
|
||||
cards = gravity_sig_cards(self.room, self.owner)
|
||||
self.assertEqual(len(cards), 16)
|
||||
|
||||
def test_nomad_note_includes_nomad(self):
|
||||
from apps.drama.models import Note
|
||||
Note.objects.create(user=self.owner, slug="nomad", earned_at=self._tz.now())
|
||||
cards = levity_sig_cards(self.room, self.owner)
|
||||
self.assertEqual(len(cards), 17)
|
||||
self.assertTrue(any(c.number == 0 and c.arcana == "MAJOR" for c in cards))
|
||||
|
||||
def test_schizo_note_includes_schizo(self):
|
||||
from apps.drama.models import Note
|
||||
Note.objects.create(user=self.owner, slug="schizo", earned_at=self._tz.now())
|
||||
cards = levity_sig_cards(self.room, self.owner)
|
||||
self.assertEqual(len(cards), 17)
|
||||
self.assertTrue(any(c.number == 1 and c.arcana == "MAJOR" for c in cards))
|
||||
|
||||
def test_levity_and_gravity_share_same_card_objects(self):
|
||||
"""Both piles draw from the same 18 TarotCard instances — visual distinction
|
||||
@@ -475,11 +494,13 @@ class SigCardHelperTest(TestCase):
|
||||
sorted(c.pk for c in gravity),
|
||||
)
|
||||
|
||||
def test_returns_empty_when_no_equipped_deck(self):
|
||||
def test_returns_empty_when_no_deck_on_seats_or_owner(self):
|
||||
"""Falls back to empty list when neither seats nor owner have a deck."""
|
||||
self.room.table_seats.update(deck_variant=None)
|
||||
self.owner.equipped_deck = None
|
||||
self.owner.save()
|
||||
self.assertEqual(levity_sig_cards(self.room), [])
|
||||
self.assertEqual(gravity_sig_cards(self.room), [])
|
||||
self.assertEqual(levity_sig_cards(self.room, self.owner), [])
|
||||
self.assertEqual(gravity_sig_cards(self.room, self.owner), [])
|
||||
|
||||
|
||||
class TarotCardCautionsTest(TestCase):
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.drama.models import GameEvent
|
||||
from apps.drama.models import GameEvent, Note
|
||||
from apps.lyric.models import Token, User
|
||||
from apps.epic.models import (
|
||||
Character, DeckVariant, GateSlot, Room, RoomInvite, SigReservation, TableSeat, TarotCard,
|
||||
@@ -1032,7 +1032,8 @@ def _full_sig_setUp(test_case, role_order=None):
|
||||
slot.status = GateSlot.FILLED
|
||||
slot.save()
|
||||
TableSeat.objects.create(
|
||||
room=room, gamer=gamer, slot_number=i, role=role, role_revealed=True,
|
||||
room=room, gamer=gamer, slot_number=i, role=role,
|
||||
role_revealed=True, deck_variant=earthman,
|
||||
)
|
||||
room.gate_status = Room.OPEN
|
||||
room.table_status = Room.SIG_SELECT
|
||||
@@ -1055,7 +1056,24 @@ class SigSelectRenderingTest(TestCase):
|
||||
response = self.client.get(self.url)
|
||||
self.assertContains(response, "id_sig_deck")
|
||||
|
||||
def test_sig_deck_contains_18_sig_cards(self):
|
||||
def test_sig_deck_contains_16_sig_cards_by_default(self):
|
||||
"""Without Note unlocks the deck shows only 16 court cards (no Nomad/Schizo)."""
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.content.decode().count('data-card-id='), 16)
|
||||
|
||||
def test_nomad_note_adds_nomad_to_sig_deck(self):
|
||||
Note.objects.create(user=self.gamers[0], slug="nomad", earned_at=timezone.now())
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.content.decode().count('data-card-id='), 17)
|
||||
|
||||
def test_schizo_note_adds_schizo_to_sig_deck(self):
|
||||
Note.objects.create(user=self.gamers[0], slug="schizo", earned_at=timezone.now())
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.content.decode().count('data-card-id='), 17)
|
||||
|
||||
def test_both_notes_gives_18_sig_cards(self):
|
||||
Note.objects.create(user=self.gamers[0], slug="nomad", earned_at=timezone.now())
|
||||
Note.objects.create(user=self.gamers[0], slug="schizo", earned_at=timezone.now())
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.content.decode().count('data-card-id='), 18)
|
||||
|
||||
|
||||
@@ -338,9 +338,9 @@ def _role_select_context(room, user):
|
||||
ctx["sig_reservations_json"] = json.dumps(reservations)
|
||||
|
||||
if user_polarity == 'levity':
|
||||
ctx["sig_cards"] = levity_sig_cards(room)
|
||||
ctx["sig_cards"] = levity_sig_cards(room, user)
|
||||
elif user_polarity == 'gravity':
|
||||
ctx["sig_cards"] = gravity_sig_cards(room)
|
||||
ctx["sig_cards"] = gravity_sig_cards(room, user)
|
||||
else:
|
||||
ctx["sig_cards"] = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user