Add gift clause rule for free gift encounters
When enabled, in-game gift Pokemon (starters, trades, fossils) do not count against a location's encounter limit. Both a gift encounter and a regular encounter can coexist on the same route, in any order. Persists encounter origin on the Encounter model so the backend can exclude gift encounters from route-lock checks bidirectionally, and the frontend can split them into a separate display layer that doesn't lock the route for regular encounters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""add origin to encounters
|
||||
|
||||
Revision ID: i0d1e2f3a4b5
|
||||
Revises: h9c0d1e2f3a4
|
||||
Create Date: 2026-02-20 12:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "i0d1e2f3a4b5"
|
||||
down_revision: str | Sequence[str] | None = "h9c0d1e2f3a4"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"encounters",
|
||||
sa.Column("origin", sa.String(20), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("encounters", "origin")
|
||||
@@ -58,12 +58,13 @@ async def create_encounter(
|
||||
detail="Cannot create encounter on a parent route. Use a child route instead.",
|
||||
)
|
||||
|
||||
# Shiny clause: shiny encounters bypass the route-lock check
|
||||
# Shiny/gift clause: certain encounters bypass the route-lock check
|
||||
shiny_clause_on = run.rules.get("shinyClause", True) if run.rules else True
|
||||
skip_route_lock = (data.is_shiny and shiny_clause_on) or data.origin in (
|
||||
"shed_evolution",
|
||||
"egg",
|
||||
"transfer",
|
||||
gift_clause_on = run.rules.get("giftClause", False) if run.rules else False
|
||||
skip_route_lock = (
|
||||
(data.is_shiny and shiny_clause_on)
|
||||
or (data.origin == "gift" and gift_clause_on)
|
||||
or data.origin in ("shed_evolution", "egg", "transfer")
|
||||
)
|
||||
|
||||
# If this route has a parent, check if sibling already has an encounter
|
||||
@@ -93,13 +94,17 @@ async def create_encounter(
|
||||
# Check if any relevant sibling already has an encounter in this run
|
||||
# Exclude transfer-target encounters so they don't block the starter
|
||||
transfer_target_ids = select(GenlockeTransfer.target_encounter_id)
|
||||
existing_encounter = await session.execute(
|
||||
select(Encounter).where(
|
||||
Encounter.run_id == run_id,
|
||||
Encounter.route_id.in_(sibling_ids),
|
||||
~Encounter.id.in_(transfer_target_ids),
|
||||
)
|
||||
lock_query = select(Encounter).where(
|
||||
Encounter.run_id == run_id,
|
||||
Encounter.route_id.in_(sibling_ids),
|
||||
~Encounter.id.in_(transfer_target_ids),
|
||||
)
|
||||
# Gift-origin encounters don't count toward route lock
|
||||
if gift_clause_on:
|
||||
lock_query = lock_query.where(
|
||||
Encounter.origin.is_(None) | (Encounter.origin != "gift")
|
||||
)
|
||||
existing_encounter = await session.execute(lock_query)
|
||||
if existing_encounter.scalar_one_or_none() is not None:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
@@ -119,6 +124,7 @@ async def create_encounter(
|
||||
status=data.status,
|
||||
catch_level=data.catch_level,
|
||||
is_shiny=data.is_shiny,
|
||||
origin=data.origin,
|
||||
)
|
||||
session.add(encounter)
|
||||
await session.commit()
|
||||
|
||||
@@ -24,6 +24,7 @@ class Encounter(Base):
|
||||
is_shiny: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, server_default=text("false")
|
||||
)
|
||||
origin: Mapped[str | None] = mapped_column(String(20))
|
||||
caught_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
@@ -35,6 +35,7 @@ class EncounterResponse(CamelModel):
|
||||
faint_level: int | None
|
||||
death_cause: str | None
|
||||
is_shiny: bool
|
||||
origin: str | None
|
||||
caught_at: datetime
|
||||
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ RUN_DEFS = [
|
||||
DEFAULT_RULES = {
|
||||
"duplicatesClause": True,
|
||||
"shinyClause": True,
|
||||
"giftClause": False,
|
||||
"pinwheelClause": True,
|
||||
"levelCaps": False,
|
||||
"hardcoreMode": False,
|
||||
|
||||
Reference in New Issue
Block a user