mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 04:01:32 -06:00
LttP: move more stuff out of core (#5049)
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
@@ -154,17 +154,11 @@ class MultiWorld():
|
|||||||
self.algorithm = 'balanced'
|
self.algorithm = 'balanced'
|
||||||
self.groups = {}
|
self.groups = {}
|
||||||
self.regions = self.RegionManager(players)
|
self.regions = self.RegionManager(players)
|
||||||
self.shops = []
|
|
||||||
self.itempool = []
|
self.itempool = []
|
||||||
self.seed = None
|
self.seed = None
|
||||||
self.seed_name: str = "Unavailable"
|
self.seed_name: str = "Unavailable"
|
||||||
self.precollected_items = {player: [] for player in self.player_ids}
|
self.precollected_items = {player: [] for player in self.player_ids}
|
||||||
self.required_locations = []
|
self.required_locations = []
|
||||||
self.light_world_light_cone = False
|
|
||||||
self.dark_world_light_cone = False
|
|
||||||
self.rupoor_cost = 10
|
|
||||||
self.aga_randomness = True
|
|
||||||
self.save_and_quit_from_boss = True
|
|
||||||
self.custom = False
|
self.custom = False
|
||||||
self.customitemarray = []
|
self.customitemarray = []
|
||||||
self.shuffle_ganon = True
|
self.shuffle_ganon = True
|
||||||
|
@@ -223,7 +223,7 @@ items_reduction_table = (
|
|||||||
|
|
||||||
|
|
||||||
def generate_itempool(world):
|
def generate_itempool(world):
|
||||||
player = world.player
|
player: int = world.player
|
||||||
multiworld = world.multiworld
|
multiworld = world.multiworld
|
||||||
|
|
||||||
if world.options.item_pool.current_key not in difficulties:
|
if world.options.item_pool.current_key not in difficulties:
|
||||||
@@ -280,7 +280,6 @@ def generate_itempool(world):
|
|||||||
if multiworld.custom:
|
if multiworld.custom:
|
||||||
pool, placed_items, precollected_items, clock_mode, treasure_hunt_required = (
|
pool, placed_items, precollected_items, clock_mode, treasure_hunt_required = (
|
||||||
make_custom_item_pool(multiworld, player))
|
make_custom_item_pool(multiworld, player))
|
||||||
multiworld.rupoor_cost = min(multiworld.customitemarray[67], 9999)
|
|
||||||
else:
|
else:
|
||||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_required, treasure_hunt_total,
|
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_required, treasure_hunt_total,
|
||||||
additional_triforce_pieces) = get_pool_core(multiworld, player)
|
additional_triforce_pieces) = get_pool_core(multiworld, player)
|
||||||
@@ -386,8 +385,8 @@ def generate_itempool(world):
|
|||||||
|
|
||||||
if world.options.retro_bow:
|
if world.options.retro_bow:
|
||||||
shop_items = 0
|
shop_items = 0
|
||||||
shop_locations = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if
|
shop_locations = [location for shop_locations in (shop.region.locations for shop in world.shops if
|
||||||
shop.type == ShopType.Shop and shop.region.player == player) for location in shop_locations if
|
shop.type == ShopType.Shop) for location in shop_locations if
|
||||||
location.shop_slot is not None]
|
location.shop_slot is not None]
|
||||||
for location in shop_locations:
|
for location in shop_locations:
|
||||||
if location.shop.inventory[location.shop_slot]["item"] == "Single Arrow":
|
if location.shop.inventory[location.shop_slot]["item"] == "Single Arrow":
|
||||||
@@ -546,7 +545,7 @@ def set_up_take_anys(multiworld, world, player):
|
|||||||
connect_entrance(multiworld, entrance.name, old_man_take_any.name, player)
|
connect_entrance(multiworld, entrance.name, old_man_take_any.name, player)
|
||||||
entrance.target = 0x58
|
entrance.target = 0x58
|
||||||
old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True, total_shop_slots)
|
old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True, total_shop_slots)
|
||||||
multiworld.shops.append(old_man_take_any.shop)
|
world.shops.append(old_man_take_any.shop)
|
||||||
|
|
||||||
sword_indices = [
|
sword_indices = [
|
||||||
index for index, item in enumerate(multiworld.itempool) if item.player == player and item.type == 'Sword'
|
index for index, item in enumerate(multiworld.itempool) if item.player == player and item.type == 'Sword'
|
||||||
@@ -574,7 +573,7 @@ def set_up_take_anys(multiworld, world, player):
|
|||||||
connect_entrance(multiworld, entrance.name, take_any.name, player)
|
connect_entrance(multiworld, entrance.name, take_any.name, player)
|
||||||
entrance.target = target
|
entrance.target = target
|
||||||
take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True, total_shop_slots + num + 1)
|
take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True, total_shop_slots + num + 1)
|
||||||
multiworld.shops.append(take_any.shop)
|
world.shops.append(take_any.shop)
|
||||||
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0)
|
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0)
|
||||||
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0)
|
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0)
|
||||||
location = ALttPLocation(player, take_any.name, shop_table_by_location[take_any.name], parent=take_any)
|
location = ALttPLocation(player, take_any.name, shop_table_by_location[take_any.name], parent=take_any)
|
||||||
|
@@ -1002,14 +1002,19 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
|
|
||||||
# set light cones
|
# set light cones
|
||||||
rom.write_byte(0x180038, 0x01 if local_world.options.mode == "standard" else 0x00)
|
rom.write_byte(0x180038, 0x01 if local_world.options.mode == "standard" else 0x00)
|
||||||
rom.write_byte(0x180039, 0x01 if world.light_world_light_cone else 0x00)
|
# light world light cone
|
||||||
rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
|
rom.write_byte(0x180039, local_world.light_world_light_cone)
|
||||||
|
# dark world light cone
|
||||||
|
rom.write_byte(0x18003A, local_world.dark_world_light_cone)
|
||||||
|
|
||||||
GREEN_TWENTY_RUPEES = 0x47
|
GREEN_TWENTY_RUPEES = 0x47
|
||||||
GREEN_CLOCK = item_table["Green Clock"].item_code
|
GREEN_CLOCK = item_table["Green Clock"].item_code
|
||||||
|
|
||||||
rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on
|
rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on
|
||||||
|
|
||||||
|
# Rupoor negative value
|
||||||
|
rom.write_int16(0x180036, local_world.rupoor_cost)
|
||||||
|
|
||||||
# handle item_functionality
|
# handle item_functionality
|
||||||
if local_world.options.item_functionality == 'hard':
|
if local_world.options.item_functionality == 'hard':
|
||||||
rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon
|
rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon
|
||||||
@@ -1027,8 +1032,6 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
# Disable catching fairies
|
# Disable catching fairies
|
||||||
rom.write_byte(0x34FD6, 0x80)
|
rom.write_byte(0x34FD6, 0x80)
|
||||||
overflow_replacement = GREEN_TWENTY_RUPEES
|
overflow_replacement = GREEN_TWENTY_RUPEES
|
||||||
# Rupoor negative value
|
|
||||||
rom.write_int16(0x180036, world.rupoor_cost)
|
|
||||||
# Set stun items
|
# Set stun items
|
||||||
rom.write_byte(0x180180, 0x02) # Hookshot only
|
rom.write_byte(0x180180, 0x02) # Hookshot only
|
||||||
elif local_world.options.item_functionality == 'expert':
|
elif local_world.options.item_functionality == 'expert':
|
||||||
@@ -1047,8 +1050,6 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
# Disable catching fairies
|
# Disable catching fairies
|
||||||
rom.write_byte(0x34FD6, 0x80)
|
rom.write_byte(0x34FD6, 0x80)
|
||||||
overflow_replacement = GREEN_TWENTY_RUPEES
|
overflow_replacement = GREEN_TWENTY_RUPEES
|
||||||
# Rupoor negative value
|
|
||||||
rom.write_int16(0x180036, world.rupoor_cost)
|
|
||||||
# Set stun items
|
# Set stun items
|
||||||
rom.write_byte(0x180180, 0x00) # Nothing
|
rom.write_byte(0x180180, 0x00) # Nothing
|
||||||
else:
|
else:
|
||||||
@@ -1066,8 +1067,6 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
rom.write_byte(0x18004F, 0x01)
|
rom.write_byte(0x18004F, 0x01)
|
||||||
# Enable catching fairies
|
# Enable catching fairies
|
||||||
rom.write_byte(0x34FD6, 0xF0)
|
rom.write_byte(0x34FD6, 0xF0)
|
||||||
# Rupoor negative value
|
|
||||||
rom.write_int16(0x180036, world.rupoor_cost)
|
|
||||||
# Set stun items
|
# Set stun items
|
||||||
rom.write_byte(0x180180, 0x03) # All standard items
|
rom.write_byte(0x180180, 0x03) # All standard items
|
||||||
# Set overflow items for progressive equipment
|
# Set overflow items for progressive equipment
|
||||||
@@ -1313,7 +1312,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
rom.write_byte(0x18008C, 0x01 if local_world.options.crystals_needed_for_gt == 0 else 0x00) # GT pre-opened if crystal requirement is 0
|
rom.write_byte(0x18008C, 0x01 if local_world.options.crystals_needed_for_gt == 0 else 0x00) # GT pre-opened if crystal requirement is 0
|
||||||
rom.write_byte(0xF5D73, 0xF0) # bees are catchable
|
rom.write_byte(0xF5D73, 0xF0) # bees are catchable
|
||||||
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
|
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
|
||||||
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
|
rom.write_byte(0x180086, 0x00) # set blue ball and ganon warp randomness
|
||||||
rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror
|
rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror
|
||||||
rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
|
rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
|
||||||
rom.write_byte(0x180174, 0x01 if local_world.fix_fake_world else 0x00)
|
rom.write_byte(0x180174, 0x01 if local_world.fix_fake_world else 0x00)
|
||||||
@@ -1618,7 +1617,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
|||||||
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
||||||
rom.write_byte(0x1800A4, 0x01 if local_world.options.glitches_required != 'no_logic' else 0x00) # enable POD EG fix
|
rom.write_byte(0x1800A4, 0x01 if local_world.options.glitches_required != 'no_logic' else 0x00) # enable POD EG fix
|
||||||
rom.write_byte(0x186383, 0x01 if local_world.options.glitches_required == 'no_logic' else 0x00) # disable glitching to Triforce from Ganons Room
|
rom.write_byte(0x186383, 0x01 if local_world.options.glitches_required == 'no_logic' else 0x00) # disable glitching to Triforce from Ganons Room
|
||||||
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
|
rom.write_byte(0x180042, 0x01 if local_world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
|
||||||
|
|
||||||
# remove shield from uncle
|
# remove shield from uncle
|
||||||
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
@@ -1739,8 +1738,7 @@ def get_price_data(price: int, price_type: int) -> List[int]:
|
|||||||
|
|
||||||
|
|
||||||
def write_custom_shops(rom, world, player):
|
def write_custom_shops(rom, world, player):
|
||||||
shops = sorted([shop for shop in world.shops if shop.custom and shop.region.player == player],
|
shops = sorted([shop for shop in world.worlds[player].shops if shop.custom], key=lambda shop: shop.sram_offset)
|
||||||
key=lambda shop: shop.sram_offset)
|
|
||||||
|
|
||||||
shop_data = bytearray()
|
shop_data = bytearray()
|
||||||
items_data = bytearray()
|
items_data = bytearray()
|
||||||
|
@@ -147,7 +147,6 @@ def set_defeat_dungeon_boss_rule(location):
|
|||||||
add_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state))
|
add_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_always_allow(spot, rule):
|
def set_always_allow(spot, rule):
|
||||||
spot.always_allow = rule
|
spot.always_allow = rule
|
||||||
|
|
||||||
@@ -980,18 +979,19 @@ def check_is_dark_world(region):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def add_conditional_lamps(world, player):
|
def add_conditional_lamps(multiworld, player):
|
||||||
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
|
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
|
||||||
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
|
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
|
||||||
|
local_world = multiworld.worlds[player]
|
||||||
|
|
||||||
def add_conditional_lamp(spot, region, spottype='Location', accessible_torch=False):
|
def add_conditional_lamp(spot, region, spottype='Location', accessible_torch=False):
|
||||||
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (
|
if (not local_world.dark_world_light_cone and check_is_dark_world(local_world.get_region(region))) or (
|
||||||
not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
|
not local_world.light_world_light_cone and not check_is_dark_world(local_world.get_region(region))):
|
||||||
if spottype == 'Location':
|
if spottype == 'Location':
|
||||||
spot = world.get_location(spot, player)
|
spot = local_world.get_location(spot)
|
||||||
else:
|
else:
|
||||||
spot = world.get_entrance(spot, player)
|
spot = local_world.get_entrance(spot)
|
||||||
add_lamp_requirement(world, spot, player, accessible_torch)
|
add_lamp_requirement(multiworld, spot, player, accessible_torch)
|
||||||
|
|
||||||
add_conditional_lamp('Misery Mire (Vitreous)', 'Misery Mire (Entrance)', 'Entrance')
|
add_conditional_lamp('Misery Mire (Vitreous)', 'Misery Mire (Entrance)', 'Entrance')
|
||||||
add_conditional_lamp('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Entrance)', 'Entrance')
|
add_conditional_lamp('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Entrance)', 'Entrance')
|
||||||
@@ -1002,7 +1002,7 @@ def add_conditional_lamps(world, player):
|
|||||||
'Location', True)
|
'Location', True)
|
||||||
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)',
|
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)',
|
||||||
'Location', True)
|
'Location', True)
|
||||||
if world.worlds[player].options.mode != 'inverted':
|
if multiworld.worlds[player].options.mode != 'inverted':
|
||||||
add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance')
|
add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance')
|
||||||
add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower')
|
add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower')
|
||||||
add_conditional_lamp('Castle Tower - Dark Archer Key Drop', 'Agahnims Tower')
|
add_conditional_lamp('Castle Tower - Dark Archer Key Drop', 'Agahnims Tower')
|
||||||
@@ -1024,10 +1024,10 @@ def add_conditional_lamps(world, player):
|
|||||||
add_conditional_lamp('Eastern Palace - Boss', 'Eastern Palace', 'Location', True)
|
add_conditional_lamp('Eastern Palace - Boss', 'Eastern Palace', 'Location', True)
|
||||||
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location', True)
|
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location', True)
|
||||||
|
|
||||||
if not world.worlds[player].options.mode == "standard":
|
if not multiworld.worlds[player].options.mode == "standard":
|
||||||
add_lamp_requirement(world, world.get_location('Sewers - Dark Cross', player), player)
|
add_lamp_requirement(multiworld, local_world.get_location("Sewers - Dark Cross"), player)
|
||||||
add_lamp_requirement(world, world.get_entrance('Sewers Back Door', player), player)
|
add_lamp_requirement(multiworld, local_world.get_entrance("Sewers Back Door"), player)
|
||||||
add_lamp_requirement(world, world.get_entrance('Throne Room', player), player)
|
add_lamp_requirement(multiworld, local_world.get_entrance("Throne Room"), player)
|
||||||
|
|
||||||
|
|
||||||
def open_rules(world, player):
|
def open_rules(world, player):
|
||||||
|
@@ -14,8 +14,6 @@ from .Items import item_name_groups
|
|||||||
|
|
||||||
from .StateHelpers import has_hearts, can_use_bombs, can_hold_arrows
|
from .StateHelpers import has_hearts, can_use_bombs, can_hold_arrows
|
||||||
|
|
||||||
logger = logging.getLogger("Shops")
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class ShopType(IntEnum):
|
class ShopType(IntEnum):
|
||||||
@@ -162,7 +160,10 @@ shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop,
|
|||||||
|
|
||||||
|
|
||||||
def push_shop_inventories(multiworld):
|
def push_shop_inventories(multiworld):
|
||||||
shop_slots = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if shop.type
|
all_shops = []
|
||||||
|
for world in multiworld.get_game_worlds(ALttPLocation.game):
|
||||||
|
all_shops.extend(world.shops)
|
||||||
|
shop_slots = [location for shop_locations in (shop.region.locations for shop in all_shops if shop.type
|
||||||
!= ShopType.TakeAny) for location in shop_locations if location.shop_slot is not None]
|
!= ShopType.TakeAny) for location in shop_locations if location.shop_slot is not None]
|
||||||
|
|
||||||
for location in shop_slots:
|
for location in shop_slots:
|
||||||
@@ -178,7 +179,7 @@ def push_shop_inventories(multiworld):
|
|||||||
get_price(multiworld, location.shop.inventory[location.shop_slot], location.player,
|
get_price(multiworld, location.shop.inventory[location.shop_slot], location.player,
|
||||||
location.shop_price_type)[1])
|
location.shop_price_type)[1])
|
||||||
|
|
||||||
for world in multiworld.get_game_worlds("A Link to the Past"):
|
for world in multiworld.get_game_worlds(ALttPLocation.game):
|
||||||
world.pushed_shop_inventories.set()
|
world.pushed_shop_inventories.set()
|
||||||
|
|
||||||
|
|
||||||
@@ -225,7 +226,7 @@ def create_shops(multiworld, player: int):
|
|||||||
if locked is None:
|
if locked is None:
|
||||||
shop.locked = True
|
shop.locked = True
|
||||||
region.shop = shop
|
region.shop = shop
|
||||||
multiworld.shops.append(shop)
|
multiworld.worlds[player].shops.append(shop)
|
||||||
for index, item in enumerate(inventory):
|
for index, item in enumerate(inventory):
|
||||||
shop.add_inventory(index, *item)
|
shop.add_inventory(index, *item)
|
||||||
if not locked and (num_slots or type == ShopType.UpgradeShop):
|
if not locked and (num_slots or type == ShopType.UpgradeShop):
|
||||||
@@ -309,50 +310,50 @@ def set_up_shops(multiworld, player: int):
|
|||||||
from .Options import small_key_shuffle
|
from .Options import small_key_shuffle
|
||||||
# TODO: move hard+ mode changes for shields here, utilizing the new shops
|
# TODO: move hard+ mode changes for shields here, utilizing the new shops
|
||||||
|
|
||||||
if multiworld.worlds[player].options.retro_bow:
|
local_world = multiworld.worlds[player]
|
||||||
|
|
||||||
|
if local_world.options.retro_bow:
|
||||||
rss = multiworld.get_region('Red Shield Shop', player).shop
|
rss = multiworld.get_region('Red Shield Shop', player).shop
|
||||||
|
# Can't just replace the single arrow with 10 arrows as retro doesn't need them.
|
||||||
replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50],
|
replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50],
|
||||||
['Blue Shield', 50], ['Small Heart',
|
['Blue Shield', 50], ['Small Heart', 10]]
|
||||||
10]] # Can't just replace the single arrow with 10 arrows as retro doesn't need them.
|
if local_world.options.small_key_shuffle == small_key_shuffle.option_universal:
|
||||||
if multiworld.worlds[player].options.small_key_shuffle == small_key_shuffle.option_universal:
|
|
||||||
replacement_items.append(['Small Key (Universal)', 100])
|
replacement_items.append(['Small Key (Universal)', 100])
|
||||||
replacement_item = multiworld.random.choice(replacement_items)
|
replacement_item = multiworld.random.choice(replacement_items)
|
||||||
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
|
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
|
||||||
rss.locked = True
|
rss.locked = True
|
||||||
|
|
||||||
if multiworld.worlds[player].options.small_key_shuffle == small_key_shuffle.option_universal or multiworld.worlds[player].options.retro_bow:
|
if local_world.options.small_key_shuffle == small_key_shuffle.option_universal or local_world.options.retro_bow:
|
||||||
for shop in multiworld.random.sample([s for s in multiworld.shops if
|
for shop in multiworld.random.sample([s for s in local_world.shops if
|
||||||
s.custom and not s.locked and s.type == ShopType.Shop
|
s.custom and not s.locked and s.type == ShopType.Shop], 5):
|
||||||
and s.region.player == player], 5):
|
|
||||||
shop.locked = True
|
shop.locked = True
|
||||||
slots = [0, 1, 2]
|
slots = [0, 1, 2]
|
||||||
multiworld.random.shuffle(slots)
|
multiworld.random.shuffle(slots)
|
||||||
slots = iter(slots)
|
slots = iter(slots)
|
||||||
if multiworld.worlds[player].options.small_key_shuffle == small_key_shuffle.option_universal:
|
if local_world.options.small_key_shuffle == small_key_shuffle.option_universal:
|
||||||
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
|
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
|
||||||
if multiworld.worlds[player].options.retro_bow:
|
if local_world.options.retro_bow:
|
||||||
shop.push_inventory(next(slots), 'Single Arrow', 80)
|
shop.push_inventory(next(slots), 'Single Arrow', 80)
|
||||||
|
|
||||||
if multiworld.worlds[player].options.shuffle_capacity_upgrades:
|
if local_world.options.shuffle_capacity_upgrades:
|
||||||
for shop in multiworld.shops:
|
for shop in local_world.shops:
|
||||||
if shop.type == ShopType.UpgradeShop and shop.region.player == player and \
|
if shop.type == ShopType.UpgradeShop and \
|
||||||
shop.region.name == "Capacity Upgrade":
|
shop.region.name == "Capacity Upgrade":
|
||||||
shop.clear_inventory()
|
shop.clear_inventory()
|
||||||
|
|
||||||
if (multiworld.worlds[player].options.shuffle_shop_inventories or multiworld.worlds[player].options.randomize_shop_prices
|
if (local_world.options.shuffle_shop_inventories or local_world.options.randomize_shop_prices
|
||||||
or multiworld.worlds[player].options.randomize_cost_types):
|
or local_world.options.randomize_cost_types):
|
||||||
shops = []
|
shops = []
|
||||||
total_inventory = []
|
total_inventory = []
|
||||||
for shop in multiworld.shops:
|
for shop in local_world.shops:
|
||||||
if shop.region.player == player:
|
if shop.type == ShopType.Shop and not shop.locked:
|
||||||
if shop.type == ShopType.Shop and not shop.locked:
|
shops.append(shop)
|
||||||
shops.append(shop)
|
total_inventory.extend(shop.inventory)
|
||||||
total_inventory.extend(shop.inventory)
|
|
||||||
|
|
||||||
for item in total_inventory:
|
for item in total_inventory:
|
||||||
item["price_type"], item["price"] = get_price(multiworld, item, player)
|
item["price_type"], item["price"] = get_price(multiworld, item, player)
|
||||||
|
|
||||||
if multiworld.worlds[player].options.shuffle_shop_inventories:
|
if local_world.options.shuffle_shop_inventories:
|
||||||
multiworld.random.shuffle(total_inventory)
|
multiworld.random.shuffle(total_inventory)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
@@ -407,7 +408,7 @@ price_rate_display = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_price_modifier(item):
|
def get_price_modifier(item) -> float:
|
||||||
if item.game == "A Link to the Past":
|
if item.game == "A Link to the Past":
|
||||||
if any(x in item.name for x in
|
if any(x in item.name for x in
|
||||||
['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
|
['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
|
||||||
@@ -418,9 +419,9 @@ def get_price_modifier(item):
|
|||||||
elif any(x in item.name for x in ['Small Key', 'Heart']):
|
elif any(x in item.name for x in ['Small Key', 'Heart']):
|
||||||
return 0.5
|
return 0.5
|
||||||
else:
|
else:
|
||||||
return 1
|
return 1.0
|
||||||
if item.advancement:
|
if item.advancement:
|
||||||
return 1
|
return 1.0
|
||||||
elif item.useful:
|
elif item.useful:
|
||||||
return 0.5
|
return 0.5
|
||||||
else:
|
else:
|
||||||
@@ -471,7 +472,7 @@ def get_price(multiworld, item, player: int, price_type=None):
|
|||||||
|
|
||||||
def shop_price_rules(state: CollectionState, player: int, location: ALttPLocation):
|
def shop_price_rules(state: CollectionState, player: int, location: ALttPLocation):
|
||||||
if location.shop_price_type == ShopPriceType.Hearts:
|
if location.shop_price_type == ShopPriceType.Hearts:
|
||||||
return has_hearts(state, player, (location.shop_price / 8) + 1)
|
return has_hearts(state, player, (location.shop_price // 8) + 1)
|
||||||
elif location.shop_price_type == ShopPriceType.Bombs:
|
elif location.shop_price_type == ShopPriceType.Bombs:
|
||||||
return can_use_bombs(state, player, location.shop_price)
|
return can_use_bombs(state, player, location.shop_price)
|
||||||
elif location.shop_price_type == ShopPriceType.Arrows:
|
elif location.shop_price_type == ShopPriceType.Arrows:
|
||||||
|
@@ -14,13 +14,13 @@ def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bo
|
|||||||
|
|
||||||
|
|
||||||
def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool:
|
def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool:
|
||||||
return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(state) for
|
return any(shop.has_unlimited(item) and shop.region.can_reach(state) for
|
||||||
shop in state.multiworld.shops)
|
shop in state.multiworld.worlds[player].shops)
|
||||||
|
|
||||||
|
|
||||||
def can_buy(state: CollectionState, item: str, player: int) -> bool:
|
def can_buy(state: CollectionState, item: str, player: int) -> bool:
|
||||||
return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(state) for
|
return any(shop.has(item) and shop.region.can_reach(state) for
|
||||||
shop in state.multiworld.shops)
|
shop in state.multiworld.worlds[player].shops)
|
||||||
|
|
||||||
|
|
||||||
def can_shoot_arrows(state: CollectionState, player: int, count: int = 0) -> bool:
|
def can_shoot_arrows(state: CollectionState, player: int, count: int = 0) -> bool:
|
||||||
|
@@ -236,6 +236,8 @@ class ALTTPWorld(World):
|
|||||||
required_client_version = (0, 4, 1)
|
required_client_version = (0, 4, 1)
|
||||||
web = ALTTPWeb()
|
web = ALTTPWeb()
|
||||||
|
|
||||||
|
shops: list[Shop]
|
||||||
|
|
||||||
pedestal_credit_texts: typing.Dict[int, str] = \
|
pedestal_credit_texts: typing.Dict[int, str] = \
|
||||||
{data.item_code: data.pedestal_credit for data in item_table.values() if data.pedestal_credit}
|
{data.item_code: data.pedestal_credit for data in item_table.values() if data.pedestal_credit}
|
||||||
sickkid_credit_texts: typing.Dict[int, str] = \
|
sickkid_credit_texts: typing.Dict[int, str] = \
|
||||||
@@ -282,6 +284,10 @@ class ALTTPWorld(World):
|
|||||||
clock_mode: str = ""
|
clock_mode: str = ""
|
||||||
treasure_hunt_required: int = 0
|
treasure_hunt_required: int = 0
|
||||||
treasure_hunt_total: int = 0
|
treasure_hunt_total: int = 0
|
||||||
|
light_world_light_cone: bool = False
|
||||||
|
dark_world_light_cone: bool = False
|
||||||
|
save_and_quit_from_boss: bool = True
|
||||||
|
rupoor_cost: int = 10
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.dungeon_local_item_names = set()
|
self.dungeon_local_item_names = set()
|
||||||
@@ -298,6 +304,7 @@ class ALTTPWorld(World):
|
|||||||
self.fix_trock_exit = None
|
self.fix_trock_exit = None
|
||||||
self.required_medallions = ["Ether", "Quake"]
|
self.required_medallions = ["Ether", "Quake"]
|
||||||
self.escape_assist = []
|
self.escape_assist = []
|
||||||
|
self.shops = []
|
||||||
super(ALTTPWorld, self).__init__(*args, **kwargs)
|
super(ALTTPWorld, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -800,7 +807,7 @@ class ALTTPWorld(World):
|
|||||||
|
|
||||||
return shop_data
|
return shop_data
|
||||||
|
|
||||||
if shop_info := [build_shop_info(shop) for shop in self.multiworld.shops if shop.custom]:
|
if shop_info := [build_shop_info(shop) for shop in self.shops if shop.custom]:
|
||||||
spoiler_handle.write('\n\nShops:\n\n')
|
spoiler_handle.write('\n\nShops:\n\n')
|
||||||
for shop_data in shop_info:
|
for shop_data in shop_info:
|
||||||
spoiler_handle.write("{} [{}]\n {}\n".format(shop_data['location'], shop_data['type'], "\n ".join(
|
spoiler_handle.write("{} [{}]\n {}\n".format(shop_data['location'], shop_data['type'], "\n ".join(
|
||||||
|
Reference in New Issue
Block a user