Skip to content

Commit

Permalink
SP Tracking, part 1
Browse files Browse the repository at this point in the history
This is the easy part: Put an `sp` field on awards, and tally it on player records.

I have a mild concern that SP transactions will clog up PlayerRecord.awards. In the current
implementation, ALL awards go in here, but but we can potentially optimize this. Of course,
this is only a hypothetical concern, so I don't need to solve it just now.
  • Loading branch information
kw committed May 2, 2024
1 parent bc9c0aa commit af091d8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 22 deletions.
58 changes: 37 additions & 21 deletions src/camp/engine/rules/tempest/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,40 +104,50 @@ class AwardRecord(BaseModel, frozen=True):
player_flags: dict[str, FlagValues | None] | None = None
character_flags: dict[str, FlagValues | None] | None = None
character_grants: list[str] | None = None
sp: int = 0

def describe(self, secrets=False) -> str:
if self.description:
return self.description
parts = []
if self.backstory_approved:
parts.append("Backstory Approval")
if self.bonus_cp:
parts.append(f"Bonus CP: {self.bonus_cp}")
if self.event_xp or self.event_cp:
parts.append(f"Event: {self.event_xp} XP + {self.event_cp} CP")
if self.character_flags or self.player_flags or self.character_grants:
if not secrets:
parts.append("Secret Flags")
else:
if self.player_flags:
pflags = []
for flag, value in self.player_flags.items():
pflags.append(f"{flag}:{value!r}")
parts.append(f"Player Flags: [{'; '.join(pflags)}]")
if self.character_flags:
pflags = []
for flag, value in self.character_flags.items():
pflags.append(f"{flag}:{value!r}")
parts.append(f"Character Flags: [{'; '.join(pflags)}]")
if self.character_grants:
parts.append(f"Grants: [{'; '.join(self.character_grants)}]")
if parts:
if self.sp > 0:
parts.append(f"SP Gained: +{self.sp}")
elif self.sp < 0:
parts.append(f"SP Spent: {-self.sp}")

if secrets and (
self.character_flags
or self.player_flags
or self.character_grants
or self.sp_purchases
):
if self.player_flags:
pflags = []
for flag, value in self.player_flags.items():
pflags.append(f"{flag}:{value!r}")
parts.append(f"Player Flags: [{'; '.join(pflags)}]")
if self.character_flags:
pflags = []
for flag, value in self.character_flags.items():
pflags.append(f"{flag}:{value!r}")
parts.append(f"Character Flags: [{'; '.join(pflags)}]")
if self.character_grants:
parts.append(f"Grants: [{'; '.join(self.character_grants)}]")
if self.description and parts:
return f"{self.description}: {", ".join(parts)}"
elif self.description:
return self.description
elif parts:
return ", ".join(parts)
return "Unknown"
return "???"

@property
def needs_character(self) -> bool:
"""True if a player or logistics needs to associated a character with this record."""
"""True if a player or logistics needs to associate a character with this record."""
# We need a character assigned if any of the following fields is set:
return (
self.event_cp != 0
Expand Down Expand Up @@ -192,6 +202,7 @@ class PlayerRecord(BaseModel, frozen=True):
user: If populated, either the username of the player or something
descriptive. This is only used for debugging purposes.
xp: Amount of XP the player has earned.
sp: Service point balance for this player.
bonus_cp: Amount of Bonus CP assigned to this player.
events_played: Number of events attended as a PC.
last_played: Last date when any character was used as a PC.
Expand All @@ -208,6 +219,7 @@ class PlayerRecord(BaseModel, frozen=True):

user: int | None = None
xp: int = 0
sp: int = 0
bonus_cp: int = 0
events_played: int = 0
events_staffed: int = 0
Expand Down Expand Up @@ -248,6 +260,7 @@ def update(
# (But also get some other bits we need)

xp = player.xp
sp = player.sp
bonus_cp = player.bonus_cp
player_flags = player.flags.copy()
player_events_played = player.events_played
Expand Down Expand Up @@ -277,6 +290,8 @@ def update(

bonus_cp += award.bonus_cp

sp += award.sp

if award.event_played:
player_events_played += 1
if player_last_played is None or player_last_played < award.date:
Expand Down Expand Up @@ -406,6 +421,7 @@ def update(
return player.model_copy(
update={
"xp": xp,
"sp": sp,
"bonus_cp": bonus_cp,
"events_played": player_events_played,
"events_staffed": player_events_staffed,
Expand Down
5 changes: 4 additions & 1 deletion tests/engine/test_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@

# Unmark the "event played" flag to indicate this was an NPC award.
AWARDS_NPC = [
award.model_copy(update={"event_played": False, "event_staffed": True})
award.model_copy(update={"event_played": False, "event_staffed": True, "sp": 25})
for award in AWARDS_SINGLE_ALL
]

Expand All @@ -105,6 +105,7 @@ def test_no_awards():
"""If you didn't get any awards, that's ok."""
updated = PLAYER.update(CAMPAIGN)
assert updated.xp == CAMPAIGN.floor_xp
assert updated.sp == 0
assert updated.events_played == 0
assert updated.events_staffed == 0
assert updated.last_played is None
Expand Down Expand Up @@ -147,6 +148,7 @@ def test_awards_single_all():
"""What a dedicated player! They get all the things."""
updated = PLAYER.update(CAMPAIGN, AWARDS_SINGLE_ALL)
assert updated.xp == CAMPAIGN.max_xp == 68
assert updated.sp == 0
assert updated.events_played == len(AWARDS_SINGLE_ALL)
assert updated.last_played == date(2023, 10, 29)

Expand Down Expand Up @@ -532,6 +534,7 @@ def test_awards_not_played():
"""The player only NPC'd, so their events played counter didn't tick up."""
updated = PLAYER.update(CAMPAIGN, AWARDS_NPC)
assert updated.xp == CAMPAIGN.max_xp == 68
assert updated.sp == 12 * 25
assert updated.events_played == 0
assert updated.events_staffed == 12
assert updated.last_played is None
Expand Down

0 comments on commit af091d8

Please sign in to comment.