Skip to content

Commit

Permalink
Merge branch 'main' into test_collect_remove
Browse files Browse the repository at this point in the history
  • Loading branch information
Berserker66 authored Sep 15, 2023
2 parents 3fa27f9 + 648d682 commit 32d5e5c
Show file tree
Hide file tree
Showing 172 changed files with 4,372 additions and 2,411 deletions.
14 changes: 4 additions & 10 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,10 @@ def get_placeable_locations(self, state=None, player=None) -> List[Location]:
def get_unfilled_locations_for_players(self, location_names: List[str], players: Iterable[int]):
for player in players:
if not location_names:
location_names = [location.name for location in self.get_unfilled_locations(player)]
for location_name in location_names:
valid_locations = [location.name for location in self.get_unfilled_locations(player)]
else:
valid_locations = location_names
for location_name in valid_locations:
location = self._location_cache.get((location_name, player), None)
if location is not None and location.item is None:
yield location
Expand Down Expand Up @@ -851,14 +853,6 @@ def can_reach(self, state: CollectionState) -> bool:
state.update_reachable_regions(self.player)
return self in state.reachable_regions[self.player]

def can_reach_private(self, state: CollectionState) -> bool:
for entrance in self.entrances:
if entrance.can_reach(state):
if not self in state.path:
state.path[self] = (self.name, state.path.get(entrance, None))
return True
return False

@property
def hint_text(self) -> str:
return self._hint_text if self._hint_text else self.name
Expand Down
83 changes: 50 additions & 33 deletions LttPAdjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from worlds.alttp.Rom import Sprite, LocalRom, apply_rom_settings, get_base_rom_bytes
from Utils import output_path, local_path, user_path, open_file, get_cert_none_ssl_context, persistent_store, \
get_adjuster_settings, tkinter_center_window, init_logging
get_adjuster_settings, get_adjuster_settings_no_defaults, tkinter_center_window, init_logging


GAME_ALTTP = "A Link to the Past"
Expand All @@ -43,6 +43,47 @@ class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
def _get_help_string(self, action):
return textwrap.dedent(action.help)

# See argparse.BooleanOptionalAction
class BooleanOptionalActionWithDisable(argparse.Action):
def __init__(self,
option_strings,
dest,
default=None,
type=None,
choices=None,
required=False,
help=None,
metavar=None):

_option_strings = []
for option_string in option_strings:
_option_strings.append(option_string)

if option_string.startswith('--'):
option_string = '--disable' + option_string[2:]
_option_strings.append(option_string)

if help is not None and default is not None:
help += " (default: %(default)s)"

super().__init__(
option_strings=_option_strings,
dest=dest,
nargs=0,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)

def __call__(self, parser, namespace, values, option_string=None):
if option_string in self.option_strings:
setattr(namespace, self.dest, not option_string.startswith('--disable'))

def format_usage(self):
return ' | '.join(self.option_strings)


def get_argparser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
Expand All @@ -52,6 +93,8 @@ def get_argparser() -> argparse.ArgumentParser:
help='Path to an ALttP Japan(1.0) rom to use as a base.')
parser.add_argument('--loglevel', default='info', const='info', nargs='?',
choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
parser.add_argument('--auto_apply', default='ask',
choices=['ask', 'always', 'never'], help='Whether or not to apply settings automatically in the future.')
parser.add_argument('--menuspeed', default='normal', const='normal', nargs='?',
choices=['normal', 'instant', 'double', 'triple', 'quadruple', 'half'],
help='''\
Expand All @@ -61,7 +104,7 @@ def get_argparser() -> argparse.ArgumentParser:
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
parser.add_argument('--deathlink', help='Enable DeathLink system.', action='store_true')
parser.add_argument('--allowcollect', help='Allow collection of other player items', action='store_true')
parser.add_argument('--disablemusic', help='Disables game music.', action='store_true')
parser.add_argument('--music', default=True, help='Enables/Disables game music.', action=BooleanOptionalActionWithDisable)
parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?',
choices=['normal', 'hide_goal', 'hide_required', 'hide_both'],
help='''\
Expand Down Expand Up @@ -104,21 +147,23 @@ def get_argparser() -> argparse.ArgumentParser:
Alternatively, can be a ALttP Rom patched with a Link
sprite that will be extracted.
''')
parser.add_argument('--sprite_pool', nargs='+', default=[], help='''
A list of sprites to pull from.
''')
parser.add_argument('--oof', help='''\
Path to a sound effect to replace Link's "oof" sound.
Needs to be in a .brr format and have a length of no
more than 2673 bytes, created from a 16-bit signed PCM
.wav at 12khz. https://github.com/boldowa/snesbrr
''')
parser.add_argument('--names', default='', type=str)
parser.add_argument('--update_sprites', action='store_true', help='Update Sprite Database, then exit.')
return parser


def main():
parser = get_argparser()
args = parser.parse_args()
args.music = not args.disablemusic
args = parser.parse_args(namespace=get_adjuster_settings_no_defaults(GAME_ALTTP))

# set up logger
loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[
args.loglevel]
Expand Down Expand Up @@ -530,9 +575,6 @@ def hide(self):

def get_rom_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)
if not adjuster_settings:
adjuster_settings = Namespace()
adjuster_settings.baserom = "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"

romFrame = Frame(parent)
baseRomLabel = Label(romFrame, text='LttP Base Rom: ')
Expand Down Expand Up @@ -560,33 +602,8 @@ def RomSelect():

return romFrame, romVar


def get_rom_options_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)
defaults = {
"auto_apply": 'ask',
"music": True,
"reduceflashing": True,
"deathlink": False,
"sprite": None,
"oof": None,
"quickswap": True,
"menuspeed": 'normal',
"heartcolor": 'red',
"heartbeep": 'normal',
"ow_palettes": 'default',
"uw_palettes": 'default',
"hud_palettes": 'default',
"sword_palettes": 'default',
"shield_palettes": 'default',
"sprite_pool": [],
"allowcollect": False,
}
if not adjuster_settings:
adjuster_settings = Namespace()
for key, defaultvalue in defaults.items():
if not hasattr(adjuster_settings, key):
setattr(adjuster_settings, key, defaultvalue)

romOptionsFrame = LabelFrame(parent, text="Rom options")
romOptionsFrame.columnconfigure(0, weight=1)
Expand Down
16 changes: 10 additions & 6 deletions MMBN3Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def __init__(self, server_address, password):
self.auth_name = None
self.slot_data = dict()
self.patching_error = False
self.sent_hints = []

async def server_auth(self, password_requested: bool = False):
if password_requested and not self.password:
Expand Down Expand Up @@ -175,13 +176,16 @@ async def parse_payload(payload: dict, ctx: MMBN3Context, force: bool):

# If trade hinting is enabled, send scout checks
if ctx.slot_data.get("trade_quest_hinting", 0) == 2:
scouted_locs = [loc.id for loc in scoutable_locations
trade_bits = [loc.id for loc in scoutable_locations
if check_location_scouted(loc, payload["locations"])]
await ctx.send_msgs([{
"cmd": "LocationScouts",
"locations": scouted_locs,
"create_as_hint": 2
}])
scouted_locs = [loc for loc in trade_bits if loc not in ctx.sent_hints]
if len(scouted_locs) > 0:
ctx.sent_hints.extend(scouted_locs)
await ctx.send_msgs([{
"cmd": "LocationScouts",
"locations": scouted_locs,
"create_as_hint": 2
}])


def check_location_packet(location, memory):
Expand Down
3 changes: 1 addition & 2 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,15 +392,14 @@ def precollect_hint(location):
f.write(bytes([3])) # version of format
f.write(multidata)

multidata_task = pool.submit(write_multidata)
output_file_futures.append(pool.submit(write_multidata))
if not check_accessibility_task.result():
if not world.can_beat_game():
raise Exception("Game appears as unbeatable. Aborting.")
else:
logger.warning("Location Accessibility requirements not fulfilled.")

# retrieve exceptions via .result() if they occurred.
multidata_task.result()
for i, future in enumerate(concurrent.futures.as_completed(output_file_futures), start=1):
if i % 10 == 0 or i == len(output_file_futures):
logger.info(f'Generating output files ({i}/{len(output_file_futures)}).')
Expand Down
4 changes: 3 additions & 1 deletion MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2118,13 +2118,15 @@ def attrtype(input_text: str):
async def console(ctx: Context):
import sys
queue = asyncio.Queue()
Utils.stream_input(sys.stdin, queue)
worker = Utils.stream_input(sys.stdin, queue)
while not ctx.exit_event.is_set():
try:
# I don't get why this while loop is needed. Works fine without it on clients,
# but the queue.get() for server never fulfills if the queue is empty when entering the await.
while queue.qsize() == 0:
await asyncio.sleep(0.05)
if not worker.is_alive():
return
input_text = await queue.get()
queue.task_done()
ctx.commandprocessor(input_text)
Expand Down
12 changes: 9 additions & 3 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def verify(self, world: typing.Type[World], player_name: str, plando_options: "P
f"Did you mean '{picks[0][0]}' ({picks[0][1]}% sure)")


class OptionDict(Option[typing.Dict[str, typing.Any]], VerifyKeys):
class OptionDict(Option[typing.Dict[str, typing.Any]], VerifyKeys, typing.Mapping[str, typing.Any]):
default: typing.Dict[str, typing.Any] = {}
supports_weighting = False

Expand All @@ -789,8 +789,14 @@ def from_any(cls, data: typing.Dict[str, typing.Any]) -> OptionDict:
def get_option_name(self, value):
return ", ".join(f"{key}: {v}" for key, v in value.items())

def __contains__(self, item):
return item in self.value
def __getitem__(self, item: str) -> typing.Any:
return self.value.__getitem__(item)

def __iter__(self) -> typing.Iterator[str]:
return self.value.__iter__()

def __len__(self) -> int:
return self.value.__len__()


class ItemDict(OptionDict):
Expand Down
6 changes: 4 additions & 2 deletions PokemonClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ async def parse_locations(data: List, ctx: GBContext):
elif (location_name_to_id["Fossil - Choice B"] in ctx.checked_locations and location_name_to_id["Fossil - Choice A"]
not in ctx.checked_locations):
hints.append("Fossil - Choice A")
hints = [location_name_to_id[loc] for loc in hints if loc not in ctx.auto_hints and location_name_to_id[loc] in
ctx.missing_locations and location_name_to_id[loc] not in ctx.locations_checked]
hints = [
location_name_to_id[loc] for loc in hints if location_name_to_id[loc] not in ctx.auto_hints and
location_name_to_id[loc] in ctx.missing_locations and location_name_to_id[loc] not in ctx.locations_checked
]
if hints:
await ctx.send_msgs([{"cmd": "LocationScouts", "locations": hints, "create_as_hint": 2}])
ctx.auto_hints.update(hints)
Expand Down
23 changes: 12 additions & 11 deletions SNIClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,11 @@ def connect_to_snes(self, snes_options: str = "") -> bool:
options = snes_options.split()
num_options = len(options)

if num_options > 0:
snes_device_number = int(options[0])

if num_options > 1:
snes_address = options[0]
snes_device_number = int(options[1])
elif num_options > 0:
snes_device_number = int(options[0])

self.ctx.snes_reconnect_address = None
if self.ctx.snes_connect_task:
Expand Down Expand Up @@ -565,14 +564,16 @@ async def snes_write(ctx: SNIContext, write_list: typing.List[typing.Tuple[int,
PutAddress_Request: SNESRequest = {"Opcode": "PutAddress", "Operands": [], 'Space': 'SNES'}
try:
for address, data in write_list:
PutAddress_Request['Operands'] = [hex(address)[2:], hex(len(data))[2:]]
# REVIEW: above: `if snes_socket is None: return False`
# Does it need to be checked again?
if ctx.snes_socket is not None:
await ctx.snes_socket.send(dumps(PutAddress_Request))
await ctx.snes_socket.send(data)
else:
snes_logger.warning(f"Could not send data to SNES: {data}")
while data:
# Divide the write into packets of 256 bytes.
PutAddress_Request['Operands'] = [hex(address)[2:], hex(min(len(data), 256))[2:]]
if ctx.snes_socket is not None:
await ctx.snes_socket.send(dumps(PutAddress_Request))
await ctx.snes_socket.send(data[:256])
address += 256
data = data[256:]
else:
snes_logger.warning(f"Could not send data to SNES: {data}")
except ConnectionClosed:
return False

Expand Down
Loading

0 comments on commit 32d5e5c

Please sign in to comment.