LttP: Rename Shop Slot 1, 2, 3 to Shop Slot Left, Center, Right
General: Move generic IDs from LttP to new Generic World Generate: ensure thread errors are collected before data from their completion may be referenced in playthrough/spoiler
This commit is contained in:
parent
e8da9924c6
commit
21255b3b46
|
@ -911,7 +911,8 @@ class Boss():
|
||||||
return f"Boss({self.name})"
|
return f"Boss({self.name})"
|
||||||
|
|
||||||
class Location():
|
class Location():
|
||||||
shop_slot: bool = False
|
# If given as integer, then this is the shop's inventory index
|
||||||
|
shop_slot: Optional[int] = None
|
||||||
shop_slot_disabled: bool = False
|
shop_slot_disabled: bool = False
|
||||||
event: bool = False
|
event: bool = False
|
||||||
locked: bool = False
|
locked: bool = False
|
||||||
|
|
18
Main.py
18
Main.py
|
@ -147,7 +147,9 @@ def main(args, seed=None):
|
||||||
longest_name = max(len(text) for text in AutoWorld.AutoWorldRegister.world_types)
|
longest_name = max(len(text) for text in AutoWorld.AutoWorldRegister.world_types)
|
||||||
numlength = 8
|
numlength = 8
|
||||||
for name, cls in AutoWorld.AutoWorldRegister.world_types.items():
|
for name, cls in AutoWorld.AutoWorldRegister.world_types.items():
|
||||||
logger.info(f" {name:{longest_name}}: {len(cls.item_names):3} Items | {len(cls.location_names):3} Locations")
|
if not getattr(cls, "hidden", False):
|
||||||
|
logger.info(f" {name:{longest_name}}: {len(cls.item_names):3} Items | "
|
||||||
|
f"{len(cls.location_names):3} Locations")
|
||||||
logger.info(f" Item IDs: {min(cls.item_id_to_name):{numlength}} - "
|
logger.info(f" Item IDs: {min(cls.item_id_to_name):{numlength}} - "
|
||||||
f"{max(cls.item_id_to_name):{numlength}} | "
|
f"{max(cls.item_id_to_name):{numlength}} | "
|
||||||
f"Location IDs: {min(cls.location_id_to_name):{numlength}} - "
|
f"Location IDs: {min(cls.location_id_to_name):{numlength}} - "
|
||||||
|
@ -383,22 +385,28 @@ def main(args, seed=None):
|
||||||
raise Exception("Game appears as unbeatable. Aborting.")
|
raise Exception("Game appears as unbeatable. Aborting.")
|
||||||
else:
|
else:
|
||||||
logger.warning("Location Accessibility requirements not fulfilled.")
|
logger.warning("Location Accessibility requirements not fulfilled.")
|
||||||
|
|
||||||
|
# retrieve exceptions via .result() if they occured.
|
||||||
if multidata_task:
|
if multidata_task:
|
||||||
multidata_task.result() # retrieve exception if one exists
|
multidata_task.result()
|
||||||
|
for future in output_file_futures:
|
||||||
|
future.result()
|
||||||
|
|
||||||
pool.shutdown() # wait for all queued tasks to complete
|
pool.shutdown() # wait for all queued tasks to complete
|
||||||
|
|
||||||
if not args.skip_playthrough:
|
if not args.skip_playthrough:
|
||||||
logger.info('Calculating playthrough.')
|
logger.info('Calculating playthrough.')
|
||||||
create_playthrough(world)
|
create_playthrough(world)
|
||||||
|
|
||||||
if args.create_spoiler:
|
if args.create_spoiler:
|
||||||
world.spoiler.to_file(os.path.join(temp_dir, '%s_Spoiler.txt' % outfilebase))
|
world.spoiler.to_file(os.path.join(temp_dir, '%s_Spoiler.txt' % outfilebase))
|
||||||
for future in output_file_futures:
|
|
||||||
future.result()
|
|
||||||
zipfilename = output_path(f"AP_{world.seed_name}.zip")
|
zipfilename = output_path(f"AP_{world.seed_name}.zip")
|
||||||
logger.info(f'Creating final archive at {zipfilename}.')
|
logger.info(f'Creating final archive at {zipfilename}.')
|
||||||
with zipfile.ZipFile(zipfilename, mode="w", compression=zipfile.ZIP_DEFLATED,
|
with zipfile.ZipFile(zipfilename, mode="w", compression=zipfile.ZIP_DEFLATED,
|
||||||
compresslevel=9) as zf:
|
compresslevel=9) as zf:
|
||||||
for file in os.scandir(temp_dir):
|
for file in os.scandir(temp_dir):
|
||||||
zf.write(os.path.join(temp_dir, file), arcname=file.name)
|
zf.write(file.path, arcname=file.name)
|
||||||
|
|
||||||
logger.info('Done. Enjoy. Total Time: %s', time.perf_counter() - start)
|
logger.info('Done. Enjoy. Total Time: %s', time.perf_counter() - start)
|
||||||
return world
|
return world
|
||||||
|
|
|
@ -500,14 +500,14 @@ def create_dynamic_shop_locations(world, player):
|
||||||
if item is None:
|
if item is None:
|
||||||
continue
|
continue
|
||||||
if item['create_location']:
|
if item['create_location']:
|
||||||
loc = ALttPLocation(player, "{} Slot {}".format(shop.region.name, i + 1), parent=shop.region)
|
loc = ALttPLocation(player, f"{shop.region.name} {shop.slot_names[i]}", parent=shop.region)
|
||||||
shop.region.locations.append(loc)
|
shop.region.locations.append(loc)
|
||||||
world.dynamic_locations.append(loc)
|
world.dynamic_locations.append(loc)
|
||||||
|
|
||||||
world.clear_location_cache()
|
world.clear_location_cache()
|
||||||
|
|
||||||
world.push_item(loc, ItemFactory(item['item'], player), False)
|
world.push_item(loc, ItemFactory(item['item'], player), False)
|
||||||
loc.shop_slot = True
|
loc.shop_slot = i
|
||||||
loc.event = True
|
loc.event = True
|
||||||
loc.locked = True
|
loc.locked = True
|
||||||
|
|
||||||
|
|
|
@ -676,12 +676,10 @@ location_table: typing.Dict[str,
|
||||||
|
|
||||||
from worlds.alttp.Shops import shop_table_by_location_id, shop_table_by_location
|
from worlds.alttp.Shops import shop_table_by_location_id, shop_table_by_location
|
||||||
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
|
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
|
||||||
lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()},
|
lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()}}
|
||||||
-1: "Cheat Console", -2: "Server"}
|
|
||||||
lookup_id_to_name.update(shop_table_by_location_id)
|
lookup_id_to_name.update(shop_table_by_location_id)
|
||||||
lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int}
|
lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int}
|
||||||
lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()},
|
lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()}}
|
||||||
"Cheat Console": -1, "Server": -2}
|
|
||||||
lookup_name_to_id.update(shop_table_by_location)
|
lookup_name_to_id.update(shop_table_by_location)
|
||||||
|
|
||||||
lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 191256: 'Kings Grave Inner Rocks',
|
lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 191256: 'Kings Grave Inner Rocks',
|
||||||
|
|
|
@ -763,7 +763,7 @@ def patch_rom(world, rom, player, enemized):
|
||||||
# patch items
|
# patch items
|
||||||
|
|
||||||
for location in world.get_locations():
|
for location in world.get_locations():
|
||||||
if location.player != player or location.address is None or location.shop_slot:
|
if location.player != player or location.address is None or location.shop_slot is not None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
itemid = location.item.code if location.item is not None else 0x5A
|
itemid = location.item.code if location.item is not None else 0x5A
|
||||||
|
|
|
@ -22,6 +22,11 @@ class Shop():
|
||||||
slots: int = 3 # slot count is not dynamic in asm, however inventory can have None as empty slots
|
slots: int = 3 # slot count is not dynamic in asm, however inventory can have None as empty slots
|
||||||
blacklist: Set[str] = set() # items that don't work, todo: actually check against this
|
blacklist: Set[str] = set() # items that don't work, todo: actually check against this
|
||||||
type = ShopType.Shop
|
type = ShopType.Shop
|
||||||
|
slot_names: Dict[int, str] = {
|
||||||
|
0: "Left",
|
||||||
|
1: "Center",
|
||||||
|
2: "Right"
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, region, room_id: int, shopkeeper_config: int, custom: bool, locked: bool, sram_offset: int):
|
def __init__(self, region, room_id: int, shopkeeper_config: int, custom: bool, locked: bool, sram_offset: int):
|
||||||
self.region = region
|
self.region = region
|
||||||
|
@ -131,23 +136,22 @@ shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop,
|
||||||
|
|
||||||
def FillDisabledShopSlots(world):
|
def FillDisabledShopSlots(world):
|
||||||
shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops)
|
shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops)
|
||||||
for location in shop_locations if location.shop_slot and location.shop_slot_disabled}
|
for location in shop_locations
|
||||||
|
if location.shop_slot is not None and location.shop_slot_disabled}
|
||||||
for location in shop_slots:
|
for location in shop_slots:
|
||||||
location.shop_slot_disabled = True
|
location.shop_slot_disabled = True
|
||||||
slot_num = int(location.name[-1]) - 1
|
|
||||||
shop: Shop = location.parent_region.shop
|
shop: Shop = location.parent_region.shop
|
||||||
location.item = ItemFactory(shop.inventory[slot_num]['item'], location.player)
|
location.item = ItemFactory(shop.inventory[location.shop_slot]['item'], location.player)
|
||||||
location.item_rule = lambda item: item.name == location.item.name and item.player == location.player
|
location.item_rule = lambda item: item.name == location.item.name and item.player == location.player
|
||||||
|
|
||||||
|
|
||||||
def ShopSlotFill(world):
|
def ShopSlotFill(world):
|
||||||
shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops)
|
shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops)
|
||||||
for location in shop_locations if location.shop_slot}
|
for location in shop_locations if location.shop_slot is not None}
|
||||||
removed = set()
|
removed = set()
|
||||||
for location in shop_slots:
|
for location in shop_slots:
|
||||||
slot_num = int(location.name[-1]) - 1
|
|
||||||
shop: Shop = location.parent_region.shop
|
shop: Shop = location.parent_region.shop
|
||||||
if not shop.can_push_inventory(slot_num) or location.shop_slot_disabled:
|
if not shop.can_push_inventory(location.shop_slot) or location.shop_slot_disabled:
|
||||||
location.shop_slot_disabled = True
|
location.shop_slot_disabled = True
|
||||||
removed.add(location)
|
removed.add(location)
|
||||||
|
|
||||||
|
@ -179,7 +183,7 @@ def ShopSlotFill(world):
|
||||||
shops_per_sphere.append(current_shops_slots)
|
shops_per_sphere.append(current_shops_slots)
|
||||||
candidates_per_sphere.append(current_candidates)
|
candidates_per_sphere.append(current_candidates)
|
||||||
for location in sphere:
|
for location in sphere:
|
||||||
if location.shop_slot:
|
if location.shop_slot is not None:
|
||||||
if not location.shop_slot_disabled:
|
if not location.shop_slot_disabled:
|
||||||
current_shops_slots.append(location)
|
current_shops_slots.append(location)
|
||||||
elif not location.locked and not location.item.name in blacklist_words:
|
elif not location.locked and not location.item.name in blacklist_words:
|
||||||
|
@ -229,7 +233,7 @@ def ShopSlotFill(world):
|
||||||
else:
|
else:
|
||||||
price = world.random.randrange(8, 56)
|
price = world.random.randrange(8, 56)
|
||||||
|
|
||||||
shop.push_inventory(int(location.name[-1]) - 1, item_name, price * 5, 1,
|
shop.push_inventory(location.shop_slot, item_name, price * 5, 1,
|
||||||
location.item.player if location.item.player != location.player else 0)
|
location.item.player if location.item.player != location.player else 0)
|
||||||
|
|
||||||
|
|
||||||
|
@ -278,10 +282,10 @@ def create_shops(world, player: int):
|
||||||
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:
|
if not locked and num_slots:
|
||||||
slot_name = "{} Slot {}".format(region.name, index + 1)
|
slot_name = f"{region.name} {shop.slot_names[index]}"
|
||||||
loc = ALttPLocation(player, slot_name, address=shop_table_by_location[slot_name],
|
loc = ALttPLocation(player, slot_name, address=shop_table_by_location[slot_name],
|
||||||
parent=region, hint_text="for sale")
|
parent=region, hint_text="for sale")
|
||||||
loc.shop_slot = True
|
loc.shop_slot = index
|
||||||
loc.locked = True
|
loc.locked = True
|
||||||
if single_purchase_slots.pop():
|
if single_purchase_slots.pop():
|
||||||
if world.goal[player] != 'icerodhunt':
|
if world.goal[player] != 'icerodhunt':
|
||||||
|
@ -337,9 +341,10 @@ total_shop_slots = len(shop_table) * 3
|
||||||
total_dynamic_shop_slots = sum(3 for shopname, data in shop_table.items() if not data[4]) # data[4] -> locked
|
total_dynamic_shop_slots = sum(3 for shopname, data in shop_table.items() if not data[4]) # data[4] -> locked
|
||||||
|
|
||||||
SHOP_ID_START = 0x400000
|
SHOP_ID_START = 0x400000
|
||||||
shop_table_by_location_id = {cnt: s for cnt, s in enumerate(
|
shop_table_by_location_id = dict(enumerate(
|
||||||
(f"{name} Slot {num}" for name in [key for key, value in sorted(shop_table.items(), key=lambda item: item[1].sram_offset)]
|
(f"{name} {Shop.slot_names[num]}" for name, shop_data in sorted(shop_table.items(), key=lambda item: item[1].sram_offset)
|
||||||
for num in range(1, 4)), start=SHOP_ID_START)}
|
for num in range(3)), start=SHOP_ID_START))
|
||||||
|
|
||||||
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots)] = "Old Man Sword Cave"
|
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots)] = "Old Man Sword Cave"
|
||||||
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 1)] = "Take-Any #1"
|
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 1)] = "Take-Any #1"
|
||||||
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 2)] = "Take-Any #2"
|
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 2)] = "Take-Any #2"
|
||||||
|
|
|
@ -26,14 +26,12 @@ class ALTTPWorld(World):
|
||||||
options = alttp_options
|
options = alttp_options
|
||||||
topology_present = True
|
topology_present = True
|
||||||
item_name_groups = item_name_groups
|
item_name_groups = item_name_groups
|
||||||
item_names = frozenset(item_table)
|
|
||||||
location_names = frozenset(lookup_name_to_id)
|
|
||||||
hint_blacklist = {"Triforce"}
|
hint_blacklist = {"Triforce"}
|
||||||
|
|
||||||
item_name_to_id = {name: data.item_code for name, data in item_table.items() if type(data.item_code) == int}
|
item_name_to_id = {name: data.item_code for name, data in item_table.items() if type(data.item_code) == int}
|
||||||
location_name_to_id = lookup_name_to_id
|
location_name_to_id = lookup_name_to_id
|
||||||
|
|
||||||
data_version = 7
|
data_version = 8
|
||||||
remote_items: bool = False
|
remote_items: bool = False
|
||||||
|
|
||||||
set_rules = set_rules
|
set_rules = set_rules
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
from typing import NamedTuple, Union
|
from typing import NamedTuple, Union
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from ..AutoWorld import World
|
||||||
|
|
||||||
|
|
||||||
|
class GenericWorld(World):
|
||||||
|
game = "Archipelago"
|
||||||
|
topology_present = False
|
||||||
|
item_name_to_id = {
|
||||||
|
"Nothing": -1
|
||||||
|
}
|
||||||
|
location_name_to_id = {
|
||||||
|
"Cheat Console" : -1,
|
||||||
|
"Server": -2
|
||||||
|
}
|
||||||
|
hidden = True
|
||||||
|
|
||||||
class PlandoItem(NamedTuple):
|
class PlandoItem(NamedTuple):
|
||||||
item: str
|
item: str
|
||||||
|
|
|
@ -19,6 +19,8 @@ class HKWorld(World):
|
||||||
item_name_to_id = {name: data.id for name, data in item_table.items() if data.type != "Event"}
|
item_name_to_id = {name: data.id for name, data in item_table.items() if data.type != "Event"}
|
||||||
location_name_to_id = lookup_name_to_id
|
location_name_to_id = lookup_name_to_id
|
||||||
|
|
||||||
|
hidden = True
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
# Link regions
|
# Link regions
|
||||||
self.world.get_entrance('Hollow Nest S&Q', self.player).connect(self.world.get_region('Hollow Nest', self.player))
|
self.world.get_entrance('Hollow Nest S&Q', self.player).connect(self.world.get_region('Hollow Nest', self.player))
|
||||||
|
|
|
@ -19,6 +19,8 @@ class OriBlindForest(World):
|
||||||
|
|
||||||
options = options
|
options = options
|
||||||
|
|
||||||
|
hidden = True
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
logic_sets = {"casual-core"}
|
logic_sets = {"casual-core"}
|
||||||
for logic_set in location_rules:
|
for logic_set in location_rules:
|
||||||
|
|
Loading…
Reference in New Issue