diff --git a/actions.py b/actions.py index 8330c90..73d0289 100644 --- a/actions.py +++ b/actions.py @@ -61,10 +61,7 @@ class PickupAction(Action): class ItemAction(Action): def __init__( - self, - entity: Actor, - item: Item, - target_xy: Optional[Tuple[int, int]] = None + self, entity: Actor, item: Item, target_xy: Optional[Tuple[int, int]] = None ): super().__init__(entity) self.item = item @@ -101,8 +98,7 @@ class TakeStairsAction(Action): if (self.entity.x, self.entity.y) == self.engine.game_map.downstairs_location: self.engine.game_world.generate_floor() self.engine.message_log.add_message( - "You descent the staircase.", - color.descend + "You descent the staircase.", color.descend ) else: raise exceptions.Impossible("There are no stairs here.") diff --git a/components/ai.py b/components/ai.py index 6633d61..6ec67a1 100644 --- a/components/ai.py +++ b/components/ai.py @@ -13,7 +13,6 @@ if TYPE_CHECKING: class BaseAI(Action): - def get_path_to(self, dest_x: int, dest_y: int) -> List[Tuple[int, int]]: """Compute and return a path to the target position. @@ -52,10 +51,7 @@ class ConfusedEnemy(BaseAI): """ def __init__( - self, - entity: Actor, - previous_ai: Optional[BaseAI], - turns_remaining: int + self, entity: Actor, previous_ai: Optional[BaseAI], turns_remaining: int ): super().__init__(entity) @@ -65,7 +61,9 @@ class ConfusedEnemy(BaseAI): def perform(self) -> None: # Rever the AI back to the original state if the effect has run its course. if self.turns_remaining <= 0: - self.engine.message_log.add_message(f"The {self.entity.name} is no longer confused.") + self.engine.message_log.add_message( + f"The {self.entity.name} is no longer confused." + ) self.entity.ai = self.previous_ai else: # Pick a random direction diff --git a/components/consumable.py b/components/consumable.py index a7ab149..8eb090a 100644 --- a/components/consumable.py +++ b/components/consumable.py @@ -11,7 +11,7 @@ from exceptions import Impossible from input_handlers import ( ActionOrHandler, AreaRangedAttackHandler, - SingleRangedAttackHandler + SingleRangedAttackHandler, ) if TYPE_CHECKING: @@ -46,8 +46,7 @@ class ConfusionConsumable(Consumable): def get_action(self, consumer: Actor) -> SingleRangedAttackHandler: self.engine.message_log.add_message( - "Select a target location.", - color.needs_target + "Select a target location.", color.needs_target ) return SingleRangedAttackHandler( @@ -90,7 +89,7 @@ class HealingConsumable(Consumable): if amount_recovered > 0: self.engine.message_log.add_message( f"You consume the {self.parent.name}, and recover {amount_recovered} HP!", - color.health_recovered + color.health_recovered, ) self.consume() else: @@ -104,8 +103,7 @@ class FireballDamageConsumable(Consumable): def get_action(self, consumer: Actor) -> AreaRangedAttackHandler: self.engine.message_log.add_message( - "Select a target location.", - color.needs_target + "Select a target location.", color.needs_target ) return AreaRangedAttackHandler( diff --git a/components/equippable.py b/components/equippable.py index 382514a..ab9f9b5 100644 --- a/components/equippable.py +++ b/components/equippable.py @@ -13,10 +13,10 @@ class Equippable(BaseComponent): parent: Item def __init__( - self, - equipment_type: EquipmentType, - power_bonus: int = 0, - defense_bonus: int = 0, + self, + equipment_type: EquipmentType, + power_bonus: int = 0, + defense_bonus: int = 0, ): self.equipment_type = equipment_type @@ -41,4 +41,4 @@ class LeatherArmor(Equippable): class ChainMail(Equippable): def __init__(self) -> None: - super().__init__(EquipmentType.ARMOR, defense_bonus=3) \ No newline at end of file + super().__init__(EquipmentType.ARMOR, defense_bonus=3) diff --git a/components/level.py b/components/level.py index b871a2d..0513191 100644 --- a/components/level.py +++ b/components/level.py @@ -12,12 +12,12 @@ class Level(BaseComponent): parent: Actor def __init__( - self, - current_level: int = 1, - current_xp: int = 0, - level_up_base: int = 0, - level_up_factor: int = 150, - xp_given: int = 0 + self, + current_level: int = 1, + current_xp: int = 0, + level_up_base: int = 0, + level_up_factor: int = 150, + xp_given: int = 0, ): self.current_level = current_level self.current_xp = current_xp diff --git a/engine.py b/engine.py index b5a390a..385f4bc 100644 --- a/engine.py +++ b/engine.py @@ -57,12 +57,12 @@ class Engine: ) render_functions.render_dungeon_level( - console, - dungeon_level=self.game_world.current_floor, - location=(0, 47) + console, dungeon_level=self.game_world.current_floor, location=(0, 47) ) - render_functions.render_names_at_mouse_location(console, x=21, y=44, engine=self) + render_functions.render_names_at_mouse_location( + console, x=21, y=44, engine=self + ) def save_as(self, filename: str) -> None: """Save this Engine instance as a compressed file.""" diff --git a/entity.py b/entity.py index 8ca799b..d040010 100644 --- a/entity.py +++ b/entity.py @@ -26,15 +26,15 @@ class Entity: parent: Union[GameMap, Inventory] def __init__( - self, - parent: Optional[GameMap] = None, - x: int = 0, - y: int = 0, - char: str = "?", - color: Tuple[int, int, int] = (255, 255, 255), - name: str = "", - blocks_movement: bool = False, - render_order: RenderOrder = RenderOrder.CORPSE, + self, + parent: Optional[GameMap] = None, + x: int = 0, + y: int = 0, + char: str = "?", + color: Tuple[int, int, int] = (255, 255, 255), + name: str = "", + blocks_movement: bool = False, + render_order: RenderOrder = RenderOrder.CORPSE, ): self.x = x self.y = y @@ -87,17 +87,17 @@ class Entity: class Actor(Entity): def __init__( - self, - *, - x: int = 0, - y: int = 0, - char: str = "?", - color: Tuple[int, int, int] = (255, 255, 255), - name: str = "", - ai_cls: Type[BaseAI], - fighter: Fighter, - inventory: Inventory, - level: Level + self, + *, + x: int = 0, + y: int = 0, + char: str = "?", + color: Tuple[int, int, int] = (255, 255, 255), + name: str = "", + ai_cls: Type[BaseAI], + fighter: Fighter, + inventory: Inventory, + level: Level, ): super().__init__( x=x, @@ -128,15 +128,15 @@ class Actor(Entity): class Item(Entity): def __init__( - self, - *, - x: int = 0, - y: int = 0, - char: str = "?", - color: Tuple[int, int, int] = (255, 255, 255), - name: str = "", - consumable: Optional[Consumable] = None, - equippable: Optional[Equippable] = None, + self, + *, + x: int = 0, + y: int = 0, + char: str = "?", + color: Tuple[int, int, int] = (255, 255, 255), + name: str = "", + consumable: Optional[Consumable] = None, + equippable: Optional[Equippable] = None, ): super().__init__( x=x, diff --git a/exceptions.py b/exceptions.py index c73eeb6..b65b5c5 100644 --- a/exceptions.py +++ b/exceptions.py @@ -6,4 +6,4 @@ class Impossible(Exception): class QuitWithoutSaving(SystemExit): - """Can be raised to exit the game without automatically saving.""" \ No newline at end of file + """Can be raised to exit the game without automatically saving.""" diff --git a/game_map.py b/game_map.py index bd4876a..fd19432 100644 --- a/game_map.py +++ b/game_map.py @@ -15,11 +15,7 @@ if TYPE_CHECKING: class GameMap: def __init__( - self, - engine: Engine, - width: int, - height: int, - entities: Iterable[Entity] = () + self, engine: Engine, width: int, height: int, entities: Iterable[Entity] = () ): self.engine = engine self.width, self.height = width, height @@ -27,14 +23,10 @@ class GameMap: self.tiles = np.full((width, height), fill_value=tile_types.wall, order="F") self.visible = np.full( - (width, height), - fill_value=False, - order="F" + (width, height), fill_value=False, order="F" ) # Tiles the player can currently see self.explored = np.full( - (width, height), - fill_value=False, - order="F" + (width, height), fill_value=False, order="F" ) # Tiles the player has seen before self.downstairs_location = (0, 0) @@ -57,15 +49,13 @@ class GameMap: yield from (entity for entity in self.entities if isinstance(entity, Item)) def get_blocking_entity_at_location( - self, - location_x: int, - location_y: int + self, location_x: int, location_y: int ) -> Optional[Entity]: for entity in self.entities: if ( - entity.blocks_movement - and entity.x == location_x - and entity.y == location_y + entity.blocks_movement + and entity.x == location_x + and entity.y == location_y ): return entity @@ -92,7 +82,7 @@ class GameMap: :param console: :return: """ - console.tiles_rgb[0: self.width, 0: self.height] = np.select( + console.tiles_rgb[0 : self.width, 0 : self.height] = np.select( condlist=[self.visible, self.explored], choicelist=[self.tiles["light"], self.tiles["dark"]], default=tile_types.SHROUD, @@ -106,10 +96,7 @@ class GameMap: # Only print entities that are in the FOV if self.visible[entity.x, entity.y]: console.print( - x=entity.x, - y=entity.y, - string=entity.char, - fg=entity.color + x=entity.x, y=entity.y, string=entity.char, fg=entity.color ) @@ -119,15 +106,15 @@ class GameWorld: """ def __init__( - self, - *, - engine: Engine, - map_width: int, - map_height: int, - max_rooms: int, - room_min_size: int, - room_max_size: int, - current_floor: int = 0 + self, + *, + engine: Engine, + map_width: int, + map_height: int, + max_rooms: int, + room_min_size: int, + room_max_size: int, + current_floor: int = 0, ): self.engine = engine diff --git a/input_handlers.py b/input_handlers.py index 962211a..c9df6ec 100644 --- a/input_handlers.py +++ b/input_handlers.py @@ -7,12 +7,7 @@ from typing import overload, Callable, Optional, Tuple, TYPE_CHECKING, Union import tcod.event import actions -from actions import ( - Action, - BumpAction, - PickupAction, - WaitAction -) +from actions import Action, BumpAction, PickupAction, WaitAction import color import exceptions @@ -30,7 +25,6 @@ MOVE_KEYS = { tcod.event.K_END: (-1, 1), tcod.event.K_PAGEUP: (1, -1), tcod.event.K_PAGEDOWN: (1, 1), - # Numpad keys tcod.event.K_KP_1: (-1, 1), tcod.event.K_KP_2: (0, 1), @@ -40,7 +34,6 @@ MOVE_KEYS = { tcod.event.K_KP_7: (-1, -1), tcod.event.K_KP_8: (0, -1), tcod.event.K_KP_9: (1, -1), - # Vi keys tcod.event.K_h: (-1, 0), tcod.event.K_j: (0, 1), @@ -183,7 +176,9 @@ class AskUserEventHandler(EventHandler): return self.on_exit() - def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> Optional[ActionOrHandler]: + def ev_mousebuttondown( + self, event: tcod.event.MouseButtonDown + ) -> Optional[ActionOrHandler]: """By default any mouse click exits this input handler.""" return self.on_exit() @@ -221,20 +216,12 @@ class CharacterScreenEventHandler(AskUserEventHandler): bg=(0, 0, 0), ) - console.print( - x + 1, - y + 1, - f"Level: {self.engine.player.level.current_level}" - ) - console.print( - x + 1, - y + 2, - f"XP: {self.engine.player.level.current_xp}" - ) + console.print(x + 1, y + 1, f"Level: {self.engine.player.level.current_level}") + console.print(x + 1, y + 2, f"XP: {self.engine.player.level.current_xp}") console.print( x + 1, y + 3, - f"XP for next Level: {self.engine.player.level.experience_to_next_level}" + f"XP for next Level: {self.engine.player.level.experience_to_next_level}", ) console.print( x=x + 1, y=y + 4, string=f"Attack: {self.engine.player.fighter.power}" @@ -263,7 +250,7 @@ class LevelUpEventHandler(AskUserEventHandler): title=self.TITLE, clear=True, fg=(255, 255, 255), - bg=(0, 0, 0) + bg=(0, 0, 0), ) console.print(x + 1, 1, "Congratulations! You level up!") @@ -272,17 +259,17 @@ class LevelUpEventHandler(AskUserEventHandler): console.print( x=x + 1, y=4, - string=f"a) Constitution (+20 HP, from {self.engine.player.fighter.max_hp})" + string=f"a) Constitution (+20 HP, from {self.engine.player.fighter.max_hp})", ) console.print( x=x + 1, y=5, - string=f"b) Strength (+1 attack, from {self.engine.player.fighter.power})" + string=f"b) Strength (+1 attack, from {self.engine.player.fighter.power})", ) console.print( x=x + 1, y=6, - string=f"c) Agility (+1 defense, from {self.engine.player.fighter.defense})" + string=f"c) Agility (+1 defense, from {self.engine.player.fighter.defense})", ) def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[ActionOrHandler]: @@ -304,7 +291,9 @@ class LevelUpEventHandler(AskUserEventHandler): return super().ev_keydown(event) - def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> Optional[ActionOrHandler]: + def ev_mousebuttondown( + self, event: tcod.event.MouseButtonDown + ) -> Optional[ActionOrHandler]: """ Don't allow the player to click to exit the menu, like normal """ @@ -443,7 +432,9 @@ class SelectIndexHandler(AskUserEventHandler): return super().ev_keydown(event) - def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> Optional[ActionOrHandler]: + def ev_mousebuttondown( + self, event: tcod.event.MouseButtonDown + ) -> Optional[ActionOrHandler]: """Left click confirms a selection.""" if self.engine.game_map.in_bounds(*event.tile): if event.button == 1: @@ -468,9 +459,9 @@ class SingleRangedAttackHandler(SelectIndexHandler): """Handles targeting a single enemy. Only the enemy selected will be affected.""" def __init__( - self, - engine: Engine, - callback: Callable[[Tuple[int, int]], Optional[ActionOrHandler]] + self, + engine: Engine, + callback: Callable[[Tuple[int, int]], Optional[ActionOrHandler]], ): super().__init__(engine) @@ -487,10 +478,10 @@ class AreaRangedAttackHandler(SelectIndexHandler): """ def __init__( - self, - engine: Engine, - radius: int, - callback: Callable[[Tuple[int, int]], Optional[ActionOrHandler]] + self, + engine: Engine, + radius: int, + callback: Callable[[Tuple[int, int]], Optional[ActionOrHandler]], ): super().__init__(engine) @@ -507,8 +498,8 @@ class AreaRangedAttackHandler(SelectIndexHandler): console.draw_frame( x=x - self.radius - 1, y=y - self.radius - 1, - width=self.radius ** 2, - height=self.radius ** 2, + width=self.radius**2, + height=self.radius**2, fg=color.red, clear=False, ) @@ -527,7 +518,7 @@ class MainGameEventHandler(EventHandler): player = self.engine.player if key == tcod.event.K_PERIOD and modifier & ( - tcod.event.KMOD_LSHIFT | tcod.event.KMOD_RSHIFT + tcod.event.KMOD_LSHIFT | tcod.event.KMOD_RSHIFT ): return actions.TakeStairsAction(player) @@ -598,12 +589,7 @@ class HistoryViewer(EventHandler): # Draw a frame with a custom banner title. log_console.draw_frame(0, 0, log_console.width, log_console.height) log_console.print_box( - 0, - 0, - log_console.width, - 1, - "┤Message history├", - alignment=tcod.CENTER + 0, 0, log_console.width, 1, "┤Message history├", alignment=tcod.CENTER ) # Render the message log using the cursor parameter. diff --git a/main.py b/main.py index 7f61b0b..f4063ae 100755 --- a/main.py +++ b/main.py @@ -21,20 +21,17 @@ def main() -> None: screen_height = 50 tileset = tcod.tileset.load_tilesheet( - "dejavu10x10_gs_tc.png", - 32, - 8, - tcod.tileset.CHARMAP_TCOD + "dejavu10x10_gs_tc.png", 32, 8, tcod.tileset.CHARMAP_TCOD ) handler: input_handlers.BaseEventHandler = setup_game.MainMenu() with tcod.context.new_terminal( - screen_width, - screen_height, - tileset=tileset, - title="Yet Another Roguelike Tutorial", - vsync=True, + screen_width, + screen_height, + tileset=tileset, + title="Yet Another Roguelike Tutorial", + vsync=True, ) as context: root_console = tcod.Console(screen_width, screen_height, order="F") try: diff --git a/message_log.py b/message_log.py index 8d59b3a..4647002 100644 --- a/message_log.py +++ b/message_log.py @@ -26,11 +26,7 @@ class MessageLog: self.messages: List[Message] = [] def add_message( - self, - text: str, - fg: Tuple[int, int, int] = color.white, - *, - stack: bool = True + self, text: str, fg: Tuple[int, int, int] = color.white, *, stack: bool = True ) -> None: """Add a message to this log. `text` is the message text, `fg` is the text color. @@ -43,12 +39,12 @@ class MessageLog: self.messages.append(Message(text, fg)) def render( - self, - console: tcod.Console, - x: int, - y: int, - width: int, - height: int, + self, + console: tcod.Console, + x: int, + y: int, + width: int, + height: int, ) -> None: """Render this log over the given area. `x`, `y`, `width`, `height` is the rectangular region to render onto @@ -60,21 +56,17 @@ class MessageLog: def wrap(string: str, width: int) -> Iterable[str]: """Return a wrapped text message.""" for line in string.splitlines(): # Handle newlines in messages. - yield from textwrap.wrap( - line, - width, - expand_tabs=True - ) + yield from textwrap.wrap(line, width, expand_tabs=True) @classmethod def render_messages( - cls, - console: tcod.Console, - x: int, - y: int, - width: int, - height: int, - messages: Reversible[Message], + cls, + console: tcod.Console, + x: int, + y: int, + width: int, + height: int, + messages: Reversible[Message], ) -> None: """Render the messages provided. The `messages` are rendered starting at the last message and working diff --git a/procgen.py b/procgen.py index 28a3a64..02a902e 100644 --- a/procgen.py +++ b/procgen.py @@ -40,24 +40,23 @@ enemy_chances: Dict[int, List[Tuple[Entity, int]]] = { def get_max_value_for_floor( - max_value_by_floor: List[Tuple[int, int]], - floor: int + max_value_by_floor: List[Tuple[int, int]], floor: int ) -> int: current_value = 0 for floor_minimum, value in max_value_by_floor: if floor_minimum > floor: - break; + break else: - current_value = value; + current_value = value return current_value def get_entities_at_random( - weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]], - number_of_entities: int, - floor: int, + weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]], + number_of_entities: int, + floor: int, ) -> List[Entity]: entity_weighted_chances = {} @@ -75,9 +74,7 @@ def get_entities_at_random( entity_weighted_chance_values = list(entity_weighted_chances.values()) chosen_entities = random.choices( - entities, - weights=entity_weighted_chance_values, - k=number_of_entities + entities, weights=entity_weighted_chance_values, k=number_of_entities ) return chosen_entities @@ -105,17 +102,17 @@ class RectangularRoom: def intersects(self, other: RectangularRoom) -> bool: """Return True if this room overlaps with another RectangularRoom.""" return ( - self.x1 <= other.x2 - and self.x2 >= other.x1 - and self.y1 <= other.y2 - and self.y2 >= other.y1 + self.x1 <= other.x2 + and self.x2 >= other.x1 + and self.y1 <= other.y2 + and self.y2 >= other.y1 ) def place_entities( - room: RectangularRoom, - dungeon: GameMap, - floor_number: int, + room: RectangularRoom, + dungeon: GameMap, + floor_number: int, ) -> None: number_of_monsters = random.randint( 0, get_max_value_for_floor(max_monsters_by_floor, floor_number) @@ -125,14 +122,10 @@ def place_entities( ) monsters: List[Entity] = get_entities_at_random( - enemy_chances, - number_of_monsters, - floor_number + enemy_chances, number_of_monsters, floor_number ) items: List[Entity] = get_entities_at_random( - item_chances, - number_of_items, - floor_number + item_chances, number_of_items, floor_number ) for entity in monsters + items: @@ -143,7 +136,9 @@ def place_entities( entity.spawn(dungeon, x, y) -def tunnel_between(start: Tuple[int, int], end: Tuple[int, int]) -> Iterator[Tuple[int, int]]: +def tunnel_between( + start: Tuple[int, int], end: Tuple[int, int] +) -> Iterator[Tuple[int, int]]: """Return an L-shaped tunnel between these two points.""" x1, y1 = start x2, y2 = end @@ -163,12 +158,12 @@ def tunnel_between(start: Tuple[int, int], end: Tuple[int, int]) -> Iterator[Tup def generate_dungeon( - max_rooms: int, - room_min_size: int, - room_max_size: int, - map_width: int, - map_height: int, - engine: Engine, + max_rooms: int, + room_min_size: int, + room_max_size: int, + map_width: int, + map_height: int, + engine: Engine, ) -> GameMap: """Generate a new dungeon map.""" player = engine.player diff --git a/render_functions.py b/render_functions.py index b2581ff..6f2690e 100644 --- a/render_functions.py +++ b/render_functions.py @@ -22,10 +22,10 @@ def get_names_at_location(x: int, y: int, game_map: GameMap) -> str: def render_bar( - console: Console, - current_value: int, - maximum_value: int, - total_width: int, + console: Console, + current_value: int, + maximum_value: int, + total_width: int, ) -> None: bar_width = int(float(current_value) / maximum_value * total_width) @@ -33,12 +33,7 @@ def render_bar( if bar_width > 0: console.draw_rect( - x=0, - y=45, - width=bar_width, - height=1, - ch=1, - bg=color.bar_filled + x=0, y=45, width=bar_width, height=1, ch=1, bg=color.bar_filled ) console.print( @@ -50,9 +45,7 @@ def render_bar( def render_dungeon_level( - console: Console, - dungeon_level: int, - location: Tuple[int, int] + console: Console, dungeon_level: int, location: Tuple[int, int] ) -> None: """ Render the level the player is currently on, at the given location. @@ -63,13 +56,10 @@ def render_dungeon_level( def render_names_at_mouse_location( - console: Console, - x: int, - y: int, - engine: Engine + console: Console, x: int, y: int, engine: Engine ) -> None: mouse_x, mouse_y = engine.mouse_location names_at_mouse_location = get_names_at_location(mouse_x, mouse_y, engine.game_map) - console.print(x, y, names_at_mouse_location) \ No newline at end of file + console.print(x, y, names_at_mouse_location) diff --git a/setup_game.py b/setup_game.py index 97eb087..965a2f0 100644 --- a/setup_game.py +++ b/setup_game.py @@ -44,8 +44,7 @@ def new_game() -> Engine: engine.update_fov() engine.message_log.add_message( - "Hello and welcome, adventurer, to yet another dungeon!", - color.welcome_text + "Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text ) return engine @@ -83,11 +82,9 @@ class MainMenu(input_handlers.BaseEventHandler): ) menu_width = 24 - for i, text in enumerate([ - "[N] Play a new game", - "[C] Continue last game", - "[Q] Quit" - ]): + for i, text in enumerate( + ["[N] Play a new game", "[C] Continue last game", "[Q] Quit"] + ): console.print( console.width // 2, console.height // 2 - 2 + i, @@ -98,7 +95,9 @@ class MainMenu(input_handlers.BaseEventHandler): bg_blend=tcod.BKGND_ALPHA(64), ) - def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[input_handlers.BaseEventHandler]: + def ev_keydown( + self, event: tcod.event.KeyDown + ) -> Optional[input_handlers.BaseEventHandler]: if event.sym in (tcod.event.K_q, tcod.event.K_ESCAPE): raise SystemExit() elif event.sym == tcod.event.K_c: diff --git a/tile_types.py b/tile_types.py index 3172f8b..600239b 100644 --- a/tile_types.py +++ b/tile_types.py @@ -7,7 +7,10 @@ graphic_dt = np.dtype( [ ("ch", np.int32), # Unicode codepoint. ("fg", "3B"), # 3 unsigned bytes, for RGB colors. - ("bg", "3B",) + ( + "bg", + "3B", + ), ] ) @@ -23,11 +26,11 @@ tile_dt = np.dtype( def new_tile( - *, # Enforce the use of keywords, so that parameter order doesn't matter - walkable: int, - transparent: int, - dark: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]], - light: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]] + *, # Enforce the use of keywords, so that parameter order doesn't matter + walkable: int, + transparent: int, + dark: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]], + light: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]] ) -> np.ndarray: """Helper function for defining individual tile types""" return np.array((walkable, transparent, dark, light), dtype=tile_dt)