Skip to content

Commit

Permalink
Merge branch 'main' into alttp-deprecated-calls
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholassaylor authored Dec 3, 2024
2 parents 7e609b0 + 6f2e1c2 commit 888dbc8
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 83 deletions.
9 changes: 8 additions & 1 deletion Generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,14 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
os.path.join(args.player_files_path, fname) not in {args.meta_file_path, args.weights_file_path}:
path = os.path.join(args.player_files_path, fname)
try:
weights_cache[fname] = read_weights_yamls(path)
weights_for_file = []
for doc_idx, yaml in enumerate(read_weights_yamls(path)):
if yaml is None:
logging.warning(f"Ignoring empty yaml document #{doc_idx + 1} in {fname}")
else:
weights_for_file.append(yaml)
weights_cache[fname] = tuple(weights_for_file)

except Exception as e:
raise ValueError(f"File {fname} is invalid. Please fix your yaml.") from e

Expand Down
58 changes: 18 additions & 40 deletions Launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,61 +126,39 @@ def handle_uri(path: str, launch_args: Tuple[str, ...]) -> None:
elif component.display_name == "Text Client":
text_client_component = component

from kvui import App, Button, BoxLayout, Label, Clock, Window
if client_component is None:
run_component(text_client_component, *launch_args)
return

class Popup(App):
timer_label: Label
remaining_time: Optional[int]
from kvui import App, Button, BoxLayout, Label, Window

class Popup(App):
def __init__(self):
self.title = "Connect to Multiworld"
self.icon = r"data/icon.png"
super().__init__()

def build(self):
layout = BoxLayout(orientation="vertical")
layout.add_widget(Label(text="Select client to open and connect with."))
button_row = BoxLayout(orientation="horizontal", size_hint=(1, 0.4))

if client_component is None:
self.remaining_time = 7
label_text = (f"A game client able to parse URIs was not detected for {game}.\n"
f"Launching Text Client in 7 seconds...")
self.timer_label = Label(text=label_text)
layout.add_widget(self.timer_label)
Clock.schedule_interval(self.update_label, 1)
else:
layout.add_widget(Label(text="Select client to open and connect with."))
button_row = BoxLayout(orientation="horizontal", size_hint=(1, 0.4))

text_client_button = Button(
text=text_client_component.display_name,
on_release=lambda *args: run_component(text_client_component, *launch_args)
)
button_row.add_widget(text_client_button)
text_client_button = Button(
text=text_client_component.display_name,
on_release=lambda *args: run_component(text_client_component, *launch_args)
)
button_row.add_widget(text_client_button)

game_client_button = Button(
text=client_component.display_name,
on_release=lambda *args: run_component(client_component, *launch_args)
)
button_row.add_widget(game_client_button)
game_client_button = Button(
text=client_component.display_name,
on_release=lambda *args: run_component(client_component, *launch_args)
)
button_row.add_widget(game_client_button)

layout.add_widget(button_row)
layout.add_widget(button_row)

return layout

def update_label(self, dt):
if self.remaining_time > 1:
# countdown the timer and string replace the number
self.remaining_time -= 1
self.timer_label.text = self.timer_label.text.replace(
str(self.remaining_time + 1), str(self.remaining_time)
)
else:
# our timer is finished so launch text client and close down
run_component(text_client_component, *launch_args)
Clock.unschedule(self.update_label)
App.get_running_app().stop()
Window.close()

def _stop(self, *largs):
# see run_gui Launcher _stop comment for details
self.root_window.close()
Expand Down
14 changes: 9 additions & 5 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No

def write_multidata():
import NetUtils
from NetUtils import HintStatus
slot_data = {}
client_versions = {}
games = {}
Expand All @@ -266,10 +267,10 @@ def write_multidata():
for slot in multiworld.player_ids:
slot_data[slot] = multiworld.worlds[slot].fill_slot_data()

def precollect_hint(location):
def precollect_hint(location: Location, auto_status: HintStatus):
entrance = er_hint_data.get(location.player, {}).get(location.address, "")
hint = NetUtils.Hint(location.item.player, location.player, location.address,
location.item.code, False, entrance, location.item.flags, False)
location.item.code, False, entrance, location.item.flags, auto_status)
precollected_hints[location.player].add(hint)
if location.item.player not in multiworld.groups:
precollected_hints[location.item.player].add(hint)
Expand All @@ -288,13 +289,16 @@ def precollect_hint(location):
f"{locations_data[location.player][location.address]}")
locations_data[location.player][location.address] = \
location.item.code, location.item.player, location.item.flags
auto_status = HintStatus.HINT_AVOID if location.item.trap else HintStatus.HINT_PRIORITY
if location.name in multiworld.worlds[location.player].options.start_location_hints:
precollect_hint(location)
if not location.item.trap: # Unspecified status for location hints, except traps
auto_status = HintStatus.HINT_UNSPECIFIED
precollect_hint(location, auto_status)
elif location.item.name in multiworld.worlds[location.item.player].options.start_hints:
precollect_hint(location)
precollect_hint(location, auto_status)
elif any([location.item.name in multiworld.worlds[player].options.start_hints
for player in multiworld.groups.get(location.item.player, {}).get("players", [])]):
precollect_hint(location)
precollect_hint(location, auto_status)

# embedded data package
data_package = {
Expand Down
5 changes: 5 additions & 0 deletions MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,11 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
[{'cmd': 'InvalidPacket', "type": "arguments",
"text": 'UpdateHint: Invalid Status', "original_cmd": cmd}])
return
if status == HintStatus.HINT_FOUND:
await ctx.send_msgs(client,
[{'cmd': 'InvalidPacket', "type": "arguments",
"text": 'UpdateHint: Cannot manually update status to "HINT_FOUND"', "original_cmd": cmd}])
return
new_hint = new_hint.re_prioritize(ctx, status)
if hint == new_hint:
return
Expand Down
2 changes: 1 addition & 1 deletion Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ def _cleanup():
import platform
logging.info(
f"Archipelago ({__version__}) logging initialized"
f" on {platform.platform()}"
f" on {platform.platform()} process {os.getpid()}"
f" running Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
f"{' (frozen)' if is_frozen() else ''}"
)
Expand Down
5 changes: 3 additions & 2 deletions WebHostLib/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ def roll_options(options: Dict[str, Union[dict, str]],
plando_options=plando_options)
else:
for i, yaml_data in enumerate(yaml_datas):
rolled_results[f"{filename}/{i + 1}"] = roll_settings(yaml_data,
plando_options=plando_options)
if yaml_data is not None:
rolled_results[f"{filename}/{i + 1}"] = roll_settings(yaml_data,
plando_options=plando_options)
except Exception as e:
if e.__cause__:
results[filename] = f"Failed to generate options in {filename}: {e} - {e.__cause__}"
Expand Down
17 changes: 11 additions & 6 deletions docs/network protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,20 +351,24 @@ Sent to the server to update the status of a Hint. The client must be the 'recei
| ---- | ---- | ----- |
| player | int | The ID of the player whose location is being hinted for. |
| location | int | The ID of the location to update the hint for. If no hint exists for this location, the packet is ignored. |
| status | [HintStatus](#HintStatus) | Optional. If included, sets the status of the hint to this status. |
| status | [HintStatus](#HintStatus) | Optional. If included, sets the status of the hint to this status. Cannot set `HINT_FOUND`, or change the status from `HINT_FOUND`. |

#### HintStatus
An enumeration containing the possible hint states.

```python
import enum
class HintStatus(enum.IntEnum):
HINT_FOUND = 0
HINT_UNSPECIFIED = 1
HINT_NO_PRIORITY = 10
HINT_AVOID = 20
HINT_PRIORITY = 30
HINT_FOUND = 0 # The location has been collected. Status cannot be changed once found.
HINT_UNSPECIFIED = 1 # The receiving player has not specified any status
HINT_NO_PRIORITY = 10 # The receiving player has specified that the item is unneeded
HINT_AVOID = 20 # The receiving player has specified that the item is detrimental
HINT_PRIORITY = 30 # The receiving player has specified that the item is needed
```
- Hints for items with `ItemClassification.trap` default to `HINT_AVOID`.
- Hints created with `LocationScouts`, `!hint_location`, or similar (hinting a location) default to `HINT_UNSPECIFIED`.
- Hints created with `!hint` or similar (hinting an item for yourself) default to `HINT_PRIORITY`.
- Once a hint is collected, its' status is updated to `HINT_FOUND` automatically, and can no longer be changed.

### StatusUpdate
Sent to the server to update on the sender's status. Examples include readiness or goal completion. (Example: defeated Ganon in A Link to the Past)
Expand Down Expand Up @@ -668,6 +672,7 @@ class Hint(typing.NamedTuple):
found: bool
entrance: str = ""
item_flags: int = 0
status: HintStatus = HintStatus.HINT_UNSPECIFIED
```

### Data Package Contents
Expand Down
13 changes: 13 additions & 0 deletions worlds/kh2/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,16 @@ def __init__(self, world: KH2World) -> None:
RegionName.Master: lambda state: self.multi_form_region_access(),
RegionName.Final: lambda state: self.final_form_region_access(state)
}
# Accessing Final requires being able to reach one of the locations in final_leveling_access, but reaching a
# location requires being able to reach the region the location is in, so an indirect condition is required.
# The access rules of each of the locations in final_leveling_access do not check for being able to reach other
# locations or other regions, so it is only the parent region of each location that needs to be added as an
# indirect condition.
self.form_region_indirect_condition_regions = {
RegionName.Final: {
self.world.get_location(location).parent_region for location in final_leveling_access
}
}

def final_form_region_access(self, state: CollectionState) -> bool:
"""
Expand Down Expand Up @@ -388,12 +398,15 @@ def set_kh2_form_rules(self):
for region_name in drive_form_list:
if region_name == RegionName.Summon and not self.world.options.SummonLevelLocationToggle:
continue
indirect_condition_regions = self.form_region_indirect_condition_regions.get(region_name, ())
# could get the location of each of these, but I feel like that would be less optimal
region = self.multiworld.get_region(region_name, self.player)
# if region_name in form_region_rules
if region_name != RegionName.Summon:
for entrance in region.entrances:
entrance.access_rule = self.form_region_rules[region_name]
for indirect_condition_region in indirect_condition_regions:
self.multiworld.register_indirect_condition(indirect_condition_region, entrance)
for loc in region.locations:
loc.access_rule = self.form_rules[loc.name]

Expand Down
2 changes: 0 additions & 2 deletions worlds/ladx/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class ItemName:
BOMB = "Bomb"
SWORD = "Progressive Sword"
FLIPPERS = "Flippers"
MAGNIFYING_LENS = "Magnifying Lens"
MEDICINE = "Medicine"
TAIL_KEY = "Tail Key"
ANGLER_KEY = "Angler Key"
Expand Down Expand Up @@ -191,7 +190,6 @@ class ItemName:
ItemData(ItemName.BOMB, "BOMB", ItemClassification.progression),
ItemData(ItemName.SWORD, "SWORD", ItemClassification.progression),
ItemData(ItemName.FLIPPERS, "FLIPPERS", ItemClassification.progression),
ItemData(ItemName.MAGNIFYING_LENS, "MAGNIFYING_LENS", ItemClassification.progression),
ItemData(ItemName.MEDICINE, "MEDICINE", ItemClassification.useful),
ItemData(ItemName.TAIL_KEY, "TAIL_KEY", ItemClassification.progression),
ItemData(ItemName.ANGLER_KEY, "ANGLER_KEY", ItemClassification.progression),
Expand Down
2 changes: 1 addition & 1 deletion worlds/ladx/LADXR/locations/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
PEGASUS_BOOTS: 0x05,
OCARINA: 0x06,
FEATHER: 0x07, SHOVEL: 0x08, MAGIC_POWDER: 0x09, BOMB: 0x0A, SWORD: 0x0B, FLIPPERS: 0x0C,
MAGNIFYING_LENS: 0x0D, MEDICINE: 0x10,
MEDICINE: 0x10,
TAIL_KEY: 0x11, ANGLER_KEY: 0x12, FACE_KEY: 0x13, BIRD_KEY: 0x14, GOLD_LEAF: 0x15,
RUPEES_50: 0x1B, RUPEES_20: 0x1C, RUPEES_100: 0x1D, RUPEES_200: 0x1E, RUPEES_500: 0x1F,
SEASHELL: 0x20, MESSAGE: 0x21, GEL: 0x22,
Expand Down
1 change: 0 additions & 1 deletion worlds/ladx/LADXR/locations/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
BOMB = "BOMB"
SWORD = "SWORD"
FLIPPERS = "FLIPPERS"
MAGNIFYING_LENS = "MAGNIFYING_LENS"
MEDICINE = "MEDICINE"
TAIL_KEY = "TAIL_KEY"
ANGLER_KEY = "ANGLER_KEY"
Expand Down
4 changes: 2 additions & 2 deletions worlds/lingo/player_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ def randomize_paintings(self, world: "LingoWorld") -> bool:
required_painting_rooms += REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS
req_exits = [painting_id for painting_id, painting in PAINTINGS.items() if painting.required_when_no_doors]

def is_req_enterable(painting_id: str, painting: Painting) -> bool:
def is_req_enterable(painting: Painting) -> bool:
if painting.exit_only or painting.disable or painting.req_blocked\
or painting.room in required_painting_rooms:
return False
Expand All @@ -433,7 +433,7 @@ def is_req_enterable(painting_id: str, painting: Painting) -> bool:
return True

req_enterable = [painting_id for painting_id, painting in PAINTINGS.items()
if is_req_enterable(painting_id, painting)]
if is_req_enterable(painting)]
req_exits += [painting_id for painting_id, painting in PAINTINGS.items()
if painting.exit_only and painting.required]
req_entrances = world.random.sample(req_enterable, len(req_exits))
Expand Down
1 change: 0 additions & 1 deletion worlds/lingo/utils/pickle_static_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import hashlib
import pickle
import sys
import Utils


Expand Down
1 change: 0 additions & 1 deletion worlds/pokemon_emerald/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

### Features

- Added many new item and location groups.
- Added a Swedish translation of the setup guide.
- The client communicates map transitions to any trackers connected to the slot.
- Added the player's Normalize Encounter Rates option to slot data for trackers.
Expand Down
Loading

0 comments on commit 888dbc8

Please sign in to comment.