Source code for bacommon.bs._displayitem
# Released under the MIT License. See LICENSE for details.
#
"""DisplayItem related functionality."""
from __future__ import annotations
from enum import Enum
from dataclasses import dataclass
from typing import Annotated, override, assert_never
from efro.util import pairs_to_flat
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
from bacommon.bs._chest import ClassicChestAppearance
[docs]
class DisplayItemTypeID(Enum):
"""Type ID for each of our subclasses."""
UNKNOWN = 'u'
TICKETS = 't'
TOKENS = 'k'
TEST = 's'
CHEST = 'c'
[docs]
class DisplayItem(IOMultiType[DisplayItemTypeID]):
"""Some amount of something that can be shown or described.
Used to depict chest contents, inventory, rewards, etc.
"""
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
# Require child classes to supply this themselves. If we did a
# full type registry/lookup here it would require us to import
# everything and would prevent lazy loading.
raise NotImplementedError()
[docs]
@override
@classmethod
def get_type(cls, type_id: DisplayItemTypeID) -> type[DisplayItem]:
"""Return the subclass for each of our type-ids."""
# pylint: disable=cyclic-import
t = DisplayItemTypeID
if type_id is t.UNKNOWN:
return UnknownDisplayItem
if type_id is t.TICKETS:
return TicketsDisplayItem
if type_id is t.TOKENS:
return TokensDisplayItem
if type_id is t.TEST:
return TestDisplayItem
if type_id is t.CHEST:
return ChestDisplayItem
# Important to make sure we provide all types.
assert_never(type_id)
[docs]
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
"""Return a string description and subs for the item.
These decriptions are baked into the DisplayItemWrapper and
should be accessed from there when available. This allows
clients to give descriptions even for newer display items they
don't recognize.
"""
raise NotImplementedError()
# Implement fallbacks so client can digest item lists even if they
# contain unrecognized stuff. DisplayItemWrapper contains basic
# baked down info that they can still use in such cases.
[docs]
@override
@classmethod
def get_unknown_type_fallback(cls) -> DisplayItem:
return UnknownDisplayItem()
[docs]
@ioprepped
@dataclass
class UnknownDisplayItem(DisplayItem):
"""Something we don't know how to display."""
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.UNKNOWN
[docs]
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
import logging
# Make noise but don't break.
logging.exception(
'UnknownDisplayItem.get_description() should never be called.'
' Always access descriptions on the DisplayItemWrapper.'
)
return 'Unknown', []
[docs]
@ioprepped
@dataclass
class TicketsDisplayItem(DisplayItem):
"""Some amount of tickets."""
count: int
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.TICKETS
[docs]
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${C} Tickets', [('${C}', str(self.count))]
[docs]
@ioprepped
@dataclass
class TokensDisplayItem(DisplayItem):
"""Some amount of tokens."""
count: int
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.TOKENS
[docs]
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${C} Tokens', [('${C}', str(self.count))]
[docs]
@ioprepped
@dataclass
class TestDisplayItem(DisplayItem):
"""Fills usable space for a display-item - good for calibration."""
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.TEST
[docs]
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return 'Test Display Item Here', []
[docs]
@ioprepped
@dataclass
class ChestDisplayItem(DisplayItem):
"""Display a chest."""
appearance: ClassicChestAppearance
[docs]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.CHEST
[docs]
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return self.appearance.pretty_name, []
[docs]
@ioprepped
@dataclass
class DisplayItemWrapper:
"""Wraps a DisplayItem and common info."""
item: DisplayItem
description: str
description_subs: list[str] | None
[docs]
@classmethod
def for_display_item(cls, item: DisplayItem) -> DisplayItemWrapper:
"""Convenience method to wrap a DisplayItem."""
desc, subs = item.get_description()
return DisplayItemWrapper(item, desc, pairs_to_flat(subs))
# 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)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum