Skip to content

Commit

Permalink
Make dead senators have more info
Browse files Browse the repository at this point in the history
Make dead senators show more info, specifically by making the senator's portrait, list item and details show former faction and titles. After death, senators are now considered to remain "in" the faction that they were in when they died - which is a bit weird but easier than making a new model to represent the former allegiance of dead senators.
  • Loading branch information
iamlogand committed Oct 8, 2023
1 parent d94aab3 commit 920dcd9
Show file tree
Hide file tree
Showing 17 changed files with 161 additions and 51 deletions.
2 changes: 1 addition & 1 deletion backend/rorapp/admin/senator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# Admin configuration for senators
@admin.register(Senator)
class SenatorAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'game', 'faction', 'alive', 'code', 'generation', 'rank')
list_display = ('id', 'name', 'game', 'faction', 'death_step', 'code', 'generation', 'rank')
29 changes: 14 additions & 15 deletions backend/rorapp/functions/face_mortality.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,18 @@ def face_mortality(game, faction, potential_action, step):

# Perform mortality
drawn_codes = draw_mortality_chits(1)
killed_senator_count = 0
for code in drawn_codes:
senators = Senator.objects.filter(game=game, alive=True, code=code)
senators = Senator.objects.filter(game=game, death_step__isnull=True, code=code)
if senators.exists():
senator = senators.first()
senators_former_faction = senator.faction

# Kill the senator
senator.alive = False
senator.death_step = step
senator.faction = None
senator.save()

killed_senator_count += 1

messages_to_send.append(ws_message_update("senator", SenatorSerializer(senator).data))

Expand All @@ -70,8 +71,6 @@ def face_mortality(game, faction, potential_action, step):
if title.major_office == True:
ended_major_office = title.name

messages_to_send.append(ws_message_destroy("title", title.id))

# If the title is faction leader, create an heir senator as faction leader
if title.name == "Faction Leader":

Expand Down Expand Up @@ -118,16 +117,16 @@ def face_mortality(game, faction, potential_action, step):
heir_senator_action_log.save()
messages_to_send.append(ws_message_create("senator_action_log", SenatorActionLogSerializer(heir_senator_action_log).data))

# If nobody dies, issue a notification to say so
else:
new_action_log_index = ActionLog.objects.filter(step__phase__turn__game=game).order_by('-index')[0].index + 1
action_log = ActionLog(
index=new_action_log_index,
step=step,
type="face_mortality"
)
action_log.save()
messages_to_send.append(ws_message_create("action_log", ActionLogSerializer(action_log).data))
# If nobody dies, issue a notification to say so
if killed_senator_count == 0:
new_action_log_index = ActionLog.objects.filter(step__phase__turn__game=game).order_by('-index')[0].index + 1
action_log = ActionLog(
index=new_action_log_index,
step=step,
type="face_mortality"
)
action_log.save()
messages_to_send.append(ws_message_create("action_log", ActionLogSerializer(action_log).data))

# Update senator ranks
messages_to_send.extend(rank_senators_and_factions(game.id))
Expand Down
8 changes: 4 additions & 4 deletions backend/rorapp/functions/rank_senators_and_factions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def rank_senators_and_factions(game_id):
'''

# Get aligned alive senators
senators = Senator.objects.filter(game=game_id, alive=True, faction__isnull=False)
senators = Senator.objects.filter(game=game_id, death_step__isnull=True, faction__isnull=False)

# Sort by descending influence, descending oratory and ascending code (ID)
senators = senators.order_by('-influence', '-oratory', 'code')
Expand Down Expand Up @@ -63,8 +63,8 @@ def rank_senators_and_factions(game_id):
rank_to_assign += 1

# Get unaligned and dead senators
unaligned_senators = Senator.objects.filter(game=game_id, alive=True, faction__isnull=True)
dead_senators = Senator.objects.filter(game=game_id, alive=False)
unaligned_senators = Senator.objects.filter(game=game_id, death_step__isnull=True, faction__isnull=True)
dead_senators = Senator.objects.filter(game=game_id, death_step__isnull=False)
unaligned_and_dead_senators = unaligned_senators.union(dead_senators)

# Set rank to None for unaligned and dead senators
Expand All @@ -78,7 +78,7 @@ def rank_senators_and_factions(game_id):
messages_to_send.append(ws_message_update("senator", SenatorSerializer(senator).data))

# Get the HRAO
hrao = Senator.objects.filter(game=game_id, alive=True, rank=0).first()
hrao = Senator.objects.filter(game=game_id, death_step__isnull=True, rank=0).first()

# Get factions in order of position
factions = Faction.objects.filter(game=game_id).order_by('position')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 4.2.2 on 2023-10-08 13:55

from django.db import migrations, models
import django.db.models.deletion


def convert_alive_to_senator_death(apps, schema_editor):
Senator = apps.get_model('rorapp', 'Senator')
dead_senators = Senator.objects.filter(alive=False)

ActionLog = apps.get_model('rorapp', 'ActionLog')
death_action_logs = ActionLog.objects.filter(type='face_mortality')

for dead_action_log in death_action_logs:
if (dead_action_log.data is None):
continue
dead_senator_id = dead_action_log.data.get('senator')
if (dead_senator_id is None):
continue
dead_senator = dead_senators.get(id=dead_senator_id)
dead_senator.death_step = dead_action_log.step
dead_senator.save()


def convert_senator_death_to_alive(apps, schema_editor):
Senator = apps.get_model('rorapp', 'Senator')
dead_senators = Senator.objects.filter(death_step__isnull=False)
dead_senators.update(alive=False)


class Migration(migrations.Migration):

dependencies = [
('rorapp', '0035_alter_faction_game_alter_player_game_and_more'),
]

operations = [
migrations.AddField(
model_name='senator',
name='death_step',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='rorapp.step'),
),
migrations.RunPython(convert_alive_to_senator_death, convert_senator_death_to_alive),
migrations.RemoveField(
model_name='senator',
name='alive',
),
]
39 changes: 39 additions & 0 deletions backend/rorapp/migrations/0037_dead_senators_faction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 4.2.2 on 2023-10-08 13:55

from django.db import migrations


def set_dead_senators_faction(apps, schema_editor):
Senator = apps.get_model('rorapp', 'Senator')
dead_senators = Senator.objects.filter(death_step__isnull=False)

ActionLog = apps.get_model('rorapp', 'ActionLog')
death_action_logs = ActionLog.objects.filter(type='face_mortality')

for dead_action_log in death_action_logs:
if (dead_action_log.data is None):
continue
dead_senator_id = dead_action_log.data.get('senator')
if (dead_senator_id is None):
continue
dead_senator = dead_senators.get(id=dead_senator_id)
dead_senator.faction = dead_action_log.faction
dead_senator.save()


def unset_dead_senators_faction(apps, schema_editor):
Senator = apps.get_model('rorapp', 'Senator')
dead_senators = Senator.objects.filter(death_step__isnull=False)
dead_senators.update(faction=None)



class Migration(migrations.Migration):

dependencies = [
('rorapp', '0036_add_senator_death_step_remove_senator_alive'),
]

operations = [
migrations.RunPython(set_dead_senators_faction, unset_dead_senators_faction),
]
3 changes: 2 additions & 1 deletion backend/rorapp/models/senator.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from django.db import models
from rorapp.models.game import Game
from rorapp.models.faction import Faction
from rorapp.models.step import Step


# Model for representing senators
class Senator(models.Model):
name = models.CharField(max_length=10)
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name='senators')
faction = models.ForeignKey(Faction, blank=True, null=True, on_delete=models.SET_NULL)
alive = models.BooleanField(default=True)
death_step = models.ForeignKey(Step, on_delete=models.CASCADE, blank=True, null=True) # Null means the senator is alive
code = models.IntegerField()
generation = models.IntegerField(default=1)
rank = models.IntegerField(blank=True, null=True)
Expand Down
2 changes: 1 addition & 1 deletion backend/rorapp/serializers/senator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Meta:
'name',
'game',
'faction',
'alive',
'death_step',
'code',
'generation',
'rank',
Expand Down
11 changes: 6 additions & 5 deletions backend/rorapp/views/title.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.db.models import Q, F
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rorapp.models import Title
Expand All @@ -6,7 +7,7 @@

class TitleViewSet(viewsets.ReadOnlyModelViewSet):
"""
Read titles.
Read titles. Optionally accepts a `relevant` URL parameter for filtering.
"""

permission_classes = [IsAuthenticated]
Expand All @@ -20,10 +21,10 @@ def get_queryset(self):
if game_id is not None:
queryset = queryset.filter(senator__game__id=game_id)

# Filter against an `active` query parameter in the URL
# Active means that the title's end step is null
active = self.request.query_params.get('active', None)
# Filter against an `relevant` query parameter in the URL
# Active means that the title's end step is null or the senator is dead
active = self.request.query_params.get('relevant', None)
if active is not None:
queryset = queryset.filter(end_step__isnull=True)
queryset = queryset.filter(Q(end_step__isnull=True) | Q(end_step=F('senator__death_step')))

return queryset
4 changes: 2 additions & 2 deletions frontend/classes/Senator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface SenatorData {
name: string
game: number
faction: number
alive: boolean
death_step: number | null
code: number
generation: number
rank: number | null
Expand Down Expand Up @@ -43,7 +43,7 @@ class Senator {
this.id = data.id
this.name = data.name
this.game = data.game
this.alive = data.alive
this.alive = data.death_step === null // Senator is alive if death_step is null (a simplification compared to the backend)
this.code = data.code
this.generation = data.generation
this.rank = data.rank
Expand Down
6 changes: 4 additions & 2 deletions frontend/components/FactionListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ const FactionListItem = (props: FactionListItemProps) => {
const player = allPlayers.byId[props.faction.player] ?? null
const senators = new Collection<Senator>(
allSenators.asArray
.filter((s) => s.faction === props.faction.id)
.sort((a, b) => a.name.localeCompare(b.name)) ?? []
.filter(s => s.alive) // Filter by alive
.filter(s => s.faction === props.faction.id) // Filter by faction
.sort((a, b) => a.generation - b.generation) // Sort by generation
.sort((a, b) => a.name.localeCompare(b.name)) ?? [] // Sort by name
)
const senatorsCount = senators.allIds.length
const totalInfluence = senators.asArray.reduce(
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/GamePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ const GamePage = (props: GamePageProps) => {
const fetchTitles = useCallback(async () => {
const response = await request(
"GET",
`titles/?game=${props.gameId}&active`,
`titles/?game=${props.gameId}&relevant`,
accessToken,
refreshToken,
setAccessToken,
Expand Down
5 changes: 4 additions & 1 deletion frontend/components/SenatorList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ const SenatorList = (props: SenatorListProps) => {
(s) => (filterAlive && s.alive) || (filterDead && !s.alive)
)

// Sort by name in alphabetical order as base/default order
// Sort by generation in ascending order as base/default order
senators = senators.sort((a, b) => a.generation - b.generation)

// Sort by name in alphabetical order
senators = senators.sort((a, b) => a.name.localeCompare(b.name))

// Sort by the selected attribute
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/SenatorListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ const SenatorListItem = ({ senator, ...props }: SenatorListItemProps) => {
{faction
? props.selectableFactions && (
<span>
<FactionLink faction={faction} includeIcon />
{factionLeader ? " Leader" : null}
<FactionLink faction={faction} includeIcon />{" "}
{factionLeader ? "Leader" : "Member"}
{player ? <span> ({player.user?.username})</span> : null}
</span>
)
Expand Down
4 changes: 0 additions & 4 deletions frontend/components/SenatorPortrait.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@
user-select: none;
}

.dead {
filter: grayscale(80%);
}

.imageContainer {
position: absolute;
z-index: 1;
Expand Down
10 changes: 7 additions & 3 deletions frontend/components/SenatorPortrait.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const SenatorPortrait = ({ senator, size, ...props }: SenatorPortraitProps) => {
const getBgStyle = () => {
// Get base background color
let bgColor = ""
if (faction) {
if (faction && senator.alive) {
if (hover) {
bgColor = faction.getColor("bgHover") // Brighter on hover
} else {
Expand Down Expand Up @@ -228,7 +228,7 @@ const SenatorPortrait = ({ senator, size, ...props }: SenatorPortraitProps) => {
)}
<Image
className={`${styles.picture} ${
senator.alive ? "" : styles.dead
senator.alive ? "" : "grayscale-[80%]"
}`}
width={size + getZoom()}
height={size + getZoom()}
Expand All @@ -245,7 +245,11 @@ const SenatorPortrait = ({ senator, size, ...props }: SenatorPortraitProps) => {
</Tooltip>
)}
{majorOffice && (
<TitleIcon title={majorOffice} size={getIconSize()} />
<TitleIcon
title={majorOffice}
size={getIconSize()}
dead={!senator.alive}
/>
)}
{senator.alive === false && (
<Image
Expand Down
3 changes: 2 additions & 1 deletion frontend/components/TitleIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import Title from "@/classes/Title"
interface TitleIconProps {
title: Title
size: number
dead?: boolean
}

const TitleIcon = (props: TitleIconProps) => {
if (props.title.name.includes("Rome Consul")) {
return (
<Image
className={styles.titleIcon}
className={`${styles.titleIcon} ${props.dead ? "grayscale-[100%]" : ""}`}
src={RomeConsulIcon}
height={props.size}
width={props.size}
Expand Down
Loading

0 comments on commit 920dcd9

Please sign in to comment.