Generation improvements
Hera Lobby fix Added gauntlet 3 to cut carpet
This commit is contained in:
@@ -1419,10 +1419,9 @@ class Sector(object):
|
|||||||
self.branch_factor -= cnt_dead - 1
|
self.branch_factor -= cnt_dead - 1
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
for ent in region.entrances:
|
for ent in region.entrances:
|
||||||
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or ent.parent_region.name == 'Sewer Drop':
|
if (ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] and ent.parent_region.name != 'Menu') or ent.parent_region.name == 'Sewer Drop':
|
||||||
# same sector as another entrance
|
self.branch_factor += 1
|
||||||
if region.name not in ['Skull Pot Circle', 'Skull Back Drop', 'Desert East Lobby', 'Desert West Lobby']:
|
break # you only ever get one allowance for an entrance region, multiple entrances don't help
|
||||||
self.branch_factor += 1
|
|
||||||
return self.branch_factor
|
return self.branch_factor
|
||||||
|
|
||||||
def branches(self):
|
def branches(self):
|
||||||
|
|||||||
@@ -51,10 +51,7 @@ def link_doors(world, player):
|
|||||||
for portal in world.dungeon_portals[player]:
|
for portal in world.dungeon_portals[player]:
|
||||||
connect_portal(portal, world, player)
|
connect_portal(portal, world, player)
|
||||||
world.get_portal('Desert East', player).destination = True
|
world.get_portal('Desert East', player).destination = True
|
||||||
world.get_portal('Desert Back', player).deadEnd = True
|
|
||||||
world.get_portal('Skull 1', player).deadEnd = True
|
|
||||||
world.get_portal('Skull 2 West', player).destination = True
|
world.get_portal('Skull 2 West', player).destination = True
|
||||||
world.get_portal('Skull 3', player).deadEnd = True
|
|
||||||
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
|
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
|
||||||
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
|
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
|
||||||
|
|
||||||
@@ -343,10 +340,12 @@ def choose_portals(world, player):
|
|||||||
for portal in portal_list:
|
for portal in portal_list:
|
||||||
placeholder = world.get_region(portal + ' Placeholder', player)
|
placeholder = world.get_region(portal + ' Placeholder', player)
|
||||||
portal_region = placeholder.exits[0].connected_region
|
portal_region = placeholder.exits[0].connected_region
|
||||||
|
name = portal_region.name
|
||||||
if portal_region.type == RegionType.LightWorld:
|
if portal_region.type == RegionType.LightWorld:
|
||||||
world.get_portal(portal, player).light_world = True
|
world.get_portal(portal, player).light_world = True
|
||||||
if portal_region.name in world.inaccessible_regions[player]:
|
if name in world.inaccessible_regions[player]:
|
||||||
region_map[portal_region.name].append(portal)
|
name_key = 'Desert Ledge' if name == 'Desert Palace Entrance (North) Spot' else name
|
||||||
|
region_map[name_key].append(portal)
|
||||||
inaccessible_portals.append(portal)
|
inaccessible_portals.append(portal)
|
||||||
else:
|
else:
|
||||||
reachable_portals.append(portal)
|
reachable_portals.append(portal)
|
||||||
@@ -403,6 +402,9 @@ def choose_portals(world, player):
|
|||||||
if not sanctuary_door.entranceFlag:
|
if not sanctuary_door.entranceFlag:
|
||||||
world.get_room(0x12, player).delete(3)
|
world.get_room(0x12, player).delete(3)
|
||||||
world.get_room(0x12, player).change(2, DoorKind.NormalLow)
|
world.get_room(0x12, player).change(2, DoorKind.NormalLow)
|
||||||
|
hera_door = world.get_door('Hera Lobby S', player)
|
||||||
|
if not hera_door.entranceFlag:
|
||||||
|
world.get_room(0x77, player).change(0, DoorKind.NormalLow2)
|
||||||
|
|
||||||
if not world.swamp_patch_required[player]:
|
if not world.swamp_patch_required[player]:
|
||||||
swamp_region = world.get_entrance('Swamp Palace', player).connected_region
|
swamp_region = world.get_entrance('Swamp Palace', player).connected_region
|
||||||
@@ -425,10 +427,12 @@ def connect_portal(portal, world, player):
|
|||||||
else:
|
else:
|
||||||
edit_entrance = world.get_entrance(ent, player)
|
edit_entrance = world.get_entrance(ent, player)
|
||||||
entrance_region = portal_entrance.parent_region
|
entrance_region = portal_entrance.parent_region
|
||||||
|
old_region = edit_entrance.connected_region
|
||||||
edit_entrance.connected_region = entrance_region
|
edit_entrance.connected_region = entrance_region
|
||||||
entrance_region.exits.remove(portal_entrance)
|
entrance_region.exits.remove(portal_entrance)
|
||||||
entrance_region.exits.append(target_exit)
|
entrance_region.exits.append(target_exit)
|
||||||
entrance_region.entrances.append(edit_entrance)
|
entrance_region.entrances.append(edit_entrance)
|
||||||
|
old_region.entrances.remove(edit_entrance)
|
||||||
world.regions.remove(placeholder)
|
world.regions.remove(placeholder)
|
||||||
|
|
||||||
|
|
||||||
@@ -485,7 +489,7 @@ def assign_portal(candidates, possible_portals, world, player):
|
|||||||
old_door = portal.door
|
old_door = portal.door
|
||||||
if old_door:
|
if old_door:
|
||||||
old_door.entranceFlag = False
|
old_door.entranceFlag = False
|
||||||
if old_door.name not in ['Hyrule Castle Lobby S', 'Sanctuary S']:
|
if old_door.name not in ['Hyrule Castle Lobby S', 'Sanctuary S', 'Hera Lobby S']:
|
||||||
old_door_kind = DoorKind.NormalLow if old_door.layer or old_door.pseudo_bg else DoorKind.Normal
|
old_door_kind = DoorKind.NormalLow if old_door.layer or old_door.pseudo_bg else DoorKind.Normal
|
||||||
world.get_room(old_door.roomIndex, player).change(old_door.doorListPos, old_door_kind)
|
world.get_room(old_door.roomIndex, player).change(old_door.doorListPos, old_door_kind)
|
||||||
portal.change_door(candidate)
|
portal.change_door(candidate)
|
||||||
@@ -510,6 +514,7 @@ def clean_up_portal_assignment(portal_assignment, dungeon, portal, master_door_l
|
|||||||
def create_dungeon_entrances(world, player):
|
def create_dungeon_entrances(world, player):
|
||||||
entrance_map = defaultdict(list)
|
entrance_map = defaultdict(list)
|
||||||
split_map: DefaultDict[str, DefaultDict[str, List]] = defaultdict(lambda: defaultdict(list))
|
split_map: DefaultDict[str, DefaultDict[str, List]] = defaultdict(lambda: defaultdict(list))
|
||||||
|
originating: DefaultDict[str, DefaultDict[str, Dict]] = defaultdict(lambda: defaultdict(dict))
|
||||||
for key, portal_list in dungeon_portals.items():
|
for key, portal_list in dungeon_portals.items():
|
||||||
if world.mode[player] == 'standard' and key in standard_starts.keys():
|
if world.mode[player] == 'standard' and key in standard_starts.keys():
|
||||||
portal = world.get_portal('Hyrule Castle South', player)
|
portal = world.get_portal('Hyrule Castle South', player)
|
||||||
@@ -523,25 +528,36 @@ def create_dungeon_entrances(world, player):
|
|||||||
the_rest = []
|
the_rest = []
|
||||||
for portal_name in portal_list:
|
for portal_name in portal_list:
|
||||||
portal = world.get_portal(portal_name, player)
|
portal = world.get_portal(portal_name, player)
|
||||||
r_name = portal.door.entrance.parent_region.name
|
entrance_map[key].append(portal.door.entrance.parent_region.name)
|
||||||
entrance_map[key].append(r_name)
|
|
||||||
if portal.deadEnd:
|
if portal.deadEnd:
|
||||||
dead_ends.append(r_name)
|
dead_ends.append(portal)
|
||||||
elif portal.destination:
|
elif portal.destination:
|
||||||
destinations.append(r_name)
|
destinations.append(portal)
|
||||||
else:
|
else:
|
||||||
the_rest.append(r_name)
|
the_rest.append(portal)
|
||||||
choices = list(split_portals[key])
|
choices = list(split_portals[key])
|
||||||
for r_name in dead_ends:
|
for portal in dead_ends:
|
||||||
choice = random.choice(choices)
|
choice = random.choice(choices)
|
||||||
choices.remove(choice)
|
choices.remove(choice)
|
||||||
|
r_name = portal.door.entrance.parent_region.name
|
||||||
split_map[key][choice].append(r_name)
|
split_map[key][choice].append(r_name)
|
||||||
for r_name in the_rest:
|
for portal in the_rest:
|
||||||
|
if len(choices) == 0:
|
||||||
|
choices.append('Extra')
|
||||||
choice = random.choice(choices)
|
choice = random.choice(choices)
|
||||||
|
p_entrance = portal.door.entrance
|
||||||
|
r_name = p_entrance.parent_region.name
|
||||||
split_map[key][choice].append(r_name)
|
split_map[key][choice].append(r_name)
|
||||||
|
originating[key][choice][p_entrance.connected_region.name] = None
|
||||||
dest_choices = [x for x in choices if len(split_map[key][x]) > 0]
|
dest_choices = [x for x in choices if len(split_map[key][x]) > 0]
|
||||||
for r_name in destinations:
|
for portal in destinations:
|
||||||
choice = random.choice(dest_choices)
|
restricted = portal.door.entrance.connected_region.name in world.inaccessible_regions[player]
|
||||||
|
if restricted:
|
||||||
|
filtered_choices = [x for x in choices if any(y not in world.inaccessible_regions[player] for y in originating[key][x].keys())]
|
||||||
|
else:
|
||||||
|
filtered_choices = dest_choices
|
||||||
|
choice = random.choice(filtered_choices)
|
||||||
|
r_name = portal.door.entrance.parent_region.name
|
||||||
split_map[key][choice].append(r_name)
|
split_map[key][choice].append(r_name)
|
||||||
else:
|
else:
|
||||||
for portal_name in portal_list:
|
for portal_name in portal_list:
|
||||||
@@ -605,7 +621,10 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map,
|
|||||||
split_builders = split_dungeon_builder(builder, split_list, builder_info)
|
split_builders = split_dungeon_builder(builder, split_list, builder_info)
|
||||||
dungeon_builders.update(split_builders)
|
dungeon_builders.update(split_builders)
|
||||||
for sub_name, split_entrances in split_list.items():
|
for sub_name, split_entrances in split_list.items():
|
||||||
sub_builder = dungeon_builders[name+' '+sub_name]
|
key = name+' '+sub_name
|
||||||
|
if key not in dungeon_builders:
|
||||||
|
continue
|
||||||
|
sub_builder = dungeon_builders[key]
|
||||||
sub_builder.split_flag = True
|
sub_builder.split_flag = True
|
||||||
entrance_list = list(split_entrances)
|
entrance_list = list(split_entrances)
|
||||||
for ent in entrances_map[name]:
|
for ent in entrances_map[name]:
|
||||||
@@ -700,9 +719,10 @@ def determine_entrance_list_2(world, player):
|
|||||||
if parent.name not in world.inaccessible_regions[player]:
|
if parent.name not in world.inaccessible_regions[player]:
|
||||||
entrance_map[key].append(region_name)
|
entrance_map[key].append(region_name)
|
||||||
else:
|
else:
|
||||||
if ent.parent_region not in potential_entrances.keys():
|
if parent not in potential_entrances.keys():
|
||||||
potential_entrances[parent] = []
|
potential_entrances[parent] = []
|
||||||
potential_entrances[parent].append(region_name)
|
if region_name not in potential_entrances[parent]:
|
||||||
|
potential_entrances[parent].append(region_name)
|
||||||
connections[region_name] = parent
|
connections[region_name] = parent
|
||||||
return entrance_map, potential_entrances, connections
|
return entrance_map, potential_entrances, connections
|
||||||
|
|
||||||
@@ -1047,7 +1067,9 @@ def combine_layouts(recombinant_builders, dungeon_builders, entrances_map):
|
|||||||
|
|
||||||
|
|
||||||
def valid_region_to_explore(region, world, player):
|
def valid_region_to_explore(region, world, player):
|
||||||
return region and (region.type == RegionType.Dungeon or region.name in world.inaccessible_regions[player])
|
return region and (region.type == RegionType.Dungeon
|
||||||
|
or region.name in world.inaccessible_regions[player]
|
||||||
|
or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard'))
|
||||||
|
|
||||||
|
|
||||||
def shuffle_key_doors(builder, world, player):
|
def shuffle_key_doors(builder, world, player):
|
||||||
@@ -1503,6 +1525,9 @@ def valid_inaccessible_region(r):
|
|||||||
|
|
||||||
|
|
||||||
def add_inaccessible_doors(world, player):
|
def add_inaccessible_doors(world, player):
|
||||||
|
if world.mode[player] == 'standard':
|
||||||
|
create_door(world, player, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Ledge')
|
||||||
|
create_door(world, player, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Ledge')
|
||||||
# todo: ignore standard mode hyrule castle ledge?
|
# todo: ignore standard mode hyrule castle ledge?
|
||||||
for inaccessible_region in world.inaccessible_regions[player]:
|
for inaccessible_region in world.inaccessible_regions[player]:
|
||||||
region = world.get_region(inaccessible_region, player)
|
region = world.get_region(inaccessible_region, player)
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ class GraphPiece:
|
|||||||
# Dungeons shouldn't be generated until all entrances are appropriately accessible
|
# Dungeons shouldn't be generated until all entrances are appropriately accessible
|
||||||
def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
||||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||||
|
excluded = {}
|
||||||
|
for region in entrance_regions:
|
||||||
|
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||||
|
if portal and portal.destination:
|
||||||
|
excluded[region] = None
|
||||||
|
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
||||||
proposed_map = {}
|
proposed_map = {}
|
||||||
doors_to_connect = {}
|
doors_to_connect = {}
|
||||||
all_regions = set()
|
all_regions = set()
|
||||||
@@ -90,6 +96,12 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
name = builder.name
|
name = builder.name
|
||||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||||
|
excluded = {}
|
||||||
|
for region in entrance_regions:
|
||||||
|
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||||
|
if portal and portal.destination:
|
||||||
|
excluded[region] = None
|
||||||
|
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
||||||
doors_to_connect = {}
|
doors_to_connect = {}
|
||||||
all_regions = set()
|
all_regions = set()
|
||||||
bk_needed = False
|
bk_needed = False
|
||||||
@@ -1076,14 +1088,18 @@ def special_big_key_found(state, world, player):
|
|||||||
def valid_region_to_explore_in_regions(region, all_regions, world, player):
|
def valid_region_to_explore_in_regions(region, all_regions, world, player):
|
||||||
if region is None:
|
if region is None:
|
||||||
return False
|
return False
|
||||||
return (region.type == RegionType.Dungeon and region in all_regions) or region.name in world.inaccessible_regions[player]
|
return (region.type == RegionType.Dungeon and region in all_regions)\
|
||||||
|
or region.name in world.inaccessible_regions[player]\
|
||||||
|
or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard')
|
||||||
|
|
||||||
|
|
||||||
# cross-utility methods
|
# cross-utility methods
|
||||||
def valid_region_to_explore(region, name, world, player):
|
def valid_region_to_explore(region, name, world, player):
|
||||||
if region is None:
|
if region is None:
|
||||||
return False
|
return False
|
||||||
return (region.type == RegionType.Dungeon and region.dungeon.name in name) or region.name in world.inaccessible_regions[player]
|
return (region.type == RegionType.Dungeon and region.dungeon.name in name)\
|
||||||
|
or region.name in world.inaccessible_regions[player]\
|
||||||
|
or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard')
|
||||||
|
|
||||||
|
|
||||||
def get_doors(world, region, player):
|
def get_doors(world, region, player):
|
||||||
@@ -1159,9 +1175,7 @@ class DungeonBuilder(object):
|
|||||||
self.key_door_proposal = None
|
self.key_door_proposal = None
|
||||||
|
|
||||||
self.allowance = None
|
self.allowance = None
|
||||||
if name in dungeon_dead_end_allowance.keys():
|
if 'Stonewall' in name:
|
||||||
self.allowance = dungeon_dead_end_allowance[name]
|
|
||||||
elif 'Stonewall' in name:
|
|
||||||
self.allowance = 1
|
self.allowance = 1
|
||||||
elif 'Prewall' in name:
|
elif 'Prewall' in name:
|
||||||
orig_name = name[:-8]
|
orig_name = name[:-8]
|
||||||
@@ -2597,6 +2611,8 @@ def categorize_groupings(sectors):
|
|||||||
|
|
||||||
|
|
||||||
def valid_assignment(builder, sector_list, builder_info):
|
def valid_assignment(builder, sector_list, builder_info):
|
||||||
|
if not valid_entrance(builder, sector_list, builder_info):
|
||||||
|
return False
|
||||||
if not valid_c_switch(builder, sector_list):
|
if not valid_c_switch(builder, sector_list):
|
||||||
return False
|
return False
|
||||||
if not valid_polarized_assignment(builder, sector_list):
|
if not valid_polarized_assignment(builder, sector_list):
|
||||||
@@ -2605,6 +2621,34 @@ def valid_assignment(builder, sector_list, builder_info):
|
|||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
|
def valid_entrance(builder, sector_list, builder_info):
|
||||||
|
is_dead_end = False
|
||||||
|
if len(builder.sectors) == 0:
|
||||||
|
is_dead_end = True
|
||||||
|
else:
|
||||||
|
entrances, splits, world, player = builder_info
|
||||||
|
if builder.name not in entrances.keys():
|
||||||
|
name_parts = builder.name.rsplit(' ', 1)
|
||||||
|
entrance_list = splits[name_parts[0]][name_parts[1]]
|
||||||
|
entrances = []
|
||||||
|
for sector in builder.sectors:
|
||||||
|
if sector.is_entrance_sector():
|
||||||
|
sector.region_set()
|
||||||
|
entrances.append(sector)
|
||||||
|
all_dead = True
|
||||||
|
for sector in entrances:
|
||||||
|
for region in entrance_list:
|
||||||
|
if region in sector.region_set():
|
||||||
|
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == region))
|
||||||
|
if not portal.deadEnd:
|
||||||
|
all_dead = False
|
||||||
|
break
|
||||||
|
if not all_dead:
|
||||||
|
break
|
||||||
|
is_dead_end = all_dead
|
||||||
|
return len(sector_list) == 0 if is_dead_end else True
|
||||||
|
|
||||||
|
|
||||||
def valid_c_switch(builder, sector_list):
|
def valid_c_switch(builder, sector_list):
|
||||||
if builder.c_switch_present:
|
if builder.c_switch_present:
|
||||||
return True
|
return True
|
||||||
@@ -2699,17 +2743,38 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
builder.split_dungeon_map[name].valid_proposal = proposal
|
builder.split_dungeon_map[name].valid_proposal = proposal
|
||||||
return builder.split_dungeon_map # we made this earlier in gen, just use it
|
return builder.split_dungeon_map # we made this earlier in gen, just use it
|
||||||
|
|
||||||
attempts, comb_w_replace = 0, None
|
attempts, comb_w_replace, merge_attempt = 0, None, False
|
||||||
while attempts < 5: # does not solve coin flips 3% of the time
|
while attempts < 5: # does not solve coin flips 3% of the time
|
||||||
try:
|
try:
|
||||||
candidate_sectors = dict.fromkeys(builder.sectors)
|
candidate_sectors = dict.fromkeys(builder.sectors)
|
||||||
global_pole = GlobalPolarity(candidate_sectors)
|
global_pole = GlobalPolarity(candidate_sectors)
|
||||||
|
|
||||||
dungeon_map = {}
|
dungeon_map, sub_builder, merge_keys = {}, None, []
|
||||||
|
if merge_attempt:
|
||||||
|
candidates = []
|
||||||
|
for name, split_entrances in split_list.items():
|
||||||
|
if len(split_entrances) > 1:
|
||||||
|
candidates.append(name)
|
||||||
|
continue
|
||||||
|
elif len(split_entrances) <= 0:
|
||||||
|
continue
|
||||||
|
x, y, world, player = builder_info
|
||||||
|
r_name = split_entrances[0]
|
||||||
|
p = next(x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name)
|
||||||
|
if not p.deadEnd:
|
||||||
|
candidates.append(name)
|
||||||
|
merge_keys = random.sample(candidates, 2)
|
||||||
for name, split_entrances in split_list.items():
|
for name, split_entrances in split_list.items():
|
||||||
key = builder.name + ' ' + name
|
key = builder.name + ' ' + name
|
||||||
dungeon_map[key] = sub_builder = DungeonBuilder(key)
|
if merge_keys and name in merge_keys:
|
||||||
sub_builder.all_entrances = split_entrances
|
other_key = builder.name + ' ' + [x for x in merge_keys if x != name][0]
|
||||||
|
if other_key in dungeon_map:
|
||||||
|
key = other_key
|
||||||
|
sub_builder = dungeon_map[other_key]
|
||||||
|
sub_builder.all_entrances.extend(split_entrances)
|
||||||
|
if key not in dungeon_map:
|
||||||
|
dungeon_map[key] = sub_builder = DungeonBuilder(key)
|
||||||
|
sub_builder.all_entrances = list(split_entrances)
|
||||||
for r_name in split_entrances:
|
for r_name in split_entrances:
|
||||||
assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole)
|
assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole)
|
||||||
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
||||||
@@ -2719,6 +2784,9 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
attempts += 5 # all the combinations were tried already, no use repeating
|
attempts += 5 # all the combinations were tried already, no use repeating
|
||||||
else:
|
else:
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
if attempts >= 5 and not merge_attempt:
|
||||||
|
merge_attempt, attempts = True, 0
|
||||||
|
|
||||||
raise GenerationException('Unable to resolve in 5 attempts')
|
raise GenerationException('Unable to resolve in 5 attempts')
|
||||||
|
|
||||||
|
|
||||||
@@ -2735,8 +2803,8 @@ def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
|||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
chosen_sectors[choice].append(main_sector_list[i])
|
chosen_sectors[choice].append(main_sector_list[i])
|
||||||
all_valid = True
|
all_valid = True
|
||||||
for name, sector_list in chosen_sectors.items():
|
for name, builder in dungeon_map.items():
|
||||||
if not valid_assignment(dungeon_map[name], sector_list, builder_info):
|
if not valid_assignment(builder, chosen_sectors[name], builder_info):
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
if all_valid:
|
if all_valid:
|
||||||
@@ -3287,7 +3355,7 @@ def find_priority_equation(equations, access_id, current_access):
|
|||||||
if len(filtered_candidates) == 1:
|
if len(filtered_candidates) == 1:
|
||||||
return filtered_candidates[0]
|
return filtered_candidates[0]
|
||||||
|
|
||||||
neutral_candidates = [x for x in filtered_candidates if x[0].neutral_profit() or x[0].neutral()]
|
neutral_candidates = [x for x in filtered_candidates if (x[0].neutral_profit() or x[0].neutral()) and x[0].profit(current_access) == local_profit_map[x[2]]]
|
||||||
if len(neutral_candidates) == 0:
|
if len(neutral_candidates) == 0:
|
||||||
neutral_candidates = filtered_candidates
|
neutral_candidates = filtered_candidates
|
||||||
if len(neutral_candidates) == 1:
|
if len(neutral_candidates) == 1:
|
||||||
|
|||||||
2
Rom.py
2
Rom.py
@@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '98e8426901b441858631ae444c12d4fc'
|
RANDOMIZERBASEHASH = '7d4881c390855dbe2f9c308bed2e61ef'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
|
|||||||
25
Rules.py
25
Rules.py
@@ -42,7 +42,7 @@ def set_rules(world, player):
|
|||||||
elif world.goal[player] == 'ganon':
|
elif world.goal[player] == 'ganon':
|
||||||
# require aga2 to beat ganon
|
# require aga2 to beat ganon
|
||||||
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
|
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
|
||||||
|
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
set_big_bomb_rules(world, player)
|
set_big_bomb_rules(world, player)
|
||||||
else:
|
else:
|
||||||
@@ -593,7 +593,7 @@ def inverted_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player))
|
set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player))
|
||||||
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player))
|
||||||
|
|
||||||
set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player))
|
set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player))
|
||||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!)
|
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!)
|
||||||
|
|
||||||
set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player))
|
set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player))
|
||||||
@@ -608,7 +608,7 @@ def inverted_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
|
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
|
||||||
set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player))
|
set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!)
|
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!)
|
||||||
|
|
||||||
# new inverted spots
|
# new inverted spots
|
||||||
set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player))
|
set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player))
|
||||||
set_rule(world.get_entrance('Mire Mirror Spot', player), lambda state: state.has_Mirror(player))
|
set_rule(world.get_entrance('Mire Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||||
@@ -626,7 +626,7 @@ def inverted_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Graveyard Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
set_rule(world.get_entrance('Graveyard Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||||
set_rule(world.get_entrance('Bomb Hut Mirror Spot', player), lambda state: state.has_Mirror(player))
|
set_rule(world.get_entrance('Bomb Hut Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||||
set_rule(world.get_entrance('Skull Woods Mirror Spot', player), lambda state: state.has_Mirror(player))
|
set_rule(world.get_entrance('Skull Woods Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||||
|
|
||||||
# inverted flute spots
|
# inverted flute spots
|
||||||
|
|
||||||
set_rule(world.get_entrance('DDM Flute', player), lambda state: state.can_flute(player))
|
set_rule(world.get_entrance('DDM Flute', player), lambda state: state.can_flute(player))
|
||||||
@@ -639,7 +639,7 @@ def inverted_rules(world, player):
|
|||||||
set_rule(world.get_entrance('EDDM Flute', player), lambda state: state.can_flute(player))
|
set_rule(world.get_entrance('EDDM Flute', player), lambda state: state.can_flute(player))
|
||||||
set_rule(world.get_entrance('Dark Grassy Lawn Flute', player), lambda state: state.can_flute(player))
|
set_rule(world.get_entrance('Dark Grassy Lawn Flute', player), lambda state: state.can_flute(player))
|
||||||
set_rule(world.get_entrance('Hammer Peg Area Flute', player), lambda state: state.can_flute(player))
|
set_rule(world.get_entrance('Hammer Peg Area Flute', player), lambda state: state.can_flute(player))
|
||||||
|
|
||||||
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player])
|
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player])
|
||||||
set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic.
|
set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic.
|
||||||
|
|
||||||
@@ -776,7 +776,7 @@ def swordless_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
||||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
||||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player))
|
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player))
|
||||||
else:
|
else:
|
||||||
# only need ddm access for aga tower in inverted
|
# only need ddm access for aga tower in inverted
|
||||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
||||||
@@ -904,7 +904,9 @@ def find_rules_for_zelda_delivery(world, player):
|
|||||||
region, path_rules, path = queue.popleft()
|
region, path_rules, path = queue.popleft()
|
||||||
for ext in region.exits:
|
for ext in region.exits:
|
||||||
connect = ext.connected_region
|
connect = ext.connected_region
|
||||||
if connect and connect.type == RegionType.Dungeon and connect not in visited:
|
valid_region = connect and connect not in visited and\
|
||||||
|
(connect.type == RegionType.Dungeon or connect.name == 'Hyrule Castle Ledge')
|
||||||
|
if valid_region:
|
||||||
rule = ext.access_rule
|
rule = ext.access_rule
|
||||||
rule_list = list(path_rules)
|
rule_list = list(path_rules)
|
||||||
next_path = list(path)
|
next_path = list(path)
|
||||||
@@ -1262,7 +1264,7 @@ def set_inverted_big_bomb_rules(world, player):
|
|||||||
LW_bush_entrances = ['Bush Covered House',
|
LW_bush_entrances = ['Bush Covered House',
|
||||||
'Light World Bomb Hut',
|
'Light World Bomb Hut',
|
||||||
'Graveyard Cave']
|
'Graveyard Cave']
|
||||||
|
|
||||||
set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Inverted Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player))
|
set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Inverted Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player))
|
||||||
|
|
||||||
# crossing peg bridge starting from the southern dark world
|
# crossing peg bridge starting from the southern dark world
|
||||||
@@ -1562,7 +1564,8 @@ def add_key_logic_rules(world, player):
|
|||||||
for d_name, d_logic in key_logic.items():
|
for d_name, d_logic in key_logic.items():
|
||||||
for door_name, keys in d_logic.door_rules.items():
|
for door_name, keys in d_logic.door_rules.items():
|
||||||
spot = world.get_entrance(door_name, player)
|
spot = world.get_entrance(door_name, player)
|
||||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys))
|
if not world.retro[player] or world.mode[player] != 'standard' or not retro_in_hc(spot):
|
||||||
|
add_rule(spot, create_advanced_key_rule(d_logic, player, keys))
|
||||||
if keys.opposite:
|
if keys.opposite:
|
||||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys.opposite), 'or')
|
add_rule(spot, create_advanced_key_rule(d_logic, player, keys.opposite), 'or')
|
||||||
for location in d_logic.bk_restricted:
|
for location in d_logic.bk_restricted:
|
||||||
@@ -1576,6 +1579,10 @@ def add_key_logic_rules(world, player):
|
|||||||
add_rule(world.get_location(chest.name, player), create_rule(d_logic.bk_name, player))
|
add_rule(world.get_location(chest.name, player), create_rule(d_logic.bk_name, player))
|
||||||
|
|
||||||
|
|
||||||
|
def retro_in_hc(spot):
|
||||||
|
return spot.parent_region.dungeon.name == 'Hyrule Castle' if spot.parent_region.dungeon else False
|
||||||
|
|
||||||
|
|
||||||
def create_rule(item_name, player):
|
def create_rule(item_name, player):
|
||||||
return lambda state: state.has(item_name, player)
|
return lambda state: state.has(item_name, player)
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ def main(args=None):
|
|||||||
['Std ', ' --mode standard'],
|
['Std ', ' --mode standard'],
|
||||||
['Inv ', ' --mode inverted']]:
|
['Inv ', ' --mode inverted']]:
|
||||||
|
|
||||||
basecommand = f"py -{py_version} DungeonRandomizer.py --door_shuffle {args.dr} --intensity {args.tense} --suppress_rom --suppress_spoiler"
|
basecommand = f"py DungeonRandomizer.py --door_shuffle {args.dr} --intensity {args.tense} --suppress_rom --suppress_spoiler"
|
||||||
|
|
||||||
def gen_seed():
|
def gen_seed():
|
||||||
taskcommand = basecommand + " " + command + mode[1]
|
taskcommand = basecommand + " " + command + mode[1]
|
||||||
return subprocess.run(taskcommand, capture_output=True, shell=False, text=True)
|
return subprocess.run(taskcommand, capture_output=True, shell=True, text=True)
|
||||||
|
|
||||||
for x in range(1, max_attempts + 1):
|
for x in range(1, max_attempts + 1):
|
||||||
task = pool.submit(gen_seed)
|
task = pool.submit(gen_seed)
|
||||||
@@ -80,7 +80,7 @@ def main(args=None):
|
|||||||
success = f"{testname}{mode} Rate: {(len(alive) / len(dead_or_alive)) * 100:.2f}%"
|
success = f"{testname}{mode} Rate: {(len(alive) / len(dead_or_alive)) * 100:.2f}%"
|
||||||
successes.append(success)
|
successes.append(success)
|
||||||
print(success)
|
print(success)
|
||||||
result += f"{(len(alive)/len(dead_or_alive))*100:.2f}% "
|
result += f"{(len(alive)/len(dead_or_alive))*100:.2f}%\t"
|
||||||
return result.strip()
|
return result.strip()
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
196
Utils.py
196
Utils.py
@@ -457,7 +457,201 @@ def print_graph(world):
|
|||||||
ET.dump(root)
|
ET.dump(root)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_data_from_us_rom(rom):
|
||||||
|
with open(rom, 'rb') as stream:
|
||||||
|
rom_data = bytearray(stream.read())
|
||||||
|
|
||||||
|
rooms = [0x9a, 0x69, 0x78, 0x79, 0x7a, 0x88, 0x8a, 0xad]
|
||||||
|
for room in rooms:
|
||||||
|
b2idx = room*2
|
||||||
|
b3idx = room*3
|
||||||
|
headerptr = 0x110000 + b2idx # zscream specific
|
||||||
|
headerloc = rom_data[headerptr] + rom_data[headerptr+1]*0x100 + 0x108000 # zscream specific
|
||||||
|
header, objectdata, spritedata, secretdata = [], [], [], []
|
||||||
|
for i in range(0, 14):
|
||||||
|
header.append(rom_data[headerloc+i])
|
||||||
|
objectptr = 0xF8000 + b3idx
|
||||||
|
objectloc = rom_data[objectptr] + rom_data[objectptr+1]*0x100 + rom_data[objectptr+2]*0x10000
|
||||||
|
objectloc -= 0x100000
|
||||||
|
stop, idx = False, 0
|
||||||
|
ffcnt = 0
|
||||||
|
mode = 0
|
||||||
|
while ffcnt < 2:
|
||||||
|
b1 = rom_data[objectloc+idx]
|
||||||
|
b2 = rom_data[objectloc+idx+1]
|
||||||
|
b3 = rom_data[objectloc+idx+2]
|
||||||
|
objectdata.append(b1)
|
||||||
|
objectdata.append(b2)
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
ffcnt += 1
|
||||||
|
mode = 0
|
||||||
|
if b1 == 0xf0 and b2 == 0xff:
|
||||||
|
mode = 1
|
||||||
|
if not mode and ffcnt < 2:
|
||||||
|
objectdata.append(b3)
|
||||||
|
idx += 3
|
||||||
|
else:
|
||||||
|
idx += 2
|
||||||
|
spriteptr = 0x4d62e + b2idx
|
||||||
|
spriteloc = rom_data[spriteptr] + rom_data[spriteptr+1]*0x100 + 0x40000
|
||||||
|
done, idx = False, 0
|
||||||
|
while not done:
|
||||||
|
b1 = rom_data[spriteloc+idx]
|
||||||
|
spritedata.append(b1)
|
||||||
|
if b1 == 0xff:
|
||||||
|
done = True
|
||||||
|
idx += 1
|
||||||
|
secretptr = 0xdb69 + b2idx
|
||||||
|
secretloc = rom_data[secretptr] + rom_data[secretptr+1]*0x100
|
||||||
|
done, idx = False, 0
|
||||||
|
while not done:
|
||||||
|
b1 = rom_data[secretloc+idx]
|
||||||
|
b2 = rom_data[secretloc+idx+1]
|
||||||
|
b3 = rom_data[secretloc+idx+2]
|
||||||
|
secretdata.append(b1)
|
||||||
|
secretdata.append(b2)
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
done = True
|
||||||
|
else:
|
||||||
|
secretdata.append(b3)
|
||||||
|
idx += 3
|
||||||
|
|
||||||
|
print(f'Room {room:02x}')
|
||||||
|
print(f'db {",".join([f"${x:02x}" for x in header])}')
|
||||||
|
print(f'Obj Length: {len(objectdata)}')
|
||||||
|
print_data_block(objectdata)
|
||||||
|
print('Sprites')
|
||||||
|
print_data_block(spritedata)
|
||||||
|
print('Secrets')
|
||||||
|
print_data_block(secretdata)
|
||||||
|
|
||||||
|
blockdata, torchdata = [], []
|
||||||
|
blockloc = 0x271de
|
||||||
|
for i in range(0, 128):
|
||||||
|
idx = i*4
|
||||||
|
b1 = rom_data[blockloc+idx]
|
||||||
|
b2 = rom_data[blockloc+idx+1]
|
||||||
|
room_idx = b1 + b2*0x100
|
||||||
|
if room_idx in rooms:
|
||||||
|
blockdata.append(b1)
|
||||||
|
blockdata.append(b2)
|
||||||
|
blockdata.append(rom_data[blockloc+idx+2])
|
||||||
|
blockdata.append(rom_data[blockloc+idx+3])
|
||||||
|
torchloc = 0x2736A
|
||||||
|
nomatch = False
|
||||||
|
append = False
|
||||||
|
for i in range(0, 192):
|
||||||
|
idx = i*2
|
||||||
|
b1 = rom_data[torchloc+idx]
|
||||||
|
b2 = rom_data[torchloc+idx+1]
|
||||||
|
if nomatch:
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
nomatch = False
|
||||||
|
elif not append:
|
||||||
|
room_idx = b1 + b2*0x100
|
||||||
|
if room_idx in rooms:
|
||||||
|
append = True
|
||||||
|
else:
|
||||||
|
nomatch = True
|
||||||
|
if append:
|
||||||
|
torchdata.append(b1)
|
||||||
|
torchdata.append(b2)
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
append = False
|
||||||
|
print('Blocks')
|
||||||
|
print_data_block(blockdata)
|
||||||
|
print('Torches')
|
||||||
|
print_data_block(torchdata)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def print_data_block(block):
|
||||||
|
for i in range(0, len(block)//16 + 1):
|
||||||
|
slice = block[i*16:i*16+16]
|
||||||
|
print(f'db {",".join([f"${x:02x}" for x in slice])}')
|
||||||
|
|
||||||
|
|
||||||
|
def extract_data_from_jp_rom(rom):
|
||||||
|
with open(rom, 'rb') as stream:
|
||||||
|
rom_data = bytearray(stream.read())
|
||||||
|
|
||||||
|
rooms = [0x7b, 0x7c, 0x7d, 0x8b, 0x8c, 0x8d, 0x9b, 0x9c, 0x9d]
|
||||||
|
for room in rooms:
|
||||||
|
b2idx = room*2
|
||||||
|
b3idx = room*3
|
||||||
|
headerptr = 0x271e2 + b2idx
|
||||||
|
headerloc = rom_data[headerptr] + rom_data[headerptr+1]*0x100 + 0x18000
|
||||||
|
header, objectdata, spritedata, secretdata = [], [], [], []
|
||||||
|
for i in range(0, 14):
|
||||||
|
header.append(rom_data[headerloc+i])
|
||||||
|
objectptr = 0xF8000 + b3idx
|
||||||
|
objectloc = rom_data[objectptr] + rom_data[objectptr+1]*0x100 + rom_data[objectptr+2]*0x10000
|
||||||
|
objectloc -= 0x100000
|
||||||
|
stop, idx = False, 0
|
||||||
|
ffcnt = 0
|
||||||
|
mode = 0
|
||||||
|
while ffcnt < 2:
|
||||||
|
b1 = rom_data[objectloc+idx]
|
||||||
|
b2 = rom_data[objectloc+idx+1]
|
||||||
|
b3 = rom_data[objectloc+idx+2]
|
||||||
|
objectdata.append(b1)
|
||||||
|
objectdata.append(b2)
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
ffcnt += 1
|
||||||
|
mode = 0
|
||||||
|
if b1 == 0xf0 and b2 == 0xff:
|
||||||
|
mode = 1
|
||||||
|
if not mode and ffcnt < 2:
|
||||||
|
objectdata.append(b3)
|
||||||
|
idx += 3
|
||||||
|
else:
|
||||||
|
idx += 2
|
||||||
|
spriteptr = 0x4d62e + b2idx
|
||||||
|
spriteloc = rom_data[spriteptr] + rom_data[spriteptr+1]*0x100 + 0x40000
|
||||||
|
secretptr = 0xdb67 + b2idx
|
||||||
|
secretloc = rom_data[secretptr] + rom_data[secretptr+1]*0x100
|
||||||
|
done, idx = False, 0
|
||||||
|
while not done:
|
||||||
|
b1 = rom_data[spriteloc+idx]
|
||||||
|
spritedata.append(b1)
|
||||||
|
if b1 == 0xff:
|
||||||
|
done = True
|
||||||
|
idx += 1
|
||||||
|
done, idx = False, 0
|
||||||
|
while not done:
|
||||||
|
b1 = rom_data[secretloc+idx]
|
||||||
|
b2 = rom_data[secretloc+idx+1]
|
||||||
|
b3 = rom_data[secretloc+idx+2]
|
||||||
|
secretdata.append(b1)
|
||||||
|
secretdata.append(b2)
|
||||||
|
if b1 == 0xff and b2 == 0xff:
|
||||||
|
done = True
|
||||||
|
else:
|
||||||
|
secretdata.append(b3)
|
||||||
|
idx += 3
|
||||||
|
print(f'Room {room:02x}')
|
||||||
|
print(f'HeaderPtr {headerptr:06x}')
|
||||||
|
print(f'HeaderLoc {headerloc:06x}')
|
||||||
|
print(f'db {",".join([f"${x:02x}" for x in header])}')
|
||||||
|
print(f'Obj Length: {len(objectdata)}')
|
||||||
|
print(f'ObjectPtr {objectptr:06x}')
|
||||||
|
print(f'ObjectLoc {objectloc:06x}')
|
||||||
|
for i in range(0, len(objectdata)//16 + 1):
|
||||||
|
slice = objectdata[i*16:i*16+16]
|
||||||
|
print(f'db {",".join([f"${x:02x}" for x in slice])}')
|
||||||
|
print(f'SpritePtr {spriteptr:06x}')
|
||||||
|
print(f'SpriteLoc {spriteloc:06x}')
|
||||||
|
print_data_block(spritedata)
|
||||||
|
print(f'SecretPtr {secretptr:06x}')
|
||||||
|
print(f'SecretLoc {secretloc:06x}')
|
||||||
|
print_data_block(secretdata)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# make_new_base2current()
|
# make_new_base2current()
|
||||||
# read_entrance_data(old_rom=sys.argv[1])
|
# read_entrance_data(old_rom=sys.argv[1])
|
||||||
room_palette_data(old_rom=sys.argv[1])
|
# room_palette_data(old_rom=sys.argv[1])
|
||||||
|
extract_data_from_us_rom(sys.argv[1])
|
||||||
|
# extract_data_from_jp_rom(sys.argv[1])
|
||||||
|
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ SuctionOverworldFix:
|
|||||||
+ rtl
|
+ rtl
|
||||||
|
|
||||||
; TT Alcove, Mire bridges, pod falling, SW torch room, TR Pipe room, Bob's Room, Ice Many Pots, Mire Hub
|
; TT Alcove, Mire bridges, pod falling, SW torch room, TR Pipe room, Bob's Room, Ice Many Pots, Mire Hub
|
||||||
; swamp waterfall
|
; swamp waterfall, Gauntlet 3
|
||||||
CutoffRooms:
|
CutoffRooms:
|
||||||
db $bc, $a2, $1a, $49, $14, $8c, $9f, $c2
|
db $bc, $a2, $1a, $49, $14, $8c, $9f, $c2
|
||||||
db $66
|
db $66, $5d
|
||||||
|
|
||||||
CutoffEntranceRug:
|
CutoffEntranceRug:
|
||||||
pha : phx
|
pha : phx
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user