shapez: Typing Cleanup + Small Docs Rewordings (#5189)

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
BlastSlimey
2025-07-28 17:01:57 +02:00
committed by GitHub
parent 4d17366662
commit ad17c7fd21
13 changed files with 144 additions and 143 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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"]:

View File

@@ -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,7 +18,8 @@ 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`.
@@ -28,7 +29,7 @@ Alle Spiele in Archipelago müssen eine Liste aller möglichen Locations **unabh
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

View File

@@ -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 `<AP installation>/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.

View File

@@ -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
- Upgrades
- Große Upgrades (addiert 1 zum Geschwindigkeitsmultiplikator)
- Kleine Upgrades (addiert 0.1 zum Geschwindigkeitsmultiplikator)
- Andere ungewöhnliche Upgrades (optional)
- 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)

View File

@@ -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.

View File

@@ -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("<Nachricht>"")` schreibst)
- Universal Tracker (schau im Kanal von UT auf dem Discord-Server nach der aktuellen Anleitung und für weitere
Informationen)
## Installation

View File

@@ -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("<message>"")`)
- Universal Tracker (check UT's channel in the discord server for more information and instructions)
## Installation

View File

@@ -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)

View File

@@ -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

View File

@@ -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():

View File

@@ -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