Source code for bacommon.docui.routes.classicchest
# Released under the MIT License. See LICENSE for details.
#
"""Routes for the classic chest doc-ui domain.
One chest slot: what's in it, its prize odds and its unlock wait, with
buttons to open it (now, for tokens, or once unlocked) or to watch an
ad to cut the wait. Opening answers with the chest's contents and the
open animation as client-effects.
"""
from __future__ import annotations # Docs-generation hack.
from dataclasses import dataclass
from typing import Annotated, override
from efro.dataclassio import ioprepped, IOAttrs
import bacommon.docui.v2 as dui2
from bacommon.docui.routes import (
DocUIRoute,
DocUILocalActionBase,
PressSound,
family_members,
)
[docs]
class ChestRoute(DocUIRoute):
"""Family class for classic chest routes."""
[docs]
@override
@classmethod
def get_route_types(cls) -> tuple[type[DocUIRoute], ...]:
return family_members(AnyChestRoute)
[docs]
@override
@classmethod
def get_window_layout(cls) -> dui2.WindowLayout:
# One chest centered, its odds beside it, buttons below.
return dui2.WindowLayout.WIDE
[docs]
@ioprepped
@dataclass
class Root(ChestRoute, path='/'):
"""A chest slot: its chest (if any) and what can be done with it.
The two flags are things only the client knows, so it states them
when asking.
"""
#: Which slot (0-based).
slot: int
#: Whether an ad is ready to show right now (so a 'watch an ad to
#: reduce the wait' button can work).
ad_ready: bool = False
#: Whether to remind about opening early with tokens (the player
#: can switch the reminder off).
open_me: bool = False
[docs]
@ioprepped
@dataclass
class Open(ChestRoute, path='/open', method=dui2.RequestMethod.POST):
"""Open a chest, paying any tokens its wait currently costs.
Answered with its contents and the open animation.
"""
slot: int
#: Tokens the player agreed to pay (0 for an unlocked chest). The
#: open fails if the cost has since changed.
token_payment: int
[docs]
@ioprepped
@dataclass
class AdWatched(ChestRoute, path='/adwatched', method=dui2.RequestMethod.POST):
"""The player watched an ad to reduce a chest's wait.
Answered like :class:`Open` if that unlocked it; otherwise with the
slot as it now stands.
"""
slot: int
# All chest routes.
AnyChestRoute = Root | Open | AdWatched
[docs]
class ChestLocalAction(DocUILocalActionBase):
"""Family class for classic chest local-actions."""
[docs]
@override
@classmethod
def get_action_types(cls) -> tuple[type[DocUILocalActionBase], ...]:
return family_members(AnyChestLocalAction)
[docs]
@ioprepped
@dataclass
class WatchAd(ChestLocalAction, name='watch_ad'):
"""Show an ad; once watched, ask for :class:`AdWatched`."""
slot: int
[docs]
@ioprepped
@dataclass
class StopReminding(ChestLocalAction, name='stop_reminding'):
"""Stop reminding about opening chests early; re-show the slot."""
slot: int
[docs]
@ioprepped
@dataclass
class GetTokens(ChestLocalAction, name='get_tokens'):
"""Offer to get more tokens (not enough to open a chest now)."""
[docs]
@override
@classmethod
def get_press_sound(cls) -> PressSound:
return PressSound.SWISH
# All chest local-actions.
AnyChestLocalAction = WatchAd | StopReminding | GetTokens
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self, TypeIs)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum