Discard completed obstacles automatically and verify scene refresh rules
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- [ ] Check against rules on correct quantity of of obstacles and obstacle refresh rules
|
- [x] Check against rules on correct quantity of of obstacles and obstacle refresh rules
|
||||||
- [ ] Check against the rules on pi-rat hand size and conditions for drawing more cards
|
- [ ] Check against the rules on pi-rat hand size and conditions for drawing more cards
|
||||||
- [ ] Assisting other pi-rats should only be available if there are multiple obstacles in the current challenge
|
- [ ] Assisting other pi-rats should only be available if there are multiple obstacles in the current challenge
|
||||||
- [ ] Checking off personal objectives (gat/name/death) should be handled by group vote rather than adjudication by the deep
|
- [ ] Checking off personal objectives (gat/name/death) should be handled by group vote rather than adjudication by the deep
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
// - Add a CHANGELOG entry only when a commit changes something players can
|
// - Add a CHANGELOG entry only when a commit changes something players can
|
||||||
// see. Skip refactors, tests, and tooling. Keep wording player-facing.
|
// see. Skip refactors, tests, and tooling. Keep wording player-facing.
|
||||||
|
|
||||||
export const VERSION = 31;
|
export const VERSION = 32;
|
||||||
|
|
||||||
// Newest first. Each entry: { version, date: 'YYYY-MM-DD', changes: [string, ...] }.
|
// Newest first. Each entry: { version, date: 'YYYY-MM-DD', changes: [string, ...] }.
|
||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
|
{ version: 32, date: '2026-09-04', changes: ['Obstacles are automatically discarded with their columns once they reach one success per player. Unfinished obstacles carry over between scenes.'] },
|
||||||
{ version: 31, date: '2026-09-04', changes: ['Change your rank-up vote until everyone has voted. The final vote now reveals the result and advances automatically, without a Ready button.', 'The vote result stays visible through upkeep and next-scene setup.'] },
|
{ version: 31, date: '2026-09-04', changes: ['Change your rank-up vote until everyone has voted. The final vote now reveals the result and advances automatically, without a Ready button.', 'The vote result stays visible through upkeep and next-scene setup.'] },
|
||||||
{ version: 30, date: '2026-09-04', changes: ['The crew roster shows voting progress with icons.', 'Your submitted vote now shows the crewmate you nominated.'] },
|
{ version: 30, date: '2026-09-04', changes: ['The crew roster shows voting progress with icons.', 'Your submitted vote now shows the crewmate you nominated.'] },
|
||||||
{ version: 29, date: '2026-09-04', changes: ['Removed the remaining deck count from the table.'] },
|
{ version: 29, date: '2026-09-04', changes: ['Removed the remaining deck count from the table.'] },
|
||||||
|
|||||||
@@ -167,6 +167,11 @@ def play_challenge_card(
|
|||||||
role_note = "" if player.id == challenge.acting_player_id else " (assist)"
|
role_note = "" if player.id == challenge.acting_player_id else " (assist)"
|
||||||
add_game_event(db, game.id, f"{player.name} played {cards.parse_card(card_code)['display']} on '{obstacle.title}'{role_note}. {result['details']}", kind="card")
|
add_game_event(db, game.id, f"{player.name} played {cards.parse_card(card_code)['display']} on '{obstacle.title}'{role_note}. {result['details']}", kind="card")
|
||||||
|
|
||||||
|
if obstacle.success_count >= len(game.players):
|
||||||
|
add_game_event(db, game.id, f"'{obstacle.title}' is complete and discarded with its column.", kind="obstacle")
|
||||||
|
db.delete(obstacle)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
result["drew_card"] = drew_card
|
result["drew_card"] = drew_card
|
||||||
result["is_joker"] = False
|
result["is_joker"] = False
|
||||||
return True, "Card played successfully.", result
|
return True, "Card played successfully.", result
|
||||||
|
|||||||
@@ -405,7 +405,8 @@ def grant_story_bonus_rank(db: Session, game: Game, granter: Player, target):
|
|||||||
|
|
||||||
def clear_completed_obstacle(db: Session, game_id: str, obstacle_id: str):
|
def clear_completed_obstacle(db: Session, game_id: str, obstacle_id: str):
|
||||||
obstacle = db.get(Obstacle, obstacle_id)
|
obstacle = db.get(Obstacle, obstacle_id)
|
||||||
if obstacle:
|
game = get_game(db, game_id)
|
||||||
|
if obstacle and game and obstacle.game_id == game_id and obstacle.success_count >= len(game.players):
|
||||||
add_game_event(db, game_id, f"The Deep cleared the completed obstacle '{obstacle.title}'.", kind="obstacle")
|
add_game_event(db, game_id, f"The Deep cleared the completed obstacle '{obstacle.title}'.", kind="obstacle")
|
||||||
db.delete(obstacle)
|
db.delete(obstacle)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -2651,3 +2651,44 @@ def test_kick_last_pending_voter_completes_voting(session):
|
|||||||
assert crud.kick_player(session, game, b)[0]
|
assert crud.kick_player(session, game, b)[0]
|
||||||
assert game.phase == "deep_upkeep"
|
assert game.phase == "deep_upkeep"
|
||||||
assert game.last_rank_up_player_id == a.id
|
assert game.last_rank_up_player_id == a.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_completed_obstacle_discards_without_replacement(session):
|
||||||
|
game, deep, (rat,) = make_scene_game(session)
|
||||||
|
obs = game.obstacles[0]
|
||||||
|
oid = obs.id
|
||||||
|
obs.played_cards = json.dumps([{"card": "KC", "success": True}])
|
||||||
|
rat.hand_cards = json.dumps(["QD"])
|
||||||
|
session.add(obs)
|
||||||
|
session.add(rat)
|
||||||
|
session.commit()
|
||||||
|
assert crud.create_challenge(session, game.id, deep.id, rat.id, [oid])[0]
|
||||||
|
assert crud.play_challenge_card(session, rat.id, oid, "QD")[0]
|
||||||
|
assert session.get(Obstacle, oid) is None
|
||||||
|
session.refresh(game)
|
||||||
|
assert len(game.obstacles) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_cannot_clear_unfinished_obstacle(session):
|
||||||
|
game, deep, (rat,) = make_scene_game(session)
|
||||||
|
oid = game.obstacles[0].id
|
||||||
|
crud.clear_completed_obstacle(session, game.id, oid)
|
||||||
|
assert session.get(Obstacle, oid) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_setup_joker_only_increases_following_scene(session, monkeypatch):
|
||||||
|
game, deep, (rat,) = make_scene_game(session)
|
||||||
|
for obs in list(game.obstacles):
|
||||||
|
session.delete(obs)
|
||||||
|
game.current_scene_number = 2
|
||||||
|
deep.previous_role = "pirat"
|
||||||
|
rat.previous_role = "deep"
|
||||||
|
session.commit()
|
||||||
|
monkeypatch.setattr('pirats.crud_scene.reshuffle_discard_pile', lambda db, game: None)
|
||||||
|
game.deck_cards = json.dumps(["Joker1", "2H", "3H", "4H"])
|
||||||
|
session.commit()
|
||||||
|
assert crud.confirm_scene_setup(session, game.id)[0]
|
||||||
|
session.refresh(game)
|
||||||
|
assert len(game.obstacles) == 2
|
||||||
|
assert game.extra_obstacles == 1
|
||||||
|
assert crud.get_game_deck(game) == ["4H"]
|
||||||
|
|||||||
Reference in New Issue
Block a user