diff --git a/BaseClasses.py b/BaseClasses.py index 895e02c3..cdeb86e4 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -41,7 +41,7 @@ class World(object): self.shuffle_bonk_prizes = False self.light_world_light_cone = False self.dark_world_light_cone = False - self.clock_mode = 'off' + self.clock_mode = 'none' self.rupoor_cost = 10 self.aga_randomness = True self.lock_aga_door_in_escape = False diff --git a/CLI.py b/CLI.py index b3bbe78b..92aa3571 100644 --- a/CLI.py +++ b/CLI.py @@ -188,7 +188,7 @@ def parse_arguments(argv, no_defaults=False): base game. ''') parser.add_argument('--experimental', default=defval(settings["experimental"] != 0), help='Enable experimental features', action='store_true') - parser.add_argument('--dungeon_counters', default=defval(settings["dungeon_counters"]), help='Enable dungeon chest counters', const='off', nargs='?', choices=['off', 'on', 'pickup']) + parser.add_argument('--dungeon_counters', default=defval(settings["dungeon_counters"]), help='Enable dungeon chest counters', const='off', nargs='?', choices=['off', 'on', 'pickup', 'default']) parser.add_argument('--crystals_ganon', default=defval(settings["crystals_ganon"]), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], help='''\ How many crystals are needed to defeat ganon. Any other @@ -358,7 +358,7 @@ def get_settings(): "keysanity": False, "door_shuffle": "basic", "experimental": 0, - "dungeon_counters": "off", + "dungeon_counters": "default", "multi": 1, "names": "", diff --git a/DoorShuffle.py b/DoorShuffle.py index b46e3a4b..8bd93efc 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -202,8 +202,7 @@ def connect_simple_door(world, exit_name, region_name, player): d.dest = region -def connect_door_only(world, exit_name, region_name, player): - region = world.get_region(region_name, player) +def connect_door_only(world, exit_name, region, player): d = world.check_for_door(exit_name, player) if d is not None: d.dest = region @@ -1357,8 +1356,8 @@ def add_inaccessible_doors(world, player): # todo: ignore standard mode hyrule castle ledge? for inaccessible_region in world.inaccessible_regions[player]: region = world.get_region(inaccessible_region, player) - for exit in region.exits: - create_door(world, player, exit.name, region.name) + for ext in region.exits: + create_door(world, player, ext.name, region.name) def create_door(world, player, entName, region_name): @@ -1464,6 +1463,7 @@ def check_for_pinball_fix(state, bad_region, world, player): @unique class DROptions(Flag): + NoOptions = 0x00 Eternal_Mini_Bosses = 0x01 # If on, GT minibosses marked as defeated when they try to spawn a heart Town_Portal = 0x02 # If on, Players will start with mirror scroll Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required diff --git a/ItemList.py b/ItemList.py index 971424fc..42e48179 100644 --- a/ItemList.py +++ b/ItemList.py @@ -177,6 +177,7 @@ def get_custom_array_key(item): key = label_switcher.get(key) return key + def generate_itempool(world, player): if (world.difficulty[player] not in ['normal', 'hard', 'expert'] or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'] or world.mode[player] not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']): @@ -194,7 +195,7 @@ def generate_itempool(world, player): region = world.get_region('Light World',player) loc = Location(player, "Murahdahla", parent=region) - loc.access_rule = lambda state: state.item_count(get_custom_array_key('Triforce Piece'), player) + state.item_count(get_custom_array_key('Power Star'), player) > state.world.treasure_hunt_count[player] + loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > state.world.treasure_hunt_count[player] region.locations.append(loc) world.dynamic_locations.append(loc) @@ -253,7 +254,6 @@ def generate_itempool(world, player): world.get_location('Zelda Drop Off', player).event = True world.get_location('Zelda Drop Off', player).locked = True - # set up item pool if world.custom: (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray) @@ -265,10 +265,10 @@ def generate_itempool(world, player): amt = world.pool_adjustment[player] if amt < 0: for i in range(0, amt): - pool.remove(get_custom_array_key('Rupees (20)')) + pool.remove('Rupees (20)') elif amt > 0: for i in range(0, amt): - pool.append(get_custom_array_key('Rupees (20)')) + pool.append('Rupees (20)') for item in precollected_items: world.push_precollected(ItemFactory(item, player)) @@ -407,6 +407,7 @@ def set_up_take_anys(world, player): world.initialize_regions() + def create_dynamic_shop_locations(world, player): for shop in world.shops: if shop.region.player == player: diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 9c3d07eb..4d82d257 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -705,7 +705,7 @@ def unique_doors(doors): def count_unique_sm_doors(doors): unique_d_set = set() for d in doors: - if d not in unique_d_set and d.dest not in unique_d_set and not d.bigKey: + if d not in unique_d_set and (d.dest not in unique_d_set or d.type == DoorType.SpiralStairs) and not d.bigKey: unique_d_set.add(d) return len(unique_d_set) @@ -718,7 +718,8 @@ def count_unique_small_doors(key_counter, proposal): if door in proposal and door not in counted: cnt += 1 counted.add(door) - counted.add(door.dest) + if door.type != DoorType.SpiralStairs: + counted.add(door.dest) return cnt @@ -1069,15 +1070,6 @@ def invalid_self_locking_key(state, prev_state, prev_avail, world, player): return prev_avail - 1 == 0 -# does not allow dest doors -def count_unique_sm_doors(doors): - unique_d_set = set() - for d in doors: - if d not in unique_d_set and d.dest not in unique_d_set and not d.bigKey: - unique_d_set.add(d) - return len(unique_d_set) - - def enough_small_locations(state, avail_small_loc): unique_d_set = set() for exp_door in state.small_doors: diff --git a/Main.py b/Main.py index 978d3e7e..4bf383a2 100644 --- a/Main.py +++ b/Main.py @@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute from ItemList import generate_itempool, difficulties, fill_prizes from Utils import output_path, parse_player_names -__version__ = '0.0.18dev' +__version__ = '0.0.18.3d' def main(args, seed=None): diff --git a/Mystery.py b/Mystery.py index 9937b5ad..d175be3e 100644 --- a/Mystery.py +++ b/Mystery.py @@ -148,6 +148,7 @@ def roll_settings(weights): ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' door_shuffle = get_choice('door_shuffle') ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla' + ret.experimental = get_choice('experimental') == 'on' goal = get_choice('goals') ret.goal = {'ganon': 'ganon', @@ -156,7 +157,7 @@ def roll_settings(weights): 'pedestal': 'pedestal', 'triforce-hunt': 'triforcehunt' }[goal] - ret.openpyramid = goal == 'fast_ganon' + ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False ret.crystals_gt = get_choice('tower_open') diff --git a/README.md b/README.md index 4b7e469d..531e1253 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,6 @@ Doors are shuffled between dungeons as well. Doors are not shuffled. -### Experimental - -Used for development testing. This will be removed in a future version. Use at your own risk. Might play like a plando. - ## Map/Compass/Small Key/Big Key shuffle (aka Keysanity) These settings allow dungeon specific items to be distributed anywhere in the world and not just in their native dungeon. diff --git a/Rom.py b/Rom.py index 73bb6de8..7f74a53f 100644 --- a/Rom.py +++ b/Rom.py @@ -591,7 +591,7 @@ def patch_rom(world, rom, player, team, enemized): patch_shuffled_dark_sanc(world, rom, player) # patch doors - dr_flags = DROptions.Eternal_Mini_Bosses if not world.experimental[player] else DROptions.Town_Portal + dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' or not world.experimental[player] else DROptions.Town_Portal if world.doorShuffle[player] == 'crossed': rom.write_byte(0x139004, 2) rom.write_byte(0x151f1, 2) @@ -899,7 +899,7 @@ def patch_rom(world, rom, player, team, enemized): ERtimeincrease = 20 if world.keyshuffle[player] or world.bigkeyshuffle[player] or world.mapshuffle[player]: ERtimeincrease = ERtimeincrease + 15 - if world.clock_mode == 'off': + if world.clock_mode == 'none': rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32) @@ -1157,7 +1157,7 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] else 0x00) # maps showing crystals on overworld # compasses showing dungeon count - if world.clock_mode != 'off' or world.dungeon_counters[player] == 'off': + if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off': rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location elif world.dungeon_counters[player] == 'on': rom.write_byte(0x18003C, 0x02) # always on diff --git a/gui/custom/overview.py b/gui/custom/overview.py index e7c7fa63..2c37a8bb 100644 --- a/gui/custom/overview.py +++ b/gui/custom/overview.py @@ -1,11 +1,12 @@ -from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y +from tkinter import ttk, Frame, N, LEFT, VERTICAL, Y import gui.widgets as widgets import json import os import classes.constants as CONST -def custom_page(top,parent): + +def custom_page(top, parent): # Custom Item Pool self = ttk.Frame(parent) @@ -31,17 +32,17 @@ def custom_page(top,parent): # Custom Item Pool option sections self.frames = {} - create_list_frame(self,"itemList1") + create_list_frame(self, "itemList1") create_vertical_rule(2) - create_list_frame(self,"itemList2") + create_list_frame(self, "itemList2") create_vertical_rule(2) - create_list_frame(self,"itemList3") + create_list_frame(self, "itemList3") create_vertical_rule(2) - create_list_frame(self,"itemList4") + create_list_frame(self, "itemList4") create_vertical_rule(2) - create_list_frame(self,"itemList5") + create_list_frame(self, "itemList5") - with open(os.path.join("resources","app","gui","custom","overview","widgets.json")) as widgetDefns: + with open(os.path.join("resources", "app", "gui", "custom", "overview", "widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename]) diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index 60255d8e..295f3098 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -37,9 +37,10 @@ "selectbox": { "side": "right" }, - "default": "Off" + "default": "Auto" }, "options": { + "Auto": "default", "Off": "off", "On": "on", "On Compass Pickup": "pickup"