Files
polylan-submitter/polylan_submitter/place/models.py
T
2026-09-17 01:33:54 +02:00

82 lines
2.6 KiB
Python

from django.conf import settings
from django.db import models
from events.models import Event
from events.scoping import EventScopedManager
DEFAULT_GRID_WIDTH = 64
DEFAULT_GRID_HEIGHT = 64
PLACE_COOLDOWN_SECONDS = 30
# 16-entry palette, classic r/place-style colors. The index into this list is
# what's sent over the wire (see wire.py) - frontend/src/palette.ts must
# mirror this list exactly, in the same order.
PALETTE = [
"#FFFFFF", # 0 white
"#E4E4E4", # 1 light grey
"#888888", # 2 grey
"#222222", # 3 black
"#FFA7D1", # 4 pink
"#E50000", # 5 red
"#E59500", # 6 orange
"#A06A42", # 7 brown
"#E5D900", # 8 yellow
"#94E044", # 9 light green
"#02BE01", # 10 green
"#00D3DD", # 11 cyan
"#0083C7", # 12 blue
"#0000EA", # 13 dark blue
"#CF6EE4", # 14 purple
"#820080", # 15 dark purple
]
class PlaceSettings(models.Model):
"""One row per event, same event-scoped-singleton pattern as
clock.models.ClockTimer. width/height are the live grid dimensions - the
canonical source of truth is here, not a fixed constant, so an admin can
resize the board per event (see signals.py for how that propagates).
Unlike pplace's original PlaceSettings, there's no is_enabled here - the
games registry (EventGame.enabled) is the single on/off switch, checked
before a client can reach this app at all.
"""
event = models.OneToOneField(
Event, on_delete=models.CASCADE, related_name="place_settings"
)
width = models.PositiveSmallIntegerField(default=DEFAULT_GRID_WIDTH)
height = models.PositiveSmallIntegerField(default=DEFAULT_GRID_HEIGHT)
def __str__(self):
return f"PlaceSettings({self.event}, size={self.width}x{self.height})"
@classmethod
def current(cls, event: Event) -> "PlaceSettings":
obj, _ = cls.objects.get_or_create(event=event)
return obj
class PixelPlacement(models.Model):
event = models.ForeignKey(
Event, on_delete=models.CASCADE, related_name="pixel_placements"
)
player = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="pixel_placements",
)
x = models.PositiveSmallIntegerField()
y = models.PositiveSmallIntegerField()
color_index = models.PositiveSmallIntegerField()
placed_at = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
for_event = EventScopedManager()
class Meta:
ordering = ["-placed_at"]
def __str__(self):
return f"{self.player} placed ({self.x}, {self.y})=#{self.color_index} ({self.event})"