Compare commits

...

10 Commits

Author SHA1 Message Date
Holly 04efdf11d7 Make webhost directly executable 2024-06-10 23:38:02 +00:00
Fabian Dill 75bef3ddb1
Various: fix absolute imports in worlds (#3489) 2024-06-11 00:42:57 +02:00
JusticePS 484082616f
Adventure: Update to use new options api (#3326) 2024-06-11 00:42:01 +02:00
Aaron Wagener 35617bdac5
Tests: Add checksum validation to the postgen datapackage test (#3456)
* Tests: Add checksum validation to the postgen datapackage test

* add a special case for the test world datapackage rather than hidden

* add the test world to the datapackage instead of special casing around it

---------

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
2024-06-10 09:28:28 +02:00
Phaneros 0a912808e3
SC2: update inno_setup.iss to remove old sc2wol world folder (#3495) 2024-06-10 02:05:39 +02:00
Phaneros 84a6d50ae7
sc2: Fixed sc2 client's /received command breaking after PR 1933 merged (#3497) 2024-06-09 16:55:05 +02:00
jamesbrq 5f8a8e6dad
Update Rom.py (#3498) 2024-06-09 16:54:07 +02:00
Phaneros 2198a70251
Core: CommonClient: command history and echo (#3236)
* client: Added command history access with up/down and command echo in common client

* client: Changed command echo colour to orange

* client: removed star import from typing

* client: updated code style to match style guideline

* client: adjusted ordering of calling parent constructor in command prompt input constructor

* client: Fixed issues identified by beauxq in PR; fixed some typing issues

* client: PR comments; replaced command history list with deque
2024-06-09 04:08:47 +02:00
Fabian Dill c478e55d7a
Generate: improve logging capture (#3484) 2024-06-09 03:13:27 +02:00
Fabian Dill 76804d295b
Core: explicitly import importlib.util (#3224) 2024-06-08 20:04:17 +02:00
22 changed files with 162 additions and 75 deletions

View File

@ -80,7 +80,7 @@ class AdventureContext(CommonContext):
self.local_item_locations = {} self.local_item_locations = {}
self.dragon_speed_info = {} self.dragon_speed_info = {}
options = Utils.get_options() options = Utils.get_settings()
self.display_msgs = options["adventure_options"]["display_msgs"] self.display_msgs = options["adventure_options"]["display_msgs"]
async def server_auth(self, password_requested: bool = False): async def server_auth(self, password_requested: bool = False):
@ -102,7 +102,7 @@ class AdventureContext(CommonContext):
def on_package(self, cmd: str, args: dict): def on_package(self, cmd: str, args: dict):
if cmd == 'Connected': if cmd == 'Connected':
self.locations_array = None self.locations_array = None
if Utils.get_options()["adventure_options"].get("death_link", False): if Utils.get_settings()["adventure_options"].get("death_link", False):
self.set_deathlink = True self.set_deathlink = True
async_start(self.get_freeincarnates_used()) async_start(self.get_freeincarnates_used())
elif cmd == "RoomInfo": elif cmd == "RoomInfo":
@ -415,8 +415,8 @@ async def atari_sync_task(ctx: AdventureContext):
async def run_game(romfile): async def run_game(romfile):
auto_start = Utils.get_options()["adventure_options"].get("rom_start", True) auto_start = Utils.get_settings()["adventure_options"].get("rom_start", True)
rom_args = Utils.get_options()["adventure_options"].get("rom_args") rom_args = Utils.get_settings()["adventure_options"].get("rom_args")
if auto_start is True: if auto_start is True:
import webbrowser import webbrowser
webbrowser.open(romfile) webbrowser.open(romfile)

View File

@ -494,6 +494,11 @@ class CommonContext:
Returned text is sent, or sending is aborted if None is returned.""" Returned text is sent, or sending is aborted if None is returned."""
return text return text
def on_ui_command(self, text: str) -> None:
"""Gets called by kivy when the user executes a command starting with `/` or `!`.
The command processor is still called; this is just intended for command echoing."""
self.ui.print_json([{"text": text, "type": "color", "color": "orange"}])
def update_permissions(self, permissions: typing.Dict[str, int]): def update_permissions(self, permissions: typing.Dict[str, int]):
for permission_name, permission_flag in permissions.items(): for permission_name, permission_flag in permissions.items():
try: try:

View File

@ -1,10 +1,12 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import copy
import logging import logging
import os import os
import random import random
import string import string
import sys
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from collections import Counter from collections import Counter
@ -15,21 +17,16 @@ import ModuleUpdate
ModuleUpdate.update() ModuleUpdate.update()
import copy
import Utils import Utils
import Options import Options
from BaseClasses import seeddigits, get_seed, PlandoOptions from BaseClasses import seeddigits, get_seed, PlandoOptions
from Main import main as ERmain
from settings import get_settings
from Utils import parse_yamls, version_tuple, __version__, tuplize_version from Utils import parse_yamls, version_tuple, __version__, tuplize_version
from worlds.alttp.EntranceRandomizer import parse_arguments
from worlds.AutoWorld import AutoWorldRegister
from worlds import failed_world_loads
def mystery_argparse(): def mystery_argparse():
options = get_settings() from settings import get_settings
defaults = options.generator settings = get_settings()
defaults = settings.generator
parser = argparse.ArgumentParser(description="CMD Generation Interface, defaults come from host.yaml.") parser = argparse.ArgumentParser(description="CMD Generation Interface, defaults come from host.yaml.")
parser.add_argument('--weights_file_path', default=defaults.weights_file_path, parser.add_argument('--weights_file_path', default=defaults.weights_file_path,
@ -41,7 +38,7 @@ def mystery_argparse():
parser.add_argument('--seed', help='Define seed number to generate.', type=int) parser.add_argument('--seed', help='Define seed number to generate.', type=int)
parser.add_argument('--multi', default=defaults.players, type=lambda value: max(int(value), 1)) parser.add_argument('--multi', default=defaults.players, type=lambda value: max(int(value), 1))
parser.add_argument('--spoiler', type=int, default=defaults.spoiler) parser.add_argument('--spoiler', type=int, default=defaults.spoiler)
parser.add_argument('--outputpath', default=options.general_options.output_path, parser.add_argument('--outputpath', default=settings.general_options.output_path,
help="Path to output folder. Absolute or relative to cwd.") # absolute or relative to cwd help="Path to output folder. Absolute or relative to cwd.") # absolute or relative to cwd
parser.add_argument('--race', action='store_true', default=defaults.race) parser.add_argument('--race', action='store_true', default=defaults.race)
parser.add_argument('--meta_file_path', default=defaults.meta_file_path) parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
@ -61,20 +58,21 @@ def mystery_argparse():
if not os.path.isabs(args.meta_file_path): if not os.path.isabs(args.meta_file_path):
args.meta_file_path = os.path.join(args.player_files_path, args.meta_file_path) args.meta_file_path = os.path.join(args.player_files_path, args.meta_file_path)
args.plando: PlandoOptions = PlandoOptions.from_option_string(args.plando) args.plando: PlandoOptions = PlandoOptions.from_option_string(args.plando)
return args, options return args
def get_seed_name(random_source) -> str: def get_seed_name(random_source) -> str:
return f"{random_source.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits) return f"{random_source.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits)
def main(args=None, callback=ERmain): def main(args=None):
if not args: if not args:
args, options = mystery_argparse() args = mystery_argparse()
else:
options = get_settings()
seed = get_seed(args.seed) seed = get_seed(args.seed)
# __name__ == "__main__" check so unittests that already imported worlds don't trip this.
if __name__ == "__main__" and "worlds" in sys.modules:
raise Exception("Worlds system should not be loaded before logging init.")
Utils.init_logging(f"Generate_{seed}", loglevel=args.log_level) Utils.init_logging(f"Generate_{seed}", loglevel=args.log_level)
random.seed(seed) random.seed(seed)
seed_name = get_seed_name(random) seed_name = get_seed_name(random)
@ -143,6 +141,9 @@ def main(args=None, callback=ERmain):
raise Exception(f"No weights found. " raise Exception(f"No weights found. "
f"Provide a general weights file ({args.weights_file_path}) or individual player files. " f"Provide a general weights file ({args.weights_file_path}) or individual player files. "
f"A mix is also permitted.") f"A mix is also permitted.")
from worlds.AutoWorld import AutoWorldRegister
from worlds.alttp.EntranceRandomizer import parse_arguments
erargs = parse_arguments(['--multi', str(args.multi)]) erargs = parse_arguments(['--multi', str(args.multi)])
erargs.seed = seed erargs.seed = seed
erargs.plando_options = args.plando erargs.plando_options = args.plando
@ -234,7 +235,8 @@ def main(args=None, callback=ERmain):
with open(os.path.join(args.outputpath if args.outputpath else ".", f"generate_{seed_name}.yaml"), "wt") as f: with open(os.path.join(args.outputpath if args.outputpath else ".", f"generate_{seed_name}.yaml"), "wt") as f:
yaml.dump(important, f) yaml.dump(important, f)
return callback(erargs, seed) from Main import main as ERmain
return ERmain(erargs, seed)
def read_weights_yamls(path) -> Tuple[Any, ...]: def read_weights_yamls(path) -> Tuple[Any, ...]:
@ -359,6 +361,8 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any: def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any:
from worlds import AutoWorldRegister
if not game: if not game:
return get_choice(option_key, category_dict) return get_choice(option_key, category_dict)
if game in AutoWorldRegister.world_types: if game in AutoWorldRegister.world_types:
@ -436,10 +440,13 @@ def handle_option(ret: argparse.Namespace, game_weights: dict, option_key: str,
except Exception as e: except Exception as e:
raise Options.OptionError(f"Error generating option {option_key} in {ret.game}") from e raise Options.OptionError(f"Error generating option {option_key} in {ret.game}") from e
else: else:
from worlds import AutoWorldRegister
player_option.verify(AutoWorldRegister.world_types[ret.game], ret.name, plando_options) player_option.verify(AutoWorldRegister.world_types[ret.game], ret.name, plando_options)
def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.bosses): def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.bosses):
from worlds import AutoWorldRegister
if "linked_options" in weights: if "linked_options" in weights:
weights = roll_linked_options(weights) weights = roll_linked_options(weights)
@ -466,6 +473,7 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
ret.game = get_choice("game", weights) ret.game = get_choice("game", weights)
if ret.game not in AutoWorldRegister.world_types: if ret.game not in AutoWorldRegister.world_types:
from worlds import failed_world_loads
picks = Utils.get_fuzzy_results(ret.game, list(AutoWorldRegister.world_types) + failed_world_loads, limit=1)[0] picks = Utils.get_fuzzy_results(ret.game, list(AutoWorldRegister.world_types) + failed_world_loads, limit=1)[0]
if picks[0] in failed_world_loads: if picks[0] in failed_world_loads:
raise Exception(f"No functional world found to handle game {ret.game}. " raise Exception(f"No functional world found to handle game {ret.game}. "

View File

@ -198,7 +198,8 @@ class JSONtoTextParser(metaclass=HandlerMeta):
"slateblue": "6D8BE8", "slateblue": "6D8BE8",
"plum": "AF99EF", "plum": "AF99EF",
"salmon": "FA8072", "salmon": "FA8072",
"white": "FFFFFF" "white": "FFFFFF",
"orange": "FF7700",
} }
def __init__(self, ctx): def __init__(self, ctx):

View File

@ -553,6 +553,7 @@ def init_logging(name: str, loglevel: typing.Union[str, int] = logging.INFO, wri
f"Archipelago ({__version__}) logging initialized" f"Archipelago ({__version__}) logging initialized"
f" on {platform.platform()}" f" on {platform.platform()}"
f" running Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" f" running Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
f"{' (frozen)' if is_frozen() else ''}"
) )

2
WebHost.py Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env python3
import os import os
import multiprocessing import multiprocessing
import logging import logging

View File

@ -13,6 +13,7 @@
plum: "AF99EF" # typically progression item plum: "AF99EF" # typically progression item
salmon: "FA8072" # typically trap item salmon: "FA8072" # typically trap item
white: "FFFFFF" # not used, if you want to change the generic text color change color in Label white: "FFFFFF" # not used, if you want to change the generic text color change color in Label
orange: "FF7700" # Used for command echo
<Label>: <Label>:
color: "FFFFFF" color: "FFFFFF"
<TabbedPanel>: <TabbedPanel>:

View File

@ -89,6 +89,9 @@ Type: files; Name: "{app}\ArchipelagoPokemonClient.exe"
Type: files; Name: "{app}\data\lua\connector_pkmn_rb.lua" Type: files; Name: "{app}\data\lua\connector_pkmn_rb.lua"
Type: filesandordirs; Name: "{app}\lib\worlds\rogue-legacy" Type: filesandordirs; Name: "{app}\lib\worlds\rogue-legacy"
Type: dirifempty; Name: "{app}\lib\worlds\rogue-legacy" Type: dirifempty; Name: "{app}\lib\worlds\rogue-legacy"
Type: files; Name: "{app}\lib\worlds\sc2wol.apworld"
Type: filesandordirs; Name: "{app}\lib\worlds\sc2wol"
Type: dirifempty; Name: "{app}\lib\worlds\sc2wol"
Type: filesandordirs; Name: "{app}\lib\worlds\bk_sudoku" Type: filesandordirs; Name: "{app}\lib\worlds\bk_sudoku"
Type: dirifempty; Name: "{app}\lib\worlds\bk_sudoku" Type: dirifempty; Name: "{app}\lib\worlds\bk_sudoku"
Type: files; Name: "{app}\ArchipelagoLauncher(DEBUG).exe" Type: files; Name: "{app}\ArchipelagoLauncher(DEBUG).exe"

62
kvui.py
View File

@ -3,6 +3,7 @@ import logging
import sys import sys
import typing import typing
import re import re
from collections import deque
if sys.platform == "win32": if sys.platform == "win32":
import ctypes import ctypes
@ -380,6 +381,57 @@ class ConnectBarTextInput(TextInput):
return super(ConnectBarTextInput, self).insert_text(s, from_undo=from_undo) return super(ConnectBarTextInput, self).insert_text(s, from_undo=from_undo)
def is_command_input(string: str) -> bool:
return len(string) > 0 and string[0] in "/!"
class CommandPromptTextInput(TextInput):
MAXIMUM_HISTORY_MESSAGES = 50
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self._command_history_index = -1
self._command_history: typing.Deque[str] = deque(maxlen=CommandPromptTextInput.MAXIMUM_HISTORY_MESSAGES)
def update_history(self, new_entry: str) -> None:
self._command_history_index = -1
if is_command_input(new_entry):
self._command_history.appendleft(new_entry)
def keyboard_on_key_down(
self,
window,
keycode: typing.Tuple[int, str],
text: typing.Optional[str],
modifiers: typing.List[str]
) -> bool:
"""
:param window: The kivy window object
:param keycode: A tuple of (keycode, keyname). Keynames are always lowercase
:param text: The text printed by this key, not accounting for modifiers, or `None` if no text.
Seems to pretty naively interpret the keycode as unicode, so numlock can return odd characters.
:param modifiers: A list of string modifiers, like `ctrl` or `numlock`
"""
if keycode[1] == 'up':
self._change_to_history_text_if_available(self._command_history_index + 1)
return True
if keycode[1] == 'down':
self._change_to_history_text_if_available(self._command_history_index - 1)
return True
return super().keyboard_on_key_down(window, keycode, text, modifiers)
def _change_to_history_text_if_available(self, new_index: int) -> None:
if new_index < -1:
return
if new_index >= len(self._command_history):
return
self._command_history_index = new_index
if new_index == -1:
self.text = ""
return
self.text = self._command_history[self._command_history_index]
class MessageBox(Popup): class MessageBox(Popup):
class MessageBoxLabel(Label): class MessageBoxLabel(Label):
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -415,7 +467,7 @@ class GameManager(App):
self.commandprocessor = ctx.command_processor(ctx) self.commandprocessor = ctx.command_processor(ctx)
self.icon = r"data/icon.png" self.icon = r"data/icon.png"
self.json_to_kivy_parser = KivyJSONtoTextParser(ctx) self.json_to_kivy_parser = KivyJSONtoTextParser(ctx)
self.log_panels = {} self.log_panels: typing.Dict[str, Widget] = {}
# keep track of last used command to autofill on click # keep track of last used command to autofill on click
self.last_autofillable_command = "hint" self.last_autofillable_command = "hint"
@ -499,7 +551,7 @@ class GameManager(App):
info_button = Button(size=(dp(100), dp(30)), text="Command:", size_hint_x=None) info_button = Button(size=(dp(100), dp(30)), text="Command:", size_hint_x=None)
info_button.bind(on_release=self.command_button_action) info_button.bind(on_release=self.command_button_action)
bottom_layout.add_widget(info_button) bottom_layout.add_widget(info_button)
self.textinput = TextInput(size_hint_y=None, height=dp(30), multiline=False, write_tab=False) self.textinput = CommandPromptTextInput(size_hint_y=None, height=dp(30), multiline=False, write_tab=False)
self.textinput.bind(on_text_validate=self.on_message) self.textinput.bind(on_text_validate=self.on_message)
self.textinput.text_validate_unfocus = False self.textinput.text_validate_unfocus = False
bottom_layout.add_widget(self.textinput) bottom_layout.add_widget(self.textinput)
@ -557,14 +609,18 @@ class GameManager(App):
self.ctx.exit_event.set() self.ctx.exit_event.set()
def on_message(self, textinput: TextInput): def on_message(self, textinput: CommandPromptTextInput):
try: try:
input_text = textinput.text.strip() input_text = textinput.text.strip()
textinput.text = "" textinput.text = ""
textinput.update_history(input_text)
if self.ctx.input_requests > 0: if self.ctx.input_requests > 0:
self.ctx.input_requests -= 1 self.ctx.input_requests -= 1
self.ctx.input_queue.put_nowait(input_text) self.ctx.input_queue.put_nowait(input_text)
elif is_command_input(input_text):
self.ctx.on_ui_command(input_text)
self.commandprocessor(input_text)
elif input_text: elif input_text:
self.commandprocessor(input_text) self.commandprocessor(input_text)

View File

@ -2,6 +2,7 @@ from argparse import Namespace
from typing import List, Optional, Tuple, Type, Union from typing import List, Optional, Tuple, Type, Union
from BaseClasses import CollectionState, Item, ItemClassification, Location, MultiWorld, Region from BaseClasses import CollectionState, Item, ItemClassification, Location, MultiWorld, Region
from worlds import network_data_package
from worlds.AutoWorld import World, call_all from worlds.AutoWorld import World, call_all
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill") gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
@ -60,6 +61,10 @@ class TestWorld(World):
hidden = True hidden = True
# add our test world to the data package, so we can test it later
network_data_package["games"][TestWorld.game] = TestWorld.get_data_package_data()
def generate_test_multiworld(players: int = 1) -> MultiWorld: def generate_test_multiworld(players: int = 1) -> MultiWorld:
""" """
Generates a multiworld using a special Test Case World class, and seed of 0. Generates a multiworld using a special Test Case World class, and seed of 0.

View File

@ -1,6 +1,7 @@
import unittest import unittest
from Fill import distribute_items_restrictive from Fill import distribute_items_restrictive
from worlds import network_data_package
from worlds.AutoWorld import AutoWorldRegister, call_all from worlds.AutoWorld import AutoWorldRegister, call_all
from . import setup_solo_multiworld from . import setup_solo_multiworld
@ -84,3 +85,4 @@ class TestIDs(unittest.TestCase):
f"{loc_name} is not a valid item name for location_name_to_id") f"{loc_name} is not a valid item name for location_name_to_id")
self.assertIsInstance(loc_id, int, self.assertIsInstance(loc_id, int,
f"{loc_id} for {loc_name} should be an int") f"{loc_id} for {loc_name} should be an int")
self.assertEqual(datapackage["checksum"], network_data_package["games"][gamename]["checksum"])

View File

@ -1,4 +1,5 @@
import importlib import importlib
import importlib.util
import logging import logging
import os import os
import sys import sys

View File

@ -2,7 +2,8 @@ from __future__ import annotations
from typing import Dict from typing import Dict
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle from dataclasses import dataclass
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle, PerGameCommonOptions
class FreeincarnateMax(Range): class FreeincarnateMax(Range):
@ -223,22 +224,22 @@ class StartCastle(Choice):
option_white = 2 option_white = 2
default = option_yellow default = option_yellow
@dataclass
class AdventureOptions(PerGameCommonOptions):
dragon_slay_check: DragonSlayCheck
death_link: DeathLink
bat_logic: BatLogic
freeincarnate_max: FreeincarnateMax
dragon_rando_type: DragonRandoType
connector_multi_slot: ConnectorMultiSlot
yorgle_speed: YorgleStartingSpeed
yorgle_min_speed: YorgleMinimumSpeed
grundle_speed: GrundleStartingSpeed
grundle_min_speed: GrundleMinimumSpeed
rhindle_speed: RhindleStartingSpeed
rhindle_min_speed: RhindleMinimumSpeed
difficulty_switch_a: DifficultySwitchA
difficulty_switch_b: DifficultySwitchB
start_castle: StartCastle
adventure_option_definitions: Dict[str, type(Option)] = {
"dragon_slay_check": DragonSlayCheck,
"death_link": DeathLink,
"bat_logic": BatLogic,
"freeincarnate_max": FreeincarnateMax,
"dragon_rando_type": DragonRandoType,
"connector_multi_slot": ConnectorMultiSlot,
"yorgle_speed": YorgleStartingSpeed,
"yorgle_min_speed": YorgleMinimumSpeed,
"grundle_speed": GrundleStartingSpeed,
"grundle_min_speed": GrundleMinimumSpeed,
"rhindle_speed": RhindleStartingSpeed,
"rhindle_min_speed": RhindleMinimumSpeed,
"difficulty_switch_a": DifficultySwitchA,
"difficulty_switch_b": DifficultySwitchB,
"start_castle": StartCastle,
}

View File

@ -1,4 +1,5 @@
from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType
from Options import PerGameCommonOptions
from .Locations import location_table, LocationData, AdventureLocation, dragon_room_to_region from .Locations import location_table, LocationData, AdventureLocation, dragon_room_to_region
@ -24,7 +25,7 @@ def connect(world: MultiWorld, player: int, source: str, target: str, rule: call
connect(world, player, target, source, rule, True) connect(world, player, target, source, rule, True)
def create_regions(multiworld: MultiWorld, player: int, dragon_rooms: []) -> None: def create_regions(options: PerGameCommonOptions, multiworld: MultiWorld, player: int, dragon_rooms: []) -> None:
menu = Region("Menu", player, multiworld) menu = Region("Menu", player, multiworld)
@ -74,7 +75,7 @@ def create_regions(multiworld: MultiWorld, player: int, dragon_rooms: []) -> Non
credits_room_far_side.exits.append(Entrance(player, "CreditsFromFarSide", credits_room_far_side)) credits_room_far_side.exits.append(Entrance(player, "CreditsFromFarSide", credits_room_far_side))
multiworld.regions.append(credits_room_far_side) multiworld.regions.append(credits_room_far_side)
dragon_slay_check = multiworld.dragon_slay_check[player].value dragon_slay_check = options.dragon_slay_check.value
priority_locations = determine_priority_locations(multiworld, dragon_slay_check) priority_locations = determine_priority_locations(multiworld, dragon_slay_check)
for name, location_data in location_table.items(): for name, location_data in location_table.items():

View File

@ -6,7 +6,7 @@ from BaseClasses import LocationProgressType
def set_rules(self) -> None: def set_rules(self) -> None:
world = self.multiworld world = self.multiworld
use_bat_logic = world.bat_logic[self.player].value == BatLogic.option_use_logic use_bat_logic = self.options.bat_logic.value == BatLogic.option_use_logic
set_rule(world.get_entrance("YellowCastlePort", self.player), set_rule(world.get_entrance("YellowCastlePort", self.player),
lambda state: state.has("Yellow Key", self.player)) lambda state: state.has("Yellow Key", self.player))
@ -28,7 +28,7 @@ def set_rules(self) -> None:
lambda state: state.has("Bridge", self.player) or lambda state: state.has("Bridge", self.player) or
state.has("Magnet", self.player)) state.has("Magnet", self.player))
dragon_slay_check = world.dragon_slay_check[self.player].value dragon_slay_check = self.options.dragon_slay_check.value
if dragon_slay_check: if dragon_slay_check:
if self.difficulty_switch_b == DifficultySwitchB.option_hard_with_unlock_item: if self.difficulty_switch_b == DifficultySwitchB.option_hard_with_unlock_item:
set_rule(world.get_location("Slay Yorgle", self.player), set_rule(world.get_location("Slay Yorgle", self.player),

View File

@ -15,7 +15,8 @@ from Options import AssembleOptions
from worlds.AutoWorld import WebWorld, World from worlds.AutoWorld import WebWorld, World
from Fill import fill_restrictive from Fill import fill_restrictive
from worlds.generic.Rules import add_rule, set_rule from worlds.generic.Rules import add_rule, set_rule
from .Options import adventure_option_definitions, DragonRandoType, DifficultySwitchA, DifficultySwitchB from .Options import DragonRandoType, DifficultySwitchA, DifficultySwitchB, \
AdventureOptions
from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, \ from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, \
AdventureAutoCollectLocation AdventureAutoCollectLocation
from .Items import item_table, ItemData, nothing_item_id, event_table, AdventureItem, standard_item_max from .Items import item_table, ItemData, nothing_item_id, event_table, AdventureItem, standard_item_max
@ -109,7 +110,7 @@ class AdventureWorld(World):
game: ClassVar[str] = "Adventure" game: ClassVar[str] = "Adventure"
web: ClassVar[WebWorld] = AdventureWeb() web: ClassVar[WebWorld] = AdventureWeb()
option_definitions: ClassVar[Dict[str, AssembleOptions]] = adventure_option_definitions options_dataclass = AdventureOptions
settings: ClassVar[AdventureSettings] settings: ClassVar[AdventureSettings]
item_name_to_id: ClassVar[Dict[str, int]] = {name: data.id for name, data in item_table.items()} item_name_to_id: ClassVar[Dict[str, int]] = {name: data.id for name, data in item_table.items()}
location_name_to_id: ClassVar[Dict[str, int]] = {name: data.location_id for name, data in location_table.items()} location_name_to_id: ClassVar[Dict[str, int]] = {name: data.location_id for name, data in location_table.items()}
@ -149,18 +150,18 @@ class AdventureWorld(World):
bytearray(f"ADVENTURE{__version__.replace('.', '')[:3]}_{self.player}_{self.multiworld.seed}", "utf8")[:21] bytearray(f"ADVENTURE{__version__.replace('.', '')[:3]}_{self.player}_{self.multiworld.seed}", "utf8")[:21]
self.rom_name.extend([0] * (21 - len(self.rom_name))) self.rom_name.extend([0] * (21 - len(self.rom_name)))
self.dragon_rando_type = self.multiworld.dragon_rando_type[self.player].value self.dragon_rando_type = self.options.dragon_rando_type.value
self.dragon_slay_check = self.multiworld.dragon_slay_check[self.player].value self.dragon_slay_check = self.options.dragon_slay_check.value
self.connector_multi_slot = self.multiworld.connector_multi_slot[self.player].value self.connector_multi_slot = self.options.connector_multi_slot.value
self.yorgle_speed = self.multiworld.yorgle_speed[self.player].value self.yorgle_speed = self.options.yorgle_speed.value
self.yorgle_min_speed = self.multiworld.yorgle_min_speed[self.player].value self.yorgle_min_speed = self.options.yorgle_min_speed.value
self.grundle_speed = self.multiworld.grundle_speed[self.player].value self.grundle_speed = self.options.grundle_speed.value
self.grundle_min_speed = self.multiworld.grundle_min_speed[self.player].value self.grundle_min_speed = self.options.grundle_min_speed.value
self.rhindle_speed = self.multiworld.rhindle_speed[self.player].value self.rhindle_speed = self.options.rhindle_speed.value
self.rhindle_min_speed = self.multiworld.rhindle_min_speed[self.player].value self.rhindle_min_speed = self.options.rhindle_min_speed.value
self.difficulty_switch_a = self.multiworld.difficulty_switch_a[self.player].value self.difficulty_switch_a = self.options.difficulty_switch_a.value
self.difficulty_switch_b = self.multiworld.difficulty_switch_b[self.player].value self.difficulty_switch_b = self.options.difficulty_switch_b.value
self.start_castle = self.multiworld.start_castle[self.player].value self.start_castle = self.options.start_castle.value
self.created_items = 0 self.created_items = 0
if self.dragon_slay_check == 0: if self.dragon_slay_check == 0:
@ -227,7 +228,7 @@ class AdventureWorld(World):
extra_filler_count = num_locations - self.created_items extra_filler_count = num_locations - self.created_items
# traps would probably go here, if enabled # traps would probably go here, if enabled
freeincarnate_max = self.multiworld.freeincarnate_max[self.player].value freeincarnate_max = self.options.freeincarnate_max.value
actual_freeincarnates = min(extra_filler_count, freeincarnate_max) actual_freeincarnates = min(extra_filler_count, freeincarnate_max)
self.multiworld.itempool += [self.create_item("Freeincarnate") for _ in range(actual_freeincarnates)] self.multiworld.itempool += [self.create_item("Freeincarnate") for _ in range(actual_freeincarnates)]
self.created_items += actual_freeincarnates self.created_items += actual_freeincarnates
@ -247,7 +248,7 @@ class AdventureWorld(World):
self.created_items += 1 self.created_items += 1
def create_regions(self) -> None: def create_regions(self) -> None:
create_regions(self.multiworld, self.player, self.dragon_rooms) create_regions(self.options, self.multiworld, self.player, self.dragon_rooms)
set_rules = set_rules set_rules = set_rules
@ -354,7 +355,7 @@ class AdventureWorld(World):
auto_collect_locations: [AdventureAutoCollectLocation] = [] auto_collect_locations: [AdventureAutoCollectLocation] = []
local_item_to_location: {int, int} = {} local_item_to_location: {int, int} = {}
bat_no_touch_locs: [LocationData] = [] bat_no_touch_locs: [LocationData] = []
bat_logic: int = self.multiworld.bat_logic[self.player].value bat_logic: int = self.options.bat_logic.value
try: try:
rom_deltas: { int, int } = {} rom_deltas: { int, int } = {}
self.place_dragons(rom_deltas) self.place_dragons(rom_deltas)
@ -421,7 +422,7 @@ class AdventureWorld(World):
item_position_data_start = get_item_position_data_start(unplaced_item.table_index) item_position_data_start = get_item_position_data_start(unplaced_item.table_index)
rom_deltas[item_position_data_start] = 0xff rom_deltas[item_position_data_start] = 0xff
if self.multiworld.connector_multi_slot[self.player].value: if self.options.connector_multi_slot.value:
rom_deltas[connector_port_offset] = (self.player & 0xff) rom_deltas[connector_port_offset] = (self.player & 0xff)
else: else:
rom_deltas[connector_port_offset] = 0 rom_deltas[connector_port_offset] = 0

View File

@ -5,7 +5,7 @@ from schema import And, Optional, Or, Schema
from Options import Accessibility, Choice, DeathLinkMixin, DefaultOnToggle, OptionDict, PerGameCommonOptions, \ from Options import Accessibility, Choice, DeathLinkMixin, DefaultOnToggle, OptionDict, PerGameCommonOptions, \
PlandoConnections, Range, StartInventoryPool, Toggle, Visibility PlandoConnections, Range, StartInventoryPool, Toggle, Visibility
from worlds.messenger.portals import CHECKPOINTS, PORTALS, SHOP_POINTS from .portals import CHECKPOINTS, PORTALS, SHOP_POINTS
class MessengerAccessibility(Accessibility): class MessengerAccessibility(Accessibility):

View File

@ -306,7 +306,6 @@ def write_tokens(world: "MLSSWorld", patch: MLSSProcedurePatch) -> None:
if world.options.scale_stats: if world.options.scale_stats:
patch.write_token(APTokenTypes.WRITE, 0xD00002, bytes([0x1])) patch.write_token(APTokenTypes.WRITE, 0xD00002, bytes([0x1]))
if world.options.xp_multiplier:
patch.write_token(APTokenTypes.WRITE, 0xD00003, bytes([world.options.xp_multiplier.value])) patch.write_token(APTokenTypes.WRITE, 0xD00003, bytes([world.options.xp_multiplier.value]))
if world.options.tattle_hp: if world.options.tattle_hp:

View File

@ -107,10 +107,10 @@ class ColouredMessage:
def coloured(self, text: str, colour: str) -> 'ColouredMessage': def coloured(self, text: str, colour: str) -> 'ColouredMessage':
add_json_text(self.parts, text, type="color", color=colour) add_json_text(self.parts, text, type="color", color=colour)
return self return self
def location(self, location_id: int, player_id: int = 0) -> 'ColouredMessage': def location(self, location_id: int, player_id: int) -> 'ColouredMessage':
add_json_location(self.parts, location_id, player_id) add_json_location(self.parts, location_id, player_id)
return self return self
def item(self, item_id: int, player_id: int = 0, flags: int = 0) -> 'ColouredMessage': def item(self, item_id: int, player_id: int, flags: int = 0) -> 'ColouredMessage':
add_json_item(self.parts, item_id, player_id, flags) add_json_item(self.parts, item_id, player_id, flags)
return self return self
def player(self, player_id: int) -> 'ColouredMessage': def player(self, player_id: int) -> 'ColouredMessage':
@ -122,7 +122,6 @@ class ColouredMessage:
class StarcraftClientProcessor(ClientCommandProcessor): class StarcraftClientProcessor(ClientCommandProcessor):
ctx: SC2Context ctx: SC2Context
echo_commands = True
def formatted_print(self, text: str) -> None: def formatted_print(self, text: str) -> None:
"""Prints with kivy formatting to the GUI, and also prints to command-line and to all logs""" """Prints with kivy formatting to the GUI, and also prints to command-line and to all logs"""
@ -257,7 +256,7 @@ class StarcraftClientProcessor(ClientCommandProcessor):
for item in received_items_of_this_type: for item in received_items_of_this_type:
print_faction_title() print_faction_title()
has_printed_faction_title = True has_printed_faction_title = True
(ColouredMessage('* ').item(item.item, flags=item.flags) (ColouredMessage('* ').item(item.item, self.ctx.slot, flags=item.flags)
(" from ").location(item.location, self.ctx.slot) (" from ").location(item.location, self.ctx.slot)
(" by ").player(item.player) (" by ").player(item.player)
).send(self.ctx) ).send(self.ctx)
@ -278,7 +277,7 @@ class StarcraftClientProcessor(ClientCommandProcessor):
received_items_of_this_type = items_received.get(child_item, []) received_items_of_this_type = items_received.get(child_item, [])
for item in received_items_of_this_type: for item in received_items_of_this_type:
filter_match_count += len(received_items_of_this_type) filter_match_count += len(received_items_of_this_type)
(ColouredMessage(' * ').item(item.item, flags=item.flags) (ColouredMessage(' * ').item(item.item, self.ctx.slot, flags=item.flags)
(" from ").location(item.location, self.ctx.slot) (" from ").location(item.location, self.ctx.slot)
(" by ").player(item.player) (" by ").player(item.player)
).send(self.ctx) ).send(self.ctx)

View File

@ -1,5 +1,6 @@
from worlds.generic.Rules import forbid_items_for_player, add_rule from worlds.generic.Rules import forbid_items_for_player, add_rule
from worlds.shorthike.Options import Goal, GoldenFeatherProgression, MinShopCheckLogic, ShopCheckLogic from .Options import Goal, GoldenFeatherProgression, MinShopCheckLogic, ShopCheckLogic
def create_rules(self, location_table): def create_rules(self, location_table):
multiworld = self.multiworld multiworld = self.multiworld

View File

@ -5,7 +5,7 @@ from NetUtils import ClientStatus, NetworkItem
import worlds._bizhawk as bizhawk import worlds._bizhawk as bizhawk
from worlds._bizhawk.client import BizHawkClient from worlds._bizhawk.client import BizHawkClient
from worlds.yugioh06 import item_to_index from . import item_to_index
if TYPE_CHECKING: if TYPE_CHECKING:
from worlds._bizhawk.context import BizHawkClientContext from worlds._bizhawk.context import BizHawkClientContext

View File

@ -3,8 +3,8 @@ from typing import Dict, List, NamedTuple, Optional, Union
from BaseClasses import MultiWorld from BaseClasses import MultiWorld
from worlds.generic.Rules import CollectionRule from worlds.generic.Rules import CollectionRule
from worlds.yugioh06 import item_to_index, tier_1_opponents, yugioh06_difficulty from . import item_to_index, tier_1_opponents, yugioh06_difficulty
from worlds.yugioh06.locations import special from .locations import special
class OpponentData(NamedTuple): class OpponentData(NamedTuple):