Skip to content

Commit

Permalink
Type Fixes, More Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Cuphat committed May 19, 2023
1 parent cbf50e8 commit 3c6d1f1
Show file tree
Hide file tree
Showing 39 changed files with 455 additions and 277 deletions.
4 changes: 2 additions & 2 deletions Colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
}

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


def lum_color_ratio(val: int) -> float:
def lum_color_ratio(val: float) -> float:
val /= 255
if val <= 0.03928:
return val / 12.92
Expand Down
4 changes: 1 addition & 3 deletions Cosmetics.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def patch_dpad(rom: "Rom", settings: "Settings", log: 'CosmeticsLog', symbols: D
rom.write_byte(symbols['CFG_DISPLAY_DPAD'], 0x01)
else:
rom.write_byte(symbols['CFG_DISPLAY_DPAD'], 0x00)
log.display_dpad = settings.display_dpad


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


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

patch_sets: Dict[int, Dict[str, Any]] = {}
global_patch_sets: List[Callable[["Rom", "Settings", 'CosmeticsLog', Dict[str, int]], type(None)]] = [
global_patch_sets: List[Callable[["Rom", "Settings", 'CosmeticsLog', Dict[str, int]], None]] = [
patch_targeting,
patch_music,
patch_tunic_colors,
Expand Down
12 changes: 9 additions & 3 deletions Entrance.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, List, Optional, Callable, Dict, Any
from typing import TYPE_CHECKING, List, Optional, Dict, Any

from RulesCommon import AccessRule

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

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

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

def get_new_target(self) -> 'Entrance':
if self.world is None:
raise Exception(f"`get_new_target()` called without a valid `world` for entrance {self.name}.")
if self.connected_region is None:
raise Exception(f"`get_new_target()` called without a valid `connected_region` for entrance {self.name}.")
root = self.world.get_region('Root Exits')
target_entrance = Entrance('Root -> ' + self.connected_region.name, root)
target_entrance.connect(self.connected_region)
Expand Down
3 changes: 2 additions & 1 deletion EntranceShuffle.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def assume_entrance_pool(entrance_pool: "List[Entrance]") -> "List[Entrance]":
if entrance.reverse is not None:
assumed_return = entrance.reverse.assume_reachable()
if (entrance.type in ('Dungeon', 'Grotto', 'Grave') and entrance.reverse.name != 'Spirit Temple Lobby -> Desert Colossus From Spirit Lobby') or \
(entrance.type == 'Interior' and entrance.world.shuffle_special_interior_entrances):
(entrance.type == 'Interior' and entrance.world and entrance.world.shuffle_special_interior_entrances):
# In most cases, Dungeon, Grotto/Grave and Simple Interior exits shouldn't be assumed able to give access to their parent region
assumed_return.set_rule(lambda state, **kwargs: False)
assumed_forward.bind_two_way(assumed_return)
Expand Down Expand Up @@ -444,6 +444,7 @@ def shuffle_random_entrances(worlds: "List[World]") -> None:
non_drop_locations = [location for world in worlds for location in world.get_locations() if location.type not in ('Drop', 'Event')]
max_search.visit_locations(non_drop_locations)
locations_to_ensure_reachable = list(filter(max_search.visited, non_drop_locations))
placed_one_way_entrances = None

# Shuffle all entrances within their own worlds
for world in worlds:
Expand Down
19 changes: 13 additions & 6 deletions Goals.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import sys
from collections import defaultdict
from typing import TYPE_CHECKING, List, Union, Dict, Optional, Any, Tuple, Iterable, Callable, Collection

from HintList import goalTable, get_hint_group, hint_exclusions
from ItemList import item_table
from RulesCommon import AccessRule
from Search import Search, ValidGoals
from Utils import TypeAlias

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = str

if TYPE_CHECKING:
from Location import Location
Expand Down Expand Up @@ -274,7 +280,7 @@ def update_goal_items(spoiler: "Spoiler") -> None:
spoiler.goal_locations = required_locations_dict


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


def unlock_category_entrances(category_locks: "Dict[int, Dict[str, Callable[[State, ...], bool]]]",
def unlock_category_entrances(category_locks: "Dict[int, Dict[str, AccessRule]]",
state_list: "List[State]") -> None:
# Restore access rules
for state_id, exits in category_locks.items():
Expand Down Expand Up @@ -345,10 +351,11 @@ def search_goals(categories: Dict[str, GoalCategory], reachable_goals: ValidGoal
if search_woth and not valid_goals['way of the hero']:
required_locations['way of the hero'].append(location)
location.item = old_item
location.maybe_set_misc_item_hints()
location.maybe_set_misc_hints()
remaining_locations.remove(location)
search.state_list[location.item.world.id].collect(location.item)
if location.item.solver_id is not None:
search.state_list[location.item.world.id].collect(location.item)
for location in remaining_locations:
# finally, collect unreachable locations for misc. item hints
location.maybe_set_misc_item_hints()
location.maybe_set_misc_hints()
return required_locations
4 changes: 2 additions & 2 deletions HintList.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@


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

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

hintTable: Dict[str, Tuple[List[str], Optional[str], Union[str, List[str]]]] = {
hintTable: Dict[str, Tuple[Union[List[str], str], Optional[str], Union[str, List[str]]]] = {
'Kokiri Emerald': (["a tree's farewell", "the Spiritual Stone of the Forest"], "the Kokiri Emerald", 'item'),
'Goron Ruby': (["the Gorons' hidden treasure", "the Spiritual Stone of Fire"], "the Goron Ruby", 'item'),
'Zora Sapphire': (["an engagement ring", "the Spiritual Stone of Water"], "the Zora Sapphire", 'item'),
Expand Down
16 changes: 11 additions & 5 deletions Hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import os
import random
import sys
import urllib.request
from collections import OrderedDict, defaultdict
from enum import Enum
Expand All @@ -16,7 +17,12 @@
from Region import Region
from Search import Search
from TextBox import line_wrap
from Utils import TypeAlias, data_path
from Utils import data_path

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = str

if TYPE_CHECKING:
from Entrance import Entrance
Expand All @@ -27,8 +33,8 @@

Spot: TypeAlias = "Union[Entrance, Location, Region]"
HintReturn: TypeAlias = "Optional[Tuple[GossipText, Optional[List[Location]]]]"
HintFunc: TypeAlias = "Callable[[Spoiler, World, MutableSet[str]], HintReturn]"
BarrenFunc: TypeAlias = "Callable[[Spoiler, World, MutableSet[str], MutableSet[str]], HintReturn]"
HintFunc: TypeAlias = Callable[["Spoiler", "World", MutableSet[str]], HintReturn]
BarrenFunc: TypeAlias = Callable[["Spoiler", "World", MutableSet[str], MutableSet[str]], HintReturn]

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

# Formats the hint text for this area with proper grammar.
# Dungeons are hinted differently depending on the clearer_hints setting.
def text(self, clearer_hints: bool, preposition: bool = False, world: "Optional[World]" = None) -> str:
if self.is_dungeon:
def text(self, clearer_hints: bool, preposition: bool = False, world: "Optional[int]" = None) -> str:
if self.is_dungeon and self.dungeon_name:
text = get_hint(self.dungeon_name, clearer_hints).text
else:
text = str(self)
Expand Down
8 changes: 7 additions & 1 deletion IconManip.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import sys
from typing import TYPE_CHECKING, Sequence, MutableSequence, Optional

from Utils import data_path, TypeAlias
from Utils import data_path

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
TypeAlias = str

if TYPE_CHECKING:
from Rom import Rom
Expand Down
40 changes: 28 additions & 12 deletions Item.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Optional, Tuple, List, Dict, Union, Iterable, Set, Any, Callable
from typing import TYPE_CHECKING, Optional, Tuple, List, Dict, Union, Iterable, Set, Any, Callable, overload

from ItemList import item_table
from RulesCommon import allowed_globals, escape_name
Expand All @@ -14,7 +14,7 @@ class ItemInfo:
bottles: Set[str] = set()
medallions: Set[str] = set()
stones: Set[str] = set()
junk: Dict[str, int] = {}
junk_weight: Dict[str, int] = {}

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

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


for item_name in item_table:
ItemInfo.items[item_name] = ItemInfo(item_name)
if ItemInfo.items[item_name].bottle:
iteminfo = ItemInfo.items[item_name] = ItemInfo(item_name)
if iteminfo.bottle:
ItemInfo.bottles.add(item_name)
ItemInfo.bottle_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
if ItemInfo.items[item_name].medallion:
if iteminfo.medallion:
ItemInfo.medallions.add(item_name)
ItemInfo.medallion_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
if ItemInfo.items[item_name].stone:
if iteminfo.stone:
ItemInfo.stones.add(item_name)
ItemInfo.stone_ids.add(ItemInfo.solver_ids[escape_name(item_name)])
if ItemInfo.items[item_name].junk is not None:
ItemInfo.junk[item_name] = ItemInfo.items[item_name].junk
if iteminfo.junk is not None:
ItemInfo.junk_weight[item_name] = iteminfo.junk


class Item:
Expand All @@ -79,7 +79,7 @@ def __init__(self, name: str = '', world: "Optional[World]" = None, event: bool
else:
self.info: ItemInfo = ItemInfo.items[name]
self.price: Optional[int] = self.info.special.get('price', None)
self.world: "World" = world
self.world: "Optional[World]" = world
self.looks_like_item: 'Optional[Item]' = None
self.advancement: bool = self.info.advancement
self.priority: bool = self.info.priority
Expand All @@ -88,9 +88,9 @@ def __init__(self, name: str = '', world: "Optional[World]" = None, event: bool
self.index: Optional[int] = self.info.index
self.alias: Optional[Tuple[str, int]] = self.info.alias

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

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

Expand Down Expand Up @@ -141,6 +141,8 @@ def dungeonitem(self) -> bool:

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

@property
def majoritem(self) -> bool:
if self.world is None:
return False
if self.type == 'Token':
return (self.world.settings.bridge == 'tokens' or self.world.settings.shuffle_ganon_bosskey == 'tokens' or
(self.world.settings.shuffle_ganon_bosskey == 'on_lacs' and self.world.settings.lacs_condition == 'tokens'))
Expand Down Expand Up @@ -184,6 +188,8 @@ def majoritem(self) -> bool:

@property
def goalitem(self) -> bool:
if self.world is None:
return False
return self.name in self.world.goal_items

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


@overload
def ItemFactory(items: str, world: "Optional[World]" = None, event: bool = False) -> Item:
pass

@overload
def ItemFactory(items: Iterable[str], world: "Optional[World]" = None, event: bool = False) -> List[Item]:
pass

def ItemFactory(items: Union[str, Iterable[str]], world: "Optional[World]" = None, event: bool = False) -> Union[Item, List[Item]]:
if isinstance(items, str):
if not event and items not in ItemInfo.items:
Expand All @@ -209,6 +223,8 @@ def ItemFactory(items: Union[str, Iterable[str]], world: "Optional[World]" = Non


def make_event_item(name: str, location: "Location", item: Optional[Item] = None) -> Item:
if location.world is None:
raise Exception(f"`make_event_item` called with location '{location.name}' that doesn't have a world.")
if item is None:
item = Item(name, location.world, event=True)
location.world.push_item(location, item)
Expand Down
2 changes: 1 addition & 1 deletion ItemList.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# False -> Priority
# None -> Normal
# Item: (type, Progressive, GetItemID, special),
item_table: Dict[str, Tuple[str, Optional[bool], int, Dict[str, Any]]] = {
item_table: Dict[str, Tuple[str, Optional[bool], Optional[int], Optional[Dict[str, Any]]]] = {
'Bombs (5)': ('Item', None, 0x0001, {'junk': 8}),
'Deku Nuts (5)': ('Item', None, 0x0002, {'junk': 5}),
'Bombchus (10)': ('Item', True, 0x0003, None),
Expand Down
Loading

0 comments on commit 3c6d1f1

Please sign in to comment.