Skip to content

Commit 3c6d1f1

Browse files
committed
Type Fixes, More Cleanup
1 parent cbf50e8 commit 3c6d1f1

39 files changed

+455
-277
lines changed

Colors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208
}
209209

210210
# C Button Pause Menu C Cursor Pause Menu C Icon C Note
211-
c_button_colors: Dict[str, Color] = {
211+
c_button_colors: Dict[str, Tuple[Color, Color, Color, Color]] = {
212212
"N64 Blue": (Color(0x5A, 0x5A, 0xFF), Color(0x00, 0x32, 0xFF), Color(0x00, 0x64, 0xFF), Color(0x50, 0x96, 0xFF)),
213213
"N64 Green": (Color(0x00, 0x96, 0x00), Color(0x00, 0x96, 0x00), Color(0x00, 0x96, 0x00), Color(0x00, 0x96, 0x00)),
214214
"N64 Red": (Color(0xC8, 0x00, 0x00), Color(0xC8, 0x00, 0x00), Color(0xC8, 0x00, 0x00), Color(0xC8, 0x00, 0x00)),
@@ -378,7 +378,7 @@ def relative_luminance(color: List[int]) -> float:
378378
return color_ratios[0] * 0.299 + color_ratios[1] * 0.587 + color_ratios[2] * 0.114
379379

380380

381-
def lum_color_ratio(val: int) -> float:
381+
def lum_color_ratio(val: float) -> float:
382382
val /= 255
383383
if val <= 0.03928:
384384
return val / 12.92

Cosmetics.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def patch_dpad(rom: "Rom", settings: "Settings", log: 'CosmeticsLog', symbols: D
3333
rom.write_byte(symbols['CFG_DISPLAY_DPAD'], 0x01)
3434
else:
3535
rom.write_byte(symbols['CFG_DISPLAY_DPAD'], 0x00)
36-
log.display_dpad = settings.display_dpad
3736

3837

3938
def patch_dpad_info(rom: "Rom", settings: "Settings", log: 'CosmeticsLog', symbols: Dict[str, int]) -> None:
@@ -42,7 +41,6 @@ def patch_dpad_info(rom: "Rom", settings: "Settings", log: 'CosmeticsLog', symbo
4241
rom.write_byte(symbols['CFG_DPAD_DUNGEON_INFO_ENABLE'], 0x01)
4342
else:
4443
rom.write_byte(symbols['CFG_DPAD_DUNGEON_INFO_ENABLE'], 0x00)
45-
log.dpad_dungeon_menu = settings.dpad_dungeon_menu
4644

4745

4846
def patch_music(rom: "Rom", settings: "Settings", log: 'CosmeticsLog', symbols: Dict[str, int]) -> None:
@@ -954,7 +952,7 @@ def patch_music_changes(rom, settings, log, symbols):
954952
]
955953

956954
patch_sets: Dict[int, Dict[str, Any]] = {}
957-
global_patch_sets: List[Callable[["Rom", "Settings", 'CosmeticsLog', Dict[str, int]], type(None)]] = [
955+
global_patch_sets: List[Callable[["Rom", "Settings", 'CosmeticsLog', Dict[str, int]], None]] = [
958956
patch_targeting,
959957
patch_music,
960958
patch_tunic_colors,

Entrance.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, List, Optional, Callable, Dict, Any
1+
from typing import TYPE_CHECKING, List, Optional, Dict, Any
22

33
from RulesCommon import AccessRule
44

@@ -11,7 +11,7 @@ class Entrance:
1111
def __init__(self, name: str = '', parent: "Optional[Region]" = None) -> None:
1212
self.name: str = name
1313
self.parent_region: "Optional[Region]" = parent
14-
self.world: "World" = parent.world
14+
self.world: "Optional[World]" = parent.world if parent is not None else None
1515
self.connected_region: "Optional[Region]" = None
1616
self.access_rule: AccessRule = lambda state, **kwargs: True
1717
self.access_rules: List[AccessRule] = []
@@ -28,7 +28,7 @@ def __init__(self, name: str = '', parent: "Optional[Region]" = None) -> None:
2828

2929
def copy(self, new_region: "Region") -> 'Entrance':
3030
new_entrance = Entrance(self.name, new_region)
31-
new_entrance.connected_region = self.connected_region.name
31+
new_entrance.connected_region = self.connected_region.name # TODO: Revamp World/Region copying such that this is not a type error.
3232
new_entrance.access_rule = self.access_rule
3333
new_entrance.access_rules = list(self.access_rules)
3434
new_entrance.reverse = self.reverse
@@ -62,6 +62,8 @@ def connect(self, region: "Region") -> None:
6262
region.entrances.append(self)
6363

6464
def disconnect(self) -> "Optional[Region]":
65+
if self.connected_region is None:
66+
raise Exception(f"`disconnect()` called without a valid `connected_region` for entrance {self.name}.")
6567
self.connected_region.entrances.remove(self)
6668
previously_connected = self.connected_region
6769
self.connected_region = None
@@ -72,6 +74,10 @@ def bind_two_way(self, other_entrance: 'Entrance') -> None:
7274
other_entrance.reverse = self
7375

7476
def get_new_target(self) -> 'Entrance':
77+
if self.world is None:
78+
raise Exception(f"`get_new_target()` called without a valid `world` for entrance {self.name}.")
79+
if self.connected_region is None:
80+
raise Exception(f"`get_new_target()` called without a valid `connected_region` for entrance {self.name}.")
7581
root = self.world.get_region('Root Exits')
7682
target_entrance = Entrance('Root -> ' + self.connected_region.name, root)
7783
target_entrance.connect(self.connected_region)

EntranceShuffle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def assume_entrance_pool(entrance_pool: "List[Entrance]") -> "List[Entrance]":
4545
if entrance.reverse is not None:
4646
assumed_return = entrance.reverse.assume_reachable()
4747
if (entrance.type in ('Dungeon', 'Grotto', 'Grave') and entrance.reverse.name != 'Spirit Temple Lobby -> Desert Colossus From Spirit Lobby') or \
48-
(entrance.type == 'Interior' and entrance.world.shuffle_special_interior_entrances):
48+
(entrance.type == 'Interior' and entrance.world and entrance.world.shuffle_special_interior_entrances):
4949
# In most cases, Dungeon, Grotto/Grave and Simple Interior exits shouldn't be assumed able to give access to their parent region
5050
assumed_return.set_rule(lambda state, **kwargs: False)
5151
assumed_forward.bind_two_way(assumed_return)
@@ -444,6 +444,7 @@ def shuffle_random_entrances(worlds: "List[World]") -> None:
444444
non_drop_locations = [location for world in worlds for location in world.get_locations() if location.type not in ('Drop', 'Event')]
445445
max_search.visit_locations(non_drop_locations)
446446
locations_to_ensure_reachable = list(filter(max_search.visited, non_drop_locations))
447+
placed_one_way_entrances = None
447448

448449
# Shuffle all entrances within their own worlds
449450
for world in worlds:

Goals.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import sys
12
from collections import defaultdict
23
from typing import TYPE_CHECKING, List, Union, Dict, Optional, Any, Tuple, Iterable, Callable, Collection
34

45
from HintList import goalTable, get_hint_group, hint_exclusions
56
from ItemList import item_table
7+
from RulesCommon import AccessRule
68
from Search import Search, ValidGoals
7-
from Utils import TypeAlias
9+
10+
if sys.version_info >= (3, 10):
11+
from typing import TypeAlias
12+
else:
13+
TypeAlias = str
814

915
if TYPE_CHECKING:
1016
from Location import Location
@@ -274,7 +280,7 @@ def update_goal_items(spoiler: "Spoiler") -> None:
274280
spoiler.goal_locations = required_locations_dict
275281

276282

277-
def lock_category_entrances(category: GoalCategory, state_list: "Iterable[State]") -> "Dict[int, Dict[str, Callable[[State, ...], bool]]]":
283+
def lock_category_entrances(category: GoalCategory, state_list: "Iterable[State]") -> "Dict[int, Dict[str, AccessRule]]":
278284
# Disable access rules for specified entrances
279285
category_locks = {}
280286
if category.lock_entrances is not None:
@@ -287,7 +293,7 @@ def lock_category_entrances(category: GoalCategory, state_list: "Iterable[State]
287293
return category_locks
288294

289295

290-
def unlock_category_entrances(category_locks: "Dict[int, Dict[str, Callable[[State, ...], bool]]]",
296+
def unlock_category_entrances(category_locks: "Dict[int, Dict[str, AccessRule]]",
291297
state_list: "List[State]") -> None:
292298
# Restore access rules
293299
for state_id, exits in category_locks.items():
@@ -345,10 +351,11 @@ def search_goals(categories: Dict[str, GoalCategory], reachable_goals: ValidGoal
345351
if search_woth and not valid_goals['way of the hero']:
346352
required_locations['way of the hero'].append(location)
347353
location.item = old_item
348-
location.maybe_set_misc_item_hints()
354+
location.maybe_set_misc_hints()
349355
remaining_locations.remove(location)
350-
search.state_list[location.item.world.id].collect(location.item)
356+
if location.item.solver_id is not None:
357+
search.state_list[location.item.world.id].collect(location.item)
351358
for location in remaining_locations:
352359
# finally, collect unreachable locations for misc. item hints
353-
location.maybe_set_misc_item_hints()
360+
location.maybe_set_misc_hints()
354361
return required_locations

HintList.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727

2828
class Hint:
29-
def __init__(self, name: str, text: Union[str, List[str]], hint_type: Union[str, List[str]], choice: int = None) -> None:
29+
def __init__(self, name: str, text: Union[str, List[str]], hint_type: Union[str, List[str]], choice: Optional[int] = None) -> None:
3030
self.name: str = name
3131
self.type: List[str] = [hint_type] if not isinstance(hint_type, list) else hint_type
3232

@@ -290,7 +290,7 @@ def tokens_required_by_settings(world: "World") -> int:
290290
# \u00A9 Down arrow
291291
# \u00AA Joystick
292292

293-
hintTable: Dict[str, Tuple[List[str], Optional[str], Union[str, List[str]]]] = {
293+
hintTable: Dict[str, Tuple[Union[List[str], str], Optional[str], Union[str, List[str]]]] = {
294294
'Kokiri Emerald': (["a tree's farewell", "the Spiritual Stone of the Forest"], "the Kokiri Emerald", 'item'),
295295
'Goron Ruby': (["the Gorons' hidden treasure", "the Spiritual Stone of Fire"], "the Goron Ruby", 'item'),
296296
'Zora Sapphire': (["an engagement ring", "the Spiritual Stone of Water"], "the Zora Sapphire", 'item'),

Hints.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import os
55
import random
6+
import sys
67
import urllib.request
78
from collections import OrderedDict, defaultdict
89
from enum import Enum
@@ -16,7 +17,12 @@
1617
from Region import Region
1718
from Search import Search
1819
from TextBox import line_wrap
19-
from Utils import TypeAlias, data_path
20+
from Utils import data_path
21+
22+
if sys.version_info >= (3, 10):
23+
from typing import TypeAlias
24+
else:
25+
TypeAlias = str
2026

2127
if TYPE_CHECKING:
2228
from Entrance import Entrance
@@ -27,8 +33,8 @@
2733

2834
Spot: TypeAlias = "Union[Entrance, Location, Region]"
2935
HintReturn: TypeAlias = "Optional[Tuple[GossipText, Optional[List[Location]]]]"
30-
HintFunc: TypeAlias = "Callable[[Spoiler, World, MutableSet[str]], HintReturn]"
31-
BarrenFunc: TypeAlias = "Callable[[Spoiler, World, MutableSet[str], MutableSet[str]], HintReturn]"
36+
HintFunc: TypeAlias = Callable[["Spoiler", "World", MutableSet[str]], HintReturn]
37+
BarrenFunc: TypeAlias = Callable[["Spoiler", "World", MutableSet[str], MutableSet[str]], HintReturn]
3238

3339
bingoBottlesForHints: Set[str] = {
3440
"Bottle", "Bottle with Red Potion", "Bottle with Green Potion", "Bottle with Blue Potion",
@@ -467,8 +473,8 @@ def is_dungeon_item(self, item: Item) -> bool:
467473

468474
# Formats the hint text for this area with proper grammar.
469475
# Dungeons are hinted differently depending on the clearer_hints setting.
470-
def text(self, clearer_hints: bool, preposition: bool = False, world: "Optional[World]" = None) -> str:
471-
if self.is_dungeon:
476+
def text(self, clearer_hints: bool, preposition: bool = False, world: "Optional[int]" = None) -> str:
477+
if self.is_dungeon and self.dungeon_name:
472478
text = get_hint(self.dungeon_name, clearer_hints).text
473479
else:
474480
text = str(self)

IconManip.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import sys
12
from typing import TYPE_CHECKING, Sequence, MutableSequence, Optional
23

3-
from Utils import data_path, TypeAlias
4+
from Utils import data_path
5+
6+
if sys.version_info >= (3, 10):
7+
from typing import TypeAlias
8+
else:
9+
TypeAlias = str
410

511
if TYPE_CHECKING:
612
from Rom import Rom

Item.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, Optional, Tuple, List, Dict, Union, Iterable, Set, Any, Callable
1+
from typing import TYPE_CHECKING, Optional, Tuple, List, Dict, Union, Iterable, Set, Any, Callable, overload
22

33
from ItemList import item_table
44
from RulesCommon import allowed_globals, escape_name
@@ -14,7 +14,7 @@ class ItemInfo:
1414
bottles: Set[str] = set()
1515
medallions: Set[str] = set()
1616
stones: Set[str] = set()
17-
junk: Dict[str, int] = {}
17+
junk_weight: Dict[str, int] = {}
1818

1919
solver_ids: Dict[str, int] = {}
2020
bottle_ids: Set[int] = set()
@@ -44,7 +44,7 @@ def __init__(self, name: str = '', event: bool = False) -> None:
4444
self.junk: Optional[int] = self.special.get('junk', None)
4545
self.trade: bool = self.special.get('trade', False)
4646

47-
self.solver_id = None
47+
self.solver_id: Optional[int] = None
4848
if name and self.junk is None:
4949
esc = escape_name(name)
5050
if esc not in ItemInfo.solver_ids:
@@ -53,18 +53,18 @@ def __init__(self, name: str = '', event: bool = False) -> None:
5353

5454

5555
for item_name in item_table:
56-
ItemInfo.items[item_name] = ItemInfo(item_name)
57-
if ItemInfo.items[item_name].bottle:
56+
iteminfo = ItemInfo.items[item_name] = ItemInfo(item_name)
57+
if iteminfo.bottle:
5858
ItemInfo.bottles.add(item_name)
5959
ItemInfo.bottle_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
60-
if ItemInfo.items[item_name].medallion:
60+
if iteminfo.medallion:
6161
ItemInfo.medallions.add(item_name)
6262
ItemInfo.medallion_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
63-
if ItemInfo.items[item_name].stone:
63+
if iteminfo.stone:
6464
ItemInfo.stones.add(item_name)
6565
ItemInfo.stone_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
66-
if ItemInfo.items[item_name].junk is not None:
67-
ItemInfo.junk[item_name] = ItemInfo.items[item_name].junk
66+
if iteminfo.junk is not None:
67+
ItemInfo.junk_weight[item_name] = iteminfo.junk
6868

6969

7070
class Item:
@@ -79,7 +79,7 @@ def __init__(self, name: str = '', world: "Optional[World]" = None, event: bool
7979
else:
8080
self.info: ItemInfo = ItemInfo.items[name]
8181
self.price: Optional[int] = self.info.special.get('price', None)
82-
self.world: "World" = world
82+
self.world: "Optional[World]" = world
8383
self.looks_like_item: 'Optional[Item]' = None
8484
self.advancement: bool = self.info.advancement
8585
self.priority: bool = self.info.priority
@@ -88,9 +88,9 @@ def __init__(self, name: str = '', world: "Optional[World]" = None, event: bool
8888
self.index: Optional[int] = self.info.index
8989
self.alias: Optional[Tuple[str, int]] = self.info.alias
9090

91-
self.solver_id = self.info.solver_id
91+
self.solver_id: Optional[int] = self.info.solver_id
9292
# Do not alias to junk--it has no solver id!
93-
self.alias_id = ItemInfo.solver_ids[escape_name(self.alias[0])] if self.alias else None
93+
self.alias_id: Optional[int] = ItemInfo.solver_ids[escape_name(self.alias[0])] if self.alias else None
9494

9595
item_worlds_to_fix: 'Dict[Item, int]' = {}
9696

@@ -141,6 +141,8 @@ def dungeonitem(self) -> bool:
141141

142142
@property
143143
def unshuffled_dungeon_item(self) -> bool:
144+
if self.world is None:
145+
return False
144146
return ((self.type == 'SmallKey' and self.world.settings.shuffle_smallkeys in ('remove', 'vanilla', 'dungeon')) or
145147
(self.type == 'HideoutSmallKey' and self.world.settings.shuffle_hideoutkeys == 'vanilla') or
146148
(self.type == 'TCGSmallKey' and self.world.settings.shuffle_tcgkeys in ('remove', 'vanilla')) or
@@ -151,6 +153,8 @@ def unshuffled_dungeon_item(self) -> bool:
151153

152154
@property
153155
def majoritem(self) -> bool:
156+
if self.world is None:
157+
return False
154158
if self.type == 'Token':
155159
return (self.world.settings.bridge == 'tokens' or self.world.settings.shuffle_ganon_bosskey == 'tokens' or
156160
(self.world.settings.shuffle_ganon_bosskey == 'on_lacs' and self.world.settings.lacs_condition == 'tokens'))
@@ -184,6 +188,8 @@ def majoritem(self) -> bool:
184188

185189
@property
186190
def goalitem(self) -> bool:
191+
if self.world is None:
192+
return False
187193
return self.name in self.world.goal_items
188194

189195
def __str__(self) -> str:
@@ -193,6 +199,14 @@ def __unicode__(self) -> str:
193199
return '%s' % self.name
194200

195201

202+
@overload
203+
def ItemFactory(items: str, world: "Optional[World]" = None, event: bool = False) -> Item:
204+
pass
205+
206+
@overload
207+
def ItemFactory(items: Iterable[str], world: "Optional[World]" = None, event: bool = False) -> List[Item]:
208+
pass
209+
196210
def ItemFactory(items: Union[str, Iterable[str]], world: "Optional[World]" = None, event: bool = False) -> Union[Item, List[Item]]:
197211
if isinstance(items, str):
198212
if not event and items not in ItemInfo.items:
@@ -209,6 +223,8 @@ def ItemFactory(items: Union[str, Iterable[str]], world: "Optional[World]" = Non
209223

210224

211225
def make_event_item(name: str, location: "Location", item: Optional[Item] = None) -> Item:
226+
if location.world is None:
227+
raise Exception(f"`make_event_item` called with location '{location.name}' that doesn't have a world.")
212228
if item is None:
213229
item = Item(name, location.world, event=True)
214230
location.world.push_item(location, item)

ItemList.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# False -> Priority
55
# None -> Normal
66
# Item: (type, Progressive, GetItemID, special),
7-
item_table: Dict[str, Tuple[str, Optional[bool], int, Dict[str, Any]]] = {
7+
item_table: Dict[str, Tuple[str, Optional[bool], Optional[int], Optional[Dict[str, Any]]]] = {
88
'Bombs (5)': ('Item', None, 0x0001, {'junk': 8}),
99
'Deku Nuts (5)': ('Item', None, 0x0002, {'junk': 5}),
1010
'Bombchus (10)': ('Item', True, 0x0003, None),

0 commit comments

Comments
 (0)