From ad17c7fd216b9b06fcb86182f9057f7fea2b7a44 Mon Sep 17 00:00:00 2001 From: BlastSlimey <89539656+BlastSlimey@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:01:57 +0200 Subject: [PATCH] shapez: Typing Cleanup + Small Docs Rewordings (#5189) Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/shapez/__init__.py | 22 ++++---- worlds/shapez/common/options.py | 48 +++++++++--------- worlds/shapez/data/generate.py | 13 +++-- worlds/shapez/docs/datapackage_settings_de.md | 9 ++-- worlds/shapez/docs/datapackage_settings_en.md | 28 +++++------ worlds/shapez/docs/de_shapez.md | 20 ++++---- worlds/shapez/docs/en_shapez.md | 34 ++++++------- worlds/shapez/docs/setup_de.md | 7 +-- worlds/shapez/docs/setup_en.md | 6 +-- worlds/shapez/items.py | 36 ++++++------- worlds/shapez/locations.py | 50 +++++++++---------- worlds/shapez/regions.py | 12 ++--- worlds/shapez/test/__init__.py | 2 +- 13 files changed, 144 insertions(+), 143 deletions(-) diff --git a/worlds/shapez/__init__.py b/worlds/shapez/__init__.py index 2a77ed8c..5557e2a9 100644 --- a/worlds/shapez/__init__.py +++ b/worlds/shapez/__init__.py @@ -1,5 +1,5 @@ import math -from typing import Any, List, Dict, Tuple, Mapping +from typing import Mapping, Any from Options import OptionError from .data.strings import OTHER, ITEMS, CATEGORY, LOCATIONS, SLOTDATA, GOALS, OPTIONS @@ -123,23 +123,23 @@ class ShapezWorld(World): # Defining instance attributes for each shapez world # These are set to default values that should fail unit tests if not replaced with correct values self.location_count: int = 0 - self.level_logic: List[str] = [] - self.upgrade_logic: List[str] = [] + self.level_logic: list[str] = [] + self.upgrade_logic: list[str] = [] self.level_logic_type: str = "" self.upgrade_logic_type: str = "" - self.random_logic_phase_length: List[int] = [] - self.category_random_logic_amounts: Dict[str, int] = {} + self.random_logic_phase_length: list[int] = [] + self.category_random_logic_amounts: dict[str, int] = {} self.maxlevel: int = 0 self.finaltier: int = 0 - self.included_locations: Dict[str, Tuple[str, LocationProgressType]] = {} + self.included_locations: dict[str, tuple[str, LocationProgressType]] = {} self.client_seed: int = 0 - self.shapesanity_names: List[str] = [] + self.shapesanity_names: list[str] = [] self.upgrade_traps_allowed: bool = False # Universal Tracker support self.ut_active: bool = False - self.passthrough: Dict[str, any] = {} - self.location_id_to_alias: Dict[int, str] = {} + self.passthrough: dict[str, Any] = {} + self.location_id_to_alias: dict[int, str] = {} @classmethod def stage_generate_early(cls, multiworld: MultiWorld) -> None: @@ -315,7 +315,7 @@ class ShapezWorld(World): def create_items(self) -> None: # Include guaranteed items (game mechanic unlocks and 7x4 big upgrades) - included_items: List[Item] = ([self.create_item(name) for name in buildings_processing.keys()] + included_items: list[Item] = ([self.create_item(name) for name in buildings_processing.keys()] + [self.create_item(name) for name in buildings_routing.keys()] + [self.create_item(name) for name in buildings_other.keys()] + [self.create_item(name) for name in buildings_top_row.keys()] @@ -412,6 +412,6 @@ class ShapezWorld(World): **logic_type_cat_random_data, SLOTDATA.seed: self.client_seed, SLOTDATA.shapesanity: self.shapesanity_names} - def interpret_slot_data(self, slot_data: Dict[str, Any]) -> Dict[str, Any]: + def interpret_slot_data(self, slot_data: dict[str, Any]) -> dict[str, Any]: """Helper function for Universal Tracker""" return slot_data diff --git a/worlds/shapez/common/options.py b/worlds/shapez/common/options.py index aa66ced0..8a55448c 100644 --- a/worlds/shapez/common/options.py +++ b/worlds/shapez/common/options.py @@ -1,5 +1,5 @@ import random -import typing +from typing import cast, Any from Options import FreeText, NumericOption @@ -47,7 +47,7 @@ class FloatRangeText(FreeText, NumericOption): raise Exception(f"{value} is higher than maximum {self.range_end} for option {self.__class__.__name__}") @classmethod - def from_text(cls, text: str) -> typing.Any: + def from_text(cls, text: str) -> Any: return cls(text) @classmethod @@ -99,31 +99,31 @@ class FloatRangeText(FreeText, NumericOption): def get_option_name(cls, value: float) -> str: return str(value) - def __eq__(self, other: typing.Any): + def __eq__(self, other: Any): if isinstance(other, NumericOption): return self.value == other.value else: - return typing.cast(bool, self.value == other) + return cast(bool, self.value == other) - def __lt__(self, other: typing.Union[int, float, NumericOption]) -> bool: + def __lt__(self, other: int | float | NumericOption) -> bool: if isinstance(other, NumericOption): return self.value < other.value else: return self.value < other - def __le__(self, other: typing.Union[int, float, NumericOption]) -> bool: + def __le__(self, other: int | float | NumericOption) -> bool: if isinstance(other, NumericOption): return self.value <= other.value else: return self.value <= other - def __gt__(self, other: typing.Union[int, float, NumericOption]) -> bool: + def __gt__(self, other: int | float | NumericOption) -> bool: if isinstance(other, NumericOption): return self.value > other.value else: return self.value > other - def __ge__(self, other: typing.Union[int, float, NumericOption]) -> bool: + def __ge__(self, other: int | float | NumericOption) -> bool: if isinstance(other, NumericOption): return self.value >= other.value else: @@ -132,59 +132,59 @@ class FloatRangeText(FreeText, NumericOption): def __int__(self) -> int: return int(self.value) - def __and__(self, other: typing.Any) -> int: + def __and__(self, other: Any) -> int: raise TypeError("& operator not supported for float values") - def __floordiv__(self, other: typing.Any) -> int: + def __floordiv__(self, other: Any) -> int: return int(self.value // float(other)) def __invert__(self) -> int: raise TypeError("~ operator not supported for float values") - def __lshift__(self, other: typing.Any) -> int: + def __lshift__(self, other: Any) -> int: raise TypeError("<< operator not supported for float values") - def __mod__(self, other: typing.Any) -> float: + def __mod__(self, other: Any) -> float: return self.value % float(other) def __neg__(self) -> float: return -self.value - def __or__(self, other: typing.Any) -> int: + def __or__(self, other: Any) -> int: raise TypeError("| operator not supported for float values") def __pos__(self) -> float: return +self.value - def __rand__(self, other: typing.Any) -> int: + def __rand__(self, other: Any) -> int: raise TypeError("& operator not supported for float values") - def __rfloordiv__(self, other: typing.Any) -> int: + def __rfloordiv__(self, other: Any) -> int: return int(float(other) // self.value) - def __rlshift__(self, other: typing.Any) -> int: + def __rlshift__(self, other: Any) -> int: raise TypeError("<< operator not supported for float values") - def __rmod__(self, other: typing.Any) -> float: + def __rmod__(self, other: Any) -> float: return float(other) % self.value - def __ror__(self, other: typing.Any) -> int: + def __ror__(self, other: Any) -> int: raise TypeError("| operator not supported for float values") - def __round__(self, ndigits: typing.Optional[int] = None) -> float: + def __round__(self, ndigits: int | None = None) -> float: return round(self.value, ndigits) - def __rpow__(self, base: typing.Any) -> typing.Any: + def __rpow__(self, base: Any) -> Any: return base ** self.value - def __rrshift__(self, other: typing.Any) -> int: + def __rrshift__(self, other: Any) -> int: raise TypeError(">> operator not supported for float values") - def __rshift__(self, other: typing.Any) -> int: + def __rshift__(self, other: Any) -> int: raise TypeError(">> operator not supported for float values") - def __rxor__(self, other: typing.Any) -> int: + def __rxor__(self, other: Any) -> int: raise TypeError("^ operator not supported for float values") - def __xor__(self, other: typing.Any) -> int: + def __xor__(self, other: Any) -> int: raise TypeError("^ operator not supported for float values") diff --git a/worlds/shapez/data/generate.py b/worlds/shapez/data/generate.py index 27d74e86..86b660ef 100644 --- a/worlds/shapez/data/generate.py +++ b/worlds/shapez/data/generate.py @@ -1,14 +1,13 @@ import itertools import time -from typing import Dict, List from worlds.shapez.data.strings import SHAPESANITY, REGIONS -shapesanity_simple: Dict[str, str] = {} -shapesanity_1_4: Dict[str, str] = {} -shapesanity_two_sided: Dict[str, str] = {} -shapesanity_three_parts: Dict[str, str] = {} -shapesanity_four_parts: Dict[str, str] = {} +shapesanity_simple: dict[str, str] = {} +shapesanity_1_4: dict[str, str] = {} +shapesanity_two_sided: dict[str, str] = {} +shapesanity_three_parts: dict[str, str] = {} +shapesanity_four_parts: dict[str, str] = {} subshape_names = [SHAPESANITY.circle, SHAPESANITY.square, SHAPESANITY.star, SHAPESANITY.windmill] color_names = [SHAPESANITY.red, SHAPESANITY.blue, SHAPESANITY.green, SHAPESANITY.yellow, SHAPESANITY.purple, SHAPESANITY.cyan, SHAPESANITY.white, SHAPESANITY.uncolored] @@ -16,7 +15,7 @@ short_subshapes = ["C", "R", "S", "W"] short_colors = ["b", "c", "g", "p", "r", "u", "w", "y"] -def color_to_needed_building(color_list: List[str]) -> str: +def color_to_needed_building(color_list: list[str]) -> str: for next_color in color_list: if next_color in [SHAPESANITY.yellow, SHAPESANITY.purple, SHAPESANITY.cyan, SHAPESANITY.white, "y", "p", "c", "w"]: diff --git a/worlds/shapez/docs/datapackage_settings_de.md b/worlds/shapez/docs/datapackage_settings_de.md index ae375f3e..a6c1b35c 100644 --- a/worlds/shapez/docs/datapackage_settings_de.md +++ b/worlds/shapez/docs/datapackage_settings_de.md @@ -4,7 +4,7 @@ Die Maximalwerte von `goal_amount` und `shapesanity_amount` sind fest eingebaute Einstellungen, die das Datenpaket des Spiels beeinflussen. Sie sind in einer Datei names `options.json` innerhalb der APWorld festgelegt. Durch das Ändern -dieser Werte erschaffst du eine custom APWorld, die nur auf deinem PC existiert. +dieser Werte erschaffst du eine custom Version der APWorld, die nur auf deinem PC existiert. ## Wie du die Datenpaket-Einstellungen änderst @@ -18,17 +18,18 @@ ordnungsgemäß befolgt wird. Anwendung auf eigene Gefahr. - `max_shapesanity` kann nicht weniger als `4` sein, da dies die benötigte Mindestanzahl zum Verhindern von FillErrors ist. - `max_shapesanity` kann auch nicht mehr als `75800` sein, da dies die maximale Anzahl an möglichen Shapesanity-Namen - ist. Ansonsten könnte die Generierung der Multiworld fehlschlagen. + ist. Das Generieren der Multiworld wird fehlschlagen, falls die `shapesanity_amount`-Option auf einen höheren Wert + gesetzt wird. - `max_levels_and_upgrades` kann nicht weniger als `27` sein, da dies die Mindestanzahl für das `mam`-Ziel ist. 5. Schließe die Zip-Datei und benenne sie zurück zu `shapez.apworld`. ## Warum muss ich das ganze selbst machen? Alle Spiele in Archipelago müssen eine Liste aller möglichen Locations **unabhängig der Spieler-Optionen** -bereitstellen. Diese Listen aller in einer Multiworld inkludierten Spiele werden in den Daten der Multiworld gespeichert +bereitstellen. Diese Listen aller in einer Multiworld inkludierten Spiele werden in den Daten der Multiworld gespeichert und an alle verbundenen Clients gesendet. Je mehr mögliche Locations, desto größer das Datenpaket. Und mit ~80000 möglichen Locations hatte shapez zu einem gewissen Zeitpunkt ein (von der Datenmenge her) größeres Datenpaket als alle -supporteten Spiele zusammen. Um also diese Datenmenge zu reduzieren wurden die ausgeschriebenen +Core-verifizierten Spiele zusammen. Um also diese Datenmenge zu reduzieren, wurden die ausgeschriebenen Shapesanity-Locations-Namen (`Shapesanity Uncolored Circle`, `Shapesanity Blue Rectangle`, ...) durch standardisierte Namen (`Shapesanity 1`, `Shapesanity 2`, ...) ersetzt. Durch das Ändern dieser Maximalwerte, und damit das Erstellen einer custom APWorld, kannst du die Anzahl der möglichen Locations erhöhen, wirst aber auch gleichzeitig das Datenpaket diff --git a/worlds/shapez/docs/datapackage_settings_en.md b/worlds/shapez/docs/datapackage_settings_en.md index fd0ed167..64f39abf 100644 --- a/worlds/shapez/docs/datapackage_settings_en.md +++ b/worlds/shapez/docs/datapackage_settings_en.md @@ -1,14 +1,14 @@ -# Guide to change maximum locations in shapez +# Guide to change the maximum amount of locations in shapez ## Where do I find the settings to increase/decrease the amount of possible locations? -The maximum values of the `goal_amount` and `shapesanity_amount` are hardcoded settings that affect the datapackage. -They are stored in a file called `options.json` inside the apworld. By changing them, you will create a custom apworld -on your local machine. +The maximum values of the `goal_amount` and `shapesanity_amount` options are hardcoded settings that affect the +datapackage. They are stored in a file called `options.json` inside the apworld. By changing them, you will create a +custom version on your local machine. -## How to change datapackage options +## How to change datapackage settings -This tutorial is for advanced users and can result in the software not working properly, if not read carefully. +This tutorial is intended for advanced users and can result in the software not working properly, if not read carefully. Proceed at your own risk. 1. Go to `/lib/worlds`. @@ -17,17 +17,17 @@ Proceed at your own risk. 4. Edit the values in this file to your desire and save the file. - `max_shapesanity` cannot be lower than `4`, as this is the minimum amount to prevent FillErrors. - `max_shapesanity` also cannot be higher than `75800`, as this is the maximum amount of possible shapesanity names. - Else the multiworld generation might fail. + Multiworld generation will fail if the `shapesanity_amount` options is set to a higher value. - `max_levels_and_upgrades` cannot be lower than `27`, as this is the minimum amount for the `mam` goal to properly work. -5. Close the zip and rename it back to `shapez.apworld`. +5. Close the zip file and rename it back to `shapez.apworld`. ## Why do I have to do this manually? For every game in Archipelago, there must be a list of all possible locations, **regardless of player options**. When -generating a multiworld, a list of all locations of all included games will be saved in the multiworld data and sent to -all clients. The higher the amount of possible locations, the bigger the datapackage. And having ~80000 possible -locations at one point made the datapackage for shapez bigger than all other supported games combined. So to reduce the -datapackage of shapez, the locations for shapesanity are named `Shapesanity 1`, `Shapesanity 2` etc. instead of their -actual names. By creating a custom apworld, you can increase the amount of possible locations, but you will also -increase the size of the datapackage at the same time. +generating a multiworld, a list of all locations of all included games will be saved in the multiworld's data and sent +to all clients. The higher the amount of possible locations, the bigger the datapackage. And having ~80000 possible +locations at one point made the datapackage for shapez bigger than all other core-verified games combined. So, to reduce +the datapackage size of shapez, the locations for shapesanity are named `Shapesanity 1`, `Shapesanity 2` etc. instead of +their actual names. By creating a custom version of the apworld, you can increase the amount of possible locations, but +you will also increase the size of the datapackage at the same time. diff --git a/worlds/shapez/docs/de_shapez.md b/worlds/shapez/docs/de_shapez.md index 4a26ea82..494edca2 100644 --- a/worlds/shapez/docs/de_shapez.md +++ b/worlds/shapez/docs/de_shapez.md @@ -19,25 +19,27 @@ Zusätzlich gibt es zu diesem Spiel "Datenpaket-Einstellungen", die du nach Alle Belohnungen aus den Tutorial-Level (das Freischalten von Gebäuden und Spielmechaniken) und Verbesserungen durch Upgrades werden dem Itempool der Multiworld hinzugefügt. Außerdem werden, wenn so in den Spieler-Optionen festgelegt, -die Bedingungen zum Abschließen eines Levels und zum Kaufen der Upgrades randomisiert. +die Bedingungen zum Abschließen eines Levels und zum Kaufen der Upgrades randomisiert und die Reihenfolge der Gebäude +in deinen Toolbars (Haupt- und Kabelebene) gemischt. ## Was ist das Ziel von shapez in Archipelago? -Da das Spiel eigentlich kein konkretes Ziel (nach dem Tutorial) hat, kann man sich zwischen (momentan) 4 verschiedenen -Zielen entscheiden: +Da das Spiel eigentlich kein konkretes Ziel, welches das Ende des Spiels bedeuten würde, hat, kann man sich zwischen +(aktuell) 4 verschiedenen Zielen entscheiden: 1. Vanilla: Schließe Level 26 ab (eigentlich das Ende des Tutorials). 2. MAM: Schließe ein bestimmtes Level nach Level 26 ab, das zuvor in den Spieler-Optionen festgelegt wurde. Es ist empfohlen, eine Maschine zu bauen, die alles automatisch herstellt ("Make-Anything-Machine", kurz MAM). -3. Even Fasterer: Kaufe alle Upgrades bis zu einer in den Spieler-Optionen festgelegten Stufe (nach Stufe 8). +3. Even Fasterer: Kaufe alle Upgrades bis zu einer in den Spieler-Optionen festgelegten Stufe (nach Stufe VIII (8)). 4. Efficiency III: Liefere 256 Blaupausen-Formen pro Sekunde ins Zentrum. ## Welche Items können in den Welten anderer Spieler erscheinen? -- Freischalten verschiedener Gebäude +- Gebäude - Blaupausen freischalten -- Große Upgrades (addiert 1 zum Geschwindigkeitsmultiplikator) -- Kleine Upgrades (addiert 0.1 zum Geschwindigkeitsmultiplikator) -- Andere ungewöhnliche Upgrades (optional) +- Upgrades + - Große Upgrades (addiert 1 zum Geschwindigkeitsmultiplikator) + - Kleine Upgrades (addiert 0.1 zum Geschwindigkeitsmultiplikator) + - Andere ungewöhnliche (auch negative) Upgrades (optional) - Verschiedene Bündel, die bestimmte Formen enthalten - Fallen, die bestimmte Formen aus dem Zentrum dränieren (ja, das Wort gibt es) - Fallen, die zufällige Gebäude oder andere Spielmechaniken betreffen @@ -45,7 +47,7 @@ empfohlen, eine Maschine zu bauen, die alles automatisch herstellt ("Make-Anythi ## Was ist eine Location / ein Check? - Level (minimum 1-25, bis zu 499 je nach Spieler-Optionen, mit zusätzlichen Checks für Level 1 und 20) -- Upgrades (minimum Stufen II-VIII (2-8), bis zu D (500) je nach Spieler-Optionen) +- Upgrades (minimum Stufen II-VIII (2-8), bis zu D (500), je nach Spieler-Optionen) - Bestimmte Formen mindestens einmal ins Zentrum liefern ("Shapesanity", bis zu 1000 zufällig gewählte Definitionen) - Errungenschaften (bis zu 45) diff --git a/worlds/shapez/docs/en_shapez.md b/worlds/shapez/docs/en_shapez.md index dc41d73d..56c03872 100644 --- a/worlds/shapez/docs/en_shapez.md +++ b/worlds/shapez/docs/en_shapez.md @@ -4,9 +4,9 @@ shapez is an automation game about cutting, rotating, stacking, and painting shapes, that you extract from randomly generated patches on an infinite canvas, and sending them to the hub to complete levels. The "tutorial", where you -unlock a new building or game mechanic (almost) each level, lasts until level 26, where you unlock freeplay with -infinitely more levels, that require a new, randomly generated shape. Alongside the levels, you can unlock upgrades, -that make your buildings work faster. +unlock a new building or game mechanic (almost) each level, lasts until level 26, which unlocks freeplay with +infinitely more levels, that each require a new, randomly generated shape. Alongside the levels, you can unlock +upgrades, that make your buildings work faster. ## Where is the options page? @@ -17,29 +17,30 @@ There are also some advanced "datapackage settings" that can be changed by follo ## What does randomization do to this game? -Buildings and gameplay mechanics, that you normally unlock by completing a level, and upgrade improvements are put -into the item pool of the multiworld. Also, if enabled, the requirements for completing a level or buying an upgrade are -randomized. +Buildings and gameplay mechanics, which you normally unlock by completing a level, and upgrade improvements are put +into the item pool of the multiworld. You can also randomize the requirements for completing a level or buying an +upgrade and shuffle the order of building in your toolbars (main and wires layer). ## What is the goal of shapez in Archipelago? -As the game has no actual goal where the game ends, there are (currently) 4 different goals you can choose from in the -player options: +As the game has no actual goal that would represent the end of the game, there are (currently) 4 different goals you +can choose from in the player options: 1. Vanilla: Complete level 26 (the end of the tutorial). 2. MAM: Complete a player-specified level after level 26. It's recommended to build a Make-Anything-Machine (MAM). -3. Even Fasterer: Upgrade everything to a player-specified tier after tier 8. +3. Even Fasterer: Upgrade everything to a player-specified tier after tier VIII (8). 4. Efficiency III: Deliver 256 blueprint shapes per second to the hub. ## Which items can be in another player's world? -- Unlock different buildings -- Unlock blueprints -- Big upgrade improvements (adds 1 to the multiplier) -- Small upgrade improvements (adds .1 to the multiplier) -- Other unusual upgrade improvements (optional) +- Buildings +- Unlocking blueprints +- Upgrade improvements + - Big improvements, adding 1 to the multiplier + - Small improvements, adding 0.1 to the multiplier + - Optional: Other, rather unusual and even bad, improvements - Different shapes bundles - Inventory draining traps -- Different traps afflicting random buildings and game mechanics +- Different traps affecting random buildings and game mechanics ## What is considered a location check? @@ -61,5 +62,4 @@ Here's a cheat sheet: ## Can I use other mods alongside the AP client? At the moment, compatibility with other mods is not supported, but not forbidden. Gameplay altering mods will most -likely crash the game or disable loading the afflicted mods, while QoL mods might work without problems. Try at your own -risk. +likely break the game in some way, while small QoL mods might work without problems. Try at your own risk. diff --git a/worlds/shapez/docs/setup_de.md b/worlds/shapez/docs/setup_de.md index 1b927f37..a2eb92df 100644 --- a/worlds/shapez/docs/setup_de.md +++ b/worlds/shapez/docs/setup_de.md @@ -16,9 +16,10 @@ - Archipelago von der [Archipelago-Release-Seite](https://github.com/ArchipelagoMW/Archipelago/releases) * (Für den Text-Client) - * (Alternativ kannst du auch die eingebaute Konsole (nur lesbar) nutzen, indem du beim Starten des Spiels den - `-dev`-Parameter verwendest) -- Universal Tracker (schau im `#future-game-design`-Thread für UT auf dem Discord-Server nach der aktuellen Anleitung) + * (Alternativ kannst du auch die eingebaute Konsole nutzen, indem du das Spiel mit dem `-dev`-Parameter + startest und jede Nachricht als `AP.sendAPMessage(""")` schreibst) +- Universal Tracker (schau im Kanal von UT auf dem Discord-Server nach der aktuellen Anleitung und für weitere + Informationen) ## Installation diff --git a/worlds/shapez/docs/setup_en.md b/worlds/shapez/docs/setup_en.md index 4c91c16a..2036f75d 100644 --- a/worlds/shapez/docs/setup_en.md +++ b/worlds/shapez/docs/setup_en.md @@ -16,9 +16,9 @@ - Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) * (Only for the TextClient) - * (If you want, you can use the built-in console as a read-only text client by launching the game - with the `-dev` parameter) -- Universal Tracker (check UT's `#future-game-design` thread in the discord server for instructions) + * (You can alternatively use the built-in console by launching the game with the `-dev` parameter and typing + `AP.sendAPMessage(""")`) +- Universal Tracker (check UT's channel in the discord server for more information and instructions) ## Installation diff --git a/worlds/shapez/items.py b/worlds/shapez/items.py index aef4c033..2e5816b9 100644 --- a/worlds/shapez/items.py +++ b/worlds/shapez/items.py @@ -1,4 +1,4 @@ -from typing import Dict, Callable, Any, List +from typing import Callable, Any from BaseClasses import Item, ItemClassification as IClass from .options import ShapezOptions @@ -37,7 +37,7 @@ def always_trap(options: ShapezOptions) -> IClass: # would be unreasonably complicated and time-consuming. # Some buildings are not needed to complete the game, but are "logically needed" for the "MAM" achievement. -buildings_processing: Dict[str, Callable[[ShapezOptions], IClass]] = { +buildings_processing: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.cutter: always_progression, ITEMS.cutter_quad: always_progression, ITEMS.rotator: always_progression, @@ -50,7 +50,7 @@ buildings_processing: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.color_mixer: always_progression, } -buildings_routing: Dict[str, Callable[[ShapezOptions], IClass]] = { +buildings_routing: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.balancer: always_progression, ITEMS.comp_merger: always_progression, ITEMS.comp_splitter: always_progression, @@ -58,12 +58,12 @@ buildings_routing: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.tunnel_tier_ii: is_mam_achievement_included, } -buildings_other: Dict[str, Callable[[ShapezOptions], IClass]] = { +buildings_other: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trash: always_progression, ITEMS.extractor_chain: always_useful } -buildings_top_row: Dict[str, Callable[[ShapezOptions], IClass]] = { +buildings_top_row: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.belt_reader: is_mam_achievement_included, ITEMS.storage: is_achievements_included, ITEMS.switch: always_progression, @@ -71,18 +71,18 @@ buildings_top_row: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.display: always_useful } -buildings_wires: Dict[str, Callable[[ShapezOptions], IClass]] = { +buildings_wires: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.wires: always_progression, ITEMS.const_signal: always_progression, ITEMS.logic_gates: is_mam_achievement_included, ITEMS.virtual_proc: is_mam_achievement_included } -gameplay_unlocks: Dict[str, Callable[[ShapezOptions], IClass]] = { +gameplay_unlocks: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.blueprints: is_achievements_included } -upgrades: Dict[str, Callable[[ShapezOptions], IClass]] = { +upgrades: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.upgrade_big_belt: always_progression, ITEMS.upgrade_big_miner: always_useful, ITEMS.upgrade_big_proc: always_useful, @@ -93,7 +93,7 @@ upgrades: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.upgrade_small_paint: always_filler } -whacky_upgrades: Dict[str, Callable[[ShapezOptions], IClass]] = { +whacky_upgrades: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.upgrade_gigantic_belt: always_progression, ITEMS.upgrade_gigantic_miner: always_useful, ITEMS.upgrade_gigantic_proc: always_useful, @@ -106,7 +106,7 @@ whacky_upgrades: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.upgrade_small_random: always_filler, } -whacky_upgrade_traps: Dict[str, Callable[[ShapezOptions], IClass]] = { +whacky_upgrade_traps: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_upgrade_belt: always_trap, ITEMS.trap_upgrade_miner: always_trap, ITEMS.trap_upgrade_proc: always_trap, @@ -117,13 +117,13 @@ whacky_upgrade_traps: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_upgrade_demonic_paint: always_trap, } -bundles: Dict[str, Callable[[ShapezOptions], IClass]] = { +bundles: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.bundle_blueprint: always_filler, ITEMS.bundle_level: always_filler, ITEMS.bundle_upgrade: always_filler } -standard_traps: Dict[str, Callable[[ShapezOptions], IClass]] = { +standard_traps: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_locked: always_trap, ITEMS.trap_throttled: always_trap, ITEMS.trap_malfunction: always_trap, @@ -131,22 +131,22 @@ standard_traps: Dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_clear_belts: always_trap, } -random_draining_trap: Dict[str, Callable[[ShapezOptions], IClass]] = { +random_draining_trap: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_draining_inv: always_trap } -split_draining_traps: Dict[str, Callable[[ShapezOptions], IClass]] = { +split_draining_traps: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.trap_draining_blueprint: always_trap, ITEMS.trap_draining_level: always_trap, ITEMS.trap_draining_upgrade: always_trap } -belt_and_extractor: Dict[str, Callable[[ShapezOptions], IClass]] = { +belt_and_extractor: dict[str, Callable[[ShapezOptions], IClass]] = { ITEMS.belt: always_progression, ITEMS.extractor: always_progression } -item_table: Dict[str, Callable[[ShapezOptions], IClass]] = { +item_table: dict[str, Callable[[ShapezOptions], IClass]] = { **buildings_processing, **buildings_routing, **buildings_other, @@ -205,10 +205,10 @@ def trap(random: float, split_draining: bool, whacky_allowed: bool) -> str: return random_choice_nested(random, pool) -def random_choice_nested(random: float, nested: List[Any]) -> Any: +def random_choice_nested(random: float, nested: list[Any]) -> Any: """Helper function for getting a random element from a nested list.""" current: Any = nested - while isinstance(current, List): + while isinstance(current, list): index_float = random*len(current) current = current[int(index_float)] random = index_float-int(index_float) diff --git a/worlds/shapez/locations.py b/worlds/shapez/locations.py index 6d069afa..f68ca1eb 100644 --- a/worlds/shapez/locations.py +++ b/worlds/shapez/locations.py @@ -1,5 +1,5 @@ from random import Random -from typing import List, Tuple, Dict, Optional, Callable +from typing import Callable from BaseClasses import Location, LocationProgressType, Region from .data.strings import CATEGORY, LOCATIONS, REGIONS, OPTIONS, GOALS, OTHER, SHAPESANITY @@ -7,7 +7,7 @@ from .options import max_shapesanity, max_levels_and_upgrades categories = [CATEGORY.belt, CATEGORY.miner, CATEGORY.processors, CATEGORY.painting] -translate: List[Tuple[int, str]] = [ +translate: list[tuple[int, str]] = [ (1000, "M"), (900, "CM"), (500, "D"), @@ -148,17 +148,17 @@ location_description = { # TODO change keys to global strings "windmill.", } -shapesanity_simple: Dict[str, str] = {} -shapesanity_1_4: Dict[str, str] = {} -shapesanity_two_sided: Dict[str, str] = {} -shapesanity_three_parts: Dict[str, str] = {} -shapesanity_four_parts: Dict[str, str] = {} +shapesanity_simple: dict[str, str] = {} +shapesanity_1_4: dict[str, str] = {} +shapesanity_two_sided: dict[str, str] = {} +shapesanity_three_parts: dict[str, str] = {} +shapesanity_four_parts: dict[str, str] = {} -level_locations: List[str] = ([LOCATIONS.level(1, 1), LOCATIONS.level(20, 1), LOCATIONS.level(20, 2)] +level_locations: list[str] = ([LOCATIONS.level(1, 1), LOCATIONS.level(20, 1), LOCATIONS.level(20, 2)] + [LOCATIONS.level(x) for x in range(1, max_levels_and_upgrades)]) -upgrade_locations: List[str] = [LOCATIONS.upgrade(cat, roman(x)) +upgrade_locations: list[str] = [LOCATIONS.upgrade(cat, roman(x)) for cat in categories for x in range(2, max_levels_and_upgrades+1)] -achievement_locations: List[str] = [LOCATIONS.my_eyes, LOCATIONS.painter, LOCATIONS.cutter, LOCATIONS.rotater, +achievement_locations: list[str] = [LOCATIONS.my_eyes, LOCATIONS.painter, LOCATIONS.cutter, LOCATIONS.rotater, LOCATIONS.wait_they_stack, LOCATIONS.wires, LOCATIONS.storage, LOCATIONS.freedom, LOCATIONS.the_logo, LOCATIONS.to_the_moon, LOCATIONS.its_piling_up, LOCATIONS.use_it_later, LOCATIONS.efficiency_1, LOCATIONS.preparing_to_launch, @@ -172,7 +172,7 @@ achievement_locations: List[str] = [LOCATIONS.my_eyes, LOCATIONS.painter, LOCATI LOCATIONS.mam, LOCATIONS.perfectionist, LOCATIONS.next_dimension, LOCATIONS.oops, LOCATIONS.copy_pasta, LOCATIONS.ive_seen_that_before, LOCATIONS.memories, LOCATIONS.i_need_trains, LOCATIONS.a_bit_early, LOCATIONS.gps] -shapesanity_locations: List[str] = [LOCATIONS.shapesanity(x) for x in range(1, max_shapesanity+1)] +shapesanity_locations: list[str] = [LOCATIONS.shapesanity(x) for x in range(1, max_shapesanity+1)] def init_shapesanity_pool() -> None: @@ -186,12 +186,12 @@ def init_shapesanity_pool() -> None: def addlevels(maxlevel: int, logictype: str, - random_logic_phase_length: List[int]) -> Dict[str, Tuple[str, LocationProgressType]]: + random_logic_phase_length: list[int]) -> dict[str, tuple[str, LocationProgressType]]: """Returns a dictionary with all level locations based on player options (maxlevel INCLUDED). If shape requirements are not randomized, the logic type is expected to be vanilla.""" # Level 1 is always directly accessible - locations: Dict[str, Tuple[str, LocationProgressType]] \ + locations: dict[str, tuple[str, LocationProgressType]] \ = {LOCATIONS.level(1): (REGIONS.main, LocationProgressType.PRIORITY), LOCATIONS.level(1, 1): (REGIONS.main, LocationProgressType.PRIORITY)} level_regions = [REGIONS.main, REGIONS.levels_1, REGIONS.levels_2, REGIONS.levels_3, @@ -282,11 +282,11 @@ def addlevels(maxlevel: int, logictype: str, def addupgrades(finaltier: int, logictype: str, - category_random_logic_amounts: Dict[str, int]) -> Dict[str, Tuple[str, LocationProgressType]]: + category_random_logic_amounts: dict[str, int]) -> dict[str, tuple[str, LocationProgressType]]: """Returns a dictionary with all upgrade locations based on player options (finaltier INCLUDED). If shape requirements are not randomized, give logic type 0.""" - locations: Dict[str, Tuple[str, LocationProgressType]] = {} + locations: dict[str, tuple[str, LocationProgressType]] = {} upgrade_regions = [REGIONS.main, REGIONS.upgrades_1, REGIONS.upgrades_2, REGIONS.upgrades_3, REGIONS.upgrades_4, REGIONS.upgrades_5] @@ -366,13 +366,13 @@ def addupgrades(finaltier: int, logictype: str, def addachievements(excludesoftlock: bool, excludelong: bool, excludeprogressive: bool, - maxlevel: int, upgradelogictype: str, category_random_logic_amounts: Dict[str, int], - goal: str, presentlocations: Dict[str, Tuple[str, LocationProgressType]], + maxlevel: int, upgradelogictype: str, category_random_logic_amounts: dict[str, int], + goal: str, presentlocations: dict[str, tuple[str, LocationProgressType]], add_alias: Callable[[str, str], None], has_upgrade_traps: bool - ) -> Dict[str, Tuple[str, LocationProgressType]]: + ) -> dict[str, tuple[str, LocationProgressType]]: """Returns a dictionary with all achievement locations based on player options.""" - locations: Dict[str, Tuple[str, LocationProgressType]] = dict() + locations: dict[str, tuple[str, LocationProgressType]] = dict() upgrade_regions = [REGIONS.main, REGIONS.upgrades_1, REGIONS.upgrades_2, REGIONS.upgrades_3, REGIONS.upgrades_4, REGIONS.upgrades_5] @@ -472,10 +472,10 @@ def addachievements(excludesoftlock: bool, excludelong: bool, excludeprogressive def addshapesanity(amount: int, random: Random, append_shapesanity: Callable[[str], None], - add_alias: Callable[[str, str], None]) -> Dict[str, Tuple[str, LocationProgressType]]: + add_alias: Callable[[str, str], None]) -> dict[str, tuple[str, LocationProgressType]]: """Returns a dictionary with a given number of random shapesanity locations.""" - included_shapes: Dict[str, Tuple[str, LocationProgressType]] = {} + included_shapes: dict[str, tuple[str, LocationProgressType]] = {} def f(name: str, region: str, alias: str, progress: LocationProgressType = LocationProgressType.DEFAULT) -> None: included_shapes[name] = (region, progress) @@ -518,11 +518,11 @@ def addshapesanity(amount: int, random: Random, append_shapesanity: Callable[[st return included_shapes -def addshapesanity_ut(shapesanity_names: List[str], add_alias: Callable[[str, str], None] - ) -> Dict[str, Tuple[str, LocationProgressType]]: +def addshapesanity_ut(shapesanity_names: list[str], add_alias: Callable[[str, str], None] + ) -> dict[str, tuple[str, LocationProgressType]]: """Returns the same information as addshapesanity but will add specific values based on a UT rebuild.""" - included_shapes: Dict[str, Tuple[str, LocationProgressType]] = {} + included_shapes: dict[str, tuple[str, LocationProgressType]] = {} for name in shapesanity_names: for options in [shapesanity_simple, shapesanity_1_4, shapesanity_two_sided, shapesanity_three_parts, @@ -540,7 +540,7 @@ def addshapesanity_ut(shapesanity_names: List[str], add_alias: Callable[[str, st class ShapezLocation(Location): game = OTHER.game_name - def __init__(self, player: int, name: str, address: Optional[int], region: Region, + def __init__(self, player: int, name: str, address: int | None, region: Region, progress_type: LocationProgressType): super(ShapezLocation, self).__init__(player, name, address, region) self.progress_type = progress_type diff --git a/worlds/shapez/regions.py b/worlds/shapez/regions.py index c4ca1d0c..b5835461 100644 --- a/worlds/shapez/regions.py +++ b/worlds/shapez/regions.py @@ -1,5 +1,3 @@ -from typing import Dict, Tuple, List - from BaseClasses import Region, MultiWorld, LocationProgressType, ItemClassification, CollectionState from .items import ShapezItem from .locations import ShapezLocation @@ -102,7 +100,7 @@ def has_x_belt_multiplier(state: CollectionState, player: int, needed: float) -> return multiplier >= needed -def has_logic_list_building(state: CollectionState, player: int, buildings: List[str], index: int, +def has_logic_list_building(state: CollectionState, player: int, buildings: list[str], index: int, includeuseful: bool) -> bool: # Includes balancer, tunnel, and trash in logic in order to make them appear in earlier spheres @@ -126,11 +124,11 @@ def has_logic_list_building(state: CollectionState, player: int, buildings: List def create_shapez_regions(player: int, multiworld: MultiWorld, floating: bool, - included_locations: Dict[str, Tuple[str, LocationProgressType]], - location_name_to_id: Dict[str, int], level_logic_buildings: List[str], - upgrade_logic_buildings: List[str], early_useful: str, goal: str) -> List[Region]: + included_locations: dict[str, tuple[str, LocationProgressType]], + location_name_to_id: dict[str, int], level_logic_buildings: list[str], + upgrade_logic_buildings: list[str], early_useful: str, goal: str) -> list[Region]: """Creates and returns a list of all regions with entrances and all locations placed correctly.""" - regions: Dict[str, Region] = {name: Region(name, player, multiworld) for name in all_regions} + regions: dict[str, Region] = {name: Region(name, player, multiworld) for name in all_regions} # Creates ShapezLocations for every included location and puts them into the correct region for name, data in included_locations.items(): diff --git a/worlds/shapez/test/__init__.py b/worlds/shapez/test/__init__.py index d2dfad97..c8855be9 100644 --- a/worlds/shapez/test/__init__.py +++ b/worlds/shapez/test/__init__.py @@ -1,7 +1,7 @@ from unittest import TestCase from test.bases import WorldTestBase -from .. import options_presets, ShapezWorld +from .. import ShapezWorld from ..data.strings import GOALS, OTHER, ITEMS, LOCATIONS, CATEGORY, OPTIONS, SHAPESANITY from ..options import max_levels_and_upgrades, max_shapesanity