mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-07 15:13:52 -08:00
Compare commits
6 Commits
NewSoupVi-
...
ladx-marin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a90e43bf47 | ||
|
|
b1ff55dd06 | ||
|
|
f4b5422f66 | ||
|
|
d4ebace99f | ||
|
|
95e09c8e2a | ||
|
|
4623d59206 |
4
Main.py
4
Main.py
@@ -12,6 +12,7 @@ import worlds
|
||||
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld
|
||||
from Fill import FillError, balance_multiworld_progression, distribute_items_restrictive, flood_items, \
|
||||
parse_planned_blocks, distribute_planned_blocks, resolve_early_locations_for_planned
|
||||
from NetUtils import convert_to_base_types
|
||||
from Options import StartInventoryPool
|
||||
from Utils import __version__, output_path, version_tuple
|
||||
from settings import get_settings
|
||||
@@ -334,6 +335,9 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
||||
}
|
||||
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
|
||||
|
||||
for key in ("slot_data", "er_hint_data"):
|
||||
multidata[key] = convert_to_base_types(multidata[key])
|
||||
|
||||
multidata = zlib.compress(pickle.dumps(multidata), 9)
|
||||
|
||||
with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:
|
||||
|
||||
21
NetUtils.py
21
NetUtils.py
@@ -106,6 +106,27 @@ def _scan_for_TypedTuples(obj: typing.Any) -> typing.Any:
|
||||
return obj
|
||||
|
||||
|
||||
_base_types = str | int | bool | float | None | tuple["_base_types", ...] | dict["_base_types", "base_types"]
|
||||
|
||||
|
||||
def convert_to_base_types(obj: typing.Any) -> _base_types:
|
||||
if isinstance(obj, (tuple, list, set, frozenset)):
|
||||
return tuple(convert_to_base_types(o) for o in obj)
|
||||
elif isinstance(obj, dict):
|
||||
return {convert_to_base_types(key): convert_to_base_types(value) for key, value in obj.items()}
|
||||
elif obj is None or type(obj) in (str, int, float, bool):
|
||||
return obj
|
||||
# unwrap simple types to their base, such as StrEnum
|
||||
elif isinstance(obj, str):
|
||||
return str(obj)
|
||||
elif isinstance(obj, int):
|
||||
return int(obj)
|
||||
elif isinstance(obj, float):
|
||||
return float(obj)
|
||||
else:
|
||||
raise Exception(f"Cannot handle {type(obj)}")
|
||||
|
||||
|
||||
_encode = JSONEncoder(
|
||||
ensure_ascii=False,
|
||||
check_circular=False,
|
||||
|
||||
3
Utils.py
3
Utils.py
@@ -441,9 +441,6 @@ class RestrictedUnpickler(pickle.Unpickler):
|
||||
def find_class(self, module: str, name: str) -> type:
|
||||
if module == "builtins" and name in safe_builtins:
|
||||
return getattr(builtins, name)
|
||||
# used by OptionCounter
|
||||
if module == "collections" and name == "Counter":
|
||||
return collections.Counter
|
||||
# used by MultiServer -> savegame/multidata
|
||||
if module == "NetUtils" and name in {"NetworkItem", "ClientStatus", "Hint",
|
||||
"SlotType", "NetworkSlot", "HintStatus"}:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
|
||||
from Fill import distribute_items_restrictive
|
||||
from NetUtils import encode
|
||||
from NetUtils import convert_to_base_types
|
||||
from worlds.AutoWorld import AutoWorldRegister, call_all
|
||||
from worlds import failed_world_loads
|
||||
from . import setup_solo_multiworld
|
||||
@@ -47,7 +47,7 @@ class TestImplemented(unittest.TestCase):
|
||||
call_all(multiworld, "post_fill")
|
||||
for key, data in multiworld.worlds[1].fill_slot_data().items():
|
||||
self.assertIsInstance(key, str, "keys in slot data must be a string")
|
||||
self.assertIsInstance(encode(data), str, f"object {type(data).__name__} not serializable.")
|
||||
convert_to_base_types(data) # only put base data types into slot data
|
||||
|
||||
def test_no_failed_world_loads(self):
|
||||
if failed_world_loads:
|
||||
|
||||
@@ -30,7 +30,6 @@ class Group(enum.Enum):
|
||||
Deprecated = enum.auto()
|
||||
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ItemData:
|
||||
code_without_offset: offset
|
||||
@@ -98,14 +97,15 @@ def create_trap_items(world, world_options: Options.DLCQuestOptions, trap_needed
|
||||
return traps
|
||||
|
||||
|
||||
def create_items(world, world_options: Options.DLCQuestOptions, locations_count: int, excluded_items: list[str], random: Random):
|
||||
def create_items(world, world_options: Options.DLCQuestOptions, locations_count: int, excluded_items: list[str],
|
||||
random: Random):
|
||||
created_items = []
|
||||
if world_options.campaign == Options.Campaign.option_basic or world_options.campaign == Options.Campaign.option_both:
|
||||
create_items_basic(world_options, created_items, world, excluded_items)
|
||||
create_items_campaign(world_options, created_items, world, excluded_items, Group.DLCQuest, 825, 250)
|
||||
|
||||
if (world_options.campaign == Options.Campaign.option_live_freemium_or_die or
|
||||
world_options.campaign == Options.Campaign.option_both):
|
||||
create_items_lfod(world_options, created_items, world, excluded_items)
|
||||
create_items_campaign(world_options, created_items, world, excluded_items, Group.Freemium, 889, 200)
|
||||
|
||||
trap_items = create_trap_items(world, world_options, locations_count - len(created_items), random)
|
||||
created_items += trap_items
|
||||
@@ -113,27 +113,8 @@ def create_items(world, world_options: Options.DLCQuestOptions, locations_count:
|
||||
return created_items
|
||||
|
||||
|
||||
def create_items_lfod(world_options, created_items, world, excluded_items):
|
||||
for item in items_by_group[Group.Freemium]:
|
||||
if item.name in excluded_items:
|
||||
excluded_items.remove(item)
|
||||
continue
|
||||
|
||||
if item.has_any_group(Group.DLC):
|
||||
created_items.append(world.create_item(item))
|
||||
if item.has_any_group(Group.Item) and world_options.item_shuffle == Options.ItemShuffle.option_shuffled:
|
||||
created_items.append(world.create_item(item))
|
||||
if item.has_any_group(Group.Twice):
|
||||
created_items.append(world.create_item(item))
|
||||
if world_options.coinsanity == Options.CoinSanity.option_coin:
|
||||
if world_options.coinbundlequantity == -1:
|
||||
create_coin_piece(created_items, world, 889, 200, Group.Freemium)
|
||||
return
|
||||
create_coin(world_options, created_items, world, 889, 200, Group.Freemium)
|
||||
|
||||
|
||||
def create_items_basic(world_options, created_items, world, excluded_items):
|
||||
for item in items_by_group[Group.DLCQuest]:
|
||||
def create_items_campaign(world_options: Options.DLCQuestOptions, created_items: list[DLCQuestItem], world, excluded_items: list[str], group: Group, total_coins: int, required_coins: int):
|
||||
for item in items_by_group[group]:
|
||||
if item.name in excluded_items:
|
||||
excluded_items.remove(item.name)
|
||||
continue
|
||||
@@ -146,14 +127,15 @@ def create_items_basic(world_options, created_items, world, excluded_items):
|
||||
created_items.append(world.create_item(item))
|
||||
if world_options.coinsanity == Options.CoinSanity.option_coin:
|
||||
if world_options.coinbundlequantity == -1:
|
||||
create_coin_piece(created_items, world, 825, 250, Group.DLCQuest)
|
||||
create_coin_piece(created_items, world, total_coins, required_coins, group)
|
||||
return
|
||||
create_coin(world_options, created_items, world, 825, 250, Group.DLCQuest)
|
||||
create_coin(world_options, created_items, world, total_coins, required_coins, group)
|
||||
|
||||
|
||||
def create_coin(world_options, created_items, world, total_coins, required_coins, group):
|
||||
coin_bundle_required = math.ceil(required_coins / world_options.coinbundlequantity)
|
||||
coin_bundle_useful = math.ceil((total_coins - coin_bundle_required * world_options.coinbundlequantity) / world_options.coinbundlequantity)
|
||||
coin_bundle_useful = math.ceil(
|
||||
(total_coins - coin_bundle_required * world_options.coinbundlequantity) / world_options.coinbundlequantity)
|
||||
for item in items_by_group[group]:
|
||||
if item.has_any_group(Group.Coin):
|
||||
for i in range(coin_bundle_required):
|
||||
@@ -165,7 +147,7 @@ def create_coin(world_options, created_items, world, total_coins, required_coins
|
||||
def create_coin_piece(created_items, world, total_coins, required_coins, group):
|
||||
for item in items_by_group[group]:
|
||||
if item.has_any_group(Group.Piece):
|
||||
for i in range(required_coins*10):
|
||||
for i in range(required_coins * 10):
|
||||
created_items.append(world.create_item(item))
|
||||
for i in range((total_coins - required_coins) * 10):
|
||||
created_items.append(world.create_item(item, ItemClassification.useful))
|
||||
|
||||
@@ -321,7 +321,7 @@ class InventorySpillTrapCount(TrapCount):
|
||||
|
||||
class FactorioWorldGen(OptionDict):
|
||||
"""World Generation settings. Overview of options at https://wiki.factorio.com/Map_generator,
|
||||
with in-depth documentation at https://lua-api.factorio.com/latest/Concepts.html#MapGenSettings"""
|
||||
with in-depth documentation at https://lua-api.factorio.com/latest/concepts/MapGenSettings.html"""
|
||||
display_name = "World Generation"
|
||||
# FIXME: do we want default be a rando-optimized default or in-game DS?
|
||||
value: dict[str, dict[str, typing.Any]]
|
||||
|
||||
@@ -367,7 +367,7 @@ def find_root_directory(ctx: JakAndDaxterContext):
|
||||
f" Close all launchers, games, clients, and console windows, then restart Archipelago.")
|
||||
|
||||
if not os.path.exists(settings_path):
|
||||
msg = (f"{err_title}: the OpenGOAL settings file does not exist.\n"
|
||||
msg = (f"{err_title}: The OpenGOAL settings file does not exist.\n"
|
||||
f"{alt_instructions}")
|
||||
ctx.on_log_error(logger, msg)
|
||||
return
|
||||
@@ -375,14 +375,44 @@ def find_root_directory(ctx: JakAndDaxterContext):
|
||||
with open(settings_path, "r") as f:
|
||||
load = json.load(f)
|
||||
|
||||
jak1_installed = load["games"]["Jak 1"]["isInstalled"]
|
||||
# This settings file has changed format once before, and may do so again in the future.
|
||||
# Guard against future incompatibilities by checking the file version first, and use that to determine
|
||||
# what JSON keys to look for next.
|
||||
try:
|
||||
settings_version = load["version"]
|
||||
logger.debug(f"OpenGOAL settings file version: {settings_version}")
|
||||
except KeyError:
|
||||
msg = (f"{err_title}: The OpenGOAL settings file has no version number!\n"
|
||||
f"{alt_instructions}")
|
||||
ctx.on_log_error(logger, msg)
|
||||
return
|
||||
|
||||
try:
|
||||
if settings_version == "2.0":
|
||||
jak1_installed = load["games"]["Jak 1"]["isInstalled"]
|
||||
mod_sources = load["games"]["Jak 1"]["modsInstalledVersion"]
|
||||
|
||||
elif settings_version == "3.0":
|
||||
jak1_installed = load["games"]["jak1"]["isInstalled"]
|
||||
mod_sources = load["games"]["jak1"]["mods"]
|
||||
|
||||
else:
|
||||
msg = (f"{err_title}: The OpenGOAL settings file has an unknown version number ({settings_version}).\n"
|
||||
f"{alt_instructions}")
|
||||
ctx.on_log_error(logger, msg)
|
||||
return
|
||||
except KeyError as e:
|
||||
msg = (f"{err_title}: The OpenGOAL settings file does not contain key entry {e}!\n"
|
||||
f"{alt_instructions}")
|
||||
ctx.on_log_error(logger, msg)
|
||||
return
|
||||
|
||||
if not jak1_installed:
|
||||
msg = (f"{err_title}: The OpenGOAL Launcher is missing a normal install of Jak 1!\n"
|
||||
f"{alt_instructions}")
|
||||
ctx.on_log_error(logger, msg)
|
||||
return
|
||||
|
||||
mod_sources = load["games"]["Jak 1"]["modsInstalledVersion"]
|
||||
if mod_sources is None:
|
||||
msg = (f"{err_title}: No mod sources have been configured in the OpenGOAL Launcher!\n"
|
||||
f"{alt_instructions}")
|
||||
|
||||
@@ -255,7 +255,6 @@ Try Bumper Stickers!
|
||||
Try Castlevania 64!
|
||||
Try Celeste 64!
|
||||
Try ChecksFinder!
|
||||
Try Clique!
|
||||
Try Dark Souls III!
|
||||
Try DLCQuest!
|
||||
Try Donkey Kong Country 3!
|
||||
@@ -268,6 +267,7 @@ Try A Hat in Time!
|
||||
Try Heretic!
|
||||
Try Hollow Knight!
|
||||
Try Hylics 2!
|
||||
Try Jak and Daxter: The Precursor Legacy!
|
||||
Try Kingdom Hearts 2!
|
||||
Try Kirby's Dream Land 3!
|
||||
Try Landstalker - The Treasures of King Nole!
|
||||
@@ -288,11 +288,10 @@ Try Pokemon Emerald!
|
||||
Try Pokemon Red and Blue!
|
||||
Try Raft!
|
||||
Try Risk of Rain 2!
|
||||
Try Rogue Legacy!
|
||||
Try Secret of Evermore!
|
||||
Try shapez!
|
||||
Try Shivers!
|
||||
Try A Short Hike!
|
||||
Try Slay the Spire!
|
||||
Try SMZ3!
|
||||
Try Sonic Adventure 2 Battle!
|
||||
Try Starcraft 2!
|
||||
@@ -300,6 +299,7 @@ Try Stardew Valley!
|
||||
Try Subnautica!
|
||||
Try Sudoku!
|
||||
Try Super Mario 64!
|
||||
Try Super Mario Land 2: 6 Golden Coins!
|
||||
Try Super Mario World!
|
||||
Try Super Metroid!
|
||||
Try Terraria!
|
||||
@@ -312,7 +312,6 @@ Try The Witness!
|
||||
Try Yoshi's Island!
|
||||
Try Yu-Gi-Oh! 2006!
|
||||
Try Zillion!
|
||||
Try Zork Grand Inquisitor!
|
||||
Try Old School Runescape!
|
||||
Try Kingdom Hearts!
|
||||
Try Mega Man 2!
|
||||
@@ -369,7 +368,6 @@ Have they added Among Us to AP yet?
|
||||
Every copy of LADX is personalized, David.
|
||||
Looks like you're going on A Short Hike. Bring back feathers please?
|
||||
Functioning Brain is at...\nWait. This isn't Witness. Wrong game, sorry.
|
||||
Don't forget to check your Clique!\nIf, y'know, you have one. No pressure...
|
||||
:3
|
||||
Sorry ######, but your progression item is in another world.
|
||||
&newgames\n&oldgames
|
||||
|
||||
Reference in New Issue
Block a user