Bombsquad-Ballistica-Modded.../dist/ba_data/python/bacommon/displayitem.py
2026-06-28 18:53:43 +05:30

202 lines
5.3 KiB
Python

# Released under the MIT License. See LICENSE for details.
#
"""Functionality for displaying currencies, prizes, owned items, etc.
.. warning::
This is an internal api and subject to change at any time. Do not use
it in mod code.
"""
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
class ItemTypeID(Enum):
"""Type ID for each of our subclasses."""
UNKNOWN = 'u'
TICKETS = 't'
TICKETS_PURPLE = 'tp'
TOKENS = 'k'
TEST = 's'
CHEST = 'c'
class Item(IOMultiType[ItemTypeID]):
"""Some amount of something that can be shown or described.
Used to depict chest contents, inventory, rewards, etc.
"""
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
# 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()
@override
@classmethod
def get_type_id_storage_name(cls) -> str:
# Pin to the original default for back-compat with stored data.
return '_dciotype'
@override
@classmethod
def get_type(cls, type_id: ItemTypeID) -> type[Item]:
"""Return the subclass for each of our type-ids."""
# pylint: disable=cyclic-import
t = ItemTypeID
if type_id is t.UNKNOWN:
return Unknown
if type_id is t.TICKETS:
return Tickets
if type_id is t.TICKETS_PURPLE:
return PurpleTickets
if type_id is t.TOKENS:
return Tokens
if type_id is t.TEST:
return Test
if type_id is t.CHEST:
from bacommon.classic._chest import ClassicChestDisplayItem
return ClassicChestDisplayItem
# Important to make sure we provide all types.
assert_never(type_id)
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
"""Return a string description and subs for the item.
Will be translated on the client using the 'displayItemNames'
Lstr category.
These decriptions are baked into the display-item wrapper and
should be accessed from there when available. This allows
clients to give descriptions even for newer display item types
they don't recognize.
"""
raise NotImplementedError()
# Implement fallbacks so client can digest item lists even if they
# contain unrecognized stuff. The wrapper contains basic
# baked down info that they can still use in such cases.
@override
@classmethod
def get_unknown_type_fallback(cls) -> Item:
return Unknown()
@ioprepped
@dataclass
class Unknown(Item):
"""Something we don't know how to display."""
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
return ItemTypeID.UNKNOWN
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
import logging
# Make noise but don't break.
logging.exception(
'Unknown.get_description() should never be called.'
' Always access descriptions on the display-item wrapper.'
)
return 'Unknown', []
@ioprepped
@dataclass
class Tickets(Item):
"""Some amount of tickets."""
count: Annotated[int, IOAttrs('c')]
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
return ItemTypeID.TICKETS
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${C} Tickets', [('${C}', str(self.count))]
@ioprepped
@dataclass
class PurpleTickets(Item):
"""Some amount of purple tickets."""
count: Annotated[int, IOAttrs('c')]
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
return ItemTypeID.TICKETS_PURPLE
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${C} Purple Tickets', [('${C}', str(self.count))]
@ioprepped
@dataclass
class Tokens(Item):
"""Some amount of tokens."""
count: Annotated[int, IOAttrs('c')]
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
return ItemTypeID.TOKENS
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${C} Tokens', [('${C}', str(self.count))]
@ioprepped
@dataclass
class Test(Item):
"""Fills usable space for a display-item - good for calibration."""
@override
@classmethod
def get_type_id(cls) -> ItemTypeID:
return ItemTypeID.TEST
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return 'Test', []
@ioprepped
@dataclass
class Wrapper:
"""Wraps a display-item and some baked out info.
This allows clients to at least give descriptions of new
display-item types they may not have locally.
"""
item: Annotated[Item, IOAttrs('i')]
description: Annotated[str, IOAttrs('d')]
description_subs: Annotated[list[str] | None, IOAttrs('s')]
@classmethod
def for_item(cls, item: Item) -> Wrapper:
"""Convenience method to wrap a display-item."""
desc, subs = item.get_description()
return Wrapper(item, desc, pairs_to_flat(subs))