Source code for scripts.nqp.ui_elements.actor_info

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import pygame
from pygame_gui.core import ObjectID
from pygame_gui.core import UIElement as PygameUiElement
from pygame_gui.elements import UIImage, UITextBox, UIVerticalScrollBar
from snecs.typedefs import EntityID

import scripts.engine.core.matter
from scripts.engine.core import utility, world
from scripts.engine.core.component import Aesthetic, Afflictions, CombatStats, Identity, Resources, Traits
from scripts.engine.core.utility import get_class_members
from scripts.engine.internal.constant import (
    ASSET_PATH,
    GAP_SIZE,
    ICON_SIZE,
    INFINITE,
    PrimaryStat,
    SecondaryStat,
    UIElement,
)
from scripts.engine.internal.event import event_hub, ExitMenuEvent
from scripts.engine.widgets.window import Window

if TYPE_CHECKING:
    from typing import List, Optional, Tuple, Union

    from pygame_gui import UIManager


[docs]class ActorInfo(Window): """ Full detail about an npc entity. """
[docs] def __init__(self, rect: pygame.Rect, manager: UIManager): # sections self.selected_entity: Optional[EntityID] = None self.sections: List[PygameUiElement] = [] self.section_base_positions: List[int] = [] # complete base class init super().__init__(rect, manager, object_id=ObjectID("#actor_info", "@menu_window")) # setup scroll bar self.scrollbar_width = 50 self.scrollbar_size = 0.3 self.scrollbar = UIVerticalScrollBar( pygame.Rect((rect.width - self.scrollbar_width, 0), (self.scrollbar_width, rect.height)), self.scrollbar_size, manager, self, self, "#scrollbar", ) # block mouse clicks outside of menu self.set_blocking(True) # show self self.show() # confirm init complete logging.debug(f"Actor Info initialised.")
############## GET / SET ########################
[docs] def set_entity(self, entity: EntityID): """ Set the selected entity to show the info for that entity. """ self.selected_entity = entity
############### ACTIONS ######################### def _shift_children(self, target_y): for i, child in enumerate(self.sections): child.set_relative_position((child.relative_rect.x, self.section_base_positions[i] - target_y))
[docs] def update(self, time_delta: float): super().update(time_delta) new_y_portion = self.scrollbar.scroll_position / self.scrollbar.bottom_limit / (1 - self.scrollbar_size) if self.sections != []: if ( self.section_base_positions[-1] + self.sections[-1].relative_rect.height > self.rect.height - self.section_base_positions[0] ): new_y = new_y_portion * ( self.section_base_positions[-1] + self.sections[-1].relative_rect.height - (self.rect.height - self.section_base_positions[0]) ) self._shift_children(new_y)
[docs] def show(self): """ Show the entity info. Builds the sections required, after clearing any existing. """ super().show() self.visible = True entity = self.selected_entity # clear to refresh first self.cleanse() if entity: info: List[Tuple[str, Union[str, pygame.Surface]]] = [] section_break_image = utility.get_image( ASSET_PATH / "ui/menu_window_n_repeat.png", (self.rect.width - self.scrollbar_width, 13) ) # get aesthetic aesthetic = scripts.engine.core.matter.get_entitys_component(entity, Aesthetic) info.append(("image", aesthetic.sprites.icon)) # get identity identity = scripts.engine.core.matter.get_entitys_component(entity, Identity) info.append(("text", identity.name)) # get resources resources = scripts.engine.core.matter.get_entitys_component(entity, Resources) if resources: info.append(("text", f"Health: {resources.health}")) info.append(("text", f"Stamina: {resources.stamina}")) info.append(("image", section_break_image)) # get stats stats = scripts.engine.core.matter.get_entitys_component(entity, CombatStats) if stats: primary_stats = utility.get_class_members(PrimaryStat) for name in primary_stats: try: stat_value = getattr(stats, name.lower()) name = name.title() name = name.replace("_", " ") info.append(("text", f"{name}: {stat_value}")) # in case it fails to pull expected attribute except AttributeError: logging.warning(f"ActorInfo: attribute {name} not found in primary stats.") secondary_stats = get_class_members(SecondaryStat) for name in secondary_stats: try: stat_value = getattr(stats, name.lower()) name = name.title() name = name.replace("_", " ") info.append(("text", f"{name}: {stat_value}")) # in case it fails to pull expected attribute except AttributeError: logging.warning(f"ActorInfo: attribute {name} not found in secondary stats.") info.append(("image", section_break_image)) # get traits (skip for projectiles) try: traits = scripts.engine.core.matter.get_entitys_component(entity, Traits) if traits: names = "" for name in traits.names: # if more than one trait add a separator if len(names) > 1: names += ", " names += f"{name}" info.append(("text", names)) info.append(("image", section_break_image)) except: pass # get afflictions afflictions = scripts.engine.core.matter.get_entitys_component(entity, Afflictions) if afflictions: for affliction in afflictions.active: # get duration if affliction.duration == INFINITE: duration = "∞" else: duration = affliction.duration affliction_icon = utility.get_image(affliction.icon_path, (32, 32)) info.append(("image", affliction_icon)) info.append(("text", f"{affliction.key.title()}: {duration}")) # if no afflictions, say so if not afflictions: info.append(("text", "Not afflicted.")) # create the box for the info self._create_sections(info) # refresh scrollbar self.scrollbar.redraw_scrollbar()
[docs] def cleanse(self): """ Cleanse existing section info. """ # kill the box and clear the reference for element in self.sections: element.kill() self.sections = []
[docs] def process_close_button(self): # post game event event_hub.post(ExitMenuEvent(UIElement.ACTOR_INFO))
############## CREATE ######################## def _create_sections(self, info: List[Tuple[str, Union[str, pygame.Surface]]]): """ Create sections for the information about the tile """ sections = [] section_base_positions = [] current_y = 0 current_text_block = "" # draw info x = 0 width = self.rect.width - self.scrollbar_width text_height = 0 # box will resize height anyway # loop each image provided and use as header for each group of info for type_str, text_or_image in info: # build current text block if type_str == "text": assert isinstance(text_or_image, str) # handle mypy error current_text_block += text_or_image + "<br>" elif type_str == "image": assert isinstance(text_or_image, pygame.Surface) # handle mypy error # if we have text in the previous block, show it if current_text_block: ## Display text rect = pygame.Rect((x, current_y), (width, text_height)) ui_text = UITextBox( html_text=current_text_block, relative_rect=rect, manager=self.ui_manager, wrap_to_height=True, layer_starting_height=1, container=self.get_container(), ) sections.append(ui_text) section_base_positions.append(ui_text.relative_rect.y) # update position current_y += ui_text.rect.height + GAP_SIZE # clear to prevent any carry over ui_text = None current_text_block = "" ## Display image # draw info image_width = text_or_image.get_width() image_height = text_or_image.get_height() # if image is the icon_path then draw centre, otherwise draw left if image_width == ICON_SIZE: draw_x = int((self.rect.width / 2) - (image_width / 2)) else: draw_x = 0 # create rect and image element image_rect = pygame.Rect((draw_x, current_y), (image_width, image_height)) ui_image = UIImage( relative_rect=image_rect, image_surface=text_or_image, manager=self.ui_manager, container=self.get_container(), ) sections.append(ui_image) section_base_positions.append(ui_image.relative_rect.y) # update position current_y += image_height + GAP_SIZE # clear to prevent any carry over ui_image = None # we've left the loop, clean up left over text if current_text_block: ## Display text rect = pygame.Rect((x, current_y), (width, text_height)) ui_text = UITextBox( html_text=current_text_block, relative_rect=rect, manager=self.ui_manager, wrap_to_height=True, layer_starting_height=1, container=self.get_container(), ) sections.append(ui_text) section_base_positions.append(ui_text.relative_rect.y) # update main sections list self.sections = sections self.section_base_positions = section_base_positions