From 7551b7f797ab516d1ad3f37462187a275b55ee7a Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Sun, 25 Aug 2024 14:48:44 +0100 Subject: [PATCH 01/13] initial commit --- main.py | 3 +- scripts/game_structure/game_essentials.py | 4 +- scripts/game_structure/ui_elements.py | 126 +++++++++- scripts/screens/ClanScreen.py | 286 +++++++++++++--------- 4 files changed, 293 insertions(+), 126 deletions(-) diff --git a/main.py b/main.py index 7d9a17ac8e..dc3353b6da 100755 --- a/main.py +++ b/main.py @@ -62,8 +62,7 @@ from scripts.housekeeping.datadir import get_log_dir, setup_data_dir from scripts.housekeeping.version import get_version_info, VERSION_NAME - -directory = os.path.dirname(__file__) +directory = os.path.dirname(os.path.realpath("__file__")) if directory: os.chdir(directory) diff --git a/scripts/game_structure/game_essentials.py b/scripts/game_structure/game_essentials.py index c014577c95..e27dec99a2 100644 --- a/scripts/game_structure/game_essentials.py +++ b/scripts/game_structure/game_essentials.py @@ -150,7 +150,7 @@ class Game: debug_settings = { "showcoords": False, - "showbounds": False, + "showbounds": True, "visualdebugmode": False, "showfps": False, } @@ -444,7 +444,6 @@ def save_faded_cats(self, clanname): copy_of_info = "" for cat in game.cat_to_fade: - inter_cat = self.cat_class.all_cats[cat] # Add ID to list of faded cats. @@ -489,7 +488,6 @@ def save_faded_cats(self, clanname): with open( get_save_dir() + "/" + clanname + "/faded_cats_info_copy.txt", "a" ) as write_file: - if not os.path.exists( get_save_dir() + "/" + clanname + "/faded_cats_info_copy.txt" ): diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index a1cf6c2f16..ef8332103f 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -1,11 +1,16 @@ import html from functools import lru_cache from math import ceil -from typing import Tuple, Optional, List +from typing import Tuple, Optional, List, Union, Dict, Iterable, Callable import pygame import pygame_gui -from pygame_gui.core import UIContainer +from pygame_gui import ( + UI_BUTTON_PRESSED, + UI_BUTTON_DOUBLE_CLICKED, + UI_BUTTON_START_PRESS, +) +from pygame_gui.core import UIContainer, IContainerLikeInterface, UIElement, ObjectID from pygame_gui.core.gui_type_hints import RectLike, Coordinate from pygame_gui.core.interfaces import IUIManagerInterface from pygame_gui.core.text.html_parser import HTMLParser @@ -14,7 +19,7 @@ from pygame_gui.elements import UIAutoResizingContainer from scripts.game_structure import image_cache -from scripts.game_structure.game_essentials import game +from scripts.game_structure.game_essentials import game, screen from scripts.utility import scale, shorten_text_to_fit @@ -22,6 +27,73 @@ class UIImageButton(pygame_gui.elements.UIButton): """Subclass of pygame_gui's button class. This allows for auto-scaling of the button image.""" + def __init__( + self, + relative_rect: Union[RectLike, Coordinate], + text: str, + manager: Optional[IUIManagerInterface] = None, + container: Optional[IContainerLikeInterface] = None, + tool_tip_text: Union[str, None] = None, + starting_height: int = 1, + parent_element: UIElement = None, + object_id: Union[ObjectID, str, None] = None, + anchors: Dict[str, Union[str, UIElement]] = None, + allow_double_clicks: bool = False, + generate_click_events_from: Iterable[int] = frozenset([pygame.BUTTON_LEFT]), + visible: int = 1, + mask: Union[pygame.Mask, pygame.Surface, None] = None, + mask_padding: int = 2, + *, + command: Union[Callable, Dict[int, Callable]] = None, + tool_tip_object_id: Optional[ObjectID] = None, + text_kwargs: Optional[Dict[str, str]] = None, + tool_tip_text_kwargs: Optional[Dict[str, str]] = None, + max_dynamic_width: Optional[int] = None, + ): + self.mask_padding = mask_padding if mask_padding is not None else 0 + super().__init__( + relative_rect, + text, + manager, + container, + tool_tip_text, + starting_height, + parent_element, + object_id, + anchors, + allow_double_clicks, + generate_click_events_from, + visible, + command=command, + tool_tip_object_id=tool_tip_object_id, + text_kwargs=text_kwargs, + tool_tip_text_kwargs=tool_tip_text_kwargs, + max_dynamic_width=max_dynamic_width, + ) + + self._mask = None + self.mask = mask + + @property + def mask(self): + return self._mask + + @mask.setter + def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): + if not isinstance(val, pygame.Mask | pygame.Surface | None): + return + if val is not None: + if isinstance(val, pygame.Surface): + val = pygame.mask.from_surface(val, threshold=250) + + size = val.get_size() + val.scale( + (size[0] + self.mask_padding * 2, size[1] + self.mask_padding * 2) + ) + self._mask = val + else: + self._mask = None + def _set_any_images_from_theme(self): changed = False normal_image = None @@ -32,6 +104,7 @@ def _set_any_images_from_theme(self): normal_image = pygame.transform.scale( normal_image, self.relative_rect.size ) # auto-rescale the image + self.mask = normal_image except LookupError: normal_image = None finally: @@ -89,6 +162,36 @@ def _set_any_images_from_theme(self): return changed + def hover_point(self, hover_x: int, hover_y: int) -> bool: + # if not self.rect.collidepoint((hover_x, hover_y)): + # return False + pos_in_mask = ( + hover_x - self.rect.x, + hover_y - self.rect.y, + ) + if self.mask is None: + return self.rect.collidepoint((hover_x, hover_y)) + if ( + 0 <= pos_in_mask[0] < self.mask.get_size()[0] + and 0 <= pos_in_mask[1] < self.mask.get_size()[1] + ): + return ( + bool(self.mask.get_at(pos_in_mask)) + if self.mask is not None + else self.rect.collidepoint((hover_x, hover_y)) + ) + + def check_hover(self, time_delta: float, hovered_higher_element: bool) -> bool: + hover = super().check_hover(time_delta, hovered_higher_element) + if game.debug_settings["showbounds"] and self.mask is not None: + olist = self.mask.outline() + olist = [(x + self.rect[0], y + self.rect[1]) for x, y in olist] + if hover: + pygame.draw.lines(screen, (0, 255, 0), True, olist, width=4) + # else: + # pygame.draw.lines(screen, (255, 0, 0), True, olist, width=4) + return hover + class UIModifiedScrollingContainer(pygame_gui.elements.UIScrollingContainer): def __init__( @@ -102,7 +205,6 @@ def __init__( allow_scroll_x: bool = False, allow_scroll_y: bool = False, ): - super().__init__( relative_rect=relative_rect, manager=manager, @@ -269,7 +371,6 @@ def __init__( visible: int = 1, starting_height: int = 1, ): - super().__init__( relative_rect=relative_rect, visible_percentage=visible_percentage, @@ -370,8 +471,8 @@ def __init__( object_id=None, tool_tip_text=None, anchors=None, + mask_padding=None, ): - # We have to scale the image before putting it into the image object. Otherwise, the method of upscaling that # UIImage uses will make the pixel art fuzzy self.image = pygame_gui.elements.UIImage( @@ -398,6 +499,8 @@ def __init__( tool_tip_text=tool_tip_text, container=container, anchors=anchors, + mask=self.image.image, + mask_padding=mask_padding, ) def return_cat_id(self): @@ -461,9 +564,12 @@ def __init__( tool_tip_text=None, container=None, anchors=None, + mask=None, + mask_padding=None, ): self.cat_id = cat_id self.cat_object = cat_object + super().__init__( relative_rect, text, @@ -475,7 +581,9 @@ def __init__( tool_tip_text=tool_tip_text, container=container, anchors=anchors, - allow_double_clicks=True + allow_double_clicks=True, + mask=mask, + mask_padding=mask_padding, ) def return_cat_id(self): @@ -511,7 +619,6 @@ def __init__( text_kwargs=None, allow_split_dashes: bool = True, ): - self.line_spaceing = line_spacing super().__init__( @@ -598,7 +705,6 @@ def __init__( manager=None, style="bars", ): - # Change the color of the bar depending on the value and if it's a negative or positive trait if percent_full > 49: if positive_trait: @@ -658,7 +764,6 @@ def __init__( manager=None, layer_starting_height=1, ): - if ids: self.ids = ids else: @@ -772,7 +877,6 @@ def __init__( manager, check: bool = False, ): - self.checked = check relative_rect = scale(pygame.Rect(position, (68, 68))) diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index dcd2ef78a9..2977a7ab17 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -15,7 +15,9 @@ class ClanScreen(Screens): - max_sprites_displayed = 400 # we don't want 100,000 sprites rendering at once. 400 is enough. + max_sprites_displayed = ( + 400 # we don't want 100,000 sprites rendering at once. 400 is enough. + ) cat_buttons = [] def __init__(self, name=None): @@ -33,18 +35,18 @@ def __init__(self, name=None): self.layout = None def on_use(self): - if game.clan.clan_settings['backgrounds']: - if game.clan.current_season == 'Newleaf': + if game.clan.clan_settings["backgrounds"]: + if game.clan.current_season == "Newleaf": screen.blit(self.newleaf_bg, (0, 0)) - elif game.clan.current_season == 'Greenleaf': + elif game.clan.current_season == "Greenleaf": screen.blit(self.greenleaf_bg, (0, 0)) - elif game.clan.current_season == 'Leaf-bare': + elif game.clan.current_season == "Leaf-bare": screen.blit(self.leafbare_bg, (0, 0)) - elif game.clan.current_season == 'Leaf-fall': + elif game.clan.current_season == "Leaf-fall": screen.blit(self.leaffall_bg, (0, 0)) def handle_event(self, event): - if game.switches['window_open']: + if game.switches["window_open"]: pass elif event.type == pygame_gui.UI_BUTTON_START_PRESS: if event.ui_element == self.save_button: @@ -56,38 +58,38 @@ def handle_event(self, event): game.clan.save_pregnancy(game.clan) game.save_events() game.save_settings() - game.switches['saved_clan'] = True + game.switches["saved_clan"] = True self.update_buttons_and_text() except RuntimeError: SaveError(traceback.format_exc()) self.change_screen("start screen") if event.ui_element in self.cat_buttons: game.switches["cat"] = event.ui_element.return_cat_id() - self.change_screen('profile screen') + self.change_screen("profile screen") if event.ui_element == self.label_toggle: - if game.clan.clan_settings['den labels']: - game.clan.clan_settings['den labels'] = False + if game.clan.clan_settings["den labels"]: + game.clan.clan_settings["den labels"] = False else: - game.clan.clan_settings['den labels'] = True + game.clan.clan_settings["den labels"] = True self.update_buttons_and_text() if event.ui_element == self.med_den_label: - self.change_screen('med den screen') + self.change_screen("med den screen") else: self.menu_button_pressed(event) if event.ui_element == self.clearing_label: - self.change_screen('clearing screen') + self.change_screen("clearing screen") else: self.menu_button_pressed(event) if event.ui_element == self.warrior_den_label: - self.change_screen('warrior den screen') + self.change_screen("warrior den screen") if event.ui_element == self.leader_den_label: - self.change_screen('leader den screen') + self.change_screen("leader den screen") - elif event.type == pygame.KEYDOWN and game.settings['keybinds']: + elif event.type == pygame.KEYDOWN and game.settings["keybinds"]: if event.key == pygame.K_RIGHT: - self.change_screen('list screen') + self.change_screen("list screen") elif event.key == pygame.K_LEFT: - self.change_screen('events screen') + self.change_screen("events screen") elif event.key == pygame.K_SPACE: self.save_button_saving_state.show() self.save_button.disable() @@ -96,12 +98,12 @@ def handle_event(self, event): game.clan.save_pregnancy(game.clan) game.save_events() game.save_settings() - game.switches['saved_clan'] = True + game.switches["saved_clan"] = True self.update_buttons_and_text() def screen_switches(self): self.update_camp_bg() - game.switches['cat'] = None + game.switches["cat"] = None if game.clan.biome + game.clan.camp_bg in game.clan.layouts: self.layout = game.clan.layouts[game.clan.biome + game.clan.camp_bg] else: @@ -110,7 +112,7 @@ def screen_switches(self): self.choose_cat_positions() self.set_disabled_menu_buttons(["camp_screen"]) - self.update_heading_text(f'{game.clan.name}Clan') + self.update_heading_text(f"{game.clan.name}Clan") self.show_menu_buttons() # Creates and places the cat sprites. @@ -120,95 +122,126 @@ def screen_switches(self): # This should be a temp solution. We should change the code that determines positions. i = 0 for x in game.clan.clan_cats: - if not Cat.all_cats[x].dead and Cat.all_cats[x].in_camp and \ - not (Cat.all_cats[x].exiled or Cat.all_cats[x].outside) and ( - Cat.all_cats[x].status != 'newborn' or game.config['fun']['all_cats_are_newborn'] or - game.config['fun']['newborns_can_roam']): - + if ( + not Cat.all_cats[x].dead + and Cat.all_cats[x].in_camp + and not (Cat.all_cats[x].exiled or Cat.all_cats[x].outside) + and ( + Cat.all_cats[x].status != "newborn" + or game.config["fun"]["all_cats_are_newborn"] + or game.config["fun"]["newborns_can_roam"] + ) + ): i += 1 if i > self.max_sprites_displayed: break try: self.cat_buttons.append( - UISpriteButton(scale(pygame.Rect(tuple(Cat.all_cats[x].placement), (100, 100))), - Cat.all_cats[x].sprite, - cat_id=x, - starting_height=i) + UISpriteButton( + scale( + pygame.Rect( + tuple(Cat.all_cats[x].placement), (100, 100) + ) + ), + Cat.all_cats[x].sprite, + cat_id=x, + starting_height=i + 1, + tool_tip_text=str(Cat.all_cats[x].name), + mask_padding=10, + ) ) except: - print(f"ERROR: placing {Cat.all_cats[x].name}\'s sprite on Clan page") + print( + f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" + ) # Den Labels # Redo the locations, so that it uses layout on the Clan page - self.warrior_den_label = UIImageButton(scale(pygame.Rect( - self.layout["warrior den"], (242, 56))), + self.warrior_den_label = UIImageButton( + scale(pygame.Rect(self.layout["warrior den"], (242, 56))), "", object_id="#warrior_den_button", - starting_height=2 + starting_height=2, ) self.leader_den_label = UIImageButton( scale(pygame.Rect(self.layout["leader den"], (224, 56))), "", object_id="#lead_den_button", - starting_height=2) - self.med_den_label = UIImageButton(scale(pygame.Rect( - self.layout["medicine den"], (302, 56))), + starting_height=2, + ) + self.med_den_label = UIImageButton( + scale(pygame.Rect(self.layout["medicine den"], (302, 56))), "", object_id="#med_den_button", - starting_height=2 + starting_height=2, ) self.elder_den_label = pygame_gui.elements.UIImage( scale(pygame.Rect(self.layout["elder den"], (206, 56))), pygame.transform.scale( - image_cache.load_image('resources/images/elder_den.png'), - (206, 56)), + image_cache.load_image("resources/images/elder_den.png"), (206, 56) + ), ) - self.nursery_label = pygame_gui.elements.UIImage(scale(pygame.Rect(self.layout['nursery'], (160, 56))), - pygame.transform.scale( - image_cache.load_image('resources/images/nursery_den.png'), - (160, 56))) - if game.clan.game_mode == 'classic': + self.nursery_label = pygame_gui.elements.UIImage( + scale(pygame.Rect(self.layout["nursery"], (160, 56))), + pygame.transform.scale( + image_cache.load_image("resources/images/nursery_den.png"), (160, 56) + ), + ) + if game.clan.game_mode == "classic": self.clearing_label = pygame_gui.elements.UIImage( - scale(pygame.Rect(self.layout['clearing'], (162, 56))), + scale(pygame.Rect(self.layout["clearing"], (162, 56))), pygame.transform.scale( - image_cache.load_image('resources/images/buttons/clearing.png'), - (162, 56))) + image_cache.load_image("resources/images/buttons/clearing.png"), + (162, 56), + ), + ) else: - self.clearing_label = UIImageButton(scale(pygame.Rect( - self.layout['clearing'], (162, 56))), + self.clearing_label = UIImageButton( + scale(pygame.Rect(self.layout["clearing"], (162, 56))), "", object_id="#clearing_button", - starting_height=2 + starting_height=2, ) self.app_den_label = pygame_gui.elements.UIImage( - scale(pygame.Rect(self.layout['apprentice den'], (294, 56))), + scale(pygame.Rect(self.layout["apprentice den"], (294, 56))), pygame.transform.scale( - image_cache.load_image('resources/images/app_den.png'), - (294, 56))) + image_cache.load_image("resources/images/app_den.png"), (294, 56) + ), + ) # Draw the toggle and text - self.show_den_labels = pygame_gui.elements.UIImage(scale(pygame.Rect((50, 1282), (334, 68))), - pygame.transform.scale( - image_cache.load_image( - 'resources/images/show_den_labels.png'), - (334, 68))) + self.show_den_labels = pygame_gui.elements.UIImage( + scale(pygame.Rect((50, 1282), (334, 68))), + pygame.transform.scale( + image_cache.load_image("resources/images/show_den_labels.png"), + (334, 68), + ), + ) self.show_den_labels.disable() - self.label_toggle = UIImageButton(scale(pygame.Rect((50, 1282), (64, 64))), "", object_id="#checked_checkbox") + self.label_toggle = UIImageButton( + scale(pygame.Rect((50, 1282), (64, 64))), "", object_id="#checked_checkbox" + ) - self.save_button = UIImageButton(scale(pygame.Rect(((686, 1286), (228, 60)))), "", object_id="#save_button") + self.save_button = UIImageButton( + scale(pygame.Rect(((686, 1286), (228, 60)))), "", object_id="#save_button" + ) self.save_button.enable() self.save_button_saved_state = pygame_gui.elements.UIImage( scale(pygame.Rect((686, 1286), (228, 60))), pygame.transform.scale( - image_cache.load_image('resources/images/save_clan_saved.png'), - (228, 60))) + image_cache.load_image("resources/images/save_clan_saved.png"), + (228, 60), + ), + ) self.save_button_saved_state.hide() self.save_button_saving_state = pygame_gui.elements.UIImage( scale(pygame.Rect((686, 1286), (228, 60))), pygame.transform.scale( - image_cache.load_image('resources/images/save_clan_saving.png'), - (228, 60))) + image_cache.load_image("resources/images/save_clan_saving.png"), + (228, 60), + ), + ) self.save_button_saving_state.hide() self.update_buttons_and_text() @@ -246,22 +279,22 @@ def exit_screen(self): del self.show_den_labels # reset save status - game.switches['saved_clan'] = False + game.switches["saved_clan"] = False def update_camp_bg(self): light_dark = "light" if game.settings["dark mode"]: light_dark = "dark" - camp_bg_base_dir = 'resources/images/camp_bg/' + camp_bg_base_dir = "resources/images/camp_bg/" leaves = ["newleaf", "greenleaf", "leafbare", "leaffall"] camp_nr = game.clan.camp_bg if camp_nr is None: - camp_nr = 'camp1' + camp_nr = "camp1" game.clan.camp_bg = camp_nr - available_biome = ['Forest', 'Mountainous', 'Plains', 'Beach'] + available_biome = ["Forest", "Mountainous", "Plains", "Beach"] biome = game.clan.biome if biome not in available_biome: biome = available_biome[0] @@ -270,17 +303,23 @@ def update_camp_bg(self): all_backgrounds = [] for leaf in leaves: - platform_dir = f'{camp_bg_base_dir}/{biome}/{leaf}_{camp_nr}_{light_dark}.png' + platform_dir = ( + f"{camp_bg_base_dir}/{biome}/{leaf}_{camp_nr}_{light_dark}.png" + ) all_backgrounds.append(platform_dir) self.newleaf_bg = pygame.transform.scale( - pygame.image.load(all_backgrounds[0]).convert(), (screen_x, screen_y)) + pygame.image.load(all_backgrounds[0]).convert(), (screen_x, screen_y) + ) self.greenleaf_bg = pygame.transform.scale( - pygame.image.load(all_backgrounds[1]).convert(), (screen_x, screen_y)) + pygame.image.load(all_backgrounds[1]).convert(), (screen_x, screen_y) + ) self.leafbare_bg = pygame.transform.scale( - pygame.image.load(all_backgrounds[2]).convert(), (screen_x, screen_y)) + pygame.image.load(all_backgrounds[2]).convert(), (screen_x, screen_y) + ) self.leaffall_bg = pygame.transform.scale( - pygame.image.load(all_backgrounds[3]).convert(), (screen_x, screen_y)) + pygame.image.load(all_backgrounds[3]).convert(), (screen_x, screen_y) + ) def choose_nonoverlapping_positions(self, first_choices, dens, weights=None): if not weights: @@ -331,8 +370,15 @@ def choose_cat_positions(self): # taken. first_choices = deepcopy(self.layout) - all_dens = ["nursery place", "leader place", "elder place", "medicine place", "apprentice place", - "clearing place", "warrior place"] + all_dens = [ + "nursery place", + "leader place", + "elder place", + "medicine place", + "apprentice place", + "clearing place", + "warrior place", + ] # Allow two cat in the same position. for x in all_dens: @@ -343,41 +389,53 @@ def choose_cat_positions(self): continue # Newborns are not meant to be placed. They are hiding. - if Cat.all_cats[x].status == 'newborn' or game.config['fun']['all_cats_are_newborn']: - if game.config['fun']['all_cats_are_newborn'] or game.config['fun']['newborns_can_roam']: + if ( + Cat.all_cats[x].status == "newborn" + or game.config["fun"]["all_cats_are_newborn"] + ): + if ( + game.config["fun"]["all_cats_are_newborn"] + or game.config["fun"]["newborns_can_roam"] + ): # Free them - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 100, 1, 1, 1, 100, 50]) + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 100, 1, 1, 1, 100, 50] + ) else: continue - if Cat.all_cats[x].status in ['apprentice', 'mediator apprentice']: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 50, 1, 1, 100, 100, 1]) - elif Cat.all_cats[x].status == 'deputy': - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 50, 1, 1, 1, 50, 1]) - - elif Cat.all_cats[x].status == 'elder': - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 1, 2000, 1, 1, 1, 1]) - elif Cat.all_cats[x].status == 'kitten': - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [60, 8, 1, 1, 1, 1, 1]) - elif Cat.all_cats[x].status in [ - 'medicine cat apprentice', 'medicine cat' - ]: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [20, 20, 20, 400, 1, 1, 1]) - elif Cat.all_cats[x].status in ['warrior', 'mediator']: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 1, 1, 1, 1, 60, 60]) + if Cat.all_cats[x].status in ["apprentice", "mediator apprentice"]: + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 50, 1, 1, 100, 100, 1] + ) + elif Cat.all_cats[x].status == "deputy": + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 50, 1, 1, 1, 50, 1] + ) + + elif Cat.all_cats[x].status == "elder": + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 1, 2000, 1, 1, 1, 1] + ) + elif Cat.all_cats[x].status == "kitten": + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [60, 8, 1, 1, 1, 1, 1] + ) + elif Cat.all_cats[x].status in ["medicine cat apprentice", "medicine cat"]: + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [20, 20, 20, 400, 1, 1, 1] + ) + elif Cat.all_cats[x].status in ["warrior", "mediator"]: + Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 1, 1, 1, 1, 60, 60] + ) elif Cat.all_cats[x].status == "leader": - game.clan.leader.placement = self.choose_nonoverlapping_positions(first_choices, all_dens, - [1, 200, 1, 1, 1, 1, 1]) + game.clan.leader.placement = self.choose_nonoverlapping_positions( + first_choices, all_dens, [1, 200, 1, 1, 1, 1, 1] + ) def update_buttons_and_text(self): - if game.switches['saved_clan']: + if game.switches["saved_clan"]: self.save_button_saving_state.hide() self.save_button_saved_state.show() self.save_button.disable() @@ -385,9 +443,13 @@ def update_buttons_and_text(self): self.save_button.enable() self.label_toggle.kill() - if game.clan.clan_settings['den labels']: - self.label_toggle = UIImageButton(scale(pygame.Rect((50, 1282), (68, 68))), "", starting_height=2, - object_id="#checked_checkbox") + if game.clan.clan_settings["den labels"]: + self.label_toggle = UIImageButton( + scale(pygame.Rect((50, 1282), (68, 68))), + "", + starting_height=2, + object_id="#checked_checkbox", + ) self.warrior_den_label.show() self.clearing_label.show() self.nursery_label.show() @@ -396,8 +458,12 @@ def update_buttons_and_text(self): self.med_den_label.show() self.elder_den_label.show() else: - self.label_toggle = UIImageButton(scale(pygame.Rect((50, 1282), (68, 68))), "", starting_height=2, - object_id="#unchecked_checkbox") + self.label_toggle = UIImageButton( + scale(pygame.Rect((50, 1282), (68, 68))), + "", + starting_height=2, + object_id="#unchecked_checkbox", + ) self.warrior_den_label.hide() self.clearing_label.hide() self.nursery_label.hide() From 0d4b54db8d8dbed8ea75011e7bf3d0d267176722 Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Sun, 25 Aug 2024 15:59:17 +0100 Subject: [PATCH 02/13] bugfix: can't disable null --- scripts/game_structure/ui_elements.py | 65 ++++++++++++++++----------- scripts/screens/ClanScreen.py | 34 +++++++------- 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index ef8332103f..eeb3ce143e 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -5,11 +5,6 @@ import pygame import pygame_gui -from pygame_gui import ( - UI_BUTTON_PRESSED, - UI_BUTTON_DOUBLE_CLICKED, - UI_BUTTON_START_PRESS, -) from pygame_gui.core import UIContainer, IContainerLikeInterface, UIElement, ObjectID from pygame_gui.core.gui_type_hints import RectLike, Coordinate from pygame_gui.core.interfaces import IUIManagerInterface @@ -51,6 +46,7 @@ def __init__( max_dynamic_width: Optional[int] = None, ): self.mask_padding = mask_padding if mask_padding is not None else 0 + self.mask_info = [relative_rect[0:2], []] super().__init__( relative_rect, text, @@ -86,11 +82,34 @@ def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): if isinstance(val, pygame.Surface): val = pygame.mask.from_surface(val, threshold=250) - size = val.get_size() - val.scale( - (size[0] + self.mask_padding * 2, size[1] + self.mask_padding * 2) + inflated_mask = pygame.Mask( + ( + val.get_size()[0] + self.mask_padding * 2, + val.get_size()[1] + self.mask_padding * 2, + ) + ) + inflated_mask.draw(val, (self.mask_padding, self.mask_padding)) + for _ in range(self.mask_padding): + outline = inflated_mask.outline() + for point in outline: + for dx in range(-1, 2): + for dy in range(-1, 2): + try: + inflated_mask.set_at((point[0] + dx, point[1] + dy), 1) + except IndexError: + continue + self._mask = inflated_mask + self.mask_info[0] = ( + self.rect[0] - self.mask_padding, + self.rect[1] - self.mask_padding, ) - self._mask = val + self.mask_info[1] = [ + ( + x + self.mask_info[0][0], + y + self.mask_info[0][1], + ) + for x, y in self.mask.outline() + ] else: self._mask = None @@ -163,35 +182,31 @@ def _set_any_images_from_theme(self): return changed def hover_point(self, hover_x: int, hover_y: int) -> bool: - # if not self.rect.collidepoint((hover_x, hover_y)): - # return False - pos_in_mask = ( - hover_x - self.rect.x, - hover_y - self.rect.y, - ) if self.mask is None: return self.rect.collidepoint((hover_x, hover_y)) + pos_in_mask = (hover_x - self.mask_info[0][0], hover_y - self.mask_info[0][1]) if ( 0 <= pos_in_mask[0] < self.mask.get_size()[0] and 0 <= pos_in_mask[1] < self.mask.get_size()[1] ): - return ( - bool(self.mask.get_at(pos_in_mask)) - if self.mask is not None - else self.rect.collidepoint((hover_x, hover_y)) - ) + return bool(self.mask.get_at(pos_in_mask)) + else: + return False def check_hover(self, time_delta: float, hovered_higher_element: bool) -> bool: hover = super().check_hover(time_delta, hovered_higher_element) if game.debug_settings["showbounds"] and self.mask is not None: - olist = self.mask.outline() - olist = [(x + self.rect[0], y + self.rect[1]) for x, y in olist] if hover: - pygame.draw.lines(screen, (0, 255, 0), True, olist, width=4) - # else: - # pygame.draw.lines(screen, (255, 0, 0), True, olist, width=4) + pygame.draw.lines(screen, (0, 255, 0), True, self.mask_info[1], width=2) + else: + pygame.draw.lines(screen, (255, 0, 0), True, self.mask_info[1], width=2) return hover + def on_hovered(self): + super().on_hovered() + if self.mask is not None and self.tool_tip is not None: + self.tool_tip.disable() + class UIModifiedScrollingContainer(pygame_gui.elements.UIScrollingContainer): def __init__( diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index 2977a7ab17..bb792c7951 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -136,25 +136,23 @@ def screen_switches(self): if i > self.max_sprites_displayed: break - try: - self.cat_buttons.append( - UISpriteButton( - scale( - pygame.Rect( - tuple(Cat.all_cats[x].placement), (100, 100) - ) - ), - Cat.all_cats[x].sprite, - cat_id=x, - starting_height=i + 1, - tool_tip_text=str(Cat.all_cats[x].name), - mask_padding=10, - ) - ) - except: - print( - f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" + # try: + self.cat_buttons.append( + UISpriteButton( + scale( + pygame.Rect(tuple(Cat.all_cats[x].placement), (100, 100)) + ), + Cat.all_cats[x].sprite, + cat_id=x, + starting_height=i + 1, + tool_tip_text=str(Cat.all_cats[x].name), + mask_padding=3, ) + ) + # except: + # print( + # f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" + # ) # Den Labels # Redo the locations, so that it uses layout on the Clan page From 7a4b5b1d1a3e9b1324d988175ab149060e77c59a Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Sun, 25 Aug 2024 17:11:15 +0100 Subject: [PATCH 03/13] hover sprites working --- scripts/game_structure/game_essentials.py | 2 +- scripts/game_structure/ui_elements.py | 43 +++++++++------- scripts/screens/ClanScreen.py | 61 +++++++++++++++++------ 3 files changed, 72 insertions(+), 34 deletions(-) diff --git a/scripts/game_structure/game_essentials.py b/scripts/game_structure/game_essentials.py index e27dec99a2..0ba7f650c2 100644 --- a/scripts/game_structure/game_essentials.py +++ b/scripts/game_structure/game_essentials.py @@ -150,7 +150,7 @@ class Game: debug_settings = { "showcoords": False, - "showbounds": True, + "showbounds": False, "visualdebugmode": False, "showfps": False, } diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index eeb3ce143e..c18f0f97bb 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -45,7 +45,7 @@ def __init__( tool_tip_text_kwargs: Optional[Dict[str, str]] = None, max_dynamic_width: Optional[int] = None, ): - self.mask_padding = mask_padding if mask_padding is not None else 0 + self.mask_padding = mask_padding if mask_padding is not None else 2 self.mask_info = [relative_rect[0:2], []] super().__init__( relative_rect, @@ -79,26 +79,31 @@ def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): if not isinstance(val, pygame.Mask | pygame.Surface | None): return if val is not None: - if isinstance(val, pygame.Surface): + if isinstance(val, pygame.Mask): + self._mask = val + self.mask_padding = (val.get_size()[0] - self.rect[0]) / 2 + else: val = pygame.mask.from_surface(val, threshold=250) - inflated_mask = pygame.Mask( - ( - val.get_size()[0] + self.mask_padding * 2, - val.get_size()[1] + self.mask_padding * 2, + inflated_mask = pygame.Mask( + ( + val.get_size()[0] + self.mask_padding * 2, + val.get_size()[1] + self.mask_padding * 2, + ) ) - ) - inflated_mask.draw(val, (self.mask_padding, self.mask_padding)) - for _ in range(self.mask_padding): - outline = inflated_mask.outline() - for point in outline: - for dx in range(-1, 2): - for dy in range(-1, 2): - try: - inflated_mask.set_at((point[0] + dx, point[1] + dy), 1) - except IndexError: - continue - self._mask = inflated_mask + inflated_mask.draw(val, (self.mask_padding, self.mask_padding)) + for _ in range(self.mask_padding): + outline = inflated_mask.outline() + for point in outline: + for dx in range(-1, 2): + for dy in range(-1, 2): + try: + inflated_mask.set_at( + (point[0] + dx, point[1] + dy), 1 + ) + except IndexError: + continue + self._mask = inflated_mask self.mask_info[0] = ( self.rect[0] - self.mask_padding, self.rect[1] - self.mask_padding, @@ -500,8 +505,8 @@ def __init__( anchors=anchors, ) self.image.disable() - # The transparent button. This a subclass that UIButton that also hold the cat_id. + # The transparent button. This a subclass that UIButton that also hold the cat_id. self.button = CatButton( relative_rect, "", diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index bb792c7951..081d6559e6 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -22,6 +22,7 @@ class ClanScreen(Screens): def __init__(self, name=None): super().__init__(name) + self.taken_spaces = {} self.show_den_labels = None self.show_den_text = None self.label_toggle = None @@ -121,6 +122,8 @@ def screen_switches(self): # We have to convert the positions to something pygame_gui buttons will understand # This should be a temp solution. We should change the code that determines positions. i = 0 + all_positions = list(self.taken_spaces.values()) + used_positions = all_positions.copy() for x in game.clan.clan_cats: if ( not Cat.all_cats[x].dead @@ -137,6 +140,11 @@ def screen_switches(self): break # try: + layer = 2 + place = self.taken_spaces[Cat.all_cats[x].ID] + layer += all_positions.count(place) - used_positions.count(place) + used_positions.remove(place) + self.cat_buttons.append( UISpriteButton( scale( @@ -144,15 +152,14 @@ def screen_switches(self): ), Cat.all_cats[x].sprite, cat_id=x, - starting_height=i + 1, + starting_height=layer, tool_tip_text=str(Cat.all_cats[x].name), - mask_padding=3, ) ) # except: - # print( - # f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" - # ) + # print( + # f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" + # ) # Den Labels # Redo the locations, so that it uses layout on the Clan page @@ -344,7 +351,7 @@ def choose_nonoverlapping_positions(self, first_choices, dens, weights=None): just_pos[0] += 15 * random.choice([-1, 1]) if "y" in pos[1]: just_pos[1] += 15 - return tuple(just_pos) + return tuple(just_pos), pos[0] dens.pop(chosen_index) weights.pop(chosen_index) if not dens: @@ -386,6 +393,7 @@ def choose_cat_positions(self): if Cat.all_cats[x].dead or Cat.all_cats[x].outside: continue + base_pos = None # Newborns are not meant to be placed. They are hiding. if ( Cat.all_cats[x].status == "newborn" @@ -396,41 +404,66 @@ def choose_cat_positions(self): or game.config["fun"]["newborns_can_roam"] ): # Free them - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 100, 1, 1, 1, 100, 50] ) else: continue if Cat.all_cats[x].status in ["apprentice", "mediator apprentice"]: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 50, 1, 1, 100, 100, 1] ) elif Cat.all_cats[x].status == "deputy": - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 50, 1, 1, 1, 50, 1] ) elif Cat.all_cats[x].status == "elder": - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 1, 2000, 1, 1, 1, 1] ) elif Cat.all_cats[x].status == "kitten": - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [60, 8, 1, 1, 1, 1, 1] ) elif Cat.all_cats[x].status in ["medicine cat apprentice", "medicine cat"]: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [20, 20, 20, 400, 1, 1, 1] ) elif Cat.all_cats[x].status in ["warrior", "mediator"]: - Cat.all_cats[x].placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 1, 1, 1, 1, 60, 60] ) elif Cat.all_cats[x].status == "leader": - game.clan.leader.placement = self.choose_nonoverlapping_positions( + [ + Cat.all_cats[x].placement, + base_pos, + ] = self.choose_nonoverlapping_positions( first_choices, all_dens, [1, 200, 1, 1, 1, 1, 1] ) + self.taken_spaces[Cat.all_cats[x].ID] = base_pos def update_buttons_and_text(self): if game.switches["saved_clan"]: From 2044d46e0e468a6e405d41822fc0fe2fc972a5af Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Sun, 25 Aug 2024 19:26:50 +0100 Subject: [PATCH 04/13] cat.sprite_mask now exists for ease of use --- scripts/cat/cats.py | 25 +- scripts/cat/pelts.py | 1081 +++++++++++++++++++------ scripts/game_structure/ui_elements.py | 75 +- scripts/screens/ClanScreen.py | 66 +- scripts/utility.py | 287 ++++--- 5 files changed, 1073 insertions(+), 461 deletions(-) diff --git a/scripts/cat/cats.py b/scripts/cat/cats.py index 6a33c13ae3..6292a68664 100644 --- a/scripts/cat/cats.py +++ b/scripts/cat/cats.py @@ -9,8 +9,9 @@ import os.path import sys from random import choice, randint, sample, random, choices, getrandbits, randrange -from typing import Dict, List, Any +from typing import Dict, List, Any, Optional +import pygame import ujson # type: ignore from scripts.cat.history import History @@ -38,6 +39,7 @@ event_text_adjust, update_sprite, leader_ceremony_text_adjust, + update_mask, ) @@ -349,7 +351,8 @@ def __init__( ) # Private Sprite - self._sprite = None + self._sprite: Optional[pygame.Surface] = None + self._sprite_mask: Optional[pygame.Mask] = None # SAVE CAT INTO ALL_CATS DICTIONARY IN CATS-CLASS self.all_cats[self.ID] = self @@ -1494,6 +1497,7 @@ def one_moon(self): if old_age != self.age: # Things to do if the age changes self.personality.facet_wobble(facet_max=2) + self.pelt.rebuild_sprite = True # Set personality to correct type self.personality.set_kit(self.is_baby()) @@ -2043,7 +2047,7 @@ def get_permanent_condition(self, name, born_with=False, event_triggered=False): "GULL FEATHERS", "SPARROW FEATHERS", "CLOVER", - "DAISY" + "DAISY", ]: self.pelt.accessory = None if "HALFTAIL" in self.pelt.scars and self.pelt.accessory in [ @@ -2053,7 +2057,7 @@ def get_permanent_condition(self, name, born_with=False, event_triggered=False): "GULL FEATHERS", "SPARROW FEATHERS", "CLOVER", - "DAISY" + "DAISY", ]: self.pelt.accessory = None @@ -3329,13 +3333,24 @@ def moons(self, value: int): @property def sprite(self): # Update the sprite - update_sprite(self) + if self.pelt.rebuild_sprite: + self.pelt.rebuild_sprite = False + update_sprite(self) + update_mask(self) return self._sprite @sprite.setter def sprite(self, new_sprite): self._sprite = new_sprite + @property + def sprite_mask(self): + return self._sprite_mask + + @sprite_mask.setter + def sprite_mask(self, val): + self._sprite_mask = val + # ---------------------------------------------------------------------------- # # other # # ---------------------------------------------------------------------------- # diff --git a/scripts/cat/pelts.py b/scripts/cat/pelts.py index f95d3f39bc..fca2d0e84e 100644 --- a/scripts/cat/pelts.py +++ b/scripts/cat/pelts.py @@ -8,96 +8,393 @@ class Pelt: sprites_names = { - "SingleColour": 'single', - 'TwoColour': 'single', - 'Tabby': 'tabby', - 'Marbled': 'marbled', - 'Rosette': 'rosette', - 'Smoke': 'smoke', - 'Ticked': 'ticked', - 'Speckled': 'speckled', - 'Bengal': 'bengal', - 'Mackerel': 'mackerel', - 'Classic': 'classic', - 'Sokoke': 'sokoke', - 'Agouti': 'agouti', - 'Singlestripe': 'singlestripe', - 'Masked': 'masked', - 'Tortie': None, - 'Calico': None, + "SingleColour": "single", + "TwoColour": "single", + "Tabby": "tabby", + "Marbled": "marbled", + "Rosette": "rosette", + "Smoke": "smoke", + "Ticked": "ticked", + "Speckled": "speckled", + "Bengal": "bengal", + "Mackerel": "mackerel", + "Classic": "classic", + "Sokoke": "sokoke", + "Agouti": "agouti", + "Singlestripe": "singlestripe", + "Masked": "masked", + "Tortie": None, + "Calico": None, } # ATTRIBUTES, including non-pelt related pelt_colours = [ - 'WHITE', 'PALEGREY', 'SILVER', 'GREY', 'DARKGREY', 'GHOST', 'BLACK', 'CREAM', 'PALEGINGER', - 'GOLDEN', 'GINGER', 'DARKGINGER', 'SIENNA', 'LIGHTBROWN', 'LILAC', 'BROWN', 'GOLDEN-BROWN', 'DARKBROWN', - 'CHOCOLATE' + "WHITE", + "PALEGREY", + "SILVER", + "GREY", + "DARKGREY", + "GHOST", + "BLACK", + "CREAM", + "PALEGINGER", + "GOLDEN", + "GINGER", + "DARKGINGER", + "SIENNA", + "LIGHTBROWN", + "LILAC", + "BROWN", + "GOLDEN-BROWN", + "DARKBROWN", + "CHOCOLATE", ] pelt_c_no_white = [ - 'PALEGREY', 'SILVER', 'GREY', 'DARKGREY', 'GHOST', 'BLACK', 'CREAM', 'PALEGINGER', - 'GOLDEN', 'GINGER', 'DARKGINGER', 'SIENNA', 'LIGHTBROWN', 'LILAC', 'BROWN', 'GOLDEN-BROWN', 'DARKBROWN', - 'CHOCOLATE' + "PALEGREY", + "SILVER", + "GREY", + "DARKGREY", + "GHOST", + "BLACK", + "CREAM", + "PALEGINGER", + "GOLDEN", + "GINGER", + "DARKGINGER", + "SIENNA", + "LIGHTBROWN", + "LILAC", + "BROWN", + "GOLDEN-BROWN", + "DARKBROWN", + "CHOCOLATE", ] pelt_c_no_bw = [ - 'PALEGREY', 'SILVER', 'GREY', 'DARKGREY', 'CREAM', 'PALEGINGER', - 'GOLDEN', 'GINGER', 'DARKGINGER', 'SIENNA', 'LIGHTBROWN', 'LILAC', 'BROWN', 'GOLDEN-BROWN', 'DARKBROWN', - 'CHOCOLATE' + "PALEGREY", + "SILVER", + "GREY", + "DARKGREY", + "CREAM", + "PALEGINGER", + "GOLDEN", + "GINGER", + "DARKGINGER", + "SIENNA", + "LIGHTBROWN", + "LILAC", + "BROWN", + "GOLDEN-BROWN", + "DARKBROWN", + "CHOCOLATE", ] - tortiepatterns = ['ONE', 'TWO', 'THREE', 'FOUR', 'REDTAIL', 'DELILAH', 'MINIMALONE', 'MINIMALTWO', 'MINIMALTHREE', - 'MINIMALFOUR', 'HALF', - 'OREO', 'SWOOP', 'MOTTLED', 'SIDEMASK', 'EYEDOT', 'BANDANA', 'PACMAN', 'STREAMSTRIKE', 'ORIOLE', - 'CHIMERA', 'DAUB', 'EMBER', 'BLANKET', - 'ROBIN', 'BRINDLE', 'PAIGE', 'ROSETAIL', 'SAFI', 'SMUDGED', 'DAPPLENIGHT', 'STREAK', 'MASK', - 'CHEST', 'ARMTAIL', 'SMOKE', 'GRUMPYFACE', - 'BRIE', 'BELOVED', 'BODY', 'SHILOH', 'FRECKLED', 'HEARTBEAT'] - tortiebases = ['single', 'tabby', 'bengal', 'marbled', 'ticked', 'smoke', 'rosette', 'speckled', 'mackerel', - 'classic', 'sokoke', 'agouti', 'singlestripe', 'masked'] + tortiepatterns = [ + "ONE", + "TWO", + "THREE", + "FOUR", + "REDTAIL", + "DELILAH", + "MINIMALONE", + "MINIMALTWO", + "MINIMALTHREE", + "MINIMALFOUR", + "HALF", + "OREO", + "SWOOP", + "MOTTLED", + "SIDEMASK", + "EYEDOT", + "BANDANA", + "PACMAN", + "STREAMSTRIKE", + "ORIOLE", + "CHIMERA", + "DAUB", + "EMBER", + "BLANKET", + "ROBIN", + "BRINDLE", + "PAIGE", + "ROSETAIL", + "SAFI", + "SMUDGED", + "DAPPLENIGHT", + "STREAK", + "MASK", + "CHEST", + "ARMTAIL", + "SMOKE", + "GRUMPYFACE", + "BRIE", + "BELOVED", + "BODY", + "SHILOH", + "FRECKLED", + "HEARTBEAT", + ] + tortiebases = [ + "single", + "tabby", + "bengal", + "marbled", + "ticked", + "smoke", + "rosette", + "speckled", + "mackerel", + "classic", + "sokoke", + "agouti", + "singlestripe", + "masked", + ] pelt_length = ["short", "medium", "long"] - eye_colours = ['YELLOW', 'AMBER', 'HAZEL', 'PALEGREEN', 'GREEN', 'BLUE', 'DARKBLUE', 'GREY', 'CYAN', 'EMERALD', - 'PALEBLUE', - 'PALEYELLOW', 'GOLD', 'HEATHERBLUE', 'COPPER', 'SAGE', 'COBALT', 'SUNLITICE', 'GREENYELLOW', - 'BRONZE', 'SILVER'] - yellow_eyes = ['YELLOW', 'AMBER', 'PALEYELLOW', 'GOLD', 'COPPER', 'GREENYELLOW', 'BRONZE', 'SILVER'] - blue_eyes = ['BLUE', 'DARKBLUE', 'CYAN', 'PALEBLUE', 'HEATHERBLUE', 'COBALT', 'SUNLITICE', 'GREY'] - green_eyes = ['PALEGREEN', 'GREEN', 'EMERALD', 'SAGE', 'HAZEL'] + eye_colours = [ + "YELLOW", + "AMBER", + "HAZEL", + "PALEGREEN", + "GREEN", + "BLUE", + "DARKBLUE", + "GREY", + "CYAN", + "EMERALD", + "PALEBLUE", + "PALEYELLOW", + "GOLD", + "HEATHERBLUE", + "COPPER", + "SAGE", + "COBALT", + "SUNLITICE", + "GREENYELLOW", + "BRONZE", + "SILVER", + ] + yellow_eyes = [ + "YELLOW", + "AMBER", + "PALEYELLOW", + "GOLD", + "COPPER", + "GREENYELLOW", + "BRONZE", + "SILVER", + ] + blue_eyes = [ + "BLUE", + "DARKBLUE", + "CYAN", + "PALEBLUE", + "HEATHERBLUE", + "COBALT", + "SUNLITICE", + "GREY", + ] + green_eyes = ["PALEGREEN", "GREEN", "EMERALD", "SAGE", "HAZEL"] # bite scars by @wood pank on discord # scars from other cats, other animals - scars1 = ["ONE", "TWO", "THREE", "TAILSCAR", "SNOUT", "CHEEK", "SIDE", "THROAT", "TAILBASE", "BELLY", - "LEGBITE", "NECKBITE", "FACE", "MANLEG", "BRIGHTHEART", "MANTAIL", "BRIDGE", "RIGHTBLIND", "LEFTBLIND", - "BOTHBLIND", "BEAKCHEEK", "BEAKLOWER", "CATBITE", "RATBITE", "QUILLCHUNK", "QUILLSCRATCH", "HINDLEG", - "BACK", "QUILLSIDE", "SCRATCHSIDE", "BEAKSIDE", "CATBITETWO", "FOUR"] + scars1 = [ + "ONE", + "TWO", + "THREE", + "TAILSCAR", + "SNOUT", + "CHEEK", + "SIDE", + "THROAT", + "TAILBASE", + "BELLY", + "LEGBITE", + "NECKBITE", + "FACE", + "MANLEG", + "BRIGHTHEART", + "MANTAIL", + "BRIDGE", + "RIGHTBLIND", + "LEFTBLIND", + "BOTHBLIND", + "BEAKCHEEK", + "BEAKLOWER", + "CATBITE", + "RATBITE", + "QUILLCHUNK", + "QUILLSCRATCH", + "HINDLEG", + "BACK", + "QUILLSIDE", + "SCRATCHSIDE", + "BEAKSIDE", + "CATBITETWO", + "FOUR", + ] # missing parts - scars2 = ["LEFTEAR", "RIGHTEAR", "NOTAIL", "HALFTAIL", "NOPAW", "NOLEFTEAR", "NORIGHTEAR", "NOEAR"] + scars2 = [ + "LEFTEAR", + "RIGHTEAR", + "NOTAIL", + "HALFTAIL", + "NOPAW", + "NOLEFTEAR", + "NORIGHTEAR", + "NOEAR", + ] # "special" scars that could only happen in a special event - scars3 = ["SNAKE", "TOETRAP", "BURNPAWS", "BURNTAIL", "BURNBELLY", "BURNRUMP", "FROSTFACE", "FROSTTAIL", - "FROSTMITT", "FROSTSOCK", "TOE", "SNAKETWO"] + scars3 = [ + "SNAKE", + "TOETRAP", + "BURNPAWS", + "BURNTAIL", + "BURNBELLY", + "BURNRUMP", + "FROSTFACE", + "FROSTTAIL", + "FROSTMITT", + "FROSTSOCK", + "TOE", + "SNAKETWO", + ] # make sure to add plural and singular forms of new accs to acc_display.json so that they will display nicely - plant_accessories = ["MAPLE LEAF", "HOLLY", "BLUE BERRIES", "FORGET ME NOTS", "RYE STALK", "CATTAIL", "POPPY", "ORANGE POPPY", "CYAN POPPY", "WHITE POPPY", "PINK POPPY", - "BLUEBELLS", "LILY OF THE VALLEY", "SNAPDRAGON", "HERBS", "PETALS", "NETTLE", "HEATHER", "GORSE", "JUNIPER", "RASPBERRY", "LAVENDER", - "OAK LEAVES", "CATMINT", "MAPLE SEED", "LAUREL", "BULB WHITE", "BULB YELLOW", "BULB ORANGE", "BULB PINK", "BULB BLUE", "CLOVER", "DAISY", - "CLOVER", "DAISY", "LILY OF THE VALLEY", "HEATHER", "SNAPDRAGON", "GORSE", "BULB WHITE", "BULB YELLOW", - "DRY HERBS", "DRY CATMINT", "DRY NETTLES", "DRY LAURELS" - ] - wild_accessories = ["RED FEATHERS", "BLUE FEATHERS", "JAY FEATHERS", "GULL FEATHERS", "SPARROW FEATHERS", "MOTH WINGS", "ROSY MOTH WINGS", "MORPHO BUTTERFLY", "MONARCH BUTTERFLY", "CICADA WINGS", "BLACK CICADA"] - - tail_accessories = ["RED FEATHERS", "BLUE FEATHERS", "JAY FEATHERS", "GULL FEATHERS", "SPARROW FEATHERS", "CLOVER", "DAISY"] + plant_accessories = [ + "MAPLE LEAF", + "HOLLY", + "BLUE BERRIES", + "FORGET ME NOTS", + "RYE STALK", + "CATTAIL", + "POPPY", + "ORANGE POPPY", + "CYAN POPPY", + "WHITE POPPY", + "PINK POPPY", + "BLUEBELLS", + "LILY OF THE VALLEY", + "SNAPDRAGON", + "HERBS", + "PETALS", + "NETTLE", + "HEATHER", + "GORSE", + "JUNIPER", + "RASPBERRY", + "LAVENDER", + "OAK LEAVES", + "CATMINT", + "MAPLE SEED", + "LAUREL", + "BULB WHITE", + "BULB YELLOW", + "BULB ORANGE", + "BULB PINK", + "BULB BLUE", + "CLOVER", + "DAISY", + "CLOVER", + "DAISY", + "LILY OF THE VALLEY", + "HEATHER", + "SNAPDRAGON", + "GORSE", + "BULB WHITE", + "BULB YELLOW", + "DRY HERBS", + "DRY CATMINT", + "DRY NETTLES", + "DRY LAURELS", + ] + wild_accessories = [ + "RED FEATHERS", + "BLUE FEATHERS", + "JAY FEATHERS", + "GULL FEATHERS", + "SPARROW FEATHERS", + "MOTH WINGS", + "ROSY MOTH WINGS", + "MORPHO BUTTERFLY", + "MONARCH BUTTERFLY", + "CICADA WINGS", + "BLACK CICADA", + ] + + tail_accessories = [ + "RED FEATHERS", + "BLUE FEATHERS", + "JAY FEATHERS", + "GULL FEATHERS", + "SPARROW FEATHERS", + "CLOVER", + "DAISY", + ] collars = [ - "CRIMSON", "BLUE", "YELLOW", "CYAN", "RED", "LIME", "GREEN", "RAINBOW", - "BLACK", "SPIKES", "WHITE", "PINK", "PURPLE", "MULTI", "INDIGO", "CRIMSONBELL", "BLUEBELL", - "YELLOWBELL", "CYANBELL", "REDBELL", "LIMEBELL", "GREENBELL", - "RAINBOWBELL", "BLACKBELL", "SPIKESBELL", "WHITEBELL", "PINKBELL", "PURPLEBELL", - "MULTIBELL", "INDIGOBELL", "CRIMSONBOW", "BLUEBOW", "YELLOWBOW", "CYANBOW", "REDBOW", - "LIMEBOW", "GREENBOW", "RAINBOWBOW", "BLACKBOW", "SPIKESBOW", "WHITEBOW", "PINKBOW", - "PURPLEBOW", "MULTIBOW", "INDIGOBOW", "CRIMSONNYLON", "BLUENYLON", "YELLOWNYLON", "CYANNYLON", - "REDNYLON", "LIMENYLON", "GREENNYLON", "RAINBOWNYLON", - "BLACKNYLON", "SPIKESNYLON", "WHITENYLON", "PINKNYLON", "PURPLENYLON", "MULTINYLON", "INDIGONYLON", + "CRIMSON", + "BLUE", + "YELLOW", + "CYAN", + "RED", + "LIME", + "GREEN", + "RAINBOW", + "BLACK", + "SPIKES", + "WHITE", + "PINK", + "PURPLE", + "MULTI", + "INDIGO", + "CRIMSONBELL", + "BLUEBELL", + "YELLOWBELL", + "CYANBELL", + "REDBELL", + "LIMEBELL", + "GREENBELL", + "RAINBOWBELL", + "BLACKBELL", + "SPIKESBELL", + "WHITEBELL", + "PINKBELL", + "PURPLEBELL", + "MULTIBELL", + "INDIGOBELL", + "CRIMSONBOW", + "BLUEBOW", + "YELLOWBOW", + "CYANBOW", + "REDBOW", + "LIMEBOW", + "GREENBOW", + "RAINBOWBOW", + "BLACKBOW", + "SPIKESBOW", + "WHITEBOW", + "PINKBOW", + "PURPLEBOW", + "MULTIBOW", + "INDIGOBOW", + "CRIMSONNYLON", + "BLUENYLON", + "YELLOWNYLON", + "CYANNYLON", + "REDNYLON", + "LIMENYLON", + "GREENNYLON", + "RAINBOWNYLON", + "BLACKNYLON", + "SPIKESNYLON", + "WHITENYLON", + "PINKNYLON", + "PURPLENYLON", + "MULTINYLON", + "INDIGONYLON", ] tabbies = ["Tabby", "Ticked", "Mackerel", "Classic", "Sokoke", "Agouti"] @@ -109,74 +406,259 @@ class Pelt: # SPRITE NAMES single_colours = [ - 'WHITE', 'PALEGREY', 'SILVER', 'GREY', 'DARKGREY', 'GHOST', 'BLACK', 'CREAM', 'PALEGINGER', - 'GOLDEN', 'GINGER', 'DARKGINGER', 'SIENNA', 'LIGHTBROWN', 'LILAC', 'BROWN', 'GOLDEN-BROWN', 'DARKBROWN', - 'CHOCOLATE' + "WHITE", + "PALEGREY", + "SILVER", + "GREY", + "DARKGREY", + "GHOST", + "BLACK", + "CREAM", + "PALEGINGER", + "GOLDEN", + "GINGER", + "DARKGINGER", + "SIENNA", + "LIGHTBROWN", + "LILAC", + "BROWN", + "GOLDEN-BROWN", + "DARKBROWN", + "CHOCOLATE", + ] + ginger_colours = ["CREAM", "PALEGINGER", "GOLDEN", "GINGER", "DARKGINGER", "SIENNA"] + black_colours = ["GREY", "DARKGREY", "GHOST", "BLACK"] + white_colours = ["WHITE", "PALEGREY", "SILVER"] + brown_colours = [ + "LIGHTBROWN", + "LILAC", + "BROWN", + "GOLDEN-BROWN", + "DARKBROWN", + "CHOCOLATE", ] - ginger_colours = ['CREAM', 'PALEGINGER', 'GOLDEN', 'GINGER', 'DARKGINGER', 'SIENNA'] - black_colours = ['GREY', 'DARKGREY', 'GHOST', 'BLACK'] - white_colours = ['WHITE', 'PALEGREY', 'SILVER'] - brown_colours = ['LIGHTBROWN', 'LILAC', 'BROWN', 'GOLDEN-BROWN', 'DARKBROWN', 'CHOCOLATE'] colour_categories = [ginger_colours, black_colours, white_colours, brown_colours] eye_sprites = [ - 'YELLOW', 'AMBER', 'HAZEL', 'PALEGREEN', 'GREEN', 'BLUE', 'DARKBLUE', 'BLUEYELLOW', 'BLUEGREEN', - 'GREY', 'CYAN', 'EMERALD', 'PALEBLUE', 'PALEYELLOW', 'GOLD', 'HEATHERBLUE', 'COPPER', 'SAGE', 'COBALT', - 'SUNLITICE', 'GREENYELLOW', 'BRONZE', 'SILVER' + "YELLOW", + "AMBER", + "HAZEL", + "PALEGREEN", + "GREEN", + "BLUE", + "DARKBLUE", + "BLUEYELLOW", + "BLUEGREEN", + "GREY", + "CYAN", + "EMERALD", + "PALEBLUE", + "PALEYELLOW", + "GOLD", + "HEATHERBLUE", + "COPPER", + "SAGE", + "COBALT", + "SUNLITICE", + "GREENYELLOW", + "BRONZE", + "SILVER", + ] + little_white = [ + "LITTLE", + "LIGHTTUXEDO", + "BUZZARDFANG", + "TIP", + "BLAZE", + "BIB", + "VEE", + "PAWS", + "BELLY", + "TAILTIP", + "TOES", + "BROKENBLAZE", + "LILTWO", + "SCOURGE", + "TOESTAIL", + "RAVENPAW", + "HONEY", + "LUNA", + "EXTRA", + "MUSTACHE", + "REVERSEHEART", + "SPARKLE", + "RIGHTEAR", + "LEFTEAR", + "ESTRELLA", + "REVERSEEYE", + "BACKSPOT", + "EYEBAGS", + "LOCKET", + "BLAZEMASK", + "TEARS", + ] + mid_white = [ + "TUXEDO", + "FANCY", + "UNDERS", + "DAMIEN", + "SKUNK", + "MITAINE", + "SQUEAKS", + "STAR", + "WINGS", + "DIVA", + "SAVANNAH", + "FADESPOTS", + "BEARD", + "DAPPLEPAW", + "TOPCOVER", + "WOODPECKER", + "MISS", + "BOWTIE", + "VEST", + "FADEBELLY", + "DIGIT", + "FCTWO", + "FCONE", + "MIA", + "ROSINA", + "PRINCESS", + "DOUGIE", + ] + high_white = [ + "ANY", + "ANYTWO", + "BROKEN", + "FRECKLES", + "RINGTAIL", + "HALFFACE", + "PANTSTWO", + "GOATEE", + "PRINCE", + "FAROFA", + "MISTER", + "PANTS", + "REVERSEPANTS", + "HALFWHITE", + "APPALOOSA", + "PIEBALD", + "CURVED", + "GLASS", + "MASKMANTLE", + "MAO", + "PAINTED", + "SHIBAINU", + "OWL", + "BUB", + "SPARROW", + "TRIXIE", + "SAMMY", + "FRONT", + "BLOSSOMSTEP", + "BULLSEYE", + "FINN", + "SCAR", + "BUSTER", + "HAWKBLAZE", + "CAKE", + ] + mostly_white = [ + "VAN", + "ONEEAR", + "LIGHTSONG", + "TAIL", + "HEART", + "MOORISH", + "APRON", + "CAPSADDLE", + "CHESTSPECK", + "BLACKSTAR", + "PETAL", + "HEARTTWO", + "PEBBLESHINE", + "BOOTS", + "COW", + "COWTWO", + "LOVEBUG", + "SHOOTINGSTAR", + "EYESPOT", + "PEBBLE", + "TAILTWO", + "BUDDY", + "KROPKA", + ] + point_markings = ["COLOURPOINT", "RAGDOLL", "SEPIAPOINT", "MINKPOINT", "SEALPOINT"] + vit = [ + "VITILIGO", + "VITILIGOTWO", + "MOON", + "PHANTOM", + "KARPATI", + "POWDER", + "BLEACHED", + "SMOKEY", ] - little_white = ['LITTLE', 'LIGHTTUXEDO', 'BUZZARDFANG', 'TIP', 'BLAZE', 'BIB', 'VEE', 'PAWS', - 'BELLY', 'TAILTIP', 'TOES', 'BROKENBLAZE', 'LILTWO', 'SCOURGE', 'TOESTAIL', 'RAVENPAW', 'HONEY', - 'LUNA', - 'EXTRA', 'MUSTACHE', 'REVERSEHEART', 'SPARKLE', 'RIGHTEAR', 'LEFTEAR', 'ESTRELLA', 'REVERSEEYE', - 'BACKSPOT', - 'EYEBAGS', 'LOCKET', 'BLAZEMASK', 'TEARS'] - mid_white = ['TUXEDO', 'FANCY', 'UNDERS', 'DAMIEN', 'SKUNK', 'MITAINE', 'SQUEAKS', 'STAR', 'WINGS', - 'DIVA', 'SAVANNAH', 'FADESPOTS', 'BEARD', 'DAPPLEPAW', 'TOPCOVER', 'WOODPECKER', 'MISS', 'BOWTIE', - 'VEST', - 'FADEBELLY', 'DIGIT', 'FCTWO', 'FCONE', 'MIA', 'ROSINA', 'PRINCESS', 'DOUGIE'] - high_white = ['ANY', 'ANYTWO', 'BROKEN', 'FRECKLES', 'RINGTAIL', 'HALFFACE', 'PANTSTWO', - 'GOATEE', 'PRINCE', 'FAROFA', 'MISTER', 'PANTS', 'REVERSEPANTS', 'HALFWHITE', 'APPALOOSA', 'PIEBALD', - 'CURVED', 'GLASS', 'MASKMANTLE', 'MAO', 'PAINTED', 'SHIBAINU', 'OWL', 'BUB', 'SPARROW', 'TRIXIE', - 'SAMMY', 'FRONT', 'BLOSSOMSTEP', 'BULLSEYE', 'FINN', 'SCAR', 'BUSTER', 'HAWKBLAZE', 'CAKE'] - mostly_white = ['VAN', 'ONEEAR', 'LIGHTSONG', 'TAIL', 'HEART', 'MOORISH', 'APRON', 'CAPSADDLE', - 'CHESTSPECK', 'BLACKSTAR', 'PETAL', 'HEARTTWO', 'PEBBLESHINE', 'BOOTS', 'COW', 'COWTWO', 'LOVEBUG', - 'SHOOTINGSTAR', 'EYESPOT', 'PEBBLE', 'TAILTWO', 'BUDDY', 'KROPKA'] - point_markings = ['COLOURPOINT', 'RAGDOLL', 'SEPIAPOINT', 'MINKPOINT', 'SEALPOINT'] - vit = ['VITILIGO', 'VITILIGOTWO', 'MOON', 'PHANTOM', 'KARPATI', 'POWDER', 'BLEACHED', 'SMOKEY'] white_sprites = [ - little_white, mid_white, high_white, mostly_white, point_markings, vit, 'FULLWHITE'] + little_white, + mid_white, + high_white, + mostly_white, + point_markings, + vit, + "FULLWHITE", + ] - skin_sprites = ['BLACK', 'PINK', 'DARKBROWN', 'BROWN', 'LIGHTBROWN', 'DARK', 'DARKGREY', 'GREY', 'DARKSALMON', - 'SALMON', 'PEACH', 'DARKMARBLED', 'MARBLED', 'LIGHTMARBLED', 'DARKBLUE', 'BLUE', 'LIGHTBLUE', 'RED'] + skin_sprites = [ + "BLACK", + "PINK", + "DARKBROWN", + "BROWN", + "LIGHTBROWN", + "DARK", + "DARKGREY", + "GREY", + "DARKSALMON", + "SALMON", + "PEACH", + "DARKMARBLED", + "MARBLED", + "LIGHTMARBLED", + "DARKBLUE", + "BLUE", + "LIGHTBLUE", + "RED", + ] """Holds all appearance information for a cat. """ - def __init__(self, - name: str = "SingleColour", - length: str = "short", - colour: str = "WHITE", - white_patches: str = None, - eye_color: str = "BLUE", - eye_colour2: str = None, - tortiebase: str = None, - tortiecolour: str = None, - pattern: str = None, - tortiepattern: str = None, - vitiligo: str = None, - points: str = None, - accessory: str = None, - paralyzed: bool = False, - opacity: int = 100, - scars: list = None, - tint: str = "none", - skin: str = "BLACK", - white_patches_tint: str = "none", - kitten_sprite: int = None, - adol_sprite: int = None, - adult_sprite: int = None, - senior_sprite: int = None, - para_adult_sprite: int = None, - reverse: bool = False, - ) -> None: + def __init__( + self, + name: str = "SingleColour", + length: str = "short", + colour: str = "WHITE", + white_patches: str = None, + eye_color: str = "BLUE", + eye_colour2: str = None, + tortiebase: str = None, + tortiecolour: str = None, + pattern: str = None, + tortiepattern: str = None, + vitiligo: str = None, + points: str = None, + accessory: str = None, + paralyzed: bool = False, + opacity: int = 100, + scars: list = None, + tint: str = "none", + skin: str = "BLACK", + white_patches_tint: str = "none", + kitten_sprite: int = None, + adol_sprite: int = None, + adult_sprite: int = None, + senior_sprite: int = None, + para_adult_sprite: int = None, + reverse: bool = False, + ) -> None: self.name = name self.colour = colour self.white_patches = white_patches @@ -189,27 +671,48 @@ def __init__(self, self.vitiligo = vitiligo self.length = length self.points = points - self.accessory = accessory - self.paralyzed = paralyzed + self.rebuild_sprite = True + self._accessory = accessory + self._paralyzed = paralyzed self.opacity = opacity self.scars = scars if isinstance(scars, list) else [] self.tint = tint self.white_patches_tint = white_patches_tint - self.cat_sprites = {"kitten": kitten_sprite if kitten_sprite is not None else 0, - "adolescent": adol_sprite if adol_sprite is not None else 0, - "young adult": adult_sprite if adult_sprite is not None else 0, - "adult": adult_sprite if adult_sprite is not None else 0, - "senior adult": adult_sprite if adult_sprite is not None else 0, - "senior": senior_sprite if senior_sprite is not None else 0, - "para_adult": para_adult_sprite if para_adult_sprite is not None else 0, - 'newborn': 20, - 'para_young': 17, - "sick_adult": 18, - "sick_young": 19} + self.cat_sprites = { + "kitten": kitten_sprite if kitten_sprite is not None else 0, + "adolescent": adol_sprite if adol_sprite is not None else 0, + "young adult": adult_sprite if adult_sprite is not None else 0, + "adult": adult_sprite if adult_sprite is not None else 0, + "senior adult": adult_sprite if adult_sprite is not None else 0, + "senior": senior_sprite if senior_sprite is not None else 0, + "para_adult": para_adult_sprite if para_adult_sprite is not None else 0, + "newborn": 20, + "para_young": 17, + "sick_adult": 18, + "sick_young": 19, + } self.reverse = reverse self.skin = skin + @property + def accessory(self): + return self._accessory + + @accessory.setter + def accessory(self, val): + self.rebuild_sprite = True + self._accessory = val + + @property + def paralyzed(self): + return self._paralyzed + + @paralyzed.setter + def paralyzed(self, val): + self.rebuild_sprite = True + self._paralyzed = val + @staticmethod def generate_new_pelt(gender: str, parents: tuple = (), age: str = "adult"): new_pelt = Pelt() @@ -227,22 +730,22 @@ def generate_new_pelt(gender: str, parents: tuple = (), age: str = "adult"): def check_and_convert(self, convert_dict): """Checks for old-type properties for the appearance-related properties - that are stored in Pelt, and converts them. To be run when loading a cat in. """ - - # First, convert from some old names that may be in white_patches. - if self.white_patches == 'POINTMARK': + that are stored in Pelt, and converts them. To be run when loading a cat in.""" + + # First, convert from some old names that may be in white_patches. + if self.white_patches == "POINTMARK": self.white_patches = "SEALPOINT" - elif self.white_patches == 'PANTS2': - self.white_patches = 'PANTSTWO' - elif self.white_patches == 'ANY2': - self.white_patches = 'ANYTWO' + elif self.white_patches == "PANTS2": + self.white_patches = "PANTSTWO" + elif self.white_patches == "ANY2": + self.white_patches = "ANYTWO" elif self.white_patches == "VITILIGO2": self.white_patches = "VITILIGOTWO" - + if self.vitiligo == "VITILIGO2": self.vitiligo = "VITILIGOTWO" - # Move white_patches that should be in vit or points. + # Move white_patches that should be in vit or points. if self.white_patches in Pelt.vit: self.vitiligo = self.white_patches self.white_patches = None @@ -258,7 +761,7 @@ def check_and_convert(self, convert_dict): if self.white_patches in convert_dict["old_creamy_patches"]: self.white_patches = convert_dict["old_creamy_patches"][self.white_patches] self.white_patches_tint = "darkcream" - elif self.white_patches in ['SEPIAPOINT', 'MINKPOINT', 'SEALPOINT']: + elif self.white_patches in ["SEPIAPOINT", "MINKPOINT", "SEALPOINT"]: self.white_patches_tint = "none" # Eye Color Convert Stuff @@ -274,27 +777,27 @@ def check_and_convert(self, convert_dict): self.eye_colour2 = "GREEN" self.eye_colour = "BLUE" - if self.length == 'long': - if self.cat_sprites['adult'] not in [9, 10, 11]: - if self.cat_sprites['adult'] == 0: - self.cat_sprites['adult'] = 9 - elif self.cat_sprites['adult'] == 1: - self.cat_sprites['adult'] = 10 - elif self.cat_sprites['adult'] == 2: - self.cat_sprites['adult'] = 11 - self.cat_sprites['young adult'] = self.cat_sprites['adult'] - self.cat_sprites['senior adult'] = self.cat_sprites['adult'] - self.cat_sprites['para_adult'] = 16 + if self.length == "long": + if self.cat_sprites["adult"] not in [9, 10, 11]: + if self.cat_sprites["adult"] == 0: + self.cat_sprites["adult"] = 9 + elif self.cat_sprites["adult"] == 1: + self.cat_sprites["adult"] = 10 + elif self.cat_sprites["adult"] == 2: + self.cat_sprites["adult"] = 11 + self.cat_sprites["young adult"] = self.cat_sprites["adult"] + self.cat_sprites["senior adult"] = self.cat_sprites["adult"] + self.cat_sprites["para_adult"] = 16 else: - self.cat_sprites['para_adult'] = 15 - if self.cat_sprites['senior'] not in [12, 13, 14]: - if self.cat_sprites['senior'] == 3: - self.cat_sprites['senior'] = 12 - elif self.cat_sprites['senior'] == 4: - self.cat_sprites['senior'] = 13 - elif self.cat_sprites['senior'] == 5: - self.cat_sprites['senior'] = 14 - + self.cat_sprites["para_adult"] = 15 + if self.cat_sprites["senior"] not in [12, 13, 14]: + if self.cat_sprites["senior"] == 3: + self.cat_sprites["senior"] = 12 + elif self.cat_sprites["senior"] == 4: + self.cat_sprites["senior"] = 13 + elif self.cat_sprites["senior"] == 5: + self.cat_sprites["senior"] = 14 + if self.pattern in convert_dict["old_tortie_patches"]: old_pattern = self.pattern self.pattern = convert_dict["old_tortie_patches"][old_pattern][1] @@ -305,7 +808,7 @@ def check_and_convert(self, convert_dict): # tortiecolour and pelt_colour will be the same. Therefore, let's also re-set the pelt color self.colour = self.tortiecolour self.tortiecolour = convert_dict["old_tortie_patches"][old_pattern][0] - + if self.pattern == "MINIMAL1": self.pattern = "MINIMALONE" elif self.pattern == "MINIMAL2": @@ -314,18 +817,23 @@ def check_and_convert(self, convert_dict): self.pattern = "MINIMALTHREE" elif self.pattern == "MINIMAL4": self.pattern = "MINIMALFOUR" - + def init_eyes(self, parents): if not parents: self.eye_colour = choice(Pelt.eye_colours) else: - self.eye_colour = choice([i.pelt.eye_colour for i in parents] + [choice(Pelt.eye_colours)]) + self.eye_colour = choice( + [i.pelt.eye_colour for i in parents] + [choice(Pelt.eye_colours)] + ) # White patches must be initalized before eye color. num = game.config["cat_generation"]["base_heterochromia"] - if self.white_patches in [Pelt.high_white, Pelt.mostly_white, 'FULLWHITE'] or self.colour == 'WHITE': + if ( + self.white_patches in [Pelt.high_white, Pelt.mostly_white, "FULLWHITE"] + or self.colour == "WHITE" + ): num = num - 90 - if self.white_patches == 'FULLWHITE' or self.colour == 'WHITE': + if self.white_patches == "FULLWHITE" or self.colour == "WHITE": num -= 10 for _par in parents: if _par.pelt.eye_colour2: @@ -389,7 +897,9 @@ def pattern_color_inheritance(self, parents: tuple = (), gender="female"): return self.randomize_pattern_color(gender) # There is a 1/10 chance for kits to have the exact same pelt as one of their parents - if not random.randint(0, game.config["cat_generation"]["direct_inheritance"]): # 1/10 chance + if not random.randint( + 0, game.config["cat_generation"]["direct_inheritance"] + ): # 1/10 chance selected = choice(par_pelts) self.name = selected.name self.length = selected.length @@ -402,7 +912,12 @@ def pattern_color_inheritance(self, parents: tuple = (), gender="female"): # ------------------------------------------------------------------------------------------------------------# # Determine pelt. - weights = [0, 0, 0, 0] # Weights for each pelt group. It goes: (tabbies, spotted, plain, exotic) + weights = [ + 0, + 0, + 0, + 0, + ] # Weights for each pelt group. It goes: (tabbies, spotted, plain, exotic) for p_ in par_peltnames: if p_ in Pelt.tabbies: add_weight = (50, 10, 5, 7) @@ -412,7 +927,9 @@ def pattern_color_inheritance(self, parents: tuple = (), gender="female"): add_weight = (5, 5, 50, 0) elif p_ in Pelt.exotic: add_weight = (15, 15, 1, 45) - elif p_ is None: # If there is at least one unknown parent, a None will be added to the set. + elif ( + p_ is None + ): # If there is at least one unknown parent, a None will be added to the set. add_weight = (35, 20, 30, 15) else: add_weight = (0, 0, 0, 0) @@ -431,7 +948,8 @@ def pattern_color_inheritance(self, parents: tuple = (), gender="female"): # Tortie chance tortie_chance_f = game.config["cat_generation"][ - "base_female_tortie"] # There is a default chance for female tortie + "base_female_tortie" + ] # There is a default chance for female tortie tortie_chance_m = game.config["cat_generation"]["base_male_tortie"] for p_ in par_pelts: if p_.name in Pelt.torties: @@ -539,7 +1057,9 @@ def pattern_color_inheritance(self, parents: tuple = (), gender="female"): self.name = chosen_pelt self.colour = chosen_pelt_color self.length = chosen_pelt_length - self.tortiebase = chosen_tortie_base # This will be none if the cat isn't a tortie. + self.tortiebase = ( + chosen_tortie_base # This will be none if the cat isn't a tortie. + ) return chosen_white def randomize_pattern_color(self, gender): @@ -574,9 +1094,7 @@ def randomize_pattern_color(self, gender): # PELT COLOUR # ------------------------------------------------------------------------------------------------------------# - chosen_pelt_color = choice( - random.choices(Pelt.colour_categories, k=1)[0] - ) + chosen_pelt_color = choice(random.choices(Pelt.colour_categories, k=1)[0]) # ------------------------------------------------------------------------------------------------------------# # PELT LENGTH @@ -603,15 +1121,17 @@ def randomize_pattern_color(self, gender): self.name = chosen_pelt self.colour = chosen_pelt_color self.length = chosen_pelt_length - self.tortiebase = chosen_tortie_base # This will be none if the cat isn't a tortie. + self.tortiebase = ( + chosen_tortie_base # This will be none if the cat isn't a tortie. + ) return chosen_white def init_pattern_color(self, parents, gender) -> bool: - """Inits self.name, self.colour, self.length, - self.tortiebase and determines if the cat - will have white patche or not. - Return TRUE is the cat should have white patches, - false is not. """ + """Inits self.name, self.colour, self.length, + self.tortiebase and determines if the cat + will have white patche or not. + Return TRUE is the cat should have white patches, + false is not.""" if parents: # If the cat has parents, use inheritance to decide pelt. @@ -623,45 +1143,42 @@ def init_pattern_color(self, parents, gender) -> bool: def init_sprite(self): self.cat_sprites = { - 'newborn': 20, - 'kitten': random.randint(0, 2), - 'adolescent': random.randint(3, 5), - 'senior': random.randint(12, 14), - 'sick_young': 19, - 'sick_adult': 18 + "newborn": 20, + "kitten": random.randint(0, 2), + "adolescent": random.randint(3, 5), + "senior": random.randint(12, 14), + "sick_young": 19, + "sick_adult": 18, } self.reverse = choice([True, False]) # skin chances self.skin = choice(Pelt.skin_sprites) - if self.length != 'long': - self.cat_sprites['adult'] = random.randint(6, 8) - self.cat_sprites['para_adult'] = 16 + if self.length != "long": + self.cat_sprites["adult"] = random.randint(6, 8) + self.cat_sprites["para_adult"] = 16 else: - self.cat_sprites['adult'] = random.randint(9, 11) - self.cat_sprites['para_adult'] = 15 - self.cat_sprites['young adult'] = self.cat_sprites['adult'] - self.cat_sprites['senior adult'] = self.cat_sprites['adult'] + self.cat_sprites["adult"] = random.randint(9, 11) + self.cat_sprites["para_adult"] = 15 + self.cat_sprites["young adult"] = self.cat_sprites["adult"] + self.cat_sprites["senior adult"] = self.cat_sprites["adult"] def init_scars(self, age): if age == "newborn": return - if age in ['kitten', 'adolescent']: + if age in ["kitten", "adolescent"]: scar_choice = random.randint(0, 50) # 2% - elif age in ['young adult', 'adult']: + elif age in ["young adult", "adult"]: scar_choice = random.randint(0, 20) # 5% else: scar_choice = random.randint(0, 15) # 6.67% if scar_choice == 1: - self.scars.append(choice([ - choice(Pelt.scars1), - choice(Pelt.scars3) - ])) + self.scars.append(choice([choice(Pelt.scars1), choice(Pelt.scars3)])) - if 'NOTAIL' in self.scars and 'HALFTAIL' in self.scars: - self.scars.remove('HALFTAIL') + if "NOTAIL" in self.scars and "HALFTAIL" in self.scars: + self.scars.remove("HALFTAIL") def init_accessories(self, age): if age == "newborn": @@ -669,16 +1186,15 @@ def init_accessories(self, age): return acc_display_choice = random.randint(0, 80) - if age in ['kitten', 'adolescent']: + if age in ["kitten", "adolescent"]: acc_display_choice = random.randint(0, 180) - elif age in ['young adult', 'adult']: + elif age in ["young adult", "adult"]: acc_display_choice = random.randint(0, 100) if acc_display_choice == 1: - self.accessory = choice([ - choice(Pelt.plant_accessories), - choice(Pelt.wild_accessories) - ]) + self.accessory = choice( + [choice(Pelt.plant_accessories), choice(Pelt.wild_accessories)] + ) else: self.accessory = None @@ -709,10 +1225,21 @@ def init_pattern(self): else: # Normal generation if self.tortiebase in ["singlestripe", "smoke", "single"]: - self.tortiepattern = choice(['tabby', 'mackerel', 'classic', 'single', 'smoke', 'agouti', - 'ticked']) + self.tortiepattern = choice( + [ + "tabby", + "mackerel", + "classic", + "single", + "smoke", + "agouti", + "ticked", + ] + ) else: - self.tortiepattern = random.choices([self.tortiebase, 'single'], weights=[97, 3], k=1)[0] + self.tortiepattern = random.choices( + [self.tortiebase, "single"], weights=[97, 3], k=1 + )[0] if self.colour == "WHITE": possible_colors = Pelt.white_colours.copy() @@ -720,14 +1247,22 @@ def init_pattern(self): self.colour = choice(possible_colors) # Ginger is often duplicated to increase its chances - if (self.colour in Pelt.black_colours) or (self.colour in Pelt.white_colours): - self.tortiecolour = choice((Pelt.ginger_colours * 2) + Pelt.brown_colours) + if (self.colour in Pelt.black_colours) or ( + self.colour in Pelt.white_colours + ): + self.tortiecolour = choice( + (Pelt.ginger_colours * 2) + Pelt.brown_colours + ) elif self.colour in Pelt.ginger_colours: - self.tortiecolour = choice(Pelt.brown_colours + Pelt.black_colours * 2) + self.tortiecolour = choice( + Pelt.brown_colours + Pelt.black_colours * 2 + ) elif self.colour in Pelt.brown_colours: possible_colors = Pelt.brown_colours.copy() possible_colors.remove(self.colour) - possible_colors.extend(Pelt.black_colours + (Pelt.ginger_colours * 2)) + possible_colors.extend( + Pelt.black_colours + (Pelt.ginger_colours * 2) + ) self.tortiecolour = choice(possible_colors) else: self.tortiecolour = "GOLDEN" @@ -741,7 +1276,6 @@ def init_pattern(self): self.pattern = None def white_patches_inheritance(self, parents: tuple): - par_whitepatches = set() par_points = [] for p in parents: @@ -757,7 +1291,9 @@ def white_patches_inheritance(self, parents: tuple): return # Direct inheritance. Will only work if at least one parent has white patches, otherwise continue on. - if par_whitepatches and not random.randint(0, game.config["cat_generation"]["direct_inheritance"]): + if par_whitepatches and not random.randint( + 0, game.config["cat_generation"]["direct_inheritance"] + ): # This ensures Torties and Calicos won't get direct inheritance of incorrect white patch types _temp = par_whitepatches.copy() if self.name == "Tortie": @@ -792,7 +1328,13 @@ def white_patches_inheritance(self, parents: tuple): else: self.points = None - white_list = [Pelt.little_white, Pelt.mid_white, Pelt.high_white, Pelt.mostly_white, ['FULLWHITE']] + white_list = [ + Pelt.little_white, + Pelt.mid_white, + Pelt.high_white, + Pelt.mostly_white, + ["FULLWHITE"], + ] weights = [0, 0, 0, 0, 0] # Same order as white_list for p_ in par_whitepatches: @@ -814,7 +1356,9 @@ def white_patches_inheritance(self, parents: tuple): # If all the weights are still 0, that means none of the parents have white patches. if not any(weights): - if not all(parents): # If any of the parents are None (unknown), use the following distribution: + if not all( + parents + ): # If any of the parents are None (unknown), use the following distribution: weights = [20, 10, 10, 5, 0] else: # Otherwise, all parents are known and don't have any white patches. Focus distribution on little_white. @@ -839,13 +1383,18 @@ def white_patches_inheritance(self, parents: tuple): ) self.white_patches = chosen_white_patches - if self.points and self.white_patches in [Pelt.high_white, Pelt.mostly_white, 'FULLWHITE']: + if self.points and self.white_patches in [ + Pelt.high_white, + Pelt.mostly_white, + "FULLWHITE", + ]: self.points = None def randomize_white_patches(self): - # Points determination. Tortie can't be pointed - if self.name != "Tortie" and not random.getrandbits(game.config["cat_generation"]["random_point_chance"]): + if self.name != "Tortie" and not random.getrandbits( + game.config["cat_generation"]["random_point_chance"] + ): # Cat has colorpoint! self.points = choice(Pelt.point_markings) else: @@ -859,17 +1408,27 @@ def randomize_white_patches(self): else: weights = (10, 10, 10, 10, 1) - white_list = [Pelt.little_white, Pelt.mid_white, Pelt.high_white, Pelt.mostly_white, ['FULLWHITE']] + white_list = [ + Pelt.little_white, + Pelt.mid_white, + Pelt.high_white, + Pelt.mostly_white, + ["FULLWHITE"], + ] chosen_white_patches = choice( random.choices(white_list, weights=weights, k=1)[0] ) self.white_patches = chosen_white_patches - if self.points and self.white_patches in [Pelt.high_white, Pelt.mostly_white, 'FULLWHITE']: + if self.points and self.white_patches in [ + Pelt.high_white, + Pelt.mostly_white, + "FULLWHITE", + ]: self.points = None def init_white_patches(self, pelt_white, parents: tuple): - # Vit can roll for anyone, not just cats who rolled to have white in their pelt. + # Vit can roll for anyone, not just cats who rolled to have white in their pelt. par_vit = [] for p in parents: if p: @@ -881,7 +1440,7 @@ def init_white_patches(self, pelt_white, parents: tuple): self.vitiligo = choice(Pelt.vit) # If the cat was rolled previously to have white patches, then determine the patch they will have - # these functions also handle points. + # these functions also handle points. if pelt_white: if parents: self.white_patches_inheritance(parents) @@ -913,7 +1472,9 @@ def init_tint(self): # Now for white patches base_tints = sprites.white_patches_tints["possible_tints"]["basic"] if self.colour in sprites.cat_tints["colour_groups"]: - color_group = sprites.white_patches_tints["colour_groups"].get(self.colour, "white") + color_group = sprites.white_patches_tints["colour_groups"].get( + self.colour, "white" + ) color_tints = sprites.white_patches_tints["possible_tints"][color_group] else: color_tints = [] @@ -958,7 +1519,7 @@ def describe_appearance(cat, short=False): "golden-brown": "brown", "darkbrown": "brown", "chocolate": "brown", - "ghost": "black" + "ghost": "black", } else: renamed_colors = { @@ -974,7 +1535,7 @@ def describe_appearance(cat, short=False): "golden-brown": "golden brown", "darkbrown": "dark brown", "chocolate": "dark brown", - "ghost": "black" + "ghost": "black", } pattern_des = { @@ -990,7 +1551,7 @@ def describe_appearance(cat, short=False): "Singlestripe": "dorsal-striped c_n", "Rosette": "unusually spotted c_n", "Sokoke": "c_n tabby", - "Masked": "masked c_n tabby" + "Masked": "masked c_n tabby", } # Start with determining the base color name @@ -999,7 +1560,10 @@ def describe_appearance(cat, short=False): color_name = renamed_colors[color_name] # Replace "white" with "pale" if the cat is white - if cat.pelt.name not in ["SingleColour", "TwoColour", "Tortie", "Calico"] and color_name == "white": + if ( + cat.pelt.name not in ["SingleColour", "TwoColour", "Tortie", "Calico"] + and color_name == "white" + ): color_name = "pale" # Time to describe the pattern and any additional colors @@ -1010,25 +1574,37 @@ def describe_appearance(cat, short=False): if short: # If using short, don't describe the colors of calicos and torties. # Just call them calico, tortie, or mottled - if cat.pelt.colour in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours and \ - cat.pelt.tortiecolour in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours: + if ( + cat.pelt.colour + in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours + and cat.pelt.tortiecolour + in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours + ): color_name = "mottled" else: color_name = cat.pelt.name.lower() else: base = cat.pelt.tortiebase.lower() - if base in [tabby.lower() for tabby in Pelt.tabbies] + ['bengal', 'rosette', 'speckled']: - base = ' tabby' # the extra space is intentional + if base in [tabby.lower() for tabby in Pelt.tabbies] + [ + "bengal", + "rosette", + "speckled", + ]: + base = " tabby" # the extra space is intentional else: - base = '' + base = "" patches_color = cat.pelt.tortiecolour.lower() if patches_color in renamed_colors: patches_color = renamed_colors[patches_color] color_name = f"{color_name}/{patches_color}" - if cat.pelt.colour in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours and \ - cat.pelt.tortiecolour in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours: + if ( + cat.pelt.colour + in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours + and cat.pelt.tortiecolour + in Pelt.black_colours + Pelt.brown_colours + Pelt.white_colours + ): color_name = f"{color_name} mottled{base}" else: color_name = f"{color_name} {cat.pelt.name.lower()}{base}" @@ -1037,7 +1613,10 @@ def describe_appearance(cat, short=False): if cat.pelt.white_patches == "FULLWHITE": # If the cat is fullwhite, discard all other information. They are just white color_name = "white" - if cat.pelt.white_patches in Pelt.mostly_white and cat.pelt.name != "Calico": + if ( + cat.pelt.white_patches in Pelt.mostly_white + and cat.pelt.name != "Calico" + ): color_name = f"white and {color_name}" elif cat.pelt.name != "Calico": color_name = f"{color_name} and white" @@ -1061,21 +1640,23 @@ def describe_appearance(cat, short=False): # Here is the place where we can add some additional details about the cat, for the full non-short one # These include notable missing limbs, vitiligo, long-furred-ness, and 3 or more scars if not short: - scar_details = { "NOTAIL": "no tail", "HALFTAIL": "half a tail", "NOPAW": "three legs", "NOLEFTEAR": "a missing ear", "NORIGHTEAR": "a missing ear", - "NOEAR": "no ears" + "NOEAR": "no ears", } additional_details = [] if cat.pelt.vitiligo: additional_details.append("vitiligo") for scar in cat.pelt.scars: - if scar in scar_details and scar_details[scar] not in additional_details: + if ( + scar in scar_details + and scar_details[scar] not in additional_details + ): additional_details.append(scar_details[scar]) if len(additional_details) > 1: diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index c18f0f97bb..ecce799dee 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -78,45 +78,44 @@ def mask(self): def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): if not isinstance(val, pygame.Mask | pygame.Surface | None): return - if val is not None: - if isinstance(val, pygame.Mask): - self._mask = val - self.mask_padding = (val.get_size()[0] - self.rect[0]) / 2 - else: - val = pygame.mask.from_surface(val, threshold=250) - inflated_mask = pygame.Mask( - ( - val.get_size()[0] + self.mask_padding * 2, - val.get_size()[1] + self.mask_padding * 2, - ) - ) - inflated_mask.draw(val, (self.mask_padding, self.mask_padding)) - for _ in range(self.mask_padding): - outline = inflated_mask.outline() - for point in outline: - for dx in range(-1, 2): - for dy in range(-1, 2): - try: - inflated_mask.set_at( - (point[0] + dx, point[1] + dy), 1 - ) - except IndexError: - continue - self._mask = inflated_mask - self.mask_info[0] = ( - self.rect[0] - self.mask_padding, - self.rect[1] - self.mask_padding, - ) - self.mask_info[1] = [ + if val is None: + self._mask = None + return + if isinstance(val, pygame.Mask): + self._mask = val + self.mask_padding = (val.get_size()[0] - self.rect[2]) / 2 + else: + val = pygame.mask.from_surface(val, threshold=250) + + inflated_mask = pygame.Mask( ( - x + self.mask_info[0][0], - y + self.mask_info[0][1], + self.relative_rect[2] + self.mask_padding * 2, + self.relative_rect[3] + self.mask_padding * 2, ) - for x, y in self.mask.outline() - ] - else: - self._mask = None + ) + inflated_mask.draw(val, (self.mask_padding, self.mask_padding)) + for _ in range(self.mask_padding): + outline = inflated_mask.outline() + for point in outline: + for dx in range(-1, 2): + for dy in range(-1, 2): + try: + inflated_mask.set_at((point[0] + dx, point[1] + dy), 1) + except IndexError: + continue + self._mask = inflated_mask + self.mask_info[0] = ( + self.rect[0] - self.mask_padding, + self.rect[1] - self.mask_padding, + ) + self.mask_info[1] = [ + ( + x + self.mask_info[0][0], + y + self.mask_info[0][1], + ) + for x, y in self.mask.outline() + ] def _set_any_images_from_theme(self): changed = False @@ -491,6 +490,7 @@ def __init__( object_id=None, tool_tip_text=None, anchors=None, + mask=None, mask_padding=None, ): # We have to scale the image before putting it into the image object. Otherwise, the method of upscaling that @@ -519,7 +519,7 @@ def __init__( tool_tip_text=tool_tip_text, container=container, anchors=anchors, - mask=self.image.image, + mask=mask, mask_padding=mask_padding, ) @@ -1149,6 +1149,7 @@ def create_cat_button(self, i, kitty, container): kitty.sprite, cat_object=kitty, cat_id=kitty.ID, + mask=None, container=container, object_id=f"#sprite{str(i)}", tool_tip_text=str(kitty.name) if self.tool_tip_name else None, diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index 081d6559e6..5315a76f6d 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -124,42 +124,38 @@ def screen_switches(self): i = 0 all_positions = list(self.taken_spaces.values()) used_positions = all_positions.copy() - for x in game.clan.clan_cats: - if ( - not Cat.all_cats[x].dead - and Cat.all_cats[x].in_camp - and not (Cat.all_cats[x].exiled or Cat.all_cats[x].outside) - and ( - Cat.all_cats[x].status != "newborn" - or game.config["fun"]["all_cats_are_newborn"] - or game.config["fun"]["newborns_can_roam"] - ) - ): - i += 1 - if i > self.max_sprites_displayed: - break - - # try: - layer = 2 - place = self.taken_spaces[Cat.all_cats[x].ID] - layer += all_positions.count(place) - used_positions.count(place) - used_positions.remove(place) - - self.cat_buttons.append( - UISpriteButton( - scale( - pygame.Rect(tuple(Cat.all_cats[x].placement), (100, 100)) - ), - Cat.all_cats[x].sprite, - cat_id=x, - starting_height=layer, - tool_tip_text=str(Cat.all_cats[x].name), - ) + cat_list = [ + Cat.all_cats[x] + for i, x in enumerate(game.clan.clan_cats) + if i < self.max_sprites_displayed + and not Cat.all_cats[x].dead + and Cat.all_cats[x].in_camp + and not (Cat.all_cats[x].exiled or Cat.all_cats[x].outside) + and ( + Cat.all_cats[x].status != "newborn" + or game.config["fun"]["all_cats_are_newborn"] + or game.config["fun"]["newborns_can_roam"] + ) + ] + layers = [] + for x in cat_list: + layers.append(2) + place = self.taken_spaces[x.ID] + layers[-1] += all_positions.count(place) - used_positions.count(place) + used_positions.remove(place) + + for i, x in enumerate(cat_list): + self.cat_buttons.append( + UISpriteButton( + scale(pygame.Rect(tuple(x.placement), (100, 100))), + x.sprite, + mask=x.sprite_mask, + cat_id=x.ID, + cat_object=x, + starting_height=layers[i], + tool_tip_text=str(x.name), ) - # except: - # print( - # f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" - # ) + ) # Den Labels # Redo the locations, so that it uses layout on the Clan page diff --git a/scripts/utility.py b/scripts/utility.py index 1163f59b92..9b7ec3a864 100644 --- a/scripts/utility.py +++ b/scripts/utility.py @@ -45,16 +45,16 @@ def get_alive_clan_queens(living_cats): cat.fetch_cat(i) for i in parents if cat.fetch_cat(i) - and not (cat.fetch_cat(i).dead or cat.fetch_cat(i).outside) + and not (cat.fetch_cat(i).dead or cat.fetch_cat(i).outside) ] if not parents: continue if ( - len(parents) == 1 - or len(parents) > 2 - or all(i.gender == "male" for i in parents) - or parents[0].gender == "female" + len(parents) == 1 + or len(parents) > 2 + or all(i.gender == "male" for i in parents) + or parents[0].gender == "female" ): if parents[0].ID in queen_dict: queen_dict[parents[0].ID].append(cat) @@ -73,7 +73,7 @@ def get_alive_clan_queens(living_cats): def get_alive_status_cats( - Cat, get_status: list, working: bool = False, sort: bool = False + Cat, get_status: list, working: bool = False, sort: bool = False ) -> list: """ returns a list of cat objects for all living cats of get_status in Clan @@ -145,8 +145,8 @@ def get_cats_same_age(Cat, cat, age_range=10): continue if ( - inter_cat.moons <= cat.moons + age_range - and inter_cat.moons <= cat.moons - age_range + inter_cat.moons <= cat.moons + age_range + and inter_cat.moons <= cat.moons - age_range ): cats.append(inter_cat) @@ -174,7 +174,7 @@ def get_free_possible_mates(cat): def get_random_moon_cat( - Cat, main_cat, parent_child_modifier=True, mentor_app_modifier=True + Cat, main_cat, parent_child_modifier=True, mentor_app_modifier=True ): """ returns a random cat for use in moon events @@ -191,9 +191,9 @@ def get_random_moon_cat( possible_r_c = list( filter( lambda c: not c.dead - and not c.exiled - and not c.outside - and (c.ID != main_cat.ID), + and not c.exiled + and not c.outside + and (c.ID != main_cat.ID), Cat.all_cats.values(), ) ) @@ -216,10 +216,10 @@ def get_random_moon_cat( random_cat = Cat.fetch_cat(choice(possible_parents)) if mentor_app_modifier: if ( - main_cat.status - in ["apprentice", "mediator apprentice", "medicine cat apprentice"] - and main_cat.mentor - and not int(random() * 3) + main_cat.status + in ["apprentice", "mediator apprentice", "medicine cat apprentice"] + and main_cat.mentor + and not int(random() * 3) ): random_cat = Cat.fetch_cat(main_cat.mentor) elif main_cat.apprentice and not int(random() * 3): @@ -298,7 +298,7 @@ def change_clan_relations(other_clan, difference): def create_new_cat_block( - Cat, Relationship, event, in_event_cats: dict, i: int, attribute_list: List[str] + Cat, Relationship, event, in_event_cats: dict, i: int, attribute_list: List[str] ) -> list: """ Creates a single new_cat block and then generates and returns the cats within the block @@ -375,7 +375,7 @@ def create_new_cat_block( elif "female" in attribute_list: gender = "female" elif ( - "can_birth" in attribute_list and not game.clan.clan_settings["same sex birth"] + "can_birth" in attribute_list and not game.clan.clan_settings["same sex birth"] ): gender = "female" else: @@ -599,7 +599,6 @@ def create_new_cat_block( # THIS DOES NOT ADD RELATIONS TO CATS IN THE EVENT, those are added within the relationships block of the event for n_c in new_cats: - # SET MATES for inter_cat in give_mates: if n_c == inter_cat or n_c.ID in inter_cat.mate: @@ -659,22 +658,22 @@ def get_other_clan(clan_name): def create_new_cat( - Cat, - new_name: bool = False, - loner: bool = False, - kittypet: bool = False, - kit: bool = False, - litter: bool = False, - other_clan: bool = None, - backstory: bool = None, - status: str = None, - age: int = None, - gender: str = None, - thought: str = "Is looking around the camp with wonder", - alive: bool = True, - outside: bool = False, - parent1: str = None, - parent2: str = None, + Cat, + new_name: bool = False, + loner: bool = False, + kittypet: bool = False, + kit: bool = False, + litter: bool = False, + other_clan: bool = None, + backstory: bool = None, + status: str = None, + age: int = None, + gender: str = None, + thought: str = "Is looking around the camp with wonder", + alive: bool = True, + outside: bool = False, + parent1: str = None, + parent2: str = None, ) -> list: """ This function creates new cats and then returns a list of those cats @@ -702,8 +701,8 @@ def create_new_cat( backstory = choice(backstory) if backstory in ( - BACKSTORIES["backstory_categories"]["former_clancat_backstories"] - or BACKSTORIES["backstory_categories"]["otherclan_categories"] + BACKSTORIES["backstory_categories"]["former_clancat_backstories"] + or BACKSTORIES["backstory_categories"]["otherclan_categories"] ): other_clan = True @@ -768,7 +767,7 @@ def create_new_cat( if choice([1, 2]) == 1: accessory = choice(Pelt.collars) elif ( - loner and choice([1, 2]) == 1 + loner and choice([1, 2]) == 1 ): # try to give name from full loner name list name = choice(names.names_dict["loner_names"]) else: @@ -877,8 +876,8 @@ def create_new_cat( new_cat.get_permanent_condition(chosen_condition, born_with) if ( - new_cat.permanent_condition[chosen_condition]["moons_until"] - == 0 + new_cat.permanent_condition[chosen_condition]["moons_until"] + == 0 ): new_cat.permanent_condition[chosen_condition][ "moons_until" @@ -919,7 +918,7 @@ def create_new_cat( def get_highest_romantic_relation( - relationships, exclude_mate=False, potential_mate=False + relationships, exclude_mate=False, potential_mate=False ): """Returns the relationship with the highest romantic value.""" max_love_value = 0 @@ -930,7 +929,7 @@ def get_highest_romantic_relation( if exclude_mate and rel.cat_from.ID in rel.cat_to.mate: continue if potential_mate and not rel.cat_to.is_potential_mate( - rel.cat_from, for_love_interest=True + rel.cat_from, for_love_interest=True ): continue if rel.romantic_love > max_love_value: @@ -1026,8 +1025,8 @@ def get_cats_of_romantic_interest(cat): # Extra check to ensure they are potential mates if ( - inter_cat.is_potential_mate(cat, for_love_interest=True) - and cat.relationships[inter_cat.ID].romantic_love > 0 + inter_cat.is_potential_mate(cat, for_love_interest=True) + and cat.relationships[inter_cat.ID].romantic_love > 0 ): cats.append(inter_cat) return cats @@ -1082,7 +1081,7 @@ def get_amount_of_cats_with_relation_value_towards(cat, value, all_cats): def filter_relationship_type( - group: list, filter_types: List[str], event_id: str = None, patrol_leader=None + group: list, filter_types: List[str], event_id: str = None, patrol_leader=None ): """ filters for specific types of relationships between groups of cat objects, returns bool @@ -1259,7 +1258,7 @@ def filter_relationship_type( relevant_relationships = list( filter( lambda rel: rel.cat_to.ID in group_ids - and rel.cat_to.ID != inter_cat.ID, + and rel.cat_to.ID != inter_cat.ID, list(inter_cat.relationships.values()), ) ) @@ -1313,7 +1312,7 @@ def filter_relationship_type( def gather_cat_objects( - Cat, abbr_list: List[str], event, stat_cat=None, extra_cat=None + Cat, abbr_list: List[str], event, stat_cat=None, extra_cat=None ) -> list: """ gathers cat objects from list of abbreviations used within an event format block @@ -1371,7 +1370,7 @@ def gather_cat_objects( def unpack_rel_block( - Cat, relationship_effects: List[dict], event=None, stat_cat=None, extra_cat=None + Cat, relationship_effects: List[dict], event=None, stat_cat=None, extra_cat=None ): """ Unpacks the info from the relationship effect block used in patrol and moon events, then adjusts rel values @@ -1524,17 +1523,17 @@ def unpack_rel_block( def change_relationship_values( - cats_to: list, - cats_from: list, - romantic_love: int = 0, - platonic_like: int = 0, - dislike: int = 0, - admiration: int = 0, - comfortable: int = 0, - jealousy: int = 0, - trust: int = 0, - auto_romance: bool = False, - log: str = None, + cats_to: list, + cats_from: list, + romantic_love: int = 0, + platonic_like: int = 0, + dislike: int = 0, + admiration: int = 0, + comfortable: int = 0, + jealousy: int = 0, + trust: int = 0, + auto_romance: bool = False, + log: str = None, ): """ changes relationship values according to the parameters. @@ -1566,7 +1565,6 @@ def change_relationship_values( # pick out the correct cats for single_cat_from in cats_from: for single_cat_to in cats_to: - # make sure we aren't trying to change a cat's relationship with themself if single_cat_from == single_cat_to: continue @@ -1579,8 +1577,8 @@ def change_relationship_values( # here we just double-check that the cats are allowed to be romantic with each other if ( - single_cat_from.is_potential_mate(single_cat_to, for_love_interest=True) - or single_cat_to.ID in single_cat_from.mate + single_cat_from.is_potential_mate(single_cat_to, for_love_interest=True) + or single_cat_to.ID in single_cat_from.mate ): # if cat already has romantic feelings then automatically increase romantic feelings # when platonic feelings would increase @@ -1612,15 +1610,15 @@ def change_relationship_values( if log and isinstance(log, str): if single_cat_to.moons <= 1: log_text = ( - log - + f"- {single_cat_to.name} was {single_cat_to.moons} moon old" + log + + f"- {single_cat_to.name} was {single_cat_to.moons} moon old" ) if log_text not in rel.log: rel.log.append(log_text) else: log_text = ( - log - + f"- {single_cat_to.name} was {single_cat_to.moons} moons old" + log + + f"- {single_cat_to.name} was {single_cat_to.moons} moons old" ) if log_text not in rel.log: rel.log.append(log_text) @@ -1753,7 +1751,7 @@ def adjust_prey_abbr(patrol_text): def get_special_snippet_list( - chosen_list, amount, sense_groups=None, return_string=True + chosen_list, amount, sense_groups=None, return_string=True ): """ function to grab items from various lists in snippet_collections.json @@ -1777,14 +1775,13 @@ def get_special_snippet_list( # these lists don't get sense specific snippets, so is handled first if chosen_list in ["dream_list", "story_list"]: - if ( - chosen_list == "story_list" + chosen_list == "story_list" ): # story list has some biome specific things to collect snippets = SNIPPETS[chosen_list]["general"] snippets.extend(SNIPPETS[chosen_list][biome]) elif ( - chosen_list == "clair_list" + chosen_list == "clair_list" ): # the clair list also pulls from the dream list snippets = SNIPPETS[chosen_list] snippets.extend(SNIPPETS["dream_list"]) @@ -1888,7 +1885,7 @@ def history_text_adjust(text, other_clan_name, clan, other_cat_rc=None): text = " ".join(modify) break - text = text.replace('o_c_n', str(other_clan_name)) + text = text.replace("o_c_n", str(other_clan_name)) if "c_n" in text: text = text.replace("c_n", clan.name) @@ -1908,7 +1905,7 @@ def selective_replace(text, pattern, replacement): if start_brace != -1 and end_brace != -1 and start_brace < index < end_brace: i = index + len(pattern) else: - text = text[:index] + replacement + text[index + len(pattern):] + text = text[:index] + replacement + text[index + len(pattern) :] i = index + len(replacement) return text @@ -1952,20 +1949,20 @@ def ongoing_event_text_adjust(Cat, text, clan=None, other_clan_name=None): def event_text_adjust( - Cat, - text, - patrol_leader=None, - main_cat=None, - random_cat=None, - stat_cat=None, - victim_cat=None, - patrol_cats: list = None, - patrol_apprentices: list = None, - new_cats: list = None, - multi_cats: list = None, - clan=None, - other_clan=None, - chosen_herb: str = None, + Cat, + text, + patrol_leader=None, + main_cat=None, + random_cat=None, + stat_cat=None, + victim_cat=None, + patrol_cats: list = None, + patrol_apprentices: list = None, + new_cats: list = None, + multi_cats: list = None, + clan=None, + other_clan=None, + chosen_herb: str = None, ): """ handles finding abbreviations in the text and replacing them appropriately, returns the adjusted text @@ -2162,12 +2159,12 @@ def event_text_adjust( def leader_ceremony_text_adjust( - Cat, - text, - leader, - life_giver=None, - virtue=None, - extra_lives=None, + Cat, + text, + leader, + life_giver=None, + virtue=None, + extra_lives=None, ): """ used to adjust the text for leader ceremonies @@ -2198,16 +2195,16 @@ def leader_ceremony_text_adjust( def ceremony_text_adjust( - Cat, - text, - cat, - old_name=None, - dead_mentor=None, - mentor=None, - previous_alive_mentor=None, - random_honor=None, - living_parents=(), - dead_parents=(), + Cat, + text, + cat, + old_name=None, + dead_mentor=None, + mentor=None, + previous_alive_mentor=None, + random_honor=None, + living_parents=(), + dead_parents=(), ): clanname = str(game.clan.name + "Clan") @@ -2271,9 +2268,9 @@ def ceremony_text_adjust( ) if ( - "dead_par1" in adjust_text - and "dead_par2" in adjust_text - and len(dead_parents) >= 2 + "dead_par1" in adjust_text + and "dead_par2" in adjust_text + and len(dead_parents) >= 2 ): cat_dict["dead_par1"] = ( str(dead_parents[0].name), @@ -2298,10 +2295,11 @@ def ceremony_text_adjust( return adjust_text, random_living_parent, random_dead_parent + def get_pronouns(Cat): - """ Get a cat's pronoun even if the cat has faded to prevent crashes (use gender-neutral pronouns when the cat has faded) """ + """Get a cat's pronoun even if the cat has faded to prevent crashes (use gender-neutral pronouns when the cat has faded)""" if Cat.pronouns == []: - return{ + return { "subject": "they", "object": "them", "poss": "their", @@ -2314,7 +2312,7 @@ def get_pronouns(Cat): def shorten_text_to_fit( - name, length_limit, font_size=None, font_type="resources/fonts/NotoSans-Medium.ttf" + name, length_limit, font_size=None, font_type="resources/fonts/NotoSans-Medium.ttf" ): length_limit = ( length_limit // 2 if not game.settings["fullscreen"] else length_limit @@ -2385,6 +2383,28 @@ def update_sprite(cat): cat.all_cats[cat.ID] = cat +def update_mask(cat): + val = pygame.mask.from_surface(cat.sprite, threshold=250) + + inflated_mask = pygame.Mask( + ( + val.get_size()[0] + 10, + val.get_size()[1] + 10, + ) + ) + inflated_mask.draw(val, (5, 5)) + for _ in range(5): + outline = inflated_mask.outline() + for point in outline: + for dx in range(-1, 2): + for dy in range(-1, 2): + try: + inflated_mask.set_at((point[0] + dx, point[1] + dy), 1) + except IndexError: + continue + cat.sprite_mask = inflated_mask + + def clan_symbol_sprite(clan, return_string=False, force_light=False): """ returns the clan symbol for the given clan_name, if no symbol exists then random symbol is chosen @@ -2438,12 +2458,12 @@ def clan_symbol_sprite(clan, return_string=False, force_light=False): def generate_sprite( - cat, - life_state=None, - scars_hidden=False, - acc_hidden=False, - always_living=False, - no_not_working=False, + cat, + life_state=None, + scars_hidden=False, + acc_hidden=False, + always_living=False, + no_not_working=False, ) -> pygame.Surface: """ Generates the sprite for a cat, with optional arguments that will override certain things. @@ -2468,10 +2488,10 @@ def generate_sprite( # setting the cat_sprite (bc this makes things much easier) if ( - not no_not_working - and cat.not_working() - and age != "newborn" - and game.config["cat_sprites"]["sick_sprites"] + not no_not_working + and cat.not_working() + and age != "newborn" + and game.config["cat_sprites"]["sick_sprites"] ): if age in ["kitten", "adolescent"]: cat_sprite = str(19) @@ -2534,8 +2554,8 @@ def generate_sprite( # TINTS if ( - cat.pelt.tint != "none" - and cat.pelt.tint in sprites.cat_tints["tint_colours"] + cat.pelt.tint != "none" + and cat.pelt.tint in sprites.cat_tints["tint_colours"] ): # Multiply with alpha does not work as you would expect - it just lowers the alpha of the # entire surface. To get around this, we first blit the tint onto a white background to dull it, @@ -2544,8 +2564,8 @@ def generate_sprite( tint.fill(tuple(sprites.cat_tints["tint_colours"][cat.pelt.tint])) new_sprite.blit(tint, (0, 0), special_flags=pygame.BLEND_RGB_MULT) if ( - cat.pelt.tint != "none" - and cat.pelt.tint in sprites.cat_tints["dilute_tint_colours"] + cat.pelt.tint != "none" + and cat.pelt.tint in sprites.cat_tints["dilute_tint_colours"] ): tint = pygame.Surface((sprites.size, sprites.size)).convert_alpha() tint.fill(tuple(sprites.cat_tints["dilute_tint_colours"][cat.pelt.tint])) @@ -2559,9 +2579,9 @@ def generate_sprite( # Apply tint to white patches. if ( - cat.pelt.white_patches_tint != "none" - and cat.pelt.white_patches_tint - in sprites.white_patches_tints["tint_colours"] + cat.pelt.white_patches_tint != "none" + and cat.pelt.white_patches_tint + in sprites.white_patches_tints["tint_colours"] ): tint = pygame.Surface((sprites.size, sprites.size)).convert_alpha() tint.fill( @@ -2580,9 +2600,9 @@ def generate_sprite( if cat.pelt.points: points = sprites.sprites["white" + cat.pelt.points + cat_sprite].copy() if ( - cat.pelt.white_patches_tint != "none" - and cat.pelt.white_patches_tint - in sprites.white_patches_tints["tint_colours"] + cat.pelt.white_patches_tint != "none" + and cat.pelt.white_patches_tint + in sprites.white_patches_tints["tint_colours"] ): tint = pygame.Surface((sprites.size, sprites.size)).convert_alpha() tint.fill( @@ -2666,12 +2686,11 @@ def generate_sprite( # Apply fading fog if ( - cat.pelt.opacity <= 97 - and not cat.prevent_fading - and game.clan.clan_settings["fading"] - and dead + cat.pelt.opacity <= 97 + and not cat.prevent_fading + and game.clan.clan_settings["fading"] + and dead ): - stage = "0" if 80 >= cat.pelt.opacity > 45: # Stage 1 @@ -2725,7 +2744,7 @@ def apply_opacity(surface, opacity): def chunks(L, n): - return [L[x: x + n] for x in range(0, len(L), n)] + return [L[x : x + n] for x in range(0, len(L), n)] def is_iterable(y): From d097bf7a61056892e3c3b79b1cc327bc48af2e8d Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:43:54 +0000 Subject: [PATCH 05/13] hover sprites improved --- resources/theme/master_screen_scale.json | 1 + scripts/game_structure/ui_elements.py | 10 ++++++---- scripts/screens/ClanScreen.py | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/theme/master_screen_scale.json b/resources/theme/master_screen_scale.json index 9a584e0996..f81117186b 100644 --- a/resources/theme/master_screen_scale.json +++ b/resources/theme/master_screen_scale.json @@ -91,6 +91,7 @@ }, "@checked_checkbox_smalltooltip.tool_tip": {"misc": {"rect_width": "100"}}, "@unchecked_checkbox_smalltooltip.tool_tip": {"misc": {"rect_width": "100"}}, + "#clan_screen_cat_tooltip": {"misc": {"rect_width": "120"}}, "@checked_checkbox.tool_tip": {"misc": {"rect_width": "200"}}, "@unchecked_checkbox.tool_tip": {"misc": {"rect_width": "200"}}, "#fav_star.tool_tip": {"misc": {"rect_width": "125"}}, diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index 071e56f4eb..f84223ea9c 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -4,9 +4,6 @@ from typing import ( Tuple, Optional, - Dict, - Iterable, - Callable, List, Union, Dict, @@ -25,7 +22,8 @@ from pygame_gui.elements import UIAutoResizingContainer from scripts.game_structure import image_cache -from scripts.game_structure.game_essentials import game, screen +from scripts.game_structure.game_essentials import game +from scripts.game_structure.screen_settings import screen from scripts.utility import ( ui_scale, shorten_text_to_fit, @@ -727,6 +725,7 @@ def __init__( manager: IUIManagerInterface = None, container=None, object_id=None, + tool_tip_object_id=None, tool_tip_text=None, anchors=None, mask=None, @@ -768,6 +767,7 @@ def __init__( starting_height=starting_height, manager=manager, tool_tip_text=tool_tip_text, + tool_tip_object_id=tool_tip_object_id, container=container, anchors=anchors, mask=mask, @@ -838,6 +838,7 @@ def __init__( mask=None, mask_padding=None, auto_disable_if_no_data=False, + tool_tip_object_id=None, ): self.cat_id = cat_id self.cat_object = cat_object @@ -856,6 +857,7 @@ def __init__( allow_double_clicks=True, mask=mask, mask_padding=mask_padding, + tool_tip_object_id=tool_tip_object_id, ) if auto_disable_if_no_data and cat_id is None and cat_object is None: self.disable() diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index 46d30f0a92..7aeb92ec45 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -161,6 +161,7 @@ def screen_switches(self): cat_object=x, starting_height=layers[i], tool_tip_text=str(x.name), + tool_tip_object_id="#clan_screen_cat_tooltip", ) ) From ccfb08582e0526a8412602dfab939b23ce8c17fc Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:00:38 +0000 Subject: [PATCH 06/13] Centering the tooltip names --- resources/theme/master_screen_scale.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/theme/master_screen_scale.json b/resources/theme/master_screen_scale.json index f81117186b..55fcc1e976 100644 --- a/resources/theme/master_screen_scale.json +++ b/resources/theme/master_screen_scale.json @@ -91,7 +91,8 @@ }, "@checked_checkbox_smalltooltip.tool_tip": {"misc": {"rect_width": "100"}}, "@unchecked_checkbox_smalltooltip.tool_tip": {"misc": {"rect_width": "100"}}, - "#clan_screen_cat_tooltip": {"misc": {"rect_width": "120"}}, + "#clan_screen_cat_tooltip": {"misc": {"rect_width": "120", "text_horiz_alignment": "center"}}, + "#clan_screen_cat_tooltip.text_box": {"misc": {"text_horiz_alignment": "center"}}, "@checked_checkbox.tool_tip": {"misc": {"rect_width": "200"}}, "@unchecked_checkbox.tool_tip": {"misc": {"rect_width": "200"}}, "#fav_star.tool_tip": {"misc": {"rect_width": "125"}}, From b93980061ca5c76466a4c1f782441b29c524154f Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:13:01 +0000 Subject: [PATCH 07/13] somehow, "patrol" became "l". a real L. --- scripts/utility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utility.py b/scripts/utility.py index 60d01afb44..8ee3b2411a 100644 --- a/scripts/utility.py +++ b/scripts/utility.py @@ -2019,7 +2019,7 @@ def event_text_adjust( Cat, text, *, - l_leader=None, + patrol_leader=None, main_cat=None, random_cat=None, stat_cat=None, From c9928a572e9a8930a36fa2218918dfa0a202811f Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:38:24 +0000 Subject: [PATCH 08/13] pylint, do you love me now? --- scripts/game_structure/ui_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index f84223ea9c..0d73297eb4 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -316,7 +316,7 @@ def mask(self): @mask.setter def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): - if not isinstance(val, pygame.Mask | pygame.Surface | None): + if not isinstance(val, Union[pygame.Mask, pygame.Surface, None]): return if val is None: From 127f1dde6dd9fae52a6ef4e7d35189138b9e3de8 Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:44:29 +0000 Subject: [PATCH 09/13] Revert backstories.json --- resources/dicts/backstories.json | 139 ++++++++++++++++--------------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/resources/dicts/backstories.json b/resources/dicts/backstories.json index f716d64d0e..915eb4f82c 100644 --- a/resources/dicts/backstories.json +++ b/resources/dicts/backstories.json @@ -1,61 +1,77 @@ { "backstories": { - "clan_founder": "m_c is one of the founding members of the Clan.", - "clanborn": "m_c was born into the Clan where {PRONOUN/m_c/subject} currently {VERB/m_c/reside/resides}.", - "halfclan1": "m_c was born into the Clan, but one of {PRONOUN/m_c/poss} parents resides in another Clan.", - "halfclan2": "m_c was born in another Clan, but chose to come to this Clan to be with {PRONOUN/m_c/poss} other parent.", - "outsider_roots1": "m_c was born into the Clan, but one of {PRONOUN/m_c/poss} parents is an outsider that belongs to no Clan.", - "outsider_roots2": "m_c was born outside the Clan, but came to live in the Clan with {PRONOUN/m_c/poss} parent at a young age.", - "loner1": "m_c joined the Clan by choice after living life as a loner.", - "loner2": "m_c used to live in a barn, but mostly stayed away from Twolegs. {PRONOUN/m_c/subject/CAP} decided Clan life might be an interesting change of pace.", + "clan_founder": "m_c is one of the founding members of the Clan.", + "clanborn": "m_c was born into the Clan where {PRONOUN/m_c/subject} currently {VERB/m_c/reside/resides}.", + + "halfclan1": "m_c was born into the Clan, but one of {PRONOUN/m_c/poss} parents resides in another Clan.", + "halfclan2": "m_c was born in another Clan, but chose to come to this Clan to be with {PRONOUN/m_c/poss} other parent.", + + "outsider_roots1": "m_c was born into the Clan, but one of {PRONOUN/m_c/poss} parents is an outsider that belongs to no Clan.", + "outsider_roots2": "m_c was born outside the Clan, but came to live in the Clan with {PRONOUN/m_c/poss} parent at a young age.", + + "loner1": "m_c joined the Clan by choice after living life as a loner.", + "loner2": "m_c used to live in a barn, but mostly stayed away from Twolegs. {PRONOUN/m_c/subject/CAP} decided Clan life might be an interesting change of pace.", "loner3": "m_c wandered the world, aimless and free, but always alone.", "loner4": "m_c wandered the world, aimless and free, but always alone - until {PRONOUN/m_c/subject} found someone worth staying for.", - "kittypet1": "m_c joined the Clan by choice after living life with Twolegs as a kittypet.", - "kittypet2": "m_c used to live on something called a 'boat' with Twolegs, but decided to join the Clan.", - "kittypet3": "m_c used to be a kittypet. {PRONOUN/m_c/subject/CAP} got lost after wandering away, and when {PRONOUN/m_c/subject} returned home, {PRONOUN/m_c/subject} found {PRONOUN/m_c/poss} Twolegs were gone. {PRONOUN/m_c/subject/CAP} eventually found {PRONOUN/m_c/poss} way to the Clan.", - "kittypet4": "m_c used to be a kittypet. One day, {PRONOUN/m_c/subject} got sick, and {PRONOUN/m_c/poss} Twolegs brought {PRONOUN/m_c/object} into the belly of a monster. The Twolegs then left {PRONOUN/m_c/object} to fend for {PRONOUN/m_c/self}.", - "rogue1": "m_c joined the Clan by choice after living life as a rogue.", - "rogue2": "m_c used to live in a Twolegplace, scrounging for what {PRONOUN/m_c/subject} could find. {PRONOUN/m_c/subject/CAP} thought the Clan might offer {PRONOUN/m_c/object} more security.", - "rogue3": "m_c used to live alone in {PRONOUN/m_c/poss} own territory, but was chased out by something and eventually found the Clan.", - "abandoned1": "m_c was found by the Clan as a kit and has been living with them ever since.", - "abandoned2": "m_c was born outside of the Clan, but was brought to the Clan as a kit and has lived here ever since.", - "abandoned3": "m_c was born into another Clan, but {PRONOUN/m_c/subject} {VERB/m_c/were/was} left here as a kit for the Clan to raise.", - "abandoned4": "m_c was found and taken in after being abandoned by {PRONOUN/m_c/poss} Twolegs as a kit.", - "otherclan1": "m_c was born into another Clan, but came to this Clan by choice.", - "otherclan2": "m_c was unhappy in {PRONOUN/m_c/poss} old Clan and decided to come here instead.", - "otherclan3": "m_c's Clan stayed with the Clan after a disaster struck {PRONOUN/m_c/poss} old one, and decided to stay after the rest of {PRONOUN/m_c/poss} Clan returned home.", - "otherclan4": "m_c grew up in another Clan, but chose to leave that life and join the Clan {PRONOUN/m_c/subject} now {VERB/m_c/live/lives} in.", - "disgraced1": "m_c was cast out of {PRONOUN/m_c/poss} old Clan for some transgression that {PRONOUN/m_c/subject}{VERB/m_c/'re/'s} not keen on talking about.", - "disgraced2": "m_c was exiled from {PRONOUN/m_c/poss} old Clan for something {PRONOUN/m_c/subject} didn't do and came here to seek safety.", - "disgraced3": "m_c once held a high rank in another Clan but was exiled for reasons {PRONOUN/m_c/subject} {VERB/m_c/refuse/refuses} to share.", - "retired_leader": "m_c used to be the leader of another Clan before deciding {PRONOUN/m_c/subject} needed a change of scenery after leadership became too much. {PRONOUN/m_c/subject/CAP} returned {PRONOUN/m_c/poss} nine lives and let {PRONOUN/m_c/poss} deputy take over before coming here.", - "medicine_cat": "m_c was once a medicine cat in another Clan.", - "ostracized_warrior": "m_c was ostracized from {PRONOUN/m_c/poss} old Clan, but no one really knows why.", - "refugee1": "m_c came to this Clan after fleeing from {PRONOUN/m_c/poss} former Clan and the tyrannical leader that had taken over.", - "refugee2": "m_c used to live as a loner, but after another cat chased {PRONOUN/m_c/object} from {PRONOUN/m_c/poss} home, {PRONOUN/m_c/subject} took refuge in the Clan.", - "refugee3": "m_c used to be a kittypet, but joined the Clan after fleeing from {PRONOUN/m_c/poss} cruel Twoleg.", - "refugee4": "m_c used to be in a rogue group, but joined the Clan after fleeing from the group's tyrannical leader.", - "refugee5": "m_c got washed away from {PRONOUN/m_c/poss} former territory in a flood that destroyed {PRONOUN/m_c/poss} home but was glad to find a new home in {PRONOUN/m_c/poss} new Clan here.", - "refugee6": "m_c used to be a kittypet, but joined the Clan, sick and weak after eating something {PRONOUN/m_c/subject} shouldn't have.", - "tragedy_survivor1": "Something horrible happened to m_c's previous Clan. {PRONOUN/m_c/subject/CAP} {VERB/m_c/refuse/refuses} to speak about it.", - "tragedy_survivor2": "m_c used to be part of a rogue group, but joined the Clan after something terrible happened to it.", - "tragedy_survivor3": "m_c used to be a kittypet, but joined the Clan after something terrible happened to {PRONOUN/m_c/poss} Twolegs.", - "tragedy_survivor4": "m_c used to be a loner, but joined the Clan after something terrible made {PRONOUN/m_c/object} leave {PRONOUN/m_c/poss} old home behind.", - "wandering_healer1": "m_c used to wander, helping those where {PRONOUN/m_c/subject} could, and eventually found the Clan.", - "wandering_healer2": "m_c used to live in a specific spot, offering help to all who wandered by, but eventually found {PRONOUN/m_c/poss} way to the Clan.", - "guided1": "m_c used to be a kittypet, but after dreaming of starry-furred cats, {PRONOUN/m_c/subject} followed their whispers to the Clan.", - "guided2": "m_c used to live a rough life as a rogue. While wandering, {PRONOUN/m_c/subject} found a set of starry pawprints, and followed them to the Clan.", - "guided3": "m_c used to live as a loner. A starry-furred cat appeared to {PRONOUN/m_c/object} one day, and then led {PRONOUN/m_c/object} to the Clan.", - "guided4": "m_c used to live in a different Clan, until a sign from StarClan told {PRONOUN/m_c/object} to leave.", - "orphaned1": "m_c was found with a deceased parent. The Clan took {PRONOUN/m_c/object} in, but doesn't hide where {PRONOUN/m_c/subject} came from.", - "orphaned2": "m_c was found with a deceased parent. The Clan took {PRONOUN/m_c/object} in, but doesn't tell {PRONOUN/m_c/object} where {PRONOUN/m_c/subject} really came from.", - "orphaned3": "m_c was found as a kit among the wreckage of a Monster with no parents in sight and got brought to live in the Clan.", - "orphaned4": "m_c was found as a kit hiding near a place of battle where there were no survivors and got brought to live in the Clan.", - "orphaned5": "m_c was found as a kit hiding near {PRONOUN/m_c/poss} parents' bodies and got brought to live in the Clan.", - "orphaned6": "m_c was found flailing in the ocean as a teeny kitten, no parent in sight.", - "outsider1": "m_c was born outside of a Clan.", - "outsider2": "m_c was born outside of a Clan, but at {PRONOUN/m_c/poss} birth one parent was a member of a Clan.", - "outsider3": "m_c was born outside of a Clan, while {PRONOUN/m_c/poss} parent was lost." + + "kittypet1": "m_c joined the Clan by choice after living life with Twolegs as a kittypet.", + "kittypet2": "m_c used to live on something called a 'boat' with Twolegs, but decided to join the Clan.", + "kittypet3": "m_c used to be a kittypet. {PRONOUN/m_c/subject/CAP} got lost after wandering away, and when {PRONOUN/m_c/subject} returned home, {PRONOUN/m_c/subject} found {PRONOUN/m_c/poss} Twolegs were gone. {PRONOUN/m_c/subject/CAP} eventually found {PRONOUN/m_c/poss} way to the Clan.", + "kittypet4": "m_c used to be a kittypet. One day, {PRONOUN/m_c/subject} got sick, and {PRONOUN/m_c/poss} Twolegs brought {PRONOUN/m_c/object} into the belly of a monster. The Twolegs then left {PRONOUN/m_c/object} to fend for {PRONOUN/m_c/self}.", + + "rogue1": "m_c joined the Clan by choice after living life as a rogue.", + "rogue2": "m_c used to live in a Twolegplace, scrounging for what {PRONOUN/m_c/subject} could find. {PRONOUN/m_c/subject/CAP} thought the Clan might offer {PRONOUN/m_c/object} more security.", + "rogue3": "m_c used to live alone in {PRONOUN/m_c/poss} own territory, but was chased out by something and eventually found the Clan.", + + "abandoned1": "m_c was found by the Clan as a kit and has been living with them ever since.", + "abandoned2": "m_c was born outside of the Clan, but was brought to the Clan as a kit and has lived here ever since.", + "abandoned3": "m_c was born into another Clan, but {PRONOUN/m_c/subject} {VERB/m_c/were/was} left here as a kit for the Clan to raise.", + "abandoned4": "m_c was found and taken in after being abandoned by {PRONOUN/m_c/poss} Twolegs as a kit.", + + "otherclan1": "m_c was born into another Clan, but came to this Clan by choice.", + "otherclan2": "m_c was unhappy in {PRONOUN/m_c/poss} old Clan and decided to come here instead.", + "otherclan3": "m_c's Clan stayed with the Clan after a disaster struck {PRONOUN/m_c/poss} old one, and decided to stay after the rest of {PRONOUN/m_c/poss} Clan returned home.", + "otherclan4": "m_c grew up in another Clan, but chose to leave that life and join the Clan {PRONOUN/m_c/subject} now {VERB/m_c/live/lives} in.", + + + "disgraced1": "m_c was cast out of {PRONOUN/m_c/poss} old Clan for some transgression that {PRONOUN/m_c/subject}{VERB/m_c/'re/'s} not keen on talking about.", + "disgraced2": "m_c was exiled from {PRONOUN/m_c/poss} old Clan for something {PRONOUN/m_c/subject} didn't do and came here to seek safety.", + "disgraced3": "m_c once held a high rank in another Clan but was exiled for reasons {PRONOUN/m_c/subject} {VERB/m_c/refuse/refuses} to share.", + + "retired_leader": "m_c used to be the leader of another Clan before deciding {PRONOUN/m_c/subject} needed a change of scenery after leadership became too much. {PRONOUN/m_c/subject/CAP} returned {PRONOUN/m_c/poss} nine lives and let {PRONOUN/m_c/poss} deputy take over before coming here.", + "medicine_cat": "m_c was once a medicine cat in another Clan.", + "ostracized_warrior": "m_c was ostracized from {PRONOUN/m_c/poss} old Clan, but no one really knows why.", + + "refugee1": "m_c came to this Clan after fleeing from {PRONOUN/m_c/poss} former Clan and the tyrannical leader that had taken over.", + "refugee2": "m_c used to live as a loner, but after another cat chased {PRONOUN/m_c/object} from {PRONOUN/m_c/poss} home, {PRONOUN/m_c/subject} took refuge in the Clan.", + "refugee3": "m_c used to be a kittypet, but joined the Clan after fleeing from {PRONOUN/m_c/poss} cruel Twoleg.", + "refugee4": "m_c used to be in a rogue group, but joined the Clan after fleeing from the group's tyrannical leader.", + "refugee5": "m_c got washed away from {PRONOUN/m_c/poss} former territory in a flood that destroyed {PRONOUN/m_c/poss} home but was glad to find a new home in {PRONOUN/m_c/poss} new Clan here.", + "refugee6": "m_c used to be a kittypet, but joined the Clan, sick and weak after eating something {PRONOUN/m_c/subject} shouldn't have.", + + "tragedy_survivor1": "Something horrible happened to m_c's previous Clan. {PRONOUN/m_c/subject/CAP} {VERB/m_c/refuse/refuses} to speak about it.", + "tragedy_survivor2": "m_c used to be part of a rogue group, but joined the Clan after something terrible happened to it.", + "tragedy_survivor3": "m_c used to be a kittypet, but joined the Clan after something terrible happened to {PRONOUN/m_c/poss} Twolegs.", + "tragedy_survivor4": "m_c used to be a loner, but joined the Clan after something terrible made {PRONOUN/m_c/object} leave {PRONOUN/m_c/poss} old home behind.", + + "wandering_healer1": "m_c used to wander, helping those where {PRONOUN/m_c/subject} could, and eventually found the Clan.", + "wandering_healer2": "m_c used to live in a specific spot, offering help to all who wandered by, but eventually found {PRONOUN/m_c/poss} way to the Clan.", + + "guided1": "m_c used to be a kittypet, but after dreaming of starry-furred cats, {PRONOUN/m_c/subject} followed their whispers to the Clan.", + "guided2": "m_c used to live a rough life as a rogue. While wandering, {PRONOUN/m_c/subject} found a set of starry pawprints, and followed them to the Clan.", + "guided3": "m_c used to live as a loner. A starry-furred cat appeared to {PRONOUN/m_c/object} one day, and then led {PRONOUN/m_c/object} to the Clan.", + "guided4": "m_c used to live in a different Clan, until a sign from StarClan told {PRONOUN/m_c/object} to leave.", + + "orphaned1": "m_c was found with a deceased parent. The Clan took {PRONOUN/m_c/object} in, but doesn't hide where {PRONOUN/m_c/subject} came from.", + "orphaned2": "m_c was found with a deceased parent. The Clan took {PRONOUN/m_c/object} in, but doesn't tell {PRONOUN/m_c/object} where {PRONOUN/m_c/subject} really came from.", + "orphaned3": "m_c was found as a kit among the wreckage of a Monster with no parents in sight and got brought to live in the Clan.", + "orphaned4": "m_c was found as a kit hiding near a place of battle where there were no survivors and got brought to live in the Clan.", + "orphaned5": "m_c was found as a kit hiding near {PRONOUN/m_c/poss} parents' bodies and got brought to live in the Clan.", + "orphaned6": "m_c was found flailing in the ocean as a teeny kitten, no parent in sight.", + + "outsider1": "m_c was born outside of a Clan.", + "outsider2": "m_c was born outside of a Clan, but at {PRONOUN/m_c/poss} birth one parent was a member of a Clan.", + "outsider3": "m_c was born outside of a Clan, while {PRONOUN/m_c/poss} parent was lost." }, "backstory_display": { @@ -95,20 +111,7 @@ "kittypet1", "kittypet2", "kittypet3", "kittypet4", "refugee3", "tragedy_survivor3", "guided1", "refugee6" ], "former_clancat_backstories": [ - "otherclan1", - "otherclan2", - "otherclan3", - "otherclan4", - "ostracized_warrior", - "disgraced1", - "disgraced2", - "disgraced3", - "retired_leader", - "refugee1", - "tragedy_survivor1", - "medicine_cat", - "guided4", - "refugee5" + "otherclan1", "otherclan2", "otherclan3", "otherclan4", "ostracized_warrior", "disgraced1", "disgraced2", "disgraced3", "retired_leader", "refugee1", "tragedy_survivor1", "medicine_cat", "guided4", "refugee5" ], "healer_backstories": [ "medicine_cat", "wandering_healer1", "wandering_healer2" From ee06a49ead5dbe36ff8ef7e0b91dc0063f10d363 Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Fri, 6 Dec 2024 08:46:22 +0000 Subject: [PATCH 10/13] fixed the strobing glitch hopefully permanently --- scripts/cat/cats.py | 7 +++ scripts/cat/pelts.py | 10 +++- scripts/game_structure/ui_elements.py | 48 ++++++++++--------- scripts/screens/ClanScreen.py | 67 +++++++++++---------------- scripts/utility.py | 6 ++- 5 files changed, 72 insertions(+), 66 deletions(-) diff --git a/scripts/cat/cats.py b/scripts/cat/cats.py index 6f9c6161d6..22666aed63 100644 --- a/scripts/cat/cats.py +++ b/scripts/cat/cats.py @@ -43,6 +43,7 @@ leader_ceremony_text_adjust, update_mask, ) +import scripts.game_structure.screen_settings class Cat: @@ -3369,6 +3370,12 @@ def sprite(self, new_sprite): @property def sprite_mask(self): + if ( + scripts.game_structure.screen_settings.screen_scale + != self.pelt.screen_scale + ): + self.pelt.screen_scale = scripts.game_structure.screen_settings.screen_scale + update_mask(self) return self._sprite_mask @sprite_mask.setter diff --git a/scripts/cat/pelts.py b/scripts/cat/pelts.py index c2f4077e69..f83548dc6a 100644 --- a/scripts/cat/pelts.py +++ b/scripts/cat/pelts.py @@ -2,6 +2,7 @@ from random import choice from re import sub +import scripts.game_structure.screen_settings from scripts.cat.sprites import sprites from scripts.game_structure.game_essentials import game @@ -678,6 +679,7 @@ def __init__( self.scars = scars if isinstance(scars, list) else [] self.tint = tint self.white_patches_tint = white_patches_tint + self.screen_scale = scripts.game_structure.screen_settings.screen_scale self.cat_sprites = { "kitten": kitten_sprite if kitten_sprite is not None else 0, "adolescent": adol_sprite if adol_sprite is not None else 0, @@ -846,8 +848,12 @@ def init_eyes(self, parents): colour_wheel = [Pelt.yellow_eyes, Pelt.blue_eyes, Pelt.green_eyes] for colour in colour_wheel[:]: if self.eye_colour in colour: - colour_wheel.remove(colour) # removes the selected list from the options - self.eye_colour2 = choice(choice(colour_wheel)) # choose from the remaining two lists + colour_wheel.remove( + colour + ) # removes the selected list from the options + self.eye_colour2 = choice( + choice(colour_wheel) + ) # choose from the remaining two lists break def pattern_color_inheritance(self, parents: tuple = (), gender="female"): diff --git a/scripts/game_structure/ui_elements.py b/scripts/game_structure/ui_elements.py index 0d73297eb4..34e1f75605 100644 --- a/scripts/game_structure/ui_elements.py +++ b/scripts/game_structure/ui_elements.py @@ -285,6 +285,7 @@ def __init__( self.sound_id = sound_id self.mask_padding = mask_padding if mask_padding is not None else 2 self.mask_info = [relative_rect[0:2], []] + super().__init__( relative_rect=relative_rect, text=text, @@ -326,6 +327,8 @@ def mask(self, val: Union[pygame.Mask, pygame.Surface, None]): self._mask = val self.mask_padding = (val.get_size()[0] - self.rect[2]) / 2 else: + # if you're looking for the cat's sprite mask, that's + # set in utility.py:update_mask val = pygame.mask.from_surface(val, threshold=250) inflated_mask = pygame.Mask( @@ -449,11 +452,6 @@ def check_hover(self, time_delta: float, hovered_higher_element: bool) -> bool: pygame.draw.lines(screen, (255, 0, 0), True, self.mask_info[1], width=2) return hover - def on_hovered(self): - super().on_hovered() - if self.mask is not None and self.tool_tip is not None: - self.tool_tip.disable() - class UIModifiedScrollingContainer(pygame_gui.elements.UIScrollingContainer): def __init__( @@ -731,6 +729,24 @@ def __init__( mask=None, mask_padding=None, ): + # The transparent button. This a subclass that UIButton that also hold the cat_id. + self.button = CatButton( + relative_rect, + "", + object_id="#cat_button", + visible=visible, + cat_id=cat_id, + cat_object=cat_object, + starting_height=starting_height + 1, + manager=manager, + tool_tip_text=tool_tip_text, + tool_tip_object_id=tool_tip_object_id, + container=container, + anchors=anchors, + mask=mask, + mask_padding=mask_padding, + ) + input_sprite = sprite.premul_alpha() # if it's going to be small on the screen, smoothscale out the crunch input_sprite = ( @@ -752,28 +768,11 @@ def __init__( container=container, object_id=object_id, anchors=anchors, + starting_height=starting_height, ) self.image.disable() del input_sprite - # The transparent button. This a subclass that UIButton that also hold the cat_id. - self.button = CatButton( - relative_rect, - "", - object_id="#cat_button", - visible=visible, - cat_id=cat_id, - cat_object=cat_object, - starting_height=starting_height, - manager=manager, - tool_tip_text=tool_tip_text, - tool_tip_object_id=tool_tip_object_id, - container=container, - anchors=anchors, - mask=mask, - mask_padding=mask_padding, - ) - def return_cat_id(self): return self.button.return_cat_id() @@ -816,6 +815,9 @@ def __eq__(self, __o: object) -> bool: def get_abs_rect(self): return self.button.get_abs_rect() + def on_hovered(self): + self.button.on_hovered() + class CatButton(UIImageButton): """Basic UIButton subclass for at sprite buttons. It stores the cat ID. diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index 8c463c1b67..0505c39fd1 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -154,47 +154,36 @@ def screen_switches(self): layers[-1] += all_positions.count(place) - used_positions.count(place) used_positions.remove(place) - try: - image = Cat.all_cats[x].sprite.convert_alpha() - blend_layer = ( - self.game_bgs[self.active_bg] - .subsurface( - ui_scale( - pygame.Rect(tuple(Cat.all_cats[x].placement), (50, 50)) - ) - ) - .convert_alpha() - ) - blend_layer = pygame.transform.box_blur( - blend_layer, self.layout["cat_shading"]["blur"] - ) + try: + image = x.sprite.convert_alpha() + blend_layer = ( + self.game_bgs[self.active_bg] + .subsurface(ui_scale(pygame.Rect(tuple(x.placement), (50, 50)))) + .convert_alpha() + ) + blend_layer = pygame.transform.box_blur( + blend_layer, self.layout["cat_shading"]["blur"] + ) - sprite = image.copy() - sprite.fill( - (255, 255, 255, 255), special_flags=pygame.BLEND_RGB_MAX - ) - sprite.blit( - blend_layer, (0, 0), special_flags=pygame.BLEND_RGBA_MULT - ) - image.set_alpha(self.layout["cat_shading"]["blend_strength"]) - sprite.blit(image, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2) - sprite.set_alpha(255) - - self.cat_buttons.append( - UISpriteButton( - ui_scale( - pygame.Rect(tuple(Cat.all_cats[x].placement), (50, 50)) - ), - sprite, - mask=x.sprite_mask, - cat_id=x, - starting_height=i, - ) - ) - except: - print( - f"ERROR: placing {Cat.all_cats[x].name}'s sprite on Clan page" + sprite = image.copy() + sprite.fill((255, 255, 255, 255), special_flags=pygame.BLEND_RGB_MAX) + sprite.blit(blend_layer, (0, 0), special_flags=pygame.BLEND_RGBA_MULT) + image.set_alpha(self.layout["cat_shading"]["blend_strength"]) + sprite.blit(image, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2) + sprite.set_alpha(255) + + self.cat_buttons.append( + UISpriteButton( + ui_scale(pygame.Rect(tuple(x.placement), (50, 50))), + sprite, + mask=x.sprite_mask, + cat_id=x.ID, + starting_height=layers[-1], + tool_tip_text=str(x.name), ) + ) + except: + print(f"ERROR: placing {x.name}'s sprite on Clan page") # Den Labels # Redo the locations, so that it uses layout on the Clan page diff --git a/scripts/utility.py b/scripts/utility.py index 406909ee6e..983a22d6a3 100644 --- a/scripts/utility.py +++ b/scripts/utility.py @@ -2522,7 +2522,9 @@ def update_sprite(cat): def update_mask(cat): - val = pygame.mask.from_surface(cat.sprite, threshold=250) + val = pygame.mask.from_surface( + pygame.transform.scale(cat.sprite, ui_scale_dimensions((50, 50))), threshold=250 + ) inflated_mask = pygame.Mask( ( @@ -2531,7 +2533,7 @@ def update_mask(cat): ) ) inflated_mask.draw(val, (5, 5)) - for _ in range(5): + for _ in range(3): outline = inflated_mask.outline() for point in outline: for dx in range(-1, 2): From a0af265c3bb17d7ccd37e40fda934dee71140200 Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Fri, 6 Dec 2024 08:54:06 +0000 Subject: [PATCH 11/13] ...fixed a doofy merge. --- resources/dicts/events/misc/general.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/resources/dicts/events/misc/general.json b/resources/dicts/events/misc/general.json index b48535389a..7a061fc13b 100644 --- a/resources/dicts/events/misc/general.json +++ b/resources/dicts/events/misc/general.json @@ -4339,15 +4339,16 @@ }, "new_gender": ["trans male", "nonbinary"] }, - { - "event_id": "gen_misc_transition_from_male1", - "location": ["any"], - "season": ["any"], - "sub_type": ["transition"], - "weight": 10, - "event_text": "m_c has realized that tom doesn't describe how {PRONOUN/m_c/subject} {VERB/m_c/feel/feels} anymore.", - "m_c": { - "gender": ["male"] + { + "event_id": "gen_misc_transition_from_male1", + "location": ["any"], + "season": ["any"], + "sub_type": ["transition"], + "weight": 10, + "event_text": "m_c has realized that tom doesn't describe how {PRONOUN/m_c/subject} {VERB/m_c/feel/feels} anymore.", + "m_c": { + "gender": ["male"] + } }, { "event_id": "gen_misc_transition_from_male1", From b8d44c3e71e357dd3acb451cdff2b565357dd50e Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Sat, 14 Dec 2024 01:50:27 +0000 Subject: [PATCH 12/13] removed unexpectedly doubled event --- resources/dicts/events/misc/general.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/resources/dicts/events/misc/general.json b/resources/dicts/events/misc/general.json index 7a061fc13b..5648c5f69b 100644 --- a/resources/dicts/events/misc/general.json +++ b/resources/dicts/events/misc/general.json @@ -4339,17 +4339,6 @@ }, "new_gender": ["trans male", "nonbinary"] }, - { - "event_id": "gen_misc_transition_from_male1", - "location": ["any"], - "season": ["any"], - "sub_type": ["transition"], - "weight": 10, - "event_text": "m_c has realized that tom doesn't describe how {PRONOUN/m_c/subject} {VERB/m_c/feel/feels} anymore.", - "m_c": { - "gender": ["male"] - } - }, { "event_id": "gen_misc_transition_from_male1", "location": ["any"], From 544fa34fc890b4f4f6f5420c8afae2d87e402d49 Mon Sep 17 00:00:00 2001 From: J Gynn <48025294+j-gynn@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:51:33 +0000 Subject: [PATCH 13/13] removed tool_tip_text from cats on clan screen :( --- scripts/screens/ClanScreen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/screens/ClanScreen.py b/scripts/screens/ClanScreen.py index 9908e67b72..99501b3840 100644 --- a/scripts/screens/ClanScreen.py +++ b/scripts/screens/ClanScreen.py @@ -180,7 +180,6 @@ def screen_switches(self): mask=x.sprite_mask, cat_id=x.ID, starting_height=layers[-1], - tool_tip_text=str(x.name), ) ) except: