mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-07 15:13:52 -08:00
Compare commits
1 Commits
fix-text-c
...
NewSoupVi-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3d7f247d1 |
@@ -728,7 +728,7 @@ class CollectionState():
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_from_list_unique(self, items: Iterable[str], player: int, count: int) -> bool:
|
||||
def has_from_list_exclusive(self, items: Iterable[str], player: int, count: int) -> bool:
|
||||
"""Returns True if the state contains at least `count` items matching any of the item names from a list.
|
||||
Ignores duplicates of the same item."""
|
||||
found: int = 0
|
||||
@@ -743,7 +743,7 @@ class CollectionState():
|
||||
"""Returns the cumulative count of items from a list present in state."""
|
||||
return sum(self.prog_items[player][item_name] for item_name in items)
|
||||
|
||||
def count_from_list_unique(self, items: Iterable[str], player: int) -> int:
|
||||
def count_from_list_exclusive(self, items: Iterable[str], player: int) -> int:
|
||||
"""Returns the cumulative count of items from a list present in state. Ignores duplicates of the same item."""
|
||||
return sum(self.prog_items[player][item_name] > 0 for item_name in items)
|
||||
|
||||
@@ -758,7 +758,7 @@ class CollectionState():
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_group_unique(self, item_name_group: str, player: int, count: int = 1) -> bool:
|
||||
def has_group_exclusive(self, item_name_group: str, player: int, count: int = 1) -> bool:
|
||||
"""Returns True if the state contains at least `count` items present in a specified item group.
|
||||
Ignores duplicates of the same item.
|
||||
"""
|
||||
@@ -778,7 +778,7 @@ class CollectionState():
|
||||
for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]
|
||||
)
|
||||
|
||||
def count_group_unique(self, item_name_group: str, player: int) -> int:
|
||||
def count_group_exclusive(self, item_name_group: str, player: int) -> int:
|
||||
"""Returns the cumulative count of items from an item group present in state.
|
||||
Ignores duplicates of the same item."""
|
||||
player_prog_items = self.prog_items[player]
|
||||
|
||||
10
Generate.py
10
Generate.py
@@ -432,6 +432,7 @@ def handle_option(ret: argparse.Namespace, game_weights: dict, option_key: str,
|
||||
player_option = option.from_any(game_weights[option_key])
|
||||
else:
|
||||
player_option = option.from_any(get_choice(option_key, game_weights))
|
||||
del game_weights[option_key]
|
||||
else:
|
||||
player_option = option.from_any(option.default) # call the from_any here to support default "random"
|
||||
setattr(ret, option_key, player_option)
|
||||
@@ -445,9 +446,9 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||
if "linked_options" in weights:
|
||||
weights = roll_linked_options(weights)
|
||||
|
||||
valid_keys = set()
|
||||
valid_trigger_names = set()
|
||||
if "triggers" in weights:
|
||||
weights = roll_triggers(weights, weights["triggers"], valid_keys)
|
||||
weights = roll_triggers(weights, weights["triggers"], valid_trigger_names)
|
||||
|
||||
requirements = weights.get("requires", {})
|
||||
if requirements:
|
||||
@@ -489,7 +490,7 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||
raise Exception(f"Remove tag cannot be used outside of trigger contexts. Found {weight}")
|
||||
|
||||
if "triggers" in game_weights:
|
||||
weights = roll_triggers(weights, game_weights["triggers"], valid_keys)
|
||||
weights = roll_triggers(weights, game_weights["triggers"], valid_trigger_names)
|
||||
game_weights = weights[ret.game]
|
||||
|
||||
ret.name = get_choice('name', weights)
|
||||
@@ -498,9 +499,8 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||
|
||||
for option_key, option in world_type.options_dataclass.type_hints.items():
|
||||
handle_option(ret, game_weights, option_key, option, plando_options)
|
||||
valid_keys.add(option_key)
|
||||
for option_key in game_weights:
|
||||
if option_key in {"triggers", *valid_keys}:
|
||||
if option_key in {"triggers", *valid_trigger_names}:
|
||||
continue
|
||||
logging.warning(f"{option_key} is not a valid option name for {ret.game} and is not present in triggers.")
|
||||
if PlandoOptions.items in plando_options:
|
||||
|
||||
12
Main.py
12
Main.py
@@ -372,17 +372,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
|
||||
checks_in_area: Dict[int, Dict[str, Union[int, List[int]]]] = {}
|
||||
|
||||
# get spheres -> filter address==None -> skip empty
|
||||
spheres: List[Dict[int, Set[int]]] = []
|
||||
for sphere in multiworld.get_spheres():
|
||||
current_sphere: Dict[int, Set[int]] = collections.defaultdict(set)
|
||||
for sphere_location in sphere:
|
||||
if type(sphere_location.address) is int:
|
||||
current_sphere[sphere_location.player].add(sphere_location.address)
|
||||
|
||||
if current_sphere:
|
||||
spheres.append(dict(current_sphere))
|
||||
|
||||
multidata = {
|
||||
"slot_data": slot_data,
|
||||
"slot_info": slot_info,
|
||||
@@ -397,7 +386,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
"tags": ["AP"],
|
||||
"minimum_versions": minimum_versions,
|
||||
"seed_name": multiworld.seed_name,
|
||||
"spheres": spheres,
|
||||
"datapackage": data_package,
|
||||
}
|
||||
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
|
||||
|
||||
@@ -175,11 +175,8 @@ class Context:
|
||||
all_item_and_group_names: typing.Dict[str, typing.Set[str]]
|
||||
all_location_and_group_names: typing.Dict[str, typing.Set[str]]
|
||||
non_hintable_names: typing.Dict[str, typing.Set[str]]
|
||||
spheres: typing.List[typing.Dict[int, typing.Set[int]]]
|
||||
""" each sphere is { player: { location_id, ... } } """
|
||||
logger: logging.Logger
|
||||
|
||||
|
||||
def __init__(self, host: str, port: int, server_password: str, password: str, location_check_points: int,
|
||||
hint_cost: int, item_cheat: bool, release_mode: str = "disabled", collect_mode="disabled",
|
||||
remaining_mode: str = "disabled", auto_shutdown: typing.SupportsFloat = 0, compatibility: int = 2,
|
||||
@@ -241,7 +238,6 @@ class Context:
|
||||
self.stored_data = {}
|
||||
self.stored_data_notification_clients = collections.defaultdict(weakref.WeakSet)
|
||||
self.read_data = {}
|
||||
self.spheres = []
|
||||
|
||||
# init empty to satisfy linter, I suppose
|
||||
self.gamespackage = {}
|
||||
@@ -470,9 +466,6 @@ class Context:
|
||||
for game_name, data in self.location_name_groups.items():
|
||||
self.read_data[f"location_name_groups_{game_name}"] = lambda lgame=game_name: self.location_name_groups[lgame]
|
||||
|
||||
# sorted access spheres
|
||||
self.spheres = decoded_obj.get("spheres", [])
|
||||
|
||||
# saving
|
||||
|
||||
def save(self, now=False) -> bool:
|
||||
@@ -631,16 +624,6 @@ class Context:
|
||||
self.recheck_hints(team, slot)
|
||||
return self.hints[team, slot]
|
||||
|
||||
def get_sphere(self, player: int, location_id: int) -> int:
|
||||
"""Get sphere of a location, -1 if spheres are not available."""
|
||||
if self.spheres:
|
||||
for i, sphere in enumerate(self.spheres):
|
||||
if location_id in sphere.get(player, set()):
|
||||
return i
|
||||
raise KeyError(f"No Sphere found for location ID {location_id} belonging to player {player}. "
|
||||
f"Location or player may not exist.")
|
||||
return -1
|
||||
|
||||
def get_players_package(self):
|
||||
return [NetworkPlayer(t, p, self.get_aliased_name(t, p), n) for (t, p), n in self.player_names.items()]
|
||||
|
||||
@@ -1566,9 +1549,6 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
self.ctx.random.shuffle(not_found_hints)
|
||||
# By popular vote, make hints prefer non-local placements
|
||||
not_found_hints.sort(key=lambda hint: int(hint.receiving_player != hint.finding_player))
|
||||
# By another popular vote, prefer early sphere
|
||||
not_found_hints.sort(key=lambda hint: self.ctx.get_sphere(hint.finding_player, hint.location),
|
||||
reverse=True)
|
||||
|
||||
hints = found_hints + old_hints
|
||||
while can_pay > 0:
|
||||
@@ -1578,10 +1558,10 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
hints.append(hint)
|
||||
can_pay -= 1
|
||||
self.ctx.hints_used[self.client.team, self.client.slot] += 1
|
||||
points_available = get_client_points(self.ctx, self.client)
|
||||
|
||||
self.ctx.notify_hints(self.client.team, hints)
|
||||
if not_found_hints:
|
||||
points_available = get_client_points(self.ctx, self.client)
|
||||
if hints and cost and int((points_available // cost) == 0):
|
||||
self.output(
|
||||
f"There may be more hintables, however, you cannot afford to pay for any more. "
|
||||
|
||||
46
Options.py
46
Options.py
@@ -1130,41 +1130,9 @@ class OptionGroup(typing.NamedTuple):
|
||||
"""Name of the group to categorize these options in for display on the WebHost and in generated YAMLS."""
|
||||
options: typing.List[typing.Type[Option[typing.Any]]]
|
||||
"""Options to be in the defined group."""
|
||||
start_collapsed: bool = False
|
||||
"""Whether the group will start collapsed on the WebHost options pages."""
|
||||
|
||||
|
||||
item_and_loc_options = [LocalItems, NonLocalItems, StartInventory, StartInventoryPool, StartHints,
|
||||
StartLocationHints, ExcludeLocations, PriorityLocations, ItemLinks]
|
||||
"""
|
||||
Options that are always populated in "Item & Location Options" Option Group. Cannot be moved to another group.
|
||||
If desired, a custom "Item & Location Options" Option Group can be defined, but only for adding additional options to
|
||||
it.
|
||||
"""
|
||||
|
||||
|
||||
def get_option_groups(world: typing.Type[World], visibility_level: Visibility = Visibility.template) -> typing.Dict[
|
||||
str, typing.Dict[str, typing.Type[Option[typing.Any]]]]:
|
||||
"""Generates and returns a dictionary for the option groups of a specified world."""
|
||||
option_groups = {option: option_group.name
|
||||
for option_group in world.web.option_groups
|
||||
for option in option_group.options}
|
||||
# add a default option group for uncategorized options to get thrown into
|
||||
ordered_groups = ["Game Options"]
|
||||
[ordered_groups.append(group) for group in option_groups.values() if group not in ordered_groups]
|
||||
grouped_options = {group: {} for group in ordered_groups}
|
||||
for option_name, option in world.options_dataclass.type_hints.items():
|
||||
if visibility_level & option.visibility:
|
||||
grouped_options[option_groups.get(option, "Game Options")][option_name] = option
|
||||
|
||||
# if the world doesn't have any ungrouped options, this group will be empty so just remove it
|
||||
if not grouped_options["Game Options"]:
|
||||
del grouped_options["Game Options"]
|
||||
|
||||
return grouped_options
|
||||
|
||||
|
||||
def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True) -> None:
|
||||
def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True):
|
||||
import os
|
||||
|
||||
import yaml
|
||||
@@ -1202,7 +1170,17 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
|
||||
|
||||
for game_name, world in AutoWorldRegister.world_types.items():
|
||||
if not world.hidden or generate_hidden:
|
||||
grouped_options = get_option_groups(world)
|
||||
|
||||
option_groups = {option: option_group.name
|
||||
for option_group in world.web.option_groups
|
||||
for option in option_group.options}
|
||||
ordered_groups = ["Game Options"]
|
||||
[ordered_groups.append(group) for group in option_groups.values() if group not in ordered_groups]
|
||||
grouped_options = {group: {} for group in ordered_groups}
|
||||
for option_name, option in world.options_dataclass.type_hints.items():
|
||||
if option.visibility >= Visibility.template:
|
||||
grouped_options[option_groups.get(option, "Game Options")][option_name] = option
|
||||
|
||||
with open(local_path("data", "options.yaml")) as f:
|
||||
file_data = f.read()
|
||||
res = Template(file_data).render(
|
||||
|
||||
45
README.md
45
README.md
@@ -1,10 +1,8 @@
|
||||
# [Archipelago](https://archipelago.gg)  | [Install](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
||||
Archipelago provides a generic framework for developing multiworld capability for game randomizers. In all cases,
|
||||
presently, Archipelago is also the randomizer itself.
|
||||
Archipelago provides a generic framework for developing multiworld capability for game randomizers. In all cases, presently, Archipelago is also the randomizer itself.
|
||||
|
||||
Currently, the following games are supported:
|
||||
|
||||
* The Legend of Zelda: A Link to the Past
|
||||
* Factorio
|
||||
* Minecraft
|
||||
@@ -79,57 +77,36 @@ windows binaries.
|
||||
|
||||
## History
|
||||
|
||||
Archipelago is built upon a strong legacy of brilliant hobbyists. We want to honor that legacy by showing it here.
|
||||
The repositories which Archipelago is built upon, inspired by, or otherwise owes its gratitude to are:
|
||||
Archipelago is built upon a strong legacy of brilliant hobbyists. We want to honor that legacy by showing it here. The repositories which Archipelago is built upon, inspired by, or otherwise owes its gratitude to are:
|
||||
|
||||
* [bonta0's MultiWorld](https://github.com/Bonta0/ALttPEntranceRandomizer/tree/multiworld_31)
|
||||
* [AmazingAmpharos' Entrance Randomizer](https://github.com/AmazingAmpharos/ALttPEntranceRandomizer)
|
||||
* [VT Web Randomizer](https://github.com/sporchia/alttp_vt_randomizer)
|
||||
* [Dessyreqt's alttprandomizer](https://github.com/Dessyreqt/alttprandomizer)
|
||||
* [Zarby89's](https://github.com/Ijwu/Enemizer/commits?author=Zarby89)
|
||||
and [sosuke3's](https://github.com/Ijwu/Enemizer/commits?author=sosuke3) contributions to Enemizer, which make up the
|
||||
vast majority of Enemizer contributions.
|
||||
* [Zarby89's](https://github.com/Ijwu/Enemizer/commits?author=Zarby89) and [sosuke3's](https://github.com/Ijwu/Enemizer/commits?author=sosuke3) contributions to Enemizer, which make the vast majority of Enemizer contributions.
|
||||
|
||||
We recognize that there is a strong community of incredibly smart people that have come before us and helped pave the
|
||||
path. Just because one person's name may be in a repository title does not mean that only one person made that project
|
||||
happen. We can't hope to perfectly cover every single contribution that lead up to Archipelago, but we hope to honor
|
||||
them fairly.
|
||||
We recognize that there is a strong community of incredibly smart people that have come before us and helped pave the path. Just because one person's name may be in a repository title does not mean that only one person made that project happen. We can't hope to perfectly cover every single contribution that lead up to Archipelago but we hope to honor them fairly.
|
||||
|
||||
### Path to the Archipelago
|
||||
|
||||
Archipelago was directly forked from bonta0's `multiworld_31` branch of ALttPEntranceRandomizer (this project has a
|
||||
long legacy of its own, please check it out linked above) on January 12, 2020. The repository was then named to
|
||||
_MultiWorld-Utilities_ to better encompass its intended function. As Archipelago matured, then known as
|
||||
"Berserker's MultiWorld" by some, we found it necessary to transform our repository into a root level repository
|
||||
(as opposed to a 'forked repo') and change the name (which came later) to better reflect our project.
|
||||
Archipelago was directly forked from bonta0's `multiworld_31` branch of ALttPEntranceRandomizer (this project has a long legacy of its own, please check it out linked above) on January 12, 2020. The repository was then named to _MultiWorld-Utilities_ to better encompass its intended function. As Archipelago matured, then known as "Berserker's MultiWorld" by some, we found it necessary to transform our repository into a root level repository (as opposed to a 'forked repo') and change the name (which came later) to better reflect our project.
|
||||
|
||||
## Running Archipelago
|
||||
For most people, all you need to do is head over to the [releases](https://github.com/ArchipelagoMW/Archipelago/releases) page then download and run the appropriate installer, or AppImage for Linux-based systems.
|
||||
|
||||
For most people, all you need to do is head over to
|
||||
the [releases page](https://github.com/ArchipelagoMW/Archipelago/releases), then download and run the appropriate
|
||||
installer, or AppImage for Linux-based systems.
|
||||
|
||||
If you are a developer or are running on a platform with no compiled releases available, please see our doc on
|
||||
[running Archipelago from source](docs/running%20from%20source.md).
|
||||
If you are a developer or are running on a platform with no compiled releases available, please see our doc on [running Archipelago from source](docs/running%20from%20source.md).
|
||||
|
||||
## Related Repositories
|
||||
|
||||
This project makes use of multiple other projects. We wouldn't be here without these other repositories and the
|
||||
contributions of their developers, past and present.
|
||||
This project makes use of multiple other projects. We wouldn't be here without these other repositories and the contributions of their developers, past and present.
|
||||
|
||||
* [z3randomizer](https://github.com/ArchipelagoMW/z3randomizer)
|
||||
* [Enemizer](https://github.com/Ijwu/Enemizer)
|
||||
* [Ocarina of Time Randomizer](https://github.com/TestRunnerSRL/OoT-Randomizer)
|
||||
|
||||
## Contributing
|
||||
|
||||
To contribute to Archipelago, including the WebHost, core program, or by adding a new game, see our
|
||||
[Contributing guidelines](/docs/contributing.md).
|
||||
For contribution guidelines, please see our [Contributing doc.](/docs/contributing.md)
|
||||
|
||||
## FAQ
|
||||
|
||||
For Frequently asked questions, please see the website's [FAQ Page](https://archipelago.gg/faq/en/).
|
||||
For Frequently asked questions, please see the website's [FAQ Page.](https://archipelago.gg/faq/en/)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please refer to our [code of conduct](/docs/code_of_conduct.md).
|
||||
Please refer to our [code of conduct.](/docs/code_of_conduct.md)
|
||||
|
||||
@@ -106,9 +106,9 @@ class WebHostContext(Context):
|
||||
static_gamespackage = self.gamespackage # this is shared across all rooms
|
||||
static_item_name_groups = self.item_name_groups
|
||||
static_location_name_groups = self.location_name_groups
|
||||
self.gamespackage = {"Archipelago": static_gamespackage.get("Archipelago", {})} # this may be modified by _load
|
||||
self.item_name_groups = {"Archipelago": static_item_name_groups.get("Archipelago", {})}
|
||||
self.location_name_groups = {"Archipelago": static_location_name_groups.get("Archipelago", {})}
|
||||
self.gamespackage = {"Archipelago": static_gamespackage["Archipelago"]} # this may be modified by _load
|
||||
self.item_name_groups = {}
|
||||
self.location_name_groups = {}
|
||||
|
||||
for game in list(multidata.get("datapackage", {})):
|
||||
game_data = multidata["datapackage"][game]
|
||||
|
||||
@@ -6,7 +6,7 @@ import random
|
||||
import tempfile
|
||||
import zipfile
|
||||
from collections import Counter
|
||||
from typing import Any, Dict, List, Optional, Union, Set
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from flask import flash, redirect, render_template, request, session, url_for
|
||||
from pony.orm import commit, db_session
|
||||
@@ -16,7 +16,6 @@ from Generate import PlandoOptions, handle_name
|
||||
from Main import main as ERmain
|
||||
from Utils import __version__
|
||||
from WebHostLib import app
|
||||
from settings import ServerOptions, GeneratorOptions
|
||||
from worlds.alttp.EntranceRandomizer import parse_arguments
|
||||
from .check import get_yaml_data, roll_options
|
||||
from .models import Generation, STATE_ERROR, STATE_QUEUED, Seed, UUID
|
||||
@@ -24,22 +23,25 @@ from .upload import upload_zip_to_db
|
||||
|
||||
|
||||
def get_meta(options_source: dict, race: bool = False) -> Dict[str, Union[List[str], Dict[str, Any]]]:
|
||||
plando_options: Set[str] = set()
|
||||
for substr in ("bosses", "items", "connections", "texts"):
|
||||
if options_source.get(f"plando_{substr}", substr in GeneratorOptions.plando_options):
|
||||
plando_options.add(substr)
|
||||
plando_options = {
|
||||
options_source.get("plando_bosses", ""),
|
||||
options_source.get("plando_items", ""),
|
||||
options_source.get("plando_connections", ""),
|
||||
options_source.get("plando_texts", "")
|
||||
}
|
||||
plando_options -= {""}
|
||||
|
||||
server_options = {
|
||||
"hint_cost": int(options_source.get("hint_cost", ServerOptions.hint_cost)),
|
||||
"release_mode": options_source.get("release_mode", ServerOptions.release_mode),
|
||||
"remaining_mode": options_source.get("remaining_mode", ServerOptions.remaining_mode),
|
||||
"collect_mode": options_source.get("collect_mode", ServerOptions.collect_mode),
|
||||
"item_cheat": bool(int(options_source.get("item_cheat", not ServerOptions.disable_item_cheat))),
|
||||
"hint_cost": int(options_source.get("hint_cost", 10)),
|
||||
"release_mode": options_source.get("release_mode", "goal"),
|
||||
"remaining_mode": options_source.get("remaining_mode", "disabled"),
|
||||
"collect_mode": options_source.get("collect_mode", "disabled"),
|
||||
"item_cheat": bool(int(options_source.get("item_cheat", 1))),
|
||||
"server_password": options_source.get("server_password", None),
|
||||
}
|
||||
generator_options = {
|
||||
"spoiler": int(options_source.get("spoiler", GeneratorOptions.spoiler)),
|
||||
"race": race,
|
||||
"spoiler": int(options_source.get("spoiler", 0)),
|
||||
"race": race
|
||||
}
|
||||
|
||||
if race:
|
||||
|
||||
@@ -11,7 +11,6 @@ import Options
|
||||
from Utils import local_path
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
from . import app, cache
|
||||
from .generate import get_meta
|
||||
|
||||
|
||||
def create() -> None:
|
||||
@@ -28,21 +27,26 @@ def get_world_theme(game_name: str) -> str:
|
||||
|
||||
|
||||
def render_options_page(template: str, world_name: str, is_complex: bool = False) -> Union[Response, str]:
|
||||
visibility_flag = Options.Visibility.complex_ui if is_complex else Options.Visibility.simple_ui
|
||||
world = AutoWorldRegister.world_types[world_name]
|
||||
if world.hidden or world.web.options_page is False:
|
||||
return redirect("games")
|
||||
visibility_flag = Options.Visibility.complex_ui if is_complex else Options.Visibility.simple_ui
|
||||
|
||||
start_collapsed = {"Game Options": False}
|
||||
for group in world.web.option_groups:
|
||||
start_collapsed[group.name] = group.start_collapsed
|
||||
option_groups = {option: option_group.name
|
||||
for option_group in world.web.option_groups
|
||||
for option in option_group.options}
|
||||
ordered_groups = ["Game Options", *[group.name for group in world.web.option_groups]]
|
||||
grouped_options = {group: {} for group in ordered_groups}
|
||||
for option_name, option in world.options_dataclass.type_hints.items():
|
||||
# Exclude settings from options pages if their visibility is disabled
|
||||
if visibility_flag in option.visibility:
|
||||
grouped_options[option_groups.get(option, "Game Options")][option_name] = option
|
||||
|
||||
return render_template(
|
||||
template,
|
||||
world_name=world_name,
|
||||
world=world,
|
||||
option_groups=Options.get_option_groups(world, visibility_level=visibility_flag),
|
||||
start_collapsed=start_collapsed,
|
||||
option_groups=grouped_options,
|
||||
issubclass=issubclass,
|
||||
Options=Options,
|
||||
theme=get_world_theme(world_name),
|
||||
@@ -51,7 +55,7 @@ def render_options_page(template: str, world_name: str, is_complex: bool = False
|
||||
|
||||
def generate_game(options: Dict[str, Union[dict, str]]) -> Union[Response, str]:
|
||||
from .generate import start_generation
|
||||
return start_generation(options, get_meta({}))
|
||||
return start_generation(options, {"plando_options": ["items", "connections", "texts", "bosses"]})
|
||||
|
||||
|
||||
def send_yaml(player_name: str, formatted_options: dict) -> Response:
|
||||
@@ -169,9 +173,9 @@ def generate_yaml(game: str):
|
||||
else:
|
||||
options[key] = val
|
||||
|
||||
# Detect and build ItemDict options from their name pattern
|
||||
for key, val in options.copy().items():
|
||||
key_parts = key.rsplit("||", 2)
|
||||
# Detect and build ItemDict options from their name pattern
|
||||
if key_parts[-1] == "qty":
|
||||
if key_parts[0] not in options:
|
||||
options[key_parts[0]] = {}
|
||||
@@ -179,13 +183,6 @@ def generate_yaml(game: str):
|
||||
options[key_parts[0]][key_parts[1]] = int(val)
|
||||
del options[key]
|
||||
|
||||
# Detect keys which end with -custom, indicating a TextChoice with a possible custom value
|
||||
elif key_parts[-1].endswith("-custom"):
|
||||
if val:
|
||||
options[key_parts[-1][:-7]] = val
|
||||
|
||||
del options[key]
|
||||
|
||||
# Detect random-* keys and set their options accordingly
|
||||
for key, val in options.copy().items():
|
||||
if key.startswith("random-"):
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<div id="option-groups">
|
||||
{% for group_name, group_options in option_groups.items() %}
|
||||
<details class="group-container" {% if not start_collapsed[group_name] %}open{% endif %}>
|
||||
<details class="group-container" {% if loop.index == 1 %}open{% endif %}>
|
||||
<summary class="h2">{{ group_name }}</summary>
|
||||
<div class="game-options">
|
||||
<div class="left">
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
<div id="{{ world_name }}-container">
|
||||
{% for group_name, group_options in option_groups.items() %}
|
||||
<details {% if not start_collapsed[group_name] %}open{% endif %}>
|
||||
<details {% if loop.index == 1 %}open{% endif %}>
|
||||
<summary class="h2">{{ group_name }}</summary>
|
||||
{% for option_name, option in group_options.items() %}
|
||||
<div class="option-wrapper">
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#
|
||||
# All usernames must be GitHub usernames (and are case sensitive).
|
||||
|
||||
###################
|
||||
## Active Worlds ##
|
||||
###################
|
||||
|
||||
# Adventure
|
||||
/worlds/adventure/ @JusticePS
|
||||
|
||||
@@ -63,6 +67,9 @@
|
||||
# Factorio
|
||||
/worlds/factorio/ @Berserker66
|
||||
|
||||
# Final Fantasy
|
||||
/worlds/ff1/ @jtoyoda
|
||||
|
||||
# Final Fantasy Mystic Quest
|
||||
/worlds/ffmq/ @Alchav @wildham0
|
||||
|
||||
@@ -208,22 +215,9 @@
|
||||
# Zork Grand Inquisitor
|
||||
/worlds/zork_grand_inquisitor/ @nbrochu
|
||||
|
||||
|
||||
## Active Unmaintained Worlds
|
||||
|
||||
# The following worlds in this repo are currently unmaintained, but currently still work in core. If any update breaks
|
||||
# compatibility, these worlds may be moved to `worlds_disabled`. If you are interested in stepping up as maintainer for
|
||||
# any of these worlds, please review `/docs/world maintainer.md` documentation.
|
||||
|
||||
# Final Fantasy (1)
|
||||
# /worlds/ff1/
|
||||
|
||||
|
||||
## Disabled Unmaintained Worlds
|
||||
|
||||
# The following worlds in this repo are currently unmaintained and disabled as they do not work in core. If you are
|
||||
# interested in stepping up as maintainer for any of these worlds, please review `/docs/world maintainer.md`
|
||||
# documentation.
|
||||
##################################
|
||||
## Disabled Unmaintained Worlds ##
|
||||
##################################
|
||||
|
||||
# Ori and the Blind Forest
|
||||
# /worlds_disabled/oribf/
|
||||
# /worlds_disabled/oribf/ <Unmaintained>
|
||||
|
||||
@@ -1,49 +1,43 @@
|
||||
# Contributing
|
||||
|
||||
All contributions are welcome, though we have a few requests of contributors, whether they be for core, webhost, or new
|
||||
game contributions:
|
||||
Contributions are welcome. We have a few requests for new contributors:
|
||||
|
||||
* **Follow styling guidelines.**
|
||||
Please take a look at the [code style documentation](/docs/style.md)
|
||||
to ensure ease of communication and uniformity.
|
||||
|
||||
* **Ensure that critical changes are covered by tests.**
|
||||
It is strongly recommended that unit tests are used to avoid regression and to ensure everything is still working.
|
||||
If you wish to contribute by adding a new game, please take a look at
|
||||
the [logic unit test documentation](/docs/tests.md).
|
||||
If you wish to contribute to the website, please take a look at [these tests](/test/webhost).
|
||||
* **Ensure that critical changes are covered by tests.**
|
||||
It is strongly recommended that unit tests are used to avoid regression and to ensure everything is still working.
|
||||
If you wish to contribute by adding a new game, please take a look at the [logic unit test documentation](/docs/tests.md).
|
||||
If you wish to contribute to the website, please take a look at [these tests](/test/webhost).
|
||||
|
||||
* **Do not introduce unit test failures/regressions.**
|
||||
Archipelago supports multiple versions of Python. You may need to download older Python versions to fully test
|
||||
your changes. Currently, the oldest supported version
|
||||
is [Python 3.8](https://www.python.org/downloads/release/python-380/).
|
||||
It is recommended that automated github actions are turned on in your fork to have github run unit tests after
|
||||
pushing.
|
||||
You can turn them on here:
|
||||

|
||||
Archipelago supports multiple versions of Python. You may need to download older Python versions to fully test
|
||||
your changes. Currently, the oldest supported version is [Python 3.8](https://www.python.org/downloads/release/python-380/).
|
||||
It is recommended that automated github actions are turned on in your fork to have github run all of the unit tests after pushing.
|
||||
You can turn them on here:
|
||||

|
||||
|
||||
* **When reviewing PRs, please leave a message about what was done.**
|
||||
We don't have full test coverage, so manual testing can help.
|
||||
For code changes that could affect multiple worlds or that could have changes in unexpected code paths, manual testing
|
||||
or checking if all code paths are covered by automated tests is desired. The original author may not have been able
|
||||
to test all possibly affected worlds, or didn't know it would affect another world. In such cases, it is helpful to
|
||||
state which games or settings were rolled, if any.
|
||||
Please also tell us if you looked at code, just did functional testing, did both, or did neither.
|
||||
If testing the PR depends on other PRs, please state what you merged into what for testing.
|
||||
We cannot determine what "LGTM" means without additional context, so that should not be the norm.
|
||||
We don't have full test coverage, so manual testing can help.
|
||||
For code changes that could affect multiple worlds or that could have changes in unexpected code paths, manual testing
|
||||
or checking if all code paths are covered by automated tests is desired. The original author may not have been able
|
||||
to test all possibly affected worlds, or didn't know it would affect another world. In such cases, it is helpful to
|
||||
state which games or settings were rolled, if any.
|
||||
Please also tell us if you looked at code, just did functional testing, did both, or did neither.
|
||||
If testing the PR depends on other PRs, please state what you merged into what for testing.
|
||||
We cannot determine what "LGTM" means without additional context, so that should not be the norm.
|
||||
|
||||
Other than these requests, we tend to judge code on a case-by-case basis.
|
||||
Other than these requests, we tend to judge code on a case-by-case basis.
|
||||
|
||||
For contribution to the website, please refer to the [WebHost README](/WebHostLib/README.md).
|
||||
|
||||
If you want to contribute to the core, you will be subject to stricter review on your pull requests. It is recommended
|
||||
that you get in touch with other core maintainers via the [Discord](https://archipelago.gg/discord).
|
||||
|
||||
If you want to add Archipelago support for a new game, please take a look at
|
||||
the [adding games documentation](/docs/adding%20games.md)
|
||||
which details what is required to implement support for a game, and has tips on to get started.
|
||||
If you want to merge a new game into the main Archipelago repo, please make sure to read the responsibilities as a
|
||||
[world maintainer](/docs/world%20maintainer.md).
|
||||
If you want to add Archipelago support for a new game, please take a look at the [adding games documentation](/docs/adding%20games.md), which details what is required
|
||||
to implement support for a game, as well as tips for how to get started.
|
||||
If you want to merge a new game into the main Archipelago repo, please make sure to read the responsibilities as a
|
||||
[world maintainer](/docs/world%20maintainer.md).
|
||||
|
||||
For other questions, feel free to explore the [main documentation folder](/docs), and ask us questions in the
|
||||
#ap-world-dev channel of the [Discord](https://archipelago.gg/discord).
|
||||
For other questions, feel free to explore the [main documentation folder](/docs/) and ask us questions in the #archipelago-dev channel
|
||||
of the [Discord](https://archipelago.gg/discord).
|
||||
|
||||
11
settings.py
11
settings.py
@@ -643,6 +643,17 @@ class GeneratorOptions(Group):
|
||||
PLAYTHROUGH = 2
|
||||
FULL = 3
|
||||
|
||||
class GlitchTriforceRoom(IntEnum):
|
||||
"""
|
||||
Glitch to Triforce room from Ganon
|
||||
When disabled, you have to have a weapon that can hurt ganon (master sword or swordless/easy item functionality
|
||||
+ hammer) and have completed the goal required for killing ganon to be able to access the triforce room.
|
||||
1 -> Enabled.
|
||||
0 -> Disabled (except in no-logic)
|
||||
"""
|
||||
OFF = 0
|
||||
ON = 1
|
||||
|
||||
class PlandoOptions(str):
|
||||
"""
|
||||
List of options that can be plando'd. Can be combined, for example "bosses, items"
|
||||
|
||||
2
setup.py
2
setup.py
@@ -21,7 +21,7 @@ from pathlib import Path
|
||||
|
||||
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
||||
try:
|
||||
requirement = 'cx-Freeze==7.0.0'
|
||||
requirement = 'cx-Freeze>=7.0.0'
|
||||
import pkg_resources
|
||||
try:
|
||||
pkg_resources.require(requirement)
|
||||
|
||||
@@ -10,7 +10,10 @@ from dataclasses import make_dataclass
|
||||
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple,
|
||||
TYPE_CHECKING, Type, Union)
|
||||
|
||||
from Options import item_and_loc_options, OptionGroup, PerGameCommonOptions
|
||||
from Options import (
|
||||
ExcludeLocations, ItemLinks, LocalItems, NonLocalItems, OptionGroup, PerGameCommonOptions,
|
||||
PriorityLocations, StartHints, StartInventory, StartInventoryPool, StartLocationHints
|
||||
)
|
||||
from BaseClasses import CollectionState
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -116,19 +119,13 @@ class WebWorldRegister(type):
|
||||
# don't allow an option to appear in multiple groups, allow "Item & Location Options" to appear anywhere by the
|
||||
# dev, putting it at the end if they don't define options in it
|
||||
option_groups: List[OptionGroup] = dct.get("option_groups", [])
|
||||
prebuilt_options = ["Game Options", "Item & Location Options"]
|
||||
item_and_loc_options = [LocalItems, NonLocalItems, StartInventory, StartInventoryPool, StartHints,
|
||||
StartLocationHints, ExcludeLocations, PriorityLocations, ItemLinks]
|
||||
seen_options = []
|
||||
item_group_in_list = False
|
||||
for group in option_groups:
|
||||
assert group.options, "A custom defined Option Group must contain at least one Option."
|
||||
# catch incorrectly titled versions of the prebuilt groups so they don't create extra groups
|
||||
title_name = group.name.title()
|
||||
if title_name in prebuilt_options:
|
||||
group.name = title_name
|
||||
|
||||
assert group.name != "Game Options", "Game Options is a pre-determined group and can not be defined."
|
||||
if group.name == "Item & Location Options":
|
||||
assert not any(option in item_and_loc_options for option in group.options), \
|
||||
f"Item and Location Options cannot be specified multiple times"
|
||||
group.options.extend(item_and_loc_options)
|
||||
item_group_in_list = True
|
||||
else:
|
||||
@@ -140,7 +137,7 @@ class WebWorldRegister(type):
|
||||
assert option not in seen_options, f"{option} found in two option groups"
|
||||
seen_options.append(option)
|
||||
if not item_group_in_list:
|
||||
option_groups.append(OptionGroup("Item & Location Options", item_and_loc_options, True))
|
||||
option_groups.append(OptionGroup("Item & Location Options", item_and_loc_options))
|
||||
return super().__new__(mcs, name, bases, dct)
|
||||
|
||||
|
||||
|
||||
@@ -177,8 +177,7 @@ async def _game_watcher(ctx: BizHawkClientContext):
|
||||
|
||||
if ctx.client_handler is None:
|
||||
if not showed_no_handler_message:
|
||||
logger.info("No handler was found for this game. Double-check that the apworld is installed "
|
||||
"correctly and that you loaded the right ROM file.")
|
||||
logger.info("No handler was found for this game")
|
||||
showed_no_handler_message = True
|
||||
continue
|
||||
else:
|
||||
|
||||
@@ -5,17 +5,17 @@ from .Regions import Stages
|
||||
|
||||
|
||||
def graffitiM(state: CollectionState, player: int, limit: bool, spots: int) -> bool:
|
||||
return state.count_group_unique("graffitim", player) * 7 >= spots if limit \
|
||||
return state.count_group_exclusive("graffitim", player) * 7 >= spots if limit \
|
||||
else state.has_group("graffitim", player)
|
||||
|
||||
|
||||
def graffitiL(state: CollectionState, player: int, limit: bool, spots: int) -> bool:
|
||||
return state.count_group_unique("graffitil", player) * 6 >= spots if limit \
|
||||
return state.count_group_exclusive("graffitil", player) * 6 >= spots if limit \
|
||||
else state.has_group("graffitil", player)
|
||||
|
||||
|
||||
def graffitiXL(state: CollectionState, player: int, limit: bool, spots: int) -> bool:
|
||||
return state.count_group_unique("graffitixl", player) * 4 >= spots if limit \
|
||||
return state.count_group_exclusive("graffitixl", player) * 4 >= spots if limit \
|
||||
else state.has_group("graffitixl", player)
|
||||
|
||||
|
||||
@@ -469,7 +469,7 @@ def spots_s_glitchless(state: CollectionState, player: int, limit: bool, access_
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = 5 + (state.count_group_unique("characters", player) * 5)
|
||||
sprayable: int = 5 + (state.count_group_exclusive("characters", player) * 5)
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -492,7 +492,7 @@ def spots_s_glitched(state: CollectionState, player: int, limit: bool, access_ca
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = 5 + (state.count_group_unique("characters", player) * 5)
|
||||
sprayable: int = 5 + (state.count_group_exclusive("characters", player) * 5)
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -537,7 +537,7 @@ def spots_m_glitchless(state: CollectionState, player: int, limit: bool, access_
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitim", player) * 7
|
||||
sprayable: int = state.count_group_exclusive("graffitim", player) * 7
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -563,7 +563,7 @@ def spots_m_glitched(state: CollectionState, player: int, limit: bool, access_ca
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitim", player) * 7
|
||||
sprayable: int = state.count_group_exclusive("graffitim", player) * 7
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -614,7 +614,7 @@ def spots_l_glitchless(state: CollectionState, player: int, limit: bool, access_
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitil", player) * 6
|
||||
sprayable: int = state.count_group_exclusive("graffitil", player) * 6
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -641,7 +641,7 @@ def spots_l_glitched(state: CollectionState, player: int, limit: bool, access_ca
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitil", player) * 6
|
||||
sprayable: int = state.count_group_exclusive("graffitil", player) * 6
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -685,7 +685,7 @@ def spots_xl_glitchless(state: CollectionState, player: int, limit: bool, access
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitixl", player) * 4
|
||||
sprayable: int = state.count_group_exclusive("graffitixl", player) * 4
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
@@ -712,7 +712,7 @@ def spots_xl_glitched(state: CollectionState, player: int, limit: bool, access_c
|
||||
break
|
||||
|
||||
if limit:
|
||||
sprayable: int = state.count_group_unique("graffitixl", player) * 4
|
||||
sprayable: int = state.count_group_exclusive("graffitixl", player) * 4
|
||||
if total <= sprayable:
|
||||
return total
|
||||
else:
|
||||
|
||||
@@ -79,7 +79,7 @@ are `description`, `name`, `game`, `requires`, and the name of the games you wan
|
||||
different weights.
|
||||
|
||||
* `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,
|
||||
is good for detailing the version of Archipelago this YAML was prepared for as, 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.
|
||||
|
||||
@@ -137,7 +137,7 @@ guide: [Archipelago Plando Guide](/tutorial/Archipelago/plando/en)
|
||||
locations.
|
||||
* `item_links` allows players to link their items into a group with the same item link name and game. The items declared
|
||||
in `item_pool` get combined and when an item is found for the group, all players in the group receive it. Item links
|
||||
can also have local and non-local items, forcing the items to either be placed within the worlds of the group or in
|
||||
can also have local and non local items, forcing the items to either be placed within the worlds of the group or in
|
||||
worlds outside the group. If players have a varying amount of a specific item in the link, the lowest amount from the
|
||||
players will be the amount put into the group.
|
||||
|
||||
@@ -277,7 +277,7 @@ one file, removing the need to manage separate files if one chooses to do so.
|
||||
|
||||
As a precautionary measure, before submitting a multi-game yaml like this one in a synchronous/sync multiworld, please
|
||||
confirm that the other players in the multi are OK with what you are submitting, and please be fairly reasonable about
|
||||
the submission. (i.e. Multiple long games (SMZ3, OoT, HK, etc.) for a game intended to be <2 hrs is not likely considered
|
||||
the submission. (ie. Multiple long games (SMZ3, OoT, HK, etc.) for a game intended to be <2 hrs is not likely considered
|
||||
reasonable, but submitting a ChecksFinder alongside another game OR submitting multiple Slay the Spire runs is likely
|
||||
OK)
|
||||
|
||||
@@ -295,7 +295,7 @@ requires:
|
||||
version: 0.3.2
|
||||
Super Mario 64:
|
||||
progression_balancing: 50
|
||||
accessibility: items
|
||||
accessibilty: items
|
||||
EnableCoinStars: false
|
||||
StrictCapRequirements: true
|
||||
StrictCannonRequirements: true
|
||||
@@ -315,7 +315,7 @@ name: Minecraft
|
||||
game: Minecraft
|
||||
Minecraft:
|
||||
progression_balancing: 50
|
||||
accessibility: items
|
||||
accessibilty: items
|
||||
advancement_goal: 40
|
||||
combat_difficulty: hard
|
||||
include_hard_advancements: false
|
||||
@@ -341,7 +341,7 @@ game: ChecksFinder
|
||||
|
||||
ChecksFinder:
|
||||
progression_balancing: 50
|
||||
accessibility: items
|
||||
accessibilty: items
|
||||
```
|
||||
|
||||
The above example will generate 3 worlds - one Super Mario 64, one Minecraft, and one ChecksFinder.
|
||||
|
||||
@@ -129,7 +129,7 @@ List, set, and dict options can additionally have values added to or removed fro
|
||||
option value by prefixing the option name in the trigger block with `+` (add) or `-` (remove). The exact behavior for
|
||||
each will depend on the option type.
|
||||
|
||||
- For sets, `+` will add the value(s) to the set and `-` will remove the value(s) from the set. Sets do not allow
|
||||
- For sets, `+` will add the value(s) to the set and `-` will remove any value(s) of the set. Sets do not allow
|
||||
duplicates.
|
||||
- For lists, `+` will add new values(s) to the list and `-` will remove the first matching values(s) it comes across.
|
||||
Lists allow duplicate values.
|
||||
@@ -160,3 +160,6 @@ Super Metroid:
|
||||
In this example, if the `start_location` option rolls `landing_site`, only a starting hint for Morph Ball will be
|
||||
created. If `aqueduct` is rolled, a starting hint for Gravity Suit will also be created alongside the hint for Morph
|
||||
Ball.
|
||||
|
||||
Note that for lists, items can only be added, not removed or replaced. For dicts, defining a value for a present key
|
||||
will replace that value within the dict.
|
||||
|
||||
@@ -9,7 +9,7 @@ def set_godhome_rules(hk_world, hk_set_rule):
|
||||
fn = partial(hk_set_rule, hk_world)
|
||||
|
||||
required_events = {
|
||||
"Godhome_Flower_Quest": lambda state: state.count('Defeated_Pantheon_5', player) and state.count('Room_Mansion[left1]', player) and state.count('Fungus3_49[right1]', player) and state.has('Godtuner', player),
|
||||
"Godhome_Flower_Quest": lambda state: state.count('Defeated_Pantheon_5', player) and state.count('Room_Mansion[left1]', player) and state.count('Fungus3_49[right1]', player),
|
||||
|
||||
"Defeated_Pantheon_5": lambda state: state.has('GG_Atrium_Roof', player) and state.has('WINGS', player) and (state.has('LEFTCLAW', player) or state.has('RIGHTCLAW', player)) and ((state.has('Defeated_Pantheon_1', player) and state.has('Defeated_Pantheon_2', player) and state.has('Defeated_Pantheon_3', player) and state.has('Defeated_Pantheon_4', player) and state.has('COMBAT[Radiance]', player))),
|
||||
"GG_Atrium_Roof": lambda state: state.has('GG_Atrium', player) and state.has('Hit_Pantheon_5_Unlock_Orb', player) and state.has('LEFTCLAW', player),
|
||||
|
||||
@@ -105,7 +105,7 @@ default_on = {
|
||||
"RandomizeVesselFragments",
|
||||
"RandomizeCharmNotches",
|
||||
"RandomizePaleOre",
|
||||
"RandomizeRancidEggs",
|
||||
"RandomizeRancidEggs"
|
||||
"RandomizeRelics",
|
||||
"RandomizeStags",
|
||||
"RandomizeLifebloodCocoons"
|
||||
|
||||
@@ -659,8 +659,6 @@ class HKItem(Item):
|
||||
def __init__(self, name, advancement, code, type: str, player: int = None):
|
||||
if name == "Mimic_Grub":
|
||||
classification = ItemClassification.trap
|
||||
elif name == "Godtuner":
|
||||
classification = ItemClassification.progression
|
||||
elif type in ("Grub", "DreamWarrior", "Root", "Egg", "Dreamer"):
|
||||
classification = ItemClassification.progression_skip_balancing
|
||||
elif type == "Charm" and name not in progression_charms:
|
||||
|
||||
@@ -519,7 +519,7 @@ Hey Vincent.|43-49|MD Plus Project|True|6|8|10|
|
||||
Meteor feat. TEA|43-50|MD Plus Project|True|3|6|9|
|
||||
Narcissism Angel|43-51|MD Plus Project|True|1|3|6|
|
||||
AlterLuna|43-52|MD Plus Project|True|6|8|11|12
|
||||
Niki Tousen|43-53|MD Plus Project|True|6|8|10|12
|
||||
Niki Tousen|43-53|MD Plus Project|True|6|8|10|11
|
||||
Rettou Joutou|70-0|Rin Len's Mirrorland|False|4|7|9|
|
||||
Telecaster B-Boy|70-1|Rin Len's Mirrorland|False|5|7|10|
|
||||
Iya Iya Iya|70-2|Rin Len's Mirrorland|False|2|4|7|
|
||||
@@ -551,9 +551,3 @@ Dance Dance Good Night Dance|73-2|Happy Otaku Pack Vol.19|True|2|4|7|
|
||||
Ops Limone|73-3|Happy Otaku Pack Vol.19|True|5|8|11|
|
||||
NOVA|73-4|Happy Otaku Pack Vol.19|True|6|8|10|
|
||||
Heaven's Gradius|73-5|Happy Otaku Pack Vol.19|True|6|8|10|
|
||||
Ray Tuning|74-0|CHUNITHM COURSE MUSE|True|6|8|10|
|
||||
World Vanquisher|74-1|CHUNITHM COURSE MUSE|True|6|8|10|11
|
||||
Territory Battles|74-2|CHUNITHM COURSE MUSE|True|5|7|9|
|
||||
The wheel to the right|74-3|CHUNITHM COURSE MUSE|True|5|7|9|11
|
||||
Climax|74-4|CHUNITHM COURSE MUSE|True|4|8|11|11
|
||||
Spider's Thread|74-5|CHUNITHM COURSE MUSE|True|5|8|10|12
|
||||
|
||||
@@ -38,7 +38,7 @@ class AdditionalSongs(Range):
|
||||
- The final song count may be lower due to other settings.
|
||||
"""
|
||||
range_start = 15
|
||||
range_end = 534 # Note will probably not reach this high if any other settings are done.
|
||||
range_end = 528 # Note will probably not reach this high if any other settings are done.
|
||||
default = 40
|
||||
display_name = "Additional Song Count"
|
||||
|
||||
|
||||
@@ -636,7 +636,7 @@ location_data = [
|
||||
LocationData("Rock Tunnel B1F-W", "PokeManiac 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_2_ITEM"], EventFlag(11), inclusion=trainersanity),
|
||||
LocationData("Route 10-N", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_3_ITEM"], EventFlag(308), inclusion=trainersanity),
|
||||
LocationData("Route 10-C", "PokeManiac 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_0_ITEM"], EventFlag(311), inclusion=trainersanity),
|
||||
LocationData("Route 10-S", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_5_ITEM"], EventFlag(306), inclusion=trainersanity),
|
||||
LocationData("Route 10-S", "J.r Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_5_ITEM"], EventFlag(306), inclusion=trainersanity),
|
||||
LocationData("Route 10-S", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_1_ITEM"], EventFlag(310), inclusion=trainersanity),
|
||||
LocationData("Route 10-S", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_4_ITEM"], EventFlag(307), inclusion=trainersanity),
|
||||
LocationData("Route 10-S", "PokeManiac 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_2_ITEM"], EventFlag(309), inclusion=trainersanity),
|
||||
|
||||
@@ -966,8 +966,8 @@ def kerrigan_primal(ctx: SC2Context, kerrigan_level: int) -> bool:
|
||||
return kerrigan_level >= 35
|
||||
elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_half_completion:
|
||||
total_missions = len(ctx.mission_id_to_location_ids)
|
||||
completed = sum((mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations
|
||||
for mission_id in ctx.mission_id_to_location_ids)
|
||||
completed = len([(mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations
|
||||
for mission_id in ctx.mission_id_to_location_ids])
|
||||
return completed >= (total_missions / 2)
|
||||
elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_item:
|
||||
codes = [item.item for item in ctx.items_received]
|
||||
|
||||
@@ -22,7 +22,7 @@ from .MissionTables import MissionInfo, SC2Campaign, lookup_name_to_mission, SC2
|
||||
|
||||
|
||||
class Starcraft2WebWorld(WebWorld):
|
||||
setup_en = Tutorial(
|
||||
setup = Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Starcraft 2 randomizer connected to an Archipelago Multiworld",
|
||||
"English",
|
||||
@@ -31,16 +31,7 @@ class Starcraft2WebWorld(WebWorld):
|
||||
["TheCondor", "Phaneros"]
|
||||
)
|
||||
|
||||
setup_fr = Tutorial(
|
||||
setup_en.tutorial_name,
|
||||
setup_en.description,
|
||||
"Français",
|
||||
"setup_fr.md",
|
||||
"setup/fr",
|
||||
["Neocerber"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_fr]
|
||||
tutorials = [setup]
|
||||
|
||||
|
||||
class SC2World(World):
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Starcraft 2
|
||||
|
||||
## Game page in other languages:
|
||||
* [Français](/games/Starcraft%202/info/fr)
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
The following unlocks are randomized as items:
|
||||
@@ -42,7 +39,7 @@ The goal is to beat the final mission in the mission order. The yaml configurati
|
||||
## Which of my items can be in another player's world?
|
||||
|
||||
By default, any of StarCraft 2's items (specified above) can be in another player's world. See the
|
||||
[Advanced YAML Guide](/tutorial/Archipelago/advanced_settings/en)
|
||||
[Advanced YAML Guide](https://archipelago.gg/tutorial/Archipelago/advanced_settings/en)
|
||||
for more information on how to change this.
|
||||
|
||||
## Unique Local Commands
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
# *StarCraft 2*
|
||||
|
||||
## Quel est l'effet de la *randomization* sur ce jeu ?
|
||||
|
||||
Les éléments qui suivent sont les *items* qui sont *randomized* et qui doivent être débloqués pour être utilisés dans
|
||||
le jeu:
|
||||
1. La capacité de produire des unités, excepté les drones/probes/scv.
|
||||
2. Des améliorations spécifiques à certaines unités incluant quelques combinaisons qui ne sont pas disponibles dans les
|
||||
campagnes génériques, comme le fait d'avoir les deux types d'évolution en même temps pour une unité *Zerg* et toutes
|
||||
les améliorations de la *Spear of Adun* simultanément pour les *Protoss*.
|
||||
3. L'accès aux améliorations génériques des unités, e.g. les améliorations d'attaque et d'armure.
|
||||
4. D'autres améliorations diverses telles que les améliorations de laboratoire et les mercenaires pour les *Terran*,
|
||||
les niveaux et les améliorations de Kerrigan pour les *Zerg*, et les améliorations de la *Spear of Adun* pour les
|
||||
*Protoss*.
|
||||
5. Avoir des *minerals*, du *vespene gas*, et du *supply* au début de chaque mission.
|
||||
|
||||
Les *items* sont trouvés en accomplissant du progrès dans les catégories suivantes:
|
||||
* Terminer des missions
|
||||
* Réussir des objectifs supplémentaires (e.g., récolter le matériel pour les recherches dans *Wings of Liberty*)
|
||||
* Atteindre des étapes importantes dans la mission, e.g. réussir des sous-objectifs
|
||||
* Réussir des défis basés sur les succès du jeu de base, e.g. éliminer tous les *Zerg* dans la mission
|
||||
*Devil's Playground*
|
||||
|
||||
Ces catégories, outre la première, peuvent être désactivées dans les options du jeu.
|
||||
Par exemple, vous pouvez désactiver le fait d'obtenir des *items* lorsque des étapes importantes d'une mission sont
|
||||
accomplies.
|
||||
|
||||
Quand vous recevez un *item*, il devient immédiatement disponible, même pendant une mission, et vous serez avertis via
|
||||
la boîte de texte situé dans le coin en haut à droite de *StarCraft 2*.
|
||||
L'acquisition d'un *item* est aussi indiquée dans le client d'Archipelago.
|
||||
|
||||
Les missions peuvent être lancées par le client *StarCraft 2 Archipelago*, via l'interface graphique de l'onglet
|
||||
*StarCraft 2 Launcher*.
|
||||
Les segments qui se passent sur l'*Hyperion*, un Léviathan et la *Spear of Adun* ne sont pas inclus.
|
||||
De plus, les points de progression tels que les crédits ou la Solarite ne sont pas utilisés dans *StarCraft 2
|
||||
Archipelago*.
|
||||
|
||||
## Quel est le but de ce jeu quand il est *randomized*?
|
||||
|
||||
Le but est de réussir la mission finale dans la disposition des missions (e.g. *blitz*, *grid*, etc.).
|
||||
Les choix faits dans le fichier *yaml* définissent la disposition des missions et comment elles sont mélangées.
|
||||
|
||||
## Quelles sont les modifications non aléatoires comparativement à la version de base de *StarCraft 2*
|
||||
|
||||
1. Certaines des missions ont plus de *vespene geysers* pour permettre l'utilisation d'une plus grande variété d'unités.
|
||||
2. Plusieurs unités et améliorations ont été ajoutées sous la forme d*items*.
|
||||
Ils proviennent de la version *co-op*, *melee*, des autres campagnes, d'expansions ultérieures, de *Brood War*, ou de
|
||||
l'imagination des développeurs de *StarCraft 2 Archipelago*.
|
||||
3. Les structures de production, e.g. *Factory*, *Starport*, *Robotics Facility*, and *Stargate*, n'ont plus
|
||||
d'exigences technologiques.
|
||||
4. Les missions avec la race *Zerg* ont été modifiées pour que les joueurs débuttent avec un *Lair* lorsqu'elles
|
||||
commençaient avec une *Hatchery*.
|
||||
5. Les désavantages des améliorations ont été enlevés, e.g. *automated refinery* qui coûte plus cher ou les *tech
|
||||
reactors* qui prennent plus de temps à construire.
|
||||
6. La collision des unités dans les couloirs de la mission *Enemy Within* a été ajustée pour permettre des unités
|
||||
plus larges de les traverser sans être coincés dans des endroits étranges.
|
||||
7. Plusieurs *bugs* du jeu original ont été corrigés.
|
||||
|
||||
## Quels sont les *items* qui peuvent être dans le monde d'un autre joueur?
|
||||
|
||||
Par défaut, tous les *items* de *StarCraft 2 Archipelago* (voir la section précédente) peuvent être dans le monde d'un
|
||||
autre joueur.
|
||||
Consulter [*Advanced YAML Guide*](/tutorial/Archipelago/advanced_settings/en) pour savoir comment
|
||||
changer ça.
|
||||
|
||||
## Commandes du client qui sont uniques à ce jeu
|
||||
|
||||
Les commandes qui suivent sont seulement disponibles uniquement pour le client de *StarCraft 2 Archipelago*.
|
||||
Vous pouvez les afficher en utilisant la commande `/help` dans le client de *StarCraft 2 Archipelago*.
|
||||
Toutes ces commandes affectent seulement le client où elles sont utilisées.
|
||||
|
||||
* `/download_data` Télécharge les versions les plus récentes des fichiers pour jouer à *StarCraft 2 Archipelago*.
|
||||
Les fichiers existants vont être écrasés.
|
||||
* `/difficulty [difficulty]` Remplace la difficulté choisie pour le monde.
|
||||
* Les options sont *casual*, *normal*, *hard*, et *brutal*.
|
||||
* `/game_speed [game_speed]` Remplace la vitesse du jeu pour le monde.
|
||||
* Les options sont *default*, *slower*, *slow*, *normal*, *fast*, and *faster*.
|
||||
* `/color [faction] [color]` Remplace la couleur d'une des *factions* qui est jouable.
|
||||
* Les options de *faction*: raynor, kerrigan, primal, protoss, nova.
|
||||
* Les options de couleur: *white*, *red*, *blue*, *teal*, *purple*, *yellow*, *orange*, *green*, *lightpink*,
|
||||
*violet*, *lightgrey*, *darkgreen*, *brown*, *lightgreen*, *darkgrey*, *pink*, *rainbow*, *random*, *default*.
|
||||
* `/option [option_name] [option_value]` Permet de changer un option normalement définit dans le *yaml*.
|
||||
* Si la commande est lancée sans option, la liste des options qui sont modifiables va être affichée.
|
||||
* Les options qui peuvent être changées avec cette commande incluent sauter les cinématiques automatiquement, la
|
||||
présence de Kerrigan dans les missions, la disponibilité de la *Spear of Adun*, la quantité de ressources
|
||||
supplémentaires données au début des missions, la capacité de contrôler les alliées IA, etc.
|
||||
* `/disable_mission_check` Désactive les requit pour lancer les missions.
|
||||
Cette option a pour but de permettre de jouer en mode coopératif en permettant à un joueur de jouer à la prochaine
|
||||
mission de la chaîne qu'un autre joueur est en train d'entamer.
|
||||
* `/play [mission_id]` Lance la mission correspondant à l'identifiant donné.
|
||||
* `/available` Affiche les missions qui sont présentement accessibles.
|
||||
* `/unfinished` Affiche les missions qui sont présentement accessibles et dont certains des objectifs permettant
|
||||
l'accès à un *item* n'ont pas été accomplis.
|
||||
* `/set_path [path]` Permet de définir manuellement où *StarCraft 2* est installé ce qui est pertinent seulement si la
|
||||
détection automatique de cette dernière échoue.
|
||||
@@ -23,20 +23,18 @@ Yaml files are configuration files that tell Archipelago how you'd like your gam
|
||||
When you're setting up a multiworld, every world needs its own yaml file.
|
||||
|
||||
There are three basic ways to get a yaml:
|
||||
* You can go to the [Player Options](/games/Starcraft%202/player-options) page, set your options in the GUI, and export the yaml.
|
||||
* You can generate a template, either by downloading it from the [Player Options](/games/Starcraft%202/player-options) page or by generating it from the Launcher (ArchipelagoLauncher.exe). The template includes descriptions of each option, you just have to edit it in your text editor of choice.
|
||||
* You can go to the [Player Options](https://archipelago.gg/games/Starcraft%202/player-options) page, set your options in the GUI, and export the yaml.
|
||||
* You can generate a template, either by downloading it from the [Player Options](https://archipelago.gg/games/Starcraft%202/player-options) page or by generating it from the Launcher (ArchipelagoLauncher.exe). The template includes descriptions of each option, you just have to edit it in your text editor of choice.
|
||||
* You can ask someone else to share their yaml to use it for yourself or adjust it as you wish.
|
||||
|
||||
Remember the name you enter in the options page or in the yaml file, you'll need it to connect later!
|
||||
|
||||
Check out [Creating a YAML](/tutorial/Archipelago/setup/en#creating-a-yaml) for more game-agnostic information.
|
||||
Check out [Creating a YAML](https://archipelago.gg/tutorial/Archipelago/setup/en#creating-a-yaml) for more game-agnostic information.
|
||||
|
||||
### Common yaml questions
|
||||
#### How do I know I set my yaml up correctly?
|
||||
|
||||
The simplest way to check is to use the website [validator](/check).
|
||||
|
||||
You can also test it by attempting to generate a multiworld with your yaml. Save your yaml to the Players/ folder within your Archipelago installation and run ArchipelagoGenerate.exe. You should see a new .zip file within the output/ folder of your Archipelago installation if things worked correctly. It's advisable to run ArchipelagoGenerate through a terminal so that you can see the printout, which will include any errors and the precise output file name if it's successful. If you don't like terminals, you can also check the log file in the logs/ folder.
|
||||
The simplest way to check is to test it out. Save your yaml to the Players/ folder within your Archipelago installation and run ArchipelagoGenerate.exe. You should see a new .zip file within the output/ folder of your Archipelago installation if things worked correctly. It's advisable to run ArchipelagoGenerate through a terminal so that you can see the printout, which will include any errors and the precise output file name if it's successful. If you don't like terminals, you can also check the log file in the logs/ folder.
|
||||
|
||||
#### What does Progression Balancing do?
|
||||
|
||||
@@ -66,15 +64,9 @@ start_inventory:
|
||||
|
||||
An empty mapping is just a matching pair of curly braces: `{}`. That's the default value in the template, which should let you know to use this syntax.
|
||||
|
||||
#### How do I know the exact names of items and locations?
|
||||
#### How do I know the exact names of items?
|
||||
|
||||
The [*datapackage*](/datapackage) page of the Archipelago website provides a complete list of the items and locations for each game that it currently supports, including StarCraft 2.
|
||||
|
||||
You can also look up a complete list of the item names in the [Icon Repository](https://matthewmarinets.github.io/ap_sc2_icons/) page.
|
||||
This page also contains supplementary information of each item.
|
||||
However, the items shown in that page might differ from those shown in the datapackage page of Archipelago since the former is generated, most of the time, from beta versions of StarCraft 2 Archipelago undergoing development.
|
||||
|
||||
As for the locations, you can see all the locations associated to a mission in your world by placing your cursor over the mission in the 'StarCraft 2 Launcher' tab in the client.
|
||||
You can look up a complete list if item names in the [Icon Repository](https://matthewmarinets.github.io/ap_sc2_icons/).
|
||||
|
||||
## How do I join a MultiWorld game?
|
||||
|
||||
@@ -94,7 +86,7 @@ specific description of what's going wrong and attach your log file to your mess
|
||||
|
||||
## Running in macOS
|
||||
|
||||
To run StarCraft 2 through Archipelago in macOS, you will need to run the client via source as seen here: [macOS Guide](/tutorial/Archipelago/mac/en). Note: to launch the client, you will need to run the command `python3 Starcraft2Client.py`.
|
||||
To run StarCraft 2 through Archipelago in macOS, you will need to run the client via source as seen here: [macOS Guide](https://archipelago.gg/tutorial/Archipelago/mac/en). Note: when running the client, you will need to run the command `python3 Starcraft2Client.py`.
|
||||
|
||||
## Running in Linux
|
||||
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
# Guide d'installation du *StarCraft 2 Randomizer*
|
||||
|
||||
Ce guide contient les instructions pour installer et dépanner le client de *StarCraft 2 Archipelago*, ainsi que des
|
||||
indications pour obtenir un fichier de configuration de *StarCraft 2 Archipelago* et comment modifier ce dernier.
|
||||
|
||||
## Logiciels requis
|
||||
|
||||
- [*StarCraft 2*](https://starcraft2.com/en-us/)
|
||||
- [La version la plus récente d'Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
||||
## Comment est-ce que j'installe ce *randomizer*?
|
||||
|
||||
1. Installer *StarCraft 2* et Archipelago en suivant les instructions indiquées dans les liens précédents. Le client de
|
||||
*StarCraft 2 Archipelago* est téléchargé par le programme d'installation d'Archipelago.
|
||||
- Les utilisateurs de Linux devraient aussi suivre les instructions qui se retrouvent à la fin de cette page
|
||||
(["Exécuter sous Linux"](#exécuter-sous-linux)).
|
||||
- Notez que votre jeu *StarCraft 2* doit être en anglais pour fonctionner avec Archipelago.
|
||||
2. Exécuter `ArchipelagoStarcraft2Client.exe`.
|
||||
- Uniquement pour cette étape, les utilisateurs de macOS devraient plutôt suivre les instructions qui se trouvent à
|
||||
["Exécuter sous macOS"](#exécuter-sous-macos).
|
||||
3. Dans le client de *StarCraft 2 Archipelago*, écrire la commande `/download_data`. Cette commande va lancer
|
||||
l'installation des fichiers qui sont nécessaires pour jouer à *StarCraft 2 Archipelago*.
|
||||
|
||||
## Où est-ce que j'obtiens le fichier de configuration (i.e., le *yaml*) pour ce jeu?
|
||||
|
||||
Un fichier dans le format *yaml* est utilisé pour communiquer à Archipelago comment vous voulez que votre jeu soit
|
||||
*randomized*.
|
||||
Ce dernier est nécessaire même si vous voulez utiliser les options par défaut.
|
||||
L'approche usuelle pour générer un *multiworld* consiste à avoir un fichier *yaml* par monde.
|
||||
|
||||
Il y a trois approches pour obtenir un fichier *yaml* pour *StarCraft 2 Randomizer*:
|
||||
* Vous pouvez aller à la page [*Player options*](/games/Starcraft%202/player-options) qui vous permet de définir vos
|
||||
choix via une interface graphique et ensuite télécharger le *yaml* correspondant à ces choix.
|
||||
* Vous pouvez obtenir le modèle de base en le téléchargeant à la page
|
||||
[*Player options*](/games/Starcraft%202/player-options) ou en cliquant sur *Generate template* après avoir exécuté le
|
||||
*Launcher* d'Archipelago (i.e., `ArchipelagoLauncher.exe`). Ce modèle de base inclut une description pour chacune des
|
||||
options et vous n'avez qu'à modifier les options dans un éditeur de texte de votre choix.
|
||||
* Vous pouvez demander à quelqu'un d'autre de partager un de ces fichiers *yaml* pour l'utiliser ou l'ajuster à vos
|
||||
préférences.
|
||||
|
||||
Prenez soin de vous rappeler du nom de joueur que vous avez inscrit dans la page à options ou dans le fichier *yaml*
|
||||
puisque vous en aurez besoin pour vous connecter à votre monde!
|
||||
|
||||
Notez que la page *Player options* ne permet pas de définir certaines des options avancées, e.g., l'exclusion de
|
||||
certaines unités ou de leurs améliorations.
|
||||
Utilisez la page [*Weighted Options*](/weighted-options) pour avoir accès à ces dernières.
|
||||
|
||||
Si vous désirez des informations et/ou instructions générales sur l'utilisation d'un fichier *yaml* pour Archipelago,
|
||||
veuillez consulter [*Creating a YAML*](/tutorial/Archipelago/setup/en#creating-a-yaml).
|
||||
|
||||
### Questions récurrentes à propos du fichier *yaml*
|
||||
#### Comment est-ce que je sais que mon *yaml* est bien défini?
|
||||
|
||||
La manière la plus simple de valider votre *yaml* est d'utiliser le
|
||||
[système de validation](/check) du site web.
|
||||
|
||||
Vous pouvez aussi le tester en tentant de générer un *multiworld* avec votre *yaml*.
|
||||
Pour faire ça, sauvegardez votre *yaml* dans le dossier `Players/` de votre installation d'Archipelago et exécutez
|
||||
`ArchipelagoGenerate.exe`.
|
||||
Si votre *yaml* est bien défini, vous devriez voir un nouveau fichier, avec l'extension `.zip`, apparaître dans le
|
||||
dossier `output/` de votre installation d'Archipelago.
|
||||
Il est recommandé de lancer `ArchipelagoGenerate.exe` via un terminal afin que vous puissiez voir les messages générés
|
||||
par le logiciel, ce qui va inclure toutes erreurs qui ont eu lieu et le nom de fichier généré.
|
||||
Si vous n'appréciez pas le fait d'utiliser un terminal, vous pouvez aussi regarder le fichier *log* qui va être produit
|
||||
dans le dossier `logs/`.
|
||||
|
||||
#### À quoi sert l'option *Progression Balancing*?
|
||||
|
||||
Pour *Starcraft 2*, cette option ne fait pas grand-chose.
|
||||
Il s'agit d'une option d'Archipelago permettant d'équilibrer la progression des mondes en interchangeant les *items*
|
||||
dans les *spheres*.
|
||||
Si le *Progression Balancing* d'un monde est plus grand que ceux des autres, les *items* de progression de ce monde ont
|
||||
plus de chance d'être obtenus tôt et vice-versa si sa valeur est plus petite que celle des autres mondes.
|
||||
Cependant, *Starcraft 2* est beaucoup plus permissif en termes d'*items* qui permettent de progresser, ce réglage à
|
||||
donc peu d'influence sur la progression dans *StarCraft 2*.
|
||||
Vu qu'il augmente le temps de génération d'un *MultiWorld*, nous recommandons de le désactiver, c-à-d le définir à
|
||||
zéro, pour *Starcraft 2*.
|
||||
|
||||
|
||||
#### Comment est-ce que je définis une liste d'*items*, e.g. pour l'option *excluded items*?
|
||||
|
||||
Vous pouvez lire sur la syntaxe des conteneurs dans le format *yaml* à la page
|
||||
[*YAML specification*](https://yaml.org/spec/1.2.2/#21-collections).
|
||||
Pour les listes, chaque *item* doit être sur sa propre ligne et doit être précédé par un trait d'union.
|
||||
|
||||
```yaml
|
||||
excluded_items:
|
||||
- Battlecruiser
|
||||
- Drop-Pods (Kerrigan Tier 7)
|
||||
```
|
||||
|
||||
Une liste vide est représentée par une paire de crochets: `[]`.
|
||||
Il s'agit de la valeur par défaut dans le modèle de base, ce qui devrait vous aider à apprendre à utiliser cette
|
||||
syntaxe.
|
||||
|
||||
#### Comment est-ce que je fais pour avoir des *items* dès le départ?
|
||||
|
||||
L'option *starting inventory* est un *map* et non une liste.
|
||||
Ainsi, elle permet de spécifier le nombre de chaque *item* avec lequel vous allez commencer.
|
||||
Sa syntaxe consiste à indiquer le nom de l'*item*, suivi par un deux-points, puis par un espace et enfin par le nombre
|
||||
désiré de cet *item*.
|
||||
|
||||
```yaml
|
||||
start_inventory:
|
||||
Micro-Filtering: 1
|
||||
Additional Starting Vespene: 5
|
||||
```
|
||||
|
||||
Un *map* vide est représenté par une paire d'accolades: `{}`.
|
||||
Il s'agit de la valeur par défaut dans le modèle de base, ce qui devrait vous aider à apprendre à utiliser cette
|
||||
syntaxe.
|
||||
|
||||
#### Comment est-ce que je fais pour connaître le nom des *items* et des *locations* dans *StarCraft 2 Archipelago*?
|
||||
|
||||
La page [*datapackage*](/datapackage) d'Archipelago liste l'ensemble des *items* et des *locations* de tous les jeux
|
||||
que le site web prend en charge actuellement, dont ceux de *StarCraft 2*.
|
||||
|
||||
Vous trouverez aussi la liste complète des *items* de *StarCraft 2 Archipelago* à la page
|
||||
[*Icon Repository*](https://matthewmarinets.github.io/ap_sc2_icons/).
|
||||
Notez que cette page contient diverses informations supplémentaires sur chacun des *items*.
|
||||
Cependant, l'information présente dans cette dernière peut différer de celle du *datapackage* d'Archipelago
|
||||
puisqu'elle est générée, habituellement, à partir de la version en développement de *StarCraft 2 Archipelago* qui
|
||||
n'ont peut-être pas encore été inclus dans le site web d'Archipelago.
|
||||
|
||||
## Comment est-ce que je peux joindre un *MultiWorld*?
|
||||
|
||||
1. Exécuter `ArchipelagoStarcraft2Client.exe`.
|
||||
- Uniquement pour cette étape, les utilisateurs de macOS devraient plutôt suivre les instructions à la page
|
||||
["Exécuter sous macOS"](#exécuter-sous-macos).
|
||||
2. Entrer la commande `/connect [server ip]`.
|
||||
- Si le *MultiWorld* est hébergé via un siteweb, l'IP du server devrait être indiqué dans le haut de la page de
|
||||
votre *room*.
|
||||
3. Inscrivez le nom de joueur spécifié dans votre *yaml* lorsque vous y êtes invité.
|
||||
4. Si le serveur a un mot de passe, l'inscrire lorsque vous y êtes invité.
|
||||
5. Une fois connecté, aller sur l'onglet *StarCraft 2 Launcher* dans le client. Dans cet onglet, vous devriez trouver
|
||||
toutes les missions de votre monde. Les missions qui ne sont pas disponibles présentement auront leur texte dans une
|
||||
nuance de gris. Vous n'avez qu'à cliquer une des missions qui est disponible pour la commencer!
|
||||
|
||||
## *StarCraft 2* ne démarre pas quand je tente de commencer une mission
|
||||
|
||||
Pour commencer, regarder le fichier *log* pour trouver le problème (ce dernier devrait être dans
|
||||
`[Archipelago Directory]/logs/SC2Client.txt`).
|
||||
Si vous ne comprenez pas le problème avec le fichier *log*, visitez notre
|
||||
[*Discord*](https://discord.com/invite/8Z65BR2) pour demander de l'aide dans le forum *tech-support*.
|
||||
Dans votre message, veuillez inclure une description détaillée de ce qui ne marche pas et ajouter en pièce jointe le
|
||||
fichier *log*.
|
||||
|
||||
## Mon profil de raccourcis clavier n'est pas disponibles quand je joue à *StarCraft 2 Archipelago*
|
||||
|
||||
Pour que votre profil de raccourcis clavier fonctionne dans Archipelago, vous devez copier votre fichier de raccourcis
|
||||
qui se trouve dans `Documents/StarCraft II/Accounts/######/Hotkeys` vers `Documents/StarCraft II/Hotkeys`.
|
||||
Si le dossier n'existe pas, créez-le.
|
||||
|
||||
Pour que *StarCraft 2 Archipelago* utilise votre profil, suivez les étapes suivantes.
|
||||
Lancez *Starcraft 2* via l'application *Battle.net*.
|
||||
Changez votre profil de raccourcis clavier pour le mode standard et acceptez, puis sélectionnez votre profil
|
||||
personnalisé et acceptez.
|
||||
Vous n'aurez besoin de faire ça qu'une seule fois.
|
||||
|
||||
## Exécuter sous macOS
|
||||
|
||||
Pour exécuter *StarCraft 2* via Archipelago sous macOS, vous devez exécuter le client à partir de la source
|
||||
comme indiqué ici: [*macOS Guide*](/tutorial/Archipelago/mac/en).
|
||||
Notez que pour lancer le client, vous devez exécuter la commande `python3 Starcraft2Client.py`.
|
||||
|
||||
## Exécuter sous Linux
|
||||
|
||||
Pour exécuter *StarCraft 2* via Archipelago sous Linux, vous allez devoir installer le jeu avec *Wine* et ensuite
|
||||
exécuter le client d'Archipelago pour Linux.
|
||||
|
||||
Confirmez que vous avez installé *StarCraft 2* via *Wine* et que vous avez suivi les
|
||||
[instructions d'installation](#comment-est-ce-que-j'installe-ce-randomizer?) pour ajouter les *Maps* et les *Data
|
||||
files* nécessairent pour *StarCraft 2 Archipelago* au bon endroit.
|
||||
Vous n'avez pas besoin de copier les fichiers `.dll`.
|
||||
Si vous avez des difficultés pour installer ou exécuter *StarCraft 2* sous Linux, il est recommandé d'utiliser le
|
||||
logiciel *Lutris*.
|
||||
|
||||
Copier ce qui suit dans un fichier avec l'extension `.sh`, en prenant soin de définir les variables **WINE** et
|
||||
**SC2PATH** avec les bons chemins et de définir **PATH_TO_ARCHIPELAGO** avec le chemin vers le dossier qui contient le
|
||||
*AppImage* si ce dernier n'est pas dans le même dossier que ce script.
|
||||
|
||||
```sh
|
||||
# Permet au client de savoir que SC2 est exécuté via Wine
|
||||
export SC2PF=WineLinux
|
||||
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
|
||||
|
||||
# À_CHANGER Remplacer le chemin avec celui qui correspond à la version de Wine utilisé pour exécuter SC2
|
||||
export WINE="/usr/bin/wine"
|
||||
|
||||
# À_CHANGER Remplacer le chemin par celui qui indique où StarCraft II est installé
|
||||
export SC2PATH="/home/user/Games/starcraft-ii/drive_c/Program Files (x86)/StarCraft II/"
|
||||
|
||||
# À_CHANGER Indiquer le dossier qui contient l'AppImage d'Archipelago
|
||||
PATH_TO_ARCHIPELAGO=
|
||||
|
||||
# Obtiens la dernière version de l'AppImage de Archipelago dans le dossier PATH_TO_ARCHIPELAGO.
|
||||
# Si PATH_TO_ARCHIPELAGO n'est pas défini, la valeur par défaut est le dossier qui contient ce script.
|
||||
ARCHIPELAGO="$(ls ${PATH_TO_ARCHIPELAGO:-$(dirname $0)}/Archipelago_*.AppImage | sort -r | head -1)"
|
||||
|
||||
# Lance le client de Archipelago
|
||||
$ARCHIPELAGO Starcraft2Client
|
||||
```
|
||||
|
||||
Pour une installation via Lutris, vous pouvez exécuter `lutris -l` pour obtenir l'identifiant numérique de votre
|
||||
installation *StarCraft II* et ensuite exécuter la commande suivante, en remplacant **${ID}** pour cet identifiant
|
||||
numérique.
|
||||
|
||||
lutris lutris:rungameid/${ID} --output-script sc2.sh
|
||||
|
||||
Cette commande va définir toutes les variables d'environnement nécessaires pour exécuter *StarCraft 2* dans un script,
|
||||
incluant le chemin vers l'exécutable *Wine* que Lutris utilise.
|
||||
Après ça, vous pouvez enlever la ligne qui permet de démarrer *Battle.Net* et copier le code décrit plus haut dans le
|
||||
script produit.
|
||||
|
||||
@@ -735,26 +735,26 @@ id,name,classification,groups,mod_name
|
||||
10007,Tractor Garage,useful,,Tractor Mod
|
||||
10008,Woods Obelisk,progression,,DeepWoods
|
||||
10009,Spell: Clear Debris,progression,MAGIC_SPELL,Magic
|
||||
10010,Spell: Till,progression,MAGIC_SPELL,Magic
|
||||
10010,Spell: Till,useful,MAGIC_SPELL,Magic
|
||||
10011,Spell: Water,progression,MAGIC_SPELL,Magic
|
||||
10012,Spell: Blink,progression,MAGIC_SPELL,Magic
|
||||
10013,Spell: Evac,progression,MAGIC_SPELL,Magic
|
||||
10014,Spell: Haste,progression,MAGIC_SPELL,Magic
|
||||
10013,Spell: Evac,useful,MAGIC_SPELL,Magic
|
||||
10014,Spell: Haste,useful,MAGIC_SPELL,Magic
|
||||
10015,Spell: Heal,progression,MAGIC_SPELL,Magic
|
||||
10016,Spell: Buff,progression,MAGIC_SPELL,Magic
|
||||
10016,Spell: Buff,useful,MAGIC_SPELL,Magic
|
||||
10017,Spell: Shockwave,progression,MAGIC_SPELL,Magic
|
||||
10018,Spell: Fireball,progression,MAGIC_SPELL,Magic
|
||||
10019,Spell: Frostbolt,progression,MAGIC_SPELL,Magic
|
||||
10020,Spell: Teleport,progression,MAGIC_SPELL,Magic
|
||||
10021,Spell: Lantern,progression,MAGIC_SPELL,Magic
|
||||
10021,Spell: Lantern,useful,MAGIC_SPELL,Magic
|
||||
10022,Spell: Tendrils,progression,MAGIC_SPELL,Magic
|
||||
10023,Spell: Photosynthesis,progression,MAGIC_SPELL,Magic
|
||||
10023,Spell: Photosynthesis,useful,MAGIC_SPELL,Magic
|
||||
10024,Spell: Descend,progression,MAGIC_SPELL,Magic
|
||||
10025,Spell: Meteor,progression,MAGIC_SPELL,Magic
|
||||
10026,Spell: Bloodmana,progression,MAGIC_SPELL,Magic
|
||||
10027,Spell: Lucksteal,progression,MAGIC_SPELL,Magic
|
||||
10026,Spell: Bloodmana,useful,MAGIC_SPELL,Magic
|
||||
10027,Spell: Lucksteal,useful,MAGIC_SPELL,Magic
|
||||
10028,Spell: Spirit,progression,MAGIC_SPELL,Magic
|
||||
10029,Spell: Rewind,progression,MAGIC_SPELL,Magic
|
||||
10029,Spell: Rewind,useful,MAGIC_SPELL,Magic
|
||||
10030,Pendant of Community,progression,,DeepWoods
|
||||
10031,Pendant of Elders,progression,,DeepWoods
|
||||
10032,Pendant of Depths,progression,,DeepWoods
|
||||
|
||||
|
@@ -8,7 +8,7 @@ from ...mods.mod_data import ModNames
|
||||
from ...stardew_rule import StardewRule, False_
|
||||
from ...strings.ap_names.skill_level_names import ModSkillLevel
|
||||
from ...strings.region_names import MagicRegion
|
||||
from ...strings.spells import MagicSpell, all_spells
|
||||
from ...strings.spells import MagicSpell
|
||||
|
||||
|
||||
class MagicLogicMixin(BaseLogicMixin):
|
||||
@@ -27,8 +27,7 @@ class MagicLogic(BaseLogic[Union[RegionLogicMixin, ReceivedLogicMixin, HasLogicM
|
||||
def can_use_altar(self) -> StardewRule:
|
||||
if ModNames.magic not in self.options.mods:
|
||||
return False_()
|
||||
spell_rule = False_()
|
||||
return self.logic.region.can_reach(MagicRegion.altar) & self.logic.received_any(*all_spells)
|
||||
return self.logic.region.can_reach(MagicRegion.altar)
|
||||
|
||||
def has_any_spell(self) -> StardewRule:
|
||||
if ModNames.magic not in self.options.mods:
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
all_spells = []
|
||||
|
||||
|
||||
def spell(name: str) -> str:
|
||||
all_spells.append(name)
|
||||
return name
|
||||
|
||||
|
||||
class MagicSpell:
|
||||
clear_debris = spell("Spell: Clear Debris")
|
||||
till = spell("Spell: Till")
|
||||
water = spell("Spell: Water")
|
||||
blink = spell("Spell: Blink")
|
||||
evac = spell("Spell: Evac")
|
||||
haste = spell("Spell: Haste")
|
||||
heal = spell("Spell: Heal")
|
||||
buff = spell("Spell: Buff")
|
||||
shockwave = spell("Spell: Shockwave")
|
||||
fireball = spell("Spell: Fireball")
|
||||
frostbite = spell("Spell: Frostbolt")
|
||||
teleport = spell("Spell: Teleport")
|
||||
lantern = spell("Spell: Lantern")
|
||||
tendrils = spell("Spell: Tendrils")
|
||||
photosynthesis = spell("Spell: Photosynthesis")
|
||||
descend = spell("Spell: Descend")
|
||||
meteor = spell("Spell: Meteor")
|
||||
bloodmana = spell("Spell: Bloodmana")
|
||||
lucksteal = spell("Spell: Lucksteal")
|
||||
spirit = spell("Spell: Spirit")
|
||||
rewind = spell("Spell: Rewind")
|
||||
clear_debris = "Spell: Clear Debris"
|
||||
till = "Spell: Till"
|
||||
water = "Spell: Water"
|
||||
blink = "Spell: Blink"
|
||||
evac = "Spell: Evac"
|
||||
haste = "Spell: Haste"
|
||||
heal = "Spell: Heal"
|
||||
buff = "Spell: Buff"
|
||||
shockwave = "Spell: Shockwave"
|
||||
fireball = "Spell: Fireball"
|
||||
frostbite = "Spell: Frostbolt"
|
||||
teleport = "Spell: Teleport"
|
||||
lantern = "Spell: Lantern"
|
||||
tendrils = "Spell: Tendrils"
|
||||
photosynthesis = "Spell: Photosynthesis"
|
||||
descend = "Spell: Descend"
|
||||
meteor = "Spell: Meteor"
|
||||
bloodmana = "Spell: Bloodmana"
|
||||
lucksteal = "Spell: Lucksteal"
|
||||
spirit = "Spell: Spirit"
|
||||
rewind = "Spell: Rewind"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from Options import Choice, DeathLink, PerGameCommonOptions
|
||||
from Options import Choice, Option, Toggle, DeathLink
|
||||
import typing
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
@@ -49,9 +49,9 @@ class FillExtraChecksWith(Choice):
|
||||
default = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class TerrariaOptions(PerGameCommonOptions):
|
||||
goal: Goal
|
||||
achievements: Achievements
|
||||
fill_extra_checks_with: FillExtraChecksWith
|
||||
death_link: DeathLink
|
||||
options: typing.Dict[str, type(Option)] = { # type: ignore
|
||||
"goal": Goal,
|
||||
"achievements": Achievements,
|
||||
"fill_extra_checks_with": FillExtraChecksWith,
|
||||
"death_link": DeathLink,
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ from .Checks import (
|
||||
armor_minions,
|
||||
accessory_minions,
|
||||
)
|
||||
from .Options import TerrariaOptions
|
||||
from .Options import options
|
||||
|
||||
|
||||
class TerrariaWeb(WebWorld):
|
||||
@@ -49,8 +49,7 @@ class TerrariaWorld(World):
|
||||
|
||||
game = "Terraria"
|
||||
web = TerrariaWeb()
|
||||
options_dataclass = TerrariaOptions
|
||||
options: TerrariaOptions
|
||||
option_definitions = options
|
||||
|
||||
# data_version is used to signal that items, locations or their names
|
||||
# changed. Set this to 0 during development so other games' clients do not
|
||||
@@ -71,7 +70,7 @@ class TerrariaWorld(World):
|
||||
goal_locations: Set[str]
|
||||
|
||||
def generate_early(self) -> None:
|
||||
goal, goal_locations = goals[self.options.goal.value]
|
||||
goal, goal_locations = goals[self.multiworld.goal[self.player].value]
|
||||
ter_goals = {}
|
||||
goal_items = set()
|
||||
for location in goal_locations:
|
||||
@@ -80,7 +79,7 @@ class TerrariaWorld(World):
|
||||
ter_goals[item] = location
|
||||
goal_items.add(item)
|
||||
|
||||
achievements = self.options.achievements.value
|
||||
achievements = self.multiworld.achievements[self.player].value
|
||||
location_count = 0
|
||||
locations = []
|
||||
for rule, flags, _, _ in rules[:goal]:
|
||||
@@ -90,7 +89,7 @@ class TerrariaWorld(World):
|
||||
or (achievements < 2 and "Grindy" in flags)
|
||||
or (achievements < 3 and "Fishing" in flags)
|
||||
or (
|
||||
rule == "Zenith" and self.options.goal.value != 11
|
||||
rule == "Zenith" and self.multiworld.goal[self.player].value != 11
|
||||
) # Bad hardcoding
|
||||
):
|
||||
continue
|
||||
@@ -124,7 +123,7 @@ class TerrariaWorld(World):
|
||||
# Event
|
||||
items.append(rule)
|
||||
|
||||
extra_checks = self.options.fill_extra_checks_with.value
|
||||
extra_checks = self.multiworld.fill_extra_checks_with[self.player].value
|
||||
ordered_rewards = [
|
||||
reward
|
||||
for reward in labels["ordered"]
|
||||
@@ -242,7 +241,7 @@ class TerrariaWorld(World):
|
||||
elif condition == "calamity":
|
||||
return sign == self.calamity
|
||||
elif condition == "grindy":
|
||||
return sign == (self.options.achievements.value >= 2)
|
||||
return sign == (self.multiworld.achievements[self.player].value >= 2)
|
||||
elif condition == "pickaxe":
|
||||
if type(arg) is not int:
|
||||
raise Exception("@pickaxe requires an integer argument")
|
||||
@@ -341,6 +340,6 @@ class TerrariaWorld(World):
|
||||
def fill_slot_data(self) -> Dict[str, object]:
|
||||
return {
|
||||
"goal": list(self.goal_locations),
|
||||
"achievements": self.options.achievements.value,
|
||||
"deathlink": bool(self.options.death_link),
|
||||
"achievements": self.multiworld.achievements[self.player].value,
|
||||
"deathlink": bool(self.multiworld.death_link[self.player]),
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ class DoorGroupings(Choice):
|
||||
"""
|
||||
Controls how door items are grouped.
|
||||
|
||||
- Off: There will be one key for each door, potentially resulting in upwards of 120 keys being added to the item pool.
|
||||
- Regional: All doors in the same general region will open at once with a single key, reducing the amount of door items and complexity.
|
||||
- None: There will be one key for each door, potentially resulting in upwards of 120 keys being added to the item pool.
|
||||
- Regional: - All doors in the same general region will open at once with a single key, reducing the amount of door items and complexity.
|
||||
"""
|
||||
display_name = "Door Groupings"
|
||||
option_off = 0
|
||||
|
||||
@@ -154,11 +154,11 @@ def set_rules(world):
|
||||
lambda state: state.has_all(["Yata-Garasu", "Chaos Emperor Dragon - Envoy of the End", "Sangan"], player)
|
||||
and state.has_any(["No Banlist", "Banlist September 2003"], player),
|
||||
"Can Stall with Monsters":
|
||||
lambda state: state.count_from_list_unique(
|
||||
lambda state: state.count_from_list_exclusive(
|
||||
["Spirit Reaper", "Giant Germ", "Marshmallon", "Nimble Momonga"], player) >= 2,
|
||||
"Can Stall with ST":
|
||||
lambda state: state.count_from_list_unique(["Level Limit - Area B", "Gravity Bind", "Messenger of Peace"],
|
||||
player) >= 2,
|
||||
lambda state: state.count_from_list_exclusive(["Level Limit - Area B", "Gravity Bind", "Messenger of Peace"],
|
||||
player) >= 2,
|
||||
"Has Back-row removal":
|
||||
lambda state: back_row_removal(state, player)
|
||||
|
||||
@@ -201,8 +201,8 @@ def set_rules(world):
|
||||
lambda state: yugioh06_difficulty(state, player, 3),
|
||||
"LD18 Attacks forbidden":
|
||||
lambda state: state.has_all(["Wave-Motion Cannon", "Stealth Bird"], player)
|
||||
and state.count_from_list_unique(["Dark World Lightning", "Nobleman of Crossout",
|
||||
"Shield Crash", "Tribute to the Doomed"], player) >= 2
|
||||
and state.count_from_list_exclusive(["Dark World Lightning", "Nobleman of Crossout",
|
||||
"Shield Crash", "Tribute to the Doomed"], player) >= 2
|
||||
and yugioh06_difficulty(state, player, 3),
|
||||
"LD19 All except E-Hero's forbidden":
|
||||
lambda state: state.has_any(["Polymerization", "Fusion Gate"], player) and
|
||||
@@ -363,7 +363,7 @@ def set_rules(world):
|
||||
"TD30 Tribute Summon":
|
||||
lambda state: state.has("Treeborn Frog", player) and yugioh06_difficulty(state, player, 2),
|
||||
"TD31 Special Summon C":
|
||||
lambda state: state.count_from_list_unique(
|
||||
lambda state: state.count_from_list_exclusive(
|
||||
["Aqua Spirit", "Rock Spirit", "Spirit of Flames",
|
||||
"Garuda the Wind Spirit", "Gigantes", "Inferno", "Megarock Dragon", "Silpheed"],
|
||||
player) > 4 and yugioh06_difficulty(state, player, 3),
|
||||
@@ -393,11 +393,11 @@ def set_rules(world):
|
||||
and yugioh06_difficulty(state, player, 3),
|
||||
"TD39 Raviel, Lord of Phantasms":
|
||||
lambda state: state.has_all(["Raviel, Lord of Phantasms", "Giant Germ"], player) and
|
||||
state.count_from_list_unique(["Archfiend Soldier",
|
||||
"Skull Descovery Knight",
|
||||
"Slate Warrior",
|
||||
"D. D. Trainer",
|
||||
"Earthbound Spirit"], player) >= 3
|
||||
state.count_from_list_exclusive(["Archfiend Soldier",
|
||||
"Skull Descovery Knight",
|
||||
"Slate Warrior",
|
||||
"D. D. Trainer",
|
||||
"Earthbound Spirit"], player) >= 3
|
||||
and yugioh06_difficulty(state, player, 3),
|
||||
"TD40 Make a Chain":
|
||||
lambda state: state.has("Ultimate Offering", player)
|
||||
@@ -450,20 +450,20 @@ def set_rules(world):
|
||||
|
||||
|
||||
def only_light(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Dunames Dark Witch",
|
||||
"X-Head Cannon",
|
||||
"Homunculus the Alchemic Being",
|
||||
"Hysteric Fairy",
|
||||
"Ninja Grandmaster Sasuke"], player, 2)\
|
||||
and state.has_from_list_unique([
|
||||
and state.has_from_list_exclusive([
|
||||
"Chaos Command Magician",
|
||||
"Cybernetic Magician",
|
||||
"Kaiser Glider",
|
||||
"The Agent of Judgment - Saturn",
|
||||
"Zaborg the Thunder Monarch",
|
||||
"Cyber Dragon"], player, 1) \
|
||||
and state.has_from_list_unique([
|
||||
and state.has_from_list_exclusive([
|
||||
"D.D. Warrior Lady",
|
||||
"Mystic Swordsman LV2",
|
||||
"Y-Dragon Head",
|
||||
@@ -472,7 +472,7 @@ def only_light(state, player):
|
||||
|
||||
|
||||
def only_dark(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Dark Elf",
|
||||
"Archfiend Soldier",
|
||||
"Mad Dog of Darkness",
|
||||
@@ -501,7 +501,7 @@ def only_dark(state, player):
|
||||
"Jinzo",
|
||||
"Ryu Kokki"
|
||||
], player) \
|
||||
and state.has_from_list_unique([
|
||||
and state.has_from_list_exclusive([
|
||||
"Legendary Fiend",
|
||||
"Don Zaloog",
|
||||
"Newdoria",
|
||||
@@ -512,7 +512,7 @@ def only_dark(state, player):
|
||||
|
||||
|
||||
def only_earth(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Berserk Gorilla",
|
||||
"Gemini Elf",
|
||||
"Insect Knight",
|
||||
@@ -527,7 +527,7 @@ def only_earth(state, player):
|
||||
"Granmarg the Rock Monarch",
|
||||
"Hieracosphinx",
|
||||
"Saber Beetle"
|
||||
], player) and state.has_from_list_unique([
|
||||
], player) and state.has_from_list_exclusive([
|
||||
"Hyper Hammerhead",
|
||||
"Green Gadget",
|
||||
"Red Gadget",
|
||||
@@ -539,7 +539,7 @@ def only_earth(state, player):
|
||||
|
||||
|
||||
def only_water(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Gagagigo",
|
||||
"Familiar-Possessed - Eria",
|
||||
"7 Colored Fish",
|
||||
@@ -550,7 +550,7 @@ def only_water(state, player):
|
||||
"Amphibian Beast",
|
||||
"Terrorking Salmon",
|
||||
"Mobius the Frost Monarch"
|
||||
], player) and state.has_from_list_unique([
|
||||
], player) and state.has_from_list_exclusive([
|
||||
"Revival Jam",
|
||||
"Yomi Ship",
|
||||
"Treeborn Frog"
|
||||
@@ -558,7 +558,7 @@ def only_water(state, player):
|
||||
|
||||
|
||||
def only_fire(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Blazing Inpachi",
|
||||
"Familiar-Possessed - Hiita",
|
||||
"Great Angus",
|
||||
@@ -566,7 +566,7 @@ def only_fire(state, player):
|
||||
], player, 2) and state.has_any([
|
||||
"Thestalos the Firestorm Monarch",
|
||||
"Horus the Black Flame Dragon LV6"
|
||||
], player) and state.has_from_list_unique([
|
||||
], player) and state.has_from_list_exclusive([
|
||||
"Solar Flare Dragon",
|
||||
"Tenkabito Shien",
|
||||
"Ultimate Baseball Kid"
|
||||
@@ -574,7 +574,7 @@ def only_fire(state, player):
|
||||
|
||||
|
||||
def only_wind(state, player):
|
||||
return state.has_from_list_unique([
|
||||
return state.has_from_list_exclusive([
|
||||
"Luster Dragon",
|
||||
"Slate Warrior",
|
||||
"Spear Dragon",
|
||||
@@ -588,7 +588,7 @@ def only_wind(state, player):
|
||||
"Luster Dragon #2",
|
||||
"Armed Dragon LV5",
|
||||
"Roc from the Valley of Haze"
|
||||
], player) and state.has_from_list_unique([
|
||||
], player) and state.has_from_list_exclusive([
|
||||
"Armed Dragon LV3",
|
||||
"Twin-Headed Behemoth",
|
||||
"Harpie Lady 1"
|
||||
@@ -599,7 +599,7 @@ def only_fairy(state, player):
|
||||
return state.has_any([
|
||||
"Dunames Dark Witch",
|
||||
"Hysteric Fairy"
|
||||
], player) and (state.count_from_list_unique([
|
||||
], player) and (state.count_from_list_exclusive([
|
||||
"Dunames Dark Witch",
|
||||
"Hysteric Fairy",
|
||||
"Dancing Fairy",
|
||||
@@ -623,7 +623,7 @@ def only_warrior(state, player):
|
||||
"Gearfried the Iron knight",
|
||||
"Ninja Grandmaster Sasuke",
|
||||
"Warrior Beaters"
|
||||
], player) and (state.count_from_list_unique([
|
||||
], player) and (state.count_from_list_exclusive([
|
||||
"Warrior Lady of the Wasteland",
|
||||
"Exiled Force",
|
||||
"Mystic Swordsman LV2",
|
||||
@@ -644,7 +644,7 @@ def only_warrior(state, player):
|
||||
|
||||
def only_zombie(state, player):
|
||||
return state.has("Pyramid Turtle", player) \
|
||||
and state.has_from_list_unique([
|
||||
and state.has_from_list_exclusive([
|
||||
"Regenerating Mummy",
|
||||
"Ryu Kokki",
|
||||
"Spirit Reaper",
|
||||
@@ -665,7 +665,7 @@ def only_dragon(state, player):
|
||||
"Luster Dragon",
|
||||
"Spear Dragon",
|
||||
"Cave Dragon"
|
||||
], player) and (state.count_from_list_unique([
|
||||
], player) and (state.count_from_list_exclusive([
|
||||
"Luster Dragon",
|
||||
"Spear Dragon",
|
||||
"Cave Dragon"
|
||||
@@ -692,7 +692,7 @@ def only_spellcaster(state, player):
|
||||
"Toon Gemini Elf",
|
||||
"Kycoo the Ghost Destroyer",
|
||||
"Familiar-Possessed - Aussa"
|
||||
], player) and (state.count_from_list_unique([
|
||||
], player) and (state.count_from_list_exclusive([
|
||||
"Dark Elf",
|
||||
"Gemini Elf",
|
||||
"Skilled Dark Magician",
|
||||
@@ -730,7 +730,7 @@ def equip_unions(state, player):
|
||||
|
||||
|
||||
def can_gain_lp_every_turn(state, player):
|
||||
return state.count_from_list_unique([
|
||||
return state.count_from_list_exclusive([
|
||||
"Solemn Wishes",
|
||||
"Cure Mermaid",
|
||||
"Dancing Fairy",
|
||||
@@ -739,7 +739,7 @@ def can_gain_lp_every_turn(state, player):
|
||||
|
||||
|
||||
def only_normal(state, player):
|
||||
return (state.has_from_list_unique([
|
||||
return (state.has_from_list_exclusive([
|
||||
"Archfiend Soldier",
|
||||
"Gemini Elf",
|
||||
"Insect Knight",
|
||||
@@ -784,21 +784,21 @@ def only_level(state, player):
|
||||
|
||||
def spell_counter(state, player):
|
||||
return (state.has("Pitch-Black Power Stone", player) and
|
||||
state.has_from_list_unique(["Blast Magician",
|
||||
"Magical Marionette",
|
||||
"Mythical Beast Cerberus",
|
||||
"Royal Magical Library",
|
||||
"Spell-Counter Cards"], player, 2))
|
||||
state.has_from_list_exclusive(["Blast Magician",
|
||||
"Magical Marionette",
|
||||
"Mythical Beast Cerberus",
|
||||
"Royal Magical Library",
|
||||
"Spell-Counter Cards"], player, 2))
|
||||
|
||||
|
||||
def take_control(state, player):
|
||||
return state.has_from_list_unique(["Aussa the Earth Charmer",
|
||||
"Jowls of Dark Demise",
|
||||
"Brain Control",
|
||||
"Creature Swap",
|
||||
"Enemy Controller",
|
||||
"Mind Control",
|
||||
"Magician of Faith"], player, 5)
|
||||
return state.has_from_list_exclusive(["Aussa the Earth Charmer",
|
||||
"Jowls of Dark Demise",
|
||||
"Brain Control",
|
||||
"Creature Swap",
|
||||
"Enemy Controller",
|
||||
"Mind Control",
|
||||
"Magician of Faith"], player, 5)
|
||||
|
||||
|
||||
def only_toons(state, player):
|
||||
@@ -818,51 +818,51 @@ def only_spirit(state, player):
|
||||
|
||||
|
||||
def pacman_deck(state, player):
|
||||
return state.has_from_list_unique(["Des Lacooda",
|
||||
"Swarm of Locusts",
|
||||
"Swarm of Scarabs",
|
||||
"Wandering Mummy",
|
||||
"Golem Sentry",
|
||||
"Great Spirit",
|
||||
"Royal Keeper",
|
||||
"Stealth Bird"], player, 4)
|
||||
return state.has_from_list_exclusive(["Des Lacooda",
|
||||
"Swarm of Locusts",
|
||||
"Swarm of Scarabs",
|
||||
"Wandering Mummy",
|
||||
"Golem Sentry",
|
||||
"Great Spirit",
|
||||
"Royal Keeper",
|
||||
"Stealth Bird"], player, 4)
|
||||
|
||||
|
||||
def quick_plays(state, player):
|
||||
return state.has_from_list_unique(["Collapse",
|
||||
"Emergency Provisions",
|
||||
"Enemy Controller",
|
||||
"Graceful Dice",
|
||||
"Mystik Wok",
|
||||
"Offerings to the Doomed",
|
||||
"Poison of the Old Man",
|
||||
"Reload",
|
||||
"Rush Recklessly",
|
||||
"The Reliable Guardian"], player, 4)
|
||||
return state.has_from_list_exclusive(["Collapse",
|
||||
"Emergency Provisions",
|
||||
"Enemy Controller",
|
||||
"Graceful Dice",
|
||||
"Mystik Wok",
|
||||
"Offerings to the Doomed",
|
||||
"Poison of the Old Man",
|
||||
"Reload",
|
||||
"Rush Recklessly",
|
||||
"The Reliable Guardian"], player, 4)
|
||||
|
||||
|
||||
def counter_traps(state, player):
|
||||
return state.has_from_list_unique(["Cursed Seal of the Forbidden Spell",
|
||||
"Divine Wrath",
|
||||
"Horn of Heaven",
|
||||
"Magic Drain",
|
||||
"Magic Jammer",
|
||||
"Negate Attack",
|
||||
"Seven Tools of the Bandit",
|
||||
"Solemn Judgment",
|
||||
"Spell Shield Type-8"], player, 5)
|
||||
return state.has_from_list_exclusive(["Cursed Seal of the Forbidden Spell",
|
||||
"Divine Wrath",
|
||||
"Horn of Heaven",
|
||||
"Magic Drain",
|
||||
"Magic Jammer",
|
||||
"Negate Attack",
|
||||
"Seven Tools of the Bandit",
|
||||
"Solemn Judgment",
|
||||
"Spell Shield Type-8"], player, 5)
|
||||
|
||||
|
||||
def back_row_removal(state, player):
|
||||
return state.has_from_list_unique(["Anteatereatingant",
|
||||
"B.E.S. Tetran",
|
||||
"Breaker the Magical Warrior",
|
||||
"Calamity of the Wicked",
|
||||
"Chiron the Mage",
|
||||
"Dust Tornado",
|
||||
"Heavy Storm",
|
||||
"Mystical Space Typhoon",
|
||||
"Mobius the Frost Monarch",
|
||||
"Raigeki Break",
|
||||
"Stamping Destruction",
|
||||
"Swarm of Locusts"], player, 2)
|
||||
return state.has_from_list_exclusive(["Anteatereatingant",
|
||||
"B.E.S. Tetran",
|
||||
"Breaker the Magical Warrior",
|
||||
"Calamity of the Wicked",
|
||||
"Chiron the Mage",
|
||||
"Dust Tornado",
|
||||
"Heavy Storm",
|
||||
"Mystical Space Typhoon",
|
||||
"Mobius the Frost Monarch",
|
||||
"Raigeki Break",
|
||||
"Stamping Destruction",
|
||||
"Swarm of Locusts"], player, 2)
|
||||
|
||||
Reference in New Issue
Block a user