from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from apps.applets.utils import applet_context, apply_applet_toggle 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 annotate_latest_event, rooms_for_user from apps.lyric.models import Token GAMEBOARD_APPLET_ORDER = [ "new-game", "my-games", "game-kit", ] @login_required(login_url="/") def gameboard(request): pass_token = request.user.tokens.filter(token_type=Token.PASS).first() if request.user.is_staff else None coin = request.user.tokens.filter(token_type=Token.COIN).first() carte = request.user.tokens.filter(token_type=Token.CARTE).first() free_tokens = list(request.user.tokens.filter( token_type=Token.FREE, expires_at__gt=timezone.now() ).order_by("expires_at")) return render( request, "apps/gameboard/gameboard.html", { "pass_token": pass_token, "coin": coin, "carte": carte, "equipped_trinket_id": request.user.equipped_trinket_id, "equipped_deck_id": request.user.equipped_deck_id, "deck_variants": _annotate_deck_in_use(list(request.user.unlocked_decks.all()), request.user), "free_tokens": free_tokens, "free_count": len(free_tokens), "applets": applet_context(request.user, "gameboard"), "page_class": "page-gameboard", "my_games": annotate_latest_event(rooms_for_user(request.user)), } ) @login_required(login_url="/") def toggle_game_applets(request): checked = request.POST.getlist("applets") apply_applet_toggle(request.user, "gameboard", checked) if request.headers.get("HX-Request"): free_tokens = list(request.user.tokens.filter( token_type=Token.FREE, expires_at__gt=timezone.now() ).order_by("expires_at")) return render(request, "apps/gameboard/_partials/_applets.html", { "applets": applet_context(request.user, "gameboard"), "pass_token": request.user.tokens.filter(token_type=Token.PASS).first() if request.user.is_staff else None, "coin": request.user.tokens.filter(token_type=Token.COIN).first(), "carte": request.user.tokens.filter(token_type=Token.CARTE).first(), "equipped_trinket_id": request.user.equipped_trinket_id, "equipped_deck_id": request.user.equipped_deck_id, "deck_variants": _annotate_deck_in_use(list(request.user.unlocked_decks.all()), request.user), "free_tokens": free_tokens, "free_count": len(free_tokens), "my_games": annotate_latest_event(rooms_for_user(request.user)), }) return redirect("gameboard") @login_required(login_url="/") def equip_trinket(request, token_id): token = get_object_or_404(Token, pk=token_id, user=request.user) if request.method == "POST": request.user.equipped_trinket = token request.user.save(update_fields=["equipped_trinket"]) return HttpResponse(status=204) return render( request, "apps/gameboard/_partials/_equip_trinket_btn.html", {"token": token}, ) @login_required(login_url="/") def equip_deck(request, deck_id): deck = get_object_or_404(DeckVariant, pk=deck_id) if request.method == "POST": request.user.equipped_deck = deck request.user.save(update_fields=["equipped_deck"]) return HttpResponse(status=204) return HttpResponse(status=405) @login_required(login_url="/") def unequip_trinket(request, token_id): token = get_object_or_404(Token, pk=token_id, user=request.user) if request.method == "POST": if request.user.equipped_trinket_id == token.pk: request.user.equipped_trinket = None request.user.save(update_fields=["equipped_trinket"]) return HttpResponse(status=204) return HttpResponse(status=405) @login_required(login_url="/") def unequip_deck(request, deck_id): get_object_or_404(DeckVariant, pk=deck_id) if request.method == "POST": if request.user.equipped_deck_id == deck_id: request.user.equipped_deck = None request.user.save(update_fields=["equipped_deck"]) return HttpResponse(status=204) return HttpResponse(status=405) def _game_kit_context(user): from apps.lyric.models import PRONOUN_CHOICES coin = user.tokens.filter(token_type=Token.COIN).first() pass_token = user.tokens.filter(token_type=Token.PASS).first() if user.is_staff else None carte = user.tokens.filter(token_type=Token.CARTE).first() free_tokens = list(user.tokens.filter( token_type=Token.FREE, expires_at__gt=timezone.now() ).order_by("expires_at")) tithe_tokens = list(user.tokens.filter(token_type=Token.TITHE)) pronoun_options = [ {"key": k, "label": label, "active": (k == user.pronouns)} for (k, label) in PRONOUN_CHOICES ] return { "coin": coin, "pass_token": pass_token, "carte": carte, "free_tokens": free_tokens, "tithe_tokens": tithe_tokens, "unlocked_decks": list(user.unlocked_decks.all()), "applets": applet_context(user, "game-kit"), "pronoun_options": pronoun_options, "current_pronouns": user.pronouns, } @login_required(login_url="/") def game_kit(request): return render(request, "apps/gameboard/game_kit.html", { **_game_kit_context(request.user), "page_class": "page-gameboard", }) @login_required(login_url="/") def toggle_game_kit_sections(request): checked = request.POST.getlist("applets") apply_applet_toggle(request.user, "game-kit", checked) if request.headers.get("HX-Request"): return render(request, "apps/gameboard/_partials/_game_kit_sections.html", _game_kit_context(request.user)) return redirect("game_kit") @login_required(login_url="/") def tarot_fan(request, deck_id): from apps.epic.models import TarotCard deck = get_object_or_404(DeckVariant, pk=deck_id) if not request.user.unlocked_decks.filter(pk=deck_id).exists(): return HttpResponse(status=403) _suit_order = {"BRANDS": 0, "GRAILS": 1, "BLADES": 2, "CROWNS": 3, "WANDS": 0, "CUPS": 1, "SWORDS": 2, "PENTACLES": 3} cards = sorted( TarotCard.objects.filter(deck_variant=deck), key=lambda c: (0 if c.arcana == "MAJOR" else 1, _suit_order.get(c.suit or "", 9), c.number), ) return render(request, "apps/gameboard/_partials/_tarot_fan.html", { "deck": deck, "cards": cards, })