mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-21 23:23:24 -07:00
181 lines
7.2 KiB
Python
181 lines
7.2 KiB
Python
import pkgutil
|
|
from collections.abc import Buffer
|
|
from enum import Enum
|
|
from io import BytesIO
|
|
from typing import Literal, NamedTuple, Protocol, cast
|
|
|
|
from kivy.uix.image import CoreImage
|
|
|
|
from CommonClient import logger
|
|
|
|
from .. import game
|
|
from ..game.graphics import Graphic
|
|
|
|
|
|
# The import "from kivy.graphics.texture import Texture" does not work correctly.
|
|
# We never need the class directly, so we need to use a protocol.
|
|
class Texture(Protocol):
|
|
mag_filter: Literal["nearest"]
|
|
|
|
def get_region(self, x: int, y: int, w: int, h: int) -> "Texture": ...
|
|
|
|
|
|
class RelatedTexture(NamedTuple):
|
|
base_texture_file: str
|
|
x: int
|
|
y: int
|
|
width: int
|
|
height: int
|
|
|
|
|
|
IMAGE_GRAPHICS: dict[Graphic, str | RelatedTexture] = {
|
|
Graphic.WALL: RelatedTexture("inanimates.png", 16, 32, 16, 16),
|
|
Graphic.BREAKABLE_BLOCK: RelatedTexture("inanimates.png", 32, 32, 16, 16),
|
|
Graphic.CHEST: RelatedTexture("inanimates.png", 0, 16, 16, 16),
|
|
Graphic.BUSH: RelatedTexture("inanimates.png", 16, 16, 16, 16),
|
|
Graphic.KEY_DOOR: RelatedTexture("inanimates.png", 32, 16, 16, 16),
|
|
Graphic.BUTTON_NOT_ACTIVATED: RelatedTexture("inanimates.png", 0, 0, 16, 16),
|
|
Graphic.BUTTON_ACTIVATED: RelatedTexture("inanimates.png", 16, 0, 16, 16),
|
|
Graphic.BUTTON_DOOR: RelatedTexture("inanimates.png", 32, 0, 16, 16),
|
|
|
|
Graphic.NORMAL_ENEMY_1_HEALTH: RelatedTexture("normal_enemy.png", 0, 0, 16, 16),
|
|
Graphic.NORMAL_ENEMY_2_HEALTH: RelatedTexture("normal_enemy.png", 16, 0, 16, 16),
|
|
|
|
Graphic.BOSS_5_HEALTH: RelatedTexture("boss.png", 16, 16, 16, 16),
|
|
Graphic.BOSS_4_HEALTH: RelatedTexture("boss.png", 0, 16, 16, 16),
|
|
Graphic.BOSS_3_HEALTH: RelatedTexture("boss.png", 32, 32, 16, 16),
|
|
Graphic.BOSS_2_HEALTH: RelatedTexture("boss.png", 16, 32, 16, 16),
|
|
Graphic.BOSS_1_HEALTH: RelatedTexture("boss.png", 0, 32, 16, 16),
|
|
|
|
Graphic.EMPTY_HEART: RelatedTexture("hearts.png", 0, 0, 16, 16),
|
|
Graphic.HEART: RelatedTexture("hearts.png", 16, 0, 16, 16),
|
|
Graphic.HALF_HEART: RelatedTexture("hearts.png", 32, 0, 16, 16),
|
|
|
|
Graphic.REMOTE_ITEM: RelatedTexture("items.png", 0, 16, 16, 16),
|
|
Graphic.CONFETTI_CANNON: RelatedTexture("items.png", 16, 16, 16, 16),
|
|
Graphic.HAMMER: RelatedTexture("items.png", 32, 16, 16, 16),
|
|
Graphic.KEY: RelatedTexture("items.png", 0, 0, 16, 16),
|
|
Graphic.SHIELD: RelatedTexture("items.png", 16, 0, 16, 16),
|
|
Graphic.SWORD: RelatedTexture("items.png", 32, 0, 16, 16),
|
|
|
|
Graphic.ITEMS_TEXT: "items_text.png",
|
|
|
|
Graphic.ZERO: RelatedTexture("numbers.png", 0, 16, 16, 16),
|
|
Graphic.ONE: RelatedTexture("numbers.png", 16, 16, 16, 16),
|
|
Graphic.TWO: RelatedTexture("numbers.png", 32, 16, 16, 16),
|
|
Graphic.THREE: RelatedTexture("numbers.png", 48, 16, 16, 16),
|
|
Graphic.FOUR: RelatedTexture("numbers.png", 64, 16, 16, 16),
|
|
Graphic.FIVE: RelatedTexture("numbers.png", 0, 0, 16, 16),
|
|
Graphic.SIX: RelatedTexture("numbers.png", 16, 0, 16, 16),
|
|
Graphic.SEVEN: RelatedTexture("numbers.png", 32, 0, 16, 16),
|
|
Graphic.EIGHT: RelatedTexture("numbers.png", 48, 0, 16, 16),
|
|
Graphic.NINE: RelatedTexture("numbers.png", 64, 0, 16, 16),
|
|
|
|
Graphic.LETTER_A: RelatedTexture("letters.png", 0, 16, 16, 16),
|
|
Graphic.LETTER_E: RelatedTexture("letters.png", 16, 16, 16, 16),
|
|
Graphic.LETTER_H: RelatedTexture("letters.png", 32, 16, 16, 16),
|
|
Graphic.LETTER_I: RelatedTexture("letters.png", 0, 0, 16, 16),
|
|
Graphic.LETTER_M: RelatedTexture("letters.png", 16, 0, 16, 16),
|
|
Graphic.LETTER_T: RelatedTexture("letters.png", 32, 0, 16, 16),
|
|
|
|
Graphic.DIVIDE: RelatedTexture("symbols.png", 0, 16, 16, 16),
|
|
Graphic.EQUALS: RelatedTexture("symbols.png", 16, 16, 16, 16),
|
|
Graphic.MINUS: RelatedTexture("symbols.png", 32, 16, 16, 16),
|
|
Graphic.PLUS: RelatedTexture("symbols.png", 0, 0, 16, 16),
|
|
Graphic.TIMES: RelatedTexture("symbols.png", 16, 0, 16, 16),
|
|
Graphic.NO: RelatedTexture("symbols.png", 32, 0, 16, 16),
|
|
|
|
Graphic.UNKNOWN: RelatedTexture("symbols.png", 32, 0, 16, 16), # Same as "No"
|
|
}
|
|
|
|
BACKGROUND_TILE = RelatedTexture("inanimates.png", 0, 32, 16, 16)
|
|
|
|
|
|
class PlayerSprite(Enum):
|
|
HUMAN = 0
|
|
DUCK = 1
|
|
HORSE = 2
|
|
CAT = 3
|
|
UNKNOWN = -1
|
|
|
|
|
|
PLAYER_GRAPHICS = {
|
|
Graphic.PLAYER_DOWN: {
|
|
PlayerSprite.HUMAN: RelatedTexture("human.png", 0, 16, 16, 16),
|
|
PlayerSprite.DUCK: RelatedTexture("duck.png", 0, 16, 16, 16),
|
|
PlayerSprite.HORSE: RelatedTexture("horse.png", 0, 16, 16, 16),
|
|
PlayerSprite.CAT: RelatedTexture("cat.png", 0, 16, 16, 16),
|
|
},
|
|
Graphic.PLAYER_UP: {
|
|
PlayerSprite.HUMAN: RelatedTexture("human.png", 16, 0, 16, 16),
|
|
PlayerSprite.DUCK: RelatedTexture("duck.png", 16, 0, 16, 16),
|
|
PlayerSprite.HORSE: RelatedTexture("horse.png", 16, 0, 16, 16),
|
|
PlayerSprite.CAT: RelatedTexture("cat.png", 16, 0, 16, 16),
|
|
},
|
|
Graphic.PLAYER_LEFT: {
|
|
PlayerSprite.HUMAN: RelatedTexture("human.png", 16, 16, 16, 16),
|
|
PlayerSprite.DUCK: RelatedTexture("duck.png", 16, 16, 16, 16),
|
|
PlayerSprite.HORSE: RelatedTexture("horse.png", 16, 16, 16, 16),
|
|
PlayerSprite.CAT: RelatedTexture("cat.png", 16, 16, 16, 16),
|
|
},
|
|
Graphic.PLAYER_RIGHT: {
|
|
PlayerSprite.HUMAN: RelatedTexture("human.png", 0, 0, 16, 16),
|
|
PlayerSprite.DUCK: RelatedTexture("duck.png", 0, 0, 16, 16),
|
|
PlayerSprite.HORSE: RelatedTexture("horse.png", 0, 0, 16, 16),
|
|
PlayerSprite.CAT: RelatedTexture("cat.png", 0, 0, 16, 16),
|
|
},
|
|
}
|
|
|
|
ALL_GRAPHICS = [
|
|
BACKGROUND_TILE,
|
|
*IMAGE_GRAPHICS.values(),
|
|
*[graphic for sub_dict in PLAYER_GRAPHICS.values() for graphic in sub_dict.values()],
|
|
]
|
|
|
|
_textures: dict[str | RelatedTexture, Texture] = {}
|
|
|
|
|
|
def get_texture_by_identifier(texture_identifier: str | RelatedTexture) -> Texture:
|
|
if texture_identifier in _textures:
|
|
return _textures[texture_identifier]
|
|
|
|
if isinstance(texture_identifier, str):
|
|
image_data = pkgutil.get_data(game.__name__, f"graphics/{texture_identifier}")
|
|
if image_data is None:
|
|
raise RuntimeError(f'Could not find file "graphics/{texture_identifier}" for texture {texture_identifier}')
|
|
|
|
image_bytes = BytesIO(cast(Buffer, image_data))
|
|
texture = cast(Texture, CoreImage(image_bytes, ext="png").texture)
|
|
texture.mag_filter = "nearest"
|
|
_textures[texture_identifier] = texture
|
|
return texture
|
|
|
|
base_texture_filename, x, y, w, h = texture_identifier
|
|
|
|
base_texture = get_texture_by_identifier(base_texture_filename)
|
|
|
|
sub_texture = base_texture.get_region(x, y, w, h)
|
|
sub_texture.mag_filter = "nearest"
|
|
_textures[texture_identifier] = sub_texture
|
|
return sub_texture
|
|
|
|
|
|
def get_texture(graphic: Graphic | Literal["Grass"], player_sprite: PlayerSprite | None = None) -> Texture | None:
|
|
if graphic == Graphic.EMPTY:
|
|
return None
|
|
|
|
if graphic == "Grass":
|
|
return get_texture_by_identifier(BACKGROUND_TILE)
|
|
|
|
if graphic in IMAGE_GRAPHICS:
|
|
return get_texture_by_identifier(IMAGE_GRAPHICS[graphic])
|
|
|
|
if graphic in PLAYER_GRAPHICS:
|
|
if player_sprite is None:
|
|
raise ValueError("Tried to load a player graphic without specifying a player_sprite")
|
|
|
|
return get_texture_by_identifier(PLAYER_GRAPHICS[graphic][player_sprite])
|
|
|
|
logger.exception(f"Tried to load unknown graphic {graphic}.")
|
|
return get_texture(Graphic.UNKNOWN)
|