deck contribution sprint 2 + Carte Blanche safeguards — TDD
Sprint 2 UI (game kit applet): - _applet-game-kit.html: in-use deck → two disabled × buttons, .tt-deck-game-name; in-use Carte Blanche → two disabled × buttons, data-current-room-name, .tt-token-room-name; tooltip content mirrors kit bag panel (Default, card count, description, Stock version) - gameboard.js buildMiniContent: 'In-Use' for tokens w. data-current-room-name set - _kit_bag_panel.html: Deck section always renders (placeholder when unequipped) View safeguards: - select_role: look up existing deck from prior seat in same room before equipped_deck (Carte Blanche multi-seat); only unequip when using equipped_deck - drop_token Carte: reject 409 if token.current_room is a different room; unequip from equipped_trinket on drop ITs: SelectRoleMultiSeatTest (2), DropTokenViewTest +3 (carte drop, unequip, lock) 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:
@@ -142,6 +142,39 @@ class DropTokenViewTest(TestCase):
|
|||||||
response, reverse("epic:gatekeeper", args=[self.room.id])
|
response, reverse("epic:gatekeeper", args=[self.room.id])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_carte_drop_sets_current_room(self):
|
||||||
|
carte = Token.objects.create(user=self.gamer, token_type=Token.CARTE)
|
||||||
|
self.client.post(
|
||||||
|
reverse("epic:drop_token", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"token_id": carte.pk},
|
||||||
|
)
|
||||||
|
carte.refresh_from_db()
|
||||||
|
self.assertEqual(carte.current_room, self.room)
|
||||||
|
|
||||||
|
def test_carte_drop_unequips_trinket(self):
|
||||||
|
carte = Token.objects.create(user=self.gamer, token_type=Token.CARTE)
|
||||||
|
self.gamer.equipped_trinket = carte
|
||||||
|
self.gamer.save(update_fields=["equipped_trinket"])
|
||||||
|
self.client.post(
|
||||||
|
reverse("epic:drop_token", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"token_id": carte.pk},
|
||||||
|
)
|
||||||
|
self.gamer.refresh_from_db()
|
||||||
|
self.assertIsNone(self.gamer.equipped_trinket)
|
||||||
|
|
||||||
|
def test_carte_drop_rejected_when_already_in_different_room(self):
|
||||||
|
other_room = Room.objects.create(name="Other Room", owner=self.gamer)
|
||||||
|
carte = Token.objects.create(
|
||||||
|
user=self.gamer, token_type=Token.CARTE, current_room=other_room,
|
||||||
|
)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("epic:drop_token", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"token_id": carte.pk},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 409)
|
||||||
|
carte.refresh_from_db()
|
||||||
|
self.assertEqual(carte.current_room, other_room) # unchanged
|
||||||
|
|
||||||
|
|
||||||
class ConfirmTokenViewTest(TestCase):
|
class ConfirmTokenViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -701,6 +734,15 @@ class SelectRoleViewTest(TestCase):
|
|||||||
seat = TableSeat.objects.get(room=self.room, slot_number=1)
|
seat = TableSeat.objects.get(room=self.room, slot_number=1)
|
||||||
self.assertIsNone(seat.deck_variant)
|
self.assertIsNone(seat.deck_variant)
|
||||||
|
|
||||||
|
def test_select_role_unequips_deck_from_user(self):
|
||||||
|
earthman = DeckVariant.objects.get(slug="earthman")
|
||||||
|
self.client.post(
|
||||||
|
reverse("epic:select_role", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"role": "PC"},
|
||||||
|
)
|
||||||
|
self.founder.refresh_from_db()
|
||||||
|
self.assertIsNone(self.founder.equipped_deck)
|
||||||
|
|
||||||
def test_select_role_requires_login(self):
|
def test_select_role_requires_login(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@@ -764,6 +806,55 @@ class SelectRoleViewTest(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectRoleMultiSeatTest(TestCase):
|
||||||
|
"""Carte Blanche multi-seat: second role reuses the deck from the first seat."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.founder = User.objects.create(email="founder@test.io")
|
||||||
|
self.client.force_login(self.founder)
|
||||||
|
self.room = Room.objects.create(name="Test Room", owner=self.founder)
|
||||||
|
self.room.gate_status = Room.OPEN
|
||||||
|
self.room.table_status = Room.ROLE_SELECT
|
||||||
|
self.room.save()
|
||||||
|
self.earthman = DeckVariant.objects.get(slug="earthman")
|
||||||
|
|
||||||
|
def test_second_role_inherits_deck_from_first_seat_in_room(self):
|
||||||
|
# Founder's first seat: PC already taken with deck assigned
|
||||||
|
TableSeat.objects.create(
|
||||||
|
room=self.room, gamer=self.founder, slot_number=1,
|
||||||
|
role="PC", deck_variant=self.earthman,
|
||||||
|
)
|
||||||
|
# Deck unequipped after first role
|
||||||
|
self.founder.equipped_deck = None
|
||||||
|
self.founder.save(update_fields=["equipped_deck"])
|
||||||
|
# Founder's second seat (Carte Blanche): no role yet
|
||||||
|
second_seat = TableSeat.objects.create(
|
||||||
|
room=self.room, gamer=self.founder, slot_number=2,
|
||||||
|
)
|
||||||
|
self.client.post(
|
||||||
|
reverse("epic:select_role", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"role": "BC"},
|
||||||
|
)
|
||||||
|
second_seat.refresh_from_db()
|
||||||
|
self.assertEqual(second_seat.deck_variant, self.earthman)
|
||||||
|
|
||||||
|
def test_second_role_does_not_unequip_again(self):
|
||||||
|
"""No-op unequip when deck was already cleared by the first role."""
|
||||||
|
TableSeat.objects.create(
|
||||||
|
room=self.room, gamer=self.founder, slot_number=1,
|
||||||
|
role="PC", deck_variant=self.earthman,
|
||||||
|
)
|
||||||
|
self.founder.equipped_deck = None
|
||||||
|
self.founder.save(update_fields=["equipped_deck"])
|
||||||
|
TableSeat.objects.create(room=self.room, gamer=self.founder, slot_number=2)
|
||||||
|
self.client.post(
|
||||||
|
reverse("epic:select_role", kwargs={"room_id": self.room.id}),
|
||||||
|
data={"role": "BC"},
|
||||||
|
)
|
||||||
|
self.founder.refresh_from_db()
|
||||||
|
self.assertIsNone(self.founder.equipped_deck) # still None, not broken
|
||||||
|
|
||||||
|
|
||||||
class RoomViewAllRolesFilledTest(TestCase):
|
class RoomViewAllRolesFilledTest(TestCase):
|
||||||
"""Room view in ROLE_SELECT with all seats assigned shows PICK SIGS button."""
|
"""Room view in ROLE_SELECT with all seats assigned shows PICK SIGS button."""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -405,8 +405,13 @@ def drop_token(request, room_id):
|
|||||||
if token.token_type == Token.CARTE:
|
if token.token_type == Token.CARTE:
|
||||||
# CARTE enters the machine without reserving a slot — all slots
|
# CARTE enters the machine without reserving a slot — all slots
|
||||||
# become individually claimable via .drop-token-btn
|
# become individually claimable via .drop-token-btn
|
||||||
|
if token.current_room_id and token.current_room_id != room.id:
|
||||||
|
return HttpResponse(status=409)
|
||||||
token.current_room = room
|
token.current_room = room
|
||||||
token.save()
|
token.save()
|
||||||
|
if request.user.equipped_trinket_id == token.pk:
|
||||||
|
request.user.equipped_trinket = None
|
||||||
|
request.user.save(update_fields=["equipped_trinket"])
|
||||||
request.session["kit_token_id"] = str(token.id)
|
request.session["kit_token_id"] = str(token.id)
|
||||||
_notify_gate_update(room_id)
|
_notify_gate_update(room_id)
|
||||||
return redirect("epic:gatekeeper", room_id=room_id)
|
return redirect("epic:gatekeeper", room_id=room_id)
|
||||||
@@ -566,6 +571,7 @@ def select_role(request, room_id):
|
|||||||
valid_roles = [code for code, _ in TableSeat.ROLE_CHOICES]
|
valid_roles = [code for code, _ in TableSeat.ROLE_CHOICES]
|
||||||
if not role or role not in valid_roles:
|
if not role or role not in valid_roles:
|
||||||
return redirect("epic:room", room_id=room_id)
|
return redirect("epic:room", room_id=room_id)
|
||||||
|
existing = None
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
active_seat = room.table_seats.select_for_update().filter(
|
active_seat = room.table_seats.select_for_update().filter(
|
||||||
role__isnull=True
|
role__isnull=True
|
||||||
@@ -575,8 +581,16 @@ def select_role(request, room_id):
|
|||||||
if room.table_seats.filter(role=role).exists():
|
if room.table_seats.filter(role=role).exists():
|
||||||
return HttpResponse(status=409)
|
return HttpResponse(status=409)
|
||||||
active_seat.role = role
|
active_seat.role = role
|
||||||
active_seat.deck_variant = request.user.equipped_deck
|
existing = room.table_seats.filter(
|
||||||
|
gamer=request.user, deck_variant__isnull=False,
|
||||||
|
).exclude(pk=active_seat.pk).first()
|
||||||
|
active_seat.deck_variant = (
|
||||||
|
existing.deck_variant if existing else request.user.equipped_deck
|
||||||
|
)
|
||||||
active_seat.save()
|
active_seat.save()
|
||||||
|
if not existing and request.user.equipped_deck:
|
||||||
|
request.user.equipped_deck = None
|
||||||
|
request.user.save(update_fields=["equipped_deck"])
|
||||||
record(room, GameEvent.ROLE_SELECTED, actor=request.user,
|
record(room, GameEvent.ROLE_SELECTED, actor=request.user,
|
||||||
role=role, slot_number=active_seat.slot_number,
|
role=role, slot_number=active_seat.slot_number,
|
||||||
role_display=dict(TableSeat.ROLE_CHOICES).get(role, role))
|
role_display=dict(TableSeat.ROLE_CHOICES).get(role, role))
|
||||||
|
|||||||
@@ -64,10 +64,20 @@ function initGameKitTooltips() {
|
|||||||
const tokenId = token.dataset.tokenId;
|
const tokenId = token.dataset.tokenId;
|
||||||
const equippedId = gameKit.dataset.equippedId || '';
|
const equippedId = gameKit.dataset.equippedId || '';
|
||||||
const equippedDeckId = gameKit.dataset.equippedDeckId || '';
|
const equippedDeckId = gameKit.dataset.equippedDeckId || '';
|
||||||
|
const inUseDeckIds = new Set((gameKit.dataset.inUseDeckIds || '').split(',').filter(Boolean));
|
||||||
if (deckId) {
|
if (deckId) {
|
||||||
miniPortal.textContent = (equippedDeckId && deckId === equippedDeckId) ? 'Equipped' : 'Not Equipped';
|
if (inUseDeckIds.has(deckId)) {
|
||||||
|
miniPortal.textContent = 'In-Use';
|
||||||
|
} else {
|
||||||
|
miniPortal.textContent = (equippedDeckId && deckId === equippedDeckId) ? 'Equipped' : 'Not Equipped';
|
||||||
|
}
|
||||||
} else if (tokenId) {
|
} else if (tokenId) {
|
||||||
miniPortal.textContent = (equippedId && tokenId === equippedId) ? 'Equipped' : 'Not Equipped';
|
const currentRoomName = token.dataset.currentRoomName || '';
|
||||||
|
if (currentRoomName) {
|
||||||
|
miniPortal.textContent = 'In-Use';
|
||||||
|
} else {
|
||||||
|
miniPortal.textContent = (equippedId && tokenId === equippedId) ? 'Equipped' : 'Not Equipped';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.test import TestCase
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from apps.applets.models import Applet, UserApplet
|
from apps.applets.models import Applet, UserApplet
|
||||||
from apps.epic.models import DeckVariant
|
from apps.epic.models import DeckVariant, Room, TableSeat
|
||||||
from apps.lyric.models import Token, User
|
from apps.lyric.models import Token, User
|
||||||
|
|
||||||
|
|
||||||
@@ -61,6 +61,45 @@ class GameboardViewTest(TestCase):
|
|||||||
[_] = self.parsed.cssselect("#id_game_kit #id_kit_dice_set")
|
[_] = self.parsed.cssselect("#id_game_kit #id_kit_dice_set")
|
||||||
|
|
||||||
|
|
||||||
|
class GameboardDeckInUseTest(TestCase):
|
||||||
|
"""Sprint 2: game kit applet renders in-use state for a deck assigned to an active seat."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create(email="gamer@test.io")
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
Applet.objects.get_or_create(slug="game-kit", defaults={"name": "Game Kit", "context": "gameboard"})
|
||||||
|
self.earthman = DeckVariant.objects.get(slug="earthman")
|
||||||
|
self.room = Room.objects.create(name="Wildfire", owner=self.user)
|
||||||
|
self.seat = TableSeat.objects.create(
|
||||||
|
room=self.room, gamer=self.user, slot_number=1,
|
||||||
|
deck_variant=self.earthman,
|
||||||
|
)
|
||||||
|
response = self.client.get("/gameboard/")
|
||||||
|
self.parsed = lxml.html.fromstring(response.content)
|
||||||
|
|
||||||
|
def test_in_use_deck_don_is_disabled(self):
|
||||||
|
[don] = self.parsed.cssselect("#id_kit_earthman_deck .btn-equip")
|
||||||
|
self.assertIn("btn-disabled", don.get("class", ""))
|
||||||
|
|
||||||
|
def test_in_use_deck_doff_is_absent(self):
|
||||||
|
active_doff = self.parsed.cssselect(
|
||||||
|
"#id_kit_earthman_deck .btn-unequip:not(.btn-disabled)"
|
||||||
|
)
|
||||||
|
self.assertEqual(len(active_doff), 0)
|
||||||
|
|
||||||
|
def test_in_use_deck_tooltip_shows_game_name(self):
|
||||||
|
[label] = self.parsed.cssselect("#id_kit_earthman_deck .tt-deck-game-name")
|
||||||
|
self.assertIn("Wildfire", label.text_content())
|
||||||
|
|
||||||
|
def test_non_in_use_deck_has_normal_don(self):
|
||||||
|
fiorentine = DeckVariant.objects.get(slug="fiorentine-minchiate")
|
||||||
|
self.user.unlocked_decks.add(fiorentine)
|
||||||
|
response = self.client.get("/gameboard/")
|
||||||
|
parsed = lxml.html.fromstring(response.content)
|
||||||
|
[don] = parsed.cssselect("#id_kit_fiorentine_deck .btn-equip")
|
||||||
|
self.assertNotIn("btn-disabled", don.get("class", ""))
|
||||||
|
|
||||||
|
|
||||||
class ToggleGameAppletsViewTest(TestCase):
|
class ToggleGameAppletsViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = User.objects.create(email="gamer@test.io")
|
self.user = User.objects.create(email="gamer@test.io")
|
||||||
|
|||||||
@@ -4,7 +4,20 @@ from django.shortcuts import get_object_or_404, redirect, render
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from apps.applets.utils import applet_context, apply_applet_toggle
|
from apps.applets.utils import applet_context, apply_applet_toggle
|
||||||
from apps.epic.models import DeckVariant, Room
|
|
||||||
|
|
||||||
|
def _annotate_deck_in_use(decks, user):
|
||||||
|
"""Attach .in_use_room_name to each deck — the name of the active room using it, or None."""
|
||||||
|
active = {
|
||||||
|
ts.deck_variant_id: ts.room.name
|
||||||
|
for ts in TableSeat.objects.filter(
|
||||||
|
gamer=user, deck_variant__isnull=False,
|
||||||
|
).select_related("room")
|
||||||
|
}
|
||||||
|
for deck in decks:
|
||||||
|
deck.in_use_room_name = active.get(deck.pk)
|
||||||
|
return decks
|
||||||
|
from apps.epic.models import DeckVariant, Room, TableSeat
|
||||||
from apps.epic.utils import rooms_for_user
|
from apps.epic.utils import rooms_for_user
|
||||||
from apps.lyric.models import Token
|
from apps.lyric.models import Token
|
||||||
|
|
||||||
@@ -31,7 +44,7 @@ def gameboard(request):
|
|||||||
"carte": carte,
|
"carte": carte,
|
||||||
"equipped_trinket_id": request.user.equipped_trinket_id,
|
"equipped_trinket_id": request.user.equipped_trinket_id,
|
||||||
"equipped_deck_id": request.user.equipped_deck_id,
|
"equipped_deck_id": request.user.equipped_deck_id,
|
||||||
"deck_variants": list(request.user.unlocked_decks.all()),
|
"deck_variants": _annotate_deck_in_use(list(request.user.unlocked_decks.all()), request.user),
|
||||||
"free_tokens": free_tokens,
|
"free_tokens": free_tokens,
|
||||||
"free_count": len(free_tokens),
|
"free_count": len(free_tokens),
|
||||||
"applets": applet_context(request.user, "gameboard"),
|
"applets": applet_context(request.user, "gameboard"),
|
||||||
@@ -55,7 +68,7 @@ def toggle_game_applets(request):
|
|||||||
"carte": request.user.tokens.filter(token_type=Token.CARTE).first(),
|
"carte": request.user.tokens.filter(token_type=Token.CARTE).first(),
|
||||||
"equipped_trinket_id": request.user.equipped_trinket_id,
|
"equipped_trinket_id": request.user.equipped_trinket_id,
|
||||||
"equipped_deck_id": request.user.equipped_deck_id,
|
"equipped_deck_id": request.user.equipped_deck_id,
|
||||||
"deck_variants": list(request.user.unlocked_decks.all()),
|
"deck_variants": _annotate_deck_in_use(list(request.user.unlocked_decks.all()), request.user),
|
||||||
"free_tokens": free_tokens,
|
"free_tokens": free_tokens,
|
||||||
"free_count": len(free_tokens),
|
"free_count": len(free_tokens),
|
||||||
"my_games": rooms_for_user(request.user),
|
"my_games": rooms_for_user(request.user),
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class DeckContributionTest(FunctionalTest):
|
|||||||
))
|
))
|
||||||
|
|
||||||
# Navigate to Game Kit → Card Decks to verify UI state
|
# Navigate to Game Kit → Card Decks to verify UI state
|
||||||
self.browser.get(self.live_server_url + "/gameboard/game-kit/")
|
self.browser.get(self.live_server_url + "/gameboard/")
|
||||||
decks_btn = self.wait_for(
|
decks_btn = self.wait_for(
|
||||||
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
||||||
)
|
)
|
||||||
@@ -152,7 +152,7 @@ class DeckInUseGameKitTest(FunctionalTest):
|
|||||||
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
||||||
self.browser.get(self.live_server_url)
|
self.browser.get(self.live_server_url)
|
||||||
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
||||||
self.browser.get(self.live_server_url + "/gameboard/game-kit/")
|
self.browser.get(self.live_server_url + "/gameboard/")
|
||||||
self.wait_for(
|
self.wait_for(
|
||||||
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
||||||
).click()
|
).click()
|
||||||
@@ -180,7 +180,7 @@ class DeckInUseGameKitTest(FunctionalTest):
|
|||||||
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
||||||
self.browser.get(self.live_server_url)
|
self.browser.get(self.live_server_url)
|
||||||
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
||||||
self.browser.get(self.live_server_url + "/gameboard/game-kit/")
|
self.browser.get(self.live_server_url + "/gameboard/")
|
||||||
self.wait_for(
|
self.wait_for(
|
||||||
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
||||||
).click()
|
).click()
|
||||||
@@ -201,7 +201,7 @@ class DeckInUseGameKitTest(FunctionalTest):
|
|||||||
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
||||||
self.browser.get(self.live_server_url)
|
self.browser.get(self.live_server_url)
|
||||||
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
||||||
self.browser.get(self.live_server_url + "/gameboard/game-kit/")
|
self.browser.get(self.live_server_url + "/gameboard/")
|
||||||
self.wait_for(
|
self.wait_for(
|
||||||
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
||||||
).click()
|
).click()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
style="--applet-cols: {{ entry.applet.grid_cols }}; --applet-rows: {{ entry.applet.grid_rows }};"
|
style="--applet-cols: {{ entry.applet.grid_cols }}; --applet-rows: {{ entry.applet.grid_rows }};"
|
||||||
>
|
>
|
||||||
<h2><a href="{% url 'game_kit' %}">Game Kit</a></h2>
|
<h2><a href="{% url 'game_kit' %}">Game Kit</a></h2>
|
||||||
<div id="id_game_kit" data-equipped-id="{{ equipped_trinket_id|default:'' }}" data-equipped-deck-id="{{ equipped_deck_id|default:'' }}">
|
<div id="id_game_kit" data-equipped-id="{{ equipped_trinket_id|default:'' }}" data-equipped-deck-id="{{ equipped_deck_id|default:'' }}" data-in-use-deck-ids="{% for d in deck_variants %}{% if d.in_use_room_name %}{{ d.pk }},{% endif %}{% endfor %}">
|
||||||
{% if pass_token %}
|
{% if pass_token %}
|
||||||
<div id="id_kit_pass" class="token" data-token-id="{{ pass_token.pk }}">
|
<div id="id_kit_pass" class="token" data-token-id="{{ pass_token.pk }}">
|
||||||
<i class="fa-solid fa-clipboard"></i>
|
<i class="fa-solid fa-clipboard"></i>
|
||||||
@@ -21,11 +21,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if carte %}
|
{% if carte %}
|
||||||
<div id="id_kit_carte_blanche" class="token" data-token-id="{{ carte.pk }}">
|
<div id="id_kit_carte_blanche" class="token" data-token-id="{{ carte.pk }}" data-current-room-name="{{ carte.current_room.name|default:'' }}">
|
||||||
<i class="fa-solid fa-money-check"></i>
|
<i class="fa-solid fa-money-check"></i>
|
||||||
<div class="tt">
|
<div class="tt">
|
||||||
<div class="tt-equip-btns">
|
<div class="tt-equip-btns">
|
||||||
{% if carte.pk == equipped_trinket_id %}<button class="btn btn-equip btn-disabled" data-token-id="{{ carte.pk }}">×</button><button class="btn btn-unequip" data-token-id="{{ carte.pk }}">DOFF</button>{% else %}<button class="btn btn-equip" data-token-id="{{ carte.pk }}">DON</button><button class="btn btn-unequip btn-disabled" data-token-id="{{ carte.pk }}">×</button>{% endif %}
|
{% if carte.current_room %}<button class="btn btn-equip btn-disabled" data-token-id="{{ carte.pk }}">×</button><button class="btn btn-unequip btn-disabled" data-token-id="{{ carte.pk }}">×</button>{% elif carte.pk == equipped_trinket_id %}<button class="btn btn-equip btn-disabled" data-token-id="{{ carte.pk }}">×</button><button class="btn btn-unequip" data-token-id="{{ carte.pk }}">DOFF</button>{% else %}<button class="btn btn-equip" data-token-id="{{ carte.pk }}">DON</button><button class="btn btn-unequip btn-disabled" data-token-id="{{ carte.pk }}">×</button>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h4 class="tt-title">{{ carte.tooltip_name }}</h4>
|
<h4 class="tt-title">{{ carte.tooltip_name }}</h4>
|
||||||
<p class="tt-description">{{ carte.tooltip_description }}</p>
|
<p class="tt-description">{{ carte.tooltip_description }}</p>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
<p class="tt-shoptalk"><em>{{ carte.tooltip_shoptalk }}</em></p>
|
<p class="tt-shoptalk"><em>{{ carte.tooltip_shoptalk }}</em></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p class="tt-expiry">{{ carte.tooltip_expiry }}</p>
|
<p class="tt-expiry">{{ carte.tooltip_expiry }}</p>
|
||||||
|
{% if carte.current_room %}<p class="tt-token-room-name">In game: {{ carte.current_room.name }}</p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -72,10 +73,13 @@
|
|||||||
<i class="fa-regular fa-id-badge"></i>
|
<i class="fa-regular fa-id-badge"></i>
|
||||||
<div class="tt">
|
<div class="tt">
|
||||||
<div class="tt-equip-btns">
|
<div class="tt-equip-btns">
|
||||||
{% if deck.pk == equipped_deck_id %}<button class="btn btn-equip btn-disabled" data-deck-id="{{ deck.pk }}">×</button><button class="btn btn-unequip" data-deck-id="{{ deck.pk }}">DOFF</button>{% else %}<button class="btn btn-equip" data-deck-id="{{ deck.pk }}">DON</button><button class="btn btn-unequip btn-disabled" data-deck-id="{{ deck.pk }}">×</button>{% endif %}
|
{% if deck.in_use_room_name %}<button class="btn btn-equip btn-disabled" data-deck-id="{{ deck.pk }}">×</button><button class="btn btn-unequip btn-disabled" data-deck-id="{{ deck.pk }}">×</button>{% elif deck.pk == equipped_deck_id %}<button class="btn btn-equip btn-disabled" data-deck-id="{{ deck.pk }}">×</button><button class="btn btn-unequip" data-deck-id="{{ deck.pk }}">DOFF</button>{% else %}<button class="btn btn-equip" data-deck-id="{{ deck.pk }}">DON</button><button class="btn btn-unequip btn-disabled" data-deck-id="{{ deck.pk }}">×</button>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h4 class="tt-title">{{ deck.name }}</h4>
|
<h4 class="tt-title">{{ deck.name }}{% if deck.is_default %} <span class="token-count">(Default)</span>{% endif %}</h4>
|
||||||
<p class="tt-description">{{ deck.card_count }} cards</p>
|
<p class="tt-description">{{ deck.card_count }}-card Tarot deck</p>
|
||||||
|
{% if deck.description %}<p class="tt-shoptalk"><em>{{ deck.description }}</em></p>{% endif %}
|
||||||
|
<p class="tt-shoptalk">Stock version <span class="tt-subcounter">(0 substitutions)</span></p>
|
||||||
|
{% if deck.in_use_room_name %}<p class="tt-deck-game-name">In game: {{ deck.in_use_room_name }}</p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
{% if equipped_deck %}
|
|
||||||
<div class="kit-bag-section">
|
<div class="kit-bag-section">
|
||||||
<span class="kit-bag-label">Deck</span>
|
<span class="kit-bag-label">Deck</span>
|
||||||
<div class="kit-bag-row">
|
<div class="kit-bag-row">
|
||||||
|
{% if equipped_deck %}
|
||||||
<div class="kit-bag-deck" data-deck-id="{{ equipped_deck.pk }}">
|
<div class="kit-bag-deck" data-deck-id="{{ equipped_deck.pk }}">
|
||||||
<i class="fa-regular fa-id-badge"></i>
|
<i class="fa-regular fa-id-badge"></i>
|
||||||
<div class="tt">
|
<div class="tt">
|
||||||
<h4 class="tt-title">{{ equipped_deck.name }}{% if equipped_deck.is_default %} <span class="token-count">(Default)</span>{% endif %}</h4>
|
<h4 class="tt-title">{{ equipped_deck.name }}{% if equipped_deck.is_default %} <span class="token-count">(Default)</span>{% endif %}</h4>
|
||||||
<p class="tt-description">{{ equipped_deck.card_count }}-card Tarot deck</p>
|
<p class="tt-description">{{ equipped_deck.card_count }}-card Tarot deck</p>
|
||||||
<p class="tt-shoptalk"><em>placeholder comment</em></p>
|
{% if equipped_deck.description %}<p class="tt-shoptalk"><em>{{ equipped_deck.description }}</em></p>{% endif %}
|
||||||
<p class="tt-effect">active</p>
|
<p class="tt-shoptalk">Stock version <span class="tt-subcounter">(0 substitutions)</span></p>
|
||||||
<p class="tt-expiry">Stock version</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="kit-bag-placeholder">
|
||||||
|
<i class="fa-regular fa-id-badge"></i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="kit-bag-section">
|
<div class="kit-bag-section">
|
||||||
<span class="kit-bag-label">Dice</span>
|
<span class="kit-bag-label">Dice</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user