Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions schemas/Manual.items.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
"description": "(Optional) Skips the item ID forward to the given value.\nThis can be used to provide buffer space for future items.",
"type": "integer"
},
"sort-key": {
"description": "(Optional) A string to sort the items by. If not provided, items will always be sorted by name.",
"type": "string"
},
"_comment": {"$ref": "#/definitions/comment"}
},
"required": ["name"]
Expand Down
4 changes: 4 additions & 0 deletions schemas/Manual.locations.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
"description": "(Optional) Skips the item ID forward to the given value.\nThis can be used to provide buffer space for future items.",
"type": "integer"
},
"sort-key": {
"description": "(Optional) A string to sort the items by. If not provided, locations will always be sorted by name.",
"type": "string"
},
"_comment": {"$ref": "#/definitions/comment"}
}
},
Expand Down
94 changes: 86 additions & 8 deletions src/ManualClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
import sys
import time
import typing
from typing import Any, Optional
from typing import Any, Dict, List, Optional
from enum import IntEnum

import requests
from worlds import AutoWorldRegister, network_data_package
from worlds.LauncherComponents import icon_paths
import json
import traceback


import ModuleUpdate
ModuleUpdate.update()

Expand All @@ -37,6 +37,12 @@
if typing.TYPE_CHECKING:
import kvui

class SortingOrder(IntEnum):
custom = 1
inverted_custom = -1
alphabetical = 2
inverted_alphabetical = -2

class ManualClientCommandProcessor(ClientCommandProcessor):
def _cmd_resync(self) -> bool:
"""Manually trigger a resync."""
Expand All @@ -60,6 +66,43 @@ def _cmd_send(self, location_name: str) -> bool:
self.output(response)
return False

@mark_raw
def _cmd_items_sorting(self, algorithm: Optional[str] = None) -> bool:
"""Set or get the current items sorting algorithm."""
return self.sorting_commands_logic(algorithm, target_items=True)

@mark_raw
def _cmd_locations_sorting(self, algorithm: Optional[str] = None) -> bool:
"""Set or get the current locations sorting algorithm."""
return self.sorting_commands_logic(algorithm, target_items=False)

def sorting_commands_logic(self, algorithm: Optional[str] = None, target_items: bool = False)-> bool:
if algorithm is None: #Get
cur_sort = self.ctx.items_sorting if target_items else self.ctx.locations_sorting
self.output(f"Currently {'Items' if target_items else 'Locations'} are sorted by: {cur_sort}")

return True

valid_algorithms = [e.name for e in SortingOrder] # Set
algorithm, usable, response = Utils.get_intended_text(
algorithm,
valid_algorithms
)

if usable:
if target_items:
self.ctx.items_sorting = algorithm
else:
self.ctx.locations_sorting = algorithm
self.ctx.ui.build_tracker_and_locations_table() #The best place I could find to sort the locations

self.ctx.ui.request_update_tracker_and_locations_table()
self.ctx.save_options()
self.output(f"Set {'Items' if target_items else 'Locations'} sorting algorithm to {algorithm}")
else:
self.output(response)
return False




Expand All @@ -83,6 +126,9 @@ class ManualContext(SuperContext):
deathlink_out = False

search_term = ""
settings = None
items_sorting = "alphabetical"
locations_sorting = "alphabetical"

colors = {
'location_default': [219/255, 218/255, 213/255, 1],
Expand Down Expand Up @@ -134,6 +180,13 @@ async def server_auth(self, password_requested: bool = False):
self.victory_names = ["__Manual Game Complete__"]
self.goal_location = self.get_location_by_name("__Manual Game Complete__")

self.settings = Utils.get_settings().get("manual_settings", None) #.get(self.game.lower(), None)
if self.settings is not None:
if hasattr(self.settings, "items_sorting_order"):
self.items_sorting = self.settings.items_sorting_order
if hasattr(self.settings, "locations_sorting_order"):
self.locations_sorting = self.settings.locations_sorting_order

await self.get_username()
await self.send_connect()

Expand Down Expand Up @@ -184,6 +237,12 @@ def set_search(self, search_term: str):
def clear_search(self):
self.search_term = ""

def save_options(self):
if self.settings is not None:
self.settings.items_sorting_order = self.items_sorting
self.settings.locations_sorting_order = self.locations_sorting
Utils.get_settings().save()

@property
def endpoints(self):
if self.server:
Expand Down Expand Up @@ -385,7 +444,7 @@ def build(self) -> Layout:
def clear_lists(self):
self.listed_items = {"(No Category)": []}
self.item_categories = ["(No Category)"]
self.listed_locations = {"(No Category)": [], "(Hinted)": []}
self.listed_locations: Dict[str, List[int]] = {"(No Category)": [], "(Hinted)": []}
self.location_categories = ["(No Category)", "(Hinted)"]

def set_active_item_accordion(self, instance):
Expand Down Expand Up @@ -531,8 +590,14 @@ def build_tracker_and_locations_table(self):
if not victory_categories:
victory_categories.add("(No Category)")

for category in self.listed_locations:
self.listed_locations[category].sort(key=self.ctx.location_names.lookup_in_game)
loc_sorting = SortingOrder[self.ctx.locations_sorting]

if abs(loc_sorting) == SortingOrder.alphabetical:
for category in self.listed_locations:
self.listed_locations[category].sort(key=self.ctx.location_names.lookup_in_game, reverse=loc_sorting < 0)
elif abs(loc_sorting) == SortingOrder.custom:
for category in self.listed_locations:
self.listed_locations[category].sort(key=lambda i: self.ctx.get_location_by_id(i).get("sort-key", None) or self.ctx.get_location_by_id(i).get("name", ""), reverse=loc_sorting < 0)

items_length = len(self.ctx.items_received)
tracker_panel_scrollable = TrackerLayoutScrollable(do_scroll=(False, True), bar_width=10)
Expand Down Expand Up @@ -689,11 +754,24 @@ def update_tracker_and_locations_table(self, update_highlights=False):
# instead of reusing existing item listings, clear it all out and re-draw with the sorted list
category_grid.clear_widgets()
self.listed_items[category_name].clear()
category_count = 0
category_unique_name_count = 0

# Label (for all item listings)
sorted_items_received = sorted([
i.item for i in self.ctx.items_received
], key=self.ctx.item_names.lookup_in_game)
item_sorting = SortingOrder[self.ctx.items_sorting]

if abs(item_sorting) == SortingOrder.alphabetical:
sorted_items_received = sorted([
i.item for i in self.ctx.items_received
],
key=self.ctx.item_names.lookup_in_game,
reverse=item_sorting < 0)
elif abs(item_sorting) == SortingOrder.custom:
sorted_items_received = sorted([
i.item for i in self.ctx.items_received
],
key=lambda i: self.ctx.get_item_by_id(i).get("sort-key", None) or self.ctx.get_item_by_id(i).get("name", ""),
reverse=item_sorting < 0)

for network_item in sorted_items_received:
item_name = self.ctx.item_names.lookup_in_game(network_item)
Expand Down
1 change: 1 addition & 0 deletions src/Meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

from BaseClasses import Tutorial
from enum import Enum
from worlds.AutoWorld import World, WebWorld
from .Data import meta_table
from .Helpers import convert_to_long_string
Expand Down
24 changes: 22 additions & 2 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import logging
import os
import json
from typing import Callable, Optional
from typing import Callable, Optional, Union, ClassVar
import webbrowser
import settings

import Utils
from worlds.generic.Rules import forbid_items_for_player
Expand Down Expand Up @@ -36,10 +37,29 @@
before_extend_hint_information, after_extend_hint_information
from .hooks.Data import hook_interpret_slot_data

class ManualSettings(settings.Group):
class ItemsSorting(str):
"""Set your preferred Items sorting order
valid options:
custom, inverted_custom: Sort by item ids
alphabetical, inverted_alphabetical: Sort by item name
"""
class LocationsSorting(str):
"""Set your preferred Locations sorting order
valid options:
custom, inverted_custom: Sort by location ids
alphabetical, inverted_alphabetical: Sort by location name
"""

items_sorting_order: ItemsSorting = ItemsSorting("alphabetical")
locations_sorting_order: LocationsSorting = LocationsSorting("alphabetical")

class ManualWorld(World):
__doc__ = world_description
game: str = game_name
web = world_webworld
settings: ClassVar[ManualSettings]
settings_key: ClassVar[str] = "manual_settings"

options_dataclass = manual_options_data
data_version = 2
Expand Down Expand Up @@ -459,7 +479,7 @@ def __init__(self, display_name: str, script_name: Optional[str] = None, func: O
self.version = version

def add_client_to_launcher() -> None:
version = 2024_12_13 # YYYYMMDD
version = 2025_02_25 # YYYYMMDD
found = False

if "manual" not in icon_paths:
Expand Down
Loading