from django.conf import settings from django.db import models class GameEvent(models.Model): # Gate phase ROOM_CREATED = "room_created" SLOT_RESERVED = "slot_reserved" SLOT_FILLED = "slot_filled" SLOT_RETURNED = "slot_returned" SLOT_RELEASED = "slot_released" INVITE_SENT = "invite_sent" # Role Select phase ROLE_SELECT_STARTED = "role_select_started" ROLE_SELECTED = "role_selected" ROLES_REVEALED = "roles_revealed" VERB_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"), ] room = models.ForeignKey( "epic.Room", on_delete=models.CASCADE, related_name="events", ) actor = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="game_events", ) verb = models.CharField(max_length=30, choices=VERB_CHOICES) data = models.JSONField(default=dict) timestamp = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["timestamp"] def to_prose(self): """Return a human-readable action description (actor rendered separately in template).""" d = self.data if self.verb == self.SLOT_FILLED: _token_names = { "coin": "Coin-on-a-String", "Free": "Free Token", "tithe": "Tithe Token", "pass": "Backstage Pass", "carte": "Carte Blanche", } code = d.get("token_type", "token") token = d.get("token_display") or _token_names.get(code, code) days = d.get("renewal_days", 7) slot = d.get("slot_number", "?") return f"deposits a {token} for slot {slot} ({days} days)" if self.verb == self.SLOT_RESERVED: return "reserves a seat" if self.verb == self.SLOT_RETURNED: return "withdraws from the gate" if self.verb == self.SLOT_RELEASED: return f"releases slot {d.get('slot_number', '?')}" if self.verb == self.ROOM_CREATED: return "opens this room" if self.verb == self.INVITE_SENT: return "sends an invitation" if self.verb == self.ROLE_SELECT_STARTED: return "Role selection begins" if self.verb == self.ROLE_SELECTED: _role_names = { "PC": "Player", "BC": "Builder", "SC": "Shepherd", "AC": "Alchemist", "NC": "Narrator", "EC": "Economist", } code = d.get("role", "?") role = d.get("role_display") or _role_names.get(code, code) return f"elects to start as {role}" if self.verb == self.ROLES_REVEALED: return "All roles assigned" return self.verb def __str__(self): actor = self.actor.email if self.actor else "system" return f"[{self.timestamp:%Y-%m-%d %H:%M}] {actor} → {self.verb}" def record(room, verb, actor=None, **data): """Record a game event in the drama log.""" return GameEvent.objects.create(room=room, actor=actor, verb=verb, data=data)