- Added location name groups, so you can make your entire Water Temple priority to annoy everyone else - Significant improvement to ER generation success rate (~80% to >99%) - Changed `adult_trade_start` option to a choice option instead of a list (this shouldn't actually break any YAMLs though, due to the lesser-known property of lists parsing as a uniformly-weighted choice) - Major improvements to the option tooltips where needed. (Possibly too much text now) - Changed default hint distribution to `async` to help people's generation times. The tooltip explains that it removes WOTH hints so people hopefully don't get tripped up. - Makes stick and nut capacity upgrades useful items - Added shop prices and required trials to spoiler log - Added Cojiro to adult trade item group, because it had been forgotten previously - Fixed size-modified chests not being moved properly due to trap appearance changing the size - Fixed Thieves Hideout keyring not being allowed in start inventory - Fixed hint generation not accurately flagging barren locations on certain dungeon item shuffle settings - Fixed bug where you could plando arbitrarily-named items into the world, breaking everything
		
			
				
	
	
		
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from enum import Enum
 | 
						|
from .LocationList import location_table
 | 
						|
from BaseClasses import Location
 | 
						|
 | 
						|
non_indexed_location_types = {'Boss', 'Event', 'Drop', 'HintStone', 'Hint'}
 | 
						|
 | 
						|
location_id_offset = 67000
 | 
						|
locnames_pre_70 = {
 | 
						|
    "Gift from Sages",
 | 
						|
    "ZR Frogs Zeldas Lullaby",
 | 
						|
    "ZR Frogs Eponas Song",
 | 
						|
    "ZR Frogs Sarias Song",
 | 
						|
    "ZR Frogs Suns Song",
 | 
						|
    "ZR Frogs Song of Time",
 | 
						|
}
 | 
						|
loctypes_70 = {'Beehive', 'Pot', 'FlyingPot', 'Crate', 'SmallCrate', 'RupeeTower', 'Freestanding', 'ActorOverride'}
 | 
						|
new_name_order = sorted(location_table.keys(),
 | 
						|
    key=lambda name: 2 if location_table[name][0] in loctypes_70
 | 
						|
                else 1 if name in locnames_pre_70
 | 
						|
                else 0)
 | 
						|
 | 
						|
location_name_to_id = {name: (location_id_offset + index) for (index, name) in enumerate(new_name_order) 
 | 
						|
    if location_table[name][0] not in non_indexed_location_types}
 | 
						|
 | 
						|
class DisableType(Enum):
 | 
						|
    ENABLED  = 0
 | 
						|
    PENDING = 1
 | 
						|
    DISABLED = 2
 | 
						|
 | 
						|
class OOTLocation(Location): 
 | 
						|
    game: str = 'Ocarina of Time'
 | 
						|
 | 
						|
    def __init__(self, player, name='', code=None, address1=None, address2=None,
 | 
						|
        default=None, type='Chest', scene=None, parent=None, filter_tags=None,
 | 
						|
        internal=False, vanilla_item=None
 | 
						|
    ):
 | 
						|
        super(OOTLocation, self).__init__(player, name, code, parent)
 | 
						|
        self.address1 = address1
 | 
						|
        self.address2 = address2
 | 
						|
        self.default = default
 | 
						|
        self.type = type
 | 
						|
        self.scene = scene
 | 
						|
        self.internal = internal
 | 
						|
        self.vanilla_item = vanilla_item
 | 
						|
        if filter_tags is None: 
 | 
						|
            self.filter_tags = None
 | 
						|
        else: 
 | 
						|
            self.filter_tags = list(filter_tags)
 | 
						|
        self.never = False # no idea what this does
 | 
						|
        self.disabled = DisableType.ENABLED
 | 
						|
 | 
						|
        if type == 'Event': 
 | 
						|
            self.event = True
 | 
						|
 | 
						|
    @property
 | 
						|
    def dungeon(self):
 | 
						|
        return self.parent_region.dungeon
 | 
						|
 | 
						|
 | 
						|
def LocationFactory(locations, player: int):
 | 
						|
    ret = []
 | 
						|
    singleton = False
 | 
						|
    if isinstance(locations, str):
 | 
						|
        locations = [locations]
 | 
						|
        singleton = True
 | 
						|
    for location in locations:
 | 
						|
        if location in location_table:
 | 
						|
            match_location = location
 | 
						|
        else:
 | 
						|
            match_location = next(filter(lambda k: k.lower() == location.lower(), location_table), None)
 | 
						|
        if match_location:
 | 
						|
            type, scene, default, addresses, vanilla_item, filter_tags = location_table[match_location]
 | 
						|
            if addresses is None:
 | 
						|
                addresses = (None, None)
 | 
						|
            address1, address2 = addresses
 | 
						|
            ret.append(OOTLocation(player, match_location,
 | 
						|
                location_name_to_id.get(match_location, None),
 | 
						|
                address1, address2, default, type, scene,
 | 
						|
                filter_tags=filter_tags, vanilla_item=vanilla_item))
 | 
						|
        else:
 | 
						|
            raise KeyError('Unknown Location: %s', location)
 | 
						|
 | 
						|
    if singleton:
 | 
						|
        return ret[0]
 | 
						|
    return ret
 | 
						|
 | 
						|
 | 
						|
def build_location_name_groups() -> dict:
 | 
						|
 | 
						|
    def fix_sing(t) -> tuple:
 | 
						|
        if isinstance(t, str):
 | 
						|
            return (t,)
 | 
						|
        return t
 | 
						|
 | 
						|
    def rename(d, k1, k2) -> None:
 | 
						|
        d[k2] = d[k1]
 | 
						|
        del d[k1]
 | 
						|
 | 
						|
    # whoever wrote the location table didn't realize they need to add a comma to mark a singleton as a tuple
 | 
						|
    # so we have to check types unfortunately
 | 
						|
    tags = set()
 | 
						|
    for v in location_table.values():
 | 
						|
        if v[5] is not None:
 | 
						|
            tags.update(fix_sing(v[5]))
 | 
						|
 | 
						|
    sorted_tags = sorted(list(tags))
 | 
						|
 | 
						|
    ret = {
 | 
						|
        tag: {k for k, v in location_table.items()
 | 
						|
        if v[5] is not None
 | 
						|
            and tag in fix_sing(v[5])
 | 
						|
            and v[0] not in non_indexed_location_types}
 | 
						|
        for tag in sorted_tags
 | 
						|
    }
 | 
						|
 | 
						|
    # Delete tags which are a combination of other tags
 | 
						|
    del ret['Death Mountain']
 | 
						|
    del ret['Forest']
 | 
						|
    del ret['Gerudo']
 | 
						|
    del ret['Kakariko']
 | 
						|
    del ret['Market']
 | 
						|
 | 
						|
    # Delete Vanilla and MQ tags because they are just way too broad
 | 
						|
    del ret['Vanilla']
 | 
						|
    del ret['Master Quest']
 | 
						|
 | 
						|
    rename(ret, 'Beehive', 'Beehives')
 | 
						|
    rename(ret, 'Cow', 'Cows')
 | 
						|
    rename(ret, 'Crate', 'Crates')
 | 
						|
    rename(ret, 'Deku Scrub', 'Deku Scrubs')
 | 
						|
    rename(ret, 'FlyingPot', 'Flying Pots')
 | 
						|
    rename(ret, 'Freestanding', 'Freestanding Items')
 | 
						|
    rename(ret, 'Pot', 'Pots')
 | 
						|
    rename(ret, 'RupeeTower', 'Rupee Groups')
 | 
						|
    rename(ret, 'SmallCrate', 'Small Crates')
 | 
						|
    rename(ret, 'the Market', 'Market')
 | 
						|
    rename(ret, 'the Graveyard', 'Graveyard')
 | 
						|
    rename(ret, 'the Lost Woods', 'Lost Woods')
 | 
						|
 | 
						|
    return ret
 | 
						|
 |