Bombsquad-Ballistica-Modded.../dist/ba_data/python/bacommon/displayitem.py

199 lines
5.2 KiB
Python
Raw Normal View History

2026-02-15 14:00:00 +05:30
# 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 __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
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(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))