## What is this fixing or adding? - Adds the majority of OoTR 7.0 features: - Pot shuffle, Freestanding item shuffle, Crate shuffle, Beehive shuffle - Key rings mode - Dungeon shortcuts to speed up dungeons - "Regional" shuffle for dungeon items - New options for shop pricing in shopsanity - Expanded Ganon's Boss Key shuffle options - Pre-planted beans - Improved Chest Appearance Matches Contents mode - Blue Fire Arrows - Bonk self-damage - Finer control over MQ dungeons and spawn position randomization - Several bugfixes as a result of the update: - Items recognized by the server and valid starting items are now in a 1-to-1 correspondence. In particular, starting with keys is now supported. - Entrance randomization success rate improved. Hopefully it is now at 100%. Co-authored-by: Zach Parks <zach@alliware.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from math import ceil
 | 
						|
 | 
						|
from .LocationList import location_table
 | 
						|
 | 
						|
# Create a dict of dicts of the format:
 | 
						|
# {
 | 
						|
#   scene_number_n : {
 | 
						|
#       room_setup_number: max_flags
 | 
						|
#   }
 | 
						|
# }
 | 
						|
# where room_setup_number defines the room + scene setup as ((setup << 6) + room) for scene n
 | 
						|
# and max_flags is the highest used enemy flag for that setup/room
 | 
						|
def get_collectible_flag_table(world):
 | 
						|
    scene_flags = {}
 | 
						|
    alt_list = []
 | 
						|
    for i in range(0, 101):
 | 
						|
        max_room_num = 0
 | 
						|
        max_enemy_flag = 0
 | 
						|
        scene_flags[i] = {}
 | 
						|
        for location in world.get_locations():
 | 
						|
            if(location.scene == i and location.type in ["Freestanding", "Pot", "FlyingPot", "Crate", "SmallCrate", "Beehive", "RupeeTower"]):
 | 
						|
                default = location.default
 | 
						|
                if(isinstance(default, list)): #List of alternative room/setup/flag to use
 | 
						|
                    primary_tuple = default[0]
 | 
						|
                    for c in range(1,len(default)):
 | 
						|
                        alt_list.append((location, default[c], primary_tuple))
 | 
						|
                    default = location.default[0] #Use the first tuple as the primary tuple
 | 
						|
                if(isinstance(default, tuple)):
 | 
						|
                    room, setup, flag = default
 | 
						|
                    room_setup = room + (setup << 6)
 | 
						|
                    if(room_setup in scene_flags[i].keys()):
 | 
						|
                        curr_room_max_flag = scene_flags[i][room_setup]
 | 
						|
                        if flag > curr_room_max_flag:
 | 
						|
                            scene_flags[i][room_setup] = flag
 | 
						|
                    else:
 | 
						|
                        scene_flags[i][room_setup] = flag
 | 
						|
        if len(scene_flags[i].keys()) == 0:
 | 
						|
            del scene_flags[i]
 | 
						|
        #scene_flags.append((i, max_enemy_flag))
 | 
						|
    return (scene_flags, alt_list)
 | 
						|
 | 
						|
# Create a byte array from the scene flag table created by get_collectible_flag_table
 | 
						|
def get_collectible_flag_table_bytes(scene_flag_table):
 | 
						|
    num_flag_bytes = 0
 | 
						|
    bytes = bytearray()
 | 
						|
    bytes.append(len(scene_flag_table.keys()))
 | 
						|
    for scene_id in scene_flag_table.keys():
 | 
						|
        rooms = scene_flag_table[scene_id]
 | 
						|
        room_count = len(rooms.keys())
 | 
						|
        bytes.append(scene_id)
 | 
						|
        bytes.append(room_count)
 | 
						|
        for room in rooms:
 | 
						|
            bytes.append(room)
 | 
						|
            bytes.append((num_flag_bytes & 0xFF00) >> 8)
 | 
						|
            bytes.append(num_flag_bytes & 0x00FF )
 | 
						|
            num_flag_bytes += ceil((rooms[room] + 1) / 8)
 | 
						|
            
 | 
						|
    return bytes, num_flag_bytes
 | 
						|
 | 
						|
def get_alt_list_bytes(alt_list):
 | 
						|
    bytes = bytearray()
 | 
						|
    for entry in alt_list:
 | 
						|
        location, alt, primary = entry
 | 
						|
        room, scene_setup, flag = alt
 | 
						|
        alt_override = (room << 8) + (scene_setup << 14) + flag
 | 
						|
        room, scene_setup, flag = primary
 | 
						|
        primary_override = (room << 8) + (scene_setup << 14) + flag
 | 
						|
        bytes.append(location.scene)
 | 
						|
        bytes.append(0x06)
 | 
						|
        bytes.append((alt_override & 0xFF00) >> 8)
 | 
						|
        bytes.append(alt_override & 0xFF)
 | 
						|
        bytes.append(location.scene)
 | 
						|
        bytes.append(0x06)
 | 
						|
        bytes.append((primary_override & 0xFF00) >> 8)
 | 
						|
        bytes.append(primary_override & 0xFF)
 | 
						|
    return bytes
 | 
						|
 | 
						|
# AP method to retrieve address + bit for each item
 | 
						|
# Based on get_collectible_flag_offset in the C code
 | 
						|
def get_collectible_flag_addresses(world, collectible_scene_flags_table):
 | 
						|
 | 
						|
    # Ported directly from get_items.c
 | 
						|
    def get_collectible_flag_offset(scene: int, room: int, setup_id: int) -> int:
 | 
						|
        num_scenes = collectible_scene_flags_table[0]
 | 
						|
        index = 1
 | 
						|
        scene_id = 0
 | 
						|
        room_id = 0
 | 
						|
        room_setup_count = 0
 | 
						|
        room_byte_offset = 0
 | 
						|
        # Loop through collectible_scene_flags_table until we find the right scene
 | 
						|
        while num_scenes > 0:
 | 
						|
            scene_id = collectible_scene_flags_table[index]
 | 
						|
            room_setup_count = collectible_scene_flags_table[index+1]
 | 
						|
            index += 2
 | 
						|
            if scene_id == scene:  # found the scene
 | 
						|
                # Loop through each room/setup combination until we find the right one.
 | 
						|
                for i in range(room_setup_count):
 | 
						|
                    room_id = collectible_scene_flags_table[index] & 0x3F
 | 
						|
                    setup_id_temp = (collectible_scene_flags_table[index] & 0xC0) >> 6
 | 
						|
                    room_byte_offset = (collectible_scene_flags_table[index+1] << 8) + collectible_scene_flags_table[index+2]
 | 
						|
                    index += 3
 | 
						|
                    if room_id == room and setup_id_temp == setup_id:
 | 
						|
                        return room_byte_offset
 | 
						|
            else:  # Not the right scene, skip to the next one
 | 
						|
                index += 3 * room_setup_count
 | 
						|
            num_scenes -= 1
 | 
						|
        return -1
 | 
						|
 | 
						|
    collectible_flag_addresses = {}
 | 
						|
    for location in world.get_locations():
 | 
						|
        if location.type in ["Freestanding", "Pot", "FlyingPot", "Crate", "SmallCrate", "Beehive", "RupeeTower"]:
 | 
						|
            default = location.default
 | 
						|
            if isinstance(default, list):
 | 
						|
                default = default[0]
 | 
						|
            room, setup, flag = default
 | 
						|
            offset = get_collectible_flag_offset(location.scene, room, setup)
 | 
						|
            item_id = location.address
 | 
						|
            collectible_flag_addresses[item_id] = [offset, flag]
 | 
						|
    return collectible_flag_addresses
 | 
						|
 |