Bug fixes

This commit is contained in:
2026-06-09 19:12:19 -07:00
parent 274ae3fb07
commit 8650ebe5b2
12 changed files with 1075 additions and 383 deletions

View File

@@ -11,7 +11,7 @@ from pathlib import Path
from . import crud
from . import cards
from .database import create_db_and_tables, get_session
from .models import Game, Player, Obstacle, Challenge, Vote
from .models import Game, Player, Obstacle, Vote
app = FastAPI(title="Rats with Gats Remote Play")
@@ -128,37 +128,20 @@ def scene_roster_snippet(request: Request, game_id: str, db: Session = Depends(g
})
@app.get("/game/{game_id}/scene/obstacles", response_class=HTMLResponse)
def scene_obstacles_snippet(request: Request, game_id: str, player_rank: int, db: Session = Depends(get_session)):
game = crud.get_game(db, game_id)
if not game:
return HTMLResponse("")
return templates.TemplateResponse("scene_obstacles_snippet.html", {
"request": request,
"game": game,
"player_rank": player_rank,
"parse_card": cards.parse_card,
"json_loads": json.loads
})
@app.get("/game/{game_id}/scene/challenges", response_class=HTMLResponse)
def scene_challenges_snippet(request: Request, game_id: str, player_id: str, db: Session = Depends(get_session)):
def scene_obstacles_snippet(request: Request, game_id: str, player_id: str, db: Session = Depends(get_session)):
game = crud.get_game(db, game_id)
player = crud.get_player(db, player_id)
if not game or not player:
return HTMLResponse("")
def get_obstacle_by_id(obs_id: str) -> Optional[Obstacle]:
return db.get(Obstacle, obs_id)
hand = crud.get_player_hand(player)
return templates.TemplateResponse("scene_challenges_snippet.html", {
return templates.TemplateResponse("scene_obstacles_snippet.html", {
"request": request,
"game": game,
"player": player,
"hand": hand,
"parse_card": cards.parse_card,
"get_obstacle_by_id": get_obstacle_by_id,
"json_loads": json.loads
"json_loads": json.loads,
"is_captain": crud.is_player_captain
})
@app.get("/game/{game_id}/between/roster", response_class=HTMLResponse)
@@ -174,7 +157,13 @@ def between_scenes_roster_snippet(request: Request, game_id: str, db: Session =
# --- HTMX View Polling Render Route ---
@app.get("/game/{game_id}/player/{player_id}/view", response_class=HTMLResponse)
def get_game_phase_view(request: Request, game_id: str, player_id: str, db: Session = Depends(get_session)):
def get_game_phase_view(
request: Request,
game_id: str,
player_id: str,
edit: bool = False,
db: Session = Depends(get_session)
):
game = crud.get_game(db, game_id)
player = crud.get_player(db, player_id)
if not game or not player:
@@ -220,7 +209,8 @@ def get_game_phase_view(request: Request, game_id: str, player_id: str, db: Sess
"is_captain": crud.is_player_captain,
"json_loads": json.loads,
"deck_count": len(crud.get_game_deck(game)),
"inbox_tasks": inbox_tasks
"inbox_tasks": inbox_tasks,
"edit_mode": edit
}
# Select template based on current game phase
@@ -272,7 +262,7 @@ def player_save_basic_details(
db.add(player)
db.commit()
return HTMLResponse(status_code=204)
return RedirectResponse(url=f"/game/{game_id}/player/{player_id}/view", status_code=status.HTTP_303_SEE_OTHER)
# 3. Edit basic character details (reset values to trigger form view)
@app.post("/game/{game_id}/player/{player_id}/edit-basic")
@@ -281,18 +271,8 @@ def player_edit_basic_details(
player_id: str,
db: Session = Depends(get_session)
):
player = crud.get_player(db, player_id)
if not player:
raise HTTPException(status_code=404, detail="Player not found")
player.avatar_look = ""
player.avatar_smell = ""
player.first_words = ""
db.add(player)
db.commit()
# Trigger HTMX reload
return RedirectResponse(url=f"/game/{game_id}/player/{player_id}/view", status_code=status.HTTP_303_SEE_OTHER)
# Trigger HTMX reload with edit mode active
return RedirectResponse(url=f"/game/{game_id}/player/{player_id}/view?edit=true", status_code=status.HTTP_303_SEE_OTHER)
# Auto-assign unassigned delegated tasks (Like and Hate) randomly to crewmates
@app.post("/game/{game_id}/player/{player_id}/delegate/auto")
@@ -458,7 +438,6 @@ def player_get_delegation_status(
# 5. Answer a delegated question for another player
@app.post("/game/{game_id}/player/{player_id}/submit-delegate/{target_player_id}/{question_type}")
def player_submit_delegated_answer(
request: Request,
game_id: str,
player_id: str, # The answering player
target_player_id: str, # The player whose sheet we are filling out
@@ -467,28 +446,7 @@ def player_submit_delegated_answer(
db: Session = Depends(get_session)
):
crud.submit_delegated_answer(db, target_player_id, question_type, answer.strip(), player_id)
# Re-fetch data to render the updated inbox snippet
game = crud.get_game(db, game_id)
player = crud.get_player(db, player_id)
if not game or not player:
raise HTTPException(status_code=404, detail="Not found")
# Compute inbox tasks
inbox_tasks = []
for p in game.players:
if p.id != player.id:
if p.other_like_from_player_id == player.id and not p.other_like:
inbox_tasks.append({"player": p, "type": "like"})
if p.other_hate_from_player_id == player.id and not p.other_hate:
inbox_tasks.append({"player": p, "type": "hate"})
return templates.TemplateResponse("inbox_snippet.html", {
"request": request,
"game": game,
"player": player,
"inbox_tasks": inbox_tasks
})
return HTMLResponse("")
# 6. Submit 3 Secret Pirate Techniques
@app.post("/game/{game_id}/player/{player_id}/submit-techniques")
@@ -545,15 +503,16 @@ def player_assign_face_techniques(
})
# 8. Set Role during Scene Setup
@app.post("/game/{game_id}/player/{player_id}/set-role")
@app.post("/game/{game_id}/player/{player_id}/set-role", response_class=HTMLResponse)
def player_set_role(
request: Request,
game_id: str,
player_id: str,
role: str,
db: Session = Depends(get_session)
):
crud.update_scene_role(db, player_id, role)
return HTMLResponse(status_code=204)
return get_game_phase_view(request, game_id, player_id, db=db)
# 9. Start Scene (Verify Setup & Shuffle & Draw Obstacles)
@app.post("/game/{game_id}/scene/start")
@@ -569,29 +528,16 @@ def start_scene_route(request: Request, game_id: str, db: Session = Depends(get_
return HTMLResponse(status_code=204)
# 10. Deep creates a new challenge
@app.post("/game/{game_id}/challenge/create")
def create_challenge_route(
game_id: str,
title: str = Form(...),
description: str = Form(""),
obstacle_ids: List[str] = Form(...),
db: Session = Depends(get_session)
):
crud.create_challenge(db, game_id, title.strip(), description.strip(), obstacle_ids)
return HTMLResponse(status_code=204)
# 11. Pi-Rat plays card against an obstacle in a challenge
# 11. Pi-Rat plays card against an obstacle
@app.post("/game/{game_id}/player/{player_id}/play-card")
def play_card_route(
game_id: str,
player_id: str,
challenge_id: str = Form(...),
obstacle_id: str = Form(...),
card_code: str = Form(...),
db: Session = Depends(get_session)
):
ok, msg, res = crud.play_card_on_obstacle(db, player_id, obstacle_id, card_code, challenge_id)
ok, msg, res = crud.play_card_on_obstacle(db, player_id, obstacle_id, card_code)
if not ok:
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
@@ -603,7 +549,8 @@ def play_card_route(
f"<div class='play-notification' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
f"<p>{color_prefix} {res['details']}{drew_text}</p>"
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
f"</div>"
f"</div>",
headers={"HX-Trigger": "hand-updated"}
)
# 12. Pi-Rat plays a Joker to replace an obstacle
@@ -615,10 +562,7 @@ def play_joker_route(
obstacle_id: str = Form(...),
db: Session = Depends(get_session)
):
# Retrieve any active challenge that contains this obstacle to use as a dummy,
# or let play_card_on_obstacle handle challenge_id=None.
# Wait, we can pass challenge_id = "" and check in play_card if it exists.
ok, msg, res = crud.play_card_on_obstacle(db, player_id, obstacle_id, card_code, "")
ok, msg, res = crud.play_card_on_obstacle(db, player_id, obstacle_id, card_code)
if not ok:
return HTMLResponse(f"<p class='alert alert-danger'>{msg}</p>", status_code=400)
@@ -626,7 +570,8 @@ def play_joker_route(
f"<div class='play-notification' style='position:fixed;bottom:20px;right:20px;background:#152238;border:1px solid #d4af37;padding:15px;border-radius:8px;z-index:1000;' onclick='this.remove()'>"
f"<p>🃏 Play Joker: Discarded obstacle and drew a new replacement!</p>"
f"<span style='font-size:0.8em;color:#aaa;'>Click to dismiss</span>"
f"</div>"
f"</div>",
headers={"HX-Trigger": "hand-updated"}
)
# 13. Toggle Objective Completion Checklist