Django Channels role-select sprint: turn_changed, roles_revealed, role_select_start consumer handlers; WS URL changed from room_slug to room_id UUID; TableSeat model - room, gamer, slot_number, role, role_revealed, seat_position fields; Room.table_status field with ROLE_SELECT, SIG_SELECT, IN_GAME choices; migration 0006_table_status_and_table_seat; pick_roles and select_role views; _role_select_context helper; _notify_turn_changed, _notify_roles_revealed, _notify_role_select_start notifiers; all gate-mutation views now call _notify_gate_update; ChannelsFunctionalTest base class with serve_static, screenshot, dump helpers; SQLite TEST NAME set to file path for ChannelsLiveServerTestCase; InMemoryChannelLayer added to test CHANNEL_LAYERS settings; FT 5 and FT 6 now passing - active seat arc and turn advance via WS, no page refresh; room.js, gatekeeper.js, role-select.js added to apps/epic/static; applets.js, game-kit.js, dashboard.js, wallet.js relocated to app-scoped static dirs; room.html: hex table, table-seat arcs, card-stack, inventory panel, role-card hand, WS scripts; _room.scss: room-shell flex layout, .table-hex polygon clip-path, .table-seat and .seat-card-arc, .card-stack eligible/ineligible states, .card flip animation, .inv-role-card stacked hand, .role-select-backdrop; gear btn and room menu always position: fixed; 375 tests, 0 skipped

This commit is contained in:
Disco DeDisco
2026-03-17 00:24:23 -04:00
parent c9defa5a81
commit 01de6e7548
32 changed files with 2148 additions and 63 deletions

View File

@@ -1,8 +1,6 @@
<div
id="id_gate_wrapper"
hx-get="{% url 'epic:gate_status' room.id %}"
hx-trigger="every 3s [!document.activeElement.closest('#id_gate_wrapper')]"
hx-swap="outerHTML"
data-gate-status-url="{% url 'epic:gate_status' room.id %}"
>
<div class="gate-overlay">
<div class="gate-modal" role="dialog" aria-label="Gatekeeper">
@@ -83,7 +81,10 @@
{% endfor %}
</div>
{% if room.gate_status == 'OPEN' %}
<button class="launch-game-btn btn btn-primary btn-xl">PICK ROLES</button>
<form method="POST" action="{% url 'epic:pick_roles' room.id %}" style="display:contents">
{% csrf_token %}
<button type="submit" class="launch-game-btn btn btn-primary btn-xl">PICK ROLES</button>
</form>
{% endif %}
{% if request.user == room.owner %}

View File

@@ -1,18 +1,74 @@
{% extends "core/base.html" %}
{% load static %}
{% block title_text %}Gameboard{% endblock title_text %}
{% block header_text %}<span>Game</span>room{% endblock header_text %}
{% block content %}
<div class="room-page">
<div class="room-page" data-room-id="{{ room.id }}"
{% if room.table_status %}data-select-role-url="{% url 'epic:select_role' room.id %}"{% endif %}>
<div class="room-shell">
{% comment "game room content" %}gaussian blur + darkening (cf., e.g., tooltip effect) {% endcomment %}
<div class="room-table"></div>
<div id="id_game_table" class="room-table">
<div class="table-hex">
<div class="table-center">
{% if room.table_status == "ROLE_SELECT" and card_stack_state %}
<div class="card-stack" data-state="{{ card_stack_state }}"
data-taken-roles="{{ taken_roles|join:',' }}"
data-user-slots="{{ user_slots|join:',' }}">
{% if card_stack_state == "ineligible" %}
<i class="fa-solid fa-ban"></i>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% for slot in room.gate_slots.all %}
<div class="table-seat{% if slot.slot_number == active_slot %} active{% endif %}"
data-slot="{{ slot.slot_number }}">
<div class="seat-portrait">{{ slot.slot_number }}</div>
<div class="seat-card-arc"></div>
<span class="seat-label">
{% if slot.gamer %}@{{ slot.gamer.username|default:slot.gamer.email }}{% endif %}
</span>
</div>
{% endfor %}
</div>
<div id="id_inventory" class="room-inventory">
<div id="id_inv_role_card">
{% if room.table_status == "ROLE_SELECT" %}
{% for seat in assigned_seats %}
<div class="card flipped">
<div class="card-back">?</div>
<div class="card-front">
<div class="card-role-name">{{ seat.get_role_display }}</div>
</div>
</div>
{% endfor %}
{% elif room.table_status == "SIG_SELECT" and user_seat %}
<div class="card face-up">
<div class="card-front">
<div class="card-role-name">{{ user_seat.get_role_display }}</div>
</div>
</div>
{% if partner_seat %}
<div class="partner-indicator">
Partner: {{ partner_seat.get_role_display }}
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% if room.gate_status == "GATHERING" or room.gate_status == "OPEN" %}
{% if not room.table_status and room.gate_status != "RENEWAL_DUE" %}
{% include "apps/gameboard/_partials/_gatekeeper.html" %}
{% endif %}
{% include "apps/gameboard/_partials/_room_gear.html" %}
</div>
{% endblock content %}
{% endblock content %}
{% block scripts %}
<script src="{% static 'apps/epic/room.js' %}"></script>
<script src="{% static 'apps/epic/gatekeeper.js' %}"></script>
<script src="{% static 'apps/epic/role-select.js' %}"></script>
{% endblock scripts %}