Merge branch 'ArchipelagoMW:main' into dev
@@ -12,7 +12,7 @@ from typing import (Any, Callable, ClassVar, Dict, FrozenSet, Iterable, List, Ma
|
||||
|
||||
from Options import item_and_loc_options, ItemsAccessibility, OptionGroup, PerGameCommonOptions
|
||||
from BaseClasses import CollectionState
|
||||
from Utils import deprecate
|
||||
from Utils import Version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from BaseClasses import MultiWorld, Item, Location, Tutorial, Region, Entrance
|
||||
@@ -22,6 +22,10 @@ if TYPE_CHECKING:
|
||||
perf_logger = logging.getLogger("performance")
|
||||
|
||||
|
||||
class InvalidItemError(KeyError):
|
||||
pass
|
||||
|
||||
|
||||
class AutoWorldRegister(type):
|
||||
world_types: Dict[str, Type[World]] = {}
|
||||
__file__: str
|
||||
@@ -71,6 +75,10 @@ class AutoWorldRegister(type):
|
||||
if "required_client_version" in base.__dict__:
|
||||
dct["required_client_version"] = max(dct["required_client_version"],
|
||||
base.__dict__["required_client_version"])
|
||||
if "world_version" in dct:
|
||||
if dct["world_version"] != Version(0, 0, 0):
|
||||
raise RuntimeError(f"{name} is attempting to set 'world_version' from within the class. world_version "
|
||||
f"can only be set from manifest.")
|
||||
|
||||
# construct class
|
||||
new_class = super().__new__(mcs, name, bases, dct)
|
||||
@@ -333,6 +341,8 @@ class World(metaclass=AutoWorldRegister):
|
||||
"""If loaded from a .apworld, this is the Path to it."""
|
||||
__file__: ClassVar[str]
|
||||
"""path it was loaded from"""
|
||||
world_version: ClassVar[Version] = Version(0, 0, 0)
|
||||
"""Optional world version loaded from archipelago.json"""
|
||||
|
||||
def __init__(self, multiworld: "MultiWorld", player: int):
|
||||
assert multiworld is not None
|
||||
|
||||
@@ -8,7 +8,8 @@ import os
|
||||
import threading
|
||||
from io import BytesIO
|
||||
|
||||
from typing import ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload, Sequence
|
||||
from typing import (ClassVar, Dict, List, Literal, Tuple, Any, Optional, Union, BinaryIO, overload, Sequence,
|
||||
TYPE_CHECKING)
|
||||
|
||||
import bsdiff4
|
||||
|
||||
@@ -16,6 +17,9 @@ semaphore = threading.Semaphore(os.cpu_count() or 4)
|
||||
|
||||
del threading
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from Utils import Version
|
||||
|
||||
|
||||
class AutoPatchRegister(abc.ABCMeta):
|
||||
patch_types: ClassVar[Dict[str, AutoPatchRegister]] = {}
|
||||
@@ -65,7 +69,7 @@ class AutoPatchExtensionRegister(abc.ABCMeta):
|
||||
return handler
|
||||
|
||||
|
||||
container_version: int = 6
|
||||
container_version: int = 7
|
||||
|
||||
|
||||
def is_ap_player_container(game: str, data: bytes, player: int):
|
||||
@@ -92,7 +96,7 @@ class APContainer:
|
||||
version: ClassVar[int] = container_version
|
||||
compression_level: ClassVar[int] = 9
|
||||
compression_method: ClassVar[int] = zipfile.ZIP_DEFLATED
|
||||
|
||||
manifest_path: str = "archipelago.json"
|
||||
path: Optional[str]
|
||||
|
||||
def __init__(self, path: Optional[str] = None):
|
||||
@@ -116,7 +120,7 @@ class APContainer:
|
||||
except Exception as e:
|
||||
raise Exception(f"Manifest {manifest} did not convert to json.") from e
|
||||
else:
|
||||
opened_zipfile.writestr("archipelago.json", manifest_str)
|
||||
opened_zipfile.writestr(self.manifest_path, manifest_str)
|
||||
|
||||
def read(self, file: Optional[Union[str, BinaryIO]] = None) -> None:
|
||||
"""Read data into patch object. file can be file-like, such as an outer zip file's stream."""
|
||||
@@ -137,7 +141,18 @@ class APContainer:
|
||||
raise InvalidDataError(f"{message}This might be the incorrect world version for this file") from e
|
||||
|
||||
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
|
||||
with opened_zipfile.open("archipelago.json", "r") as f:
|
||||
try:
|
||||
assert self.manifest_path.endswith("archipelago.json"), "Filename should be archipelago.json"
|
||||
manifest_info = opened_zipfile.getinfo(self.manifest_path)
|
||||
except KeyError as e:
|
||||
for info in opened_zipfile.infolist():
|
||||
if info.filename.endswith("archipelago.json"):
|
||||
manifest_info = info
|
||||
self.manifest_path = info.filename
|
||||
break
|
||||
else:
|
||||
raise e
|
||||
with opened_zipfile.open(manifest_info, "r") as f:
|
||||
manifest = json.load(f)
|
||||
if manifest["compatible_version"] > self.version:
|
||||
raise Exception(f"File (version: {manifest['compatible_version']}) too new "
|
||||
@@ -152,6 +167,33 @@ class APContainer:
|
||||
}
|
||||
|
||||
|
||||
class APWorldContainer(APContainer):
|
||||
"""A zipfile containing a world implementation."""
|
||||
game: str | None = None
|
||||
world_version: "Version | None" = None
|
||||
minimum_ap_version: "Version | None" = None
|
||||
maximum_ap_version: "Version | None" = None
|
||||
|
||||
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
|
||||
from Utils import tuplize_version
|
||||
manifest = super().read_contents(opened_zipfile)
|
||||
self.game = manifest["game"]
|
||||
for version_key in ("world_version", "minimum_ap_version", "maximum_ap_version"):
|
||||
if version_key in manifest:
|
||||
setattr(self, version_key, tuplize_version(manifest[version_key]))
|
||||
return manifest
|
||||
|
||||
def get_manifest(self) -> Dict[str, Any]:
|
||||
manifest = super().get_manifest()
|
||||
manifest["game"] = self.game
|
||||
manifest["compatible_version"] = 7
|
||||
for version_key in ("world_version", "minimum_ap_version", "maximum_ap_version"):
|
||||
version = getattr(self, version_key)
|
||||
if version:
|
||||
manifest[version_key] = version.as_simple_string()
|
||||
return manifest
|
||||
|
||||
|
||||
class APPlayerContainer(APContainer):
|
||||
"""A zipfile containing at least archipelago.json meant for a player"""
|
||||
game: ClassVar[Optional[str]] = None
|
||||
@@ -248,10 +290,8 @@ class APProcedurePatch(APAutoPatchInterface):
|
||||
manifest["compatible_version"] = 5
|
||||
return manifest
|
||||
|
||||
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
|
||||
super(APProcedurePatch, self).read_contents(opened_zipfile)
|
||||
with opened_zipfile.open("archipelago.json", "r") as f:
|
||||
manifest = json.load(f)
|
||||
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> Dict[str, Any]:
|
||||
manifest = super(APProcedurePatch, self).read_contents(opened_zipfile)
|
||||
if "procedure" not in manifest:
|
||||
# support patching files made before moving to procedures
|
||||
self.procedure = [("apply_bsdiff4", ["delta.bsdiff4"])]
|
||||
@@ -260,6 +300,7 @@ class APProcedurePatch(APAutoPatchInterface):
|
||||
for file in opened_zipfile.namelist():
|
||||
if file not in ["archipelago.json"]:
|
||||
self.files[file] = opened_zipfile.read(file)
|
||||
return manifest
|
||||
|
||||
def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
|
||||
super(APProcedurePatch, self).write_contents(opened_zipfile)
|
||||
|
||||
@@ -5,7 +5,7 @@ import weakref
|
||||
from enum import Enum, auto
|
||||
from typing import Optional, Callable, List, Iterable, Tuple
|
||||
|
||||
from Utils import local_path, open_filename
|
||||
from Utils import local_path, open_filename, is_frozen, is_kivy_running
|
||||
|
||||
|
||||
class Type(Enum):
|
||||
@@ -177,11 +177,10 @@ def _install_apworld(apworld_src: str = "") -> Optional[Tuple[pathlib.Path, path
|
||||
if module_name == loaded_name:
|
||||
found_already_loaded = True
|
||||
break
|
||||
if found_already_loaded:
|
||||
raise Exception(f"Installed APWorld successfully, but '{module_name}' is already loaded,\n"
|
||||
"so a Launcher restart is required to use the new installation.\n"
|
||||
"If the Launcher is not open, no action needs to be taken.")
|
||||
world_source = worlds.WorldSource(str(target), is_zip=True)
|
||||
if found_already_loaded and is_kivy_running():
|
||||
raise Exception(f"Installed APWorld successfully, but '{module_name}' is already loaded, "
|
||||
"so a Launcher restart is required to use the new installation.")
|
||||
world_source = worlds.WorldSource(str(target), is_zip=True, relative=False)
|
||||
bisect.insort(worlds.world_sources, world_source)
|
||||
world_source.load()
|
||||
|
||||
@@ -197,7 +196,7 @@ def install_apworld(apworld_path: str = "") -> None:
|
||||
source, target = res
|
||||
except Exception as e:
|
||||
import Utils
|
||||
Utils.messagebox(e.__class__.__name__, str(e), error=True)
|
||||
Utils.messagebox("Notice", str(e), error=True)
|
||||
logging.exception(e)
|
||||
else:
|
||||
import Utils
|
||||
@@ -218,8 +217,6 @@ components: List[Component] = [
|
||||
description="Install an APWorld to play games not included with Archipelago by default."),
|
||||
Component('Text Client', 'CommonClient', 'ArchipelagoTextClient', func=launch_textclient,
|
||||
description="Connect to a multiworld using the text client."),
|
||||
Component('Links Awakening DX Client', 'LinksAwakeningClient',
|
||||
file_identifier=SuffixIdentifier('.apladx')),
|
||||
Component('LttP Adjuster', 'LttPAdjuster'),
|
||||
# Ocarina of Time
|
||||
Component('OoT Client', 'OoTClient',
|
||||
@@ -229,8 +226,6 @@ components: List[Component] = [
|
||||
Component('Zelda 1 Client', 'Zelda1Client', file_identifier=SuffixIdentifier('.aptloz')),
|
||||
# ChecksFinder
|
||||
Component('ChecksFinder Client', 'ChecksFinderClient'),
|
||||
# Starcraft 2
|
||||
Component('Starcraft 2 Client', 'Starcraft2Client'),
|
||||
# Zillion
|
||||
Component('Zillion Client', 'ZillionClient',
|
||||
file_identifier=SuffixIdentifier('.apzl')),
|
||||
@@ -245,3 +240,67 @@ icon_paths = {
|
||||
'icon': local_path('data', 'icon.png'),
|
||||
'discord': local_path('data', 'discord-mark-blue.png'),
|
||||
}
|
||||
|
||||
if not is_frozen():
|
||||
def _build_apworlds(*launch_args: str):
|
||||
import json
|
||||
import os
|
||||
import zipfile
|
||||
|
||||
from worlds import AutoWorldRegister
|
||||
from worlds.Files import APWorldContainer
|
||||
from Launcher import open_folder
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser("Build script for APWorlds")
|
||||
parser.add_argument("worlds", type=str, default=(), nargs="*", help="Names of APWorlds to build.")
|
||||
args = parser.parse_args(launch_args)
|
||||
|
||||
if args.worlds:
|
||||
games = [(game, AutoWorldRegister.world_types.get(game, None)) for game in args.worlds]
|
||||
else:
|
||||
games = [(worldname, worldtype) for worldname, worldtype in AutoWorldRegister.world_types.items()
|
||||
if not worldtype.zip_path]
|
||||
|
||||
apworlds_folder = os.path.join("build", "apworlds")
|
||||
os.makedirs(apworlds_folder, exist_ok=True)
|
||||
for worldname, worldtype in games:
|
||||
if not worldtype:
|
||||
logging.error(f"Requested APWorld \"{worldname}\" does not exist.")
|
||||
continue
|
||||
file_name = os.path.split(os.path.dirname(worldtype.__file__))[1]
|
||||
world_directory = os.path.join("worlds", file_name)
|
||||
if os.path.isfile(os.path.join(world_directory, "archipelago.json")):
|
||||
with open(os.path.join(world_directory, "archipelago.json"), mode="r", encoding="utf-8") as manifest_file:
|
||||
manifest = json.load(manifest_file)
|
||||
|
||||
assert "game" in manifest, (
|
||||
f"World directory {world_directory} has an archipelago.json manifest file, but it"
|
||||
"does not define a \"game\"."
|
||||
)
|
||||
assert manifest["game"] == worldtype.game, (
|
||||
f"World directory {world_directory} has an archipelago.json manifest file, but value of the"
|
||||
f"\"game\" field ({manifest['game']} does not equal the World class's game ({worldtype.game})."
|
||||
)
|
||||
else:
|
||||
manifest = {}
|
||||
|
||||
zip_path = os.path.join(apworlds_folder, file_name + ".apworld")
|
||||
apworld = APWorldContainer(str(zip_path))
|
||||
apworld.game = worldtype.game
|
||||
manifest.update(apworld.get_manifest())
|
||||
apworld.manifest_path = f"{file_name}/archipelago.json"
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED,
|
||||
compresslevel=9) as zf:
|
||||
for path in pathlib.Path(world_directory).rglob("*"):
|
||||
relative_path = os.path.join(*path.parts[path.parts.index("worlds") + 1:])
|
||||
if "__MACOSX" in relative_path or ".DS_STORE" in relative_path or "__pycache__" in relative_path:
|
||||
continue
|
||||
if not relative_path.endswith("archipelago.json"):
|
||||
zf.write(path, relative_path)
|
||||
zf.writestr(apworld.manifest_path, json.dumps(manifest))
|
||||
open_folder(apworlds_folder)
|
||||
|
||||
|
||||
components.append(Component('Build APWorlds', func=_build_apworlds, cli=True,
|
||||
description="Build APWorlds from loose-file world folders."))
|
||||
|
||||
@@ -7,10 +7,11 @@ import warnings
|
||||
import zipimport
|
||||
import time
|
||||
import dataclasses
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
from NetUtils import DataPackage
|
||||
from Utils import local_path, user_path
|
||||
from Utils import local_path, user_path, Version, version_tuple, tuplize_version
|
||||
|
||||
local_folder = os.path.dirname(__file__)
|
||||
user_folder = user_path("worlds") if user_path() != local_path() else user_path("custom_worlds")
|
||||
@@ -38,6 +39,7 @@ class WorldSource:
|
||||
is_zip: bool = False
|
||||
relative: bool = True # relative to regular world import folder
|
||||
time_taken: float = -1.0
|
||||
version: Version = Version(0, 0, 0)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.path}, is_zip={self.is_zip}, relative={self.relative})"
|
||||
@@ -102,12 +104,94 @@ for folder in (folder for folder in (user_folder, local_folder) if folder):
|
||||
|
||||
# import all submodules to trigger AutoWorldRegister
|
||||
world_sources.sort()
|
||||
apworlds: list[WorldSource] = []
|
||||
for world_source in world_sources:
|
||||
world_source.load()
|
||||
# load all loose files first:
|
||||
if world_source.is_zip:
|
||||
apworlds.append(world_source)
|
||||
else:
|
||||
world_source.load()
|
||||
|
||||
|
||||
# Build the data package for each game.
|
||||
from .AutoWorld import AutoWorldRegister
|
||||
|
||||
for world_source in world_sources:
|
||||
if not world_source.is_zip:
|
||||
# look for manifest
|
||||
manifest = {}
|
||||
for dirpath, dirnames, filenames in os.walk(world_source.resolved_path):
|
||||
for file in filenames:
|
||||
if file.endswith("archipelago.json"):
|
||||
with open(os.path.join(dirpath, file), mode="r", encoding="utf-8") as manifest_file:
|
||||
manifest = json.load(manifest_file)
|
||||
break
|
||||
if manifest:
|
||||
break
|
||||
game = manifest.get("game")
|
||||
if game in AutoWorldRegister.world_types:
|
||||
AutoWorldRegister.world_types[game].world_version = tuplize_version(manifest.get("world_version", "0.0.0"))
|
||||
|
||||
if apworlds:
|
||||
# encapsulation for namespace / gc purposes
|
||||
def load_apworlds() -> None:
|
||||
global apworlds
|
||||
from .Files import APWorldContainer, InvalidDataError
|
||||
core_compatible: list[tuple[WorldSource, APWorldContainer]] = []
|
||||
|
||||
def fail_world(game_name: str, reason: str, add_as_failed_to_load: bool = True) -> None:
|
||||
if add_as_failed_to_load:
|
||||
failed_world_loads.append(game_name)
|
||||
logging.warning(reason)
|
||||
|
||||
for apworld_source in apworlds:
|
||||
apworld: APWorldContainer = APWorldContainer(apworld_source.resolved_path)
|
||||
# populate metadata
|
||||
try:
|
||||
apworld.read()
|
||||
except InvalidDataError as e:
|
||||
if version_tuple < (0, 7, 0):
|
||||
logging.error(
|
||||
f"Invalid or missing manifest file for {apworld_source.resolved_path}. "
|
||||
"This apworld will stop working with Archipelago 0.7.0."
|
||||
)
|
||||
logging.error(e)
|
||||
else:
|
||||
raise e
|
||||
|
||||
if apworld.minimum_ap_version and apworld.minimum_ap_version > version_tuple:
|
||||
fail_world(apworld.game,
|
||||
f"Did not load {apworld_source.path} "
|
||||
f"as its minimum core version {apworld.minimum_ap_version} "
|
||||
f"is higher than current core version {version_tuple}.")
|
||||
elif apworld.maximum_ap_version and apworld.maximum_ap_version < version_tuple:
|
||||
fail_world(apworld.game,
|
||||
f"Did not load {apworld_source.path} "
|
||||
f"as its maximum core version {apworld.maximum_ap_version} "
|
||||
f"is lower than current core version {version_tuple}.")
|
||||
else:
|
||||
core_compatible.append((apworld_source, apworld))
|
||||
# load highest version first
|
||||
core_compatible.sort(
|
||||
key=lambda element: element[1].world_version if element[1].world_version else Version(0, 0, 0),
|
||||
reverse=True)
|
||||
for apworld_source, apworld in core_compatible:
|
||||
if apworld.game and apworld.game in AutoWorldRegister.world_types:
|
||||
fail_world(apworld.game,
|
||||
f"Did not load {apworld_source.path} "
|
||||
f"as its game {apworld.game} is already loaded.",
|
||||
add_as_failed_to_load=False)
|
||||
else:
|
||||
apworld_source.load()
|
||||
if apworld.game in AutoWorldRegister.world_types:
|
||||
# world could fail to load at this point
|
||||
if apworld.world_version:
|
||||
AutoWorldRegister.world_types[apworld.game].world_version = apworld.world_version
|
||||
load_apworlds()
|
||||
del load_apworlds
|
||||
|
||||
del apworlds
|
||||
|
||||
# Build the data package for each game.
|
||||
network_data_package: DataPackage = {
|
||||
"games": {world_name: world.get_data_package_data() for world_name, world in AutoWorldRegister.world_types.items()},
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class GameData:
|
||||
"""
|
||||
:param data:
|
||||
"""
|
||||
self.abilities: Dict[int, AbilityData] = {}
|
||||
self.abilities: Dict[int, AbilityData] = {a.ability_id: AbilityData(self, a) for a in data.abilities if a.available}
|
||||
self.units: Dict[int, UnitTypeData] = {u.unit_id: UnitTypeData(self, u) for u in data.units if u.available}
|
||||
self.upgrades: Dict[int, UpgradeData] = {u.upgrade_id: UpgradeData(self, u) for u in data.upgrades}
|
||||
# Cached UnitTypeIds so that conversion does not take long. This needs to be moved elsewhere if a new GameData object is created multiple times per game
|
||||
@@ -40,7 +40,7 @@ class AbilityData:
|
||||
self._proto = proto
|
||||
|
||||
# What happens if we comment this out? Should this not be commented out? What is its purpose?
|
||||
assert self.id != 0
|
||||
# assert self.id != 0 # let the world burn
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"AbilityData(name={self._proto.button_name})"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
import Utils
|
||||
import websockets
|
||||
import functools
|
||||
@@ -208,6 +210,9 @@ async def proxy(websocket, path: str = "/", ctx: AHITContext = None):
|
||||
if not ctx.is_proxy_connected():
|
||||
break
|
||||
|
||||
if msg["cmd"] == "Bounce" and msg.get("tags") == ["DeathLink"] and "data" in msg:
|
||||
msg["data"]["time"] = time.time()
|
||||
|
||||
await ctx.send_msgs([msg])
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -623,6 +623,23 @@ class ParadeTrapWeight(Range):
|
||||
default = 20
|
||||
|
||||
|
||||
class DeathLinkAmnesty(Range):
|
||||
"""Amount of forgiven deaths before sending a Death Link.
|
||||
0 means that every death will send a Death Link."""
|
||||
display_name = "Death Link Amnesty"
|
||||
range_start = 0
|
||||
range_end = 20
|
||||
default = 0
|
||||
|
||||
|
||||
class DWDeathLinkAmnesty(Range):
|
||||
"""Amount of forgiven deaths before sending a Death Link during Death Wish levels."""
|
||||
display_name = "Death Wish Amnesty"
|
||||
range_start = 0
|
||||
range_end = 30
|
||||
default = 5
|
||||
|
||||
|
||||
@dataclass
|
||||
class AHITOptions(PerGameCommonOptions):
|
||||
start_inventory_from_pool: StartInventoryPool
|
||||
@@ -700,6 +717,8 @@ class AHITOptions(PerGameCommonOptions):
|
||||
ParadeTrapWeight: ParadeTrapWeight
|
||||
|
||||
death_link: DeathLink
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
dw_death_link_amnesty: DWDeathLinkAmnesty
|
||||
|
||||
|
||||
ahit_option_groups: Dict[str, List[Any]] = {
|
||||
@@ -769,4 +788,6 @@ slot_data_options: List[str] = [
|
||||
"MaxPonCost",
|
||||
|
||||
"death_link",
|
||||
"death_link_amnesty",
|
||||
"dw_death_link_amnesty",
|
||||
]
|
||||
|
||||
@@ -243,7 +243,7 @@ guaranteed_first_acts = [
|
||||
"Time Rift - Mafia of Cooks",
|
||||
"Time Rift - Dead Bird Studio",
|
||||
"Time Rift - Sleepy Subcon",
|
||||
"Time Rift - Alpine Skyline"
|
||||
"Time Rift - Alpine Skyline",
|
||||
"Time Rift - Tour",
|
||||
"Time Rift - Rumbi Factory",
|
||||
]
|
||||
|
||||
@@ -88,9 +88,8 @@ You only have to do these steps once.
|
||||
1. Enter the RetroArch main menu screen.
|
||||
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
|
||||
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
|
||||
Network Command Port at 55355.
|
||||
|
||||

|
||||
Network Command Port at 55355. \
|
||||

|
||||
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
|
||||
Performance)".
|
||||
|
||||
|
||||
@@ -88,9 +88,8 @@ Sólo hay que seguir estos pasos una vez.
|
||||
1. Comienza en la pantalla del menú principal de RetroArch.
|
||||
2. Ve a Ajustes --> Interfaz de usario. Configura "Mostrar ajustes avanzados" en ON.
|
||||
3. Ve a Ajustes --> Red. Pon "Comandos de red" en ON. (Se encuentra bajo Request Device 16.) Deja en 55355 el valor por defecto,
|
||||
el Puerto de comandos de red.
|
||||
|
||||

|
||||
el Puerto de comandos de red. \
|
||||

|
||||
4. Ve a Menú principal --> Actualizador en línea --> Descargador de núcleos. Desplázate y selecciona "Nintendo - SNES /
|
||||
SFC (bsnes-mercury Performance)".
|
||||
|
||||
|
||||
@@ -89,9 +89,8 @@ Vous n'avez qu'à faire ces étapes qu'une fois.
|
||||
1. Entrez dans le menu principal RetroArch
|
||||
2. Allez dans Réglages --> Interface utilisateur. Mettez "Afficher les réglages avancés" sur ON.
|
||||
3. Allez dans Réglages --> Réseau. Mettez "Commandes Réseau" sur ON. (trouvé sous Request Device 16.) Laissez le
|
||||
Port des commandes réseau à 555355.
|
||||
|
||||

|
||||
Port des commandes réseau à 555355. \
|
||||

|
||||
4. Allez dans Menu Principal --> Mise à jour en ligne --> Téléchargement de cœurs. Descendez jusqu'a"Nintendo - SNES / SFC (bsnes-mercury Performance)" et
|
||||
sélectionnez le.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,22 +0,0 @@
|
||||
import unittest
|
||||
from argparse import Namespace
|
||||
|
||||
from BaseClasses import MultiWorld, CollectionState
|
||||
from worlds import AutoWorldRegister
|
||||
|
||||
|
||||
class LTTPTestBase(unittest.TestCase):
|
||||
def world_setup(self):
|
||||
from worlds.alttp.Options import Medallion
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = "A Link to the Past"
|
||||
self.multiworld.set_seed(None)
|
||||
args = Namespace()
|
||||
for name, option in AutoWorldRegister.world_types["A Link to the Past"].options_dataclass.type_hints.items():
|
||||
setattr(args, name, {1: option.from_any(getattr(option, "default"))})
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.state = CollectionState(self.multiworld)
|
||||
self.world = self.multiworld.worlds[1]
|
||||
# by default medallion access is randomized, for unittests we set it to vanilla
|
||||
self.world.options.misery_mire_medallion.value = Medallion.option_ether
|
||||
self.world.options.turtle_rock_medallion.value = Medallion.option_quake
|
||||
|
||||
113
worlds/alttp/test/bases.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import unittest
|
||||
from argparse import Namespace
|
||||
|
||||
from BaseClasses import MultiWorld, CollectionState, ItemClassification
|
||||
from worlds import AutoWorldRegister
|
||||
from ..Items import item_factory
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
multiworld: MultiWorld
|
||||
_state_cache = {}
|
||||
|
||||
def get_state(self, items):
|
||||
if (self.multiworld, tuple(items)) in self._state_cache:
|
||||
return self._state_cache[self.multiworld, tuple(items)]
|
||||
state = CollectionState(self.multiworld)
|
||||
for item in items:
|
||||
item.classification = ItemClassification.progression
|
||||
state.collect(item, prevent_sweep=True)
|
||||
state.sweep_for_advancements()
|
||||
state.update_reachable_regions(1)
|
||||
self._state_cache[self.multiworld, tuple(items)] = state
|
||||
return state
|
||||
|
||||
def get_path(self, state, region):
|
||||
def flist_to_iter(node):
|
||||
while node:
|
||||
value, node = node
|
||||
yield value
|
||||
|
||||
from itertools import zip_longest
|
||||
reversed_path_as_flist = state.path.get(region, (region, None))
|
||||
string_path_flat = reversed(list(map(str, flist_to_iter(reversed_path_as_flist))))
|
||||
# Now we combine the flat string list into (region, exit) pairs
|
||||
pathsiter = iter(string_path_flat)
|
||||
pathpairs = zip_longest(pathsiter, pathsiter)
|
||||
return list(pathpairs)
|
||||
|
||||
def run_location_tests(self, access_pool):
|
||||
for i, (location, access, *item_pool) in enumerate(access_pool):
|
||||
items = item_pool[0]
|
||||
all_except = item_pool[1] if len(item_pool) > 1 else None
|
||||
state = self._get_items(item_pool, all_except)
|
||||
path = self.get_path(state, self.multiworld.get_location(location, 1).parent_region)
|
||||
with self.subTest(msg="Reach Location", location=location, access=access, items=items,
|
||||
all_except=all_except, path=path, entry=i):
|
||||
|
||||
self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access,
|
||||
f"failed {self.multiworld.get_location(location, 1)} with: {item_pool}")
|
||||
|
||||
# check for partial solution
|
||||
if not all_except and access: # we are not supposed to be able to reach location with partial inventory
|
||||
for missing_item in item_pool[0]:
|
||||
with self.subTest(msg="Location reachable without required item", location=location,
|
||||
items=item_pool[0], missing_item=missing_item, entry=i):
|
||||
state = self._get_items_partial(item_pool, missing_item)
|
||||
|
||||
self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), False,
|
||||
f"failed {self.multiworld.get_location(location, 1)}: succeeded with "
|
||||
f"{missing_item} removed from: {item_pool}")
|
||||
|
||||
def run_entrance_tests(self, access_pool):
|
||||
for i, (entrance, access, *item_pool) in enumerate(access_pool):
|
||||
items = item_pool[0]
|
||||
all_except = item_pool[1] if len(item_pool) > 1 else None
|
||||
state = self._get_items(item_pool, all_except)
|
||||
path = self.get_path(state, self.multiworld.get_entrance(entrance, 1).parent_region)
|
||||
with self.subTest(msg="Reach Entrance", entrance=entrance, access=access, items=items,
|
||||
all_except=all_except, path=path, entry=i):
|
||||
|
||||
self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), access)
|
||||
|
||||
# check for partial solution
|
||||
if not all_except and access: # we are not supposed to be able to reach location with partial inventory
|
||||
for missing_item in item_pool[0]:
|
||||
with self.subTest(msg="Entrance reachable without required item", entrance=entrance,
|
||||
items=item_pool[0], missing_item=missing_item, entry=i):
|
||||
state = self._get_items_partial(item_pool, missing_item)
|
||||
self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), False,
|
||||
f"failed {self.multiworld.get_entrance(entrance, 1)} with: {item_pool}")
|
||||
|
||||
def _get_items(self, item_pool, all_except):
|
||||
if all_except and len(all_except) > 0:
|
||||
items = self.multiworld.itempool[:]
|
||||
items = [item for item in items if
|
||||
item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)]
|
||||
items.extend(item_factory(item_pool[0], self.multiworld.worlds[1]))
|
||||
else:
|
||||
items = item_factory(item_pool[0], self.multiworld.worlds[1])
|
||||
return self.get_state(items)
|
||||
|
||||
def _get_items_partial(self, item_pool, missing_item):
|
||||
new_items = item_pool[0].copy()
|
||||
new_items.remove(missing_item)
|
||||
items = item_factory(new_items, self.multiworld.worlds[1])
|
||||
return self.get_state(items)
|
||||
|
||||
|
||||
class LTTPTestBase(unittest.TestCase):
|
||||
def world_setup(self):
|
||||
from worlds.alttp.Options import Medallion
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = "A Link to the Past"
|
||||
self.multiworld.set_seed(None)
|
||||
args = Namespace()
|
||||
for name, option in AutoWorldRegister.world_types["A Link to the Past"].options_dataclass.type_hints.items():
|
||||
setattr(args, name, {1: option.from_any(getattr(option, "default"))})
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.state = CollectionState(self.multiworld)
|
||||
self.world = self.multiworld.worlds[1]
|
||||
# by default medallion access is randomized, for unittests we set it to vanilla
|
||||
self.world.options.misery_mire_medallion.value = Medallion.option_ether
|
||||
self.world.options.turtle_rock_medallion.value = Medallion.option_quake
|
||||
@@ -5,7 +5,7 @@ from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from worlds.alttp.Regions import create_regions
|
||||
from worlds.alttp.Shops import create_shops
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from worlds.alttp.test.bases import LTTPTestBase
|
||||
|
||||
|
||||
class TestDungeon(LTTPTestBase):
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.EntranceShuffle import link_inverted_entrances
|
||||
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from worlds.alttp.Regions import mark_light_world_regions
|
||||
from worlds.alttp.Shops import create_shops
|
||||
from test.bases import TestBase
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...EntranceShuffle import link_inverted_entrances
|
||||
from ...InvertedRegions import create_inverted_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Regions import mark_light_world_regions
|
||||
from ...Shops import create_shops
|
||||
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestInverted(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -4,7 +4,7 @@ from worlds.alttp.EntranceShuffle import connect_entrance, Inverted_LW_Entrances
|
||||
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Rules import set_inverted_big_bomb_rules
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from worlds.alttp.test.bases import LTTPTestBase
|
||||
|
||||
|
||||
class TestInvertedBombRules(LTTPTestBase):
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.EntranceShuffle import link_inverted_entrances
|
||||
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from worlds.alttp.Options import GlitchesRequired
|
||||
from worlds.alttp.Regions import mark_light_world_regions
|
||||
from worlds.alttp.Shops import create_shops
|
||||
from test.bases import TestBase
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...EntranceShuffle import link_inverted_entrances
|
||||
from ...InvertedRegions import create_inverted_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Options import GlitchesRequired
|
||||
from ...Regions import mark_light_world_regions
|
||||
from ...Shops import create_shops
|
||||
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestInvertedMinor(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.EntranceShuffle import link_inverted_entrances
|
||||
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from worlds.alttp.Options import GlitchesRequired
|
||||
from worlds.alttp.Regions import mark_light_world_regions
|
||||
from worlds.alttp.Shops import create_shops
|
||||
from test.bases import TestBase
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...EntranceShuffle import link_inverted_entrances
|
||||
from ...InvertedRegions import create_inverted_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Options import GlitchesRequired
|
||||
from ...Regions import mark_light_world_regions
|
||||
from ...Shops import create_shops
|
||||
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestInvertedOWG(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from test.bases import TestBase
|
||||
from ...ItemPool import difficulties
|
||||
from ..bases import TestBase
|
||||
|
||||
base_items = 41
|
||||
extra_counts = (15, 15, 10, 5, 25)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.InvertedRegions import mark_dark_world_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from test.bases import TestBase
|
||||
from worlds.alttp.Options import GlitchesRequired
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...InvertedRegions import mark_dark_world_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Options import GlitchesRequired
|
||||
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestMinor(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.InvertedRegions import mark_dark_world_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from test.bases import TestBase
|
||||
from worlds.alttp.Options import GlitchesRequired
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...InvertedRegions import mark_dark_world_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Options import GlitchesRequired
|
||||
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestVanillaOWG(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from worlds.alttp.Shops import shop_table
|
||||
from test.bases import TestBase
|
||||
from ...Shops import shop_table
|
||||
from ..bases import TestBase
|
||||
|
||||
|
||||
class TestSram(TestBase):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||
from worlds.alttp.InvertedRegions import mark_dark_world_regions
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from worlds.alttp.Items import item_factory
|
||||
from test.bases import TestBase
|
||||
from worlds.alttp.Options import GlitchesRequired
|
||||
from worlds.alttp.test import LTTPTestBase
|
||||
from ...Dungeons import get_dungeon_item_pool
|
||||
from ...InvertedRegions import mark_dark_world_regions
|
||||
from ...ItemPool import difficulties
|
||||
from ...Items import item_factory
|
||||
from ...Options import GlitchesRequired
|
||||
|
||||
from ..bases import LTTPTestBase, TestBase
|
||||
|
||||
|
||||
class TestVanilla(TestBase, LTTPTestBase):
|
||||
|
||||
@@ -23,7 +23,7 @@ game you play will make sure that every game has its own save game.
|
||||
Unzip the Aquaria randomizer release and copy all unzipped files in the Aquaria game folder. The unzipped files are:
|
||||
- aquaria_randomizer.exe
|
||||
- OpenAL32.dll
|
||||
- override (directory)
|
||||
- randomizer_files (directory)
|
||||
- SDL2.dll
|
||||
- usersettings.xml
|
||||
- wrap_oal.dll
|
||||
@@ -32,7 +32,10 @@ Unzip the Aquaria randomizer release and copy all unzipped files in the Aquaria
|
||||
If there is a conflict between files in the original game folder and the unzipped files, you should overwrite
|
||||
the original files with the ones from the unzipped randomizer.
|
||||
|
||||
Finally, to launch the randomizer, you must use the command line interface (you can open the command line interface
|
||||
There is multiple way to start the game. The easiest one is using the launcher. To do that, just run
|
||||
the `aquaria_randomizer.exe` file.
|
||||
|
||||
You can also launch the randomizer using the command line interface (you can open the command line interface
|
||||
by typing `cmd` in the address bar of the Windows File Explorer). Here is the command line used to start the
|
||||
randomizer:
|
||||
|
||||
@@ -49,15 +52,17 @@ aquaria_randomizer.exe --name YourName --server theServer:thePort --password th
|
||||
### Linux when using the AppImage
|
||||
|
||||
If you use the AppImage, just copy it into the Aquaria game folder. You then have to make it executable. You
|
||||
can do that from command line by using:
|
||||
can do that from the command line by using:
|
||||
|
||||
```bash
|
||||
chmod +x Aquaria_Randomizer-*.AppImage
|
||||
```
|
||||
|
||||
or by using the Graphical Explorer of your system.
|
||||
or by using the Graphical file Explorer of your system (the permission can generally be set in the file properties).
|
||||
|
||||
To launch the randomizer, just launch in command line:
|
||||
To launch the randomizer using the integrated launcher, just execute the AppImage file.
|
||||
|
||||
You can also use command line arguments to set the server and slot of your game:
|
||||
|
||||
```bash
|
||||
./Aquaria_Randomizer-*.AppImage --name YourName --server theServer:thePort
|
||||
@@ -79,7 +84,7 @@ the original game will stop working. Copying the folder will guarantee that the
|
||||
|
||||
Untar the Aquaria randomizer release and copy all extracted files in the Aquaria game folder. The extracted files are:
|
||||
- aquaria_randomizer
|
||||
- override (directory)
|
||||
- randomizer_files (directory)
|
||||
- usersettings.xml
|
||||
- cacert.pem
|
||||
|
||||
@@ -87,7 +92,7 @@ If there is a conflict between files in the original game folder and the extract
|
||||
the original files with the ones from the extracted randomizer files.
|
||||
|
||||
Then, you should use your system package manager to install `liblua5`, `libogg`, `libvorbis`, `libopenal` and `libsdl2`.
|
||||
On Debian base system (like Ubuntu), you can use the following command:
|
||||
On Debian base systems (like Ubuntu), you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo apt install liblua5.1-0-dev libogg-dev libvorbis-dev libopenal-dev libsdl2-dev
|
||||
@@ -97,7 +102,9 @@ Also, if there are certain `.so` files in the original Aquaria game folder (`lib
|
||||
`libSDL-1.2.so.0` and `libstdc++.so.6`), you should remove them from the Aquaria Randomizer game folder. Those are
|
||||
old libraries that will not work on the recent build of the randomizer.
|
||||
|
||||
To launch the randomizer, just launch in command line:
|
||||
To launch the randomizer using the integrated launcher, just execute the `aquaria_randomizer` file.
|
||||
|
||||
You can also use command line arguments to set the server and slot of your game:
|
||||
|
||||
```bash
|
||||
./aquaria_randomizer --name YourName --server theServer:thePort
|
||||
@@ -115,6 +122,20 @@ sure that your executable has executable permission:
|
||||
```bash
|
||||
chmod +x aquaria_randomizer
|
||||
```
|
||||
### Steam deck
|
||||
|
||||
On the Steamdeck, go in desktop mode and follow the same procedure as the Linux Appimage.
|
||||
|
||||
|
||||
### No sound on Linux/Steam deck
|
||||
|
||||
If your game play without problems, but with no sound, the game probably does not use the correct
|
||||
driver for the sound system. To fix that, you can use `ALSOFT_DRIVERS=pulse` before your command
|
||||
line to make it work. Something like this (depending on the way you launch the randomizer):
|
||||
|
||||
```bash
|
||||
ALSOFT_DRIVERS=pulse ./Aquaria_Randomizer-*.AppImage --name YourName --server theServer:thePort
|
||||
```
|
||||
|
||||
## Auto-Tracking
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
## Logiciels nécessaires
|
||||
|
||||
- Une copie du jeu Aquaria non-modifiée (disponible sur la majorité des sites de ventes de jeux vidéos en ligne)
|
||||
- Une copie du jeu Aquaria non modifiée (disponible sur la majorité des sites de ventes de jeux vidéos en ligne)
|
||||
- Le client du Randomizer d'Aquaria [Aquaria randomizer](https://github.com/tioui/Aquaria_Randomizer/releases/latest)
|
||||
|
||||
## Logiciels optionnels
|
||||
|
||||
- De manière optionnel, pour pouvoir envoyer des [commandes](/tutorial/Archipelago/commands/en) comme `!hint`: utilisez le client texte de [la version la plus récente d'Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
|
||||
- De manière optionnelle, pour pouvoir envoyer des [commandes](/tutorial/Archipelago/commands/en) comme `!hint`: utilisez le client texte de [la version la plus récente d'Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
|
||||
- [Aquaria AP Tracker](https://github.com/palex00/aquaria-ap-tracker/releases/latest), pour utiliser avec [PopTracker](https://github.com/black-sliver/PopTracker/releases/latest)
|
||||
|
||||
## Procédures d'installation et d'exécution
|
||||
@@ -25,7 +25,7 @@ Désarchiver le randomizer d'Aquaria et copier tous les fichiers de l'archive da
|
||||
fichier d'archive devrait contenir les fichiers suivants:
|
||||
- aquaria_randomizer.exe
|
||||
- OpenAL32.dll
|
||||
- override (directory)
|
||||
- randomizer_files (directory)
|
||||
- SDL2.dll
|
||||
- usersettings.xml
|
||||
- wrap_oal.dll
|
||||
@@ -34,7 +34,10 @@ fichier d'archive devrait contenir les fichiers suivants:
|
||||
S'il y a des conflits entre les fichiers de l'archive zip et les fichiers du jeu original, vous devez utiliser
|
||||
les fichiers contenus dans l'archive zip.
|
||||
|
||||
Finalement, pour lancer le randomizer, vous devez utiliser la ligne de commande (vous pouvez ouvrir une interface de
|
||||
Il y a plusieurs manières de lancer le randomizer. Le plus simple consiste à utiliser le lanceur intégré en
|
||||
exécutant simplement le fichier `aquaria_randomizer.exe`.
|
||||
|
||||
Il est également possible de lancer le randomizer en utilisant la ligne de commande (vous pouvez ouvrir une interface de
|
||||
ligne de commande, entrez l'adresse `cmd` dans la barre d'adresse de l'explorateur de fichier de Windows). Voici
|
||||
la ligne de commande à utiliser pour lancer le randomizer:
|
||||
|
||||
@@ -57,9 +60,12 @@ le mettre exécutable. Vous pouvez mettre le fichier exécutable avec la command
|
||||
chmod +x Aquaria_Randomizer-*.AppImage
|
||||
```
|
||||
|
||||
ou bien en utilisant l'explorateur graphique de votre système.
|
||||
ou bien en utilisant l'explorateur de fichier graphique de votre système (la permission d'exécution est
|
||||
généralement dans les propriétés du fichier).
|
||||
|
||||
Pour lancer le randomizer, utiliser la commande suivante:
|
||||
Pour lancer le randomizer en utilisant le lanceur intégré, seulement exécuter le fichier AppImage.
|
||||
|
||||
Vous pouvez également lancer le randomizer en spécifiant les informations de connexion dans les arguments de la ligne de commande:
|
||||
|
||||
```bash
|
||||
./Aquaria_Randomizer-*.AppImage --name VotreNom --server LeServeur:LePort
|
||||
@@ -83,7 +89,7 @@ avant de déposer le randomizer à l'intérieur permet de vous assurer de garder
|
||||
Désarchiver le fichier tar et copier tous les fichiers qu'il contient dans le répertoire du jeu d'origine d'Aquaria. Les
|
||||
fichiers extraient du fichier tar devraient être les suivants:
|
||||
- aquaria_randomizer
|
||||
- override (directory)
|
||||
- randomizer_files (directory)
|
||||
- usersettings.xml
|
||||
- cacert.pem
|
||||
|
||||
@@ -102,7 +108,10 @@ Notez également que s'il y a des fichiers ".so" dans le répertoire d'Aquaria (
|
||||
`libSDL-1.2.so.0` and `libstdc++.so.6`), vous devriez les retirer. Il s'agit de vieille version des librairies qui
|
||||
ne sont plus fonctionnelles dans les systèmes modernes et qui pourrait empêcher le randomizer de fonctionner.
|
||||
|
||||
Pour lancer le randomizer, utiliser la commande suivante:
|
||||
Pour lancer le randomizer en utilisant le lanceur intégré, seulement exécuter le fichier `aquaria_randomizer`.
|
||||
|
||||
Vous pouvez également lancer le randomizer en spécifiant les information de connexion dans les arguments de la
|
||||
ligne de commande:
|
||||
|
||||
```bash
|
||||
./aquaria_randomizer --name VotreNom --server LeServeur:LePort
|
||||
@@ -120,6 +129,21 @@ pour vous assurer que votre fichier est exécutable:
|
||||
```bash
|
||||
chmod +x aquaria_randomizer
|
||||
```
|
||||
### Steam Deck
|
||||
|
||||
Pour installer le randomizer sur la Steam Deck, seulement suivre la procédure pour les fichiers AppImage
|
||||
indiquée précédemment.
|
||||
|
||||
### Aucun son sur Linux/Steam Deck
|
||||
|
||||
Si le jeu fonctionne sans problème, mais qu'il n'y a aucun son, c'est probablement parce que le jeu
|
||||
n'arrive pas à utiliser le bon pilote de son. Généralement, le problème est réglé en ajoutant la
|
||||
variable d'environnement `ALSOFT_DRIVERS=pulse`. Voici un exemple (peut varier en fonction de la manière
|
||||
que le randomizer est lancé):
|
||||
|
||||
```bash
|
||||
ALSOFT_DRIVERS=pulse ./Aquaria_Randomizer-*.AppImage --name VotreNom --server LeServeur:LePort
|
||||
```
|
||||
|
||||
## Tracking automatique
|
||||
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
item_table = (
|
||||
'An Old GeoCities Profile',
|
||||
'Very Funny Joke',
|
||||
'Motivational Video',
|
||||
'Staples Easy Button',
|
||||
'One Million Dollars',
|
||||
'Replica Master Sword',
|
||||
'VHS Copy of Jurassic Park',
|
||||
'32GB USB Drive',
|
||||
'Pocket Protector',
|
||||
'Leftover Parts from IKEA Furniture',
|
||||
'Half-Empty Ink Cartridge for a Printer',
|
||||
'Watch Battery',
|
||||
'Towel',
|
||||
'Scarf',
|
||||
'2012 Magic the Gathering Core Set Starter Box',
|
||||
'Poke\'mon Booster Pack',
|
||||
'USB Speakers',
|
||||
'Eco-Friendly Spork',
|
||||
'Cheeseburger',
|
||||
'Brand New Car',
|
||||
'Hunting Knife',
|
||||
'Zippo Lighter',
|
||||
'Red Shirt',
|
||||
'One-Up Mushroom',
|
||||
'Nokia N-GAGE',
|
||||
'2-Liter of Sprite',
|
||||
'Free trial of the critically acclaimed MMORPG Final Fantasy XIV, including the entirety of A Realm Reborn and the award winning Heavensward and Stormblood expansions up to level 70 with no restrictions on playtime!',
|
||||
'Can of Compressed Air',
|
||||
'Striped Kitten',
|
||||
'USB Power Adapter',
|
||||
'Fortune Cookie',
|
||||
'Nintendo Power Glove',
|
||||
'The Lampshade of No Real Significance',
|
||||
'Kneepads of Allure',
|
||||
'Get Out of Jail Free Card',
|
||||
'Box Set of Stargate SG-1 Season 4',
|
||||
'The Missing Left Sock',
|
||||
'Poster Tube',
|
||||
'Electronic Picture Frame',
|
||||
'Bottle of Shampoo',
|
||||
'Your Mission, Should You Choose To Accept It',
|
||||
'Fanny Pack',
|
||||
'Robocop T-Shirt',
|
||||
'Suspiciously Small Monocle',
|
||||
'Table Saw',
|
||||
'Cookies and Cream Milkshake',
|
||||
'Deflated Accordion',
|
||||
'Grandma\'s Homemade Pie',
|
||||
'Invisible Lego on the Floor',
|
||||
'Pitfall Trap',
|
||||
'Flathead Screwdriver',
|
||||
'Leftover Pizza',
|
||||
'Voodoo Doll that Looks Like You',
|
||||
'Pink Shoelaces',
|
||||
'Half a Bottle of Scotch',
|
||||
'Reminder Not to Forget Aginah',
|
||||
'Medicine Ball',
|
||||
'Yoga Mat',
|
||||
'Chocolate Orange',
|
||||
'Old Concert Tickets',
|
||||
'The Pick of Destiny',
|
||||
'McGuffin',
|
||||
'Just a Regular McMuffin',
|
||||
'34 Tacos',
|
||||
'Duct Tape',
|
||||
'Copy of Untitled Goose Game',
|
||||
'Partially Used Bed Bath & Beyond Gift Card',
|
||||
'Mostly Popped Bubble Wrap',
|
||||
'Expired Driver\'s License',
|
||||
'The Look, You Know the One',
|
||||
'Transformers Lunch Box',
|
||||
'MP3 Player',
|
||||
'Dry Sharpie',
|
||||
'Chalkboard Eraser',
|
||||
'Overhead Projector',
|
||||
'Physical Copy of the Japanese 1.0 Link to the Past',
|
||||
'Collectable Action Figure',
|
||||
'Box Set of The Lord of the Rings Books',
|
||||
'Lite-Bright',
|
||||
'Stories from the Good-Old-Days',
|
||||
'Un-Reproducable Bug Reports',
|
||||
'Autographed Copy of Shaq-Fu',
|
||||
'Game-Winning Baseball',
|
||||
'Portable Battery Bank',
|
||||
'Blockbuster Membership Card',
|
||||
'Offensive Bumper Sticker',
|
||||
'Last Sunday\'s Crossword Puzzle',
|
||||
'Rubik\'s Cube',
|
||||
'Your First Grey Hair',
|
||||
'Embarrassing Childhood Photo',
|
||||
'Abandoned Sphere One Check',
|
||||
'The Internet',
|
||||
'Late-Night Cartoons',
|
||||
'The Correct Usage of a Semicolon',
|
||||
'Microsoft Windows 95 Resource Kit',
|
||||
'Car-Phone',
|
||||
'Walkman Radio',
|
||||
'Relevant XKCD Comic',
|
||||
'Razor Scooter',
|
||||
'Set of Beyblades',
|
||||
'Box of Pogs',
|
||||
'Beanie-Baby Collection',
|
||||
'Laser Tag Gun',
|
||||
'Radio Controlled Car',
|
||||
'Boogie Board',
|
||||
'Air Jordans',
|
||||
'Rubber Duckie',
|
||||
'The Last Cookie in the Cookie Jar',
|
||||
'Tin-Foil Hat',
|
||||
'Button-Up Shirt',
|
||||
'Designer Brand Bag',
|
||||
'Trapper Keeper',
|
||||
'Fake Moustache',
|
||||
'Colored Pencils',
|
||||
'Pair of 3D Glasses',
|
||||
'Pair of Movie Tickets',
|
||||
'Refrigerator Magnets',
|
||||
'NASCAR Dinner Plates',
|
||||
'The Final Boss',
|
||||
'Unskippable Cutscenes',
|
||||
'24 Rolls of Toilet Paper',
|
||||
'Canned Soup',
|
||||
'Warm Blanket',
|
||||
'3D Printer',
|
||||
'Jetpack',
|
||||
'Hoverboard',
|
||||
'Joycons with No Drift',
|
||||
'Double Rainbow',
|
||||
'Ping Pong Ball',
|
||||
'Area 51 Arcade Cabinet',
|
||||
'Elephant in the Room',
|
||||
'The Pink Panther',
|
||||
'Denim Shorts',
|
||||
'Tennis Racket',
|
||||
'Collection of Stuffed Animals',
|
||||
'Old Cell Phone',
|
||||
'Nintendo Virtual Boy',
|
||||
'Box of 5.25 Inch Floppy Disks',
|
||||
'Bag of Miscellaneous Wires',
|
||||
'Garden Shovel',
|
||||
'Leather Gloves',
|
||||
'Knife of +9 VS Ogres',
|
||||
'Old, Smelly Cheese',
|
||||
'Linksys BEFSR41 Router',
|
||||
'Ethernet Cables for a LAN Party',
|
||||
'Mechanical Pencil',
|
||||
'Book of Graph Paper',
|
||||
'300 Sheets of Printer Paper',
|
||||
'One AAA Battery',
|
||||
'Box of Old Game Controllers',
|
||||
'Sega Dreamcast',
|
||||
'Mario\'s Overalls',
|
||||
'Betamax Player',
|
||||
'Stray Lego',
|
||||
'Chocolate Chip Pancakes',
|
||||
'Two Blueberry Muffins',
|
||||
'Nintendo 64 Controller with a Perfect Thumbstick',
|
||||
'Cuckoo Crossing the Road',
|
||||
'One Eyed, One Horned, Flying Purple People-Eater',
|
||||
'Love Potion Number Nine',
|
||||
'Wireless Headphones',
|
||||
'Festive Keychain',
|
||||
'Bundle of Twisted Cables',
|
||||
'Plank of Wood',
|
||||
'Broken Ant Farm',
|
||||
'Thirty-six American Dollars',
|
||||
'Can of Shaving Cream',
|
||||
'Blue Hair Dye',
|
||||
'Mug Engraved with the AP Logo',
|
||||
'Tube of Toothpaste',
|
||||
'Album of Elevator Music',
|
||||
'Headlight Fluid',
|
||||
'Tickets to the Renaissance Faire',
|
||||
'Bag of Golf Balls',
|
||||
'Box of Packing Peanuts',
|
||||
'Bottle of Peanut Butter',
|
||||
'Breath of the Wild Cookbook',
|
||||
'Stardew Valley Cookbook',
|
||||
'Thirteen Angry Chickens',
|
||||
'Bowl of Cereal',
|
||||
'Rubber Snake',
|
||||
'Stale Sunflower Seeds',
|
||||
'Alarm Clock Without a Snooze Button',
|
||||
'Wet Pineapple',
|
||||
'Set of Scented Candles',
|
||||
'Adorable Stuffed Animal',
|
||||
'The Broodwitch',
|
||||
'Old Photo Album',
|
||||
'Trade Quest Item',
|
||||
'Pair of Fancy Boots',
|
||||
'Shoddy Pickaxe',
|
||||
'Adventurer\'s Sword',
|
||||
'Cute Puppy',
|
||||
'Box of Matches',
|
||||
'Set of Allen Wrenches',
|
||||
'Glass of Water',
|
||||
'Magic Shaggy Carpet',
|
||||
'Macaroni and Cheese',
|
||||
'Chocolate Chip Cookie Dough Ice Cream',
|
||||
'Fresh Strawberries',
|
||||
'Delicious Tacos',
|
||||
'The Krabby Patty Recipe',
|
||||
'Map to Waldo\'s Location',
|
||||
'Stray Cat',
|
||||
'Ham and Cheese Sandwich',
|
||||
'DVD Player',
|
||||
'Motorcycle Helmet',
|
||||
'Fake Flowers',
|
||||
'6-Pack of Sponges',
|
||||
'Heated Pants',
|
||||
'Empty Glass Bottle',
|
||||
'Brown Paper Bag',
|
||||
'Model Train Set',
|
||||
'TV Remote',
|
||||
'RC Car',
|
||||
'Super Soaker 9000',
|
||||
'Giant Sunglasses',
|
||||
'World\'s Smallest Violin',
|
||||
'Pile of Fresh Warm Laundry',
|
||||
'Half-Empty Ice Cube Tray',
|
||||
'Bob Ross Afro Wig',
|
||||
'Empty Cardboard Box',
|
||||
'Packet of Soy Sauce',
|
||||
'Solutions to a Math Test',
|
||||
'Pencil Eraser',
|
||||
'The Great Pumpkin',
|
||||
'Very Expensive Toaster',
|
||||
'Pack of Colored Sharpies',
|
||||
'Bag of Chocolate Chips',
|
||||
'Grandma\'s Homemade Cookies',
|
||||
'Collection of Bottle Caps',
|
||||
'Pack of Playing Cards',
|
||||
'Boom Box',
|
||||
'Toy Sail Boat',
|
||||
'Smooth Nail File',
|
||||
'Colored Chalk',
|
||||
'Missing Button',
|
||||
'Rubber Band Ball',
|
||||
'Joystick',
|
||||
'Galaga Arcade Cabinet',
|
||||
'Anime Mouse Pad',
|
||||
'Orange and Yellow Glow Sticks',
|
||||
'Odd Bookmark',
|
||||
'Stray Dice',
|
||||
'Tooth Picks',
|
||||
'Dirty Dishes',
|
||||
'Poke\'mon Card Game Rule Book (Gen 1)',
|
||||
'Salt Shaker',
|
||||
'Digital Thermometer',
|
||||
'Infinite Improbability Drive',
|
||||
'Fire Extinguisher',
|
||||
'Beeping Smoke Alarm',
|
||||
'Greasy Spatula',
|
||||
'Progressive Auto Insurance',
|
||||
'Mace Windu\'s Purple Lightsaber',
|
||||
'An Old Fixer-Upper',
|
||||
'Gamer Chair',
|
||||
'Comfortable Reclining Chair',
|
||||
'Shirt Covered in Dog Hair',
|
||||
'Angry Praying Mantis',
|
||||
'Card Games on Motorcycles',
|
||||
'Trucker Hat',
|
||||
'The DK Rap',
|
||||
'Three Great Balls',
|
||||
'Some Very Sus Behavior',
|
||||
'Glass of Orange Juice',
|
||||
'Turkey Bacon',
|
||||
'Bald Barbie Doll',
|
||||
'Developer Commentary',
|
||||
'Subscription to Nintendo Power Magazine',
|
||||
'DeLorean Time Machine',
|
||||
'Unkillable Cockroach',
|
||||
'Dungeons & Dragons Rulebook',
|
||||
'Boxed Copy of Quest 64',
|
||||
'James Bond\'s Gadget Wristwatch',
|
||||
'Tube of Go-Gurt',
|
||||
'Digital Watch',
|
||||
'Laser Pointer',
|
||||
'The Secret Cow Level',
|
||||
'AOL Free Trial CD-ROM',
|
||||
'E.T. for Atari 2600',
|
||||
'Season 2 of Knight Rider',
|
||||
'Spam E-Mails',
|
||||
'Half-Life 3 Release Date',
|
||||
'Source Code of Jurassic Park',
|
||||
'Moldy Cheese',
|
||||
'Comic Book Collection',
|
||||
'Hardcover Copy of Scott Pilgrim VS the World',
|
||||
'Old Gym Shorts',
|
||||
'Very Cool Sunglasses',
|
||||
'Your High School Yearbook Picture',
|
||||
'Written Invitation to Prom',
|
||||
'The Star Wars Holiday Special',
|
||||
'Oil Change Coupon',
|
||||
'Finger Guns',
|
||||
'Box of Tabletop Games',
|
||||
'Sock Puppets',
|
||||
'The Dog of Wisdom',
|
||||
'Surprised Chipmunk',
|
||||
'Stonks',
|
||||
'A Shrubbery',
|
||||
'Roomba with a Knife',
|
||||
'Wet Cat',
|
||||
'The missing moderator, Frostwares',
|
||||
'1,793 Crossbows',
|
||||
'Holographic First Edition Charizard (Gen 1)',
|
||||
'VR Headset',
|
||||
'Archipelago 1.0 Release Date',
|
||||
'Strand of Galadriel\'s Hair',
|
||||
'Can of Meow-Mix',
|
||||
'Shake-Weight',
|
||||
'DVD Collection of Billy Mays Infomercials',
|
||||
'Old CD Key',
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
from BaseClasses import MultiWorld
|
||||
from worlds.AutoWorld import LogicMixin
|
||||
|
||||
|
||||
class ArchipIDLELogic(LogicMixin):
|
||||
def _archipidle_location_is_accessible(self, player_id, items_required):
|
||||
return sum(self.prog_items[player_id].values()) >= items_required
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
for i in range(16, 31):
|
||||
world.get_location(f"IDLE item number {i}", player).access_rule = lambda \
|
||||
state: state._archipidle_location_is_accessible(player, 4)
|
||||
|
||||
for i in range(31, 51):
|
||||
world.get_location(f"IDLE item number {i}", player).access_rule = lambda \
|
||||
state: state._archipidle_location_is_accessible(player, 10)
|
||||
|
||||
for i in range(51, 101):
|
||||
world.get_location(f"IDLE item number {i}", player).access_rule = lambda \
|
||||
state: state._archipidle_location_is_accessible(player, 20)
|
||||
|
||||
for i in range(101, 201):
|
||||
world.get_location(f"IDLE item number {i}", player).access_rule = lambda \
|
||||
state: state._archipidle_location_is_accessible(player, 40)
|
||||
|
||||
world.completion_condition[player] =\
|
||||
lambda state: state.can_reach(world.get_location("IDLE item number 200", player), "Location", player)
|
||||
@@ -1,128 +0,0 @@
|
||||
from BaseClasses import Item, MultiWorld, Region, Location, Entrance, Tutorial, ItemClassification
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from datetime import datetime
|
||||
from .Items import item_table
|
||||
from .Rules import set_rules
|
||||
|
||||
|
||||
class ArchipIDLEWebWorld(WebWorld):
|
||||
theme = 'partyTime'
|
||||
tutorials = [
|
||||
Tutorial(
|
||||
tutorial_name='Setup Guide',
|
||||
description='A guide to playing Archipidle',
|
||||
language='English',
|
||||
file_name='guide_en.md',
|
||||
link='guide/en',
|
||||
authors=['Farrak Kilhn']
|
||||
),
|
||||
Tutorial(
|
||||
tutorial_name='Guide d installation',
|
||||
description='Un guide pour jouer à Archipidle',
|
||||
language='Français',
|
||||
file_name='guide_fr.md',
|
||||
link='guide/fr',
|
||||
authors=['TheLynk']
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class ArchipIDLEWorld(World):
|
||||
"""
|
||||
An idle game which sends a check every thirty to sixty seconds, up to two hundred checks.
|
||||
"""
|
||||
game = "ArchipIDLE"
|
||||
topology_present = False
|
||||
hidden = (datetime.now().month != 4) # ArchipIDLE is only visible during April
|
||||
web = ArchipIDLEWebWorld()
|
||||
|
||||
item_name_to_id = {}
|
||||
start_id = 9000
|
||||
for item in item_table:
|
||||
item_name_to_id[item] = start_id
|
||||
start_id += 1
|
||||
|
||||
location_name_to_id = {}
|
||||
start_id = 9000
|
||||
for i in range(1, 201):
|
||||
location_name_to_id[f"IDLE item number {i}"] = start_id
|
||||
start_id += 1
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return Item(name, ItemClassification.progression, self.item_name_to_id[name], self.player)
|
||||
|
||||
def create_items(self):
|
||||
item_pool = [
|
||||
ArchipIDLEItem(
|
||||
item_table[0],
|
||||
ItemClassification.progression,
|
||||
self.item_name_to_id[item_table[0]],
|
||||
self.player
|
||||
)
|
||||
]
|
||||
|
||||
for i in range(40):
|
||||
item_pool.append(ArchipIDLEItem(
|
||||
item_table[1],
|
||||
ItemClassification.progression,
|
||||
self.item_name_to_id[item_table[1]],
|
||||
self.player
|
||||
))
|
||||
|
||||
for i in range(40):
|
||||
item_pool.append(ArchipIDLEItem(
|
||||
item_table[2],
|
||||
ItemClassification.filler,
|
||||
self.item_name_to_id[item_table[2]],
|
||||
self.player
|
||||
))
|
||||
|
||||
item_table_copy = list(item_table[3:])
|
||||
self.random.shuffle(item_table_copy)
|
||||
for i in range(119):
|
||||
item_pool.append(ArchipIDLEItem(
|
||||
item_table_copy[i],
|
||||
ItemClassification.progression if i < 9 else ItemClassification.filler,
|
||||
self.item_name_to_id[item_table_copy[i]],
|
||||
self.player
|
||||
))
|
||||
|
||||
self.multiworld.itempool += item_pool
|
||||
|
||||
def create_regions(self):
|
||||
self.multiworld.regions += [
|
||||
create_region(self.multiworld, self.player, 'Menu', None, ['Entrance to IDLE Zone']),
|
||||
create_region(self.multiworld, self.player, 'IDLE Zone', self.location_name_to_id)
|
||||
]
|
||||
|
||||
# link up our region with the entrance we just made
|
||||
self.multiworld.get_entrance('Entrance to IDLE Zone', self.player)\
|
||||
.connect(self.multiworld.get_region('IDLE Zone', self.player))
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.multiworld.random.choice(item_table)
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
region = Region(name, player, world)
|
||||
if locations:
|
||||
for location_name in locations.keys():
|
||||
location = ArchipIDLELocation(player, location_name, locations[location_name], region)
|
||||
region.locations.append(location)
|
||||
|
||||
if exits:
|
||||
for _exit in exits:
|
||||
region.exits.append(Entrance(player, _exit, region))
|
||||
|
||||
return region
|
||||
|
||||
|
||||
class ArchipIDLEItem(Item):
|
||||
game = "ArchipIDLE"
|
||||
|
||||
|
||||
class ArchipIDLELocation(Location):
|
||||
game: str = "ArchipIDLE"
|
||||
@@ -1,13 +0,0 @@
|
||||
# ArchipIDLE
|
||||
|
||||
## What is this game?
|
||||
|
||||
ArchipIDLE was originally the 2022 Archipelago April Fools' Day joke. It is an idle game that sends a location check
|
||||
on regular intervals. Updated annually with more items, gimmicks, and features, the game is visible
|
||||
only during the month of April.
|
||||
|
||||
## Where is the options page?
|
||||
|
||||
The [player options page for this game](../player-options) contains all the options you need to configure
|
||||
and export a config file.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# ArchipIdle Setup Guide
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
1. Generate a `.yaml` file from the [ArchipIDLE Player Options Page](/games/ArchipIDLE/player-options)
|
||||
2. Open the ArchipIDLE Client in your web browser by either:
|
||||
- Navigate to the [ArchipIDLE Client](http://idle.multiworld.link)
|
||||
- Download the client and run it locally from the
|
||||
[ArchipIDLE GitHub Releases Page](https://github.com/ArchipelagoMW/archipidle/releases)
|
||||
3. Enter the server address in the `Server Address` field and press enter
|
||||
4. Enter your slot name when prompted. This should be the same as the `name` you entered on the
|
||||
options page above, or the `name` field in your yaml file.
|
||||
5. Click the "Begin!" button.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Guide de configuration d'ArchipIdle
|
||||
|
||||
## Rejoindre une partie MultiWorld
|
||||
1. Générez un fichier `.yaml` à partir de la [page des paramètres du lecteur ArchipIDLE](/games/ArchipIDLE/player-options)
|
||||
2. Ouvrez le client ArchipIDLE dans votre navigateur Web en :
|
||||
- Accédez au [Client ArchipIDLE](http://idle.multiworld.link)
|
||||
- Téléchargez le client et exécutez-le localement à partir du [Page des versions d'ArchipIDLE GitHub](https://github.com/ArchipelagoMW/archipidle/releases)
|
||||
3. Entrez l'adresse du serveur dans le champ `Server Address` et appuyez sur Entrée
|
||||
4. Entrez votre nom d'emplacement lorsque vous y êtes invité. Il doit être le même que le `name` que vous avez saisi sur le
|
||||
page de configuration ci-dessus, ou le champ `name` dans votre fichier yaml.
|
||||
5. Cliquez sur "Commencer !" bouton.
|
||||
@@ -6,7 +6,6 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from worlds.alttp import ALTTPWorld
|
||||
|
||||
|
||||
class BumpStikLttPText(typing.NamedTuple):
|
||||
@@ -117,13 +116,17 @@ item_table = {
|
||||
item: offset + x for x, item in enumerate(LttPCreditsText.keys())
|
||||
}
|
||||
|
||||
ALTTPWorld.pedestal_credit_texts.update({item_table[name]: f"and the {texts.pedestal}"
|
||||
for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.sickkid_credit_texts.update(
|
||||
{item_table[name]: texts.sickkid for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.magicshop_credit_texts.update(
|
||||
{item_table[name]: texts.magicshop for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.zora_credit_texts.update(
|
||||
{item_table[name]: texts.zora for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.fluteboy_credit_texts.update(
|
||||
{item_table[name]: texts.fluteboy for name, texts in LttPCreditsText.items()})
|
||||
try:
|
||||
from worlds.alttp import ALTTPWorld
|
||||
ALTTPWorld.pedestal_credit_texts.update({item_table[name]: f"and the {texts.pedestal}"
|
||||
for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.sickkid_credit_texts.update(
|
||||
{item_table[name]: texts.sickkid for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.magicshop_credit_texts.update(
|
||||
{item_table[name]: texts.magicshop for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.zora_credit_texts.update(
|
||||
{item_table[name]: texts.zora for name, texts in LttPCreditsText.items()})
|
||||
ALTTPWorld.fluteboy_credit_texts.update(
|
||||
{item_table[name]: texts.fluteboy for name, texts in LttPCreditsText.items()})
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
6
worlds/bumpstik/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "Bumper Stickers",
|
||||
"authors": ["KewlioMZX"],
|
||||
"world_version": "1.0.0",
|
||||
"minimum_ap_version": "0.6.4"
|
||||
}
|
||||
2
worlds/cccharles/BaseID.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# CCCharles base ID
|
||||
base_id = 66600000
|
||||
167
worlds/cccharles/Items.py
Normal file
@@ -0,0 +1,167 @@
|
||||
from BaseClasses import Item
|
||||
from .BaseID import base_id
|
||||
|
||||
|
||||
class CCCharlesItem(Item):
|
||||
game = "Choo-Choo Charles"
|
||||
|
||||
|
||||
optional_items = {
|
||||
"Scraps": base_id + 1,
|
||||
"30 Scraps Reward": base_id + 2,
|
||||
"25 Scraps Reward": base_id + 3,
|
||||
"35 Scraps Reward": base_id + 4,
|
||||
"40 Scraps Reward": base_id + 5,
|
||||
"South Mine Key": base_id + 6,
|
||||
"North Mine Key": base_id + 7,
|
||||
"Mountain Ruin Key": base_id + 8,
|
||||
"Barn Key": base_id + 9,
|
||||
"Candice's Key": base_id + 10,
|
||||
"Dead Fish": base_id + 11,
|
||||
"Lockpicks": base_id + 12,
|
||||
"Ancient Tablet": base_id + 13,
|
||||
"Blue Box": base_id + 14,
|
||||
"Page Drawing": base_id + 15,
|
||||
"Journal": base_id + 16,
|
||||
"Timed Dynamite": base_id + 17,
|
||||
"Box of Rockets": base_id + 18,
|
||||
"Breaker": base_id + 19,
|
||||
"Broken Bob": base_id + 20,
|
||||
"Employment Contracts": base_id + 21,
|
||||
"Mob Camp Key": base_id + 22,
|
||||
"Jar of Pickles": base_id + 23
|
||||
}
|
||||
|
||||
useless_items = {
|
||||
"Orange Paint Can": base_id + 24,
|
||||
"Green Paint Can": base_id + 25,
|
||||
"White Paint Can": base_id + 26,
|
||||
"Pink Paint Can": base_id + 27,
|
||||
"Grey Paint Can": base_id + 28,
|
||||
"Blue Paint Can": base_id + 29,
|
||||
"Black Paint Can": base_id + 30,
|
||||
"Lime Paint Can": base_id + 31,
|
||||
"Teal Paint Can": base_id + 32,
|
||||
"Red Paint Can": base_id + 33,
|
||||
"Purple Paint Can": base_id + 34,
|
||||
"The Boomer": base_id + 35,
|
||||
"Bob": base_id + 36
|
||||
}
|
||||
|
||||
progression_items = {
|
||||
"Green Egg": base_id + 37,
|
||||
"Blue Egg": base_id + 38,
|
||||
"Red Egg": base_id + 39,
|
||||
"Remote Explosive": base_id + 40,
|
||||
"Remote Explosive x8": base_id + 41, # Originally, Paul gives 8 explosives at once
|
||||
"Temple Key": base_id + 42,
|
||||
"Bug Spray": base_id + 43 # Should only be considered progressive in Nightmare Mode
|
||||
}
|
||||
|
||||
item_groups = {
|
||||
"Weapons": {
|
||||
"Bug Spray",
|
||||
"The Boomer",
|
||||
"Bob"
|
||||
},
|
||||
"Paint Can": {
|
||||
"Orange Paint Can",
|
||||
"Green Paint Can",
|
||||
"White Paint Can",
|
||||
"Pink Paint Can",
|
||||
"Grey Paint Can",
|
||||
"Blue Paint Can",
|
||||
"Black Paint Can",
|
||||
"Lime Paint Can",
|
||||
"Teal Paint Can",
|
||||
"Red Paint Can",
|
||||
"Purple Paint Can"
|
||||
},
|
||||
"Train Upgrade": {
|
||||
"Scraps",
|
||||
"30 Scraps Reward",
|
||||
"25 Scraps Reward",
|
||||
"40 Scraps Reward"
|
||||
},
|
||||
"Dungeon Keys": {
|
||||
"South Mine Key",
|
||||
"North Mine Key",
|
||||
"Mountain Ruin Key"
|
||||
},
|
||||
"Building Keys": {
|
||||
"Barn Key",
|
||||
"Candice's Key",
|
||||
"Mob Camp Key",
|
||||
"Temple Key"
|
||||
},
|
||||
"Mission Items": {
|
||||
"Dead Fish",
|
||||
"Lockpicks",
|
||||
"Ancient Tablet",
|
||||
"Blue Box",
|
||||
"Page Drawing",
|
||||
"Journal",
|
||||
"Timed Dynamite",
|
||||
"Box of Rockets",
|
||||
"Breaker",
|
||||
"Broken Bob",
|
||||
"Employment Contracts",
|
||||
"Jar of Pickles",
|
||||
"Remote Explosive",
|
||||
"Remote Explosive x8"
|
||||
},
|
||||
"Eggs": {
|
||||
"Green Egg",
|
||||
"Blue Egg",
|
||||
"Red Egg"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# All items excepted the duplications (no item amount)
|
||||
unique_item_dict = {**optional_items, **useless_items, **progression_items}
|
||||
|
||||
# All 691 items to add to the item pool
|
||||
full_item_list = []
|
||||
full_item_list += ["Scraps"] * 637 # 636 + 1 as Scrap Reward (from Ronny)
|
||||
full_item_list += ["30 Scraps Reward"] * 3
|
||||
full_item_list += ["25 Scraps Reward"] * 1
|
||||
full_item_list += ["35 Scraps Reward"] * 2
|
||||
full_item_list += ["40 Scraps Reward"] * 1
|
||||
full_item_list += ["South Mine Key"] * 1
|
||||
full_item_list += ["North Mine Key"] * 1
|
||||
full_item_list += ["Mountain Ruin Key"] * 1
|
||||
full_item_list += ["Barn Key"] * 1
|
||||
full_item_list += ["Candice's Key"] * 1
|
||||
full_item_list += ["Dead Fish"] * 1
|
||||
full_item_list += ["Lockpicks"] * 1
|
||||
full_item_list += ["Ancient Tablet"] * 1
|
||||
full_item_list += ["Blue Box"] * 1
|
||||
full_item_list += ["Page Drawing"] * 8
|
||||
full_item_list += ["Journal"] * 1
|
||||
full_item_list += ["Timed Dynamite"] * 1
|
||||
full_item_list += ["Box of Rockets"] * 1
|
||||
full_item_list += ["Breaker"] * 4
|
||||
full_item_list += ["Broken Bob"] * 1
|
||||
full_item_list += ["Employment Contracts"] * 1
|
||||
full_item_list += ["Mob Camp Key"] * 1
|
||||
full_item_list += ["Jar of Pickles"] * 1
|
||||
full_item_list += ["Orange Paint Can"] * 1
|
||||
full_item_list += ["Green Paint Can"] * 1
|
||||
full_item_list += ["White Paint Can"] * 1
|
||||
full_item_list += ["Pink Paint Can"] * 1
|
||||
full_item_list += ["Grey Paint Can"] * 1
|
||||
full_item_list += ["Blue Paint Can"] * 1
|
||||
full_item_list += ["Black Paint Can"] * 1
|
||||
full_item_list += ["Lime Paint Can"] * 1
|
||||
full_item_list += ["Teal Paint Can"] * 1
|
||||
full_item_list += ["Red Paint Can"] * 1
|
||||
full_item_list += ["Purple Paint Can"] * 1
|
||||
full_item_list += ["The Boomer"] * 1
|
||||
full_item_list += ["Bob"] * 1
|
||||
full_item_list += ["Green Egg"] * 1
|
||||
full_item_list += ["Blue Egg"] * 1
|
||||
full_item_list += ["Red Egg"] * 1
|
||||
full_item_list += ["Remote Explosive x8"] * 1
|
||||
full_item_list += ["Temple Key"] * 1
|
||||
full_item_list += ["Bug Spray"] * 1
|
||||
914
worlds/cccharles/Locations.py
Normal file
@@ -0,0 +1,914 @@
|
||||
from BaseClasses import Location
|
||||
from .BaseID import base_id
|
||||
|
||||
|
||||
class CCCharlesLocation(Location):
|
||||
game = "Choo-Choo Charles"
|
||||
|
||||
# "First Station":
|
||||
# /!\ NOT CONSIDERED YET: train_keypickup Train_KeyPickup Photorealistic_Island (X=-8816.341 Y=23392.416 Z=10219.855)
|
||||
|
||||
loc_start_camp = {
|
||||
"Start Camp Scraps 1": base_id + 1000, # ItemPickup139_5 Camp (X=24006.348 Y=53777.297 Z=10860.107)
|
||||
"Start Camp Scraps 2": base_id + 1001 # ItemPickup140_8 Camp (X=23951.754 Y=54897.230 Z=10895.235)
|
||||
}
|
||||
|
||||
loc_tony_tiddle_mission = {
|
||||
"Barn Tony Tiddle Mission Start": base_id + 1002 # (dialog 5) -> barn_key (1)
|
||||
}
|
||||
|
||||
loc_barn = {
|
||||
"Barn Scraps 1": base_id + 1003, # ItemPickup12 Photorealistic_Island (X=70582.805 Y=52591.066 Z=11976.719)
|
||||
"Barn Scraps 2": base_id + 1004, # ItemPickup4_2 Photorealistic_Island (X=70536.641 Y=51890.633 Z=11986.488)
|
||||
"Barn Scraps 3": base_id + 1005, # ItemPickup6 Photorealistic_Island (X=70750.336 Y=52275.828 Z=11994.434)
|
||||
"Barn Scraps 4": base_id + 1006, # ItemPickup11 Photorealistic_Island (X=70937.719 Y=52989.066 Z=12003.523)
|
||||
"Barn Scraps 5": base_id + 1007, # ItemPickup5 Photorealistic_Island (X=71303.508 Y=52232.188 Z=12003.997)
|
||||
"Barn Scraps 6": base_id + 1008, # ItemPickup7 Photorealistic_Island (X=71678.672 Y=52825.531 Z=11977.212)
|
||||
"Barn Scraps 7": base_id + 1009, # ItemPickup8 Photorealistic_Island (X=71506.961 Y=52357.293 Z=12362.159)
|
||||
"Barn Scraps 8": base_id + 1010, # ItemPickup9 Photorealistic_Island (X=71029.875 Y=52384.613 Z=12362.159)
|
||||
"Barn Scraps 9": base_id + 1011 # ItemPickup10 Photorealistic_Island (X=71129.594 Y=52600.262 Z=12364.142)
|
||||
}
|
||||
|
||||
loc_candice_mission = {
|
||||
"Tutorial House Candice Mission Start": base_id + 1012 # (dialog 3) -> candice_key (2)
|
||||
}
|
||||
|
||||
loc_tutorial_house = {
|
||||
"Tutorial House Scraps 1": base_id + 1013, # ItemPickup17_2 Photorealistic_Island (X=74745.852 Y=73865.555 Z=11426.619)
|
||||
"Tutorial House Scraps 2": base_id + 1014, # ItemPickup18_2 Photorealistic_Island (X=74864.102 Y=73900.094 Z=11426.619)
|
||||
"Tutorial House Scraps 3": base_id + 1015, # ItemPickup14_2 Photorealistic_Island (X=74877.625 Y=73738.594 Z=11422.057) \!/ Existing match 4
|
||||
"Tutorial House Scraps 4": base_id + 1016, # ItemPickup15_8 Photorealistic_Island (X=75068.992 Y=73971.133 Z=11426.619)
|
||||
"Tutorial House Scraps 5": base_id + 1017, # ItemPickup21_1 Photorealistic_Island (X=74923.500 Y=73571.648 Z=11426.619)
|
||||
"Tutorial House Scraps 6": base_id + 1018, # ItemPickup19 Photorealistic_Island (X=75194.906 Y=73495.719 Z=11426.619)
|
||||
"Tutorial House Scraps 7": base_id + 1019, # ItemPickup13_3 Photorealistic_Island (X=75320.102 Y=73446.352 Z=11487.376)
|
||||
"Tutorial House Scraps 8": base_id + 1020, # ItemPickup20 Photorealistic_Island (X=75298.680 Y=73580.531 Z=11426.619)
|
||||
"Tutorial House Scraps 9": base_id + 1021 # ItemPickup16_3 Photorealistic_Island (X=75310.008 Y=73770.742 Z=11489.709)
|
||||
}
|
||||
|
||||
loc_swamp_edges = {
|
||||
"Swamp Edges Scraps 1": base_id + 1022, # ItemPickup465_67 Swamp_EnvironmentDetails (X=81964.398 Y=72167.305 Z=10116.385)
|
||||
"Swamp Edges Scraps 2": base_id + 1023, # ItemPickup460_52 Swamp_EnvironmentDetails (X=89674.047 Y=71610.008 Z=9482.095)
|
||||
"Swamp Edges Scraps 3": base_id + 1024, # ItemPickup459_49 Swamp_EnvironmentDetails (X=91637.156 Y=73345.672 Z=9492.019)
|
||||
"Swamp Edges Scraps 4": base_id + 1025, # ItemPickup458_46 Swamp_EnvironmentDetails (X=94601.117 Y=75064.117 Z=9567.464)
|
||||
"Swamp Edges Scraps 5": base_id + 1026, # ItemPickup457_43 Swamp_EnvironmentDetails (X=95536.641 Y=72622.969 Z=9512.531)
|
||||
"Swamp Edges Scraps 6": base_id + 1027, # ItemPickup456_40 Swamp_EnvironmentDetails (X=96419.922 Y=65508.676 Z=9838.949)
|
||||
"Swamp Edges Scraps 7": base_id + 1028, # ItemPickup455_37 Swamp_EnvironmentDetails (X=98158.680 Y=63191.629 Z=10477.084)
|
||||
"Swamp Edges Scraps 8": base_id + 1029, # ItemPickup55_29 Swamp_EnvironmentDetails (X=93421.820 Y=59200.461 Z=9545.312)
|
||||
"Swamp Edges Scraps 9": base_id + 1030, # ItemPickup453_31 Swamp_EnvironmentDetails(X=92951.648 Y=56453.527 Z=9560.638)
|
||||
"Swamp Edges Scraps 10": base_id + 1031, # ItemPickup454_34 Swamp_EnvironmentDetails (X=96943.297 Y=58754.043 Z=10728.124)
|
||||
"Swamp Edges Scraps 11": base_id + 1032, # ItemPickup452_28 Swamp_EnvironmentDetails (X=95000.617 Y=53070.859 Z=10258.078)
|
||||
"Swamp Edges Scraps 12": base_id + 1033, # ItemPickup451_25 Swamp_EnvironmentDetails (X=91390.703 Y=53628.707 Z=9498.378)
|
||||
"Swamp Edges Scraps 13": base_id + 1034, # ItemPickup53_23 Swamp_EnvironmentDetails (X=87628.742 Y=51614.957 Z=9487.013)
|
||||
"Swamp Edges Scraps 14": base_id + 1035, # ItemPickup448_16 Swamp_EnvironmentDetails (X=89785.992 Y=48603.844 Z=9573.859)
|
||||
"Swamp Edges Scraps 15": base_id + 1036, # ItemPickup447_11 Swamp_EnvironmentDetails (X=89925.383 Y=46288.707 Z=9499.904)
|
||||
"Swamp Edges Scraps 16": base_id + 1037, # ItemPickup446_8 Swamp_EnvironmentDetails (X=90848.938 Y=43133.535 Z=9729.535)
|
||||
"Swamp Edges Scraps 17": base_id + 1038, # ItemPickup445_5 Swamp_EnvironmentDetails (X=87382.383 Y=42475.191 Z=9509.929)
|
||||
"Swamp Edges Scraps 18": base_id + 1039, # ItemPickup444_2 Swamp_EnvironmentDetails (X=87481.820 Y=39316.820 Z=9757.511)
|
||||
"Swamp Edges Scraps 19": base_id + 1040, # ItemPickup54_26 Swamp_EnvironmentDetails (X=86039.180 Y=37135.004 Z=9826.263)
|
||||
"Swamp Edges Scraps 20": base_id + 1041, # ItemPickup475_97 Swamp_EnvironmentDetails (X=81798.609 Y=36766.922 Z=9479.318)
|
||||
"Swamp Edges Scraps 21": base_id + 1042, # ItemPickup474_94 Swamp_EnvironmentDetails (X=79254.055 Y=40120.293 Z=9879.539)
|
||||
"Swamp Edges Scraps 22": base_id + 1043, # ItemPickup473_91 Swamp_EnvironmentDetails (X=82251.773 Y=42454.027 Z=9482.057)
|
||||
"Swamp Edges Scraps 23": base_id + 1044, # ItemPickup472_88 Swamp_EnvironmentDetails (X=84903.977 Y=48323.543 Z=9503.382)
|
||||
"Swamp Edges Scraps 24": base_id + 1045, # ItemPickup471_85 Swamp_EnvironmentDetails (X=84238.609 Y=51239.547 Z=9529.745)
|
||||
"Swamp Edges Scraps 25": base_id + 1046, # ItemPickup470_82 Swamp_EnvironmentDetails (X=84439.063 Y=53501.563 Z=9491.291)
|
||||
"Swamp Edges Scraps 26": base_id + 1047, # ItemPickup52_20 Swamp_EnvironmentDetails (X=83025.086 Y=53275.348 Z=9694.177)
|
||||
"Swamp Edges Scraps 27": base_id + 1048, # ItemPickup469_79 Swamp_EnvironmentDetails (X=79827.055 Y=54791.504 Z=10121.452)
|
||||
"Swamp Edges Scraps 28": base_id + 1049, # ItemPickup468_76 Swamp_EnvironmentDetails (X=82266.461 Y=58126.316 Z=9660.493)
|
||||
"Swamp Edges Scraps 29": base_id + 1050, # ItemPickup467_73 Swamp_EnvironmentDetails (X=75911.297 Y=65155.836 Z=10660.832)
|
||||
"Swamp Edges Scraps 30": base_id + 1051, # ItemPickup466_70 Swamp_EnvironmentDetails (X=81171.641 Y=66836.125 Z=9673.756)
|
||||
"Swamp Edges Scraps 31": base_id + 1052, # ItemPickup449_19 Swamp_EnvironmentDetails (X=95254.992 Y=40910.563 Z=10503.727)
|
||||
"Swamp Edges Scraps 32": base_id + 1053 # ItemPickup450_22 Swamp_EnvironmentDetails (X=93992.992 Y=50773.484 Z=10238.064)
|
||||
}
|
||||
|
||||
loc_swamp_mission = {
|
||||
"Swamp Shack Scraps 1": base_id + 1054, # ItemPickup51_17 Swamp_EnvironmentDetails (X=87685.797 Y=69754.008 Z=9629.617)
|
||||
"Swamp Shack Scraps 2": base_id + 1055, # ItemPickup461_55 Swamp_EnvironmentDetails (X=87308.883 Y=69096.789 Z=9624.543)
|
||||
"Swamp Islet Scraps 1": base_id + 1056, # ItemPickup462_58 Swamp_EnvironmentDetails (X=88101.219 Y=64553.148 Z=9557.692)
|
||||
"Swamp Islet Scraps 2": base_id + 1057, # ItemPickup463_61 Swamp_EnvironmentDetails (X=87100.922 Y=63590.965 Z=9582.900)
|
||||
"Swamp Islet Scraps 3": base_id + 1058, # ItemPickup464_64 Swamp_EnvironmentDetails (X=86399.656 Y=64290.805 Z=9493.576)
|
||||
"Swamp Islet Dead Fish": base_id + 1059, # Swamp_FishPickup Swamp_EnvironmentDetails (X=87288.945 Y=64278.273 Z=9550.320)
|
||||
"Swamp Lizbeth Murkwater Mission End": base_id + 1060 # (dialog 2) -> 30_scraps_reward
|
||||
}
|
||||
|
||||
loc_junkyard_area = {
|
||||
"Junkyard Area Scraps 1": base_id + 1061, # ItemPickup185_29 Junkyard_Details1 (X=94184.391 Y=89760.258 Z=9331.188)
|
||||
"Junkyard Area Scraps 2": base_id + 1062, # ItemPickup177_5 Junkyard_Details1 (X=91919.469 Y=89681.602 Z=9407.639)
|
||||
"Junkyard Area Scraps 3": base_id + 1063, # ItemPickup46_5 Junkyard_Details (X=91696.078 Y=90453.563 Z=9480.997)
|
||||
"Junkyard Area Scraps 4": base_id + 1064, # ItemPickup178_8 Junkyard_Details1 (X=92453.719 Y=91142.531 Z=9398.951)
|
||||
"Junkyard Area Scraps 5": base_id + 1065, # ItemPickup182_20 Junkyard_Details1 (X=88645.453 Y=90374.930 Z=9507.291)
|
||||
"Junkyard Area Scraps 6": base_id + 1066, # ItemPickup48_11 Junkyard_Details (X=88461.953 Y=92077.531 Z=9712.173)
|
||||
"Junkyard Area Scraps 7": base_id + 1067, # ItemPickup49_14 Junkyard_Details (X=91521.555 Y=93773.641 Z=9421.457)
|
||||
"Junkyard Area Scraps 8": base_id + 1068, # ItemPickup50_17 Junkyard_Details (X=94741.484 Y=92565.938 Z=9221.093)
|
||||
"Junkyard Area Scraps 9": base_id + 1069, # ItemPickup186_32 Junkyard_Details1 (X=95256.008 Y=91356.789 Z=9251.082)
|
||||
"Junkyard Area Scraps 10": base_id + 1070, # ItemPickup45_2 Junkyard_Details (X=94289.664 Y=89951.477 Z=9367.076)
|
||||
"Junkyard Area Daryl Mission Start": base_id + 1071, # (dialog 4) -> Lockpicks (4)
|
||||
"Junkyard Area Chest Ancient Tablet": base_id + 1072, # Junkyard_TabletPickup Junkyard_Details (X=90715.367 Y=92168.563 Z=9402.729)
|
||||
"Junkyard Area Daryl Mission End": base_id + 1073 # (dialog 3) -> 25_scraps_reward
|
||||
}
|
||||
|
||||
loc_south_house = {
|
||||
"South House Scraps 1": base_id + 1074, # ItemPickup361_26 Secret12_ExteriorDetails (X=85865.969 Y=103869.656 Z=9453.063)
|
||||
"South House Scraps 2": base_id + 1075, # ItemPickup360_23 Secret12_ExteriorDetails (X=84403.742 Y=107229.039 Z=9067.245)
|
||||
"South House Scraps 3": base_id + 1076, # ItemPickup359_20 Secret12_ExteriorDetails (X=83389.789 Y=108817.992 Z=8752.255)
|
||||
"South House Scraps 4": base_id + 1077, # ItemPickup353_2 Secret12_ExteriorDetails (X=82413.547 Y=109697.477 Z=8637.677)
|
||||
"South House Scraps 5": base_id + 1078, # ItemPickup354_5 Secret12_ExteriorDetails (X=83000.359 Y=110323.664 Z=8560.229)
|
||||
"South House Scraps 6": base_id + 1079, # ItemPickup358_17 Secret12_ExteriorDetails (X=82072.625 Y=110482.664 Z=8682.441)
|
||||
"South House Scraps 7": base_id + 1080, # ItemPickup24_30 Secret12_Details (X=81970.766 Y=111082.117 Z=8647.703)
|
||||
"South House Scraps 8": base_id + 1081, # ItemPickup356_11 Secret12_ExteriorDetails (X=80915.375 Y=108689.758 Z=8377.754)
|
||||
"South House Scraps 9": base_id + 1082, # ItemPickup355_8 Secret12_ExteriorDetails (X=81762.180 Y=111371.023 Z=7876.312)
|
||||
"South House Scraps 10": base_id + 1083, # ItemPickup357_14 Secret12_ExteriorDetails (X=80663.336 Y=113306.695 Z=7226.475)
|
||||
"South House Scraps 11": base_id + 1084, # ItemPickup23_21 Secret12_Details (X=80520.367 Y=113747.039 Z=7252.808)
|
||||
"South House Scraps 12": base_id + 1085, # ItemPickup22_18 Secret12_Details (X=80830.273 Y=113871.383 Z=7201.687)
|
||||
"South House Chest Scraps 1": base_id + 1086, # ItemPickup21 Secret12_Details (X=82079.922 Y=110808.602 Z=8739.324)
|
||||
"South House Chest Scraps 2": base_id + 1087, # ItemPickup18 Secret12_Details (X=82102.664 Y=110813.664 Z=8726.308)
|
||||
"South House Chest Scraps 3": base_id + 1088, # ItemPickup17 Secret12_Details (X=82091.547 Y=110810.906 Z=8721.354) \!/ Existing match 1
|
||||
"South House Chest Scraps 4": base_id + 1089, # ItemPickup16 Secret12_Details (X=82102.664 Y=110813.664 Z=8708.337) KO
|
||||
"South House Chest Scraps 5": base_id + 1090, # ItemPickup14 Secret12_Details (X=82091.516 Y=110810.898 Z=8701.793) \!/ Existing match 3
|
||||
"South House Chest Scraps 6": base_id + 1091 # ItemPickup13_7 Secret12_Details (X=82102.664 Y=110813.625 Z=8688.776)
|
||||
}
|
||||
|
||||
loc_junkyard_shed = {
|
||||
"Junkyard Shed Helen Mission Start": base_id + 1092, # (dialog 8) -> south_mine_key (6)
|
||||
"Junkyard Shed Scraps 1": base_id + 1093, # ItemPickup424_23 Settlement_A_House_1 (X=98303.992 Y=84476.016 Z=9376.540)
|
||||
"Junkyard Shed Scraps 2": base_id + 1094, # ItemPickup419_8 Settlement_A_House_1 (X=98174.680 Y=84067.383 Z=9249.197)
|
||||
"Junkyard Shed Scraps 3": base_id + 1095, # ItemPickup418_5 Settlement_A_House_1 (X=97948.977 Y=83354.656 Z=9339.430)
|
||||
"Junkyard Shed Scraps 4": base_id + 1096, # ItemPickup417_2 Settlement_A_House_1 (X=98208.391 Y=83088.047 Z=9273.632)
|
||||
"Junkyard Shed Scraps 5": base_id + 1097, # ItemPickup420_11 Settlement_A_House_1 (X=97757.773 Y=82995.656 Z=9298.597)
|
||||
"Junkyard Shed Scraps 6": base_id + 1098, # ItemPickup422_17 Settlement_A_House_1 (X=98776.102 Y=80881.133 Z=9286.782)
|
||||
"Junkyard Shed Scraps 7": base_id + 1099, # ItemPickup421_14 Settlement_A_House_1 (X=99198.508 Y=82057.820 Z=9248.227)
|
||||
"Junkyard Shed Scraps 8": base_id + 1100 # ItemPickup423_20 Settlement_A_House_1 (X=99208.617 Y=84383.125 Z=9257.880)
|
||||
}
|
||||
|
||||
loc_military_base = {
|
||||
"Military Base Sgt Flint Mission End": base_id + 1101, # (dialog 2) -> bug_spray
|
||||
"Military Base Scraps 1": base_id + 1102, # ItemPickup134_17 Bugspray_Main (X=105743.531 Y=83017.492 Z=9423.290)
|
||||
"Military Base Scraps 2": base_id + 1103, # ItemPickup129_2 Bugspray_Main (X=108495.805 Y=81616.992 Z=9139.340)
|
||||
"Military Base Scraps 3": base_id + 1104, # ItemPickup135_20 Bugspray_Main (X=108709.219 Y=85981.016 Z=9650.472)
|
||||
"Military Base Scraps 4": base_id + 1105, # ItemPickup130_5 Bugspray_Main (X=112004.195 Y=83811.313 Z=8887.996)
|
||||
"Military Base Scraps 5": base_id + 1106, # ItemPickup131_8 Bugspray_Main (X=110904.867 Y=82024.781 Z=9581.007)
|
||||
"Military Base Scraps 6": base_id + 1107, # ItemPickup132_11 Bugspray_Main (X=112458.563 Y=81967.945 Z=9850.968)
|
||||
"Military Base Scraps 7": base_id + 1108, # ItemPickup22_9 Bugspray_Details (X=112541.695 Y=81345.875 Z=9896.940)
|
||||
"Military Base Scraps 8": base_id + 1109, # ItemPickup133_14 Bugspray_Main (X=111943.391 Y=79970.016 Z=10025.820)
|
||||
"Military Base Scraps 9": base_id + 1110, # ItemPickup24_8 Bugspray_Details (X=112074.063 Y=83533.398 Z=9008.831)
|
||||
"Military Base Scraps 10": base_id + 1111, # ItemPickup23_2 Bugspray_Details (X=110738.523 Y=85389.852 Z=9082.626)
|
||||
"Military Base Scraps 11": base_id + 1112, # ItemPickup136_23 Bugspray_Main (X=112962.594 Y=85872.922 Z=8638.805)
|
||||
"Military Base Scraps 12": base_id + 1113, # ItemPickup137_26 Bugspray_Main (X=116230.563 Y=84357.602 Z=8580.226)
|
||||
"Military Base Orange Paint Can": base_id + 1114 # PaintCan_5 Bugspray_Details (X=111916.102 Y=83066.195 Z=9094.554)
|
||||
}
|
||||
|
||||
loc_south_mine_outside = {
|
||||
"South Mine Outside Scraps 1": base_id + 1115, # ItemPickup20_1 Mine_1_OutsideMain (X=114794.375 Y=57211.855 Z=8523.348)
|
||||
"South Mine Outside Scraps 2": base_id + 1116, # ItemPickup15_2 Mine_1_OutsideDetails (X=112523.438 Y=57693.836 Z=8639.382)
|
||||
"South Mine Outside Scraps 3": base_id + 1117, # ItemPickup22_5 Mine_1_OutsideMain (X=112348.586 Y=59174.289 Z=8945.143)
|
||||
"South Mine Outside Scraps 4": base_id + 1118, # ItemPickup13_2 Mine_1_OutsideDetails (X=110989.156 Y=57840.090 Z=8700.936)
|
||||
"South Mine Outside Scraps 5": base_id + 1119, # ItemPickup16_1 Mine_1_OutsideDetails (X=110487.281 Y=54528.535 Z=8589.910)
|
||||
"South Mine Outside Scraps 6": base_id + 1120, # ItemPickup18_1 Mine_1_OutsideMain (X=113727.297 Y=54791.703 Z=8424.460)
|
||||
"South Mine Outside Scraps 7": base_id + 1121 # ItemPickup17_3 Mine_1_OutsideMain (X=113965.211 Y=53289.539 Z=8402.346)
|
||||
}
|
||||
|
||||
loc_south_mine_inside = {
|
||||
"South Mine Inside Scraps 1": base_id + 1122, # ItemPickup23_4 Mine_1_Interior_1 (X=108659.945 Y=58712.691 Z=8763.015)
|
||||
"South Mine Inside Scraps 2": base_id + 1123, # ItemPickup24_0 Mine_1_Interior_1 (X=104954.602 Y=61540.488 Z=7876.374)
|
||||
"South Mine Inside Scraps 3": base_id + 1124, # ItemPickup26_0 Mine_1_Interior_1 (X=104436.758 Y=64091.211 Z=7872.767)
|
||||
"South Mine Inside Scraps 4": base_id + 1125, # ItemPickup290_20 Mine_1_Interior_2 (X=101356.625 Y=66110.906 Z=8034.738)
|
||||
"South Mine Inside Scraps 5": base_id + 1126, # ItemPickup287_8 Mine_1_Interior_2 (X=96888.820 Y=64458.559 Z=7917.468)
|
||||
"South Mine Inside Scraps 6": base_id + 1127, # ItemPickup289_14 Mine_1_Interior_2 (X=95863.180 Y=63252.902 Z=7847.054)
|
||||
"South Mine Inside Scraps 7": base_id + 1128, # ItemPickup288_11 Mine_1_Interior_2 (X=97337.219 Y=62921.438 Z=7884.393)
|
||||
"South Mine Inside Scraps 8": base_id + 1129, # ItemPickup285_2 Mine_1_Interior_2 (X=96689.203 Y=61880.895 Z=7806.810)
|
||||
"South Mine Inside Scraps 9": base_id + 1130, # ItemPickup286_5 Mine_1_Interior_2 (X=98403.227 Y=62812.531 Z=7880.947)
|
||||
"South Mine Inside Green Egg": base_id + 1131, # ItemPickup14_2 Mine_1_Interior_2 (X=96753.219 Y=62909.504 Z=8030.018) \!/ Existing match 4
|
||||
"South Mine Inside Green Paint Can": base_id + 1132 # PaintCan_2 Mine_1_Interior_1 (X=108293.281 Y=64192.094 Z=7872.770) \!/ Existing match 5
|
||||
}
|
||||
|
||||
loc_middle_station = {
|
||||
"Middle Station White Paint Can": base_id + 1133, # PaintCan_2 Station_Details1 (X=34554.141 Y=-7395.408 Z=11897.556) \!/ Existing match 5
|
||||
"Middle Station Scraps 1": base_id + 1134, # ItemPickup431_20 Station_BuildingDetails (X=37710.504 Y=-6462.562 Z=11356.691)
|
||||
"Middle Station Scraps 2": base_id + 1135, # ItemPickup425_2 Station_BuildingDetails (X=37034.340 Y=-4923.256 Z=11348.328)
|
||||
"Middle Station Scraps 3": base_id + 1136, # ItemPickup427_8 Station_BuildingDetails (X=36689.164 Y=-3727.466 Z=11353.597)
|
||||
"Middle Station Scraps 4": base_id + 1137, # ItemPickup426_5 Station_BuildingDetails (X=37207.629 Y=-3393.977 Z=11379.110)
|
||||
"Middle Station Scraps 5": base_id + 1138, # ItemPickup429_14 Station_BuildingDetails (X=37988.219 Y=-3365.906 Z=11350.225)
|
||||
"Middle Station Scraps 6": base_id + 1139, # ItemPickup428_11 Station_BuildingDetails (X=36956.242 Y=-2746.948 Z=11353.506)
|
||||
"Middle Station Scraps 7": base_id + 1140, # ItemPickup430_17 Station_BuildingDetails (X=36638.492 Y=-6410.017 Z=11353.546)
|
||||
"Middle Station Scraps 8": base_id + 1141, # ItemPickup433_26 Station_BuildingDetails (X=35931.168 Y=-7558.021 Z=11899.232)
|
||||
"Middle Station Scraps 9": base_id + 1142, # ItemPickup434 Station_BuildingDetails (X=35636.855 Y=-7628.500 Z=11903.627)
|
||||
"Middle Station Scraps 10": base_id + 1143, # ItemPickup435 Station_BuildingDetails (X=34894.152 Y=-7537.087 Z=11903.627)
|
||||
"Middle Station Scraps 11": base_id + 1144, # ItemPickup13_4 Station_BuildingDetails (X=33505.609 Y=-7742.843 Z=11898.971)
|
||||
"Middle Station Scraps 12": base_id + 1145, # ItemPickup440_5 Station_Details1 (X=37394.004 Y=-8395.084 Z=11389.296)
|
||||
"Middle Station Scraps 13": base_id + 1146, # ItemPickup432_23 Station_BuildingDetails (X=36040.695 Y=-8068.016 Z=11456.609)
|
||||
"Middle Station Scraps 14": base_id + 1147, # ItemPickup16_4 Station_BuildingDetails (X=35360.320 Y=-8441.443 Z=11457.823)
|
||||
"Middle Station Scraps 15": base_id + 1148, # ItemPickup439_2 Station_Details1 (X=36311.324 Y=-9563.938 Z=11468.039)
|
||||
"Middle Station Scraps 16": base_id + 1149, # ItemPickup442_11 Station_Details1 (X=33335.656 Y=-13872.785 Z=11189.906)
|
||||
"Middle Station Scraps 17": base_id + 1150, # ItemPickup441_8 Station_Details1 (X=33129.984 Y=-14073.978 Z=11189.906)
|
||||
"Middle Station Scraps 18": base_id + 1151, # ItemPickup436_31 Station_BuildingDetails (X=33587.488 Y=-7828.651 Z=11529.446)
|
||||
"Middle Station Scraps 19": base_id + 1152, # ItemPickup14_3 Station_BuildingDetails (X=34007.254 Y=-7749.381 Z=11533.760)
|
||||
"Middle Station Scraps 20": base_id + 1153, # ItemPickup443_14 Station_Details1 (X=31457.752 Y=-7120.744 Z=11421.197)
|
||||
"Middle Station Theodore Mission End": base_id + 1154 # (dialog 2) -> 35_scraps_reward
|
||||
# "Middle Station Scraps Glitch 1" ItemPickup437_34 (X=34217.613 Y=-9481.271 Z=11505.686) /!\ Glitched scrap
|
||||
# "Middle Station Scraps Glitch 2" ItemPickup438_37 (X=36101.633 Y=-10459.024 Z=11385.937) /!\ Glitched scrap
|
||||
}
|
||||
|
||||
loc_canyon = {
|
||||
"Canyon Scraps 1": base_id + 1155, # ItemPickup156_47 Canyon_Main (X=29432.162 Y=-3164.300 Z=11540.294)
|
||||
"Canyon Scraps 2": base_id + 1156, # ItemPickup155_44 Canyon_Main (X=26331.086 Y=3036.740 Z=11701.688)
|
||||
"Canyon Scraps 3": base_id + 1157, # ItemPickup154_41 Canyon_Main (X=22688.129 Y=3906.730 Z=12249.182)
|
||||
"Canyon Scraps 4": base_id + 1158, # ItemPickup147_20 Canyon_Main (X=20546.193 Y=4371.471 Z=12128.874)
|
||||
"Canyon Scraps 5": base_id + 1159, # ItemPickup148_23 Canyon_Main (X=20006.584 Y=4928.478 Z=12174.837)
|
||||
"Canyon Scraps 6": base_id + 1160, # ItemPickup146_17 Canyon_Main (X=19251.633 Y=3798.014 Z=12170.390)
|
||||
"Canyon Scraps 7": base_id + 1161, # ItemPickup149_26 Canyon_Main (X=18302.678 Y=7323.849 Z=12595.085)
|
||||
"Canyon Scraps 8": base_id + 1162, # ItemPickup150_29 Canyon_Main (X=19019.563 Y=8172.146 Z=12640.462)
|
||||
"Canyon Scraps 9": base_id + 1163, # ItemPickup142_5 Canyon_Main (X=18001.689 Y=11138.320 Z=13035.360)
|
||||
"Canyon Scraps 10": base_id + 1164, # ItemPickup143_8 Canyon_Main (X=16381.525 Y=7191.394 Z=13682.453)
|
||||
"Canyon Scraps 11": base_id + 1165, # ItemPickup144_11 Canyon_Main (X=18294.928 Y=7870.372 Z=14350.015)
|
||||
"Canyon Scraps 12": base_id + 1166, # ItemPickup31_2 CanyonCamp_Details (X=20730.520 Y=8032.158 Z=14439.826)
|
||||
"Canyon Scraps 13": base_id + 1167, # ItemPickup145_14 Canyon_Main (X=24752.658 Y=7959.624 Z=14363.087)
|
||||
"Canyon Scraps 14": base_id + 1168, # ItemPickup141_2 Canyon_Main (X=20181.992 Y=13816.017 Z=14897.407)
|
||||
"Canyon Scraps 15": base_id + 1169, # ItemPickup151_32 Canyon_Main (X=23172.160 Y=2842.120 Z=12954.566)
|
||||
"Canyon Scraps 16": base_id + 1170, # ItemPickup152_35 Canyon_Main (X=22307.621 Y=-1180.840 Z=12451.548)
|
||||
"Canyon Scraps 17": base_id + 1171, # ItemPickup153_38 Canyon_Main (X=28473.596 Y=6741.842 Z=13314.166)
|
||||
"Canyon Blue Box": base_id + 1172 # Canyon_BlueBoxPickup CanyonCamp_Details (X=20338.525 Y=4989.111 Z=12323.649)
|
||||
}
|
||||
|
||||
loc_watchtower = {
|
||||
"Watchtower Scraps 1": base_id + 1173, # ItemPickup373_13 Secret2_WatchTowerDetails (X=32760.389 Y=-28814.084 Z=10997.447)
|
||||
"Watchtower Scraps 2": base_id + 1174, # ItemPickup22_6 Secret2_WatchTowerDetails (X=32801.668 Y=-31660.041 Z=10643.390)
|
||||
"Watchtower Scraps 3": base_id + 1175, # ItemPickup372_10 Secret2_WatchTowerDetails (X=31018.063 Y=-33375.313 Z=11100.126)
|
||||
"Watchtower Scraps 4": base_id + 1176, # ItemPickup22_16 Secret2_WatchTowerDetails (X=33308.215 Y=-35928.578 Z=10614.347)
|
||||
"Watchtower Scraps 5": base_id + 1177, # ItemPickup22_2 Secret2_WatchTowerDetails (X=34304.262 Y=-33446.063 Z=10674.936)
|
||||
"Watchtower Scraps 6": base_id + 1178, # ItemPickup370_2 Secret2_WatchTowerDetails (X=32869.453 Y=-33184.094 Z=10612.040)
|
||||
"Watchtower Scraps 7": base_id + 1179, # ItemPickup374_16 Secret2_WatchTowerDetails (X=33210.707 Y=-32097.611 Z=11211.031)
|
||||
"Watchtower Scraps 8": base_id + 1180, # ItemPickup22_10 Secret2_WatchTowerDetails (X=33246.262 Y=-32046.697 Z=11851.025)
|
||||
"Watchtower Scraps 9": base_id + 1181, # ItemPickup22_8 Secret2_WatchTowerDetails (X=33553.156 Y=-31810.645 Z=11849.521)
|
||||
"Watchtower Scraps 10": base_id + 1182, # ItemPickup371_7 Secret2_WatchTowerDetails (X=36151.621 Y=-31791.633 Z=11093.785)
|
||||
"Watchtower Pink Paint Can": base_id + 1183 # PaintCan_2 Secret2_WatchTowerDetails (X=33069.133 Y=-32168.045 Z=11859.582) \!/ Existing match 5
|
||||
}
|
||||
|
||||
loc_boulder_field = {
|
||||
"Boulder Field Page Drawing 1": base_id + 1184, # Pages_Drawing1 Pages_Environment_Details (X=46232.703 Y=-37052.875 Z=9531.116)
|
||||
"Boulder Field Page Drawing 2": base_id + 1185, # Pages_Drawing2 Pages_Environment_Details (X=51854.980 Y=-31332.070 Z=9804.927)
|
||||
"Boulder Field Page Drawing 3": base_id + 1186, # Pages_Drawing3 Pages_Environment_Details (X=47595.750 Y=-29931.740 Z=9308.014)
|
||||
"Boulder Field Page Drawing 4": base_id + 1187, # Pages_Drawing4 Pages_Environment_Details (X=43819.680 Y=-30378.770 Z=9706.599)
|
||||
"Boulder Field Page Drawing 5": base_id + 1188, # Pages_Drawing5 Pages_Environment_Details (X=47494.746 Y=-20884.781 Z=9812.398)
|
||||
"Boulder Field Page Drawing 6": base_id + 1189, # Pages_Drawing6 Pages_Environment_Details (X=43725.148 Y=-21952.570 Z=9744.351)
|
||||
"Boulder Field Page Drawing 7": base_id + 1190, # Pages_Drawing7 Pages_Environment_Details (X=44752.465 Y=-16362.510 Z=10147.004)
|
||||
"Boulder Field Page Drawing 8": base_id + 1191, # Pages_Drawing8 Pages_Environment_Details (X=50496.270 Y=-26090.533 Z=9835.365)
|
||||
"Boulder Field Scraps 1": base_id + 1192, # ItemPickup293_8 Pages_Environment_Main (X=41385.406 Y=-32281.871 Z=10240.781)
|
||||
"Boulder Field Scraps 2": base_id + 1193, # ItemPickup987321 Pages_House_Main (X=46654.969 Y=-38859.254 Z=9920.861)
|
||||
"Boulder Field Scraps 3": base_id + 1194, # ItemPickup516121 Pages_House_Main (X=44765.836 Y=-41675.559 Z=9938.179)
|
||||
"Boulder Field Scraps 4": base_id + 1195, # ItemPickup291_2 Pages_Environment_Main (X=50088.270 Y=-30669.107 Z=9267.371)
|
||||
"Boulder Field Scraps 5": base_id + 1196, # ItemPickup303_38 Pages_Environment_Main (X=48014.609 Y=-28971.115 Z=9199.659)
|
||||
"Boulder Field Scraps 6": base_id + 1197, # ItemPickup302_35 Pages_Environment_Main (X=50190.266 Y=-26243.977 Z=9648.289)
|
||||
"Boulder Field Scraps 7": base_id + 1198, # ItemPickup305_44 Pages_Environment_Main (X=47802.246 Y=-22594.684 Z=9631.879)
|
||||
"Boulder Field Scraps 8": base_id + 1199, # ItemPickup294_11 Pages_Environment_Main (X=44345.996 Y=-23408.535 Z=9659.643)
|
||||
"Boulder Field Scraps 9": base_id + 1200, # ItemPickup295_14 Pages_Environment_Main (X=41620.590 Y=-22982.641 Z=9720.177)
|
||||
"Boulder Field Scraps 10": base_id + 1201, # ItemPickup300_29 Pages_Environment_Main (X=52003.172 Y=-19163.049 Z=9925.105)
|
||||
"Boulder Field Scraps 11": base_id + 1202, # ItemPickup301_32 Pages_Environment_Main (X=51422.176 Y=-22319.322 Z=10663.813)
|
||||
"Boulder Field Scraps 12": base_id + 1203, # ItemPickup296_17 Pages_Environment_Main (X=43527.176 Y=-17952.570 Z=10812.458)
|
||||
"Boulder Field Scraps 13": base_id + 1204, # ItemPickup297_20 Pages_Environment_Main (X=45241.871 Y=-15847.636 Z=9952.198)
|
||||
"Boulder Field Scraps 14": base_id + 1205, # ItemPickup298_23 Pages_Environment_Main (X=46238.027 Y=-18407.420 Z=10199.825)
|
||||
"Boulder Field Scraps 15": base_id + 1206, # ItemPickup299_26 Pages_Environment_Main (X=49835.617 Y=-17379.959 Z=9810.836)
|
||||
"Boulder Field Scraps 16": base_id + 1207, # ItemPickup306_47 Pages_Environment_Main (X=45144.594 Y=-33817.090 Z=10136.658)
|
||||
"Boulder Field Scraps 17": base_id + 1208, # ItemPickup292_5 Pages_Environment_Main (X=44336.184 Y=-37162.367 Z=9789.548)
|
||||
"Boulder Field Scraps 18": base_id + 1209 # ItemPickup304_41 Pages_Environment_Main (X=44490.160 Y=-26442.754 Z=9974.022)
|
||||
}
|
||||
|
||||
loc_haunted_house = {
|
||||
"Haunted House Sasha Mission End": base_id + 1210, # (dialog 2) -> 40_scraps_reward
|
||||
"Haunted House Scraps 1": base_id + 1211, # ItemPickup1309876 Pages_House_Main (X=42900.188 Y=-43760.617 Z=9900.531)
|
||||
"Haunted House Scraps 2": base_id + 1212, # ItemPickup22213 Pages_House_Main (X=43608.078 Y=-44642.434 Z=9922.888)
|
||||
"Haunted House Scraps 3": base_id + 1213, # ItemPickup5423189 Pages_House_Main (X=43992.387 Y=-44259.336 Z=9877.623)
|
||||
"Haunted House Scraps 4": base_id + 1214, # ItemPickup1312312 Pages_House_Main (X=43340.012 Y=-45362.617 Z=9882.796)
|
||||
"Haunted House Scraps 5": base_id + 1215, # ItemPickup1596 Pages_House_Main (X=45105.383 Y=-45980.879 Z=9854.796)
|
||||
"Haunted House Scraps 6": base_id + 1216 # ItemPickup8624 Pages_House_Main (X=45888.406 Y=-46050.246 Z=9555.326)
|
||||
}
|
||||
|
||||
loc_santiago_house = {
|
||||
"Santiago House Scraps 1": base_id + 1217, # ItemPickup342_19 PortNPCHouse_Details (X=37271.445 Y=-46075.598 Z=10648.827)
|
||||
"Santiago House Scraps 2": base_id + 1218, # ItemPickup37_5 PortNPCHouse_Details (X=38330.512 Y=-47184.668 Z=10387.618)
|
||||
"Santiago House Scraps 3": base_id + 1219, # ItemPickup337_2 PortNPCHouse_Details (X=35720.422 Y=-49536.328 Z=10098.503)
|
||||
"Santiago House Scraps 4": base_id + 1220, # ItemPickup344_25 PortNPCHouse_Details (X=35466.285 Y=-50363.078 Z=10098.504)
|
||||
"Santiago House Scraps 5": base_id + 1221, # ItemPickup338_7 PortNPCHouse_Details (X=34274.289 Y=-49947.578 Z=10098.501)
|
||||
"Santiago House Scraps 6": base_id + 1222, # ItemPickup341_16 PortNPCHouse_Details (X=35584.359 Y=-48195.172 Z=10323.833)
|
||||
"Santiago House Scraps 7": base_id + 1223, # ItemPickup340_13 PortNPCHouse_Details (X=35019.766 Y=-49904.113 Z=10124.169)
|
||||
"Santiago House Scraps 8": base_id + 1224, # ItemPickup339_10 PortNPCHouse_Details (X=35527.711 Y=-49614.801 Z=10124.016)
|
||||
"Santiago House Scraps 9": base_id + 1225, # ItemPickup36_2 PortNPCHouse_Details (X=34471.707 Y=-49497.000 Z=10199.790)
|
||||
"Santiago House Scraps 10": base_id + 1226, # ItemPickup343_22 PortNPCHouse_Details (X=37920.277 Y=-51867.754 Z=9847.511)
|
||||
"Santiago House Journal": base_id + 1227 # Port_Journal_Pickup PortNPCHouse_Details (X=34690.777 Y=-49788.359 Z=10214.353)
|
||||
}
|
||||
|
||||
loc_port = {
|
||||
"Port Grey Paint Can": base_id + 1228, # PaintCan_13 Port_Details (X=74641.648 Y=-11320.948 Z=7551.767)
|
||||
"Port Scraps 1": base_id + 1229, # ItemPickup334_32 Port_Main (X=67315.281 Y=-13828.055 Z=10101.339)
|
||||
"Port Scraps 2": base_id + 1230, # ItemPickup335_35 Port_Main (X=67679.508 Y=-14127.952 Z=10061.037)
|
||||
"Port Scraps 3": base_id + 1231, # ItemPickup336_38 Port_Main (X=67062.219 Y=-15626.003 Z=10065.956)
|
||||
"Port Scraps 4": base_id + 1232, # ItemPickup21_15 Port_Main (X=66140.914 Y=-16079.730 Z=10092.268)
|
||||
"Port Scraps 5": base_id + 1233, # ItemPickup18_12 Port_Main (X=66824.719 Y=-14729.157 Z=10125.234)
|
||||
"Port Scraps 6": base_id + 1234, # ItemPickup333_29 Port_Main (X=69777.258 Y=-8371.526 Z=9391.735)
|
||||
"Port Scraps 7": base_id + 1235, # ItemPickup332_26 Port_Main (X=70339.695 Y=-11066.703 Z=8912.465)
|
||||
"Port Scraps 8": base_id + 1236, # ItemPickup331_23 Port_Main (X=72729.508 Y=-7048.998 Z=8245.522)
|
||||
"Port Scraps 9": base_id + 1237, # ItemPickup329_17 Port_Main (X=75896.070 Y=-8705.214 Z=7514.992)
|
||||
"Port Scraps 10": base_id + 1238, # ItemPickup330_20 Port_Main (X=74264.211 Y=-10553.446 Z=7520.141)
|
||||
"Port Scraps 11": base_id + 1239, # ItemPickup17_9 Port_Main (X=74328.117 Y=-11423.852 Z=7511.827)
|
||||
"Port Scraps 12": base_id + 1240, # ItemPickup328_14 Port_Main (X=76753.164 Y=-10744.933 Z=7437.174)
|
||||
"Port Scraps 13": base_id + 1241, # ItemPickup326_8 Port_Main (X=77330.414 Y=-11640.151 Z=7189.003)
|
||||
"Port Scraps 14": base_id + 1242, # ItemPickup327_11 Port_Main (X=76403.516 Y=-12484.995 Z=7440.368)
|
||||
"Port Scraps 15": base_id + 1243, # ItemPickup324_2 Port_Main (X=78651.977 Y=-12233.159 Z=7439.514)
|
||||
"Port Scraps 16": base_id + 1244, # ItemPickup14_5 Port_Main (X=80336.297 Y=-12276.590 Z=7436.639)
|
||||
"Port Scraps 17": base_id + 1245, # ItemPickup325_5 Port_Main (X=79845.086 Y=-13410.705 Z=7440.597)
|
||||
"Port Scraps 18": base_id + 1246, # ItemPickup13_2 Port_Main (X=76156.719 Y=-12816.718 Z=7439.269) KO
|
||||
"Port Scraps 19": base_id + 1247, # ItemPickup16 Port_Main (X=80754.914 Y=-14055.545 Z=7445.339) KO
|
||||
"Port Santiago Mission End": base_id + 1248 # (dialog 3) -> 35_scraps_reward
|
||||
}
|
||||
|
||||
loc_trench_house = {
|
||||
"Trench House Scraps 1": base_id + 1249, # ItemPickup157_2 DeadWoodsEnvironment (X=76340.328 Y=-42886.191 Z=9567.521)
|
||||
"Trench House Scraps 2": base_id + 1250, # ItemPickup158_5 DeadWoodsEnvironment (X=76013.594 Y=-44140.141 Z=9413.147)
|
||||
"Trench House Scraps 3": base_id + 1251, # ItemPickup159_11 DeadWoodsEnvironment (X=74408.320 Y=-45424.000 Z=9446.966)
|
||||
"Trench House Scraps 4": base_id + 1252, # ItemPickup69_14 Secret8_BasementHouseDetails (X=75196.344 Y=-48321.504 Z=9453.302)
|
||||
"Trench House Scraps 5": base_id + 1253, # ItemPickup160_14 DeadWoodsEnvironment (X=73467.273 Y=-48995.738 Z=9355.070)
|
||||
"Trench House Scraps 6": base_id + 1254, # ItemPickup163_23 DeadWoodsEnvironment (X=76418.469 Y=-53239.539 Z=9276.892)
|
||||
"Trench House Scraps 7": base_id + 1255, # ItemPickup173_56 DeadWoodsEnvironment (X=70719.875 Y=-54290.117 Z=9357.084)
|
||||
"Trench House Scraps 8": base_id + 1256, # ItemPickup165_29 DeadWoodsEnvironment (X=70075.938 Y=-53041.973 Z=9675.481)
|
||||
"Trench House Scraps 9": base_id + 1257, # ItemPickup162_20 DeadWoodsEnvironment (X=74745.711 Y=-52304.027 Z=9073.130)
|
||||
"Trench House Scraps 10": base_id + 1258, # ItemPickup68_11 Secret8_BasementHouseDetails (X=74519.750 Y=-53603.063 Z=9078.054)
|
||||
"Trench House Scraps 11": base_id + 1259, # ItemPickup161_17 DeadWoodsEnvironment (X=73747.492 Y=-52589.906 Z=9104.748)
|
||||
"Trench House Scraps 12": base_id + 1260, # ItemPickup67_8 Secret8_BasementHouseDetails (X=74333.125 Y=-52847.961 Z=9124.773)
|
||||
"Trench House Scraps 13": base_id + 1261, # ItemPickup66_5 Secret8_BasementHouseDetails (X=74062.195 Y=-52663.043 Z=9122.827)
|
||||
"Trench House Scraps 14": base_id + 1262, # ItemPickup65_2 Secret8_BasementHouseDetails (X=74820.492 Y=-51350.051 Z=7956.387)
|
||||
"Trench House Scraps 15": base_id + 1263, # ItemPickup164_26 DeadWoodsEnvironment (X=75286.289 Y=-51164.098 Z=7957.081)
|
||||
"Trench House Scraps 16": base_id + 1264, # ItemPickup174_59 DeadWoodsEnvironment (X=68413.258 Y=-56872.816 Z=9349.443)
|
||||
"Trench House Scraps 17": base_id + 1265, # ItemPickup175_62 DeadWoodsEnvironment (X=67281.281 Y=-59201.371 Z=9254.457)
|
||||
"Trench House Scraps 18": base_id + 1266, # ItemPickup172_50 DeadWoodsEnvironment (X=69064.219 Y=-48796.352 Z=9770.164)
|
||||
"Trench House Chest Scraps 1": base_id + 1267, # ItemPickup75123123 Secret8_BasementHouseDetails (X=75042.141 Y=-50830.891 Z=8005.156)
|
||||
"Trench House Chest Scraps 2": base_id + 1268, # ItemPickup741123 Secret8_BasementHouseDetails (X=75066.516 Y=-50824.398 Z=7995.000)
|
||||
"Trench House Chest Scraps 3": base_id + 1269, # ItemPickup729842 Secret8_BasementHouseDetails (X=75072.789 Y=-50818.441 Z=7986.979)
|
||||
"Trench House Chest Scraps 4": base_id + 1270, # ItemPickup711123 Secret8_BasementHouseDetails (X=75038.656 Y=-50827.566 Z=7973.354)
|
||||
"Trench House Chest Scraps 5": base_id + 1271, # ItemPickup73 Secret8_BasementHouseDetails (X=75060.406 Y=-50828.102 Z=7965.915)
|
||||
"Trench House Chest Scraps 6": base_id + 1272 # ItemPickup7075674 Secret8_BasementHouseDetails (X=75056.648 Y=-50818.125 Z=7959.868)
|
||||
}
|
||||
|
||||
loc_doll_woods = {
|
||||
"Doll Woods Scraps 1": base_id + 1273, # ItemPickup78_2 DeadWoodsDolls (X=60126.234 Y=-49668.906 Z=9970.880)
|
||||
"Doll Woods Scraps 2": base_id + 1274, # ItemPickup166_32 DeadWoodsEnvironment (X=59854.066 Y=-47313.121 Z=10376.684)
|
||||
"Doll Woods Scraps 3": base_id + 1275, # ItemPickup80_8 DeadWoodsDolls (X=59130.613 Y=-49597.789 Z=9930.675)
|
||||
"Doll Woods Scraps 4": base_id + 1276, # ItemPickup168_38 DeadWoodsEnvironment (X=59785.973 Y=-51269.684 Z=10180.019)
|
||||
"Doll Woods Scraps 5": base_id + 1277, # ItemPickup81_11 DeadWoodsDolls (X=58226.449 Y=-52660.801 Z=10576.626)
|
||||
"Doll Woods Scraps 6": base_id + 1278, # ItemPickup167_35 DeadWoodsEnvironment (X=56243.176 Y=-49097.793 Z=10869.889)
|
||||
"Doll Woods Scraps 7": base_id + 1279, # ItemPickup79_5 DeadWoodsDolls (X=59481.672 Y=-45288.137 Z=10897.672)
|
||||
"Doll Woods Scraps 8": base_id + 1280, # ItemPickup170_44 DeadWoodsEnvironment (X=63807.668 Y=-44674.734 Z=10337.434)
|
||||
"Doll Woods Scraps 9": base_id + 1281, # ItemPickup171_47 DeadWoodsEnvironment (X=68406.664 Y=-45721.813 Z=10021.356)
|
||||
"Doll Woods Scraps 10": base_id + 1282 # ItemPickup169_41 DeadWoodsEnvironment (X=62898.469 Y=-47565.703 Z=10744.431)
|
||||
}
|
||||
|
||||
loc_lost_stairs = {
|
||||
"Lost Stairs Scraps 1": base_id + 1283, # ItemPickup29_2 Secret1_Stairs2 (X=47087.617 Y=-53476.547 Z=9103.093)
|
||||
"Lost Stairs Scraps 2": base_id + 1284 # ItemPickup30_5 Secret1_Stairs2 (X=47162.238 Y=-55318.094 Z=9127.096)
|
||||
}
|
||||
|
||||
loc_east_house = {
|
||||
"East House Scraps 1": base_id + 1285, # ItemPickup409_5 Secret7_CliffHouseEnvironment (X=97507.664 Y=-53201.270 Z=9174.678)
|
||||
"East House Scraps 2": base_id + 1286, # ItemPickup408_2 Secret7_CliffHouseEnvironment (X=98511.242 Y=-53899.414 Z=9016.314)
|
||||
"East House Scraps 3": base_id + 1287, # ItemPickup410_8 Secret7_CliffHouseEnvironment (X=100688.102 Y=-54197.578 Z=8919.432)
|
||||
"East House Scraps 4": base_id + 1288, # ItemPickup411_11 Secret7_CliffHouseEnvironment (X=103149.773 Y=-54659.980 Z=9002.535)
|
||||
"East House Scraps 5": base_id + 1289, # ItemPickup416_26 Secret7_CliffHouseEnvironment (X=107458.172 Y=-55683.793 Z=9429.004)
|
||||
"East House Scraps 6": base_id + 1290, # ItemPickup25_2 Secret7_CliffHouseEnvironment (X=109034.164 Y=-54360.703 Z=9495.910)
|
||||
"East House Scraps 7": base_id + 1291, # ItemPickup413_17 Secret7_CliffHouseEnvironment (X=109245.148 Y=-55045.242 Z=9553.601)
|
||||
"East House Scraps 8": base_id + 1292, # ItemPickup414_20 Secret7_CliffHouseEnvironment (X=112556.445 Y=-55851.754 Z=10049.954)
|
||||
"East House Scraps 9": base_id + 1293, # ItemPickup415_23 Secret7_CliffHouseEnvironment (X=113131.469 Y=-56822.508 Z=10038.047)
|
||||
"East House Scraps 10": base_id + 1294, # ItemPickup2599786 Secret7_CliffHouseDetails (X=112279.828 Y=-56743.781 Z=10029.549)
|
||||
"East House Scraps 11": base_id + 1295, # ItemPickup253321 Secret7_CliffHouseDetails (X=112445.508 Y=-56280.320 Z=10059.164)
|
||||
"East House Scraps 12": base_id + 1296, # ItemPickup2532323 Secret7_CliffHouseDetails (X=112562.211 Y=-56736.332 Z=10454.907)
|
||||
"East House Scraps 13": base_id + 1297, # ItemPickup257655 Secret7_CliffHouseDetails (X=109313.320 Y=-58221.316 Z=9501.283)
|
||||
"East House Scraps 14": base_id + 1298, # ItemPickup412_14 Secret7_CliffHouseEnvironment (X=104077.805 Y=-55987.301 Z=9066.847)
|
||||
"East House Chest Scraps 1": base_id + 1299, # ItemPickup76246 Secret7_CliffHouseDetails (X=112317.242 Y=-55820.805 Z=10497.336)
|
||||
"East House Chest Scraps 2": base_id + 1300, # ItemPickup76245 Secret7_CliffHouseDetails (X=112326.086 Y=-55808.477 Z=10485.685)
|
||||
"East House Chest Scraps 3": base_id + 1301, # ItemPickup85131 Secret7_CliffHouseDetails (X=112329.031 Y=-55828.438 Z=10478.107)
|
||||
"East House Chest Scraps 4": base_id + 1302, # ItemPickup56124 Secret7_CliffHouseDetails (X=112315.922 Y=-55820.102 Z=10466.683)
|
||||
"East House Chest Scraps 5": base_id + 1303 # ItemPickup25123123123 Secret7_CliffHouseDetails (X=112337.922 Y=-55821.848 Z=10456.924)
|
||||
}
|
||||
|
||||
loc_rockets_testing_ground = {
|
||||
"Rockets Testing Ground Timed Dynamite": base_id + 1304, # Boomer_DynamitePickup Boomer_RangeDetails (X=76476.609 Y=-65286.738 Z=8303.742)
|
||||
"Rockets Testing Ground Scraps 1": base_id + 1305, # ItemPickup105_14 Boomer_HouseDetails (X=88925.570 Y=-63375.051 Z=8563.354)
|
||||
"Rockets Testing Ground Scraps 2": base_id + 1306, # ItemPickup106_17 Boomer_HouseDetails (X=84234.016 Y=-64475.551 Z=8382.108)
|
||||
"Rockets Testing Ground Scraps 3": base_id + 1307, # ItemPickup114_23 Boomer_RangeDetails (X=79349.438 Y=-64225.480 Z=8384.219)
|
||||
"Rockets Testing Ground Scraps 4": base_id + 1308, # ItemPickup22_0 Boomer_RangeDetails (X=79831.070 Y=-65847.766 Z=8301.337)
|
||||
"Rockets Testing Ground Scraps 5": base_id + 1309, # ItemPickup109_5 Boomer_RangeDetails (X=76526.500 Y=-65394.875 Z=8223.883)
|
||||
"Rockets Testing Ground Scraps 6": base_id + 1310, # ItemPickup108_2 Boomer_RangeDetails (X=76237.977 Y=-67087.414 Z=8361.979)
|
||||
"Rockets Testing Ground Scraps 7": base_id + 1311, # ItemPickup115_26 Boomer_RangeDetails (X=78857.672 Y=-67802.227 Z=8257.150)
|
||||
"Rockets Testing Ground Scraps 8": base_id + 1312, # ItemPickup110_8 Boomer_RangeDetails (X=74878.570 Y=-62927.297 Z=8749.549)
|
||||
"Rockets Testing Ground Scraps 9": base_id + 1313, # ItemPickup111_11 Boomer_RangeDetails (X=74542.641 Y=-61301.082 Z=9493.931)
|
||||
"Rockets Testing Ground Scraps 10": base_id + 1314 # ItemPickup23_0 Boomer_RangeDetails (X=77020.859 Y=-62031.320 Z=8873.663)
|
||||
# "Rockets Testing Ground Scraps Glitch 1" ItemPickup107_20 (X=81308.406 Y=-63482.320 Z=8533.338) /!\ Glitched scrap
|
||||
}
|
||||
|
||||
loc_rockets_testing_bunker = {
|
||||
"Rockets Testing Bunker Scraps 1": base_id + 1315, # ItemPickup113_20 Boomer_RangeDetails (X=77552.094 Y=-61144.559 Z=8523.195)
|
||||
"Rockets Testing Bunker Scraps 2": base_id + 1316, # ItemPickup112_14 Boomer_RangeDetails (X=77670.227 Y=-62029.941 Z=8570.785)
|
||||
"Rockets Testing Bunker Box of Rockets": base_id + 1317 # Boomer_RocketsPickup Boomer_RangeDetails (X=77330.086 Y=-61504.324 Z=8523.195)
|
||||
}
|
||||
|
||||
loc_workshop = {
|
||||
"Workshop Scraps 1": base_id + 1318, # ItemPickup103_8 Boomer_HouseDetails (X=93550.773 Y=-61901.797 Z=8828.551)
|
||||
"Workshop Scraps 2": base_id + 1319, # ItemPickup102_5 Boomer_HouseDetails (X=93508.047 Y=-64009.910 Z=8783.468)
|
||||
"Workshop Scraps 3": base_id + 1320, # ItemPickup101_2 Boomer_HouseDetails (X=92011.648 Y=-65572.281 Z=8736.709)
|
||||
"Workshop Scraps 4": base_id + 1321, # ItemPickup24_2 Boomer_HouseDetails (X=92311.594 Y=-63045.211 Z=8749.977)
|
||||
"Workshop Scraps 5": base_id + 1322, # ItemPickup104_11 Boomer_HouseDetails (X=91392.734 Y=-63527.629 Z=8709.268)
|
||||
"Workshop Scraps 6": base_id + 1323, # ItemPickup25_1 Boomer_HouseDetails (X=92986.789 Y=-63012.047 Z=9235.383)
|
||||
"Workshop John Smith Mission End": base_id + 1324 # (dialog 2) -> the_boomer
|
||||
}
|
||||
|
||||
loc_east_tower = {
|
||||
"Greg Mission Start": base_id + 1325, # (dialog 6) -> north_mine_key
|
||||
"East Tower Scraps 1": base_id + 1326, # ItemPickup231_17 Mine2_NPCHouseDetails (X=95448.250 Y=-67249.156 Z=8607.896)
|
||||
"East Tower Scraps 2": base_id + 1327, # ItemPickup228_8 Mine2_NPCHouseDetails (X=96339.242 Y=-66374.828 Z=8650.519)
|
||||
"East Tower Scraps 3": base_id + 1328, # ItemPickup229_11 Mine2_NPCHouseDetails (X=98540.711 Y=-67173.656 Z=8418.825)
|
||||
"East Tower Scraps 4": base_id + 1329, # ItemPickup230_14 Mine2_NPCHouseDetails (X=97276.414 Y=-68495.008 Z=8337.229)
|
||||
"East Tower Scraps 5": base_id + 1330, # ItemPickup227_5 Mine2_NPCHouseDetails (X=96470.820 Y=-66540.859 Z=8953.763)
|
||||
"East Tower Scraps 6": base_id + 1331 # ItemPickup226_2 Mine2_NPCHouseDetails (X=96141.555 Y=-67013.445 Z=9399.308)
|
||||
}
|
||||
|
||||
loc_lighthouse = {
|
||||
"Lighthouse Scraps 1": base_id + 1332, # ItemPickup200_41 LighthouseTerrainMain (X=100072.813 Y=-68645.688 Z=8150.313)
|
||||
"Lighthouse Scraps 2": base_id + 1333, # ItemPickup198_35 LighthouseTerrainMain (X=105340.594 Y=-70828.602 Z=8436.780)
|
||||
"Lighthouse Scraps 3": base_id + 1334, # ItemPickup199_38 LighthouseTerrainMain (X=103851.688 Y=-73396.625 Z=7973.290)
|
||||
"Lighthouse Scraps 4": base_id + 1335, # ItemPickup196_29 LighthouseTerrainMain (X=107040.711 Y=-74021.555 Z=8303.216)
|
||||
"Lighthouse Scraps 5": base_id + 1336, # ItemPickup197_32 LighthouseTerrainMain (X=110566.859 Y=-77435.961 Z=7642.565)
|
||||
"Lighthouse Scraps 6": base_id + 1337, # ItemPickup32_2 LighthouseShed_Main (X=111451.352 Y=-77351.117 Z=7633.413)
|
||||
"Lighthouse Scraps 7": base_id + 1338, # ItemPickup35_11 Lighthouse_Main (X=113078.500 Y=-78618.281 Z=7180.793)
|
||||
"Lighthouse Scraps 8": base_id + 1339, # ItemPickup192_17 LighthouseTerrainMain (X=113396.305 Y=-80315.383 Z=7184.260)
|
||||
"Lighthouse Scraps 9": base_id + 1340, # ItemPickup193_20 LighthouseTerrainMain (X=114057.484 Y=-81517.836 Z=7245.034)
|
||||
"Lighthouse Scraps 10": base_id + 1341, # ItemPickup194_23 LighthouseTerrainMain (X=110915.156 Y=-78376.609 Z=7676.131)
|
||||
"Lighthouse Scraps 11": base_id + 1342, # ItemPickup195_26 LighthouseTerrainMain (X=109341.703 Y=-79014.469 Z=8075.679)
|
||||
"Lighthouse Scraps 12": base_id + 1343, # ItemPickup33_5 Lighthouse_Details (X=107006.578 Y=-81377.711 Z=8821.629)
|
||||
"Lighthouse Scraps 13": base_id + 1344, # ItemPickup191_14 LighthouseTerrainMain (X=109240.195 Y=-82951.375 Z=8194.619)
|
||||
"Lighthouse Scraps 14": base_id + 1345, # ItemPickup190_11 LighthouseTerrainMain (X=106295.719 Y=-84190.578 Z=8581.896)
|
||||
"Lighthouse Scraps 15": base_id + 1346, # ItemPickup189_8 LighthouseTerrainMain (X=104233.883 Y=-84663.328 Z=7806.311)
|
||||
"Lighthouse Scraps 16": base_id + 1347, # ItemPickup188_5 LighthouseTerrainMain (X=103209.227 Y=-81564.047 Z=8140.578)
|
||||
"Lighthouse Scraps 17": base_id + 1348, # ItemPickup187_2 LighthouseTerrainMain (X=104795.555 Y=-81344.758 Z=8775.158)
|
||||
"Lighthouse Scraps 18": base_id + 1349, # ItemPickup34_8 Lighthouse_Main (X=100843.914 Y=-78038.539 Z=7197.542)
|
||||
"Lighthouse Breaker 1": base_id + 1350, # ItemPickup13_2 LighthouseShed_Main (X=110781.164 Y=-77296.813 Z=7757.248) \!/ Existing match 6
|
||||
"Lighthouse Breaker 2": base_id + 1351, # ItemPickup14 LighthouseShed_Main (X=110899.227 Y=-77239.031 Z=7757.134) \!/ Existing match 3
|
||||
"Lighthouse Breaker 3": base_id + 1352, # ItemPickup16 LighthouseShed_Main (X=110948.547 Y=-77253.336 Z=7757.134) \!/ Existing match 2
|
||||
"Lighthouse Breaker 4": base_id + 1353, # ItemPickup17 LighthouseShed_Main (X=111001.078 Y=-77205.047 Z=7757.134) \!/ Existing match 1
|
||||
"Lighthouse Claire Mission End": base_id + 1354 # (dialog 2) -> 30_scraps_reward
|
||||
}
|
||||
|
||||
loc_north_mine_outside = {
|
||||
"North Mine Outside Scraps 1": base_id + 1355, # ItemPickup241_31 Mine2_OutsideDetails (X=-52376.746 Y=-101857.492 Z=10542.841)
|
||||
"North Mine Outside Scraps 2": base_id + 1356, # ItemPickup242_34 Mine2_OutsideDetails (X=-53786.742 Y=-102067.789 Z=10858.948)
|
||||
"North Mine Outside Scraps 3": base_id + 1357, # ItemPickup239_25 Mine2_OutsideDetails (X=-57502.777 Y=-105475.336 Z=10609.405)
|
||||
"North Mine Outside Scraps 4": base_id + 1358, # ItemPickup16_2 Mine2_OutsideDetails (X=-58102.102 Y=-104007.906 Z=11146.535)
|
||||
"North Mine Outside Scraps 5": base_id + 1359, # ItemPickup238_20 Mine2_OutsideDetails (X=-59474.840 Y=-105053.734 Z=11213.524)
|
||||
"North Mine Outside Scraps 6": base_id + 1360, # ItemPickup240_28 Mine2_OutsideDetails (X=-55011.750 Y=-104936.359 Z=9935.366)
|
||||
"North Mine Outside Scraps 7": base_id + 1361, # ItemPickup236_14 Mine2_OutsideDetails (X=-55594.863 Y=-107667.594 Z=9596.611)
|
||||
"North Mine Outside Scraps 8": base_id + 1362, # ItemPickup15_1 Mine2_Interior2 (X=-56632.578 Y=-109503.406 Z=9280.788)
|
||||
"North Mine Outside Scraps 9": base_id + 1363, # ItemPickup234_8 Mine2_OutsideDetails (X=-54645.418 Y=-110747.602 Z=9553.452)
|
||||
"North Mine Outside Scraps 10": base_id + 1364, # ItemPickup232_2 Mine2_OutsideDetails (X=-51561.340 Y=-113574.813 Z=9414.959)
|
||||
"North Mine Outside Scraps 11": base_id + 1365, # ItemPickup233_5 Mine2_OutsideDetails (X=-54072.105 Y=-112672.031 Z=10077.665)
|
||||
"North Mine Outside Scraps 12": base_id + 1366, # ItemPickup237_17 Mine2_OutsideDetails (X=-58042.758 Y=-108748.656 Z=9693.470)
|
||||
"North Mine Outside Scraps 13": base_id + 1367, # ItemPickup235_11 Mine2_OutsideDetails (X=-55717.227 Y=-110610.414 Z=9487.879)
|
||||
"North Mine Outside Scraps 14": base_id + 1368 # ItemPickup13_2 Mine2_Interior2 (X=-52235.836 Y=-114501.117 Z=9462.438) KO
|
||||
}
|
||||
|
||||
loc_north_mine_inside = {
|
||||
"North Mine Inside Scraps 1": base_id + 1369, # ItemPickup17_2 Mine2_Interior1 (X=-58433.055 Y=-104081.570 Z=9378.083) KO
|
||||
"North Mine Inside Scraps 2": base_id + 1370, # ItemPickup246_2 Mine2_Interior1 (X=-58987.199 Y=-103262.906 Z=9186.494)
|
||||
"North Mine Inside Scraps 3": base_id + 1371, # ItemPickup247_5 Mine2_Interior1 (X=-58812.801 Y=-99259.570 Z=8847.714)
|
||||
"North Mine Inside Scraps 4": base_id + 1372, # ItemPickup248_8 Mine2_Interior1 (X=-56634.379 Y=-99529.563 Z=8851.877)
|
||||
"North Mine Inside Scraps 5": base_id + 1373, # ItemPickup22_4 Mine2_Interior1 (X=-55604.477 Y=-98342.906 Z=8842.766)
|
||||
"North Mine Inside Scraps 6": base_id + 1374, # ItemPickup250_14 Mine2_Interior1 (X=-54824.535 Y=-98526.492 Z=8852.156)
|
||||
"North Mine Inside Scraps 7": base_id + 1375, # ItemPickup21_14 Mine2_Interior1 (X=-54887.254 Y=-99047.141 Z=8849.855)
|
||||
"North Mine Inside Scraps 8": base_id + 1376, # ItemPickup20_2 Mine2_Interior1 (X=-55610.020 Y=-101877.961 Z=9081.042)
|
||||
"North Mine Inside Scraps 9": base_id + 1377, # ItemPickup19_2 Mine2_Interior1 (X=-56519.340 Y=-101375.008 Z=9001.270)
|
||||
"North Mine Inside Scraps 10": base_id + 1378, # ItemPickup249_11 Mine2_Interior1 (X=-53329.922 Y=-99469.773 Z=8848.643)
|
||||
"North Mine Inside Scraps 11": base_id + 1379, # ItemPickup251_17 Mine2_Interior1 (X=-52814.828 Y=-96286.969 Z=8851.372)
|
||||
"North Mine Inside Scraps 12": base_id + 1380, # ItemPickup24_1 Mine2_Interior1 (X=-52605.957 Y=-96535.156 Z=8940.480)
|
||||
"North Mine Inside Scraps 13": base_id + 1381, # ItemPickup23_20 Mine2_Interior1 (X=-53237.699 Y=-96609.461 Z=8846.201)
|
||||
"North Mine Inside Scraps 14": base_id + 1382, # ItemPickup18_5 Mine2_Interior1 (X=-58543.488 Y=-95879.695 Z=8981.646)
|
||||
"North Mine Inside Blue Egg": base_id + 1383, # Mine2_Egg Mine2_Interior2 (X=-53592.195 Y=-99177.500 Z=8975.387)
|
||||
"North Mine Inside Blue Paint Can": base_id + 1384 # PaintCan_2 Mine2_Interior2 (X=-56133.391 Y=-101870.047 Z=9004.720) \!/ Existing match 5
|
||||
# "North Mine Inside Secret Gear" ItemPickup26_2 (X=-55546.859 Y=-98209.852 Z=8429.085) /!\ Inaccessible gear
|
||||
}
|
||||
|
||||
loc_wood_bridge = {
|
||||
"Wood Bridge Scraps 1": base_id + 1385, # ItemPickup127_35 Bridge_Details (X=-66790.141 Y=-110340.367 Z=10454.417)
|
||||
"Wood Bridge Scraps 2": base_id + 1386, # ItemPickup18613654 Bridge_Details (X=-68364.586 Y=-111691.625 Z=10444.172)
|
||||
"Wood Bridge Scraps 3": base_id + 1387, # ItemPickup1311221 Bridge_StructureDetails (X=-69013.555 Y=-112353.977 Z=10399.942)
|
||||
"Wood Bridge Scraps 4": base_id + 1388, # ItemPickup93564 Bridge_StructureDetails (X=-70398.797 Y=-112916.945 Z=10372.192)
|
||||
"Wood Bridge Scraps 5": base_id + 1389, # ItemPickup161323 Bridge_Details (X=-71336.172 Y=-106966.672 Z=8430.104)
|
||||
"Wood Bridge Scraps 6": base_id + 1390, # ItemPickup128_38 Bridge_Details (X=-72776.086 Y=-107813.102 Z=8305.589)
|
||||
"Wood Bridge Scraps 7": base_id + 1391, # ItemPickup17975 Bridge_Details (X=-75224.648 Y=-108280.867 Z=7929.499)
|
||||
"Wood Bridge Scraps 8": base_id + 1392, # ItemPickup126_32 Bridge_Details (X=-68112.172 Y=-105119.656 Z=9458.937)
|
||||
"Wood Bridge Scraps 9": base_id + 1393, # ItemPickup118_8 Bridge_Details (X=-71847.625 Y=-103623.203 Z=10707.521)
|
||||
"Wood Bridge Scraps 10": base_id + 1394, # ItemPickup116_2 Bridge_Details (X=-71812.219 Y=-107256.094 Z=10780.134)
|
||||
"Wood Bridge Scraps 11": base_id + 1395, # ItemPickup134321 Bridge_Details (X=-72011.570 Y=-109054.547 Z=10866.852)
|
||||
"Wood Bridge Scraps 12": base_id + 1396, # ItemPickup117_5 Bridge_Details (X=-72862.430 Y=-106144.852 Z=10329.061)
|
||||
"Wood Bridge Scraps 13": base_id + 1397 # ItemPickup1494567 Bridge_Details (X=-71843.117 Y=-107174.133 Z=10367.116)
|
||||
}
|
||||
|
||||
loc_museum = {
|
||||
"Museum Scraps 1": base_id + 1398, # ItemPickup119_11 Bridge_Details (X=-69687.773 Y=-100002.406 Z=10806.339)
|
||||
"Museum Scraps 2": base_id + 1399, # ItemPickup120_14 Bridge_Details (X=-68035.195 Y=-99480.672 Z=11049.731)
|
||||
"Museum Scraps 3": base_id + 1400, # ItemPickup125_29 Bridge_Details (X=-66912.641 Y=-99976.750 Z=11064.357)
|
||||
"Museum Scraps 4": base_id + 1401, # ItemPickup13532 Bridge_HouseDetails (X=-64901.117 Y=-99624.953 Z=11176.359)
|
||||
"Museum Scraps 5": base_id + 1402, # ItemPickup121_17 Bridge_Details (X=-66082.328 Y=-98105.555 Z=11089.308)
|
||||
"Museum Scraps 6": base_id + 1403, # ItemPickup21765 Bridge_HouseDetails (X=-67402.742 Y=-97735.133 Z=11153.927)
|
||||
"Museum Scraps 7": base_id + 1404, # ItemPickup124_26 Bridge_Details (X=-66716.031 Y=-98282.508 Z=11195.624)
|
||||
"Museum Scraps 8": base_id + 1405, # ItemPickup122_20 Bridge_Details (X=-66582.703 Y=-99092.461 Z=11630.082)
|
||||
"Museum Scraps 9": base_id + 1406, # ItemPickup123_23 Bridge_Details (X=-66798.164 Y=-99550.266 Z=11547.321)
|
||||
"Museum Scraps 10": base_id + 1407, # ItemPickup183423 Bridge_HouseDetails (X=-66850.336 Y=-99682.844 Z=11543.618)
|
||||
"Museum Scraps 11": base_id + 1408, # ItemPickup244_40 Mine2_OutsideDetails (X=-60156.828 Y=-98516.953 Z=11811.422)
|
||||
"Museum Scraps 12": base_id + 1409, # ItemPickup245_43 Mine2_OutsideDetails (X=-61195.203 Y=-98262.422 Z=11779.118)
|
||||
"Museum Paul Mission Start": base_id + 1410, # (dialog 6) -> remote_explosive (x8)
|
||||
"Museum Paul Mission End": base_id + 1411 # (dialog 3) -> temple_key
|
||||
}
|
||||
|
||||
loc_barbed_shelter = {
|
||||
"Barbed Shelter Gertrude Mission Start": base_id + 1412, # (dialog 4) -> broken_bob
|
||||
"Barbed Shelter Scraps 1": base_id + 1413, # ItemPickup100_8 Bob_NPCHouseMain (X=-72525.500 Y=-89333.734 Z=9820.663)
|
||||
"Barbed Shelter Scraps 2": base_id + 1414, # ItemPickup98_2 Bob_NPCHouseMain (X=-74870.758 Y=-88576.641 Z=9836.814)
|
||||
"Barbed Shelter Scraps 3": base_id + 1415, # ItemPickup22_1 Bob_NPCHouseDetails (X=-76193.914 Y=-88038.836 Z=9818.776)
|
||||
"Barbed Shelter Scraps 4": base_id + 1416, # ItemPickup99_5 Bob_NPCHouseMain (X=-74494.859 Y=-87609.969 Z=9837.866)
|
||||
"Barbed Shelter Scraps 5": base_id + 1417 # ItemPickup23_3 Bob_NPCHouseDetails (X=-74826.930 Y=-88402.039 Z=9929.854)
|
||||
}
|
||||
|
||||
loc_west_beach = {
|
||||
"West Beach Chest Scraps 1": base_id + 1418, # ItemPickup9122346 Secret11_Details (X=-85934.047 Y=-89532.547 Z=7383.054)
|
||||
"West Beach Chest Scraps 2": base_id + 1419, # ItemPickup84 Secret11_Details (X=-85933.977 Y=-89532.977 Z=7369.364)
|
||||
"West Beach Chest Scraps 3": base_id + 1420, # ItemPickup9099877 Secret11_Details (X=-85951.000 Y=-89527.023 Z=7367.054)
|
||||
"West Beach Chest Scraps 4": base_id + 1421, # ItemPickup89086423 Secret11_Details (X=-85932.461 Y=-89533.148 Z=7354.001)
|
||||
"West Beach Chest Scraps 5": base_id + 1422, # ItemPickup83 Secret11_Details (X=-85950.930 Y=-89527.453 Z=7353.365)
|
||||
"West Beach Chest Scraps 6": base_id + 1423, # ItemPickup82_2 Secret11_Details (X=-85932.391 Y=-89533.578 Z=7340.312)
|
||||
"West Beach Scraps 1": base_id + 1424, # ItemPickup87_13 Secret11_Details (X=-84489.945 Y=-91235.977 Z=8360.803)
|
||||
"West Beach Scraps 2": base_id + 1425, # ItemPickup349_2 Secret11_Details1 (X=-84386.320 Y=-90391.789 Z=8376.434)
|
||||
"West Beach Scraps 3": base_id + 1426, # ItemPickup86_10 Secret11_Details (X=-84714.773 Y=-89876.992 Z=7707.064)
|
||||
"West Beach Scraps 4": base_id + 1427, # ItemPickup350_5 Secret11_Details1 (X=-85478.672 Y=-90648.414 Z=7708.377)
|
||||
"West Beach Scraps 5": base_id + 1428, # ItemPickup85_7 Secret11_Details (X=-86276.633 Y=-90674.289 Z=7532.364)
|
||||
"West Beach Scraps 6": base_id + 1429, # ItemPickup88_16 Secret11_Details (X=-84363.055 Y=-87497.938 Z=7582.647)
|
||||
"West Beach Scraps 7": base_id + 1430, # ItemPickup351_8 Secret11_Details1 (X=-86556.266 Y=-89748.484 Z=7297.274)
|
||||
"West Beach Scraps 8": base_id + 1431 # ItemPickup352_11 Secret11_Details1 (X=-83210.836 Y=-92551.953 Z=8460.213)
|
||||
}
|
||||
|
||||
loc_church = {
|
||||
"Church Black Paint Can": base_id + 1432, # PaintCan_4 Secret5_ChurchDetails (X=-67628.172 Y=-83801.375 Z=9865.983)
|
||||
"Church Scraps 1": base_id + 1433, # ItemPickup391_11 Secret5_ChurchDetails (X=-64009.039 Y=-84252.156 Z=10258.335)
|
||||
"Church Scraps 2": base_id + 1434, # ItemPickup389_5 Secret5_ChurchDetails (X=-66870.719 Y=-85202.180 Z=9843.936)
|
||||
"Church Scraps 3": base_id + 1435, # ItemPickup388_2 Secret5_ChurchDetails (X=-68588.352 Y=-84041.867 Z=9790.541)
|
||||
"Church Scraps 4": base_id + 1436, # ItemPickup396_26 Secret5_ChurchDetails (X=-67595.797 Y=-82120.094 Z=9818.303)
|
||||
"Church Scraps 5": base_id + 1437, # ItemPickup390_8 Secret5_ChurchDetails (X=-67291.000 Y=-83324.836 Z=9774.942)
|
||||
"Church Scraps 6": base_id + 1438, # ItemPickup392_14 Secret5_ChurchDetails (X=-65849.070 Y=-80676.477 Z=9895.943)
|
||||
"Church Scraps 7": base_id + 1439, # ItemPickup395_23 Secret5_ChurchDetails (X=-65170.266 Y=-79155.227 Z=9904.275)
|
||||
"Church Scraps 8": base_id + 1440, # ItemPickup24_4 Secret5_GraveyardMain (X=-64837.563 Y=-80885.305 Z=9906.755)
|
||||
"Church Scraps 9": base_id + 1441, # ItemPickup22_3 Secret5_ChurchDetails (X=-68248.359 Y=-83578.008 Z=9807.300)
|
||||
"Church Scraps 10": base_id + 1442, # ItemPickup23_5 Secret5_ChurchDetails (X=-67086.102 Y=-84605.086 Z=9805.521)
|
||||
"Church Scraps 11": base_id + 1443, # ItemPickup393_17 Secret5_ChurchDetails (X=-67901.930 Y=-83477.625 Z=9812.613)
|
||||
"Church Scraps 12": base_id + 1444 # ItemPickup394_20 Secret5_ChurchDetails (X=-65834.344 Y=-84192.102 Z=9987.823)
|
||||
}
|
||||
|
||||
loc_west_cottage = {
|
||||
"West Cottage Gale Mission Start": base_id + 1445, # (dialog 10) -> mountain_ruin_key
|
||||
"West Cottage Scraps 1": base_id + 1446, # ItemPickup15_3 Mine3_NPCHouse (X=-74407.695 Y=-81781.250 Z=10120.775)
|
||||
"West Cottage Scraps 2": base_id + 1447, # ItemPickup283_5 Mine3_NPCHouseDetails (X=-73784.695 Y=-79414.359 Z=10128.285)
|
||||
"West Cottage Scraps 3": base_id + 1448, # ItemPickup13_1 Mine3_NPCHouse (X=-73992.391 Y=-78600.094 Z=10162.495)
|
||||
"West Cottage Scraps 4": base_id + 1449, # ItemPickup284_8 Mine3_NPCHouseDetails (X=-71623.000 Y=-75998.023 Z=10275.477)
|
||||
"West Cottage Scraps 5": base_id + 1450 # ItemPickup282_2 Mine3_NPCHouseDetails (X=-72626.453 Y=-79391.070 Z=10211.037)
|
||||
}
|
||||
|
||||
loc_caravan = {
|
||||
"Caravan Scraps 1": base_id + 1451, # ItemPickup348_11 Secret10_PAth (X=-52638.109 Y=-43924.395 Z=10579.809)
|
||||
"Caravan Scraps 2": base_id + 1452, # ItemPickup347_8 Secret10_PAth (X=-50203.695 Y=-42865.672 Z=10778.871)
|
||||
"Caravan Scraps 3": base_id + 1453, # ItemPickup346_5 Secret10_PAth (X=-48467.738 Y=-42018.488 Z=10818.758)
|
||||
"Caravan Scraps 4": base_id + 1454, # ItemPickup77_14 Secret10_Details (X=-46325.219 Y=-41707.512 Z=11003.229)
|
||||
"Caravan Scraps 5": base_id + 1455, # ItemPickup345_2 Secret10_PAth (X=-44557.043 Y=-40652.930 Z=11076.221)
|
||||
"Caravan Scraps 6": base_id + 1456, # ItemPickup76_11 Secret10_Details (X=-43380.664 Y=-38207.152 Z=11165.370)
|
||||
"Caravan Scraps 7": base_id + 1457, # ItemPickup73_2 Secret10_Details (X=-42919.410 Y=-38797.738 Z=11265.633)
|
||||
"Caravan Scraps 8": base_id + 1458, # ItemPickup74_5 Secret10_Details (X=-42787.523 Y=-38601.820 Z=11254.003)
|
||||
"Caravan Scraps 9": base_id + 1459, # ItemPickup75_8 Secret10_Details (X=-42711.363 Y=-39141.523 Z=11173.905)
|
||||
"Caravan Chest Scraps 1": base_id + 1460, # ItemPickup71561 Secret10_Details (X=-42910.668 Y=-38297.309 Z=11233.402)
|
||||
"Caravan Chest Scraps 2": base_id + 1461, # ItemPickup078654 Secret10_Details (X=-42904.344 Y=-38307.332 Z=11219.678)
|
||||
"Caravan Chest Scraps 3": base_id + 1462, # ItemPickup02345 Secret10_Details (X=-42904.965 Y=-38280.383 Z=11208.191)
|
||||
"Caravan Chest Scraps 4": base_id + 1463, # ItemPickup-6546483648 Secret10_Details (X=-42911.680 Y=-38315.254 Z=11204.225)
|
||||
"Caravan Chest Scraps 5": base_id + 1464 # ItemPickup176752623547 Secret10_Details (X=-42905.090 Y=-38279.828 Z=11192.738)
|
||||
}
|
||||
|
||||
loc_trailer_cabin = {
|
||||
"Trailer Cabin Scraps 1": base_id + 1465, # ItemPickup493_17 TrailerCabin_Details (X=-50702.449 Y=-38850.020 Z=10810.316)
|
||||
"Trailer Cabin Scraps 2": base_id + 1466, # ItemPickup489_5 TrailerCabin_Details (X=-51365.684 Y=-38502.379 Z=10875.761)
|
||||
"Trailer Cabin Scraps 3": base_id + 1467, # ItemPickup491_11 TrailerCabin_Details (X=-52397.570 Y=-37530.145 Z=10873.624)
|
||||
"Trailer Cabin Scraps 4": base_id + 1468, # ItemPickup490_8 TrailerCabin_Details (X=-50625.746 Y=-37916.758 Z=10886.909)
|
||||
"Trailer Cabin Scraps 5": base_id + 1469, # ItemPickup488_2 TrailerCabin_Details (X=-51201.051 Y=-37467.137 Z=10910.795)
|
||||
"Trailer Cabin Scraps 6": base_id + 1470 # ItemPickup492_14 TrailerCabin_Details (X=-51891.320 Y=-40549.492 Z=10675.211)
|
||||
}
|
||||
|
||||
loc_towers = {
|
||||
"Towers Scraps 1": base_id + 1471, # ItemPickup486_32 Towers_Environment_Details (X=-24434.766 Y=-25708.373 Z=11200.865)
|
||||
"Towers Scraps 2": base_id + 1472, # ItemPickup483_23 Towers_Environment_Details (X=-20970.262 Y=-25678.754 Z=11731.241)
|
||||
"Towers Scraps 3": base_id + 1473, # ItemPickup481_17 Towers_Environment_Details (X=-19812.230 Y=-27768.301 Z=12051.623)
|
||||
"Towers Scraps 4": base_id + 1474, # ItemPickup484_26 Towers_Environment_Details (X=-19940.912 Y=-25411.576 Z=12035.366)
|
||||
"Towers Scraps 5": base_id + 1475, # ItemPickup41_17 Towers_Environment (X=-18596.791 Y=-25100.035 Z=12290.350)
|
||||
"Towers Scraps 6": base_id + 1476, # ItemPickup482_20 Towers_Environment_Details (X=-23302.396 Y=-23270.324 Z=12036.164)
|
||||
"Towers Scraps 7": base_id + 1477, # ItemPickup487_35 Towers_Environment_Details (X=-22955.039 Y=-27576.859 Z=11211.258)
|
||||
"Towers Scraps 8": base_id + 1478, # ItemPickup478_8 Towers_Environment_Details (X=-21485.520 Y=-29634.893 Z=11787.103)
|
||||
"Towers Scraps 9": base_id + 1479, # ItemPickup477_5 Towers_Environment_Details (X=-23667.957 Y=-29825.240 Z=12035.269)
|
||||
"Towers Scraps 10": base_id + 1480, # ItemPickup39_11 Towers_Environment (X=-25361.008 Y=-29794.301 Z=12026.073)
|
||||
"Towers Scraps 11": base_id + 1481, # ItemPickup476_2 Towers_Environment_Details (X=-26549.584 Y=-32768.133 Z=12289.732)
|
||||
"Towers Scraps 12": base_id + 1482, # ItemPickup38_8 Towers_Environment (X=-27240.127 Y=-27404.748 Z=12027.208)
|
||||
"Towers Scraps 13": base_id + 1483, # ItemPickup40_14 Towers_Environment (X=-23231.639 Y=-27799.158 Z=11829.792)
|
||||
"Towers Scraps 14": base_id + 1484, # ItemPickup485_29 Towers_Environment_Details (X=-22949.568 Y=-26146.012 Z=11702.730)
|
||||
"Towers Scraps 15": base_id + 1485, # ItemPickup479_11 Towers_Environment_Details (X=-19726.715 Y=-32464.682 Z=12118.678)
|
||||
"Towers Scraps 16": base_id + 1486, # ItemPickup1366543 Tower_BuildingsExteriorDetails (X=-23495.104 Y=-27644.689 Z=11872.844)
|
||||
"Towers Scraps 17": base_id + 1487, # ItemPickup139978 Tower_BuildingsExteriorDetails (X=-23512.971 Y=-27493.051 Z=12218.543)
|
||||
"Towers Scraps 18": base_id + 1488, # ItemPickup42_20 Towers_Environment (X=-22731.439 Y=-26331.393 Z=12102.758)
|
||||
"Towers Scraps 19": base_id + 1489, # ItemPickup131123 Tower_BuildingsExteriorDetails (X=-22599.641 Y=-26454.590 Z=11752.040)
|
||||
"Towers Scraps 20": base_id + 1490, # ItemPickup196987 Tower_BuildingsExteriorDetails (X=-22589.721 Y=-26397.414 Z=12571.282)
|
||||
"Towers Scraps 21": base_id + 1491, # ItemPickup138787 Tower_BuildingsExteriorDetails (X=-22163.268 Y=-26775.938 Z=13107.048)
|
||||
"Towers Scraps 22": base_id + 1492, # ItemPickup43_23 Towers_Environment (X=-21996.184 Y=-26754.393 Z=13105.997)
|
||||
"Towers Scraps 23": base_id + 1493, # ItemPickup837454 Tower_BuildingsExteriorDetails (X=-24068.221 Y=-27874.443 Z=12819.666)
|
||||
"Towers Scraps 24": base_id + 1494, # ItemPickup44_29 Towers_Environment (X=-23525.330 Y=-27770.035 Z=12612.871)
|
||||
"Towers Scraps 25": base_id + 1495, # ItemPickup18932 Tower_BuildingsExteriorDetails (X=-23472.215 Y=-27617.404 Z=13213.256)
|
||||
"Towers Scraps 26": base_id + 1496, # ItemPickup188348 Tower_BuildingsExteriorDetails (X=-23981.588 Y=-27984.385 Z=13219.854)
|
||||
"Towers Scraps 27": base_id + 1497, # ItemPickup480_14 Towers_Environment_Details (X=-18696.230 Y=-34511.277 Z=12704.238)
|
||||
"Towers Lime Paint Can": base_id + 1498, # PaintCan_2 Towers_BuildingsDetails (X=-22288.555 Y=-26022.281 Z=11835.892) \!/ Existing match 5
|
||||
"Towers Employment Contracts": base_id + 1499, # Towers_Files_Pickup Tower_BuildingsExteriorDetails (X=-24081.414 Y=-27637.459 Z=13679.163)
|
||||
"Towers Ronny Mission End": base_id + 1500 # (dialog 3) -> 1_scraps_reward
|
||||
}
|
||||
|
||||
loc_north_beach = {
|
||||
"North Beach Chest Scraps 1": base_id + 1501, # ItemPickup9254 Secret3_CampDetails (X=-74444.648 Y=-130627.672 Z=8793.757)
|
||||
"North Beach Chest Scraps 2": base_id + 1502, # ItemPickup14523 Secret3_CampDetails (X=-74426.539 Y=-130626.547 Z=8781.948)
|
||||
"North Beach Chest Scraps 3": base_id + 1503, # ItemPickup084537 Secret3_CampDetails (X=-74448.852 Y=-130632.367 Z=8772.555)
|
||||
"North Beach Chest Scraps 4": base_id + 1504, # ItemPickup17754234 Secret3_CampDetails (X=-74425.523 Y=-130622.875 Z=8755.526)
|
||||
"North Beach Scraps 1": base_id + 1505, # ItemPickup376_5 Secret3_CampDetails (X=-75003.078 Y=-131084.016 Z=8729.731)
|
||||
"North Beach Scraps 2": base_id + 1506, # ItemPickup378_11 Secret3_CampDetails (X=-75477.758 Y=-129413.750 Z=9082.617)
|
||||
"North Beach Scraps 3": base_id + 1507, # ItemPickup377_8 Secret3_CampDetails (X=-75608.453 Y=-130483.430 Z=8909.659)
|
||||
"North Beach Scraps 4": base_id + 1508, # ItemPickup375_2 Secret3_CampDetails (X=-74143.469 Y=-131117.953 Z=8752.130)
|
||||
"North Beach Scraps 5": base_id + 1509, # ItemPickup387_6 Secret4_BeachHouseMain (X=-80847.078 Y=-135628.719 Z=7915.444)
|
||||
"North Beach Scraps 6": base_id + 1510, # ItemPickup386_3 Secret4_BeachHouseMain (X=-84044.789 Y=-137591.000 Z=7359.172)
|
||||
"North Beach Scraps 7": base_id + 1511, # ItemPickup379_2 Secret4_BeachHouseDetails (X=-83992.836 Y=-132933.531 Z=7941.225)
|
||||
"North Beach Scraps 8": base_id + 1512, # ItemPickup380_5 Secret4_BeachHouseDetails (X=-87355.734 Y=-134216.438 Z=7237.310)
|
||||
"North Beach Scraps 9": base_id + 1513, # ItemPickup384_17 Secret4_BeachHouseDetails (X=-89213.844 Y=-134625.922 Z=7185.133)
|
||||
"North Beach Scraps 10": base_id + 1514, # ItemPickup25_0 Secret4_BeachHouseDetails (X=-88423.430 Y=-135922.969 Z=7290.801)
|
||||
"North Beach Scraps 11": base_id + 1515, # ItemPickup381_8 Secret4_BeachHouseDetails (X=-87668.977 Y=-136850.359 Z=7275.346)
|
||||
"North Beach Scraps 12": base_id + 1516, # ItemPickup383_14 Secret4_BeachHouseDetails (X=-90241.328 Y=-136381.000 Z=7199.622)
|
||||
"North Beach Scraps 13": base_id + 1517, # ItemPickup27_8 Secret4_BeachHouseDetails (X=-91728.680 Y=-135288.203 Z=7319.699)
|
||||
"North Beach Scraps 14": base_id + 1518, # ItemPickup26_2 Secret4_BeachHouseDetails (X=-88789.039 Y=-135461.719 Z=7305.800)
|
||||
"North Beach Scraps 15": base_id + 1519, # ItemPickup382_11 Secret4_BeachHouseDetails (X=-88572.078 Y=-135970.734 Z=7302.674)
|
||||
"North Beach Teal Paint Can": base_id + 1520 # PaintCan_2 Secret4_BeachHouseDetails (X=-91706.805 Y=-134988.453 Z=7347.827) \!/ Existing match 5
|
||||
# "North Beach Scraps Glitch 1" ItemPickup385_20 (X=-77651.938 Y=-139721.453 Z=7261.729) /!\ Glitched scrap
|
||||
}
|
||||
|
||||
loc_mine_shaft = {
|
||||
"Mine Shaft Chest Scraps 1": base_id + 1521, # ItemPickup0934569 Secret13_Details (X=-17360.789 Y=-74064.367 Z=8038.313)
|
||||
"Mine Shaft Chest Scraps 2": base_id + 1522, # ItemPickup17234455622 Secret13_Details (X=-17360.814 Y=-74064.367 Z=8024.187)
|
||||
"Mine Shaft Chest Scraps 3": base_id + 1523, # ItemPickup856743 Secret13_Details (X=-17361.059 Y=-74049.234 Z=8015.940)
|
||||
"Mine Shaft Chest Scraps 4": base_id + 1524, # ItemPickup16456456 Secret13_Details (X=-17336.465 Y=-74087.063 Z=8009.707)
|
||||
"Mine Shaft Chest Scraps 5": base_id + 1525, # ItemPickup234743 Secret13_Details (X=-17349.941 Y=-74075.484 Z=8004.179)
|
||||
"Mine Shaft Chest Scraps 6": base_id + 1526, # ItemPickup1434563456 Secret13_Details (X=-17361.084 Y=-74049.234 Z=8001.814)
|
||||
"Mine Shaft Chest Scraps 7": base_id + 1527, # ItemPickup13634563456 Secret13_Details (X=-17349.967 Y=-74075.484 Z=7990.054)
|
||||
"Mine Shaft Scraps 1": base_id + 1528, # ItemPickup369_23 Secret13_Details (X=-16985.645 Y=-70377.273 Z=10837.127)
|
||||
"Mine Shaft Scraps 2": base_id + 1529, # ItemPickup368_20 Secret13_Details (X=-18292.045 Y=-71003.563 Z=10975.308)
|
||||
"Mine Shaft Scraps 3": base_id + 1530, # ItemPickup367_17 Secret13_Details (X=-16150.797 Y=-72075.289 Z=10609.141)
|
||||
"Mine Shaft Scraps 4": base_id + 1531, # ItemPickup24456463 Secret13_EntranceDetails1 (X=-18404.480 Y=-71894.367 Z=10968.230)
|
||||
"Mine Shaft Scraps 5": base_id + 1532, # ItemPickup2565644 Secret13_Details (X=-17777.268 Y=-71467.172 Z=10441.745)
|
||||
"Mine Shaft Scraps 6": base_id + 1533, # ItemPickup366_14 Secret13_Details (X=-17630.971 Y=-72800.641 Z=8842.322)
|
||||
"Mine Shaft Scraps 7": base_id + 1534, # ItemPickup18_8 Secret13_Details (X=-17979.719 Y=-73413.563 Z=8118.260)
|
||||
"Mine Shaft Scraps 8": base_id + 1535, # ItemPickup21_11 Secret13_Details (X=-16554.467 Y=-74139.781 Z=7892.738)
|
||||
"Mine Shaft Scraps 9": base_id + 1536, # ItemPickup365_11 Secret13_Details (X=-16343.033 Y=-74617.555 Z=7298.177)
|
||||
"Mine Shaft Scraps 10": base_id + 1537, # ItemPickup22123 Secret13_Details (X=-12390.332 Y=-77620.320 Z=7324.504)
|
||||
"Mine Shaft Scraps 11": base_id + 1538, # ItemPickup364_8 Secret13_Details (X=-10969.702 Y=-77961.477 Z=7297.171)
|
||||
"Mine Shaft Scraps 12": base_id + 1539, # ItemPickup363_5 Secret13_Details (X=-9888.596 Y=-78208.930 Z=7269.473)
|
||||
"Mine Shaft Scraps 13": base_id + 1540, # ItemPickup362_2 Secret13_Details (X=-8865.696 Y=-79063.977 Z=7247.677)
|
||||
"Mine Shaft Scraps 14": base_id + 1541 # ItemPickup238976 Secret13_ExitDetails (X=-8143.984 Y=-79764.617 Z=7222.096)
|
||||
}
|
||||
|
||||
loc_mob_camp = {
|
||||
"Mob Camp Key": base_id + 1542, # ItemPickup29_0 Bob_CampDetails2 (X=-29114.480 Y=-53608.520 Z=12839.528)
|
||||
"Mob Camp Scraps 1": base_id + 1543, # ItemPickup25_5 Bob_CampDetails2 (X=-27373.525 Y=-53008.668 Z=12706.465)
|
||||
"Mob Camp Scraps 2": base_id + 1544, # ItemPickup26_1 Bob_CampDetails2 (X=-28786.941 Y=-53009.762 Z=12760.727)
|
||||
"Mob Camp Scraps 3": base_id + 1545, # ItemPickup13_14 Mine3_Mountain (X=-29650.207 Y=-53328.070 Z=12724.598)
|
||||
"Mob Camp Scraps 4": base_id + 1546, # ItemPickup90_5 Bob_CampDetails2 (X=-31515.057 Y=-55533.324 Z=12344.274)
|
||||
"Mob Camp Scraps 5": base_id + 1547, # ItemPickup49513294 Mine3_Mountain (X=-31847.229 Y=-55152.352 Z=11574.255)
|
||||
"Mob Camp Scraps 6": base_id + 1548, # ItemPickup92_14 Bob_CampDetails2 (X=-31323.533 Y=-55432.871 Z=11245.139)
|
||||
"Mob Camp Scraps 7": base_id + 1549, # ItemPickup91_8 Bob_CampDetails2 (X=-31583.443 Y=-55475.805 Z=11234.112)
|
||||
"Mob Camp Scraps 8": base_id + 1550, # ItemPickup95_23 Bob_CampDetails2 (X=-32925.406 Y=-57157.605 Z=11086.322)
|
||||
"Mob Camp Scraps 9": base_id + 1551, # ItemPickup96_26 Bob_CampDetails2 (X=-33052.488 Y=-58560.098 Z=11101.021)
|
||||
"Mob Camp Scraps 10": base_id + 1552, # ItemPickup13121212 Mine3_Mountain (X=-32422.406 Y=-60145.063 Z=11203.182)
|
||||
"Mob Camp Scraps 11": base_id + 1553, # ItemPickup93_17 Bob_CampDetails2 (X=-30891.457 Y=-60046.465 Z=11237.567)
|
||||
"Mob Camp Scraps 12": base_id + 1554, # ItemPickup97_29 Bob_CampDetails2 (X=-31888.428 Y=-59222.645 Z=11179.302)
|
||||
"Mob Camp Scraps 13": base_id + 1555, # ItemPickup62156 Mine3_Mountain (X=-31161.750 Y=-57410.789 Z=11279.820)
|
||||
"Mob Camp Scraps 14": base_id + 1556, # ItemPickup24_3 Bob_CampDetails2 (X=-31256.545 Y=-59865.809 Z=11904.155)
|
||||
"Mob Camp Scraps 15": base_id + 1557, # ItemPickup27_0 Bob_CampDetails2 (X=-31757.953 Y=-57179.258 Z=11295.150)
|
||||
"Mob Camp Scraps 16": base_id + 1558 # ItemPickup89_2 Bob_CampDetails2 (X=-29137.043 Y=-54824.797 Z=12418.673)
|
||||
}
|
||||
|
||||
loc_mob_camp_locked_room = {
|
||||
"Mob Camp Locked Room Scraps 1": base_id + 1559, # ItemPickup94_20 Bob_CampDetails2 (X=-31736.459 Y=-59761.465 Z=11211.379)
|
||||
"Mob Camp Locked Room Scraps 2": base_id + 1560, # ItemPickup28_14 Bob_CampDetails2 (X=-32150.889 Y=-59879.594 Z=11297.058)
|
||||
"Mob Camp Locked Room Stolen Bob": base_id + 1561 # Bob_Clickbox (X=-31771.172 Y=-59892.449 Z=11323.562)
|
||||
}
|
||||
|
||||
loc_mine_elevator_exit = {
|
||||
"Mine Elevator Exit Scraps 1": base_id + 1562, # ItemPickup266_19 Mine3_ExitCampDetails (X=-29587.271 Y=-42650.797 Z=12515.042)
|
||||
"Mine Elevator Exit Scraps 2": base_id + 1563, # ItemPickup265_16 Mine3_ExitCampDetails (X=-30727.555 Y=-42715.438 Z=12485.763)
|
||||
"Mine Elevator Exit Scraps 3": base_id + 1564, # ItemPickup267_22 Mine3_ExitCampDetails (X=-29814.680 Y=-43722.777 Z=12518.878)
|
||||
"Mine Elevator Exit Scraps 4": base_id + 1565, # ItemPickup261_2 Mine3_ExitCampDetails (X=-30983.000 Y=-42943.754 Z=12474.396)
|
||||
"Mine Elevator Exit Scraps 5": base_id + 1566, # ItemPickup262_5 Mine3_ExitCampDetails (X=-31824.576 Y=-43997.270 Z=12345.908)
|
||||
"Mine Elevator Exit Scraps 6": base_id + 1567, # ItemPickup268_25 Mine3_ExitCampDetails (X=-32553.924 Y=-44761.855 Z=12341.698)
|
||||
"Mine Elevator Exit Scraps 7": base_id + 1568, # ItemPickup263_10 Mine3_ExitCampDetails (X=-33598.023 Y=-44369.297 Z=12438.430)
|
||||
"Mine Elevator Exit Scraps 8": base_id + 1569, # ItemPickup264_13 Mine3_ExitCampDetails (X=-31947.459 Y=-42017.137 Z=12384.311)
|
||||
"Mine Elevator Exit Scraps 9": base_id + 1570 # ItemPickup269_28 Mine3_ExitCampDetails (X=-30127.123 Y=-46004.891 Z=12229.104)
|
||||
}
|
||||
|
||||
loc_mountain_ruin_outside = {
|
||||
"Mountain Ruin Outside Scraps 1": base_id + 1571, # ItemPickup112345556 Mine3_Outside (X=-2218.456 Y=-43914.789 Z=11238.952)
|
||||
"Mountain Ruin Outside Scraps 2": base_id + 1572, # ItemPickup35621 Mine3_Outside (X=-2402.206 Y=-45494.766 Z=11244.650)
|
||||
"Mountain Ruin Outside Scraps 3": base_id + 1573, # ItemPickup13000 Mine3_Outside (X=-2781.385 Y=-47365.313 Z=11235.389)
|
||||
"Mountain Ruin Outside Scraps 4": base_id + 1574, # ItemPickup995959 Mine3_Outside (X=-2961.431 Y=-45445.973 Z=11255.289)
|
||||
"Mountain Ruin Outside Scraps 5": base_id + 1575, # ItemPickup136999 Mine3_Outside (X=-5873.435 Y=-46300.633 Z=11228.667)
|
||||
"Mountain Ruin Outside Scraps 6": base_id + 1576, # ItemPickup1589994 Mine3_Outside (X=-1823.357 Y=-47071.813 Z=11161.523)
|
||||
"Mountain Ruin Outside Scraps 7": base_id + 1577, # ItemPickup09871230948 Mine3_Outside (X=-3478.155 Y=-49094.965 Z=11083.230)
|
||||
"Mountain Ruin Outside Scraps 8": base_id + 1578, # ItemPickup258_20 Mine3_Camp (X=-4606.678 Y=-50246.180 Z=11613.938)
|
||||
"Mountain Ruin Outside Scraps 9": base_id + 1579, # ItemPickup257_17 Mine3_Camp (X=-5454.638 Y=-53508.004 Z=12062.944)
|
||||
"Mountain Ruin Outside Scraps 10": base_id + 1580, # ItemPickup18_3 Mine3_Camp (X=-8192.042 Y=-53726.535 Z=11947.394)
|
||||
"Mountain Ruin Outside Scraps 11": base_id + 1581, # ItemPickup252_2 Mine3_Camp (X=-9409.834 Y=-53970.621 Z=11923.256)
|
||||
"Mountain Ruin Outside Scraps 12": base_id + 1582, # ItemPickup254_8 Mine3_Camp (X=-8977.424 Y=-57134.637 Z=12232.596)
|
||||
"Mountain Ruin Outside Scraps 13": base_id + 1583, # ItemPickup19_1 Mine3_Camp (X=-10449.292 Y=-56481.938 Z=12274.706)
|
||||
"Mountain Ruin Outside Scraps 14": base_id + 1584, # ItemPickup255_11 Mine3_Camp (X=-10490.690 Y=-56584.301 Z=12281.096)
|
||||
"Mountain Ruin Outside Scraps 15": base_id + 1585, # ItemPickup256_14 Mine3_Camp (X=-11600.937 Y=-55831.191 Z=12388.329)
|
||||
"Mountain Ruin Outside Scraps 16": base_id + 1586, # ItemPickup259_23 Mine3_Camp (X=-3307.077 Y=-49973.461 Z=11282.041)
|
||||
"Mountain Ruin Outside Scraps 17": base_id + 1587 # ItemPickup260_26 Mine3_Camp (X=-8878.345 Y=-49816.922 Z=13700.768)
|
||||
}
|
||||
|
||||
loc_mountain_ruin_inside = {
|
||||
"Mountain Ruin Inside Scraps 1": base_id + 1588, # ItemPickup253_5 Mine3_Camp (X=-10647.446 Y=-52039.848 Z=11925.344)
|
||||
"Mountain Ruin Inside Scraps 2": base_id + 1589, # ItemPickup270_2 Mine3_Interior1 (X=-12834.990 Y=-48683.176 Z=10200.083)
|
||||
"Mountain Ruin Inside Scraps 3": base_id + 1590, # ItemPickup271_5 Mine3_Interior1 (X=-15748.594 Y=-44925.523 Z=10199.975)
|
||||
"Mountain Ruin Inside Scraps 4": base_id + 1591, # ItemPickup20_3 Mine3_Interior1 (X=-15312.152 Y=-43957.055 Z=10350.783)
|
||||
"Mountain Ruin Inside Scraps 5": base_id + 1592, # ItemPickup273_11 Mine3_Interior1 (X=-16709.586 Y=-44997.316 Z=10198.888)
|
||||
"Mountain Ruin Inside Scraps 6": base_id + 1593, # ItemPickup272_8 Mine3_Interior1 (X=-17412.561 Y=-46041.977 Z=10349.650)
|
||||
"Mountain Ruin Inside Scraps 7": base_id + 1594, # ItemPickup274_14 Mine3_Interior1 (X=-17452.596 Y=-43804.941 Z=10201.436)
|
||||
"Mountain Ruin Inside Scraps 8": base_id + 1595, # ItemPickup21_2 Mine3_Interior1 (X=-18119.473 Y=-42001.453 Z=10198.855)
|
||||
"Mountain Ruin Inside Scraps 9": base_id + 1596, # ItemPickup276_20 Mine3_Interior1 (X=-18975.068 Y=-42906.488 Z=10279.690)
|
||||
"Mountain Ruin Inside Scraps 10": base_id + 1597, # ItemPickup277_26 Mine3_Interior1 (X=-19727.902 Y=-41581.039 Z=10199.614)
|
||||
"Mountain Ruin Inside Scraps 11": base_id + 1598, # ItemPickup275_17 Mine3_Interior1 (X=-19187.891 Y=-40516.941 Z=10201.049)
|
||||
"Mountain Ruin Inside Scraps 12": base_id + 1599, # ItemPickup278_29 Mine3_Interior1 (X=-22470.986 Y=-41602.875 Z=10073.847)
|
||||
"Mountain Ruin Inside Scraps 13": base_id + 1600, # ItemPickup279_2 Mine3_Interior2 (X=-23717.383 Y=-42597.141 Z=7455.452)
|
||||
"Mountain Ruin Inside Scraps 14": base_id + 1601, # ItemPickup22_7 Mine3_Interior2 (X=-24494.582 Y=-44598.086 Z=7433.633)
|
||||
"Mountain Ruin Inside Scraps 15": base_id + 1602, # ItemPickup280_5 Mine3_Interior2 (X=-26783.293 Y=-42331.145 Z=7446.357)
|
||||
"Mountain Ruin Inside Scraps 16": base_id + 1603, # ItemPickup281_8 Mine3_Interior2 (X=-28636.996 Y=-46745.340 Z=7430.463)
|
||||
"Mountain Ruin Inside Scraps 17": base_id + 1604, # ItemPickup23_1 Mine3_Interior2 (X=-27752.643 Y=-47453.973 Z=7445.047)
|
||||
"Mountain Ruin Inside Red Egg": base_id + 1605, # Mine3_EggPickup Mine3_Interior2 (X=-28254.400 Y=-45702.844 Z=7551.568)
|
||||
"Mountain Ruin Inside Red Paint Can": base_id + 1606 # PaintCan_2 Mine3_Interior2 (X=-31391.293 Y=-44953.223 Z=7448.648) \!/ Existing match 5
|
||||
}
|
||||
|
||||
loc_pickle_val = {
|
||||
"Pickle Val Scraps 1": base_id + 1607, # ItemPickup56_2 Pickles_HouseDetails (X=60402.582 Y=25252.434 Z=11151.982)
|
||||
"Pickle Val Scraps 2": base_id + 1608, # ItemPickup57_5 Pickles_HouseDetails (X=58717.516 Y=24457.180 Z=11343.938)
|
||||
"Pickle Val Scraps 3": base_id + 1609, # ItemPickup311_14 Pickles_EnvironmentDetails (X=56455.688 Y=22324.875 Z=11655.192)
|
||||
"Pickle Val Scraps 4": base_id + 1610, # ItemPickup310_11 Pickles_EnvironmentDetails (X=52888.387 Y=22541.955 Z=12428.012)
|
||||
"Pickle Val Scraps 5": base_id + 1611, # ItemPickup58_2 Pickles_EnvironmentDetails (X=50374.863 Y=22073.027 Z=12591.468)
|
||||
"Pickle Val Scraps 6": base_id + 1612, # ItemPickup309_8 Pickles_EnvironmentDetails (X=45985.988 Y=23063.826 Z=13043.497)
|
||||
"Pickle Val Scraps 7": base_id + 1613, # ItemPickup316_27 Pickles_EnvironmentDetails (X=45533.469 Y=24876.168 Z=13015.539)
|
||||
"Pickle Val Scraps 8": base_id + 1614, # ItemPickup317_30 Pickles_EnvironmentDetails (X=44928.848 Y=30913.396 Z=14273.230)
|
||||
"Pickle Val Scraps 9": base_id + 1615, # ItemPickup321_42 Pickles_EnvironmentDetails (X=41572.684 Y=37403.539 Z=13527.379)
|
||||
"Pickle Val Scraps 10": base_id + 1616, # ItemPickup61_11 Pickles_EnvironmentDetails (X=42266.566 Y=30556.238 Z=13584.196)
|
||||
"Pickle Val Scraps 11": base_id + 1617, # ItemPickup314_21 Pickles_EnvironmentDetails (X=43356.219 Y=29465.746 Z=13449.486)
|
||||
"Pickle Val Scraps 12": base_id + 1618, # ItemPickup323_48 Pickles_EnvironmentDetails (X=40893.461 Y=30669.398 Z=13465.039)
|
||||
"Pickle Val Scraps 13": base_id + 1619, # ItemPickup318_33 Pickles_EnvironmentDetails (X=41276.863 Y=32313.068 Z=13480.846)
|
||||
"Pickle Val Scraps 14": base_id + 1620, # ItemPickup319_36 Pickles_EnvironmentDetails (X=38630.031 Y=30471.996 Z=13571.207)
|
||||
"Pickle Val Scraps 15": base_id + 1621, # ItemPickup320_39 Pickles_EnvironmentDetails (X=38596.938 Y=30050.293 Z=13559.574)
|
||||
"Pickle Val Scraps 16": base_id + 1622, # ItemPickup60_8 Pickles_EnvironmentDetails (X=42042.520 Y=25136.820 Z=13077.339)
|
||||
"Pickle Val Scraps 17": base_id + 1623, # ItemPickup308_5 Pickles_EnvironmentDetails (X=43770.914 Y=23307.234 Z=12374.002)
|
||||
"Pickle Val Scraps 18": base_id + 1624, # ItemPickup59_5 Pickles_EnvironmentDetails (X=44672.641 Y=20936.520 Z=12198.017)
|
||||
"Pickle Val Scraps 19": base_id + 1625, # ItemPickup307_2 Pickles_EnvironmentDetails (X=42844.730 Y=22869.387 Z=12832.900)
|
||||
"Pickle Val Scraps 20": base_id + 1626, # ItemPickup315_24 Pickles_EnvironmentDetails (X=43397.758 Y=26367.248 Z=13005.097)
|
||||
"Pickle Val Scraps 21": base_id + 1627, # ItemPickup322_45 Pickles_EnvironmentDetails (X=39352.430 Y=32668.316 Z=14494.778)
|
||||
"Pickle Val Scraps 22": base_id + 1628, # ItemPickup313 Pickles_EnvironmentDetails (X=59688.781 Y=25266.756 Z=11425.324)
|
||||
"Pickle Val Scraps 23": base_id + 1629, # ItemPickup312_17 Pickles_EnvironmentDetails (X=59197.871 Y=24760.773 Z=11425.324)
|
||||
"Pickle Val Purple Paint Can": base_id + 1630, # PaintCan_7 Pickles_EnvironmentDetails (X=40394.855 Y=31546.465 Z=13472.573)
|
||||
"Pickle Val Jar of Pickles": base_id + 1631, # Pickles_Pickup Pickles_Cave (X=38227.535 Y=30234.848 Z=13593.506)
|
||||
"Pickle Val Pickle Lady Mission End": base_id + 1632 # (dialog 4) -> 30_scraps_reward
|
||||
}
|
||||
|
||||
loc_shrine_near_temple = {
|
||||
"Shrine Near Temple Scraps 1": base_id + 1633, # ItemPickup3 Photorealistic_Island (X=-4675.183 Y=-21143.846 Z=11984.782)
|
||||
"Shrine Near Temple Scraps 2": base_id + 1634, # ItemPickup_2 Photorealistic_Island (X=-4994.117 Y=-20496.621 Z=11874.112)
|
||||
"Shrine Near Temple Scraps 3": base_id + 1635 # ItemPickup2 Photorealistic_Island (X=-4196.896 Y=-20477.025 Z=11882.833)
|
||||
}
|
||||
|
||||
loc_morse_bunker = {
|
||||
"Morse Bunker Chest Scraps 1": base_id + 1636, # ItemPickup6512 Secret6_BunkerDetails (X=-47961.078 Y=-85433.031 Z=10615.777)
|
||||
"Morse Bunker Chest Scraps 2": base_id + 1637, # ItemPickup9363 Secret6_BunkerDetails (X=-47958.617 Y=-85444.805 Z=10604.365)
|
||||
"Morse Bunker Chest Scraps 3": base_id + 1638, # ItemPickup921436 Secret6_BunkerDetails (X=-47953.637 Y=-85426.172 Z=10592.347)
|
||||
"Morse Bunker Chest Scraps 4": base_id + 1639, # ItemPickup0128704 Secret6_BunkerDetails (X=-47958.691 Y=-85444.805 Z=10587.060)
|
||||
"Morse Bunker Chest Scraps 5": base_id + 1640, # ItemPickup64_8 Secret6_BunkerDetails (X=-47953.617 Y=-85426.172 Z=10574.149)
|
||||
"Morse Bunker Scraps 1": base_id + 1641, # ItemPickup397_2 Secret6_BunkerDetails (X=-48336.863 Y=-85458.453 Z=10574.537)
|
||||
"Morse Bunker Scraps 2": base_id + 1642, # ItemPickup62_2 Secret6_BunkerDetails (X=-48253.844 Y=-84944.609 Z=10573.793)
|
||||
"Morse Bunker Scraps 3": base_id + 1643, # ItemPickup63_5 Secret6_BunkerDetails (X=-47272.422 Y=-84549.945 Z=10543.671)
|
||||
"Morse Bunker Scraps 4": base_id + 1644, # ItemPickup398_5 Secret6_BunkerDetails (X=-47080.836 Y=-85268.281 Z=10564.355)
|
||||
"Morse Bunker Scraps 5": base_id + 1645, # ItemPickup406_31 Secret6_BunkerDetails (X=-47913.855 Y=-85234.258 Z=10819.314)
|
||||
"Morse Bunker Scraps 6": base_id + 1646, # ItemPickup405_28 Secret6_BunkerDetails (X=-46410.227 Y=-83293.742 Z=10361.746)
|
||||
"Morse Bunker Scraps 7": base_id + 1647, # ItemPickup399_8 Secret6_BunkerDetails (X=-45204.199 Y=-84841.211 Z=10329.200)
|
||||
"Morse Bunker Scraps 8": base_id + 1648, # ItemPickup401_14 Secret6_BunkerDetails (X=-43895.801 Y=-83794.750 Z=10265.661)
|
||||
"Morse Bunker Scraps 9": base_id + 1649, # ItemPickup402_17 Secret6_BunkerDetails (X=-45163.078 Y=-80832.828 Z=10090.777)
|
||||
"Morse Bunker Scraps 10": base_id + 1650, # ItemPickup403_20 Secret6_BunkerDetails (X=-46782.027 Y=-82284.805 Z=10289.180)
|
||||
"Morse Bunker Scraps 11": base_id + 1651, # ItemPickup404_23 Secret6_BunkerDetails (X=-49240.047 Y=-83654.961 Z=10898.540)
|
||||
"Morse Bunker Scraps 12": base_id + 1652, # ItemPickup407_34 Secret6_BunkerDetails (X=-46571.930 Y=-85962.773 Z=10697.339)
|
||||
"Morse Bunker Scraps 13": base_id + 1653 # ItemPickup400_11 Secret6_BunkerDetails (X=-43472.793 Y=-85983.805 Z=10310.322)
|
||||
}
|
||||
|
||||
loc_prism_temple = {
|
||||
"Prism Temple Chest Scraps 1": base_id + 1654, # ItemPickup16632 MainShrine_DetailsREPAIRED (X=12659.641 Y=-27827.016 Z=10930.621)
|
||||
"Prism Temple Chest Scraps 2": base_id + 1655, # ItemPickup148864 MainShrine_DetailsREPAIRED (X=12648.021 Y=-27825.189 Z=10916.296)
|
||||
"Prism Temple Chest Scraps 3": base_id + 1656, # ItemPickup13123 MainShrine_DetailsREPAIRED (X=12665.557 Y=-27836.633 Z=10905.999)
|
||||
"Prism Temple Scraps 1": base_id + 1657, # ItemPickup225_77 MainShrine_ExteriorDetails (X=5281.087 Y=-16620.387 Z=10520.609)
|
||||
"Prism Temple Scraps 2": base_id + 1658, # ItemPickup223_71 MainShrine_ExteriorDetails (X=15032.147 Y=-16788.352 Z=10992.200)
|
||||
"Prism Temple Scraps 3": base_id + 1659, # ItemPickup222_68 MainShrine_ExteriorDetails (X=16591.920 Y=-18725.771 Z=11004.751)
|
||||
"Prism Temple Scraps 4": base_id + 1660, # ItemPickup135123 MainShrine_DetailsREPAIRED (X=17940.854 Y=-20726.746 Z=10999.944)
|
||||
"Prism Temple Scraps 5": base_id + 1661, # ItemPickup220_62 MainShrine_ExteriorDetails (X=18187.346 Y=-21390.066 Z=11025.650)
|
||||
"Prism Temple Scraps 6": base_id + 1662, # ItemPickup218_56 MainShrine_ExteriorDetails (X=19218.670 Y=-24017.619 Z=10906.966)
|
||||
"Prism Temple Scraps 7": base_id + 1663, # ItemPickup209_29 MainShrine_ExteriorDetails (X=7710.977 Y=-23469.254 Z=11012.888)
|
||||
"Prism Temple Scraps 8": base_id + 1664, # ItemPickup138765 MainShrine_DetailsREPAIRED (X=8160.002 Y=-22335.266 Z=11016.638)
|
||||
"Prism Temple Scraps 9": base_id + 1665, # ItemPickup210_32 MainShrine_ExteriorDetails (X=8637.903 Y=-24295.850 Z=10929.299)
|
||||
"Prism Temple Scraps 10": base_id + 1666, # ItemPickup1112 MainShrine_DetailsREPAIRED (X=7923.367 Y=-25204.055 Z=10832.877)
|
||||
"Prism Temple Scraps 11": base_id + 1667, # ItemPickup214_44 MainShrine_ExteriorDetails (X=10694.420 Y=-27246.365 Z=10569.074)
|
||||
"Prism Temple Scraps 12": base_id + 1668, # ItemPickup215_47 MainShrine_ExteriorDetails (X=12892.631 Y=-26743.068 Z=10868.578)
|
||||
"Prism Temple Scraps 13": base_id + 1669, # ItemPickup213_41 MainShrine_ExteriorDetails (X=12483.535 Y=-27253.867 Z=10876.461)
|
||||
"Prism Temple Scraps 14": base_id + 1670, # ItemPickup212_38 MainShrine_ExteriorDetails (X=13259.813 Y=-27092.033 Z=10887.968)
|
||||
"Prism Temple Scraps 15": base_id + 1671, # ItemPickup211_35 MainShrine_ExteriorDetails (X=9303.245 Y=-25057.908 Z=10943.273)
|
||||
"Prism Temple Scraps 16": base_id + 1672, # ItemPickup201_2 MainShrine_ExteriorDetails (X=11750.875 Y=-22999.371 Z=12426.734)
|
||||
"Prism Temple Scraps 17": base_id + 1673, # ItemPickup203_8 MainShrine_ExteriorDetails (X=12966.615 Y=-23568.324 Z=12519.387)
|
||||
"Prism Temple Scraps 18": base_id + 1674, # ItemPickup204_11 MainShrine_ExteriorDetails (X=14093.343 Y=-22393.182 Z=12426.847)
|
||||
"Prism Temple Scraps 19": base_id + 1675, # ItemPickup206_17 MainShrine_ExteriorDetails (X=13767.980 Y=-21269.568 Z=12425.791)
|
||||
"Prism Temple Scraps 20": base_id + 1676, # ItemPickup207_23 MainShrine_ExteriorDetails (X=12882.754 Y=-20027.527 Z=12516.095)
|
||||
"Prism Temple Scraps 21": base_id + 1677, # ItemPickup114535 MainShrine_DetailsREPAIRED (X=11883.399 Y=-20801.344 Z=12428.452)
|
||||
"Prism Temple Scraps 22": base_id + 1678, # ItemPickup208_26 MainShrine_ExteriorDetails (X=13281.792 Y=-21302.344 Z=12902.728)
|
||||
"Prism Temple Scraps 23": base_id + 1679, # ItemPickup205_14 MainShrine_ExteriorDetails (X=14190.678 Y=-23671.621 Z=11781.533)
|
||||
"Prism Temple Scraps 24": base_id + 1680, # ItemPickup8903 MainShrine_DetailsREPAIRED (X=14311.736 Y=-22347.758 Z=11765.781)
|
||||
"Prism Temple Scraps 25": base_id + 1681, # ItemPickup654 MainShrine_DetailsREPAIRED (X=13826.154 Y=-19923.131 Z=11755.189)
|
||||
"Prism Temple Scraps 26": base_id + 1682, # ItemPickup224_74 MainShrine_ExteriorDetails (X=12443.228 Y=-18577.926 Z=11240.455)
|
||||
"Prism Temple Scraps 27": base_id + 1683, # ItemPickup202_5 MainShrine_ExteriorDetails (X=10993.180 Y=-23783.047 Z=11754.121)
|
||||
"Prism Temple Scraps 28": base_id + 1684, # ItemPickup13098 MainShrine_DetailsREPAIRED (X=16762.963 Y=-23634.342 Z=11031.588)
|
||||
"Prism Temple Scraps 29": base_id + 1685, # ItemPickup221_65 MainShrine_ExteriorDetails (X=17804.979 Y=-23512.395 Z=11066.884)
|
||||
"Prism Temple Scraps 30": base_id + 1686, # ItemPickup17123123565 MainShrine_DetailsREPAIRED (X=16998.229 Y=-23241.652 Z=11055.539)
|
||||
"Prism Temple Scraps 31": base_id + 1687, # ItemPickup10812783 MainShrine_DetailsREPAIRED (X=17613.518 Y=-23799.813 Z=11057.277)
|
||||
"Prism Temple Scraps 32": base_id + 1688, # ItemPickup216_50 MainShrine_ExteriorDetails (X=15342.375 Y=-24807.357 Z=11024.192)
|
||||
"Prism Temple Scraps 33": base_id + 1689, # ItemPickup217_53 MainShrine_ExteriorDetails (X=15963.284 Y=-24834.156 Z=11021.444)
|
||||
"Prism Temple Scraps 34": base_id + 1690 # ItemPickup219_59 MainShrine_ExteriorDetails (X=21563.559 Y=-23705.184 Z=10895.696)
|
||||
}
|
||||
|
||||
|
||||
# All locations
|
||||
location_table: dict[str, int] = {
|
||||
**loc_start_camp,
|
||||
**loc_tony_tiddle_mission,
|
||||
**loc_barn,
|
||||
**loc_candice_mission,
|
||||
**loc_tutorial_house,
|
||||
**loc_swamp_edges,
|
||||
**loc_swamp_mission,
|
||||
**loc_junkyard_area,
|
||||
**loc_south_house,
|
||||
**loc_junkyard_shed,
|
||||
**loc_military_base,
|
||||
**loc_south_mine_outside,
|
||||
**loc_south_mine_inside,
|
||||
**loc_middle_station,
|
||||
**loc_canyon,
|
||||
**loc_watchtower,
|
||||
**loc_boulder_field,
|
||||
**loc_haunted_house,
|
||||
**loc_santiago_house,
|
||||
**loc_port,
|
||||
**loc_trench_house,
|
||||
**loc_doll_woods,
|
||||
**loc_lost_stairs,
|
||||
**loc_east_house,
|
||||
**loc_rockets_testing_ground,
|
||||
**loc_rockets_testing_bunker,
|
||||
**loc_workshop,
|
||||
**loc_east_tower,
|
||||
**loc_lighthouse,
|
||||
**loc_north_mine_outside,
|
||||
**loc_north_mine_inside,
|
||||
**loc_wood_bridge,
|
||||
**loc_museum,
|
||||
**loc_barbed_shelter,
|
||||
**loc_west_beach,
|
||||
**loc_church,
|
||||
**loc_west_cottage,
|
||||
**loc_caravan,
|
||||
**loc_trailer_cabin,
|
||||
**loc_towers,
|
||||
**loc_north_beach,
|
||||
**loc_mine_shaft,
|
||||
**loc_mob_camp,
|
||||
**loc_mob_camp_locked_room,
|
||||
**loc_mine_elevator_exit,
|
||||
**loc_mountain_ruin_outside,
|
||||
**loc_mountain_ruin_inside,
|
||||
**loc_prism_temple,
|
||||
**loc_pickle_val,
|
||||
**loc_shrine_near_temple,
|
||||
**loc_morse_bunker
|
||||
}
|
||||
7
worlds/cccharles/Options.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
from Options import PerGameCommonOptions, StartInventoryPool
|
||||
|
||||
|
||||
@dataclass
|
||||
class CCCharlesOptions(PerGameCommonOptions):
|
||||
start_inventory_from_pool: StartInventoryPool
|
||||
283
worlds/cccharles/Regions.py
Normal file
@@ -0,0 +1,283 @@
|
||||
from BaseClasses import MultiWorld, Region, ItemClassification
|
||||
from .Items import CCCharlesItem
|
||||
from .Options import CCCharlesOptions
|
||||
from .Locations import (
|
||||
CCCharlesLocation, loc_start_camp, loc_tony_tiddle_mission, loc_barn, loc_candice_mission, \
|
||||
loc_tutorial_house, loc_swamp_edges, loc_swamp_mission, loc_junkyard_area, loc_south_house, \
|
||||
loc_junkyard_shed, loc_military_base, loc_south_mine_outside, loc_south_mine_inside, \
|
||||
loc_middle_station, loc_canyon, loc_watchtower, loc_boulder_field, loc_haunted_house, \
|
||||
loc_santiago_house, loc_port, loc_trench_house, loc_doll_woods, loc_lost_stairs, loc_east_house, \
|
||||
loc_rockets_testing_ground, loc_rockets_testing_bunker, loc_workshop, loc_east_tower, \
|
||||
loc_lighthouse, loc_north_mine_outside, loc_north_mine_inside, loc_wood_bridge, loc_museum, \
|
||||
loc_barbed_shelter, loc_west_beach, loc_church, loc_west_cottage, loc_caravan, loc_trailer_cabin, \
|
||||
loc_towers, loc_north_beach, loc_mine_shaft, loc_mob_camp, loc_mob_camp_locked_room, \
|
||||
loc_mine_elevator_exit, loc_mountain_ruin_outside, loc_mountain_ruin_inside, loc_prism_temple, \
|
||||
loc_pickle_val, loc_shrine_near_temple, loc_morse_bunker
|
||||
)
|
||||
|
||||
|
||||
def create_regions(world: MultiWorld, options: CCCharlesOptions, player: int) -> None:
|
||||
menu_region = Region("Menu", player, world, "Aranearum")
|
||||
world.regions.append(menu_region)
|
||||
|
||||
start_camp_region = Region("Start Camp", player, world)
|
||||
start_camp_region.add_locations(loc_start_camp, CCCharlesLocation)
|
||||
world.regions.append(start_camp_region)
|
||||
|
||||
tony_tiddle_mission_region = Region("Tony Tiddle Mission", player, world)
|
||||
tony_tiddle_mission_region.add_locations(loc_tony_tiddle_mission, CCCharlesLocation)
|
||||
world.regions.append(tony_tiddle_mission_region)
|
||||
|
||||
barn_region = Region("Barn", player, world)
|
||||
barn_region.add_locations(loc_barn, CCCharlesLocation)
|
||||
world.regions.append(barn_region)
|
||||
|
||||
candice_mission_region = Region("Candice Mission", player, world)
|
||||
candice_mission_region.add_locations(loc_candice_mission, CCCharlesLocation)
|
||||
world.regions.append(candice_mission_region)
|
||||
|
||||
tutorial_house_region = Region("Tutorial House", player, world)
|
||||
tutorial_house_region.add_locations(loc_tutorial_house, CCCharlesLocation)
|
||||
world.regions.append(tutorial_house_region)
|
||||
|
||||
swamp_edges_region = Region("Swamp Edges", player, world)
|
||||
swamp_edges_region.add_locations(loc_swamp_edges, CCCharlesLocation)
|
||||
world.regions.append(swamp_edges_region)
|
||||
|
||||
swamp_mission_region = Region("Swamp Mission", player, world)
|
||||
swamp_mission_region.add_locations(loc_swamp_mission, CCCharlesLocation)
|
||||
world.regions.append(swamp_mission_region)
|
||||
|
||||
junkyard_area_region = Region("Junkyard Area", player, world)
|
||||
junkyard_area_region.add_locations(loc_junkyard_area, CCCharlesLocation)
|
||||
world.regions.append(junkyard_area_region)
|
||||
|
||||
south_house_region = Region("South House", player, world)
|
||||
south_house_region.add_locations(loc_south_house, CCCharlesLocation)
|
||||
world.regions.append(south_house_region)
|
||||
|
||||
junkyard_shed_region = Region("Junkyard Shed", player, world)
|
||||
junkyard_shed_region.add_locations(loc_junkyard_shed, CCCharlesLocation)
|
||||
world.regions.append(junkyard_shed_region)
|
||||
|
||||
military_base_region = Region("Military Base", player, world)
|
||||
military_base_region.add_locations(loc_military_base, CCCharlesLocation)
|
||||
world.regions.append(military_base_region)
|
||||
|
||||
south_mine_outside_region = Region("South Mine Outside", player, world)
|
||||
south_mine_outside_region.add_locations(loc_south_mine_outside, CCCharlesLocation)
|
||||
world.regions.append(south_mine_outside_region)
|
||||
|
||||
south_mine_inside_region = Region("South Mine Inside", player, world)
|
||||
south_mine_inside_region.add_locations(loc_south_mine_inside, CCCharlesLocation)
|
||||
world.regions.append(south_mine_inside_region)
|
||||
|
||||
middle_station_region = Region("Middle Station", player, world)
|
||||
middle_station_region.add_locations(loc_middle_station, CCCharlesLocation)
|
||||
world.regions.append(middle_station_region)
|
||||
|
||||
canyon_region = Region("Canyon", player, world)
|
||||
canyon_region.add_locations(loc_canyon, CCCharlesLocation)
|
||||
world.regions.append(canyon_region)
|
||||
|
||||
watchtower_region = Region("Watchtower", player, world)
|
||||
watchtower_region.add_locations(loc_watchtower, CCCharlesLocation)
|
||||
world.regions.append(watchtower_region)
|
||||
|
||||
boulder_field_region = Region("Boulder Field", player, world)
|
||||
boulder_field_region.add_locations(loc_boulder_field, CCCharlesLocation)
|
||||
world.regions.append(boulder_field_region)
|
||||
|
||||
haunted_house_region = Region("Haunted House", player, world)
|
||||
haunted_house_region.add_locations(loc_haunted_house, CCCharlesLocation)
|
||||
world.regions.append(haunted_house_region)
|
||||
|
||||
santiago_house_region = Region("Santiago House", player, world)
|
||||
santiago_house_region.add_locations(loc_santiago_house, CCCharlesLocation)
|
||||
world.regions.append(santiago_house_region)
|
||||
|
||||
port_region = Region("Port", player, world)
|
||||
port_region.add_locations(loc_port, CCCharlesLocation)
|
||||
world.regions.append(port_region)
|
||||
|
||||
trench_house_region = Region("Trench House", player, world)
|
||||
trench_house_region.add_locations(loc_trench_house, CCCharlesLocation)
|
||||
world.regions.append(trench_house_region)
|
||||
|
||||
doll_woods_region = Region("Doll Woods", player, world)
|
||||
doll_woods_region.add_locations(loc_doll_woods, CCCharlesLocation)
|
||||
world.regions.append(doll_woods_region)
|
||||
|
||||
lost_stairs_region = Region("Lost Stairs", player, world)
|
||||
lost_stairs_region.add_locations(loc_lost_stairs, CCCharlesLocation)
|
||||
world.regions.append(lost_stairs_region)
|
||||
|
||||
east_house_region = Region("East House", player, world)
|
||||
east_house_region.add_locations(loc_east_house, CCCharlesLocation)
|
||||
world.regions.append(east_house_region)
|
||||
|
||||
rockets_testing_ground_region = Region("Rockets Testing Ground", player, world)
|
||||
rockets_testing_ground_region.add_locations(loc_rockets_testing_ground, CCCharlesLocation)
|
||||
world.regions.append(rockets_testing_ground_region)
|
||||
|
||||
rockets_testing_bunker_region = Region("Rockets Testing Bunker", player, world)
|
||||
rockets_testing_bunker_region.add_locations(loc_rockets_testing_bunker, CCCharlesLocation)
|
||||
world.regions.append(rockets_testing_bunker_region)
|
||||
|
||||
workshop_region = Region("Workshop", player, world)
|
||||
workshop_region.add_locations(loc_workshop, CCCharlesLocation)
|
||||
world.regions.append(workshop_region)
|
||||
|
||||
east_tower_region = Region("East Tower", player, world)
|
||||
east_tower_region.add_locations(loc_east_tower, CCCharlesLocation)
|
||||
world.regions.append(east_tower_region)
|
||||
|
||||
lighthouse_region = Region("Lighthouse", player, world)
|
||||
lighthouse_region.add_locations(loc_lighthouse, CCCharlesLocation)
|
||||
world.regions.append(lighthouse_region)
|
||||
|
||||
north_mine_outside_region = Region("North Mine Outside", player, world)
|
||||
north_mine_outside_region.add_locations(loc_north_mine_outside, CCCharlesLocation)
|
||||
world.regions.append(north_mine_outside_region)
|
||||
|
||||
north_mine_inside_region = Region("North Mine Inside", player, world)
|
||||
north_mine_inside_region.add_locations(loc_north_mine_inside, CCCharlesLocation)
|
||||
world.regions.append(north_mine_inside_region)
|
||||
|
||||
wood_bridge_region = Region("Wood Bridge", player, world)
|
||||
wood_bridge_region.add_locations(loc_wood_bridge, CCCharlesLocation)
|
||||
world.regions.append(wood_bridge_region)
|
||||
|
||||
museum_region = Region("Museum", player, world)
|
||||
museum_region.add_locations(loc_museum, CCCharlesLocation)
|
||||
world.regions.append(museum_region)
|
||||
|
||||
barbed_shelter_region = Region("Barbed Shelter", player, world)
|
||||
barbed_shelter_region.add_locations(loc_barbed_shelter, CCCharlesLocation)
|
||||
world.regions.append(barbed_shelter_region)
|
||||
|
||||
west_beach_region = Region("West Beach", player, world)
|
||||
west_beach_region.add_locations(loc_west_beach, CCCharlesLocation)
|
||||
world.regions.append(west_beach_region)
|
||||
|
||||
church_region = Region("Church", player, world)
|
||||
church_region.add_locations(loc_church, CCCharlesLocation)
|
||||
world.regions.append(church_region)
|
||||
|
||||
west_cottage_region = Region("West Cottage", player, world)
|
||||
west_cottage_region.add_locations(loc_west_cottage, CCCharlesLocation)
|
||||
world.regions.append(west_cottage_region)
|
||||
|
||||
caravan_region = Region("Caravan", player, world)
|
||||
caravan_region.add_locations(loc_caravan, CCCharlesLocation)
|
||||
world.regions.append(caravan_region)
|
||||
|
||||
trailer_cabin_region = Region("Trailer Cabin", player, world)
|
||||
trailer_cabin_region.add_locations(loc_trailer_cabin, CCCharlesLocation)
|
||||
world.regions.append(trailer_cabin_region)
|
||||
|
||||
towers_region = Region("Towers", player, world)
|
||||
towers_region.add_locations(loc_towers, CCCharlesLocation)
|
||||
world.regions.append(towers_region)
|
||||
|
||||
north_beach_region = Region("North beach", player, world)
|
||||
north_beach_region.add_locations(loc_north_beach, CCCharlesLocation)
|
||||
world.regions.append(north_beach_region)
|
||||
|
||||
mine_shaft_region = Region("Mine Shaft", player, world)
|
||||
mine_shaft_region.add_locations(loc_mine_shaft, CCCharlesLocation)
|
||||
world.regions.append(mine_shaft_region)
|
||||
|
||||
mob_camp_region = Region("Mob Camp", player, world)
|
||||
mob_camp_region.add_locations(loc_mob_camp, CCCharlesLocation)
|
||||
world.regions.append(mob_camp_region)
|
||||
|
||||
mob_camp_locked_room_region = Region("Mob Camp Locked Room", player, world)
|
||||
mob_camp_locked_room_region.add_locations(loc_mob_camp_locked_room, CCCharlesLocation)
|
||||
world.regions.append(mob_camp_locked_room_region)
|
||||
|
||||
mine_elevator_exit_region = Region("Mine Elevator Exit", player, world)
|
||||
mine_elevator_exit_region.add_locations(loc_mine_elevator_exit, CCCharlesLocation)
|
||||
world.regions.append(mine_elevator_exit_region)
|
||||
|
||||
mountain_ruin_outside_region = Region("Mountain Ruin Outside", player, world)
|
||||
mountain_ruin_outside_region.add_locations(loc_mountain_ruin_outside, CCCharlesLocation)
|
||||
world.regions.append(mountain_ruin_outside_region)
|
||||
|
||||
mountain_ruin_inside_region = Region("Mountain Ruin Inside", player, world)
|
||||
mountain_ruin_inside_region.add_locations(loc_mountain_ruin_inside, CCCharlesLocation)
|
||||
world.regions.append(mountain_ruin_inside_region)
|
||||
|
||||
prism_temple_region = Region("Prism Temple", player, world)
|
||||
prism_temple_region.add_locations(loc_prism_temple, CCCharlesLocation)
|
||||
world.regions.append(prism_temple_region)
|
||||
|
||||
pickle_val_region = Region("Pickle Val", player, world)
|
||||
pickle_val_region.add_locations(loc_pickle_val, CCCharlesLocation)
|
||||
world.regions.append(pickle_val_region)
|
||||
|
||||
shrine_near_temple_region = Region("Shrine Near Temple", player, world)
|
||||
shrine_near_temple_region.add_locations(loc_shrine_near_temple, CCCharlesLocation)
|
||||
world.regions.append(shrine_near_temple_region)
|
||||
|
||||
morse_bunker_region = Region("Morse Bunker", player, world)
|
||||
morse_bunker_region.add_locations(loc_morse_bunker, CCCharlesLocation)
|
||||
world.regions.append(morse_bunker_region)
|
||||
|
||||
# Place "Victory" event at "Final Boss" location
|
||||
loc_final_boss = CCCharlesLocation(player, "Final Boss", None, prism_temple_region)
|
||||
loc_final_boss.place_locked_item(CCCharlesItem("Victory", ItemClassification.progression, None, player))
|
||||
prism_temple_region.locations.append(loc_final_boss)
|
||||
|
||||
# Connect the Regions by named Entrances that must have access Rules
|
||||
menu_region.connect(start_camp_region)
|
||||
menu_region.connect(tony_tiddle_mission_region)
|
||||
menu_region.connect(barn_region, "Barn Door")
|
||||
menu_region.connect(candice_mission_region)
|
||||
menu_region.connect(tutorial_house_region, "Tutorial House Door")
|
||||
menu_region.connect(swamp_edges_region)
|
||||
menu_region.connect(swamp_mission_region)
|
||||
menu_region.connect(junkyard_area_region)
|
||||
menu_region.connect(south_house_region)
|
||||
menu_region.connect(junkyard_shed_region)
|
||||
menu_region.connect(military_base_region)
|
||||
menu_region.connect(south_mine_outside_region)
|
||||
south_mine_outside_region.connect(south_mine_inside_region, "South Mine Gate")
|
||||
menu_region.connect(middle_station_region)
|
||||
menu_region.connect(canyon_region)
|
||||
menu_region.connect(watchtower_region)
|
||||
menu_region.connect(boulder_field_region)
|
||||
menu_region.connect(haunted_house_region)
|
||||
menu_region.connect(santiago_house_region)
|
||||
menu_region.connect(port_region)
|
||||
menu_region.connect(trench_house_region)
|
||||
menu_region.connect(doll_woods_region)
|
||||
menu_region.connect(lost_stairs_region)
|
||||
menu_region.connect(east_house_region)
|
||||
menu_region.connect(rockets_testing_ground_region)
|
||||
rockets_testing_ground_region.connect(rockets_testing_bunker_region, "Stuck Bunker Door")
|
||||
menu_region.connect(workshop_region)
|
||||
menu_region.connect(east_tower_region)
|
||||
menu_region.connect(lighthouse_region)
|
||||
menu_region.connect(north_mine_outside_region)
|
||||
north_mine_outside_region.connect(north_mine_inside_region, "North Mine Gate")
|
||||
menu_region.connect(wood_bridge_region)
|
||||
menu_region.connect(museum_region)
|
||||
menu_region.connect(barbed_shelter_region)
|
||||
menu_region.connect(west_beach_region)
|
||||
menu_region.connect(church_region)
|
||||
menu_region.connect(west_cottage_region)
|
||||
menu_region.connect(caravan_region)
|
||||
menu_region.connect(trailer_cabin_region)
|
||||
menu_region.connect(towers_region)
|
||||
menu_region.connect(north_beach_region)
|
||||
menu_region.connect(mine_shaft_region)
|
||||
menu_region.connect(mob_camp_region)
|
||||
mob_camp_region.connect(mob_camp_locked_room_region, "Mob Camp Locked Door")
|
||||
menu_region.connect(mine_elevator_exit_region)
|
||||
menu_region.connect(mountain_ruin_outside_region)
|
||||
mountain_ruin_outside_region.connect(mountain_ruin_inside_region, "Mountain Ruin Gate")
|
||||
menu_region.connect(prism_temple_region)
|
||||
menu_region.connect(pickle_val_region)
|
||||
menu_region.connect(shrine_near_temple_region)
|
||||
menu_region.connect(morse_bunker_region)
|
||||
215
worlds/cccharles/Rules.py
Normal file
@@ -0,0 +1,215 @@
|
||||
from BaseClasses import MultiWorld
|
||||
from ..generic.Rules import set_rule
|
||||
from .Options import CCCharlesOptions
|
||||
|
||||
# Go mode: Green Egg + Blue Egg + Red Egg + Temple Key + Bug Spray (+ Remote Explosive x8 but the base game ignores it)
|
||||
|
||||
def set_rules(world: MultiWorld, options: CCCharlesOptions, player: int) -> None:
|
||||
# Tony Tiddle
|
||||
set_rule(world.get_entrance("Barn Door", player),
|
||||
lambda state: state.has("Barn Key", player))
|
||||
|
||||
# Candice
|
||||
set_rule(world.get_entrance("Tutorial House Door", player),
|
||||
lambda state: state.has("Candice's Key", player))
|
||||
|
||||
# Lizbeth Murkwater
|
||||
set_rule(world.get_location("Swamp Lizbeth Murkwater Mission End", player),
|
||||
lambda state: state.has("Dead Fish", player))
|
||||
|
||||
# Daryl
|
||||
set_rule(world.get_location("Junkyard Area Chest Ancient Tablet", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Junkyard Area Daryl Mission End", player),
|
||||
lambda state: state.has("Ancient Tablet", player))
|
||||
|
||||
# South House
|
||||
set_rule(world.get_location("South House Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("South House Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("South House Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("South House Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("South House Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("South House Chest Scraps 6", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# South Mine
|
||||
set_rule(world.get_entrance("South Mine Gate", player),
|
||||
lambda state: state.has("South Mine Key", player))
|
||||
|
||||
set_rule(world.get_location("South Mine Inside Green Paint Can", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Theodore
|
||||
set_rule(world.get_location("Middle Station Theodore Mission End", player),
|
||||
lambda state: state.has("Blue Box", player))
|
||||
|
||||
# Watchtower
|
||||
set_rule(world.get_location("Watchtower Pink Paint Can", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Sasha
|
||||
set_rule(world.get_location("Haunted House Sasha Mission End", player),
|
||||
lambda state: state.has("Page Drawing", player, 8))
|
||||
|
||||
# Santiago
|
||||
set_rule(world.get_location("Port Santiago Mission End", player),
|
||||
lambda state: state.has("Journal", player))
|
||||
|
||||
# Trench House
|
||||
set_rule(world.get_location("Trench House Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Trench House Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Trench House Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Trench House Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Trench House Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Trench House Chest Scraps 6", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# East House
|
||||
set_rule(world.get_location("East House Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("East House Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("East House Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("East House Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("East House Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Rocket Testing Bunker
|
||||
set_rule(world.get_entrance("Stuck Bunker Door", player),
|
||||
lambda state: state.has("Timed Dynamite", player))
|
||||
|
||||
# John Smith
|
||||
set_rule(world.get_location("Workshop John Smith Mission End", player),
|
||||
lambda state: state.has("Box of Rockets", player))
|
||||
|
||||
# Claire
|
||||
set_rule(world.get_location("Lighthouse Claire Mission End", player),
|
||||
lambda state: state.has("Breaker", player, 4))
|
||||
|
||||
# North Mine
|
||||
set_rule(world.get_entrance("North Mine Gate", player),
|
||||
lambda state: state.has("North Mine Key", player))
|
||||
|
||||
set_rule(world.get_location("North Mine Inside Blue Paint Can", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Paul
|
||||
set_rule(world.get_location("Museum Paul Mission End", player),
|
||||
lambda state: state.has("Remote Explosive x8", player))
|
||||
# lambda state: state.has("Remote Explosive", player, 8)) # TODO: Add an option to split remote explosives
|
||||
|
||||
# West Beach
|
||||
set_rule(world.get_location("West Beach Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("West Beach Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("West Beach Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("West Beach Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("West Beach Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("West Beach Chest Scraps 6", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Caravan
|
||||
set_rule(world.get_location("Caravan Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Caravan Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Caravan Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Caravan Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Caravan Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Ronny
|
||||
set_rule(world.get_location("Towers Ronny Mission End", player),
|
||||
lambda state: state.has("Employment Contracts", player))
|
||||
|
||||
# North Beach
|
||||
set_rule(world.get_location("North Beach Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("North Beach Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("North Beach Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("North Beach Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Mine Shaft
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 6", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Mine Shaft Chest Scraps 7", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Mob Camp
|
||||
set_rule(world.get_entrance("Mob Camp Locked Door", player),
|
||||
lambda state: state.has("Mob Camp Key", player))
|
||||
|
||||
set_rule(world.get_location("Mob Camp Locked Room Stolen Bob", player),
|
||||
lambda state: state.has("Broken Bob", player))
|
||||
|
||||
# Mountain Ruin
|
||||
set_rule(world.get_entrance("Mountain Ruin Gate", player),
|
||||
lambda state: state.has("Mountain Ruin Key", player))
|
||||
|
||||
set_rule(world.get_location("Mountain Ruin Inside Red Paint Can", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Prism Temple
|
||||
set_rule(world.get_location("Prism Temple Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Prism Temple Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Prism Temple Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Pickle Lady
|
||||
set_rule(world.get_location("Pickle Val Jar of Pickles", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Pickle Val Pickle Lady Mission End", player),
|
||||
lambda state: state.has("Jar of Pickles", player))
|
||||
|
||||
# Morse Bunker
|
||||
set_rule(world.get_location("Morse Bunker Chest Scraps 1", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Morse Bunker Chest Scraps 2", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Morse Bunker Chest Scraps 3", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Morse Bunker Chest Scraps 4", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
set_rule(world.get_location("Morse Bunker Chest Scraps 5", player),
|
||||
lambda state: state.has("Lockpicks", player))
|
||||
|
||||
# Add rules to reach the "Go mode"
|
||||
set_rule(world.get_location("Final Boss", player),
|
||||
lambda state: state.has("Temple Key", player)
|
||||
and state.has("Green Egg", player)
|
||||
and state.has("Blue Egg", player)
|
||||
and state.has("Red Egg", player))
|
||||
world.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
171
worlds/cccharles/__init__.py
Normal file
@@ -0,0 +1,171 @@
|
||||
from .Items import CCCharlesItem, unique_item_dict, full_item_list, item_groups
|
||||
from .Locations import location_table
|
||||
from .Options import CCCharlesOptions
|
||||
from .Rules import set_rules
|
||||
from .Regions import create_regions
|
||||
from BaseClasses import Tutorial, ItemClassification
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
|
||||
class CCCharlesWeb(WebWorld):
|
||||
"""
|
||||
Choo-Choo Charles is a horror game.
|
||||
A devil spider train from hell called Charles chases any person it finds on an island.
|
||||
The goal is to gather scraps to upgrade a train to fight Charles and travel by train to find 3 eggs
|
||||
to lead Charles to a brutal death and save the island.
|
||||
"""
|
||||
|
||||
theme = "stone"
|
||||
|
||||
setup_en = Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setup Choo-Choo Charles for the Archipelago MultiWorld Randomizer.",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
"setup/en",
|
||||
["Yaranorgoth"]
|
||||
)
|
||||
|
||||
setup_fr = Tutorial(
|
||||
"Guide d'Installation Multiworld",
|
||||
"Un guide pour mettre en place Choo-Choo Charles pour le Randomiseur Multiworld Archipelago",
|
||||
"Français",
|
||||
"setup_fr.md",
|
||||
"setup/fr",
|
||||
["Yaranorgoth"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_fr]
|
||||
|
||||
game_info_languages = ["en", "fr"]
|
||||
rich_text_options_doc = True
|
||||
|
||||
|
||||
class CCCharlesWorld(World):
|
||||
"""
|
||||
An independent 3D horror game, taking place on an island.
|
||||
The main gameplay consists of traveling and fighting a monster on board a train.
|
||||
Upgrading the train requires leaving the train to gather resources with the threat of encountering the monster.
|
||||
"""
|
||||
|
||||
game = "Choo-Choo Charles"
|
||||
|
||||
web = CCCharlesWeb()
|
||||
|
||||
item_name_to_id = unique_item_dict
|
||||
location_name_to_id = location_table
|
||||
item_name_groups = item_groups
|
||||
|
||||
# Options the player can set
|
||||
options_dataclass = CCCharlesOptions
|
||||
# Typing hints for all the options we defined
|
||||
options: CCCharlesOptions
|
||||
|
||||
topology_present = False # Hide path to required location checks in spoiler
|
||||
|
||||
def create_regions(self) -> None:
|
||||
create_regions(self.multiworld, self.options, self.player)
|
||||
|
||||
def create_item(self, name: str) -> CCCharlesItem:
|
||||
item_id = unique_item_dict[name]
|
||||
|
||||
match name:
|
||||
case "Scraps":
|
||||
classification = ItemClassification.useful
|
||||
case "30 Scraps Reward":
|
||||
classification = ItemClassification.useful
|
||||
case "25 Scraps Reward":
|
||||
classification = ItemClassification.useful
|
||||
case "35 Scraps Reward":
|
||||
classification = ItemClassification.useful
|
||||
case "40 Scraps Reward":
|
||||
classification = ItemClassification.useful
|
||||
case "South Mine Key":
|
||||
classification = ItemClassification.progression
|
||||
case "North Mine Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Mountain Ruin Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Barn Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Candice's Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Dead Fish":
|
||||
classification = ItemClassification.progression
|
||||
case "Lockpicks":
|
||||
classification = ItemClassification.progression
|
||||
case "Ancient Tablet":
|
||||
classification = ItemClassification.progression
|
||||
case "Blue Box":
|
||||
classification = ItemClassification.progression
|
||||
case "Page Drawing":
|
||||
classification = ItemClassification.progression
|
||||
case "Journal":
|
||||
classification = ItemClassification.progression
|
||||
case "Timed Dynamite":
|
||||
classification = ItemClassification.progression
|
||||
case "Box of Rockets":
|
||||
classification = ItemClassification.progression
|
||||
case "Breaker":
|
||||
classification = ItemClassification.progression
|
||||
case "Broken Bob":
|
||||
classification = ItemClassification.progression
|
||||
case "Employment Contracts":
|
||||
classification = ItemClassification.progression
|
||||
case "Mob Camp Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Jar of Pickles":
|
||||
classification = ItemClassification.progression
|
||||
case "Orange Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Green Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "White Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Pink Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Grey Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Blue Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Black Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Lime Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Teal Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Red Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "Purple Paint Can":
|
||||
classification = ItemClassification.filler
|
||||
case "The Boomer":
|
||||
classification = ItemClassification.filler
|
||||
case "Bob":
|
||||
classification = ItemClassification.filler
|
||||
case "Green Egg":
|
||||
classification = ItemClassification.progression
|
||||
case "Blue Egg":
|
||||
classification = ItemClassification.progression
|
||||
case "Red Egg":
|
||||
classification = ItemClassification.progression
|
||||
case "Remote Explosive":
|
||||
classification = ItemClassification.progression
|
||||
case "Remote Explosive x8":
|
||||
classification = ItemClassification.progression
|
||||
case "Temple Key":
|
||||
classification = ItemClassification.progression
|
||||
case "Bug Spray":
|
||||
classification = ItemClassification.progression
|
||||
case _: # Should not occur
|
||||
raise Exception("Unexpected case met: classification cannot be set for unknown item \"" + name + "\"")
|
||||
|
||||
return CCCharlesItem(name, classification, item_id, self.player)
|
||||
|
||||
def create_items(self) -> None:
|
||||
self.multiworld.itempool += [self.create_item(item) for item in full_item_list]
|
||||
|
||||
def set_rules(self) -> None:
|
||||
set_rules(self.multiworld, self.options, self.player)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return "Scraps"
|
||||
39
worlds/cccharles/docs/en_Choo-Choo Charles.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Choo-Choo Charles
|
||||
|
||||
## Game page in other languages
|
||||
* [Français](fr)
|
||||
|
||||
## Where is the options page?
|
||||
The [Player Options page](../player-options) contains all the options to configure and export a yaml config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
All scraps or any collectable item on the ground (except from Loot Crates) and items received from NPCs missions are considered as locations to check.
|
||||
|
||||
## What is the goal of Choo-Choo Charles when randomized?
|
||||
Beating the evil train from Hell named "Charles".
|
||||
|
||||
## How is the game managed in Nightmare mode?
|
||||
At death, the player has to restart a brand-new game, giving him the choice to stay under the Nightmare mode or continuing with the Normal mode if considered too hard.
|
||||
In this case, all collected items will be redistributed in the inventory and the missions states will be kept.
|
||||
The Deathlink is not implemented yet. When this option will be available, a choice will be provided to:
|
||||
* Disable the Deathlink
|
||||
* Enable the soft Deathlink with respawn at Player Train when a Deathlink event is received
|
||||
* Enable the hard Deathlink with removal of the game save when a Deathlink event is received
|
||||
|
||||
## What does another world's item look like in Choo-Choo Charles?
|
||||
Items appearance are kept unchanged.
|
||||
Any hint that cannot be normally represented in the game is replaced by the miniaturized "DeathDuck" Easter Egg that can be seen out from the physical wall limits of the original game.
|
||||
|
||||
## How is the player informed by an item transmission and hints?
|
||||
A message appears in game to inform what item is sent or received, including which world and what player the item comes from.
|
||||
The same method is used for hints.
|
||||
|
||||
## Is it possible to use hints in the game?
|
||||
No, this is a work in progress.
|
||||
The following options will be possible once the implementations are available:
|
||||
|
||||
At any moment, the player can press one of the following keys to display a console in the game:
|
||||
* "~" or "`" (qwerty)
|
||||
* "²" (azerty)
|
||||
* "F10"
|
||||
Then, a hint can be revealed by typing "/hint [player] <item>".
|
||||
36
worlds/cccharles/docs/fr_Choo-Choo Charles.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Choo-Choo Charles
|
||||
|
||||
## Où est la page d'options ?
|
||||
La [page d'options du joueur pour ce jeu](../player-options) contient toutes les options pour configurer et exporter un fichier de configuration yaml.
|
||||
|
||||
## Qu'est ce que la randomisation fait au jeu ?
|
||||
Tous les débrits ou n'importe quel objet ramassable au sol (excepté les Caisses à Butin) et objets reçus par les missions de PNJs sont considérés comme emplacements à vérifier.
|
||||
|
||||
## Quel est le but de Choo-Choo Charles lorsqu'il est randomisé ?
|
||||
Vaincre le train démoniaque de l'Enfer nommé "Charles".
|
||||
|
||||
## Comment le jeu est-il géré en mode Nightmare ?
|
||||
À sa mort, le joueur doit relancer une toute nouvelle partie, lui donnant la possisilité de rester en mode Nightmare ou de poursuivre la partie en mode Normal s'il considère la partie trop difficile.
|
||||
Dans ce cas, tous les objets collectés seront redistribués dans l'inventaire et les états des missions seront conservés.
|
||||
Le Deathlink n'est pas implémenté pour l'instant. Lorsque cette option sera disponible, un choix sera fourni pour :
|
||||
* Désactiver le Deathlink
|
||||
* Activer le Deathlink modéré avec réapparition au Train du Joueur lorsqu'un évènement Deathlink est reçu
|
||||
* Activer le Deathlink strict avec suppression de la sauvegarde lorsqu'un évènement Deathlink est reçu
|
||||
|
||||
## À quoi ressemble un objet d'un autre monde dans Choo-Choo Charles ?
|
||||
Les apparances des objets sont conservés.
|
||||
Tout indice qui ne peut pas être représenté normalement dans le jeu est remplacé par l'Easter Egg "DeadDuck" miniaturisé qui peut être vu en dehors des limites murales physiques du jeu original.
|
||||
|
||||
## Comment le joueur est-il informé par une transmission d'objet et des indices ?
|
||||
Un message apparaît en jeu pour informer quel objet est envoyé ou reçu, incluant de quel monde et de quel joueur vient l'objet.
|
||||
La même méthode est utilisée pour les indices.
|
||||
|
||||
## Est-il possible d'utiliser les indices dans le jeu ?
|
||||
Non, ceci est un travail en cours.
|
||||
Les options suivantes seront possibles une fois les implémentations disponibles :
|
||||
|
||||
À n'importe quel moment, le joueur peu appuyer sur l'une des touches suivantes pour afficher la console dans le jeu :
|
||||
* "~" (qwerty)
|
||||
* "²" (azerty)
|
||||
* "F10"
|
||||
Puis, un indice peut être révélé en tapant "/hint [player] <item>"
|
||||
52
worlds/cccharles/docs/setup_en.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Choo-Choo Charles MultiWorld Setup Guide
|
||||
This page is a simplified guide of the [Choo-Choo Charles Multiworld Randomizer Mod page](https://github.com/lgbarrere/CCCharles-Random?tab=readme-ov-file#cccharles-random).
|
||||
|
||||
## Requirements and Required Softwares
|
||||
* A computer running Windows (the Mod is not handled by Linux or Mac)
|
||||
* [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
* A legal copy of the Choo-Choo Charles original game (can be found on [Steam](https://store.steampowered.com/app/1766740/ChooChoo_Charles/))
|
||||
|
||||
## Mod Installation for playing
|
||||
### Mod Download
|
||||
All the required files of the Mod can be found in the [Releases](https://github.com/lgbarrere/CCCharles-Random/releases).
|
||||
To use the Mod, download and unzip **CCCharles_Random.zip** somewhere safe, then follow the instructions in the next sections of this guide. This archive contains:
|
||||
* The **Obscure/** folder loading the Mod itself, it runs the code handling all the randomized elements
|
||||
* The **cccharles.apworld** file containing the randomization logic, used by the host to generate a random seed with the others games
|
||||
|
||||
### Game Setup
|
||||
The Mod can be installed and played by following these steps (see the [Mod Download](setup_en#mod-download) section to get **CCCharles_Random.zip**):
|
||||
1. Copy the **Obscure/** folder from **CCCharles_Random.zip** to **\<GameFolder\>** (where the **Obscure/** folder and **Obscure.exe** are placed)
|
||||
2. Launch the game, if "OFFLINE" is visible in the upper-right corner of the screen, the Mod is working
|
||||
|
||||
### Create a Config (.yaml) File
|
||||
The purpose of a YAML file is described in the [Basic Multiworld Setup Guide](https://archipelago.gg/tutorial/Archipelago/setup/en#generating-a-game).
|
||||
|
||||
The [Player Options page](/games/Choo-Choo%20Charles/player-options) allows to configure personal options and export a config YAML file.
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
Before playing, it is highly recommended to check out the **[Known Issues](setup_en#known-issues)** section
|
||||
* The game console must be opened to type Archipelago commands, press "F10" key or "`" (or "~") key in querty ("²" key in azerty)
|
||||
* Type ``/connect <IP> <PlayerName>`` with \<IP\> and \<PlayerName\> found on the hosting Archipelago web page in the form ``archipelago.gg:XXXXX`` and ``CCCharles``
|
||||
* Disconnection is automatic at game closure but can be manually done with ``/disconnect``
|
||||
|
||||
## Hosting a MultiWorld or Single-Player Game
|
||||
See the [Mod Download](setup_en#mod-download) section to get the **cccharles.apworld** file.
|
||||
|
||||
In this section, **Archipelago/** refers to the path where [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) is installed locally.
|
||||
|
||||
Follow these steps to host a remote multiplayer or a local single-player session:
|
||||
1. Double-click the **cccharles.apworld** to automatically install the world randomization logic
|
||||
2. Put the **CCCharles.yaml** to **Archipelago/Players/** with the YAML of each player to host
|
||||
3. Launch the Archipelago launcher and click "Generate" to configure a game with the YAMLs in **Archipelago/output/**
|
||||
4. For a multiplayer session, go to the [Archipelago HOST GAME page](https://archipelago.gg/uploads)
|
||||
5. Click "Upload File" and select the generated **AP_\<seed\>.zip** in **Archipelago/output/**
|
||||
6. Send the generated room page to each player
|
||||
|
||||
For a local single-player session, click "Host" in the Archipelago launcher by using the generated **AP_\<seed\>.zip** in **Archipelago/output/**
|
||||
|
||||
## Known Issues
|
||||
### Major issues
|
||||
No major issue found.
|
||||
|
||||
### Minor issues
|
||||
* The current version of the command parser does not accept console commands with a player names containing whitespaces. It is recommended to use underscores "_" instead, for instance: CCCharles_Player_1.
|
||||
52
worlds/cccharles/docs/setup_fr.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Guide d'Installation du MultiWorld Choo-Choo Charles
|
||||
Cette page est un guide simplifié de la [page du Mod Randomiseur Multiworld de Choo-Choo Charles](https://github.com/lgbarrere/CCCharles-Random?tab=readme-ov-file#cccharles-random).
|
||||
|
||||
## Exigences et Logiciels Nécessaires
|
||||
* Un ordinateur utilisant Windows (le Mod n'est pas utilisable sous Linux ou Mac)
|
||||
* [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
* Une copie légale du jeu original Choo-Choo Charles (peut être trouvé sur [Steam](https://store.steampowered.com/app/1766740/ChooChoo_Charles/)
|
||||
|
||||
## Installation du Mod pour jouer
|
||||
### Téléchargement du Mod
|
||||
Tous les fichiers nécessaires du Mod se trouvent dans les [Releases](https://github.com/lgbarrere/CCCharles-Random/releases).
|
||||
Pour utiliser le Mod, télécharger et désarchiver **CCCharles_Random.zip** à un endroit sûr, puis suivre les instructions dans les sections suivantes de ce guide. Cette archive contient :
|
||||
* Le dossier **Obscure/** qui charge le Mod lui-même, il lance le code qui gère tous les éléments randomisés
|
||||
* Le fichier **cccharles.apworld** qui contient la logique de randomisation, utilisé par l'hôte pour générer une graine aléatoire avec les autres jeux
|
||||
|
||||
### Préparation du Jeu
|
||||
Le Mod peut être installé et joué en suivant les étapes suivantes (voir la section [Téléchargement du Mod](setup_fr#téléchargement-du-mod) pour récupérer **CCCharles_Random.zip**) :
|
||||
1. Copier le dossier **Obscure/** de **CCCharles_Random.zip** vers **\<GameFolder\>** (où se situent le dossier **Obscure/** et **Obscure.exe**)
|
||||
2. Lancer le jeu, si "OFFLINE" est visible dans le coin en haut à droite de l'écran, le Mod est actif
|
||||
|
||||
### Créer un Fichier de Configuration (.yaml)
|
||||
L'objectif d'un fichier YAML est décrit dans le [Guide d'Installation Basique du Multiworld](https://archipelago.gg/tutorial/Archipelago/setup/en#generating-a-game) (en anglais).
|
||||
|
||||
La [page d'Options Joueur](/games/Choo-Choo%20Charles/player-options) permet de configurer des options personnelles et exporter un fichier de configuration YAML.
|
||||
|
||||
## Rejoindre une Partie MultiWorld
|
||||
Avant de jouer, il est fortement recommandé de consulter la section **[Problèmes Connus](setup_fr#probl%C3%A8mes-connus)**.
|
||||
* La console du jeu doit être ouverte pour taper des commandes Archipelago, appuyer sur la touche "F10" ou "`" (ou "~") en querty (touche "²" en azerty)
|
||||
* Taper ``/connect <IP> <NomDuJoueur>`` avec \<IP\> et \<NomDuJoueur\> trouvés sur la page web d'hébergement Archipelago sous la forme ``archipelago.gg:XXXXX`` et ``CCCharles``
|
||||
* La déconnexion est automatique à la fermeture du jeu mais peut être faite manuellement avec ``/disconnect``
|
||||
|
||||
## Héberger une partie MultiWorld ou un Seul Joueur
|
||||
Voir la section [Téléchargement du Mod](setup_fr#téléchargement-du-mod) pour récupérer le fichier **cccharles.apworld**.
|
||||
|
||||
Dans cette section, **Archipelago/** fait référence au chemin d'accès où [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) est installé localement.
|
||||
|
||||
Suivre ces étapes pour héberger une session multijoueur à distance ou locale pour un seul joueur :
|
||||
1. Double-cliquer sur **cccharles.apworld** pour installer automatiquement la logique de randomisation du monde
|
||||
2. Placer le **CCCharles.yaml** dans **Archipelago/Players/** avec le YAML de chaque joueur à héberger
|
||||
3. Exécuter le lanceur Archipelago et cliquer sur "Generate" pour configurer une partie avec les YAML dans **Archipelago/output/**
|
||||
4. Pour une session multijoueur, aller à la [page Archipelago HOST GAME](https://archipelago.gg/uploads)
|
||||
5. Cliquer sur "Upload File" et selectionner le **AP_\<seed\>.zip** généré dans **Archipelago/output/**
|
||||
6. Envoyer la page de la partie générée à chaque joueur
|
||||
|
||||
Pour une session locale à un seul joueur, cliquer sur "Host" dans le lanceur Archipelago en utilisant **AP_\<seed\>.zip** généré dans **Archipelago/output/**
|
||||
|
||||
## Problèmes Connus
|
||||
### Problèmes majeurs
|
||||
Aucun problème majeur trouvé.
|
||||
|
||||
### Problèmes mineurs
|
||||
* La version actuelle de l'analyseur de commandes n'accepte pas des commandes de la console dont le nom du joueur contient des espaces. Il est recommandé d'utiliser des soulignés "_" à la place, par exemple : CCCharles_Player_1.
|
||||
27
worlds/cccharles/test/TestAccess.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from BaseClasses import CollectionState
|
||||
from .bases import CCCharlesTestBase
|
||||
|
||||
|
||||
class TestAccess(CCCharlesTestBase):
|
||||
def test_claire_breakers(self) -> None:
|
||||
"""Test locations that require 4 Breakers"""
|
||||
lighthouse_claire_mission_end = self.world.get_location("Lighthouse Claire Mission End")
|
||||
|
||||
state = CollectionState(self.multiworld)
|
||||
self.collect_all_but("Breaker")
|
||||
|
||||
breakers_in_pool = self.get_items_by_name("Breaker")
|
||||
self.assertGreaterEqual(len(breakers_in_pool), 4) # Check at least 4 Breakers are in the item pool
|
||||
|
||||
for breaker in breakers_in_pool[:3]:
|
||||
state.collect(breaker) # Collect 3 Breakers into state
|
||||
self.assertFalse(
|
||||
lighthouse_claire_mission_end.can_reach(state),
|
||||
"Lighthouse Claire Mission End should not be reachable with only three Breakers"
|
||||
)
|
||||
|
||||
state.collect(breakers_in_pool[3]) # Collect 4th breaker into state
|
||||
self.assertTrue(
|
||||
lighthouse_claire_mission_end.can_reach(state),
|
||||
"Lighthouse Claire Mission End should have been reachable with four Breakers"
|
||||
)
|
||||
0
worlds/cccharles/test/__init__.py
Normal file
5
worlds/cccharles/test/bases.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from test.bases import WorldTestBase
|
||||
|
||||
|
||||
class CCCharlesTestBase(WorldTestBase):
|
||||
game = "Choo-Choo Charles"
|
||||
27
worlds/celeste64/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Modified MIT License
|
||||
|
||||
Copyright (c) 2025 PoryGone
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, and/or distribute copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
No copy or substantial portion of the Software shall be sublicensed or relicensed
|
||||
without the express written permission of the copyright holder(s)
|
||||
|
||||
No copy or substantial portion of the Software shall be sold without the express
|
||||
written permission of the copyright holder(s)
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -27,7 +27,7 @@ strawberry_location_data_table: Dict[str, Celeste64LocationData] = {
|
||||
LocationName.strawberry_8: Celeste64LocationData(RegionName.nw_girders_island, celeste_64_base_id + 0x07),
|
||||
LocationName.strawberry_9: Celeste64LocationData(RegionName.granny_island, celeste_64_base_id + 0x08),
|
||||
LocationName.strawberry_10: Celeste64LocationData(RegionName.granny_island, celeste_64_base_id + 0x09),
|
||||
LocationName.strawberry_11: Celeste64LocationData(RegionName.granny_island, celeste_64_base_id + 0x0A),
|
||||
LocationName.strawberry_11: Celeste64LocationData(RegionName.highway_island, celeste_64_base_id + 0x0A),
|
||||
LocationName.strawberry_12: Celeste64LocationData(RegionName.badeline_tower_lower, celeste_64_base_id + 0x0B),
|
||||
LocationName.strawberry_13: Celeste64LocationData(RegionName.highway_island, celeste_64_base_id + 0x0C),
|
||||
LocationName.strawberry_14: Celeste64LocationData(RegionName.ne_feathers_island, celeste_64_base_id + 0x0D),
|
||||
|
||||
@@ -82,12 +82,10 @@ location_hard_moves_logic: Dict[str, List[List[str]]] = {
|
||||
[ItemName.double_dash_refill, ItemName.air_dash]],
|
||||
LocationName.strawberry_15: [[ItemName.feather],
|
||||
[ItemName.ground_dash, ItemName.air_dash]],
|
||||
LocationName.strawberry_17: [[ItemName.double_dash_refill, ItemName.traffic_block]],
|
||||
LocationName.strawberry_17: [[ItemName.double_dash_refill]],
|
||||
LocationName.strawberry_18: [[ItemName.air_dash, ItemName.climb],
|
||||
[ItemName.double_dash_refill, ItemName.air_dash]],
|
||||
LocationName.strawberry_19: [[ItemName.air_dash, ItemName.skid_jump],
|
||||
[ItemName.double_dash_refill, ItemName.spring, ItemName.air_dash],
|
||||
[ItemName.spring, ItemName.ground_dash, ItemName.air_dash]],
|
||||
LocationName.strawberry_19: [[ItemName.air_dash]],
|
||||
LocationName.strawberry_20: [[ItemName.breakables, ItemName.air_dash]],
|
||||
|
||||
LocationName.strawberry_21: [[ItemName.cassette, ItemName.traffic_block, ItemName.breakables, ItemName.air_dash]],
|
||||
|
||||
6
worlds/celeste64/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "Celeste 64",
|
||||
"authors": [ "PoryGone" ],
|
||||
"minimum_ap_version": "0.6.3",
|
||||
"world_version": "1.3.1"
|
||||
}
|
||||
47
worlds/celeste_open_world/CHANGELOG.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Celeste - Changelog
|
||||
|
||||
|
||||
## v1.0 - First Stable Release
|
||||
|
||||
### Features:
|
||||
|
||||
- Goal is to collect a certain number of Strawberries, finish your chosen Goal Area, and reach the credits in the Epilogue
|
||||
- Locations included:
|
||||
- Level Clears
|
||||
- Strawberries
|
||||
- Crystal Hearts
|
||||
- Cassettes
|
||||
- Golden Strawberries
|
||||
- Keys
|
||||
- Checkpoints
|
||||
- Summit Gems
|
||||
- Cars
|
||||
- Binoculars
|
||||
- Rooms
|
||||
- Items included:
|
||||
- 34 different interactable objects
|
||||
- Keys
|
||||
- Checkpoints
|
||||
- Summit Gems
|
||||
- Crystal Hearts
|
||||
- Cassettes
|
||||
- Traps
|
||||
- Bald Trap
|
||||
- Literature Trap
|
||||
- Stun Trap
|
||||
- Invisible Trap
|
||||
- Fast Trap
|
||||
- Slow Trap
|
||||
- Ice Trap
|
||||
- Reverse Trap
|
||||
- Screen Flip Trap
|
||||
- Laughter Trap
|
||||
- Hiccup Trap
|
||||
- Zoom Trap
|
||||
- Aesthetic Options:
|
||||
- Music Shuffle
|
||||
- Require Cassette items to hear music
|
||||
- Hair Length/Color options
|
||||
- Death Link
|
||||
- Amnesty option to select how many deaths must occur to send a DeathLink
|
||||
- Trap Link
|
||||
264
worlds/celeste_open_world/Items.py
Normal file
@@ -0,0 +1,264 @@
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from .Names import ItemName
|
||||
|
||||
|
||||
level_item_lists: dict[str, set[str]] = {
|
||||
"0a": set(),
|
||||
|
||||
"1a": {ItemName.springs, ItemName.traffic_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"1b": {ItemName.springs, ItemName.traffic_blocks, ItemName.dash_refills, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"1c": {ItemName.traffic_blocks, ItemName.dash_refills, ItemName.coins},
|
||||
|
||||
"2a": {ItemName.springs, ItemName.dream_blocks, ItemName.traffic_blocks, ItemName.strawberry_seeds, ItemName.dash_refills, ItemName.coins},
|
||||
"2b": {ItemName.springs, ItemName.dream_blocks, ItemName.dash_refills, ItemName.coins, ItemName.blue_cassette_blocks},
|
||||
"2c": {ItemName.springs, ItemName.dream_blocks, ItemName.dash_refills, ItemName.coins},
|
||||
|
||||
"3a": {ItemName.springs, ItemName.moving_platforms, ItemName.sinking_platforms, ItemName.dash_refills, ItemName.coins, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"3b": {ItemName.springs, ItemName.dash_refills, ItemName.sinking_platforms, ItemName.coins, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"3c": {ItemName.dash_refills, ItemName.sinking_platforms, ItemName.coins},
|
||||
|
||||
"4a": {ItemName.blue_clouds, ItemName.blue_boosters, ItemName.moving_platforms, ItemName.coins, ItemName.strawberry_seeds, ItemName.springs, ItemName.move_blocks, ItemName.pink_clouds, ItemName.white_block, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"4b": {ItemName.blue_boosters, ItemName.moving_platforms, ItemName.move_blocks, ItemName.springs, ItemName.coins, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.dash_refills, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"4c": {ItemName.blue_boosters, ItemName.move_blocks, ItemName.dash_refills, ItemName.pink_clouds},
|
||||
|
||||
"5a": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills, ItemName.coins, ItemName.springs, ItemName.torches, ItemName.seekers, ItemName.theo_crystal, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"5b": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills, ItemName.coins, ItemName.springs, ItemName.torches, ItemName.seekers, ItemName.theo_crystal, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"5c": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills},
|
||||
|
||||
"6a": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers, ItemName.springs, ItemName.coins, ItemName.badeline_boosters, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"6b": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers, ItemName.coins, ItemName.springs, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"6c": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers},
|
||||
|
||||
"7a": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.traffic_blocks, ItemName.coins, ItemName.dream_blocks, ItemName.sinking_platforms, ItemName.blue_boosters, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.move_blocks, ItemName.moving_platforms, ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.feathers, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"7b": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.traffic_blocks, ItemName.coins, ItemName.dream_blocks, ItemName.moving_platforms, ItemName.blue_boosters, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.move_blocks, ItemName.swap_blocks, ItemName.red_boosters, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"7c": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.coins, ItemName.pink_clouds},
|
||||
|
||||
# Epilogue
|
||||
"8a": set(),
|
||||
|
||||
# Core
|
||||
"9a": {ItemName.springs, ItemName.dash_refills, ItemName.fire_ice_balls, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.coins, ItemName.badeline_boosters, ItemName.feathers, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"9b": {ItemName.springs, ItemName.dash_refills, ItemName.fire_ice_balls, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.coins, ItemName.badeline_boosters, ItemName.dream_blocks, ItemName.moving_platforms, ItemName.blue_clouds, ItemName.swap_blocks, ItemName.kevin_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
|
||||
"9c": {ItemName.dash_refills, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.traffic_blocks, ItemName.dream_blocks, ItemName.pink_clouds, ItemName.swap_blocks, ItemName.kevin_blocks},
|
||||
|
||||
# Farewell Pre/Post Empty Space
|
||||
"10a": {ItemName.blue_clouds, ItemName.badeline_boosters, ItemName.dash_refills, ItemName.double_dash_refills, ItemName.swap_blocks, ItemName.springs, ItemName.pufferfish, ItemName.coins, ItemName.dream_blocks, ItemName.jellyfish, ItemName.red_boosters, ItemName.dash_switches, ItemName.move_blocks, ItemName.breaker_boxes, ItemName.traffic_blocks},
|
||||
"10b": {ItemName.dream_blocks, ItemName.badeline_boosters, ItemName.bird, ItemName.dash_refills, ItemName.double_dash_refills, ItemName.kevin_blocks, ItemName.coins, ItemName.traffic_blocks, ItemName.move_blocks, ItemName.blue_boosters, ItemName.springs, ItemName.feathers, ItemName.swap_blocks, ItemName.red_boosters, ItemName.core_blocks, ItemName.fire_ice_balls, ItemName.kevin_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks, ItemName.yellow_cassette_blocks, ItemName.green_cassette_blocks, ItemName.breaker_boxes, ItemName.pufferfish, ItemName.jellyfish},
|
||||
"10c": {ItemName.badeline_boosters, ItemName.double_dash_refills, ItemName.springs, ItemName.pufferfish, ItemName.jellyfish},
|
||||
}
|
||||
|
||||
level_cassette_items: dict[str, str] = {
|
||||
"0a": ItemName.prologue_cassette,
|
||||
"1a": ItemName.fc_a_cassette,
|
||||
"1b": ItemName.fc_b_cassette,
|
||||
"1c": ItemName.fc_c_cassette,
|
||||
"2a": ItemName.os_a_cassette,
|
||||
"2b": ItemName.os_b_cassette,
|
||||
"2c": ItemName.os_c_cassette,
|
||||
"3a": ItemName.cr_a_cassette,
|
||||
"3b": ItemName.cr_b_cassette,
|
||||
"3c": ItemName.cr_c_cassette,
|
||||
"4a": ItemName.gr_a_cassette,
|
||||
"4b": ItemName.gr_b_cassette,
|
||||
"4c": ItemName.gr_c_cassette,
|
||||
"5a": ItemName.mt_a_cassette,
|
||||
"5b": ItemName.mt_b_cassette,
|
||||
"5c": ItemName.mt_c_cassette,
|
||||
"6a": ItemName.ref_a_cassette,
|
||||
"6b": ItemName.ref_b_cassette,
|
||||
"6c": ItemName.ref_c_cassette,
|
||||
"7a": ItemName.sum_a_cassette,
|
||||
"7b": ItemName.sum_b_cassette,
|
||||
"7c": ItemName.sum_c_cassette,
|
||||
"8a": ItemName.epilogue_cassette,
|
||||
"9a": ItemName.core_a_cassette,
|
||||
"9b": ItemName.core_b_cassette,
|
||||
"9c": ItemName.core_c_cassette,
|
||||
"10a":ItemName.farewell_cassette,
|
||||
}
|
||||
|
||||
|
||||
celeste_base_id: int = 0xCA10000
|
||||
|
||||
|
||||
class CelesteItem(Item):
|
||||
game = "Celeste"
|
||||
|
||||
|
||||
class CelesteItemData(NamedTuple):
|
||||
code: Optional[int] = None
|
||||
type: ItemClassification = ItemClassification.filler
|
||||
|
||||
|
||||
collectable_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.strawberry: CelesteItemData(celeste_base_id + 0x0, ItemClassification.progression_skip_balancing),
|
||||
ItemName.raspberry: CelesteItemData(celeste_base_id + 0x1, ItemClassification.filler),
|
||||
}
|
||||
|
||||
goal_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.house_keys: CelesteItemData(celeste_base_id + 0x10, ItemClassification.progression_skip_balancing),
|
||||
}
|
||||
|
||||
trap_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.bald_trap: CelesteItemData(celeste_base_id + 0x20, ItemClassification.trap),
|
||||
ItemName.literature_trap: CelesteItemData(celeste_base_id + 0x21, ItemClassification.trap),
|
||||
ItemName.stun_trap: CelesteItemData(celeste_base_id + 0x22, ItemClassification.trap),
|
||||
ItemName.invisible_trap: CelesteItemData(celeste_base_id + 0x23, ItemClassification.trap),
|
||||
ItemName.fast_trap: CelesteItemData(celeste_base_id + 0x24, ItemClassification.trap),
|
||||
ItemName.slow_trap: CelesteItemData(celeste_base_id + 0x25, ItemClassification.trap),
|
||||
ItemName.ice_trap: CelesteItemData(celeste_base_id + 0x26, ItemClassification.trap),
|
||||
ItemName.reverse_trap: CelesteItemData(celeste_base_id + 0x28, ItemClassification.trap),
|
||||
ItemName.screen_flip_trap: CelesteItemData(celeste_base_id + 0x29, ItemClassification.trap),
|
||||
ItemName.laughter_trap: CelesteItemData(celeste_base_id + 0x2A, ItemClassification.trap),
|
||||
ItemName.hiccup_trap: CelesteItemData(celeste_base_id + 0x2B, ItemClassification.trap),
|
||||
ItemName.zoom_trap: CelesteItemData(celeste_base_id + 0x2C, ItemClassification.trap),
|
||||
}
|
||||
|
||||
checkpoint_item_data_table: dict[str, CelesteItemData] = {}
|
||||
|
||||
key_item_data_table: dict[str, CelesteItemData] = {}
|
||||
gem_item_data_table: dict[str, CelesteItemData] = {}
|
||||
|
||||
interactable_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.springs: CelesteItemData(celeste_base_id + 0x2000 + 0x00, ItemClassification.progression),
|
||||
ItemName.traffic_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x01, ItemClassification.progression),
|
||||
ItemName.pink_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x02, ItemClassification.progression),
|
||||
ItemName.blue_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x03, ItemClassification.progression),
|
||||
|
||||
ItemName.dream_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x04, ItemClassification.progression),
|
||||
ItemName.coins: CelesteItemData(celeste_base_id + 0x2000 + 0x05, ItemClassification.progression),
|
||||
ItemName.strawberry_seeds: CelesteItemData(celeste_base_id + 0x2000 + 0x1F, ItemClassification.progression),
|
||||
|
||||
ItemName.sinking_platforms: CelesteItemData(celeste_base_id + 0x2000 + 0x20, ItemClassification.progression),
|
||||
|
||||
ItemName.moving_platforms: CelesteItemData(celeste_base_id + 0x2000 + 0x06, ItemClassification.progression),
|
||||
ItemName.blue_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x07, ItemClassification.progression),
|
||||
ItemName.blue_clouds: CelesteItemData(celeste_base_id + 0x2000 + 0x08, ItemClassification.progression),
|
||||
ItemName.move_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x09, ItemClassification.progression),
|
||||
ItemName.white_block: CelesteItemData(celeste_base_id + 0x2000 + 0x21, ItemClassification.progression),
|
||||
|
||||
ItemName.swap_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x0A, ItemClassification.progression),
|
||||
ItemName.red_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x0B, ItemClassification.progression),
|
||||
ItemName.torches: CelesteItemData(celeste_base_id + 0x2000 + 0x22, ItemClassification.useful),
|
||||
ItemName.theo_crystal: CelesteItemData(celeste_base_id + 0x2000 + 0x0C, ItemClassification.progression),
|
||||
|
||||
ItemName.feathers: CelesteItemData(celeste_base_id + 0x2000 + 0x0D, ItemClassification.progression),
|
||||
ItemName.bumpers: CelesteItemData(celeste_base_id + 0x2000 + 0x0E, ItemClassification.progression),
|
||||
ItemName.kevin_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x0F, ItemClassification.progression),
|
||||
|
||||
ItemName.pink_clouds: CelesteItemData(celeste_base_id + 0x2000 + 0x10, ItemClassification.progression),
|
||||
ItemName.badeline_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x11, ItemClassification.progression),
|
||||
|
||||
ItemName.fire_ice_balls: CelesteItemData(celeste_base_id + 0x2000 + 0x12, ItemClassification.progression),
|
||||
ItemName.core_toggles: CelesteItemData(celeste_base_id + 0x2000 + 0x13, ItemClassification.progression),
|
||||
ItemName.core_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x14, ItemClassification.progression),
|
||||
|
||||
ItemName.pufferfish: CelesteItemData(celeste_base_id + 0x2000 + 0x15, ItemClassification.progression),
|
||||
ItemName.jellyfish: CelesteItemData(celeste_base_id + 0x2000 + 0x16, ItemClassification.progression),
|
||||
ItemName.breaker_boxes: CelesteItemData(celeste_base_id + 0x2000 + 0x17, ItemClassification.progression),
|
||||
ItemName.dash_refills: CelesteItemData(celeste_base_id + 0x2000 + 0x18, ItemClassification.progression),
|
||||
ItemName.double_dash_refills: CelesteItemData(celeste_base_id + 0x2000 + 0x19, ItemClassification.progression),
|
||||
ItemName.yellow_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x1A, ItemClassification.progression),
|
||||
ItemName.green_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x1B, ItemClassification.progression),
|
||||
ItemName.bird: CelesteItemData(celeste_base_id + 0x2000 + 0x23, ItemClassification.progression),
|
||||
|
||||
ItemName.dash_switches: CelesteItemData(celeste_base_id + 0x2000 + 0x1C, ItemClassification.progression),
|
||||
ItemName.seekers: CelesteItemData(celeste_base_id + 0x2000 + 0x1D, ItemClassification.progression),
|
||||
}
|
||||
|
||||
cassette_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.prologue_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x00, ItemClassification.filler),
|
||||
ItemName.fc_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x01, ItemClassification.filler),
|
||||
ItemName.fc_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x02, ItemClassification.filler),
|
||||
ItemName.fc_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x03, ItemClassification.filler),
|
||||
ItemName.os_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x04, ItemClassification.filler),
|
||||
ItemName.os_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x05, ItemClassification.filler),
|
||||
ItemName.os_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x06, ItemClassification.filler),
|
||||
ItemName.cr_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x07, ItemClassification.filler),
|
||||
ItemName.cr_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x08, ItemClassification.filler),
|
||||
ItemName.cr_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x09, ItemClassification.filler),
|
||||
ItemName.gr_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0A, ItemClassification.filler),
|
||||
ItemName.gr_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0B, ItemClassification.filler),
|
||||
ItemName.gr_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0C, ItemClassification.filler),
|
||||
ItemName.mt_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0D, ItemClassification.filler),
|
||||
ItemName.mt_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0E, ItemClassification.filler),
|
||||
ItemName.mt_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0F, ItemClassification.filler),
|
||||
ItemName.ref_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x10, ItemClassification.filler),
|
||||
ItemName.ref_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x11, ItemClassification.filler),
|
||||
ItemName.ref_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x12, ItemClassification.filler),
|
||||
ItemName.sum_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x13, ItemClassification.filler),
|
||||
ItemName.sum_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x14, ItemClassification.filler),
|
||||
ItemName.sum_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x15, ItemClassification.filler),
|
||||
ItemName.epilogue_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x16, ItemClassification.filler),
|
||||
ItemName.core_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x17, ItemClassification.filler),
|
||||
ItemName.core_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x18, ItemClassification.filler),
|
||||
ItemName.core_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x19, ItemClassification.filler),
|
||||
ItemName.farewell_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x1A, ItemClassification.filler),
|
||||
}
|
||||
|
||||
crystal_heart_item_data_table: dict[str, CelesteItemData] = {
|
||||
ItemName.crystal_heart_1: CelesteItemData(celeste_base_id + 0x3000 + 0x00, ItemClassification.filler),
|
||||
ItemName.crystal_heart_2: CelesteItemData(celeste_base_id + 0x3000 + 0x01, ItemClassification.filler),
|
||||
ItemName.crystal_heart_3: CelesteItemData(celeste_base_id + 0x3000 + 0x02, ItemClassification.filler),
|
||||
ItemName.crystal_heart_4: CelesteItemData(celeste_base_id + 0x3000 + 0x03, ItemClassification.filler),
|
||||
ItemName.crystal_heart_5: CelesteItemData(celeste_base_id + 0x3000 + 0x04, ItemClassification.filler),
|
||||
ItemName.crystal_heart_6: CelesteItemData(celeste_base_id + 0x3000 + 0x05, ItemClassification.filler),
|
||||
ItemName.crystal_heart_7: CelesteItemData(celeste_base_id + 0x3000 + 0x06, ItemClassification.filler),
|
||||
ItemName.crystal_heart_8: CelesteItemData(celeste_base_id + 0x3000 + 0x07, ItemClassification.filler),
|
||||
ItemName.crystal_heart_9: CelesteItemData(celeste_base_id + 0x3000 + 0x08, ItemClassification.filler),
|
||||
ItemName.crystal_heart_10: CelesteItemData(celeste_base_id + 0x3000 + 0x09, ItemClassification.filler),
|
||||
ItemName.crystal_heart_11: CelesteItemData(celeste_base_id + 0x3000 + 0x0A, ItemClassification.filler),
|
||||
ItemName.crystal_heart_12: CelesteItemData(celeste_base_id + 0x3000 + 0x0B, ItemClassification.filler),
|
||||
ItemName.crystal_heart_13: CelesteItemData(celeste_base_id + 0x3000 + 0x0C, ItemClassification.filler),
|
||||
ItemName.crystal_heart_14: CelesteItemData(celeste_base_id + 0x3000 + 0x0D, ItemClassification.filler),
|
||||
ItemName.crystal_heart_15: CelesteItemData(celeste_base_id + 0x3000 + 0x0E, ItemClassification.filler),
|
||||
ItemName.crystal_heart_16: CelesteItemData(celeste_base_id + 0x3000 + 0x0F, ItemClassification.filler),
|
||||
}
|
||||
|
||||
def add_checkpoint_to_table(id: int, name: str):
|
||||
checkpoint_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
|
||||
|
||||
def add_key_to_table(id: int, name: str):
|
||||
key_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
|
||||
|
||||
def add_gem_to_table(id: int, name: str):
|
||||
gem_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
|
||||
|
||||
def generate_item_data_table() -> dict[str, CelesteItemData]:
|
||||
return {**collectable_item_data_table,
|
||||
**goal_item_data_table,
|
||||
**trap_item_data_table,
|
||||
**checkpoint_item_data_table,
|
||||
**key_item_data_table,
|
||||
**gem_item_data_table,
|
||||
**cassette_item_data_table,
|
||||
**crystal_heart_item_data_table,
|
||||
**interactable_item_data_table}
|
||||
|
||||
|
||||
def generate_item_table() -> dict[str, int]:
|
||||
return {name: data.code for name, data in generate_item_data_table().items() if data.code is not None}
|
||||
|
||||
|
||||
def generate_item_groups() -> dict[str, list[str]]:
|
||||
item_groups: dict[str, list[str]] = {
|
||||
"Collectables": list(collectable_item_data_table.keys()),
|
||||
"Traps": list(trap_item_data_table.keys()),
|
||||
"Checkpoints": list(checkpoint_item_data_table.keys()),
|
||||
"Keys": list(key_item_data_table.keys()),
|
||||
"Gems": list(gem_item_data_table.keys()),
|
||||
"Cassettes": list(cassette_item_data_table.keys()),
|
||||
"Crystal Hearts": list(crystal_heart_item_data_table.keys()),
|
||||
"Interactables": list(interactable_item_data_table.keys()),
|
||||
|
||||
# Commonly mistaken names
|
||||
"Green Boosters": [ItemName.blue_boosters],
|
||||
"Green Bubbles": [ItemName.blue_boosters],
|
||||
"Blue Bubbles": [ItemName.blue_boosters],
|
||||
"Red Bubbles": [ItemName.red_boosters],
|
||||
"Touch Switches": [ItemName.coins],
|
||||
}
|
||||
|
||||
return item_groups
|
||||
27
worlds/celeste_open_world/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Modified MIT License
|
||||
|
||||
Copyright (c) 2025 PoryGone
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, and/or distribute copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
No copy or substantial portion of the Software shall be sublicensed or relicensed
|
||||
without the express written permission of the copyright holder(s)
|
||||
|
||||
No copy or substantial portion of the Software shall be sold without the express
|
||||
written permission of the copyright holder(s)
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
208
worlds/celeste_open_world/Levels.py
Normal file
@@ -0,0 +1,208 @@
|
||||
from __future__ import annotations
|
||||
from enum import IntEnum
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
|
||||
|
||||
goal_area_option_to_name: dict[int, str] = {
|
||||
0: "7a",
|
||||
1: "7b",
|
||||
2: "7c",
|
||||
3: "9a",
|
||||
4: "9b",
|
||||
5: "9c",
|
||||
6: "10a",
|
||||
7: "10b",
|
||||
8: "10c",
|
||||
}
|
||||
|
||||
|
||||
goal_area_option_to_display_name: dict[int, str] = {
|
||||
0: "The Summit A",
|
||||
1: "The Summit B",
|
||||
2: "The Summit C",
|
||||
3: "Core A",
|
||||
4: "Core B",
|
||||
5: "Core C",
|
||||
6: "Farewell",
|
||||
7: "Farewell",
|
||||
8: "Farewell",
|
||||
}
|
||||
|
||||
goal_area_to_location_name: dict[str, str] = {
|
||||
"7a": "The Summit A - Level Clear",
|
||||
"7b": "The Summit B - Level Clear",
|
||||
"7c": "The Summit C - Level Clear",
|
||||
"9a": "Core A - Level Clear",
|
||||
"9b": "Core B - Level Clear",
|
||||
"9c": "Core C - Level Clear",
|
||||
"10a": "Farewell - Crystal Heart?",
|
||||
"10b": "Farewell - Level Clear",
|
||||
"10c": "Farewell - Golden Strawberry",
|
||||
}
|
||||
|
||||
|
||||
class LocationType(IntEnum):
|
||||
strawberry = 0
|
||||
golden_strawberry = 1
|
||||
cassette = 2
|
||||
crystal_heart = 3
|
||||
checkpoint = 4
|
||||
level_clear = 5
|
||||
key = 6
|
||||
binoculars = 7
|
||||
room_enter = 8
|
||||
clutter = 9
|
||||
gem = 10
|
||||
car = 11
|
||||
|
||||
class DoorDirection(IntEnum):
|
||||
up = 0
|
||||
right = 1
|
||||
down = 2
|
||||
left = 3
|
||||
special = 4
|
||||
|
||||
|
||||
class Door:
|
||||
name: str
|
||||
room_name: str
|
||||
room: Room
|
||||
dir: DoorDirection
|
||||
blocked: bool
|
||||
closes_behind: bool
|
||||
region: PreRegion
|
||||
|
||||
def __init__(self, name: str, room_name: str, dir: DoorDirection, blocked: bool, closes_behind: bool):
|
||||
self.name = name
|
||||
self.room_name = room_name
|
||||
self.dir = dir
|
||||
self.blocked = blocked
|
||||
self.closes_behind = closes_behind
|
||||
# Find PreRegion later using our name once we know it exists
|
||||
|
||||
|
||||
class PreRegion:
|
||||
name: str
|
||||
room_name: str
|
||||
room: Room
|
||||
connections: list[RegionConnection]
|
||||
locations: list[LevelLocation]
|
||||
|
||||
def __init__(self, name: str, room_name: str, connections: list[RegionConnection], locations: list[LevelLocation]):
|
||||
self.name = name
|
||||
self.room_name = room_name
|
||||
self.connections = connections.copy()
|
||||
self.locations = locations.copy()
|
||||
|
||||
for loc in self.locations:
|
||||
loc.region = self
|
||||
|
||||
|
||||
class RegionConnection:
|
||||
source_name: str
|
||||
source: PreRegion
|
||||
destination_name: str
|
||||
destination: PreRegion
|
||||
possible_access: list[list[str]]
|
||||
|
||||
def __init__(self, source_name: str, destination_name: str, possible_access: list[list[str]] = []):
|
||||
self.source_name = source_name
|
||||
self.destination_name = destination_name
|
||||
self.possible_access = possible_access.copy()
|
||||
|
||||
|
||||
class LevelLocation:
|
||||
name: str
|
||||
display_name: str
|
||||
region_name: str
|
||||
region: PreRegion
|
||||
loc_type: LocationType
|
||||
possible_access: list[list[str]]
|
||||
|
||||
def __init__(self, name: str, display_name: str, region_name: str, loc_type: LocationType, possible_access: list[list[str]] = []):
|
||||
self.name = name
|
||||
self.display_name = display_name
|
||||
self.region_name = region_name
|
||||
self.loc_type = loc_type
|
||||
self.possible_access = possible_access.copy()
|
||||
|
||||
class Room:
|
||||
level_name: str
|
||||
name: str
|
||||
display_name: str
|
||||
regions: list[PreRegion]
|
||||
doors: list[Door]
|
||||
checkpoint: str
|
||||
checkpoint_region: str
|
||||
|
||||
def __init__(self, level_name: str, name: str, display_name: str, regions: list[PreRegion], doors: list[Door], checkpoint: str = None, checkpoint_region: str = None):
|
||||
self.level_name = level_name
|
||||
self.name = name
|
||||
self.display_name = display_name
|
||||
self.regions = regions.copy()
|
||||
self.doors = doors.copy()
|
||||
self.checkpoint = checkpoint
|
||||
self.checkpoint_region = checkpoint_region
|
||||
|
||||
from .data.CelesteLevelData import all_regions
|
||||
|
||||
for reg in self.regions:
|
||||
reg.room = self
|
||||
|
||||
for reg_con in reg.connections:
|
||||
reg_con.source = reg
|
||||
reg_con.destination = all_regions[reg_con.destination_name]
|
||||
|
||||
for door in self.doors:
|
||||
door.room = self
|
||||
|
||||
|
||||
class RoomConnection:
|
||||
level_name: str
|
||||
source: Door
|
||||
dest: Door
|
||||
two_way: bool
|
||||
|
||||
def __init__(self, level_name: str, source: Door, dest: Door):
|
||||
self.level_name = level_name
|
||||
self.source = source
|
||||
self.dest = dest
|
||||
self.two_way = not self.dest.closes_behind
|
||||
|
||||
if (self.source.dir == DoorDirection.left and self.dest.dir != DoorDirection.right or
|
||||
self.source.dir == DoorDirection.right and self.dest.dir != DoorDirection.left or
|
||||
self.source.dir == DoorDirection.up and self.dest.dir != DoorDirection.down or
|
||||
self.source.dir == DoorDirection.down and self.dest.dir != DoorDirection.up):
|
||||
raise Exception(f"Door {source.name} ({self.source.dir}) and Door {dest.name} ({self.dest.dir}) have mismatched directions.")
|
||||
|
||||
|
||||
class Level:
|
||||
name: str
|
||||
display_name: str
|
||||
rooms: list[Room]
|
||||
room_connections: list[RoomConnection]
|
||||
|
||||
def __init__(self, name: str, display_name: str, rooms: list[Room], room_connections: list[RoomConnection]):
|
||||
self.name = name
|
||||
self.display_name = display_name
|
||||
self.rooms = rooms.copy()
|
||||
self.room_connections = room_connections.copy()
|
||||
|
||||
|
||||
def load_logic_data() -> dict[str, Level]:
|
||||
from .data.CelesteLevelData import all_levels
|
||||
|
||||
#for _, level in all_levels.items():
|
||||
# print(level.display_name)
|
||||
#
|
||||
# for room in level.rooms:
|
||||
# print(" " + room.display_name)
|
||||
#
|
||||
# for region in room.regions:
|
||||
# print(" " + region.name)
|
||||
#
|
||||
# for location in region.locations:
|
||||
# print(" " + location.display_name)
|
||||
|
||||
return all_levels
|
||||
281
worlds/celeste_open_world/Locations.py
Normal file
@@ -0,0 +1,281 @@
|
||||
from typing import NamedTuple, Optional, TYPE_CHECKING
|
||||
|
||||
from BaseClasses import Location, Region
|
||||
from worlds.generic.Rules import set_rule
|
||||
|
||||
from .Levels import Level, LocationType
|
||||
from .Names import ItemName
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import CelesteOpenWorld
|
||||
else:
|
||||
CelesteOpenWorld = object
|
||||
|
||||
|
||||
celeste_base_id: int = 0xCA10000
|
||||
|
||||
|
||||
class CelesteLocation(Location):
|
||||
game = "Celeste"
|
||||
|
||||
|
||||
class CelesteLocationData(NamedTuple):
|
||||
region: str
|
||||
address: Optional[int] = None
|
||||
|
||||
|
||||
checkpoint_location_data_table: dict[str, CelesteLocationData] = {}
|
||||
key_location_data_table: dict[str, CelesteLocationData] = {}
|
||||
|
||||
location_id_offsets: dict[LocationType, int | None] = {
|
||||
LocationType.strawberry: celeste_base_id,
|
||||
LocationType.golden_strawberry: celeste_base_id + 0x1000,
|
||||
LocationType.cassette: celeste_base_id + 0x2000,
|
||||
LocationType.car: celeste_base_id + 0x2A00,
|
||||
LocationType.crystal_heart: celeste_base_id + 0x3000,
|
||||
LocationType.checkpoint: celeste_base_id + 0x4000,
|
||||
LocationType.level_clear: celeste_base_id + 0x5000,
|
||||
LocationType.key: celeste_base_id + 0x6000,
|
||||
LocationType.gem: celeste_base_id + 0x6A00,
|
||||
LocationType.binoculars: celeste_base_id + 0x7000,
|
||||
LocationType.room_enter: celeste_base_id + 0x8000,
|
||||
LocationType.clutter: None,
|
||||
}
|
||||
|
||||
|
||||
def generate_location_table() -> dict[str, int]:
|
||||
from .Levels import Level, LocationType, load_logic_data
|
||||
level_data: dict[str, Level] = load_logic_data()
|
||||
location_table = {}
|
||||
|
||||
location_counts: dict[LocationType, int] = {
|
||||
LocationType.strawberry: 0,
|
||||
LocationType.golden_strawberry: 0,
|
||||
LocationType.cassette: 0,
|
||||
LocationType.car: 0,
|
||||
LocationType.crystal_heart: 0,
|
||||
LocationType.checkpoint: 0,
|
||||
LocationType.level_clear: 0,
|
||||
LocationType.key: 0,
|
||||
LocationType.gem: 0,
|
||||
LocationType.binoculars: 0,
|
||||
LocationType.room_enter: 0,
|
||||
}
|
||||
|
||||
for _, level in level_data.items():
|
||||
for room in level.rooms:
|
||||
if room.name != "10b_GOAL":
|
||||
location_table[room.display_name] = location_id_offsets[LocationType.room_enter] + location_counts[LocationType.room_enter]
|
||||
location_counts[LocationType.room_enter] += 1
|
||||
|
||||
if room.checkpoint is not None and room.checkpoint != "Start":
|
||||
checkpoint_id: int = location_id_offsets[LocationType.checkpoint] + location_counts[LocationType.checkpoint]
|
||||
checkpoint_name: str = level.display_name + " - " + room.checkpoint
|
||||
location_table[checkpoint_name] = checkpoint_id
|
||||
location_counts[LocationType.checkpoint] += 1
|
||||
checkpoint_location_data_table[checkpoint_name] = CelesteLocationData(level.display_name, checkpoint_id)
|
||||
|
||||
from .Items import add_checkpoint_to_table
|
||||
add_checkpoint_to_table(checkpoint_id, checkpoint_name)
|
||||
|
||||
for region in room.regions:
|
||||
for location in region.locations:
|
||||
if location_id_offsets[location.loc_type] is not None:
|
||||
location_id = location_id_offsets[location.loc_type] + location_counts[location.loc_type]
|
||||
location_table[location.display_name] = location_id
|
||||
location_counts[location.loc_type] += 1
|
||||
|
||||
if location.loc_type == LocationType.key:
|
||||
from .Items import add_key_to_table
|
||||
add_key_to_table(location_id, location.display_name)
|
||||
|
||||
if location.loc_type == LocationType.gem:
|
||||
from .Items import add_gem_to_table
|
||||
add_gem_to_table(location_id, location.display_name)
|
||||
|
||||
return location_table
|
||||
|
||||
|
||||
def create_regions_and_locations(world: CelesteOpenWorld):
|
||||
menu_region = Region("Menu", world.player, world.multiworld)
|
||||
world.multiworld.regions.append(menu_region)
|
||||
|
||||
world.active_checkpoint_names: list[str] = []
|
||||
world.goal_checkpoint_names: dict[str, str] = dict()
|
||||
world.active_key_names: list[str] = []
|
||||
world.active_gem_names: list[str] = []
|
||||
world.active_clutter_names: list[str] = []
|
||||
|
||||
for _, level in world.level_data.items():
|
||||
if level.name not in world.active_levels:
|
||||
continue
|
||||
|
||||
for room in level.rooms:
|
||||
room_region = Region(room.name + "_room", world.player, world.multiworld)
|
||||
world.multiworld.regions.append(room_region)
|
||||
|
||||
for pre_region in room.regions:
|
||||
region = Region(pre_region.name, world.player, world.multiworld)
|
||||
world.multiworld.regions.append(region)
|
||||
|
||||
for level_location in pre_region.locations:
|
||||
if level_location.loc_type == LocationType.golden_strawberry:
|
||||
if level_location.display_name == "Farewell - Golden Strawberry":
|
||||
if not world.options.goal_area == "farewell_golden":
|
||||
continue
|
||||
elif not world.options.include_goldens:
|
||||
continue
|
||||
|
||||
if level_location.loc_type == LocationType.car and not world.options.carsanity:
|
||||
continue
|
||||
|
||||
if level_location.loc_type == LocationType.binoculars and not world.options.binosanity:
|
||||
continue
|
||||
|
||||
if level_location.loc_type == LocationType.key:
|
||||
world.active_key_names.append(level_location.display_name)
|
||||
|
||||
if level_location.loc_type == LocationType.gem:
|
||||
world.active_gem_names.append(level_location.display_name)
|
||||
|
||||
location_rule = None
|
||||
if len(level_location.possible_access) == 1:
|
||||
only_access = level_location.possible_access[0]
|
||||
if len(only_access) == 1:
|
||||
only_item = level_location.possible_access[0][0]
|
||||
def location_rule_func(state, only_item=only_item):
|
||||
return state.has(only_item, world.player)
|
||||
location_rule = location_rule_func
|
||||
else:
|
||||
def location_rule_func(state, only_access=only_access):
|
||||
return state.has_all(only_access, world.player)
|
||||
location_rule = location_rule_func
|
||||
elif len(level_location.possible_access) > 0:
|
||||
def location_rule_func(state, level_location=level_location):
|
||||
for sublist in level_location.possible_access:
|
||||
if state.has_all(sublist, world.player):
|
||||
return True
|
||||
return False
|
||||
location_rule = location_rule_func
|
||||
|
||||
if level_location.loc_type == LocationType.clutter:
|
||||
world.active_clutter_names.append(level_location.display_name)
|
||||
location = CelesteLocation(world.player, level_location.display_name, None, region)
|
||||
if location_rule is not None:
|
||||
set_rule(location, location_rule)
|
||||
region.locations.append(location)
|
||||
continue
|
||||
|
||||
location = CelesteLocation(world.player, level_location.display_name, world.location_name_to_id[level_location.display_name], region)
|
||||
if location_rule is not None:
|
||||
set_rule(location, location_rule)
|
||||
region.locations.append(location)
|
||||
|
||||
for pre_region in room.regions:
|
||||
region = world.get_region(pre_region.name)
|
||||
for connection in pre_region.connections:
|
||||
connection_rule = None
|
||||
if len(connection.possible_access) == 1:
|
||||
only_access = connection.possible_access[0]
|
||||
if len(only_access) == 1:
|
||||
only_item = connection.possible_access[0][0]
|
||||
def connection_rule_func(state, only_item=only_item):
|
||||
return state.has(only_item, world.player)
|
||||
connection_rule = connection_rule_func
|
||||
else:
|
||||
def connection_rule_func(state, only_access=only_access):
|
||||
return state.has_all(only_access, world.player)
|
||||
connection_rule = connection_rule_func
|
||||
elif len(connection.possible_access) > 0:
|
||||
def connection_rule_func(state, connection=connection):
|
||||
for sublist in connection.possible_access:
|
||||
if state.has_all(sublist, world.player):
|
||||
return True
|
||||
return False
|
||||
|
||||
connection_rule = connection_rule_func
|
||||
|
||||
if connection_rule is None:
|
||||
region.add_exits([connection.destination_name])
|
||||
else:
|
||||
region.add_exits([connection.destination_name], {connection.destination_name: connection_rule})
|
||||
region.add_exits([room_region.name])
|
||||
|
||||
if room.checkpoint != None:
|
||||
if room.checkpoint == "Start":
|
||||
if world.options.lock_goal_area and (level.name == world.goal_area or (level.name[:2] == world.goal_area[:2] == "10")):
|
||||
world.goal_start_region: str = room.checkpoint_region
|
||||
elif level.name == "8a":
|
||||
world.epilogue_start_region: str = room.checkpoint_region
|
||||
else:
|
||||
menu_region.add_exits([room.checkpoint_region])
|
||||
else:
|
||||
checkpoint_location_name = level.display_name + " - " + room.checkpoint
|
||||
world.active_checkpoint_names.append(checkpoint_location_name)
|
||||
checkpoint_rule = lambda state, checkpoint_location_name=checkpoint_location_name: state.has(checkpoint_location_name, world.player)
|
||||
room_region.add_locations({
|
||||
checkpoint_location_name: world.location_name_to_id[checkpoint_location_name]
|
||||
}, CelesteLocation)
|
||||
|
||||
if world.options.lock_goal_area and (level.name == world.goal_area or (level.name[:2] == world.goal_area[:2] == "10")):
|
||||
world.goal_checkpoint_names[room.checkpoint_region] = checkpoint_location_name
|
||||
else:
|
||||
menu_region.add_exits([room.checkpoint_region], {room.checkpoint_region: checkpoint_rule})
|
||||
|
||||
if world.options.roomsanity:
|
||||
if room.name != "10b_GOAL":
|
||||
room_location_name = room.display_name
|
||||
room_region.add_locations({
|
||||
room_location_name: world.location_name_to_id[room_location_name]
|
||||
}, CelesteLocation)
|
||||
|
||||
for room_connection in level.room_connections:
|
||||
source_region = world.get_region(room_connection.source.name)
|
||||
source_region.add_exits([room_connection.dest.name])
|
||||
if room_connection.two_way:
|
||||
dest_region = world.get_region(room_connection.dest.name)
|
||||
dest_region.add_exits([room_connection.source.name])
|
||||
|
||||
if level.name == "10b":
|
||||
# Manually connect the two parts of Farewell
|
||||
source_region = world.get_region("10a_e-08_east")
|
||||
source_region.add_exits(["10b_f-door_west"])
|
||||
|
||||
if level.name == "10c":
|
||||
# Manually connect the Golden room of Farewell
|
||||
golden_items: list[str] = [ItemName.traffic_blocks, ItemName.dash_refills, ItemName.double_dash_refills, ItemName.dream_blocks, ItemName.swap_blocks, ItemName.move_blocks, ItemName.blue_boosters, ItemName.springs, ItemName.feathers, ItemName.coins, ItemName.red_boosters, ItemName.kevin_blocks, ItemName.core_blocks, ItemName.fire_ice_balls, ItemName.badeline_boosters, ItemName.bird, ItemName.breaker_boxes, ItemName.pufferfish, ItemName.jellyfish, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks, ItemName.yellow_cassette_blocks, ItemName.green_cassette_blocks]
|
||||
golden_rule = lambda state: state.has_all(golden_items, world.player)
|
||||
|
||||
source_region_end = world.get_region("10b_j-19_top")
|
||||
source_region_end.add_exits(["10c_end-golden_bottom"], {"10c_end-golden_bottom": golden_rule})
|
||||
source_region_moon = world.get_region("10b_j-16_east")
|
||||
source_region_moon.add_exits(["10c_end-golden_bottom"], {"10c_end-golden_bottom": golden_rule})
|
||||
source_region_golden = world.get_region("10c_end-golden_top")
|
||||
source_region_golden.add_exits(["10b_GOAL_main"])
|
||||
|
||||
|
||||
location_data_table: dict[str, int] = generate_location_table()
|
||||
|
||||
|
||||
def generate_location_groups() -> dict[str, list[str]]:
|
||||
from .Levels import Level, LocationType, load_logic_data
|
||||
level_data: dict[str, Level] = load_logic_data()
|
||||
|
||||
location_groups: dict[str, list[str]] = {
|
||||
"Strawberries": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.strawberry] and id < location_id_offsets[LocationType.golden_strawberry]],
|
||||
"Golden Strawberries": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.golden_strawberry] and id < location_id_offsets[LocationType.cassette]],
|
||||
"Cassettes": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.cassette] and id < location_id_offsets[LocationType.car]],
|
||||
"Cars": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.car] and id < location_id_offsets[LocationType.crystal_heart]],
|
||||
"Crystal Hearts": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.crystal_heart] and id < location_id_offsets[LocationType.checkpoint]],
|
||||
"Checkpoints": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.checkpoint] and id < location_id_offsets[LocationType.level_clear]],
|
||||
"Level Clears": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.level_clear] and id < location_id_offsets[LocationType.key]],
|
||||
"Keys": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.key] and id < location_id_offsets[LocationType.gem]],
|
||||
"Gems": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.gem] and id < location_id_offsets[LocationType.binoculars]],
|
||||
"Binoculars": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.binoculars] and id < location_id_offsets[LocationType.room_enter]],
|
||||
"Rooms": [name for name, id in location_data_table.items() if id >= location_id_offsets[LocationType.room_enter]],
|
||||
}
|
||||
|
||||
for level in level_data.values():
|
||||
location_groups.update({level.display_name: [loc_name for loc_name, id in location_data_table.items() if level.display_name in loc_name]})
|
||||
|
||||
return location_groups
|
||||
210
worlds/celeste_open_world/Names/ItemName.py
Normal file
@@ -0,0 +1,210 @@
|
||||
# Collectables
|
||||
strawberry = "Strawberry"
|
||||
raspberry = "Raspberry"
|
||||
|
||||
# Goal Items
|
||||
house_keys = "Granny's House Keys"
|
||||
victory = "Victory"
|
||||
|
||||
# Traps
|
||||
bald_trap = "Bald Trap"
|
||||
literature_trap = "Literature Trap"
|
||||
stun_trap = "Stun Trap"
|
||||
invisible_trap = "Invisible Trap"
|
||||
fast_trap = "Fast Trap"
|
||||
slow_trap = "Slow Trap"
|
||||
ice_trap = "Ice Trap"
|
||||
reverse_trap = "Reverse Trap"
|
||||
screen_flip_trap = "Screen Flip Trap"
|
||||
laughter_trap = "Laughter Trap"
|
||||
hiccup_trap = "Hiccup Trap"
|
||||
zoom_trap = "Zoom Trap"
|
||||
|
||||
# Movement
|
||||
dash = "Dash"
|
||||
u_dash = "Up Dash"
|
||||
r_dash = "Right Dash"
|
||||
d_dash = "Down Dash"
|
||||
l_dash = "Left Dash"
|
||||
ur_dash = "Up-Right Dash"
|
||||
dr_dash = "Down-Right Dash"
|
||||
dl_dash = "Down-Left Dash"
|
||||
ul_dash = "Up-Left Dash"
|
||||
|
||||
# Interactables
|
||||
springs = "Springs"
|
||||
traffic_blocks = "Traffic Blocks"
|
||||
pink_cassette_blocks = "Pink Cassette Blocks"
|
||||
blue_cassette_blocks = "Blue Cassette Blocks"
|
||||
|
||||
dream_blocks = "Dream Blocks"
|
||||
coins = "Coins"
|
||||
strawberry_seeds = "Strawberry Seeds"
|
||||
|
||||
sinking_platforms = "Sinking Platforms"
|
||||
|
||||
moving_platforms = "Moving Platforms"
|
||||
blue_boosters = "Blue Boosters"
|
||||
blue_clouds = "Blue Clouds"
|
||||
move_blocks = "Move Blocks"
|
||||
white_block = "White Block"
|
||||
|
||||
swap_blocks = "Swap Blocks"
|
||||
red_boosters = "Red Boosters"
|
||||
torches = "Torches"
|
||||
theo_crystal = "Theo Crystal"
|
||||
|
||||
feathers = "Feathers"
|
||||
bumpers = "Bumpers"
|
||||
kevin_blocks = "Kevins"
|
||||
|
||||
pink_clouds = "Pink Clouds"
|
||||
badeline_boosters = "Badeline Boosters"
|
||||
|
||||
fire_ice_balls = "Fire and Ice Balls"
|
||||
core_toggles = "Core Toggles"
|
||||
core_blocks = "Core Blocks"
|
||||
|
||||
pufferfish = "Pufferfish"
|
||||
jellyfish = "Jellyfish"
|
||||
breaker_boxes = "Breaker Boxes"
|
||||
dash_refills = "Dash Refills"
|
||||
double_dash_refills = "Double Dash Refills"
|
||||
yellow_cassette_blocks = "Yellow Cassette Blocks"
|
||||
green_cassette_blocks = "Green Cassette Blocks"
|
||||
|
||||
dash_switches = "Dash Switches"
|
||||
seekers = "Seekers"
|
||||
bird = "Bird"
|
||||
|
||||
brown_clutter = "Celestial Resort A - Brown Clutter"
|
||||
green_clutter = "Celestial Resort A - Green Clutter"
|
||||
pink_clutter = "Celestial Resort A - Pink Clutter"
|
||||
|
||||
cannot_access = "Cannot Access"
|
||||
|
||||
# Checkpoints
|
||||
fc_a_checkpoint_1 = "Forsaken City A - Crossing"
|
||||
fc_a_checkpoint_2 = "Forsaken City A - Chasm"
|
||||
|
||||
fc_b_checkpoint_1 = "Forsaken City B - Contraption"
|
||||
fc_b_checkpoint_2 = "Forsaken City B - Scrap Pit"
|
||||
|
||||
os_a_checkpoint_1 = "Old Site A - Intervention"
|
||||
os_a_checkpoint_2 = "Old Site A - Awake"
|
||||
|
||||
os_b_checkpoint_1 = "Old Site B - Combination Lock"
|
||||
os_b_checkpoint_2 = "Old Site B - Dream Altar"
|
||||
|
||||
cr_a_checkpoint_1 = "Celestial Resort A - Huge Mess"
|
||||
cr_a_checkpoint_2 = "Celestial Resort A - Elevator Shaft"
|
||||
cr_a_checkpoint_3 = "Celestial Resort A - Presidential Suite"
|
||||
|
||||
cr_b_checkpoint_1 = "Celestial Resort B - Staff Quarters"
|
||||
cr_b_checkpoint_2 = "Celestial Resort B - Library"
|
||||
cr_b_checkpoint_3 = "Celestial Resort B - Rooftop"
|
||||
|
||||
gr_a_checkpoint_1 = "Golden Ridge A - Shrine"
|
||||
gr_a_checkpoint_2 = "Golden Ridge A - Old Trail"
|
||||
gr_a_checkpoint_3 = "Golden Ridge A - Cliff Face"
|
||||
|
||||
gr_b_checkpoint_1 = "Golden Ridge B - Stepping Stones"
|
||||
gr_b_checkpoint_2 = "Golden Ridge B - Gusty Canyon"
|
||||
gr_b_checkpoint_3 = "Golden Ridge B - Eye of the Storm"
|
||||
|
||||
mt_a_checkpoint_1 = "Mirror Temple A - Depths"
|
||||
mt_a_checkpoint_2 = "Mirror Temple A - Unravelling"
|
||||
mt_a_checkpoint_3 = "Mirror Temple A - Search"
|
||||
mt_a_checkpoint_4 = "Mirror Temple A - Rescue"
|
||||
|
||||
mt_b_checkpoint_1 = "Mirror Temple B - Central Chamber"
|
||||
mt_b_checkpoint_2 = "Mirror Temple B - Through the Mirror"
|
||||
mt_b_checkpoint_3 = "Mirror Temple B - Mix Master"
|
||||
|
||||
ref_a_checkpoint_1 = "Reflection A - Lake"
|
||||
ref_a_checkpoint_2 = "Reflection A - Hollows"
|
||||
ref_a_checkpoint_3 = "Reflection A - Reflection"
|
||||
ref_a_checkpoint_4 = "Reflection A - Rock Bottom"
|
||||
ref_a_checkpoint_5 = "Reflection A - Resolution"
|
||||
|
||||
ref_b_checkpoint_1 = "Reflection B - Reflection"
|
||||
ref_b_checkpoint_2 = "Reflection B - Rock Bottom"
|
||||
ref_b_checkpoint_3 = "Reflection B - Reprieve"
|
||||
|
||||
sum_a_checkpoint_1 = "The Summit A - 500 M"
|
||||
sum_a_checkpoint_2 = "The Summit A - 1000 M"
|
||||
sum_a_checkpoint_3 = "The Summit A - 1500 M"
|
||||
sum_a_checkpoint_4 = "The Summit A - 2000 M"
|
||||
sum_a_checkpoint_5 = "The Summit A - 2500 M"
|
||||
sum_a_checkpoint_6 = "The Summit A - 3000 M"
|
||||
|
||||
sum_b_checkpoint_1 = "The Summit B - 500 M"
|
||||
sum_b_checkpoint_2 = "The Summit B - 1000 M"
|
||||
sum_b_checkpoint_3 = "The Summit B - 1500 M"
|
||||
sum_b_checkpoint_4 = "The Summit B - 2000 M"
|
||||
sum_b_checkpoint_5 = "The Summit B - 2500 M"
|
||||
sum_b_checkpoint_6 = "The Summit B - 3000 M"
|
||||
|
||||
core_a_checkpoint_1 = "Core A - Into the Core"
|
||||
core_a_checkpoint_2 = "Core A - Hot and Cold"
|
||||
core_a_checkpoint_3 = "Core A - Heart of the Mountain"
|
||||
|
||||
core_b_checkpoint_1 = "Core B - Into the Core"
|
||||
core_b_checkpoint_2 = "Core B - Burning or Freezing"
|
||||
core_b_checkpoint_3 = "Core B - Heartbeat"
|
||||
|
||||
farewell_checkpoint_1 = "Farewell - Singular"
|
||||
farewell_checkpoint_2 = "Farewell - Power Source"
|
||||
farewell_checkpoint_3 = "Farewell - Remembered"
|
||||
farewell_checkpoint_4 = "Farewell - Event Horizon"
|
||||
farewell_checkpoint_5 = "Farewell - Determination"
|
||||
farewell_checkpoint_6 = "Farewell - Stubbornness"
|
||||
farewell_checkpoint_7 = "Farewell - Reconcilliation"
|
||||
farewell_checkpoint_8 = "Farewell - Farewell"
|
||||
|
||||
# Cassettes
|
||||
prologue_cassette = "Prologue Cassette"
|
||||
fc_a_cassette = "Forsaken City Cassette - A Side"
|
||||
fc_b_cassette = "Forsaken City Cassette - B Side"
|
||||
fc_c_cassette = "Forsaken City Cassette - C Side"
|
||||
os_a_cassette = "Old Site Cassette - A Side"
|
||||
os_b_cassette = "Old Site Cassette - B Side"
|
||||
os_c_cassette = "Old Site Cassette - C Side"
|
||||
cr_a_cassette = "Celestial Resort Cassette - A Side"
|
||||
cr_b_cassette = "Celestial Resort Cassette - B Side"
|
||||
cr_c_cassette = "Celestial Resort Cassette - C Side"
|
||||
gr_a_cassette = "Golden Ridge Cassette - A Side"
|
||||
gr_b_cassette = "Golden Ridge Cassette - B Side"
|
||||
gr_c_cassette = "Golden Ridge Cassette - C Side"
|
||||
mt_a_cassette = "Mirror Temple Cassette - A Side"
|
||||
mt_b_cassette = "Mirror Temple Cassette - B Side"
|
||||
mt_c_cassette = "Mirror Temple Cassette - C Side"
|
||||
ref_a_cassette = "Reflection Cassette - A Side"
|
||||
ref_b_cassette = "Reflection Cassette - B Side"
|
||||
ref_c_cassette = "Reflection Cassette - C Side"
|
||||
sum_a_cassette = "The Summit Cassette - A Side"
|
||||
sum_b_cassette = "The Summit Cassette - B Side"
|
||||
sum_c_cassette = "The Summit Cassette - C Side"
|
||||
epilogue_cassette = "Epilogue Cassette"
|
||||
core_a_cassette = "Core Cassette - A Side"
|
||||
core_b_cassette = "Core Cassette - B Side"
|
||||
core_c_cassette = "Core Cassette - C Side"
|
||||
farewell_cassette = "Farewell Cassette"
|
||||
|
||||
# Crystal Hearts
|
||||
crystal_heart_1 = "Crystal Heart 1"
|
||||
crystal_heart_2 = "Crystal Heart 2"
|
||||
crystal_heart_3 = "Crystal Heart 3"
|
||||
crystal_heart_4 = "Crystal Heart 4"
|
||||
crystal_heart_5 = "Crystal Heart 5"
|
||||
crystal_heart_6 = "Crystal Heart 6"
|
||||
crystal_heart_7 = "Crystal Heart 7"
|
||||
crystal_heart_8 = "Crystal Heart 8"
|
||||
crystal_heart_9 = "Crystal Heart 9"
|
||||
crystal_heart_10 = "Crystal Heart 10"
|
||||
crystal_heart_11 = "Crystal Heart 11"
|
||||
crystal_heart_12 = "Crystal Heart 12"
|
||||
crystal_heart_13 = "Crystal Heart 13"
|
||||
crystal_heart_14 = "Crystal Heart 14"
|
||||
crystal_heart_15 = "Crystal Heart 15"
|
||||
crystal_heart_16 = "Crystal Heart 16"
|
||||
0
worlds/celeste_open_world/Names/__init__.py
Normal file
528
worlds/celeste_open_world/Options.py
Normal file
@@ -0,0 +1,528 @@
|
||||
from dataclasses import dataclass
|
||||
import random
|
||||
|
||||
from Options import Choice, Range, DefaultOnToggle, Toggle, TextChoice, DeathLink, OptionGroup, PerGameCommonOptions, OptionError
|
||||
from worlds.AutoWorld import World
|
||||
|
||||
|
||||
class DeathLinkAmnesty(Range):
|
||||
"""
|
||||
How many deaths it takes to send a DeathLink
|
||||
"""
|
||||
display_name = "Death Link Amnesty"
|
||||
range_start = 1
|
||||
range_end = 30
|
||||
default = 10
|
||||
|
||||
class TrapLink(Toggle):
|
||||
"""
|
||||
Whether your received traps are linked to other players
|
||||
|
||||
You will also receive any linked traps from other players with Trap Link enabled,
|
||||
if you have a weight above "none" set for that trap
|
||||
"""
|
||||
display_name = "Trap Link"
|
||||
|
||||
|
||||
class GoalArea(Choice):
|
||||
"""
|
||||
What Area must be cleared to gain access to the Epilogue and complete the game
|
||||
"""
|
||||
display_name = "Goal Area"
|
||||
option_the_summit_a = 0
|
||||
option_the_summit_b = 1
|
||||
option_the_summit_c = 2
|
||||
option_core_a = 3
|
||||
option_core_b = 4
|
||||
option_core_c = 5
|
||||
option_empty_space = 6
|
||||
option_farewell = 7
|
||||
option_farewell_golden = 8
|
||||
default = 0
|
||||
|
||||
class LockGoalArea(DefaultOnToggle):
|
||||
"""
|
||||
Determines whether your Goal Area will be locked until you receive your required Strawberries, or only the Epilogue
|
||||
"""
|
||||
display_name = "Lock Goal Area"
|
||||
|
||||
class GoalAreaCheckpointsanity(Toggle):
|
||||
"""
|
||||
Determines whether the Checkpoints in your Goal Area will be shuffled into the item pool (if Checkpointsanity is active)
|
||||
"""
|
||||
display_name = "Goal Area Checkpointsanity"
|
||||
|
||||
class TotalStrawberries(Range):
|
||||
"""
|
||||
Maximum number of how many Strawberries can exist
|
||||
"""
|
||||
display_name = "Total Strawberries"
|
||||
range_start = 0
|
||||
range_end = 202
|
||||
default = 50
|
||||
|
||||
class StrawberriesRequiredPercentage(Range):
|
||||
"""
|
||||
Percentage of existing Strawberries you must receive to access your Goal Area (if Lock Goal Area is active) and the Epilogue
|
||||
"""
|
||||
display_name = "Strawberries Required Percentage"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 80
|
||||
|
||||
|
||||
class Checkpointsanity(Toggle):
|
||||
"""
|
||||
Determines whether Checkpoints will be shuffled into the item pool
|
||||
"""
|
||||
display_name = "Checkpointsanity"
|
||||
|
||||
class Binosanity(Toggle):
|
||||
"""
|
||||
Determines whether using Binoculars sends location checks
|
||||
"""
|
||||
display_name = "Binosanity"
|
||||
|
||||
class Keysanity(Toggle):
|
||||
"""
|
||||
Determines whether individual Keys are shuffled into the item pool
|
||||
"""
|
||||
display_name = "Keysanity"
|
||||
|
||||
class Gemsanity(Toggle):
|
||||
"""
|
||||
Determines whether Summit Gems are shuffled into the item pool
|
||||
"""
|
||||
display_name = "Gemsanity"
|
||||
|
||||
class Carsanity(Toggle):
|
||||
"""
|
||||
Determines whether riding on cars grants location checks
|
||||
"""
|
||||
display_name = "Carsanity"
|
||||
|
||||
class Roomsanity(Toggle):
|
||||
"""
|
||||
Determines whether entering individual rooms sends location checks
|
||||
"""
|
||||
display_name = "Roomsanity"
|
||||
|
||||
class IncludeGoldens(Toggle):
|
||||
"""
|
||||
Determines whether collecting Golden Strawberries sends location checks
|
||||
"""
|
||||
display_name = "Include Goldens"
|
||||
|
||||
|
||||
class IncludeCore(Toggle):
|
||||
"""
|
||||
Determines whether Chapter 8 - Core Levels will be included
|
||||
"""
|
||||
display_name = "Include Core"
|
||||
|
||||
class IncludeFarewell(Choice):
|
||||
"""
|
||||
Determines how much of Chapter 9 - Farewell Level will be included
|
||||
"""
|
||||
display_name = "Include Farewell"
|
||||
option_none = 0
|
||||
option_empty_space = 1
|
||||
option_farewell = 2
|
||||
default = 0
|
||||
|
||||
class IncludeBSides(Toggle):
|
||||
"""
|
||||
Determines whether the B-Side Levels will be included
|
||||
"""
|
||||
display_name = "Include B-Sides"
|
||||
|
||||
class IncludeCSides(Toggle):
|
||||
"""
|
||||
Determines whether the C-Side Levels will be included
|
||||
"""
|
||||
display_name = "Include C-Sides"
|
||||
|
||||
|
||||
class JunkFillPercentage(Range):
|
||||
"""
|
||||
Replace a percentage of non-required Strawberries in the item pool with junk items
|
||||
"""
|
||||
display_name = "Junk Fill Percentage"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 50
|
||||
|
||||
class TrapFillPercentage(Range):
|
||||
"""
|
||||
Replace a percentage of junk items in the item pool with random traps
|
||||
"""
|
||||
display_name = "Trap Fill Percentage"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
|
||||
class TrapExpirationAction(Choice):
|
||||
"""
|
||||
The type of action which causes traps to wear off
|
||||
"""
|
||||
display_name = "Trap Expiration Action"
|
||||
option_return_to_menu = 0
|
||||
option_deaths = 1
|
||||
option_new_screens = 2
|
||||
default = 1
|
||||
|
||||
class TrapExpirationAmount(Range):
|
||||
"""
|
||||
The amount of the selected Trap Expiration Action that must occur for the trap to wear off
|
||||
"""
|
||||
display_name = "Trap Expiration Amount"
|
||||
range_start = 1
|
||||
range_end = 10
|
||||
default = 5
|
||||
|
||||
class BaseTrapWeight(Choice):
|
||||
"""
|
||||
Base Class for Trap Weights
|
||||
"""
|
||||
option_none = 0
|
||||
option_low = 1
|
||||
option_medium = 2
|
||||
option_high = 4
|
||||
default = 2
|
||||
|
||||
class BaldTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which makes Maddy bald
|
||||
"""
|
||||
display_name = "Bald Trap Weight"
|
||||
|
||||
class LiteratureTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes the player to read literature
|
||||
"""
|
||||
display_name = "Literature Trap Weight"
|
||||
|
||||
class StunTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which briefly stuns Maddy
|
||||
"""
|
||||
display_name = "Stun Trap Weight"
|
||||
|
||||
class InvisibleTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which turns Maddy invisible
|
||||
"""
|
||||
display_name = "Invisible Trap Weight"
|
||||
|
||||
class FastTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which increases the game speed
|
||||
"""
|
||||
display_name = "Fast Trap Weight"
|
||||
|
||||
class SlowTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which decreases the game speed
|
||||
"""
|
||||
display_name = "Slow Trap Weight"
|
||||
|
||||
class IceTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes the level to become slippery
|
||||
"""
|
||||
display_name = "Ice Trap Weight"
|
||||
|
||||
class ReverseTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes the controls to be reversed
|
||||
"""
|
||||
display_name = "Reverse Trap Weight"
|
||||
|
||||
class ScreenFlipTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes the screen to be flipped
|
||||
"""
|
||||
display_name = "Screen Flip Trap Weight"
|
||||
|
||||
class LaughterTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes Maddy to laugh uncontrollably
|
||||
"""
|
||||
display_name = "Laughter Trap Weight"
|
||||
|
||||
class HiccupTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes Maddy to hiccup uncontrollably
|
||||
"""
|
||||
display_name = "Hiccup Trap Weight"
|
||||
|
||||
class ZoomTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of a receiving a trap which causes the camera to focus on Maddy
|
||||
"""
|
||||
display_name = "Zoom Trap Weight"
|
||||
|
||||
|
||||
class MusicShuffle(Choice):
|
||||
"""
|
||||
Music shuffle type
|
||||
|
||||
None: No Music is shuffled
|
||||
|
||||
Consistent: Each music track is consistently shuffled throughout the game
|
||||
|
||||
Singularity: The entire game uses one song for levels
|
||||
"""
|
||||
display_name = "Music Shuffle"
|
||||
option_none = 0
|
||||
option_consistent = 1
|
||||
option_singularity = 2
|
||||
default = 0
|
||||
|
||||
class RequireCassettes(Toggle):
|
||||
"""
|
||||
Determines whether you must receive a level's Cassette Item to hear that level's music
|
||||
"""
|
||||
display_name = "Require Cassettes"
|
||||
|
||||
|
||||
class MadelineHairLength(Choice):
|
||||
"""
|
||||
How long Madeline's hair is
|
||||
"""
|
||||
display_name = "Madeline Hair Length"
|
||||
option_very_short = 1
|
||||
option_short = 2
|
||||
option_default = 4
|
||||
option_long = 7
|
||||
option_very_long = 10
|
||||
option_absurd = 20
|
||||
default = 4
|
||||
|
||||
|
||||
class ColorChoice(TextChoice):
|
||||
option_strawberry = 0xAC3232
|
||||
option_empty = 0x44B7FF
|
||||
option_double = 0xFF6DEF
|
||||
option_golden = 0xFFD65C
|
||||
option_baddy = 0x9B3FB5
|
||||
option_fire_red = 0xFF0000
|
||||
option_maroon = 0x800000
|
||||
option_salmon = 0xFF3A65
|
||||
option_orange = 0xD86E0A
|
||||
option_lime_green = 0x8DF920
|
||||
option_bright_green = 0x0DAF05
|
||||
option_forest_green = 0x132818
|
||||
option_royal_blue = 0x0036BF
|
||||
option_brown = 0xB78726
|
||||
option_black = 0x000000
|
||||
option_white = 0xFFFFFF
|
||||
option_grey = 0x808080
|
||||
option_any_color = -1
|
||||
|
||||
@classmethod
|
||||
def from_text(cls, text: str) -> Choice:
|
||||
text = text.lower()
|
||||
if text == "random":
|
||||
choice_list = list(cls.name_lookup)
|
||||
choice_list.remove(cls.option_any_color)
|
||||
return cls(random.choice(choice_list))
|
||||
return super().from_text(text)
|
||||
|
||||
|
||||
class MadelineOneDashHairColor(ColorChoice):
|
||||
"""
|
||||
What color Madeline's hair is when she has one dash
|
||||
The `any_color` option will choose a fully random color
|
||||
A custom color entry may be supplied as a 6-character RGB hex color code
|
||||
e.g. F542C8
|
||||
"""
|
||||
display_name = "Madeline One Dash Hair Color"
|
||||
default = ColorChoice.option_strawberry
|
||||
|
||||
class MadelineTwoDashHairColor(ColorChoice):
|
||||
"""
|
||||
What color Madeline's hair is when she has two dashes
|
||||
The `any_color` option will choose a fully random color
|
||||
A custom color entry may be supplied as a 6-character RGB hex color code
|
||||
e.g. F542C8
|
||||
"""
|
||||
display_name = "Madeline Two Dash Hair Color"
|
||||
default = ColorChoice.option_double
|
||||
|
||||
class MadelineNoDashHairColor(ColorChoice):
|
||||
"""
|
||||
What color Madeline's hair is when she has no dashes
|
||||
The `any_color` option will choose a fully random color
|
||||
A custom color entry may be supplied as a 6-character RGB hex color code
|
||||
e.g. F542C8
|
||||
"""
|
||||
display_name = "Madeline No Dash Hair Color"
|
||||
default = ColorChoice.option_empty
|
||||
|
||||
class MadelineFeatherHairColor(ColorChoice):
|
||||
"""
|
||||
What color Madeline's hair is when she has a feather
|
||||
The `any_color` option will choose a fully random color
|
||||
A custom color entry may be supplied as a 6-character RGB hex color code
|
||||
e.g. F542C8
|
||||
"""
|
||||
display_name = "Madeline Feather Hair Color"
|
||||
default = ColorChoice.option_golden
|
||||
|
||||
|
||||
|
||||
celeste_option_groups = [
|
||||
OptionGroup("Goal Options", [
|
||||
GoalArea,
|
||||
LockGoalArea,
|
||||
GoalAreaCheckpointsanity,
|
||||
TotalStrawberries,
|
||||
StrawberriesRequiredPercentage,
|
||||
]),
|
||||
OptionGroup("Location Options", [
|
||||
Checkpointsanity,
|
||||
Binosanity,
|
||||
Keysanity,
|
||||
Gemsanity,
|
||||
Carsanity,
|
||||
Roomsanity,
|
||||
IncludeGoldens,
|
||||
IncludeCore,
|
||||
IncludeFarewell,
|
||||
IncludeBSides,
|
||||
IncludeCSides,
|
||||
]),
|
||||
OptionGroup("Junk and Traps", [
|
||||
JunkFillPercentage,
|
||||
TrapFillPercentage,
|
||||
TrapExpirationAction,
|
||||
TrapExpirationAmount,
|
||||
BaldTrapWeight,
|
||||
LiteratureTrapWeight,
|
||||
StunTrapWeight,
|
||||
InvisibleTrapWeight,
|
||||
FastTrapWeight,
|
||||
SlowTrapWeight,
|
||||
IceTrapWeight,
|
||||
ReverseTrapWeight,
|
||||
ScreenFlipTrapWeight,
|
||||
LaughterTrapWeight,
|
||||
HiccupTrapWeight,
|
||||
ZoomTrapWeight,
|
||||
]),
|
||||
OptionGroup("Aesthetic Options", [
|
||||
MusicShuffle,
|
||||
RequireCassettes,
|
||||
MadelineHairLength,
|
||||
MadelineOneDashHairColor,
|
||||
MadelineTwoDashHairColor,
|
||||
MadelineNoDashHairColor,
|
||||
MadelineFeatherHairColor,
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
def resolve_options(world: World):
|
||||
# One Dash Hair
|
||||
if isinstance(world.options.madeline_one_dash_hair_color.value, str):
|
||||
try:
|
||||
world.madeline_one_dash_hair_color = int(world.options.madeline_one_dash_hair_color.value.strip("#")[:6], 16)
|
||||
except ValueError:
|
||||
raise OptionError(f"Invalid input for option `madeline_one_dash_hair_color`:"
|
||||
f"{world.options.madeline_one_dash_hair_color.value} for "
|
||||
f"{world.player_name}")
|
||||
elif world.options.madeline_one_dash_hair_color.value == ColorChoice.option_any_color:
|
||||
world.madeline_one_dash_hair_color = world.random.randint(0, 0xFFFFFF)
|
||||
else:
|
||||
world.madeline_one_dash_hair_color = world.options.madeline_one_dash_hair_color.value
|
||||
|
||||
# Two Dash Hair
|
||||
if isinstance(world.options.madeline_two_dash_hair_color.value, str):
|
||||
try:
|
||||
world.madeline_two_dash_hair_color = int(world.options.madeline_two_dash_hair_color.value.strip("#")[:6], 16)
|
||||
except ValueError:
|
||||
raise OptionError(f"Invalid input for option `madeline_two_dash_hair_color`:"
|
||||
f"{world.options.madeline_two_dash_hair_color.value} for "
|
||||
f"{world.player_name}")
|
||||
elif world.options.madeline_two_dash_hair_color.value == ColorChoice.option_any_color:
|
||||
world.madeline_two_dash_hair_color = world.random.randint(0, 0xFFFFFF)
|
||||
else:
|
||||
world.madeline_two_dash_hair_color = world.options.madeline_two_dash_hair_color.value
|
||||
|
||||
# No Dash Hair
|
||||
if isinstance(world.options.madeline_no_dash_hair_color.value, str):
|
||||
try:
|
||||
world.madeline_no_dash_hair_color = int(world.options.madeline_no_dash_hair_color.value.strip("#")[:6], 16)
|
||||
except ValueError:
|
||||
raise OptionError(f"Invalid input for option `madeline_no_dash_hair_color`:"
|
||||
f"{world.options.madeline_no_dash_hair_color.value} for "
|
||||
f"{world.player_name}")
|
||||
elif world.options.madeline_no_dash_hair_color.value == ColorChoice.option_any_color:
|
||||
world.madeline_no_dash_hair_color = world.random.randint(0, 0xFFFFFF)
|
||||
else:
|
||||
world.madeline_no_dash_hair_color = world.options.madeline_no_dash_hair_color.value
|
||||
|
||||
# Feather Hair
|
||||
if isinstance(world.options.madeline_feather_hair_color.value, str):
|
||||
try:
|
||||
world.madeline_feather_hair_color = int(world.options.madeline_feather_hair_color.value.strip("#")[:6], 16)
|
||||
except ValueError:
|
||||
raise OptionError(f"Invalid input for option `madeline_feather_hair_color`:"
|
||||
f"{world.options.madeline_feather_hair_color.value} for "
|
||||
f"{world.player_name}")
|
||||
elif world.options.madeline_feather_hair_color.value == ColorChoice.option_any_color:
|
||||
world.madeline_feather_hair_color = world.random.randint(0, 0xFFFFFF)
|
||||
else:
|
||||
world.madeline_feather_hair_color = world.options.madeline_feather_hair_color.value
|
||||
|
||||
|
||||
@dataclass
|
||||
class CelesteOptions(PerGameCommonOptions):
|
||||
death_link: DeathLink
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
trap_link: TrapLink
|
||||
|
||||
goal_area: GoalArea
|
||||
lock_goal_area: LockGoalArea
|
||||
goal_area_checkpointsanity: GoalAreaCheckpointsanity
|
||||
total_strawberries: TotalStrawberries
|
||||
strawberries_required_percentage: StrawberriesRequiredPercentage
|
||||
|
||||
junk_fill_percentage: JunkFillPercentage
|
||||
trap_fill_percentage: TrapFillPercentage
|
||||
trap_expiration_action: TrapExpirationAction
|
||||
trap_expiration_amount: TrapExpirationAmount
|
||||
bald_trap_weight: BaldTrapWeight
|
||||
literature_trap_weight: LiteratureTrapWeight
|
||||
stun_trap_weight: StunTrapWeight
|
||||
invisible_trap_weight: InvisibleTrapWeight
|
||||
fast_trap_weight: FastTrapWeight
|
||||
slow_trap_weight: SlowTrapWeight
|
||||
ice_trap_weight: IceTrapWeight
|
||||
reverse_trap_weight: ReverseTrapWeight
|
||||
screen_flip_trap_weight: ScreenFlipTrapWeight
|
||||
laughter_trap_weight: LaughterTrapWeight
|
||||
hiccup_trap_weight: HiccupTrapWeight
|
||||
zoom_trap_weight: ZoomTrapWeight
|
||||
|
||||
checkpointsanity: Checkpointsanity
|
||||
binosanity: Binosanity
|
||||
keysanity: Keysanity
|
||||
gemsanity: Gemsanity
|
||||
carsanity: Carsanity
|
||||
roomsanity: Roomsanity
|
||||
include_goldens: IncludeGoldens
|
||||
include_core: IncludeCore
|
||||
include_farewell: IncludeFarewell
|
||||
include_b_sides: IncludeBSides
|
||||
include_c_sides: IncludeCSides
|
||||
|
||||
music_shuffle: MusicShuffle
|
||||
require_cassettes: RequireCassettes
|
||||
|
||||
madeline_hair_length: MadelineHairLength
|
||||
madeline_one_dash_hair_color: MadelineOneDashHairColor
|
||||
madeline_two_dash_hair_color: MadelineTwoDashHairColor
|
||||
madeline_no_dash_hair_color: MadelineNoDashHairColor
|
||||
madeline_feather_hair_color: MadelineFeatherHairColor
|
||||
361
worlds/celeste_open_world/__init__.py
Normal file
@@ -0,0 +1,361 @@
|
||||
from copy import deepcopy
|
||||
import math
|
||||
from typing import TextIO
|
||||
|
||||
from BaseClasses import ItemClassification, Location, MultiWorld, Region, Tutorial
|
||||
from Utils import visualize_regions
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
|
||||
from .Items import CelesteItem, generate_item_table, generate_item_data_table, generate_item_groups, level_item_lists, level_cassette_items,\
|
||||
cassette_item_data_table, crystal_heart_item_data_table, trap_item_data_table
|
||||
from .Locations import CelesteLocation, location_data_table, generate_location_groups, checkpoint_location_data_table, location_id_offsets
|
||||
from .Names import ItemName
|
||||
from .Options import CelesteOptions, celeste_option_groups, resolve_options
|
||||
from .Levels import Level, LocationType, load_logic_data, goal_area_option_to_name, goal_area_option_to_display_name, goal_area_to_location_name
|
||||
|
||||
|
||||
class CelesteOpenWebWorld(WebWorld):
|
||||
theme = "ice"
|
||||
|
||||
setup_en = Tutorial(
|
||||
tutorial_name="Start Guide",
|
||||
description="A guide to playing Celeste (Open World) in Archipelago.",
|
||||
language="English",
|
||||
file_name="guide_en.md",
|
||||
link="guide/en",
|
||||
authors=["PoryGone"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en]
|
||||
|
||||
option_groups = celeste_option_groups
|
||||
|
||||
|
||||
class CelesteOpenWorld(World):
|
||||
"""
|
||||
Celeste (Open World) is a randomizer for the original Celeste. In this acclaimed platformer created by ExOK Games, you control Madeline as she attempts to climb the titular mountain, meeting friends and obstacles along the way. Progression is found in unlocking the ability to interact with various objects in the areas, such as springs, traffic blocks, feathers, and many more. Please be safe on the climb.
|
||||
"""
|
||||
|
||||
# Class Data
|
||||
game = "Celeste (Open World)"
|
||||
web = CelesteOpenWebWorld()
|
||||
options_dataclass = CelesteOptions
|
||||
options: CelesteOptions
|
||||
|
||||
apworld_version = 10005
|
||||
|
||||
level_data: dict[str, Level] = load_logic_data()
|
||||
|
||||
location_name_to_id: dict[str, int] = location_data_table
|
||||
location_name_groups: dict[str, list[str]] = generate_location_groups()
|
||||
item_name_to_id: dict[str, int] = generate_item_table()
|
||||
item_name_groups: dict[str, list[str]] = generate_item_groups()
|
||||
|
||||
|
||||
# Instance Data
|
||||
madeline_one_dash_hair_color: int
|
||||
madeline_two_dash_hair_color: int
|
||||
madeline_no_dash_hair_color: int
|
||||
madeline_feather_hair_color: int
|
||||
|
||||
active_levels: set[str]
|
||||
active_items: set[str]
|
||||
|
||||
|
||||
def generate_early(self) -> None:
|
||||
if not self.player_name.isascii():
|
||||
raise RuntimeError(f"Invalid player_name {self.player_name} for game {self.game}. Name must be ascii.")
|
||||
|
||||
resolve_options(self)
|
||||
|
||||
self.goal_area: str = goal_area_option_to_name[self.options.goal_area.value]
|
||||
|
||||
self.active_levels = {"0a", "1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a"}
|
||||
if self.options.include_core:
|
||||
self.active_levels.add("9a")
|
||||
if self.options.include_farewell >= 1:
|
||||
self.active_levels.add("10a")
|
||||
if self.options.include_farewell == 2:
|
||||
self.active_levels.add("10b")
|
||||
if self.options.include_b_sides:
|
||||
self.active_levels.update({"1b", "2b", "3b", "4b", "5b", "6b", "7b"})
|
||||
if self.options.include_core:
|
||||
self.active_levels.add("9b")
|
||||
if self.options.include_c_sides:
|
||||
self.active_levels.update({"1c", "2c", "3c", "4c", "5c", "6c", "7c"})
|
||||
if self.options.include_core:
|
||||
self.active_levels.add("9c")
|
||||
|
||||
self.active_levels.add(self.goal_area)
|
||||
if self.goal_area == "10c":
|
||||
self.active_levels.add("10a")
|
||||
self.active_levels.add("10b")
|
||||
elif self.goal_area == "10b":
|
||||
self.active_levels.add("10a")
|
||||
|
||||
self.active_items = set()
|
||||
for level in self.active_levels:
|
||||
self.active_items.update(level_item_lists[level])
|
||||
|
||||
|
||||
def create_regions(self) -> None:
|
||||
from .Locations import create_regions_and_locations
|
||||
|
||||
create_regions_and_locations(self)
|
||||
|
||||
|
||||
def create_item(self, name: str, force_useful: bool = False) -> CelesteItem:
|
||||
item_data_table = generate_item_data_table()
|
||||
|
||||
if name == ItemName.strawberry and force_useful:
|
||||
return CelesteItem(name, ItemClassification.useful, item_data_table[name].code, self.player)
|
||||
elif name in item_data_table:
|
||||
return CelesteItem(name, item_data_table[name].type, item_data_table[name].code, self.player)
|
||||
else:
|
||||
return CelesteItem(name, ItemClassification.progression, None, self.player)
|
||||
|
||||
def create_items(self) -> None:
|
||||
item_pool: list[CelesteItem] = []
|
||||
|
||||
location_count: int = len(self.get_locations())
|
||||
goal_area_location_count: int = sum(goal_area_option_to_display_name[self.options.goal_area] in loc.name for loc in self.get_locations())
|
||||
|
||||
# Goal Items
|
||||
goal_item_loc: Location = self.get_location(goal_area_to_location_name[self.goal_area])
|
||||
goal_item_loc.place_locked_item(self.create_item(ItemName.house_keys))
|
||||
location_count -= 1
|
||||
|
||||
epilogue_region: Region = self.get_region(self.epilogue_start_region)
|
||||
epilogue_region.add_locations({ItemName.victory: None }, CelesteLocation)
|
||||
victory_loc: Location = self.get_location(ItemName.victory)
|
||||
victory_loc.place_locked_item(self.create_item(ItemName.victory))
|
||||
|
||||
# Checkpoints
|
||||
for item_name in self.active_checkpoint_names:
|
||||
if self.options.checkpointsanity:
|
||||
if not self.options.goal_area_checkpointsanity and goal_area_option_to_display_name[self.options.goal_area] in item_name:
|
||||
checkpoint_loc: Location = self.get_location(item_name)
|
||||
checkpoint_loc.place_locked_item(self.create_item(item_name))
|
||||
location_count -= 1
|
||||
else:
|
||||
item_pool.append(self.create_item(item_name))
|
||||
else:
|
||||
checkpoint_loc: Location = self.get_location(item_name)
|
||||
checkpoint_loc.place_locked_item(self.create_item(item_name))
|
||||
location_count -= 1
|
||||
|
||||
# Keys
|
||||
if self.options.keysanity:
|
||||
item_pool += [self.create_item(item_name) for item_name in self.active_key_names]
|
||||
else:
|
||||
for item_name in self.active_key_names:
|
||||
key_loc: Location = self.get_location(item_name)
|
||||
key_loc.place_locked_item(self.create_item(item_name))
|
||||
location_count -= 1
|
||||
|
||||
# Summit Gems
|
||||
if self.options.gemsanity:
|
||||
item_pool += [self.create_item(item_name) for item_name in self.active_gem_names]
|
||||
else:
|
||||
for item_name in self.active_gem_names:
|
||||
gem_loc: Location = self.get_location(item_name)
|
||||
gem_loc.place_locked_item(self.create_item(item_name))
|
||||
location_count -= 1
|
||||
|
||||
# Clutter Events
|
||||
for item_name in self.active_clutter_names:
|
||||
clutter_loc: Location = self.get_location(item_name)
|
||||
clutter_loc.place_locked_item(self.create_item(item_name))
|
||||
location_count -= 1
|
||||
|
||||
# Interactables
|
||||
item_pool += [self.create_item(item_name) for item_name in sorted(self.active_items)]
|
||||
|
||||
# Strawberries
|
||||
real_total_strawberries: int = min(self.options.total_strawberries.value, location_count - goal_area_location_count - len(item_pool))
|
||||
self.strawberries_required = int(real_total_strawberries * (self.options.strawberries_required_percentage / 100))
|
||||
|
||||
menu_region = self.get_region("Menu")
|
||||
if getattr(self, "goal_start_region", None):
|
||||
menu_region.add_exits([self.goal_start_region], {self.goal_start_region: lambda state: state.has(ItemName.strawberry, self.player, self.strawberries_required)})
|
||||
if getattr(self, "goal_checkpoint_names", None):
|
||||
for region_name, location_name in self.goal_checkpoint_names.items():
|
||||
checkpoint_rule = lambda state, location_name=location_name: state.has(location_name, self.player) and state.has(ItemName.strawberry, self.player, self.strawberries_required)
|
||||
menu_region.add_exits([region_name], {region_name: checkpoint_rule})
|
||||
|
||||
menu_region.add_exits([self.epilogue_start_region], {self.epilogue_start_region: lambda state: (state.has(ItemName.strawberry, self.player, self.strawberries_required) and state.has(ItemName.house_keys, self.player))})
|
||||
|
||||
item_pool += [self.create_item(ItemName.strawberry) for _ in range(self.strawberries_required)]
|
||||
|
||||
# Filler and Traps
|
||||
non_required_strawberries = (real_total_strawberries - self.strawberries_required)
|
||||
replacement_filler_count = math.floor(non_required_strawberries * (self.options.junk_fill_percentage.value / 100.0))
|
||||
remaining_extra_strawberries = non_required_strawberries - replacement_filler_count
|
||||
item_pool += [self.create_item(ItemName.strawberry, True) for _ in range(remaining_extra_strawberries)]
|
||||
|
||||
trap_weights = []
|
||||
trap_weights += ([ItemName.bald_trap] * self.options.bald_trap_weight.value)
|
||||
trap_weights += ([ItemName.literature_trap] * self.options.literature_trap_weight.value)
|
||||
trap_weights += ([ItemName.stun_trap] * self.options.stun_trap_weight.value)
|
||||
trap_weights += ([ItemName.invisible_trap] * self.options.invisible_trap_weight.value)
|
||||
trap_weights += ([ItemName.fast_trap] * self.options.fast_trap_weight.value)
|
||||
trap_weights += ([ItemName.slow_trap] * self.options.slow_trap_weight.value)
|
||||
trap_weights += ([ItemName.ice_trap] * self.options.ice_trap_weight.value)
|
||||
trap_weights += ([ItemName.reverse_trap] * self.options.reverse_trap_weight.value)
|
||||
trap_weights += ([ItemName.screen_flip_trap] * self.options.screen_flip_trap_weight.value)
|
||||
trap_weights += ([ItemName.laughter_trap] * self.options.laughter_trap_weight.value)
|
||||
trap_weights += ([ItemName.hiccup_trap] * self.options.hiccup_trap_weight.value)
|
||||
trap_weights += ([ItemName.zoom_trap] * self.options.zoom_trap_weight.value)
|
||||
|
||||
total_filler_count: int = (location_count - len(item_pool))
|
||||
|
||||
# Cassettes
|
||||
if self.options.require_cassettes:
|
||||
shuffled_active_levels = sorted(self.active_levels)
|
||||
self.random.shuffle(shuffled_active_levels)
|
||||
for level_name in shuffled_active_levels:
|
||||
if level_name == "10b" or level_name == "10c":
|
||||
continue
|
||||
if level_name not in self.multiworld.precollected_items[self.player]:
|
||||
if total_filler_count > 0:
|
||||
item_pool.append(self.create_item(level_cassette_items[level_name]))
|
||||
total_filler_count -= 1
|
||||
else:
|
||||
self.multiworld.push_precollected(self.create_item(level_cassette_items[level_name]))
|
||||
|
||||
# Crystal Hearts
|
||||
for name in crystal_heart_item_data_table.keys():
|
||||
if total_filler_count > 0:
|
||||
if name not in self.multiworld.precollected_items[self.player]:
|
||||
item_pool.append(self.create_item(name))
|
||||
total_filler_count -= 1
|
||||
|
||||
trap_count = 0 if (len(trap_weights) == 0) else math.ceil(total_filler_count * (self.options.trap_fill_percentage.value / 100.0))
|
||||
total_filler_count -= trap_count
|
||||
|
||||
item_pool += [self.create_item(ItemName.raspberry) for _ in range(total_filler_count)]
|
||||
|
||||
trap_pool = []
|
||||
for i in range(trap_count):
|
||||
trap_item = self.random.choice(trap_weights)
|
||||
trap_pool.append(self.create_item(trap_item))
|
||||
|
||||
item_pool += trap_pool
|
||||
|
||||
self.multiworld.itempool += item_pool
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return ItemName.raspberry
|
||||
|
||||
|
||||
def set_rules(self) -> None:
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has(ItemName.victory, self.player)
|
||||
|
||||
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"apworld_version": self.apworld_version,
|
||||
"min_mod_version": 10000,
|
||||
|
||||
"death_link": self.options.death_link.value,
|
||||
"death_link_amnesty": self.options.death_link_amnesty.value,
|
||||
"trap_link": self.options.trap_link.value,
|
||||
|
||||
"active_levels": self.active_levels,
|
||||
"goal_area": self.goal_area,
|
||||
"lock_goal_area": self.options.lock_goal_area.value,
|
||||
"strawberries_required": self.strawberries_required,
|
||||
|
||||
"checkpointsanity": self.options.checkpointsanity.value,
|
||||
"binosanity": self.options.binosanity.value,
|
||||
"keysanity": self.options.keysanity.value,
|
||||
"gemsanity": self.options.gemsanity.value,
|
||||
"carsanity": self.options.carsanity.value,
|
||||
"roomsanity": self.options.roomsanity.value,
|
||||
"include_goldens": self.options.include_goldens.value,
|
||||
|
||||
"include_core": self.options.include_core.value,
|
||||
"include_farewell": self.options.include_farewell.value,
|
||||
"include_b_sides": self.options.include_b_sides.value,
|
||||
"include_c_sides": self.options.include_c_sides.value,
|
||||
|
||||
"trap_expiration_action": self.options.trap_expiration_action.value,
|
||||
"trap_expiration_amount": self.options.trap_expiration_amount.value,
|
||||
"active_traps": self.output_active_traps(),
|
||||
|
||||
"madeline_hair_length": self.options.madeline_hair_length.value,
|
||||
"madeline_one_dash_hair_color": self.madeline_one_dash_hair_color,
|
||||
"madeline_two_dash_hair_color": self.madeline_two_dash_hair_color,
|
||||
"madeline_no_dash_hair_color": self.madeline_no_dash_hair_color,
|
||||
"madeline_feather_hair_color": self.madeline_feather_hair_color,
|
||||
|
||||
"music_shuffle": self.options.music_shuffle.value,
|
||||
"music_map": self.generate_music_data(),
|
||||
"require_cassettes": self.options.require_cassettes.value,
|
||||
"chosen_poem": self.random.randint(0, 119),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def stage_write_spoiler_header(cls, multiworld: MultiWorld, spoiler_handle: TextIO):
|
||||
major: int = cls.apworld_version // 10000
|
||||
minor: int = (cls.apworld_version % 10000) // 100
|
||||
bugfix: int = (cls.apworld_version % 100)
|
||||
spoiler_handle.write(f"\nCeleste (Open World) APWorld v{major}.{minor}.{bugfix}\n")
|
||||
|
||||
def output_active_traps(self) -> dict[int, int]:
|
||||
trap_data = {}
|
||||
|
||||
trap_data[0x20] = self.options.bald_trap_weight.value
|
||||
trap_data[0x21] = self.options.literature_trap_weight.value
|
||||
trap_data[0x22] = self.options.stun_trap_weight.value
|
||||
trap_data[0x23] = self.options.invisible_trap_weight.value
|
||||
trap_data[0x24] = self.options.fast_trap_weight.value
|
||||
trap_data[0x25] = self.options.slow_trap_weight.value
|
||||
trap_data[0x26] = self.options.ice_trap_weight.value
|
||||
trap_data[0x28] = self.options.reverse_trap_weight.value
|
||||
trap_data[0x29] = self.options.screen_flip_trap_weight.value
|
||||
trap_data[0x2A] = self.options.laughter_trap_weight.value
|
||||
trap_data[0x2B] = self.options.hiccup_trap_weight.value
|
||||
trap_data[0x2C] = self.options.zoom_trap_weight.value
|
||||
|
||||
return trap_data
|
||||
|
||||
def generate_music_data(self) -> dict[int, int]:
|
||||
if self.options.music_shuffle == "consistent":
|
||||
musiclist_o = list(range(0, 48))
|
||||
musiclist_s = musiclist_o.copy()
|
||||
self.random.shuffle(musiclist_s)
|
||||
|
||||
return dict(zip(musiclist_o, musiclist_s))
|
||||
elif self.options.music_shuffle == "singularity":
|
||||
musiclist_o = list(range(0, 48))
|
||||
musiclist_s = [self.random.choice(musiclist_o)] * len(musiclist_o)
|
||||
|
||||
return dict(zip(musiclist_o, musiclist_s))
|
||||
else:
|
||||
musiclist_o = list(range(0, 48))
|
||||
musiclist_s = musiclist_o.copy()
|
||||
|
||||
return dict(zip(musiclist_o, musiclist_s))
|
||||
|
||||
|
||||
# Useful Debugging tools, kept around for later.
|
||||
#@classmethod
|
||||
#def stage_assert_generate(cls, _multiworld: MultiWorld) -> None:
|
||||
# with open("./worlds/celeste_open_world/data/IDs.txt", "w") as f:
|
||||
# print("Items:", file=f)
|
||||
# for name in sorted(CelesteOpenWorld.item_name_to_id, key=CelesteOpenWorld.item_name_to_id.get):
|
||||
# id = CelesteOpenWorld.item_name_to_id[name]
|
||||
# print(f"{{ 0x{id:X}, \"{name}\" }},", file=f)
|
||||
# print("\nLocations:", file=f)
|
||||
# for name in sorted(CelesteOpenWorld.location_name_to_id, key=CelesteOpenWorld.location_name_to_id.get):
|
||||
# id = CelesteOpenWorld.location_name_to_id[name]
|
||||
# print(f"{{ 0x{id:X}, \"{name}\" }},", file=f)
|
||||
# print("\nLocations 2:", file=f)
|
||||
# for name in sorted(CelesteOpenWorld.location_name_to_id, key=CelesteOpenWorld.location_name_to_id.get):
|
||||
# id = CelesteOpenWorld.location_name_to_id[name]
|
||||
# print(f"{{ \"{name}\", 0x{id:X} }},", file=f)
|
||||
#
|
||||
#def generate_output(self, output_directory: str):
|
||||
# visualize_regions(self.get_region("Menu"), f"Player{self.player}.puml", show_entrance_names=False,
|
||||
# regions_to_highlight=self.multiworld.get_all_state(self.player).reachable_regions[self.player])
|
||||
6
worlds/celeste_open_world/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "Celeste (Open World)",
|
||||
"authors": [ "PoryGone" ],
|
||||
"minimum_ap_version": "0.6.3",
|
||||
"world_version": "1.0.5"
|
||||
}
|
||||
41232
worlds/celeste_open_world/data/CelesteLevelData.json
Normal file
9792
worlds/celeste_open_world/data/CelesteLevelData.py
Normal file
190
worlds/celeste_open_world/data/ParseData.py
Normal file
@@ -0,0 +1,190 @@
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
all_doors: list[str] = []
|
||||
all_region_connections: list[str] = []
|
||||
all_locations: list[str] = []
|
||||
all_regions: list[str] = []
|
||||
all_room_connections: list[str] = []
|
||||
all_rooms: list[str] = []
|
||||
all_levels: list[str] = []
|
||||
|
||||
|
||||
data_file = open('CelesteLevelData.json')
|
||||
level_data = json.load(data_file)
|
||||
data_file.close()
|
||||
|
||||
# Levels
|
||||
for level in level_data["levels"]:
|
||||
level_str = (f" \"{level['name']}\": Level(\"{level['name']}\", "
|
||||
f"\"{level['display_name']}\", "
|
||||
f"[room for _, room in all_rooms.items() if room.level_name == \"{level['name']}\"], "
|
||||
f"[room_con for _, room_con in all_room_connections.items() if room_con.level_name == \"{level['name']}\"]),"
|
||||
)
|
||||
|
||||
all_levels.append(level_str)
|
||||
|
||||
# Rooms
|
||||
for room in level["rooms"]:
|
||||
room_full_name = f"{level['name']}_{room['name']}"
|
||||
room_full_display_name = f"{level['display_name']} - Room {room['name']}"
|
||||
|
||||
room_str = (f" \"{room_full_name}\": Room(\"{level['name']}\", "
|
||||
f"\"{room_full_name}\", \"{room_full_display_name}\", "
|
||||
f"[reg for _, reg in all_regions.items() if reg.room_name == \"{room_full_name}\"], "
|
||||
f"[door for _, door in all_doors.items() if door.room_name == \"{room_full_name}\"]"
|
||||
)
|
||||
|
||||
if "checkpoint" in room and room["checkpoint"] != "":
|
||||
room_str += f", \"{room['checkpoint']}\", \"{room_full_name}_{room['checkpoint_region']}\""
|
||||
room_str += "),"
|
||||
|
||||
all_rooms.append(room_str)
|
||||
|
||||
# Regions
|
||||
for region in room["regions"]:
|
||||
region_full_name = f"{room_full_name}_{region['name']}"
|
||||
|
||||
region_str = (f" \"{region_full_name}\": PreRegion(\"{region_full_name}\", "
|
||||
f"\"{room_full_name}\", "
|
||||
f"[reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == \"{region_full_name}\"], "
|
||||
f"[loc for _, loc in all_locations.items() if loc.region_name == \"{region_full_name}\"]),"
|
||||
)
|
||||
|
||||
all_regions.append(region_str)
|
||||
|
||||
# Locations
|
||||
if "locations" in region:
|
||||
for location in region["locations"]:
|
||||
location_full_name = f"{room_full_name}_{location['name']}"
|
||||
|
||||
location_display_name = location['display_name']
|
||||
if (location['type'] == "strawberry" and location_display_name != "Moon Berry") or location['type'] == "binoculars" :
|
||||
location_display_name = f"Room {room['name']} {location_display_name}"
|
||||
location_full_display_name = f"{level['display_name']} - {location_display_name}"
|
||||
|
||||
location_str = (f" \"{location_full_name}\": LevelLocation(\"{location_full_name}\", "
|
||||
f"\"{location_full_display_name}\", \"{region_full_name}\", "
|
||||
f"LocationType.{location['type']}, ["
|
||||
)
|
||||
|
||||
if "rule" in location:
|
||||
for possible_access in location['rule']:
|
||||
location_str += f"["
|
||||
for item in possible_access:
|
||||
if "Key" in item or "Gem" in item:
|
||||
location_str += f"\"{level['display_name']} - {item}\", "
|
||||
else:
|
||||
location_str += f"ItemName.{item}, "
|
||||
location_str += f"], "
|
||||
elif "rules" in location:
|
||||
raise Exception(f"Location {location_full_name} uses 'rules' instead of 'rule")
|
||||
|
||||
location_str += "]),"
|
||||
|
||||
all_locations.append(location_str)
|
||||
|
||||
# Region Connections
|
||||
for reg_con in region["connections"]:
|
||||
dest_region_full_name = f"{room_full_name}_{reg_con['dest']}"
|
||||
reg_con_full_name = f"{region_full_name}---{dest_region_full_name}"
|
||||
|
||||
reg_con_str = f" \"{reg_con_full_name}\": RegionConnection(\"{region_full_name}\", \"{dest_region_full_name}\", ["
|
||||
|
||||
for possible_access in reg_con['rule']:
|
||||
reg_con_str += f"["
|
||||
for item in possible_access:
|
||||
if "Key" in item or "Gem" in item:
|
||||
reg_con_str += f"\"{level['display_name']} - {item}\", "
|
||||
else:
|
||||
reg_con_str += f"ItemName.{item}, "
|
||||
reg_con_str += f"], "
|
||||
|
||||
reg_con_str += "]),"
|
||||
|
||||
all_region_connections.append(reg_con_str)
|
||||
|
||||
for door in room["doors"]:
|
||||
door_full_name = f"{room_full_name}_{door['name']}"
|
||||
|
||||
door_str = (f" \"{door_full_name}\": Door(\"{door_full_name}\", "
|
||||
f"\"{room_full_name}\", "
|
||||
f"DoorDirection.{door['direction']}, "
|
||||
)
|
||||
|
||||
door_str += "True, " if door["blocked"] else "False, "
|
||||
door_str += "True)," if door["closes_behind"] else "False),"
|
||||
|
||||
all_doors.append(door_str)
|
||||
|
||||
all_regions.append("")
|
||||
all_region_connections.append("")
|
||||
all_doors.append("")
|
||||
|
||||
all_locations.append("")
|
||||
all_rooms.append("")
|
||||
|
||||
# Room Connections
|
||||
for room_con in level["room_connections"]:
|
||||
source_door_full_name = f"{level['name']}_{room_con['source_room']}_{room_con['source_door']}"
|
||||
dest_door_full_name = f"{level['name']}_{room_con['dest_room']}_{room_con['dest_door']}"
|
||||
|
||||
room_con_str = (f" \"{source_door_full_name}---{dest_door_full_name}\": RoomConnection(\"{level['name']}\", "
|
||||
f"all_doors[\"{source_door_full_name}\"], "
|
||||
f"all_doors[\"{dest_door_full_name}\"]),"
|
||||
)
|
||||
|
||||
all_room_connections.append(room_con_str)
|
||||
|
||||
all_room_connections.append("")
|
||||
|
||||
|
||||
all_levels.append("")
|
||||
|
||||
|
||||
import sys
|
||||
out_file = open("CelesteLevelData.py", "w")
|
||||
sys.stdout = out_file
|
||||
|
||||
print("# THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MANUALLY EDIT.")
|
||||
print("")
|
||||
print("from ..Levels import Level, Room, PreRegion, LevelLocation, RegionConnection, RoomConnection, Door, DoorDirection, LocationType")
|
||||
print("from ..Names import ItemName")
|
||||
print("")
|
||||
print("all_doors: dict[str, Door] = {")
|
||||
for line in all_doors:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_region_connections: dict[str, RegionConnection] = {")
|
||||
for line in all_region_connections:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_locations: dict[str, LevelLocation] = {")
|
||||
for line in all_locations:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_regions: dict[str, PreRegion] = {")
|
||||
for line in all_regions:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_room_connections: dict[str, RoomConnection] = {")
|
||||
for line in all_room_connections:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_rooms: dict[str, Room] = {")
|
||||
for line in all_rooms:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
print("all_levels: dict[str, Level] = {")
|
||||
for line in all_levels:
|
||||
print(line)
|
||||
print("}")
|
||||
print("")
|
||||
|
||||
out_file.close()
|
||||
0
worlds/celeste_open_world/data/__init__.py
Normal file
98
worlds/celeste_open_world/docs/en_Celeste (Open World).md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Celeste Open World
|
||||
|
||||
## What is this game?
|
||||
|
||||
**Celeste (Open World)** is a Randomizer for the original Celeste. In this acclaimed platformer created by ExOK Games, you control Madeline as she attempts to climb the titular mountain, meeting friends and obstacles along the way.
|
||||
This randomizer takes an "Open World" approach. All of your active areas are open to you from the start. Progression is found in unlocking the ability to interact with various objects in the areas, such as springs, traffic blocks, feathers, and many more. One area can be selected as your "Goal Area", requiring you to clear that area before you can access the Epilogue and finish the game. Additionally, you can be required to receive a customizable amount of `Strawberry` items to access the Epilogue and optionally to access your Goal Area as well.
|
||||
There are a variety of progression, location, and aesthetic options available. Please be safe on the climb.
|
||||
|
||||
## Where is the options page?
|
||||
|
||||
The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
By default, the Prologue, the A-Side levels for Chapters 1-7, and the Epilogue are included in the randomizer. Using options, B- and C-Sides can also be included, as can the Core and Farewell chapters. One level is chosen via an option to be the "Goal Area". Obtaining the required amount of Strawberry items from the multiworld and clearing this Goal Area will grant access to the Epilogue and the Credits, which is the goal of the randomizer.
|
||||
|
||||
## What items get shuffled?
|
||||
|
||||
The main collectable in this game is Strawberries, which you must collect to complete the game.
|
||||
|
||||
16 Crystal Heart items are included as filler items (Heart Gates are disabled in this mod). Any additional space in the item pool is filled by Raspberries, which do nothing, and Traps.
|
||||
|
||||
The following interactable items are included in the item pool, so long as any active level includes them:
|
||||
- Springs
|
||||
- Dash Refills
|
||||
- Traffic Blocks
|
||||
- Pink Cassette Blocks
|
||||
- Blue Cassette Blocks
|
||||
- Dream Blocks
|
||||
- Coins
|
||||
- Strawberry Seeds
|
||||
- Sinking Platforms
|
||||
- Moving Platforms
|
||||
- Blue Clouds
|
||||
- Pink Clouds
|
||||
- Blue Boosters
|
||||
- Red Boosters
|
||||
- Move Blocks
|
||||
- White Block
|
||||
- Swap Blocks
|
||||
- Dash Switches
|
||||
- Torches
|
||||
- Theo Crystal
|
||||
- Feathers
|
||||
- Bumpers
|
||||
- Kevins
|
||||
- Badeline Boosters
|
||||
- Fire and Ice Balls
|
||||
- Core Toggles
|
||||
- Core Blocks
|
||||
- Pufferfish
|
||||
- Jellyfish
|
||||
- Double Dash Refills
|
||||
- Breaker Boxes
|
||||
- Yellow Cassette Blocks
|
||||
- Green Cassette Blocks
|
||||
- Bird
|
||||
|
||||
Additionally, the following items can optionally be included in the Item Pool:
|
||||
- Keys
|
||||
- Checkpoints
|
||||
- Summit Gems
|
||||
- One Cassette per active level
|
||||
|
||||
Finally, the following Traps can be optionally included in the Item Pool:
|
||||
- Bald Trap
|
||||
- Literature Trap
|
||||
- Stun Trap
|
||||
- Invisible Trap
|
||||
- Fast Trap
|
||||
- Slow Trap
|
||||
- Ice Trap
|
||||
- Reverse Trap
|
||||
- Screen Flip Trap
|
||||
- Laughter Trap
|
||||
- Hiccup Trap
|
||||
- Zoom Trap
|
||||
|
||||
## What locations get shuffled?
|
||||
|
||||
By default, the locations in Celeste (Open World) which can contain items are:
|
||||
- Level Clears
|
||||
- Strawberries
|
||||
- Crystal Hearts
|
||||
- Cassettes
|
||||
|
||||
Additionally, the following locations can optionally be included in the Location Pool:
|
||||
- Golden Strawberries
|
||||
- Keys
|
||||
- Checkpoints
|
||||
- Summit Gems
|
||||
- Cars
|
||||
- Binoculars
|
||||
- Rooms
|
||||
|
||||
## How can I get started?
|
||||
|
||||
To get started playing Celeste (Open World) in Archipelago, [go to the setup guide for this game](../../../tutorial/Celeste%20(Open%20World)/guide/en)
|
||||
20
worlds/celeste_open_world/docs/guide_en.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Celeste (Open World) Setup Guide
|
||||
|
||||
## Required Software
|
||||
- The latest version of Celeste (1.4) from any official PC game distributor
|
||||
- Olympus (Celeste Mod Manager) from: [Olympus Download Page](https://everestapi.github.io/)
|
||||
- The latest version of the Archipelago Open World mod for Celeste from: [GitHub Release](https://github.com/PoryGoneDev/Celeste-Archipelago-Open-World/releases)
|
||||
|
||||
## Installation Procedures (Windows/Linux)
|
||||
|
||||
1. Install the latest version of Celeste (v1.4) on PC
|
||||
2. Install `Olympus` (mod manager/launcher) and `Everest` (mod loader) per its instructions: [Olympus Setup Instructions](https://everestapi.github.io/)
|
||||
3. Place the `Archipelago_Open_World.zip` from the GitHub release into the `mods` folder in your Celeste install
|
||||
4. (Recommended) From the main menu, enter `Mod Options` and set `Debug Mode` to `Everest` or `Always`. This will give you access to a rudimentary Text Client which can be toggled with the `~` key.
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
1. Load Everest from the Olympus Launcher with the Archipelago Open World mod enabled
|
||||
2. Enter the Connection Menu via the `Connect` button on the main menu
|
||||
3. Use the keyboard to enter your connection information, then press the Connect button
|
||||
4. Once connected, you can use the Debug Menu (opened with `~`) as a Text Client, by typing "`!ap `" followed by what you would normally enter into a Text Client
|
||||
@@ -20,6 +20,7 @@ class CivVIBoostData:
|
||||
Prereq: List[str]
|
||||
PrereqRequiredCount: int
|
||||
Classification: str
|
||||
EraRequired: bool = False
|
||||
|
||||
|
||||
class GoodyHutRewardData(TypedDict):
|
||||
|
||||
@@ -150,7 +150,10 @@ def generate_era_location_table() -> Dict[str, Dict[str, CivVILocationData]]:
|
||||
location = CivVILocationData(
|
||||
boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST
|
||||
)
|
||||
era_locations["ERA_ANCIENT"][boost.Type] = location
|
||||
# If EraRequired is True, place the boost in its actual era
|
||||
# Otherwise, place it in ERA_ANCIENT for early access
|
||||
target_era = boost.EraType if boost.EraRequired else "ERA_ANCIENT"
|
||||
era_locations[target_era][boost.Type] = location
|
||||
id_base += 1
|
||||
|
||||
return era_locations
|
||||
|
||||
@@ -210,8 +210,8 @@ boosts: List[CivVIBoostData] = [
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_SQUARE_RIGGING",
|
||||
"ERA_RENAISSANCE",
|
||||
["TECH_GUNPOWDER"],
|
||||
1,
|
||||
["TECH_GUNPOWDER", "TECH_MILITARY_ENGINEERING", "TECH_MINING"],
|
||||
3,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -252,15 +252,15 @@ boosts: List[CivVIBoostData] = [
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_BALLISTICS",
|
||||
"ERA_INDUSTRIAL",
|
||||
["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING"],
|
||||
2,
|
||||
["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING", "TECH_BRONZE_WORKING"],
|
||||
3,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_MILITARY_SCIENCE",
|
||||
"ERA_INDUSTRIAL",
|
||||
["TECH_STIRRUPS"],
|
||||
1,
|
||||
["TECH_BRONZE_WORKING", "TECH_STIRRUPS", "TECH_MINING"],
|
||||
3,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -301,8 +301,8 @@ boosts: List[CivVIBoostData] = [
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_REPLACEABLE_PARTS",
|
||||
"ERA_MODERN",
|
||||
["TECH_MILITARY_SCIENCE"],
|
||||
1,
|
||||
["TECH_MILITARY_SCIENCE", "TECH_MINING"],
|
||||
2,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -343,8 +343,8 @@ boosts: List[CivVIBoostData] = [
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_ADVANCED_FLIGHT",
|
||||
"ERA_ATOMIC",
|
||||
["TECH_FLIGHT"],
|
||||
1,
|
||||
["TECH_FLIGHT", "TECH_REFINING", "TECH_MINING"],
|
||||
3,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -436,8 +436,8 @@ boosts: List[CivVIBoostData] = [
|
||||
CivVIBoostData(
|
||||
"BOOST_TECH_COMPOSITES",
|
||||
"ERA_INFORMATION",
|
||||
["TECH_COMBUSTION"],
|
||||
1,
|
||||
["TECH_COMBUSTION", "TECH_REFINING", "TECH_MINING"],
|
||||
3,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -470,7 +470,7 @@ boosts: List[CivVIBoostData] = [
|
||||
"TECH_ELECTRICITY",
|
||||
"TECH_NUCLEAR_FISSION",
|
||||
],
|
||||
1,
|
||||
4,
|
||||
"DEFAULT",
|
||||
),
|
||||
CivVIBoostData(
|
||||
@@ -651,10 +651,11 @@ boosts: List[CivVIBoostData] = [
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_CIVIC_FEUDALISM",
|
||||
"ERA_MEDIEVAL",
|
||||
"ERA_CLASSICAL",
|
||||
[],
|
||||
0,
|
||||
"DEFAULT",
|
||||
True,
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_CIVIC_CIVIL_SERVICE",
|
||||
@@ -662,6 +663,7 @@ boosts: List[CivVIBoostData] = [
|
||||
[],
|
||||
0,
|
||||
"DEFAULT",
|
||||
True,
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_CIVIC_MERCENARIES",
|
||||
@@ -790,6 +792,7 @@ boosts: List[CivVIBoostData] = [
|
||||
[],
|
||||
0,
|
||||
"DEFAULT",
|
||||
True
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_CIVIC_CONSERVATION",
|
||||
@@ -885,6 +888,7 @@ boosts: List[CivVIBoostData] = [
|
||||
["TECH_ROCKETRY"],
|
||||
1,
|
||||
"DEFAULT",
|
||||
True
|
||||
),
|
||||
CivVIBoostData(
|
||||
"BOOST_CIVIC_GLOBALIZATION",
|
||||
|
||||
@@ -14,25 +14,27 @@ The following are required in order to play Civ VI in Archipelago:
|
||||
|
||||
- A copy of the game `Civilization VI` including the two expansions `Rise & Fall` and `Gathering Storm` (both the Steam and Epic version should work).
|
||||
|
||||
## Enabling the tuner
|
||||
|
||||
In the main menu, navigate to the "Game Options" page. On the "Game" menu, make sure that "Tuner (disables achievements)" is enabled.
|
||||
|
||||
## Mod Installation
|
||||
|
||||
1. Download and unzip the latest release of the mod from [GitHub](https://github.com/hesto2/civilization_archipelago_mod/releases/latest).
|
||||
|
||||
2. Copy the folder containing the mod files to your Civ VI mods folder. On Windows, this is usually located at `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods`. If you use OneDrive, check if the folder is instead located in your OneDrive file structure, and use that path when relevant in future steps.
|
||||
|
||||
3. After the Archipelago host generates a game, you should be given a `.apcivvi` file. Associate the file with the Archipelago Launcher and double click it.
|
||||
3. After the Archipelago host generates a game, you should be given a `.apcivvi` file. You can open it as a zip file, you can do this by either right clicking it and opening it with a program that handles zip files (if you associate that file with the program it will open it with that program in the future by double clicking it), or by right clicking and renaming the file extension from `apcivvi` to `zip` (only works if you are displaying file extensions). You can also associate the file with the Archipelago Launcher and double click it and it will create a folder with the mod files inside of it.
|
||||
|
||||
4. Copy the contents of the new folder it generates (it will have the same name as the `.apcivvi` file) into your Civilization VI Archipelago Mod folder. If double clicking the `.apcivvi` file doesn't generate a folder, you can instead open it as a zip file. You can do this by either right clicking it and opening it with a program that handles zip files, or by right clicking and renaming the file extension from `apcivvi` to `zip`.
|
||||
4. Copy the contents of the zip file or folder it generated (the name of the folder should be the same as the apcivvi file) into your Civilization VI Archipelago Mod folder (there should be five files placed there from the `.apcivvi` file, overwrite if asked).
|
||||
|
||||
5. Place the files generated from the `.apcivvi` in your archipelago mod folder (there should be five files placed there from the apcivvi file, overwrite if asked). Your mod path should look something like `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods\civilization_archipelago_mod`.
|
||||
5. Your mod path should look something like `C:\Users\YOUR_USER\Documents\My Games\Sid Meier's Civilization VI\Mods\civilization_archipelago_mod`. If everything was done correctly you can now connect to the game.
|
||||
|
||||
## Configuring your game
|
||||
## Connecting to a game
|
||||
|
||||
Make sure you enable the mod in the main title under Additional Content > Mods. When configuring your game, make sure to start the game in the Ancient Era and leave all settings related to starting technologies and civics as the defaults. Other than that, configure difficulty, AI, etc. as you normally would.
|
||||
1. In the main menu, navigate to the "Game Options" page. On the "Game" menu, make sure that "Tuner (disables achievements)" is enabled.
|
||||
|
||||
2. In the main menu, navigate to the "Additional Content" page, then go to "Mods" and make sure the Archipelago mod is enabled.
|
||||
|
||||
3. When starting the game make sure you are on the Gathering Storm ruleset in a Single Player game. Additionally you must start in the ancient era, other settings and game modes can be customised to your own liking. An important thing to note is that settings preset saves the mod list from when you created it, so if you want to use a setting preset with this you must create it after installing the Archipelago mod.
|
||||
|
||||
4. To connect to the room open the Archipelago Launcher, from within the launcher open the Civ6 client and connect to the room. Once connected to the room enter your slot name and if everything went right you should now be connected.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -51,3 +53,8 @@ Make sure you enable the mod in the main title under Additional Content > Mods.
|
||||
- If you still have any errors make sure the two expansions Rise & Fall and Gathering Storm are active in the mod selector (all the official DLC works without issues but Rise & Fall and Gathering Storm are required for the mod).
|
||||
|
||||
- If boostsanity is enabled and those items are not being sent out but regular techs are, make sure you placed the files from your new room in the mod folder.
|
||||
|
||||
- If you are neither receiving or sending items, make sure you have the correct client open. The client should be the Civ6 and NOT the Text Client.
|
||||
|
||||
- This should be compatible with a lot of other mods, but if you are having issues try disabling all mods other than the Archipelago mod and see if the problem still persists.
|
||||
|
||||
|
||||
@@ -105,3 +105,78 @@ class TestBoostsanityExcluded(CivVITestBase):
|
||||
if "BOOST" in location.name:
|
||||
found_locations += 1
|
||||
self.assertEqual(found_locations, 0)
|
||||
|
||||
|
||||
class TestBoostsanityEraRequired(CivVITestBase):
|
||||
options = {
|
||||
"boostsanity": "true",
|
||||
"progression_style": "none",
|
||||
"shuffle_goody_hut_rewards": "false",
|
||||
}
|
||||
|
||||
def test_era_required_boosts_not_accessible_early(self) -> None:
|
||||
# BOOST_CIVIC_FEUDALISM has EraRequired=True and ERA_CLASSICAL
|
||||
# It should NOT be accessible in Ancient era
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
|
||||
|
||||
# BOOST_CIVIC_URBANIZATION has EraRequired=True and ERA_INDUSTRIAL
|
||||
# It should NOT be accessible in Ancient era
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
# BOOST_CIVIC_SPACE_RACE has EraRequired=True and ERA_ATOMIC
|
||||
# It should NOT be accessible in Ancient era
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_SPACE_RACE"))
|
||||
|
||||
# Regular boosts without EraRequired should be accessible
|
||||
self.assertTrue(self.can_reach_location("BOOST_TECH_SAILING"))
|
||||
self.assertTrue(self.can_reach_location("BOOST_CIVIC_MILITARY_TRADITION"))
|
||||
|
||||
def test_era_required_boosts_accessible_in_correct_era(self) -> None:
|
||||
# Collect items to reach Classical era
|
||||
self.collect_by_name(["Mining", "Bronze Working", "Astrology", "Writing",
|
||||
"Irrigation", "Sailing", "Animal Husbandry",
|
||||
"State Workforce", "Foreign Trade"])
|
||||
|
||||
# BOOST_CIVIC_FEUDALISM should now be accessible in Classical era
|
||||
self.assertTrue(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
|
||||
|
||||
# BOOST_CIVIC_URBANIZATION still not accessible (requires Industrial)
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
# Collect more items to reach Industrial era
|
||||
self.collect_all_but(["TECH_ROCKETRY"])
|
||||
|
||||
# Now BOOST_CIVIC_URBANIZATION should be accessible
|
||||
self.assertTrue(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
|
||||
class TestBoostsanityEraRequiredWithProgression(CivVITestBase):
|
||||
options = {
|
||||
"boostsanity": "true",
|
||||
"progression_style": "eras_and_districts",
|
||||
"shuffle_goody_hut_rewards": "false",
|
||||
}
|
||||
|
||||
def test_era_required_with_progressive_eras(self) -> None:
|
||||
# Collect all items except Progressive Era
|
||||
self.collect_all_but(["Progressive Era"])
|
||||
|
||||
# Even with all other items, era-required boosts should not be accessible
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
# Collect enough Progressive Era items to reach Classical (needs 2)
|
||||
self.collect(self.get_item_by_name("Progressive Era"))
|
||||
self.collect(self.get_item_by_name("Progressive Era"))
|
||||
|
||||
# BOOST_CIVIC_FEUDALISM should now be accessible
|
||||
self.assertTrue(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
|
||||
|
||||
# But BOOST_CIVIC_URBANIZATION still requires Industrial era (needs 5 total)
|
||||
self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
# Collect 3 more Progressive Era items to reach Industrial
|
||||
self.collect_by_name(["Progressive Era", "Progressive Era", "Progressive Era"])
|
||||
|
||||
# Now BOOST_CIVIC_URBANIZATION should be accessible
|
||||
self.assertTrue(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
|
||||
|
||||
@@ -37,7 +37,7 @@ class CV64Web(WebWorld):
|
||||
|
||||
tutorials = [Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Archipleago Castlevania 64 randomizer on your computer and connecting it to a "
|
||||
"A guide to setting up the Archipelago Castlevania 64 randomizer on your computer and connecting it to a "
|
||||
"multiworld.",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
|
||||
@@ -609,8 +609,8 @@ class CV64PatchExtensions(APPatchExtension):
|
||||
|
||||
# Shimmy speed increase hack
|
||||
if options["increase_shimmy_speed"]:
|
||||
rom_data.write_int32(0x97EB4, 0x803FE9F0)
|
||||
rom_data.write_int32s(0xBFE9F0, patches.shimmy_speed_modifier)
|
||||
rom_data.write_int32(0x97EB4, 0x803FEA20)
|
||||
rom_data.write_int32s(0xBFEA20, patches.shimmy_speed_modifier)
|
||||
|
||||
# Disable landing fall damage
|
||||
if options["fall_guard"]:
|
||||
|
||||
@@ -41,7 +41,7 @@ class CVCotMWeb(WebWorld):
|
||||
|
||||
tutorials = [Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Archipleago Castlevania: Circle of the Moon randomizer on your computer and "
|
||||
"A guide to setting up the Archipelago Castlevania: Circle of the Moon randomizer on your computer and "
|
||||
"connecting it to a multiworld.",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
|
||||
@@ -22,7 +22,7 @@ if __name__ == '__main__':
|
||||
response = requests.get(url)
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Got {response.status_code} when downloading static randomizer locations")
|
||||
annotations = yaml.load(response.text, Loader=yaml.Loader)
|
||||
annotations = yaml.safe_load(response.text)
|
||||
|
||||
static_to_archi_regions = {
|
||||
area['Name']: area['Archipelago']
|
||||
|
||||
27
worlds/dkc3/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Modified MIT License
|
||||
|
||||
Copyright (c) 2025 PoryGone
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, and/or distribute copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
No copy or substantial portion of the Software shall be sublicensed or relicensed
|
||||
without the express written permission of the copyright holder(s)
|
||||
|
||||
No copy or substantial portion of the Software shall be sold without the express
|
||||
written permission of the copyright holder(s)
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import Utils
|
||||
from Utils import read_snes_rom
|
||||
from worlds.AutoWorld import World
|
||||
@@ -735,9 +736,9 @@ def get_base_rom_bytes(file_name: str = "") -> bytes:
|
||||
return base_rom_bytes
|
||||
|
||||
def get_base_rom_path(file_name: str = "") -> str:
|
||||
options = Utils.get_options()
|
||||
if not file_name:
|
||||
file_name = options["dkc3_options"]["rom_file"]
|
||||
from settings import get_settings
|
||||
file_name = get_settings()["dkc3_options"]["rom_file"]
|
||||
if not os.path.exists(file_name):
|
||||
file_name = Utils.user_path(file_name)
|
||||
return file_name
|
||||
|
||||
6
worlds/dkc3/archipelago.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"game": "Donkey Kong Country 3",
|
||||
"authors": [ "PoryGone" ],
|
||||
"minimum_ap_version": "0.6.3",
|
||||
"world_version": "1.1.0"
|
||||
}
|
||||
@@ -111,9 +111,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
|
||||
1. Enter the RetroArch main menu screen.
|
||||
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
|
||||
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
|
||||
Network Command Port at 55355.
|
||||
|
||||

|
||||
Network Command Port at 55355. \
|
||||

|
||||
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
|
||||
Performance)".
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import csv
|
||||
import enum
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
from functools import reduce
|
||||
from random import Random
|
||||
from typing import Dict, List, Set
|
||||
|
||||
@@ -61,7 +62,7 @@ def load_item_csv():
|
||||
item_reader = csv.DictReader(file)
|
||||
for item in item_reader:
|
||||
id = int(item["id"]) if item["id"] else None
|
||||
classification = ItemClassification[item["classification"]]
|
||||
classification = reduce((lambda a, b: a | b), {ItemClassification[str_classification] for str_classification in item["classification"].split(",")})
|
||||
groups = {Group[group] for group in item["groups"].split(",") if group}
|
||||
items.append(ItemData(id, item["name"], classification, groups))
|
||||
return items
|
||||
|
||||
@@ -22,7 +22,7 @@ id,name,classification,groups
|
||||
20,Wall Jump Pack,progression,"DLC,Freemium"
|
||||
21,Health Bar Pack,useful,"DLC,Freemium"
|
||||
22,Parallax Pack,filler,"DLC,Freemium"
|
||||
23,Harmless Plants Pack,progression,"DLC,Freemium"
|
||||
23,Harmless Plants Pack,"progression,trap","DLC,Freemium"
|
||||
24,Death of Comedy Pack,progression,"DLC,Freemium"
|
||||
25,Canadian Dialog Pack,filler,"DLC,Freemium"
|
||||
26,DLC NPC Pack,progression,"DLC,Freemium"
|
||||
|
||||
|
@@ -59,6 +59,19 @@ class FactorioCommandProcessor(ClientCommandProcessor):
|
||||
def _cmd_toggle_chat(self):
|
||||
"""Toggle sending of chat messages from players on the Factorio server to Archipelago."""
|
||||
self.ctx.toggle_bridge_chat_out()
|
||||
|
||||
def _cmd_rcon_reconnect(self) -> bool:
|
||||
"""Reconnect the RCON client if its disconnected."""
|
||||
try:
|
||||
result = self.ctx.rcon_client.send_command("/help")
|
||||
if result:
|
||||
self.output("RCON Client already connected.")
|
||||
return True
|
||||
except factorio_rcon.RCONNetworkError:
|
||||
self.ctx.rcon_client = factorio_rcon.RCONClient("localhost", self.ctx.rcon_port, self.ctx.rcon_password, timeout=5)
|
||||
self.output("RCON Client successfully reconnected.")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FactorioContext(CommonContext):
|
||||
@@ -242,7 +255,13 @@ async def game_watcher(ctx: FactorioContext):
|
||||
if ctx.rcon_client and time.perf_counter() > next_bridge:
|
||||
next_bridge = time.perf_counter() + 1
|
||||
ctx.awaiting_bridge = False
|
||||
data = json.loads(ctx.rcon_client.send_command("/ap-sync"))
|
||||
try:
|
||||
data = json.loads(ctx.rcon_client.send_command("/ap-sync"))
|
||||
except factorio_rcon.RCONNotConnected:
|
||||
continue
|
||||
except factorio_rcon.RCONNetworkError:
|
||||
bridge_logger.warning("RCON Client has unexpectedly lost connection. Please issue /rcon_reconnect.")
|
||||
continue
|
||||
if not ctx.auth:
|
||||
pass # auth failed, wait for new attempt
|
||||
elif data["slot_name"] != ctx.auth:
|
||||
@@ -294,9 +313,13 @@ async def game_watcher(ctx: FactorioContext):
|
||||
"cmd": "Set", "key": ctx.energylink_key, "operations":
|
||||
[{"operation": "add", "value": value}]
|
||||
}]))
|
||||
ctx.rcon_client.send_command(
|
||||
f"/ap-energylink -{value}")
|
||||
logger.debug(f"EnergyLink: Sent {format_SI_prefix(value)}J")
|
||||
try:
|
||||
ctx.rcon_client.send_command(
|
||||
f"/ap-energylink -{value}")
|
||||
except factorio_rcon.RCONNetworkError:
|
||||
bridge_logger.warning("RCON Client has unexpectedly lost connection. Please issue /rcon_reconnect.")
|
||||
else:
|
||||
logger.debug(f"EnergyLink: Sent {format_SI_prefix(value)}J")
|
||||
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 627 KiB After Width: | Height: | Size: 493 KiB |
@@ -92,7 +92,7 @@ appropriate to your operating system, and extract the folder to a convenient loc
|
||||
Archipelago is to place the extracted game folder into the `Archipelago` directory and rename it to just be "Factorio".
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
Next, you should launch your Factorio Server by running `factorio.exe`, which is located at: `bin/x64/factorio.exe`. You
|
||||
will be asked to log in to your Factorio account using the same credentials you used on Factorio's website. After you
|
||||
@@ -122,7 +122,7 @@ This allows you to host your own Factorio game.
|
||||
Archipelago if you chose to include it during the installation process.
|
||||
6. Enter `/connect [server-address]` into the input box at the bottom of the Archipelago Client and press "Enter"
|
||||
|
||||

|
||||

|
||||
|
||||
7. Launch your Factorio Client
|
||||
8. Click on "Multiplayer" in the main menu
|
||||
|
||||
@@ -16,6 +16,7 @@ logger = logging.getLogger("Client")
|
||||
|
||||
|
||||
rom_name_location = 0x07FFE3
|
||||
player_name_location = 0x07BCC0
|
||||
locations_array_start = 0x200
|
||||
locations_array_length = 0x100
|
||||
items_obtained = 0x03
|
||||
@@ -111,6 +112,12 @@ class FF1Client(BizHawkClient):
|
||||
|
||||
return True
|
||||
|
||||
async def set_auth(self, ctx: "BizHawkClientContext") -> None:
|
||||
auth_raw = (await bizhawk.read(
|
||||
ctx.bizhawk_ctx,
|
||||
[(player_name_location, 0x40, self.rom)]))[0]
|
||||
ctx.auth = str(auth_raw, "utf-8").replace("\x00", "").strip()
|
||||
|
||||
async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
|
||||
if ctx.server is None:
|
||||
return
|
||||
@@ -204,7 +211,7 @@ class FF1Client(BizHawkClient):
|
||||
write_list.append((location, [0], self.sram))
|
||||
elif current_item_name in no_overworld_items:
|
||||
if current_item_name == "Sigil":
|
||||
location = 0x28
|
||||
location = 0x2B
|
||||
else:
|
||||
location = 0x12
|
||||
write_list.append((location, [1], self.sram))
|
||||
|
||||
@@ -253,5 +253,17 @@
|
||||
"CubeBot": 529,
|
||||
"Sarda": 525,
|
||||
"Fairy": 531,
|
||||
"Lefein": 527
|
||||
"Lefein": 527,
|
||||
"DeepDungeon32B_Chest144": 401,
|
||||
"DeepDungeon30B_Chest145": 402,
|
||||
"DeepDungeon29B_Chest146": 403,
|
||||
"DeepDungeon29B_Chest147": 404,
|
||||
"DeepDungeon40B_Chest186": 443,
|
||||
"DeepDungeon38B_Chest188": 445,
|
||||
"DeepDungeon36B_Chest189": 446,
|
||||
"DeepDungeon33B_Chest190": 447,
|
||||
"DeepDungeon40B_Chest191": 448,
|
||||
"DeepDungeon41B_Chest192": 449,
|
||||
"DeepDungeon34B_Chest193": 450,
|
||||
"DeepDungeon39B_Chest194": 451
|
||||
}
|
||||
|
||||
@@ -115,9 +115,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
|
||||
1. Enter the RetroArch main menu screen.
|
||||
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
|
||||
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
|
||||
Network Command Port at 55355.
|
||||
|
||||

|
||||
Network Command Port at 55355. \
|
||||

|
||||
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
|
||||
Performance)".
|
||||
|
||||
|
||||
@@ -123,10 +123,8 @@ Vous ne devez faire ces étapes qu'une fois. À noter que RetroArch 1.9.x ne fon
|
||||
1. Entrez dans le menu principal de RetroArch.
|
||||
2. Allez dans Settings --> User Interface. Activez l'option "Show Advanced Settings".
|
||||
3. Allez dans Settings --> Network. Activez l'option "Network Commands", qui se trouve sous "Request Device 16".
|
||||
Laissez le "Network Command Port" à sa valeur par defaut, qui devrait être 55355.
|
||||
|
||||
|
||||

|
||||
Laissez le "Network Command Port" à sa valeur par defaut, qui devrait être 55355. \
|
||||

|
||||
4. Allez dans le Menu Principal --> Online Updater --> Core Downloader. Trouvez et sélectionnez "Nintendo - SNES / SFC (bsnes-mercury
|
||||
Performance)".
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from typing import NamedTuple, Union
|
||||
from typing_extensions import deprecated
|
||||
import logging
|
||||
|
||||
from BaseClasses import Item, Tutorial, ItemClassification
|
||||
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from ..AutoWorld import InvalidItemError, World, WebWorld
|
||||
from NetUtils import SlotType
|
||||
|
||||
|
||||
@@ -47,9 +48,10 @@ class GenericWorld(World):
|
||||
def create_item(self, name: str) -> Item:
|
||||
if name == "Nothing":
|
||||
return Item(name, ItemClassification.filler, -1, self.player)
|
||||
raise KeyError(name)
|
||||
|
||||
raise InvalidItemError(name)
|
||||
|
||||
@deprecated("worlds.generic.PlandoItem is deprecated and will be removed in the next version. "
|
||||
"Use Options.PlandoItem(s) instead.")
|
||||
class PlandoItem(NamedTuple):
|
||||
item: str
|
||||
location: str
|
||||
|
||||
@@ -81,7 +81,8 @@ are `description`, `name`, `game`, `requires`, and the name of the games you wan
|
||||
* `requires` details different requirements from the generator for the YAML to work as you expect it to. Generally this
|
||||
is good for detailing the version of Archipelago this YAML was prepared for. If it is rolled on an older version,
|
||||
options may be missing and as such it will not work as expected. If any plando is used in the file then requiring it
|
||||
here to ensure it will be used is good practice.
|
||||
here to ensure it will be used is good practice. Specific versions of custom worlds can also be required, ensuring
|
||||
that the generator is using a compatible version.
|
||||
|
||||
## Game Options
|
||||
|
||||
@@ -165,7 +166,9 @@ game:
|
||||
A Link to the Past: 10
|
||||
Timespinner: 10
|
||||
requires:
|
||||
version: 0.4.1
|
||||
version: 0.6.4
|
||||
game:
|
||||
A Link to the Past: 0.6.4
|
||||
A Link to the Past:
|
||||
accessibility: minimal
|
||||
progression_balancing: 50
|
||||
@@ -214,12 +217,13 @@ Timespinner:
|
||||
progression_balancing: 50
|
||||
item_links: # Share part of your item pool with other players.
|
||||
- name: TSAll
|
||||
item_pool:
|
||||
item_pool:
|
||||
- Everything
|
||||
local_items:
|
||||
- Twin Pyramid Key
|
||||
- Timespinner Wheel
|
||||
replacement_item: null
|
||||
skip_if_solo: true
|
||||
```
|
||||
|
||||
#### This is a fully functional yaml file that will do all the following things:
|
||||
@@ -228,7 +232,7 @@ Timespinner:
|
||||
* `name` is `Example Player` and this will be used in the server console when sending and receiving items.
|
||||
* `game` has an equal chance of being either `A Link to the Past` or `Timespinner` with a 10/20 chance for each. This is
|
||||
because each game has a weight of 10 and the total of all weights is 20.
|
||||
* `requires` is set to required release version 0.3.2 or higher.
|
||||
* `requires` is set to require Archipelago release version 0.6.4 or higher, as well as A Link to the Past version 0.6.4.
|
||||
* `accessibility` for both games is set to `minimal` which will set this seed to beatable only, so some locations and
|
||||
items may be completely inaccessible but the seed will still be completable.
|
||||
* `progression_balancing` for both games is set to 50, the default value, meaning we will likely receive important items
|
||||
@@ -262,7 +266,7 @@ Timespinner:
|
||||
* For `Timespinner` all players in the `TSAll` item link group will share their entire item pool and the `Twin Pyramid
|
||||
Key` and `Timespinner Wheel` will be forced among the worlds of those in the group. The `null` replacement item
|
||||
will, instead of forcing a specific chosen item, allow the generator to randomly pick a filler item to replace the
|
||||
player items.
|
||||
player items. This item link will only be created if there are at least two players in the group.
|
||||
* `triggers` allows us to define a trigger such that if our `smallkey_shuffle` option happens to roll the `any_world`
|
||||
result it will also ensure that `bigkey_shuffle`, `map_shuffle`, and `compass_shuffle` are also forced to the
|
||||
`any_world` result. More information on triggers can be found in the
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Archipelago does not have a compiled release on macOS. However, it is possible to run from source code on macOS. This guide expects you to have some experience with running software from the terminal.
|
||||
## Prerequisite Software
|
||||
Here is a list of software to install and source code to download.
|
||||
1. Python 3.10 "universal2" or newer from the [macOS Python downloads page](https://www.python.org/downloads/macos/).
|
||||
**Python 3.13 is not supported yet.**
|
||||
1. Python 3.11 "universal2" or newer from the [macOS Python downloads page](https://www.python.org/downloads/macos/).
|
||||
**Python 3.14 is not supported yet.**
|
||||
2. Xcode from the [macOS App Store](https://apps.apple.com/us/app/xcode/id497799835).
|
||||
3. The source code from the [Archipelago releases page](https://github.com/ArchipelagoMW/Archipelago/releases).
|
||||
4. The asset with darwin in the name from the [SNI Github releases page](https://github.com/alttpo/sni/releases).
|
||||
|
||||
BIN
worlds/generic/docs/retroarch-network-commands-en.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
worlds/generic/docs/retroarch-network-commands-fr.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -136,6 +136,27 @@ are rolling locally, ensure this file is edited to your liking **before** rollin
|
||||
when running the Archipelago Installation software. If you have changed settings in this file, and would like to retain
|
||||
them, you may rename the file to `options.yaml`.
|
||||
|
||||
### Playing with custom worlds
|
||||
|
||||
If you are generating locally, you can play with worlds that are not included in the Archipelago installation.
|
||||
These worlds are packaged as `.apworld` files. To add a world to your installation, click the "Install APWorld" button
|
||||
in the launcher and select the `.apworld` file you wish to install. Alternatively, you can drag the `.apworld` file
|
||||
onto the launcher or double-click the file itself (if on Windows). Once the world is installed, it will function like
|
||||
the worlds that are already packaged with Archipelago. Also note that while generation with custom worlds must be done
|
||||
locally, these games can then be uploaded to the website for hosting and played as normal.
|
||||
|
||||
We strongly recommend that you ensure the source of the `.apworld` is safe and trustworthy before playing with a
|
||||
custom world. Installed APWorlds are able to run custom code on your computer whenever you open Archipelago.
|
||||
|
||||
#### Alternate versions of included worlds
|
||||
|
||||
If you want to play with an alternate version of a game that is already included in Archipelago, you should also
|
||||
remove the original APWorld after completing the above installation. To do so, go to your Archipelago installation
|
||||
folder and navigate to the `lib/worlds` directory. Then move the `.apworld` or the folder corresponding to the game you
|
||||
want to play an alternate version of to somewhere else as a backup. If you want to play this original again, then
|
||||
restore the original version to `lib/worlds` and remove the alternate version, which is in the `custom_worlds` folder.
|
||||
|
||||
Note: Currently, this cannot be done on the Linux AppImage release.
|
||||
|
||||
## Hosting an Archipelago Server
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Required Software
|
||||
|
||||
- [Heretic (e.g. Steam version)](https://store.steampowered.com/app/2390/Heretic_Shadow_of_the_Serpent_Riders/)
|
||||
- [Heretic (e.g. Steam version)](https://store.steampowered.com/app/3286930/Heretic__Hexen/)
|
||||
- [Archipelago Crispy DOOM](https://github.com/Daivuk/apdoom/releases) (Same download for DOOM 1993, DOOM II and Heretic)
|
||||
|
||||
## Optional Software
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* A legal copy of Hollow Knight.
|
||||
* Steam, Gog, and Xbox Game Pass versions of the game are supported.
|
||||
* Windows, Mac, and Linux (including Steam Deck) are supported.
|
||||
|
||||
**Do NOT** install BepInEx, it is not required and is incompatible with most mods. Archipelago, along with the majority of mods use custom tooling pre-dating BepInEx, and they are only available through Lumafly and similar installers rather than sites like Nexus Mods.
|
||||
|
||||
## Installing the Archipelago Mod using Lumafly
|
||||
1. Launch Lumafly and ensure it locates your Hollow Knight installation directory.
|
||||
@@ -42,7 +44,7 @@ See the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) here on t
|
||||
You can use the [game options page for Hollow Knight](/games/Hollow%20Knight/player-options) here on the Archipelago
|
||||
website to generate a YAML using a graphical interface.
|
||||
|
||||
### Joining an Archipelago Game in Hollow Knight
|
||||
## Joining an Archipelago Game in Hollow Knight
|
||||
1. Start the game after installing all necessary mods.
|
||||
2. Create a **new save game.**
|
||||
3. Select the **Archipelago** game mode from the mode selection screen.
|
||||
|
||||