wired PICK SKY server-side polarity countdown via threading.Timer (tasks.py); fixed polarity_done overlay gating on refresh; cleared sig-select floats on overlay dismiss; filtered Redact events from Most Recent applet
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
18
src/apps/drama/migrations/0003_alter_gameevent_verb.py
Normal file
18
src/apps/drama/migrations/0003_alter_gameevent_verb.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0 on 2026-04-12 23:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('drama', '0002_scrollposition'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='gameevent',
|
||||
name='verb',
|
||||
field=models.CharField(choices=[('room_created', 'Room created'), ('slot_reserved', 'Gate slot reserved'), ('slot_filled', 'Gate slot filled'), ('slot_returned', 'Gate slot returned'), ('slot_released', 'Gate slot released'), ('invite_sent', 'Invite sent'), ('role_select_started', 'Role select started'), ('role_selected', 'Role selected'), ('roles_revealed', 'Roles revealed'), ('sig_ready', 'Sig claim staked'), ('sig_unready', 'Sig claim withdrawn')], max_length=30),
|
||||
),
|
||||
]
|
||||
@@ -1,6 +1,12 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
# ── Default gender-neutral pronouns (Baltimore original) ──────────────────────
|
||||
# Later: replace with per-actor lookup when User model gains a pronouns field.
|
||||
PRONOUN_SUBJ = "yo"
|
||||
PRONOUN_OBJ = "yo"
|
||||
PRONOUN_POSS = "yos"
|
||||
|
||||
|
||||
class GameEvent(models.Model):
|
||||
# Gate phase
|
||||
@@ -14,6 +20,9 @@ class GameEvent(models.Model):
|
||||
ROLE_SELECT_STARTED = "role_select_started"
|
||||
ROLE_SELECTED = "role_selected"
|
||||
ROLES_REVEALED = "roles_revealed"
|
||||
# Sig Select phase
|
||||
SIG_READY = "sig_ready"
|
||||
SIG_UNREADY = "sig_unready"
|
||||
|
||||
VERB_CHOICES = [
|
||||
(ROOM_CREATED, "Room created"),
|
||||
@@ -25,6 +34,8 @@ class GameEvent(models.Model):
|
||||
(ROLE_SELECT_STARTED, "Role select started"),
|
||||
(ROLE_SELECTED, "Role selected"),
|
||||
(ROLES_REVEALED, "Roles revealed"),
|
||||
(SIG_READY, "Sig claim staked"),
|
||||
(SIG_UNREADY, "Sig claim withdrawn"),
|
||||
]
|
||||
|
||||
room = models.ForeignKey(
|
||||
@@ -71,13 +82,36 @@ class GameEvent(models.Model):
|
||||
"PC": "Player", "BC": "Builder", "SC": "Shepherd",
|
||||
"AC": "Alchemist", "NC": "Narrator", "EC": "Economist",
|
||||
}
|
||||
_chair_order = ["PC", "NC", "EC", "SC", "AC", "BC"]
|
||||
_ordinals = ["1st", "2nd", "3rd", "4th", "5th", "6th"]
|
||||
code = d.get("role", "?")
|
||||
role = d.get("role_display") or _role_names.get(code, code)
|
||||
return f"elects to start as the {role}, and will enjoy affinity with this Role for the remainder of the game."
|
||||
try:
|
||||
ordinal = _ordinals[_chair_order.index(code)]
|
||||
except ValueError:
|
||||
ordinal = "?"
|
||||
return f"assumes {ordinal} Chair; yo will start the game as the {role}."
|
||||
if self.verb == self.ROLES_REVEALED:
|
||||
return "All roles assigned"
|
||||
if self.verb == self.SIG_READY:
|
||||
card_name = d.get("card_name", "a card")
|
||||
corner_rank = d.get("corner_rank", "")
|
||||
suit_icon = d.get("suit_icon", "")
|
||||
if corner_rank:
|
||||
icon_html = f' <i class="fa-solid {suit_icon}"></i>' if suit_icon else ""
|
||||
abbrev = f" ({corner_rank}{icon_html})"
|
||||
else:
|
||||
abbrev = ""
|
||||
return f"embodies as {PRONOUN_POSS} Significator the {card_name}{abbrev}."
|
||||
if self.verb == self.SIG_UNREADY:
|
||||
return f"disembodies {PRONOUN_POSS} Significator."
|
||||
return self.verb
|
||||
|
||||
@property
|
||||
def struck(self):
|
||||
"""True when this SIG_READY event was subsequently retracted (WAIT NVM)."""
|
||||
return self.data.get("retracted", False)
|
||||
|
||||
def to_activity(self, base_url):
|
||||
"""Serialise this event as an AS2 Activity dict, or None if unsupported."""
|
||||
if not self.actor or not self.actor.username:
|
||||
|
||||
@@ -40,6 +40,49 @@ class GameEventModelTest(TestCase):
|
||||
self.assertIn("actor@test.io", str(event))
|
||||
self.assertIn(GameEvent.ROLE_SELECTED, str(event))
|
||||
|
||||
# ── to_prose — ROLE_SELECTED ──────────────────────────────────────────
|
||||
|
||||
def test_role_selected_prose_uses_ordinal_chair(self):
|
||||
for role, ordinal in [("PC", "1st"), ("NC", "2nd"), ("EC", "3rd"),
|
||||
("SC", "4th"), ("AC", "5th"), ("BC", "6th")]:
|
||||
with self.subTest(role=role):
|
||||
event = record(self.room, GameEvent.ROLE_SELECTED, actor=self.user,
|
||||
role=role, role_display="")
|
||||
self.assertIn(f"assumes {ordinal} Chair", event.to_prose())
|
||||
|
||||
def test_role_selected_prose_includes_role_name(self):
|
||||
event = record(self.room, GameEvent.ROLE_SELECTED, actor=self.user,
|
||||
role="PC", role_display="Player")
|
||||
prose = event.to_prose()
|
||||
self.assertIn("Player", prose)
|
||||
self.assertIn("yo will start the game", prose)
|
||||
|
||||
# ── to_prose — SIG_READY ─────────────────────────────────────────────
|
||||
|
||||
def test_sig_ready_prose_embodies_card_with_rank_and_icon(self):
|
||||
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
|
||||
card_name="Maid of Brands", corner_rank="M",
|
||||
suit_icon="fa-wand-sparkles")
|
||||
prose = event.to_prose()
|
||||
self.assertIn("embodies as yos Significator the Maid of Brands", prose)
|
||||
self.assertIn("(M", prose)
|
||||
self.assertIn("fa-wand-sparkles", prose)
|
||||
|
||||
def test_sig_ready_prose_omits_icon_when_none(self):
|
||||
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
|
||||
card_name="The Wanderer", corner_rank="0", suit_icon="")
|
||||
prose = event.to_prose()
|
||||
self.assertIn("embodies as yos Significator the The Wanderer (0)", prose)
|
||||
self.assertNotIn("fa-", prose)
|
||||
|
||||
def test_sig_ready_prose_degrades_without_corner_rank(self):
|
||||
# Old events recorded before this change have no corner_rank key
|
||||
event = record(self.room, GameEvent.SIG_READY, actor=self.user,
|
||||
card_name="Maid of Brands")
|
||||
prose = event.to_prose()
|
||||
self.assertIn("embodies as yos Significator the Maid of Brands", prose)
|
||||
self.assertNotIn("(", prose)
|
||||
|
||||
def test_str_without_actor_shows_system(self):
|
||||
event = record(self.room, GameEvent.ROLES_REVEALED)
|
||||
self.assertIn("system", str(event))
|
||||
|
||||
Reference in New Issue
Block a user