Skip to content

Commit 508e277

Browse files
committed
Merge remote-tracking branch 'ap/main' into links_awakening
2 parents b20183c + 0cf8206 commit 508e277

File tree

128 files changed

+13948
-872
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+13948
-872
lines changed

BaseClasses.py

+4-199
Original file line numberDiff line numberDiff line change
@@ -1260,13 +1260,9 @@ def __init__(self, world):
12601260
self.multiworld = world
12611261
self.hashes = {}
12621262
self.entrances = OrderedDict()
1263-
self.medallions = {}
12641263
self.playthrough = {}
12651264
self.unreachables = set()
1266-
self.locations = {}
12671265
self.paths = {}
1268-
self.shops = []
1269-
self.bosses = OrderedDict()
12701266

12711267
def set_entrance(self, entrance: str, exit_: str, direction: str, player: int):
12721268
if self.multiworld.players == 1:
@@ -1276,126 +1272,6 @@ def set_entrance(self, entrance: str, exit_: str, direction: str, player: int):
12761272
self.entrances[(entrance, direction, player)] = OrderedDict(
12771273
[('player', player), ('entrance', entrance), ('exit', exit_), ('direction', direction)])
12781274

1279-
def parse_data(self):
1280-
from worlds.alttp.SubClasses import LTTPRegionType
1281-
self.medallions = OrderedDict()
1282-
for player in self.multiworld.get_game_players("A Link to the Past"):
1283-
self.medallions[f'Misery Mire ({self.multiworld.get_player_name(player)})'] = \
1284-
self.multiworld.required_medallions[player][0]
1285-
self.medallions[f'Turtle Rock ({self.multiworld.get_player_name(player)})'] = \
1286-
self.multiworld.required_medallions[player][1]
1287-
1288-
self.locations = OrderedDict()
1289-
listed_locations = set()
1290-
lw_locations = []
1291-
dw_locations = []
1292-
cave_locations = []
1293-
for loc in self.multiworld.get_locations():
1294-
if loc.game == "A Link to the Past":
1295-
if loc not in listed_locations and loc.parent_region and \
1296-
loc.parent_region.type == LTTPRegionType.LightWorld and loc.show_in_spoiler:
1297-
lw_locations.append(loc)
1298-
elif loc not in listed_locations and loc.parent_region and \
1299-
loc.parent_region.type == LTTPRegionType.DarkWorld and loc.show_in_spoiler:
1300-
dw_locations.append(loc)
1301-
elif loc not in listed_locations and loc.parent_region and \
1302-
loc.parent_region.type == LTTPRegionType.Cave and loc.show_in_spoiler:
1303-
cave_locations.append(loc)
1304-
1305-
self.locations['Light World'] = OrderedDict(
1306-
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
1307-
lw_locations])
1308-
listed_locations.update(lw_locations)
1309-
1310-
self.locations['Dark World'] = OrderedDict(
1311-
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
1312-
dw_locations])
1313-
listed_locations.update(dw_locations)
1314-
1315-
self.locations['Caves'] = OrderedDict(
1316-
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
1317-
cave_locations])
1318-
listed_locations.update(cave_locations)
1319-
1320-
for dungeon in self.multiworld.dungeons.values():
1321-
dungeon_locations = [loc for loc in self.multiworld.get_locations() if
1322-
loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and loc.show_in_spoiler]
1323-
self.locations[str(dungeon)] = OrderedDict(
1324-
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
1325-
dungeon_locations])
1326-
listed_locations.update(dungeon_locations)
1327-
1328-
other_locations = [loc for loc in self.multiworld.get_locations() if
1329-
loc not in listed_locations and loc.show_in_spoiler]
1330-
if other_locations:
1331-
self.locations['Other Locations'] = OrderedDict(
1332-
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
1333-
other_locations])
1334-
listed_locations.update(other_locations)
1335-
1336-
self.shops = []
1337-
from worlds.alttp.Shops import ShopType, price_type_display_name, price_rate_display
1338-
for shop in self.multiworld.shops:
1339-
if not shop.custom:
1340-
continue
1341-
shopdata = {
1342-
'location': str(shop.region),
1343-
'type': 'Take Any' if shop.type == ShopType.TakeAny else 'Shop'
1344-
}
1345-
for index, item in enumerate(shop.inventory):
1346-
if item is None:
1347-
continue
1348-
my_price = item['price'] // price_rate_display.get(item['price_type'], 1)
1349-
shopdata['item_{}'.format(
1350-
index)] = f"{item['item']}{my_price} {price_type_display_name[item['price_type']]}"
1351-
1352-
if item['player'] > 0:
1353-
shopdata['item_{}'.format(index)] = shopdata['item_{}'.format(index)].replace('—',
1354-
'(Player {}) — '.format(
1355-
item['player']))
1356-
1357-
if item['max'] == 0:
1358-
continue
1359-
shopdata['item_{}'.format(index)] += " x {}".format(item['max'])
1360-
1361-
if item['replacement'] is None:
1362-
continue
1363-
shopdata['item_{}'.format(
1364-
index)] += f", {item['replacement']} - {item['replacement_price']} {price_type_display_name[item['replacement_price_type']]}"
1365-
self.shops.append(shopdata)
1366-
1367-
for player in self.multiworld.get_game_players("A Link to the Past"):
1368-
self.bosses[str(player)] = OrderedDict()
1369-
self.bosses[str(player)]["Eastern Palace"] = self.multiworld.get_dungeon("Eastern Palace", player).boss.name
1370-
self.bosses[str(player)]["Desert Palace"] = self.multiworld.get_dungeon("Desert Palace", player).boss.name
1371-
self.bosses[str(player)]["Tower Of Hera"] = self.multiworld.get_dungeon("Tower of Hera", player).boss.name
1372-
self.bosses[str(player)]["Hyrule Castle"] = "Agahnim"
1373-
self.bosses[str(player)]["Palace Of Darkness"] = self.multiworld.get_dungeon("Palace of Darkness",
1374-
player).boss.name
1375-
self.bosses[str(player)]["Swamp Palace"] = self.multiworld.get_dungeon("Swamp Palace", player).boss.name
1376-
self.bosses[str(player)]["Skull Woods"] = self.multiworld.get_dungeon("Skull Woods", player).boss.name
1377-
self.bosses[str(player)]["Thieves Town"] = self.multiworld.get_dungeon("Thieves Town", player).boss.name
1378-
self.bosses[str(player)]["Ice Palace"] = self.multiworld.get_dungeon("Ice Palace", player).boss.name
1379-
self.bosses[str(player)]["Misery Mire"] = self.multiworld.get_dungeon("Misery Mire", player).boss.name
1380-
self.bosses[str(player)]["Turtle Rock"] = self.multiworld.get_dungeon("Turtle Rock", player).boss.name
1381-
if self.multiworld.mode[player] != 'inverted':
1382-
self.bosses[str(player)]["Ganons Tower Basement"] = \
1383-
self.multiworld.get_dungeon('Ganons Tower', player).bosses['bottom'].name
1384-
self.bosses[str(player)]["Ganons Tower Middle"] = self.multiworld.get_dungeon('Ganons Tower', player).bosses[
1385-
'middle'].name
1386-
self.bosses[str(player)]["Ganons Tower Top"] = self.multiworld.get_dungeon('Ganons Tower', player).bosses[
1387-
'top'].name
1388-
else:
1389-
self.bosses[str(player)]["Ganons Tower Basement"] = \
1390-
self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].name
1391-
self.bosses[str(player)]["Ganons Tower Middle"] = \
1392-
self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].name
1393-
self.bosses[str(player)]["Ganons Tower Top"] = \
1394-
self.multiworld.get_dungeon('Inverted Ganons Tower', player).bosses['top'].name
1395-
1396-
self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2"
1397-
self.bosses[str(player)]["Ganon"] = "Ganon"
1398-
13991275
def create_playthrough(self, create_paths: bool = True):
14001276
"""Destructive to the world while it is run, damage gets repaired afterwards."""
14011277
from itertools import chain
@@ -1547,30 +1423,7 @@ def get_path(state, region):
15471423
self.paths[str(multiworld.get_region('Inverted Big Bomb Shop', player))] = \
15481424
get_path(state, multiworld.get_region('Inverted Big Bomb Shop', player))
15491425

1550-
def to_json(self):
1551-
self.parse_data()
1552-
out = OrderedDict()
1553-
out['Entrances'] = list(self.entrances.values())
1554-
out.update(self.locations)
1555-
out['Special'] = self.medallions
1556-
if self.hashes:
1557-
out['Hashes'] = self.hashes
1558-
if self.shops:
1559-
out['Shops'] = self.shops
1560-
out['playthrough'] = self.playthrough
1561-
out['paths'] = self.paths
1562-
out['Bosses'] = self.bosses
1563-
1564-
return json.dumps(out)
1565-
15661426
def to_file(self, filename: str):
1567-
self.parse_data()
1568-
1569-
def bool_to_text(variable: Union[bool, str]) -> str:
1570-
if type(variable) == str:
1571-
return variable
1572-
return 'Yes' if variable else 'No'
1573-
15741427
def write_option(option_key: str, option_obj: type(Options.Option)):
15751428
res = getattr(self.multiworld, option_key)[player]
15761429
display_name = getattr(option_obj, "display_name", option_key)
@@ -1600,38 +1453,6 @@ def write_option(option_key: str, option_obj: type(Options.Option)):
16001453
write_option(f_option, option)
16011454
AutoWorld.call_single(self.multiworld, "write_spoiler_header", player, outfile)
16021455

1603-
if player in self.multiworld.get_game_players("A Link to the Past"):
1604-
outfile.write('%s%s\n' % ('Hash: ', self.hashes[player]))
1605-
1606-
outfile.write('Logic: %s\n' % self.multiworld.logic[player])
1607-
outfile.write('Dark Room Logic: %s\n' % self.multiworld.dark_room_logic[player])
1608-
outfile.write('Mode: %s\n' % self.multiworld.mode[player])
1609-
outfile.write('Goal: %s\n' % self.multiworld.goal[player])
1610-
if "triforce" in self.multiworld.goal[player]: # triforce hunt
1611-
outfile.write("Pieces available for Triforce: %s\n" %
1612-
self.multiworld.triforce_pieces_available[player])
1613-
outfile.write("Pieces required for Triforce: %s\n" %
1614-
self.multiworld.triforce_pieces_required[player])
1615-
outfile.write('Difficulty: %s\n' % self.multiworld.difficulty[player])
1616-
outfile.write('Item Functionality: %s\n' % self.multiworld.item_functionality[player])
1617-
outfile.write('Entrance Shuffle: %s\n' % self.multiworld.shuffle[player])
1618-
if self.multiworld.shuffle[player] != "vanilla":
1619-
outfile.write('Entrance Shuffle Seed %s\n' % self.multiworld.worlds[player].er_seed)
1620-
outfile.write('Shop inventory shuffle: %s\n' %
1621-
bool_to_text("i" in self.multiworld.shop_shuffle[player]))
1622-
outfile.write('Shop price shuffle: %s\n' %
1623-
bool_to_text("p" in self.multiworld.shop_shuffle[player]))
1624-
outfile.write('Shop upgrade shuffle: %s\n' %
1625-
bool_to_text("u" in self.multiworld.shop_shuffle[player]))
1626-
outfile.write('New Shop inventory: %s\n' %
1627-
bool_to_text("g" in self.multiworld.shop_shuffle[player] or
1628-
"f" in self.multiworld.shop_shuffle[player]))
1629-
outfile.write('Custom Potion Shop: %s\n' %
1630-
bool_to_text("w" in self.multiworld.shop_shuffle[player]))
1631-
outfile.write('Enemy health: %s\n' % self.multiworld.enemy_health[player])
1632-
outfile.write('Enemy damage: %s\n' % self.multiworld.enemy_damage[player])
1633-
outfile.write('Prize shuffle %s\n' %
1634-
self.multiworld.shuffle_prizes[player])
16351456
if self.entrances:
16361457
outfile.write('\n\nEntrances:\n\n')
16371458
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.multiworld.get_player_name(entry["player"])}: '
@@ -1640,30 +1461,14 @@ def write_option(option_key: str, option_obj: type(Options.Option)):
16401461
'<=' if entry['direction'] == 'exit' else '=>',
16411462
entry['exit']) for entry in self.entrances.values()]))
16421463

1643-
if self.medallions:
1644-
outfile.write('\n\nMedallions:\n')
1645-
for dungeon, medallion in self.medallions.items():
1646-
outfile.write(f'\n{dungeon}: {medallion}')
1647-
16481464
AutoWorld.call_all(self.multiworld, "write_spoiler", outfile)
16491465

1466+
locations = [(str(location), str(location.item) if location.item is not None else "Nothing")
1467+
for location in self.multiworld.get_locations()]
16501468
outfile.write('\n\nLocations:\n\n')
16511469
outfile.write('\n'.join(
1652-
['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in
1653-
grouping.items()]))
1654-
1655-
if self.shops:
1656-
outfile.write('\n\nShops:\n\n')
1657-
outfile.write('\n'.join("{} [{}]\n {}".format(shop['location'], shop['type'], "\n ".join(
1658-
item for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if
1659-
item)) for shop in self.shops))
1660-
1661-
for player in self.multiworld.get_game_players("A Link to the Past"):
1662-
if self.multiworld.boss_shuffle[player] != 'none':
1663-
bossmap = self.bosses[str(player)] if self.multiworld.players > 1 else self.bosses
1664-
outfile.write(
1665-
f'\n\nBosses{(f" ({self.multiworld.get_player_name(player)})" if self.multiworld.players > 1 else "")}:\n')
1666-
outfile.write(' ' + '\n '.join([f'{x}: {y}' for x, y in bossmap.items()]))
1470+
['%s: %s' % (location, item) for location, item in locations]))
1471+
16671472
outfile.write('\n\nPlaythrough:\n\n')
16681473
outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join(
16691474
[' %s: %s' % (location, item) for (location, item) in sphere.items()] if sphere_nr != '0' else [

Launcher.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ def handles_file(self, path: str):
132132
Component('Text Client', 'CommonClient', 'ArchipelagoTextClient'),
133133
# SNI
134134
Component('SNI Client', 'SNIClient',
135-
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3', '.apsmw')),
135+
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3',
136+
'.apsmw', '.apl2ac')),
136137
Component('Links Awakening DX Client', 'LinksAwakeningClient',
137138
file_identifier=SuffixIdentifier('.apladx')),
138139
Component('LttP Adjuster', 'LttPAdjuster'),
@@ -153,6 +154,8 @@ def handles_file(self, path: str):
153154
Component('ChecksFinder Client', 'ChecksFinderClient'),
154155
# Starcraft 2
155156
Component('Starcraft 2 Client', 'Starcraft2Client'),
157+
# Wargroove
158+
Component('Wargroove Client', 'WargrooveClient'),
156159
# Zillion
157160
Component('Zillion Client', 'ZillionClient',
158161
file_identifier=SuffixIdentifier('.apzl')),

Main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
7979
world.state = CollectionState(world)
8080
logger.info('Archipelago Version %s - Seed: %s\n', __version__, world.seed)
8181

82-
logger.info("Found World Types:")
82+
logger.info(f"Found {len(AutoWorld.AutoWorldRegister.world_types)} World Types:")
8383
longest_name = max(len(text) for text in AutoWorld.AutoWorldRegister.world_types)
8484

8585
max_item = 0

ModuleUpdate.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
import subprocess
44
import pkg_resources
5+
import warnings
56

67
local_dir = os.path.dirname(__file__)
78
requirements_files = {os.path.join(local_dir, 'requirements.txt')}
@@ -39,15 +40,19 @@ def update(yes=False, force=False):
3940
path = os.path.join(os.path.dirname(__file__), req_file)
4041
with open(path) as requirementsfile:
4142
for line in requirementsfile:
43+
if not line or line[0] == "#":
44+
continue # ignore comments
4245
if line.startswith(("https://", "git+https://")):
4346
# extract name and version for url
4447
rest = line.split('/')[-1]
4548
line = ""
4649
if "#egg=" in rest:
4750
# from egg info
4851
rest, egg = rest.split("#egg=", 1)
49-
egg = egg.split(";", 1)[0]
52+
egg = egg.split(";", 1)[0].rstrip()
5053
if any(compare in egg for compare in ("==", ">=", ">", "<", "<=", "!=")):
54+
warnings.warn(f"Specifying version as #egg={egg} will become unavailable in pip 25.0. "
55+
"Use name @ url#version instead.", DeprecationWarning)
5156
line = egg
5257
else:
5358
egg = ""
@@ -58,16 +63,27 @@ def update(yes=False, force=False):
5863
rest = rest.replace(".zip", "-").replace(".tar.gz", "-")
5964
name, version, _ = rest.split("-", 2)
6065
line = f'{egg or name}=={version}'
66+
elif "@" in line and "#" in line:
67+
# PEP 508 does not allow us to specify a version, so we use custom syntax
68+
# name @ url#version ; marker
69+
name, rest = line.split("@", 1)
70+
version = rest.split("#", 1)[1].split(";", 1)[0].rstrip()
71+
line = f"{name.rstrip()}=={version}"
72+
if ";" in rest: # keep marker
73+
line += rest[rest.find(";"):]
6174
requirements = pkg_resources.parse_requirements(line)
62-
for requirement in requirements:
63-
requirement = str(requirement)
75+
for requirement in map(str, requirements):
6476
try:
6577
pkg_resources.require(requirement)
6678
except pkg_resources.ResolutionError:
6779
if not yes:
6880
import traceback
6981
traceback.print_exc()
70-
input(f'Requirement {requirement} is not satisfied, press enter to install it')
82+
try:
83+
input(f"\nRequirement {requirement} is not satisfied, press enter to install it")
84+
except KeyboardInterrupt:
85+
print("\nAborting")
86+
sys.exit(1)
7187
update_command()
7288
return
7389

0 commit comments

Comments
 (0)