mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-27 18:23:22 -07:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # worlds/ahit/Regions.py
This commit is contained in:
64
Fill.py
64
Fill.py
@@ -35,8 +35,8 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
|
||||
"""
|
||||
:param multiworld: Multiworld to be filled.
|
||||
:param base_state: State assumed before fill.
|
||||
:param locations: Locations to be filled with item_pool
|
||||
:param item_pool: Items to fill into the locations
|
||||
:param locations: Locations to be filled with item_pool, gets mutated by removing locations that get filled.
|
||||
:param item_pool: Items to fill into the locations, gets mutated by removing items that get placed.
|
||||
:param single_player_placement: if true, can speed up placement if everything belongs to a single player
|
||||
:param lock: locations are set to locked as they are filled
|
||||
:param swap: if true, swaps of already place items are done in the event of a dead end
|
||||
@@ -220,7 +220,8 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
|
||||
def remaining_fill(multiworld: MultiWorld,
|
||||
locations: typing.List[Location],
|
||||
itempool: typing.List[Item],
|
||||
name: str = "Remaining") -> None:
|
||||
name: str = "Remaining",
|
||||
move_unplaceable_to_start_inventory: bool = False) -> None:
|
||||
unplaced_items: typing.List[Item] = []
|
||||
placements: typing.List[Location] = []
|
||||
swapped_items: typing.Counter[typing.Tuple[int, str]] = Counter()
|
||||
@@ -284,13 +285,21 @@ def remaining_fill(multiworld: MultiWorld,
|
||||
|
||||
if unplaced_items and locations:
|
||||
# There are leftover unplaceable items and locations that won't accept them
|
||||
raise FillError(f"No more spots to place {len(unplaced_items)} items. Remaining locations are invalid.\n"
|
||||
f"Unplaced items:\n"
|
||||
f"{', '.join(str(item) for item in unplaced_items)}\n"
|
||||
f"Unfilled locations:\n"
|
||||
f"{', '.join(str(location) for location in locations)}\n"
|
||||
f"Already placed {len(placements)}:\n"
|
||||
f"{', '.join(str(place) for place in placements)}")
|
||||
if move_unplaceable_to_start_inventory:
|
||||
last_batch = []
|
||||
for item in unplaced_items:
|
||||
logging.debug(f"Moved {item} to start_inventory to prevent fill failure.")
|
||||
multiworld.push_precollected(item)
|
||||
last_batch.append(multiworld.worlds[item.player].create_filler())
|
||||
remaining_fill(multiworld, locations, unplaced_items, name + " Start Inventory Retry")
|
||||
else:
|
||||
raise FillError(f"No more spots to place {len(unplaced_items)} items. Remaining locations are invalid.\n"
|
||||
f"Unplaced items:\n"
|
||||
f"{', '.join(str(item) for item in unplaced_items)}\n"
|
||||
f"Unfilled locations:\n"
|
||||
f"{', '.join(str(location) for location in locations)}\n"
|
||||
f"Already placed {len(placements)}:\n"
|
||||
f"{', '.join(str(place) for place in placements)}")
|
||||
|
||||
itempool.extend(unplaced_items)
|
||||
|
||||
@@ -420,7 +429,8 @@ def distribute_early_items(multiworld: MultiWorld,
|
||||
return fill_locations, itempool
|
||||
|
||||
|
||||
def distribute_items_restrictive(multiworld: MultiWorld) -> None:
|
||||
def distribute_items_restrictive(multiworld: MultiWorld,
|
||||
panic_method: typing.Literal["swap", "raise", "start_inventory"] = "swap") -> None:
|
||||
fill_locations = sorted(multiworld.get_unfilled_locations())
|
||||
multiworld.random.shuffle(fill_locations)
|
||||
# get items to distribute
|
||||
@@ -470,8 +480,29 @@ def distribute_items_restrictive(multiworld: MultiWorld) -> None:
|
||||
|
||||
if progitempool:
|
||||
# "advancement/progression fill"
|
||||
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, single_player_placement=multiworld.players == 1,
|
||||
name="Progression")
|
||||
if panic_method == "swap":
|
||||
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
|
||||
swap=True,
|
||||
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
|
||||
elif panic_method == "raise":
|
||||
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
|
||||
swap=False,
|
||||
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
|
||||
elif panic_method == "start_inventory":
|
||||
fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool,
|
||||
swap=False, allow_partial=True,
|
||||
on_place=mark_for_locking, name="Progression", single_player_placement=multiworld.players == 1)
|
||||
if progitempool:
|
||||
for item in progitempool:
|
||||
logging.debug(f"Moved {item} to start_inventory to prevent fill failure.")
|
||||
multiworld.push_precollected(item)
|
||||
filleritempool.append(multiworld.worlds[item.player].create_filler())
|
||||
logging.warning(f"{len(progitempool)} items moved to start inventory,"
|
||||
f" due to failure in Progression fill step.")
|
||||
progitempool[:] = []
|
||||
|
||||
else:
|
||||
raise ValueError(f"Generator Panic Method {panic_method} not recognized.")
|
||||
if progitempool:
|
||||
raise FillError(
|
||||
f"Not enough locations for progression items. "
|
||||
@@ -486,7 +517,9 @@ def distribute_items_restrictive(multiworld: MultiWorld) -> None:
|
||||
|
||||
inaccessible_location_rules(multiworld, multiworld.state, defaultlocations)
|
||||
|
||||
remaining_fill(multiworld, excludedlocations, filleritempool, "Remaining Excluded")
|
||||
remaining_fill(multiworld, excludedlocations, filleritempool, "Remaining Excluded",
|
||||
move_unplaceable_to_start_inventory=panic_method=="start_inventory")
|
||||
|
||||
if excludedlocations:
|
||||
raise FillError(
|
||||
f"Not enough filler items for excluded locations. "
|
||||
@@ -495,7 +528,8 @@ def distribute_items_restrictive(multiworld: MultiWorld) -> None:
|
||||
|
||||
restitempool = filleritempool + usefulitempool
|
||||
|
||||
remaining_fill(multiworld, defaultlocations, restitempool)
|
||||
remaining_fill(multiworld, defaultlocations, restitempool,
|
||||
move_unplaceable_to_start_inventory=panic_method=="start_inventory")
|
||||
|
||||
unplaced = restitempool
|
||||
unfilled = defaultlocations
|
||||
|
||||
4
Main.py
4
Main.py
@@ -13,7 +13,7 @@ import worlds
|
||||
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld, Region
|
||||
from Fill import balance_multiworld_progression, distribute_items_restrictive, distribute_planned, flood_items
|
||||
from Options import StartInventoryPool
|
||||
from Utils import __version__, output_path, version_tuple
|
||||
from Utils import __version__, output_path, version_tuple, get_settings
|
||||
from settings import get_settings
|
||||
from worlds import AutoWorld
|
||||
from worlds.generic.Rules import exclusion_rules, locality_rules
|
||||
@@ -272,7 +272,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
if multiworld.algorithm == 'flood':
|
||||
flood_items(multiworld) # different algo, biased towards early game progress items
|
||||
elif multiworld.algorithm == 'balanced':
|
||||
distribute_items_restrictive(multiworld)
|
||||
distribute_items_restrictive(multiworld, get_settings().generator.panic_method)
|
||||
|
||||
AutoWorld.call_all(multiworld, 'post_fill')
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ Currently, the following games are supported:
|
||||
* Yoshi's Island
|
||||
* Mario & Luigi: Superstar Saga
|
||||
* Bomb Rush Cyberfunk
|
||||
* Aquaria
|
||||
* Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006
|
||||
* A Hat in Time
|
||||
|
||||
|
||||
@@ -270,15 +270,19 @@ def run_server_process(name: str, ponyconfig: dict, static_server_data: dict,
|
||||
await ctx.shutdown_task
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
except Exception:
|
||||
if ctx.saving:
|
||||
ctx._save()
|
||||
except Exception as e:
|
||||
with db_session:
|
||||
room = Room.get(id=room_id)
|
||||
room.last_port = -1
|
||||
logger.exception(e)
|
||||
raise
|
||||
else:
|
||||
if ctx.saving:
|
||||
ctx._save()
|
||||
finally:
|
||||
try:
|
||||
ctx._save()
|
||||
with (db_session):
|
||||
# ensure the Room does not spin up again on its own, minute of safety buffer
|
||||
room = Room.get(id=room_id)
|
||||
|
||||
@@ -665,6 +665,14 @@ class GeneratorOptions(Group):
|
||||
OFF = 0
|
||||
ON = 1
|
||||
|
||||
class PanicMethod(str):
|
||||
"""
|
||||
What to do if the current item placements appear unsolvable.
|
||||
raise -> Raise an exception and abort.
|
||||
swap -> Attempt to fix it by swapping prior placements around. (Default)
|
||||
start_inventory -> Move remaining items to start_inventory, generate additional filler items to fill locations.
|
||||
"""
|
||||
|
||||
enemizer_path: EnemizerPath = EnemizerPath("EnemizerCLI/EnemizerCLI.Core") # + ".exe" is implied on Windows
|
||||
player_files_path: PlayerFilesPath = PlayerFilesPath("Players")
|
||||
players: Players = Players(0)
|
||||
@@ -673,6 +681,7 @@ class GeneratorOptions(Group):
|
||||
spoiler: Spoiler = Spoiler(3)
|
||||
race: Race = Race(0)
|
||||
plando_options: PlandoOptions = PlandoOptions("bosses, connections, texts")
|
||||
panic_method: PanicMethod = PanicMethod("swap")
|
||||
|
||||
|
||||
class SNIOptions(Group):
|
||||
|
||||
@@ -8,14 +8,16 @@ from typing import Optional
|
||||
from enum import Enum
|
||||
from BaseClasses import Item, ItemClassification
|
||||
|
||||
|
||||
class ItemType(Enum):
|
||||
"""
|
||||
Used to indicate to the multi-world if an item is usefull or not
|
||||
Used to indicate to the multi-world if an item is useful or not
|
||||
"""
|
||||
NORMAL = 0
|
||||
PROGRESSION = 1
|
||||
JUNK = 2
|
||||
|
||||
|
||||
class ItemGroup(Enum):
|
||||
"""
|
||||
Used to group items
|
||||
@@ -28,6 +30,7 @@ class ItemGroup(Enum):
|
||||
SONG = 5
|
||||
TURTLE = 6
|
||||
|
||||
|
||||
class AquariaItem(Item):
|
||||
"""
|
||||
A single item in the Aquaria game.
|
||||
@@ -40,22 +43,23 @@ class AquariaItem(Item):
|
||||
"""
|
||||
Initialisation of the Item
|
||||
:param name: The name of the item
|
||||
:param classification: If the item is usefull or not
|
||||
:param classification: If the item is useful or not
|
||||
:param code: The ID of the item (if None, it is an event)
|
||||
:param player: The ID of the player in the multiworld
|
||||
"""
|
||||
super().__init__(name, classification, code, player)
|
||||
|
||||
|
||||
class ItemData:
|
||||
"""
|
||||
Data of an item.
|
||||
"""
|
||||
id:int
|
||||
count:int
|
||||
type:ItemType
|
||||
group:ItemGroup
|
||||
id: int
|
||||
count: int
|
||||
type: ItemType
|
||||
group: ItemGroup
|
||||
|
||||
def __init__(self, id:int, count:int, type:ItemType, group:ItemGroup):
|
||||
def __init__(self, id: int, count: int, type: ItemType, group: ItemGroup):
|
||||
"""
|
||||
Initialisation of the item data
|
||||
@param id: The item ID
|
||||
@@ -68,6 +72,7 @@ class ItemData:
|
||||
self.type = type
|
||||
self.group = group
|
||||
|
||||
|
||||
"""Information data for every (not event) item."""
|
||||
item_table = {
|
||||
# name: ID, Nb, Item Type, Item Group
|
||||
@@ -207,4 +212,3 @@ item_table = {
|
||||
"Transturtle Simon says": ItemData(698132, 1, ItemType.PROGRESSION, ItemGroup.TURTLE), # transport_forest05
|
||||
"Transturtle Arnassi ruins": ItemData(698133, 1, ItemType.PROGRESSION, ItemGroup.TURTLE), # transport_seahorse
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class AquariaLocations:
|
||||
|
||||
locations_home_water = {
|
||||
"Home water, bulb below the grouper fish": 698058,
|
||||
"Home water, bulb in the path bellow Nautilus Prime": 698059,
|
||||
"Home water, bulb in the path below Nautilus Prime": 698059,
|
||||
"Home water, bulb in the little room above the grouper fish": 698060,
|
||||
"Home water, bulb in the end of the left path from the verse cave": 698061,
|
||||
"Home water, bulb in the top left path": 698062,
|
||||
@@ -129,7 +129,7 @@ class AquariaLocations:
|
||||
|
||||
locations_openwater_bl = {
|
||||
"Open water bottom left area, bulb behind the chomper fish": 698011,
|
||||
"Open water bottom left area, bulb inside the downest fish pass": 698010,
|
||||
"Open water bottom left area, bulb inside the lowest fish pass": 698010,
|
||||
}
|
||||
|
||||
locations_skeleton_path = {
|
||||
@@ -226,7 +226,7 @@ class AquariaLocations:
|
||||
"Mithalas cathedral, third urn in the path behind the flesh vein": 698146,
|
||||
"Mithalas cathedral, one of the urns in the top right room": 698147,
|
||||
"Mithalas cathedral, Mithalan Dress": 698189,
|
||||
"Mithalas cathedral right area, urn bellow the left entrance": 698198,
|
||||
"Mithalas cathedral right area, urn below the left entrance": 698198,
|
||||
}
|
||||
|
||||
locations_cathedral_underground = {
|
||||
@@ -457,7 +457,7 @@ class AquariaLocations:
|
||||
locations_body_l = {
|
||||
"The body left area, first bulb in the top face room": 698066,
|
||||
"The body left area, second bulb in the top face room": 698069,
|
||||
"The body left area, bulb bellow the water stream": 698067,
|
||||
"The body left area, bulb below the water stream": 698067,
|
||||
"The body left area, bulb in the top path to the top face room": 698068,
|
||||
"The body left area, bulb in the bottom face room": 698070,
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ from Options import Toggle, Choice, Range, DeathLink, PerGameCommonOptions, Defa
|
||||
|
||||
class IngredientRandomizer(Choice):
|
||||
"""
|
||||
Randomize Ingredients. Select if the simple ingredients (that does not have
|
||||
a recipe) should be randomized. If 'common_ingredients' is selected, the
|
||||
randomization will exclude the "Red Bulb", "Special Bulb" and "Rukh Egg".
|
||||
Select if the simple ingredients (that do not have a recipe) should be randomized.
|
||||
If "Common Ingredients" is selected, the randomization will exclude the "Red Bulb", "Special Bulb" and "Rukh Egg".
|
||||
"""
|
||||
display_name = "Randomize Ingredients"
|
||||
option_off = 0
|
||||
@@ -29,27 +28,25 @@ class DishRandomizer(Toggle):
|
||||
class TurtleRandomizer(Choice):
|
||||
"""Randomize the transportation turtle."""
|
||||
display_name = "Turtle Randomizer"
|
||||
option_no_turtle_randomization = 0
|
||||
option_randomize_all_turtle = 1
|
||||
option_randomize_turtle_other_than_the_final_one = 2
|
||||
option_none = 0
|
||||
option_all = 1
|
||||
option_all_except_final = 2
|
||||
default = 2
|
||||
|
||||
|
||||
class EarlyEnergyForm(DefaultOnToggle):
|
||||
"""
|
||||
Force the Energy Form to be in a location before leaving the areas around the Home Water.
|
||||
"""
|
||||
""" Force the Energy Form to be in a location early in the game """
|
||||
display_name = "Early Energy Form"
|
||||
|
||||
|
||||
class AquarianTranslation(Toggle):
|
||||
"""Translate to English the Aquarian scripture in the game."""
|
||||
"""Translate the Aquarian scripture in the game into English."""
|
||||
display_name = "Translate Aquarian"
|
||||
|
||||
|
||||
class BigBossesToBeat(Range):
|
||||
"""
|
||||
A number of big bosses to beat before having access to the creator (the final boss). The big bosses are
|
||||
The number of big bosses to beat before having access to the creator (the final boss). The big bosses are
|
||||
"Fallen God", "Mithalan God", "Drunian God", "Sun God" and "The Golem".
|
||||
"""
|
||||
display_name = "Big bosses to beat"
|
||||
@@ -60,12 +57,12 @@ class BigBossesToBeat(Range):
|
||||
|
||||
class MiniBossesToBeat(Range):
|
||||
"""
|
||||
A number of Minibosses to beat before having access to the creator (the final boss). Mini bosses are
|
||||
The number of minibosses to beat before having access to the creator (the final boss). The minibosses are
|
||||
"Nautilus Prime", "Blaster Peg Prime", "Mergog", "Mithalan priests", "Octopus Prime", "Crabbius Maximus",
|
||||
"Mantis Shrimp Prime" and "King Jellyfish God Prime". Note that the Energy statue and Simon says are not
|
||||
mini bosses.
|
||||
"Mantis Shrimp Prime" and "King Jellyfish God Prime".
|
||||
Note that the Energy Statue and Simon Says are not minibosses.
|
||||
"""
|
||||
display_name = "Mini bosses to beat"
|
||||
display_name = "Minibosses to beat"
|
||||
range_start = 0
|
||||
range_end = 8
|
||||
default = 0
|
||||
@@ -73,47 +70,50 @@ class MiniBossesToBeat(Range):
|
||||
|
||||
class Objective(Choice):
|
||||
"""
|
||||
The game objective can be only to kill the creator or to kill the creator
|
||||
and having obtained the three every secret memories
|
||||
The game objective can be to kill the creator or to kill the creator after obtaining all three secret memories.
|
||||
"""
|
||||
display_name = "Objective"
|
||||
option_kill_the_creator = 0
|
||||
option_obtain_secrets_and_kill_the_creator = 1
|
||||
default = 0
|
||||
|
||||
|
||||
class SkipFirstVision(Toggle):
|
||||
"""
|
||||
The first vision in the game; where Naija transform to Energy Form and get fload by enemy; is quite cool but
|
||||
The first vision in the game, where Naija transforms into Energy Form and gets flooded by enemies, is quite cool but
|
||||
can be quite long when you already know what is going on. This option can be used to skip this vision.
|
||||
"""
|
||||
display_name = "Skip first Naija's vision"
|
||||
display_name = "Skip Naija's first vision"
|
||||
|
||||
|
||||
class NoProgressionHardOrHiddenLocation(Toggle):
|
||||
"""
|
||||
Make sure that there is no progression items at hard to get or hard to find locations.
|
||||
Those locations that will be very High location (that need beast form, soup and skill to get), every
|
||||
location in the bubble cave, locations that need you to cross a false wall without any indication, Arnassi
|
||||
race, bosses and mini-bosses. Usefull for those that want a casual run.
|
||||
Make sure that there are no progression items at hard-to-reach or hard-to-find locations.
|
||||
Those locations are very High locations (that need beast form, soup and skill to get),
|
||||
every location in the bubble cave, locations where need you to cross a false wall without any indication,
|
||||
the Arnassi race, bosses and minibosses. Useful for those that want a more casual run.
|
||||
"""
|
||||
display_name = "No progression in hard or hidden locations"
|
||||
|
||||
|
||||
class LightNeededToGetToDarkPlaces(DefaultOnToggle):
|
||||
"""
|
||||
Make sure that the sun form or the dumbo pet can be aquired before getting to dark places. Be aware that navigating
|
||||
in dark place without light is extremely difficult.
|
||||
Make sure that the sun form or the dumbo pet can be acquired before getting to dark places.
|
||||
Be aware that navigating in dark places without light is extremely difficult.
|
||||
"""
|
||||
display_name = "Light needed to get to dark places"
|
||||
|
||||
|
||||
class BindSongNeededToGetUnderRockBulb(Toggle):
|
||||
"""
|
||||
Make sure that the bind song can be aquired before having to obtain sing bulb under rocks.
|
||||
Make sure that the bind song can be acquired before having to obtain sing bulbs under rocks.
|
||||
"""
|
||||
display_name = "Bind song needed to get sing bulbs under rocks"
|
||||
|
||||
|
||||
class UnconfineHomeWater(Choice):
|
||||
"""
|
||||
Open the way out of Home water area so that Naija can go to open water and beyond without the bind song.
|
||||
Open the way out of the Home water area so that Naija can go to open water and beyond without the bind song.
|
||||
"""
|
||||
display_name = "Unconfine Home Water Area"
|
||||
option_off = 0
|
||||
|
||||
@@ -5,7 +5,7 @@ Description: Used to manage Regions in the Aquaria game multiworld randomizer
|
||||
"""
|
||||
|
||||
from typing import Dict, Optional
|
||||
from BaseClasses import MultiWorld, Region, Entrance, ItemClassification, LocationProgressType, CollectionState
|
||||
from BaseClasses import MultiWorld, Region, Entrance, ItemClassification, CollectionState
|
||||
from .Items import AquariaItem
|
||||
from .Locations import AquariaLocations, AquariaLocation
|
||||
from .Options import AquariaOptions
|
||||
@@ -223,8 +223,6 @@ class AquariaRegions:
|
||||
region.add_locations(locations, AquariaLocation)
|
||||
return region
|
||||
|
||||
|
||||
|
||||
def __create_home_water_area(self) -> None:
|
||||
"""
|
||||
Create the `verse_cave`, `home_water` and `song_cave*` regions
|
||||
@@ -941,7 +939,7 @@ class AquariaRegions:
|
||||
"""
|
||||
Add secrets events to the `world`
|
||||
"""
|
||||
self.__add_event_location(self.first_secret, # Doit ajouter une région pour le "first secret"
|
||||
self.__add_event_location(self.first_secret, # Doit ajouter une région pour le "first secret"
|
||||
"First secret",
|
||||
"First secret obtained")
|
||||
self.__add_event_location(self.mithalas_city,
|
||||
@@ -1095,12 +1093,10 @@ class AquariaRegions:
|
||||
add_rule(self.multiworld.get_entrance("Veil left of sun temple to Sun temple left area", self.player),
|
||||
lambda state: _has_light(state, self.player) or _has_sun_crystal(state, self.player))
|
||||
|
||||
|
||||
|
||||
def __adjusting_manual_rules(self) -> None:
|
||||
add_rule(self.multiworld.get_location("Mithalas cathedral, Mithalan Dress", self.player),
|
||||
lambda state: _has_beast_form(state, self.player))
|
||||
add_rule(self.multiworld.get_location("Open water bottom left area, bulb inside the downest fish pass", self.player),
|
||||
add_rule(self.multiworld.get_location("Open water bottom left area, bulb inside the lowest fish pass", self.player),
|
||||
lambda state: _has_fish_form(state, self.player))
|
||||
add_rule(self.multiworld.get_location("Kelp forest bottom left area, Walker baby", self.player),
|
||||
lambda state: _has_spirit_form(state, self.player))
|
||||
@@ -1122,7 +1118,7 @@ class AquariaRegions:
|
||||
self.player), lambda state: _has_energy_form(state, self.player))
|
||||
add_rule(self.multiworld.get_location("Home water, bulb in the bottom left room", self.player),
|
||||
lambda state: _has_bind_song(state, self.player))
|
||||
add_rule(self.multiworld.get_location("Home water, bulb in the path bellow Nautilus Prime", self.player),
|
||||
add_rule(self.multiworld.get_location("Home water, bulb in the path below Nautilus Prime", self.player),
|
||||
lambda state: _has_bind_song(state, self.player))
|
||||
add_rule(self.multiworld.get_location("Naija's home, bulb after the energy door", self.player),
|
||||
lambda state: _has_energy_form(state, self.player))
|
||||
@@ -1133,9 +1129,6 @@ class AquariaRegions:
|
||||
lambda state: _has_fish_form(state, self.player) and
|
||||
_has_spirit_form(state, self.player))
|
||||
|
||||
|
||||
|
||||
|
||||
def __no_progression_hard_or_hidden_location(self) -> None:
|
||||
self.multiworld.get_location("Energy temple boss area, Fallen god tooth",
|
||||
self.player).item_rule =\
|
||||
@@ -1242,11 +1235,7 @@ class AquariaRegions:
|
||||
add_rule(self.multiworld.get_entrance("Home Water to Open water top left area", self.player),
|
||||
lambda state: _has_bind_song(state, self.player) and _has_energy_form(state, self.player))
|
||||
if options.early_energy_form:
|
||||
add_rule(self.multiworld.get_entrance("Home Water to Home water transturtle room", self.player),
|
||||
lambda state: _has_energy_form(state, self.player))
|
||||
if options.early_energy_form:
|
||||
add_rule(self.multiworld.get_entrance("Home Water to Open water top left area", self.player),
|
||||
lambda state: _has_energy_form(state, self.player))
|
||||
self.multiworld.early_items[self.player]["Energy form"] = 1
|
||||
|
||||
if options.no_progression_hard_or_hidden_locations:
|
||||
self.__no_progression_hard_or_hidden_location()
|
||||
|
||||
@@ -5,7 +5,7 @@ Description: Main module for Aquaria game multiworld randomizer
|
||||
"""
|
||||
|
||||
from typing import List, Dict, ClassVar, Any
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from BaseClasses import Tutorial, MultiWorld, ItemClassification
|
||||
from .Items import item_table, AquariaItem, ItemType, ItemGroup
|
||||
from .Locations import location_table
|
||||
@@ -114,7 +114,7 @@ class AquariaWorld(World):
|
||||
|
||||
def create_item(self, name: str) -> AquariaItem:
|
||||
"""
|
||||
Create an AquariaItem using `name' as item name.
|
||||
Create an AquariaItem using 'name' as item name.
|
||||
"""
|
||||
result: AquariaItem
|
||||
try:
|
||||
|
||||
@@ -11,39 +11,39 @@ options page link: [Aquaria Player Options Page](../player-options).
|
||||
## What does randomization do to this game?
|
||||
The locations in the randomizer are:
|
||||
|
||||
- All sing bulbs;
|
||||
- All Mithalas Urns;
|
||||
- All Sunken City crates;
|
||||
- Collectible treasure locations (including pet eggs and costumes);
|
||||
- Beating Simon says;
|
||||
- Li cave;
|
||||
- Every Transportation Turtle (also called transturtle);
|
||||
- Locations where you get songs,
|
||||
* Erulian spirit cristal,
|
||||
* Energy status mini-boss,
|
||||
* Beating Mithalan God boss,
|
||||
* Fish cave puzzle,
|
||||
* Beating Drunian God boss,
|
||||
* Beating Sun God boss,
|
||||
* Breaking Li cage in the body
|
||||
- All sing bulbs
|
||||
- All Mithalas Urns
|
||||
- All Sunken City crates
|
||||
- Collectible treasure locations (including pet eggs and costumes)
|
||||
- Beating Simon says
|
||||
- Li cave
|
||||
- Every Transportation Turtle (also called transturtle)
|
||||
- Locations where you get songs:
|
||||
* Erulian spirit cristal
|
||||
* Energy status mini-boss
|
||||
* Beating Mithalan God boss
|
||||
* Fish cave puzzle
|
||||
* Beating Drunian God boss
|
||||
* Beating Sun God boss
|
||||
* Breaking Li cage in the body
|
||||
|
||||
Note that, unlike the vanilla game, when opening sing bulbs, Mithalas urns and Sunken City crates,
|
||||
nothing will come out of them. The moment those bulbs, urns and crates are opened, the location is considered received.
|
||||
nothing will come out of them. The moment those bulbs, urns and crates are opened, the location is considered checked.
|
||||
|
||||
The items in the randomizer are:
|
||||
- Dishes (used to learn recipes*);
|
||||
- Some ingredients;
|
||||
- The Wok (third plate used to cook 3 ingredients recipes everywhere);
|
||||
- All collectible treasure (including pet eggs and costumes);
|
||||
- Li and Li song;
|
||||
- All songs (other than Li's song since it is learned when Li is obtained);
|
||||
- Transportation to transturtles.
|
||||
- Dishes (used to learn recipes)<sup>*</sup>
|
||||
- Some ingredients
|
||||
- The Wok (third plate used to cook 3-ingredient recipes everywhere)
|
||||
- All collectible treasure (including pet eggs and costumes)
|
||||
- Li and Li's song
|
||||
- All songs (other than Li's song since it is learned when Li is obtained)
|
||||
- Transportation to transturtles
|
||||
|
||||
Also, there is the option to randomize every ingredient drops (from fishes, monsters
|
||||
or plants).
|
||||
|
||||
*Note that, unlike in the vanilla game, the recipes for dishes (other than the Sea Loaf)
|
||||
cannot be cooked (and learn) before being obtained as randomized items. Also, enemies and plants
|
||||
<sup>*</sup> Note that, unlike in the vanilla game, the recipes for dishes (other than the Sea Loaf)
|
||||
cannot be cooked (or learned) before being obtained as randomized items. Also, enemies and plants
|
||||
that drop dishes that have not been learned before will drop ingredients of this dish instead.
|
||||
|
||||
## What is the goal of the game?
|
||||
@@ -57,8 +57,8 @@ Any items specified above can be in another player's world.
|
||||
No visuals are shown when finding locations other than collectible treasure.
|
||||
For those treasures, the visual of the treasure is visually unchanged.
|
||||
After collecting a location check, a message will be shown to inform the player
|
||||
what has been collected, and who will receive it.
|
||||
what has been collected and who will receive it.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
When you receive an item, a message will pop up to inform you where you received
|
||||
the item from, and which one it is.
|
||||
the item from and which one it was.
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
## Required Software
|
||||
|
||||
- The original Aquaria Game (buyable from a lot of online game seller);
|
||||
- The original Aquaria Game (purchasable from most online game stores)
|
||||
- The [Aquaria randomizer](https://github.com/tioui/Aquaria_Randomizer/releases)
|
||||
- Optional, for sending [commands](/tutorial/Archipelago/commands/en) like `!hint`: the TextClient from [the most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
||||
## Optional Software
|
||||
|
||||
- For sending [commands](/tutorial/Archipelago/commands/en) like `!hint`: the TextClient from [the most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
||||
## Installation and execution Procedures
|
||||
|
||||
@@ -13,10 +16,9 @@
|
||||
First, you should copy the original Aquaria folder game. The randomizer will possibly modify the game so that
|
||||
the original game will stop working. Copying the folder will guarantee that the original game keeps on working.
|
||||
Also, in Windows, the save files are stored in the Aquaria folder. So copying the Aquaria folder for every Multiworld
|
||||
game you play will make sure that every game has their own save game.
|
||||
game you play will make sure that every game has its own save game.
|
||||
|
||||
Unzip the Aquaria randomizer release and copy all unzipped files in the Aquaria game folder. The unzipped files
|
||||
are those:
|
||||
Unzip the Aquaria randomizer release and copy all unzipped files in the Aquaria game folder. The unzipped files are:
|
||||
- aquaria_randomizer.exe
|
||||
- OpenAL32.dll
|
||||
- override (directory)
|
||||
@@ -25,11 +27,11 @@ are those:
|
||||
- wrap_oal.dll
|
||||
- cacert.pem
|
||||
|
||||
If there is a conflict between file in the original game folder and the unzipped files, you should override
|
||||
the original files with the one of the unzipped randomizer.
|
||||
If there is a conflict between files in the original game folder and the unzipped files, you should overwrite
|
||||
the original files with the ones from the unzipped randomizer.
|
||||
|
||||
Finally, to launch the randomizer, you must use the command line interface (you can open the command line interface
|
||||
by writing `cmd` in the address bar of the Windows file explorer). Here is the command line to use to start the
|
||||
by typing `cmd` in the address bar of the Windows File Explorer). Here is the command line used to start the
|
||||
randomizer:
|
||||
|
||||
```bash
|
||||
@@ -44,8 +46,8 @@ aquaria_randomizer.exe --name YourName --server theServer:thePort --password th
|
||||
|
||||
### Linux when using the AppImage
|
||||
|
||||
If you use the AppImage, just copy it in the Aquaria game folder. You then have to make it executable. You
|
||||
can do that from command line by using
|
||||
If you use the AppImage, just copy it into the Aquaria game folder. You then have to make it executable. You
|
||||
can do that from command line by using:
|
||||
|
||||
```bash
|
||||
chmod +x Aquaria_Randomizer-*.AppImage
|
||||
@@ -65,7 +67,7 @@ or, if the room has a password:
|
||||
./Aquaria_Randomizer-*.AppImage --name YourName --server theServer:thePort --password thePassword
|
||||
```
|
||||
|
||||
Note that you should not have multiple Aquaria_Randomizer AppImage file in the same folder. If this situation occurred,
|
||||
Note that you should not have multiple Aquaria_Randomizer AppImage file in the same folder. If this situation occurs,
|
||||
the preceding commands will launch the game multiple times.
|
||||
|
||||
### Linux when using the tar file
|
||||
@@ -73,24 +75,23 @@ the preceding commands will launch the game multiple times.
|
||||
First, you should copy the original Aquaria folder game. The randomizer will possibly modify the game so that
|
||||
the original game will stop working. Copying the folder will guarantee that the original game keeps on working.
|
||||
|
||||
Untar the Aquaria randomizer release and copy all extracted files in the Aquaria game folder. The extracted
|
||||
files are those:
|
||||
Untar the Aquaria randomizer release and copy all extracted files in the Aquaria game folder. The extracted files are:
|
||||
- aquaria_randomizer
|
||||
- override (directory)
|
||||
- usersettings.xml
|
||||
- cacert.pem
|
||||
|
||||
If there is a conflict between file in the original game folder and the extracted files, you should override
|
||||
the original files with the one of the extracted randomizer files.
|
||||
If there is a conflict between files in the original game folder and the extracted files, you should overwrite
|
||||
the original files with the ones from the extracted randomizer files.
|
||||
|
||||
Then, you should use your system package manager to install liblua5, libogg, libvorbis, libopenal and libsdl2.
|
||||
Then, you should use your system package manager to install `liblua5`, `libogg`, `libvorbis`, `libopenal` and `libsdl2`.
|
||||
On Debian base system (like Ubuntu), you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo apt install liblua5.1-0-dev libogg-dev libvorbis-dev libopenal-dev libsdl2-dev
|
||||
```
|
||||
|
||||
Also, if there is some `.so` files in the Aquaria original game folder (`libgcc_s.so.1`, `libopenal.so.1`,
|
||||
Also, if there are certain `.so` files in the original Aquaria game folder (`libgcc_s.so.1`, `libopenal.so.1`,
|
||||
`libSDL-1.2.so.0` and `libstdc++.so.6`), you should remove them from the Aquaria Randomizer game folder. Those are
|
||||
old libraries that will not work on the recent build of the randomizer.
|
||||
|
||||
@@ -106,7 +107,7 @@ or, if the room has a password:
|
||||
./aquaria_randomizer --name YourName --server theServer:thePort --password thePassword
|
||||
```
|
||||
|
||||
Note: If you have a permission denied error when using the command line, you can use this command line to be
|
||||
Note: If you get a permission denied error when using the command line, you can use this command to be
|
||||
sure that your executable has executable permission:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -25,7 +25,7 @@ after_home_water_locations = [
|
||||
"Open water top right area, bulb in the turtle room",
|
||||
"Open water top right area, Transturtle",
|
||||
"Open water bottom left area, bulb behind the chomper fish",
|
||||
"Open water bottom left area, bulb inside the downest fish pass",
|
||||
"Open water bottom left area, bulb inside the lowest fish pass",
|
||||
"Open water skeleton path, bulb close to the right exit",
|
||||
"Open water skeleton path, bulb behind the chomper fish",
|
||||
"Open water skeleton path, King skull",
|
||||
@@ -82,7 +82,7 @@ after_home_water_locations = [
|
||||
"Mithalas cathedral, third urn in the path behind the flesh vein",
|
||||
"Mithalas cathedral, one of the urns in the top right room",
|
||||
"Mithalas cathedral, Mithalan Dress",
|
||||
"Mithalas cathedral right area, urn bellow the left entrance",
|
||||
"Mithalas cathedral right area, urn below the left entrance",
|
||||
"Cathedral underground, bulb in the center part",
|
||||
"Cathedral underground, first bulb in the top left part",
|
||||
"Cathedral underground, second bulb in the top left part",
|
||||
@@ -178,7 +178,7 @@ after_home_water_locations = [
|
||||
"The body main area, bulb on the main path blocking tube",
|
||||
"The body left area, first bulb in the top face room",
|
||||
"The body left area, second bulb in the top face room",
|
||||
"The body left area, bulb bellow the water stream",
|
||||
"The body left area, bulb below the water stream",
|
||||
"The body left area, bulb in the top path to the top face room",
|
||||
"The body left area, bulb in the bottom face room",
|
||||
"The body right area, bulb in the top face room",
|
||||
|
||||
@@ -18,7 +18,7 @@ class BindSongAccessTest(AquariaTestBase):
|
||||
"""Test locations that require Bind song"""
|
||||
locations = [
|
||||
"Verse cave right area, Big Seed",
|
||||
"Home water, bulb in the path bellow Nautilus Prime",
|
||||
"Home water, bulb in the path below Nautilus Prime",
|
||||
"Home water, bulb in the bottom left room",
|
||||
"Home water, Nautilus Egg",
|
||||
"Song cave, Verse egg",
|
||||
|
||||
@@ -24,7 +24,7 @@ class BindSongOptionAccessTest(AquariaTestBase):
|
||||
"Song cave, bulb under the rock close to the song door",
|
||||
"Song cave, bulb under the rock in the path to the singing statues",
|
||||
"Naija's home, bulb under the rock at the right of the main path",
|
||||
"Home water, bulb in the path bellow Nautilus Prime",
|
||||
"Home water, bulb in the path below Nautilus Prime",
|
||||
"Home water, bulb in the bottom left room",
|
||||
"Home water, Nautilus Egg",
|
||||
"Song cave, Verse egg",
|
||||
|
||||
@@ -39,7 +39,7 @@ class EnergyFormAccessTest(AquariaTestBase):
|
||||
"Mithalas cathedral, third urn in the path behind the flesh vein",
|
||||
"Mithalas cathedral, one of the urns in the top right room",
|
||||
"Mithalas cathedral, Mithalan Dress",
|
||||
"Mithalas cathedral right area, urn bellow the left entrance",
|
||||
"Mithalas cathedral right area, urn below the left entrance",
|
||||
"Cathedral boss area, beating Mithalan God",
|
||||
"Kelp Forest top left area, bulb close to the Verse egg",
|
||||
"Kelp forest top left area, Verse egg",
|
||||
@@ -67,7 +67,6 @@ class EnergyFormAccessTest(AquariaTestBase):
|
||||
"First secret",
|
||||
"Sunken City cleared",
|
||||
"Objective complete",
|
||||
|
||||
]
|
||||
items = [["Energy form"]]
|
||||
self.assertAccessDependency(locations, items)
|
||||
@@ -1,31 +0,0 @@
|
||||
"""
|
||||
Author: Louis M
|
||||
Date: Thu, 18 Apr 2024 18:45:56 +0000
|
||||
Description: Unit test used to test accessibility of locations with and without the bind song (with the early
|
||||
energy form option)
|
||||
"""
|
||||
|
||||
from worlds.aquaria.test import AquariaTestBase, after_home_water_locations
|
||||
|
||||
|
||||
class EnergyFormAccessTest(AquariaTestBase):
|
||||
"""Unit test used to test accessibility of locations with and without the energy form"""
|
||||
options = {
|
||||
"early_energy_form": True,
|
||||
}
|
||||
|
||||
def test_energy_form_location(self) -> None:
|
||||
"""Test locations that require Energy form with early energy song enable"""
|
||||
locations = [
|
||||
"Home water, Nautilus Egg",
|
||||
"Naija's home, bulb after the energy door",
|
||||
"Energy temple first area, bulb in the bottom room blocked by a rock",
|
||||
"Energy temple second area, bulb under the rock",
|
||||
"Energy temple bottom entrance, Krotite armor",
|
||||
"Energy temple third area, bulb in the bottom path",
|
||||
"Energy temple boss area, Fallen god tooth",
|
||||
"Energy temple blaster room, Blaster egg",
|
||||
*after_home_water_locations
|
||||
]
|
||||
items = [["Energy form"]]
|
||||
self.assertAccessDependency(locations, items)
|
||||
@@ -21,7 +21,7 @@ class FishFormAccessTest(AquariaTestBase):
|
||||
"Mithalas city, urn inside a home fish pass",
|
||||
"Kelp Forest top right area, bulb in the top fish pass",
|
||||
"The veil bottom area, Verse egg",
|
||||
"Open water bottom left area, bulb inside the downest fish pass",
|
||||
"Open water bottom left area, bulb inside the lowest fish pass",
|
||||
"Kelp Forest top left area, bulb close to the Verse egg",
|
||||
"Kelp forest top left area, Verse egg",
|
||||
"Mermog cave, bulb in the left part of the cave",
|
||||
|
||||
@@ -27,7 +27,7 @@ class LiAccessTest(AquariaTestBase):
|
||||
"The body main area, bulb on the main path blocking tube",
|
||||
"The body left area, first bulb in the top face room",
|
||||
"The body left area, second bulb in the top face room",
|
||||
"The body left area, bulb bellow the water stream",
|
||||
"The body left area, bulb below the water stream",
|
||||
"The body left area, bulb in the top path to the top face room",
|
||||
"The body left area, bulb in the bottom face room",
|
||||
"The body right area, bulb in the top face room",
|
||||
|
||||
@@ -41,7 +41,7 @@ class NatureFormAccessTest(AquariaTestBase):
|
||||
"The body main area, bulb on the main path blocking tube",
|
||||
"The body left area, first bulb in the top face room",
|
||||
"The body left area, second bulb in the top face room",
|
||||
"The body left area, bulb bellow the water stream",
|
||||
"The body left area, bulb below the water stream",
|
||||
"The body left area, bulb in the top path to the top face room",
|
||||
"The body left area, bulb in the bottom face room",
|
||||
"The body right area, bulb in the top face room",
|
||||
|
||||
@@ -9,7 +9,9 @@ else:
|
||||
|
||||
|
||||
class Logic(Choice):
|
||||
"""Choose the logic used by the randomizer."""
|
||||
"""
|
||||
Choose the logic used by the randomizer.
|
||||
"""
|
||||
display_name = "Logic"
|
||||
option_glitchless = 0
|
||||
option_glitched = 1
|
||||
@@ -17,26 +19,38 @@ class Logic(Choice):
|
||||
|
||||
|
||||
class SkipIntro(DefaultOnToggle):
|
||||
"""Skips escaping the police station.
|
||||
Graffiti spots tagged during the intro will not unlock items."""
|
||||
"""
|
||||
Skips escaping the police station.
|
||||
|
||||
Graffiti spots tagged during the intro will not unlock items.
|
||||
"""
|
||||
display_name = "Skip Intro"
|
||||
|
||||
|
||||
class SkipDreams(Toggle):
|
||||
"""Skips the dream sequences at the end of each chapter.
|
||||
This can be changed later in the options menu inside the Archipelago phone app."""
|
||||
"""
|
||||
Skips the dream sequences at the end of each chapter.
|
||||
|
||||
This can be changed later in the options menu inside the Archipelago phone app.
|
||||
"""
|
||||
display_name = "Skip Dreams"
|
||||
|
||||
|
||||
class SkipHands(Toggle):
|
||||
"""Skips spraying the lion statue hands after the dream in Chapter 5."""
|
||||
"""
|
||||
Skips spraying the lion statue hands after the dream in Chapter 5.
|
||||
"""
|
||||
display_name = "Skip Statue Hands"
|
||||
|
||||
|
||||
class TotalRep(Range):
|
||||
"""Change the total amount of REP in your world.
|
||||
"""
|
||||
Change the total amount of REP in your world.
|
||||
|
||||
At least 960 REP is needed to finish the game.
|
||||
Will be rounded to the nearest number divisible by 8."""
|
||||
|
||||
Will be rounded to the nearest number divisible by 8.
|
||||
"""
|
||||
display_name = "Total REP"
|
||||
range_start = 1000
|
||||
range_end = 2000
|
||||
@@ -74,12 +88,16 @@ class TotalRep(Range):
|
||||
|
||||
|
||||
class EndingREP(Toggle):
|
||||
"""Changes the final boss to require 1000 REP instead of 960 REP to start."""
|
||||
"""
|
||||
Changes the final boss to require 1000 REP instead of 960 REP to start.
|
||||
"""
|
||||
display_name = "Extra REP Required"
|
||||
|
||||
|
||||
class StartStyle(Choice):
|
||||
"""Choose which movestyle to start with."""
|
||||
"""
|
||||
Choose which movestyle to start with.
|
||||
"""
|
||||
display_name = "Starting Movestyle"
|
||||
option_skateboard = 2
|
||||
option_inline_skates = 3
|
||||
@@ -88,17 +106,22 @@ class StartStyle(Choice):
|
||||
|
||||
|
||||
class LimitedGraffiti(Toggle):
|
||||
"""Each graffiti design can only be used a limited number of times before being removed from your inventory.
|
||||
In some cases, such as completing a dream, using graffiti to defeat enemies, or spraying over your own graffiti,
|
||||
uses will not be counted.
|
||||
If enabled, doing graffiti is disabled during crew battles, to prevent softlocking."""
|
||||
"""
|
||||
Each graffiti design can only be used a limited number of times before being removed from your inventory.
|
||||
|
||||
In some cases, such as completing a dream, using graffiti to defeat enemies, or spraying over your own graffiti, uses will not be counted.
|
||||
|
||||
If enabled, doing graffiti is disabled during crew battles, to prevent softlocking.
|
||||
"""
|
||||
display_name = "Limited Graffiti"
|
||||
|
||||
|
||||
class SGraffiti(Choice):
|
||||
"""Choose if small graffiti should be separate, meaning that you will need to switch characters every time you run
|
||||
out, or combined, meaning that unlocking new characters will add 5 uses that any character can use.
|
||||
Has no effect if Limited Graffiti is disabled."""
|
||||
"""
|
||||
Choose if small graffiti should be separate, meaning that you will need to switch characters every time you run out, or combined, meaning that unlocking new characters will add 5 uses that any character can use.
|
||||
|
||||
Has no effect if Limited Graffiti is disabled.
|
||||
"""
|
||||
display_name = "Small Graffiti Uses"
|
||||
option_separate = 0
|
||||
option_combined = 1
|
||||
@@ -106,19 +129,27 @@ class SGraffiti(Choice):
|
||||
|
||||
|
||||
class JunkPhotos(Toggle):
|
||||
"""Skip taking pictures of Polo for items."""
|
||||
"""
|
||||
Skip taking pictures of Polo for items.
|
||||
"""
|
||||
display_name = "Skip Polo Photos"
|
||||
|
||||
|
||||
class DontSavePhotos(Toggle):
|
||||
"""Photos taken with the Camera app will not be saved.
|
||||
This can be changed later in the options menu inside the Archipelago phone app."""
|
||||
"""
|
||||
Photos taken with the Camera app will not be saved.
|
||||
|
||||
This can be changed later in the options menu inside the Archipelago phone app.
|
||||
"""
|
||||
display_name = "Don't Save Photos"
|
||||
|
||||
|
||||
class ScoreDifficulty(Choice):
|
||||
"""Alters the score required to win score challenges and crew battles.
|
||||
This can be changed later in the options menu inside the Archipelago phone app."""
|
||||
"""
|
||||
Alters the score required to win score challenges and crew battles.
|
||||
|
||||
This can be changed later in the options menu inside the Archipelago phone app.
|
||||
"""
|
||||
display_name = "Score Difficulty"
|
||||
option_normal = 0
|
||||
option_medium = 1
|
||||
@@ -129,10 +160,14 @@ class ScoreDifficulty(Choice):
|
||||
|
||||
|
||||
class DamageMultiplier(Range):
|
||||
"""Multiplies all damage received.
|
||||
"""
|
||||
Multiplies all damage received.
|
||||
|
||||
At 3x, most damage will OHKO the player, including falling into pits.
|
||||
At 6x, all damage will OHKO the player.
|
||||
This can be changed later in the options menu inside the Archipelago phone app."""
|
||||
|
||||
This can be changed later in the options menu inside the Archipelago phone app.
|
||||
"""
|
||||
display_name = "Damage Multiplier"
|
||||
range_start = 1
|
||||
range_end = 6
|
||||
@@ -140,8 +175,11 @@ class DamageMultiplier(Range):
|
||||
|
||||
|
||||
class BRCDeathLink(DeathLink):
|
||||
"""When you die, everyone dies. The reverse is also true.
|
||||
This can be changed later in the options menu inside the Archipelago phone app."""
|
||||
"""
|
||||
When you die, everyone dies. The reverse is also true.
|
||||
|
||||
This can be changed later in the options menu inside the Archipelago phone app.
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import Choice, Range, Toggle, DeathLink, PerGameCommonOptions
|
||||
from Options import Choice, Range, Toggle, DeathLink, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class DeathLinkAmnesty(Range):
|
||||
@@ -47,7 +47,9 @@ class MoveShuffle(Toggle):
|
||||
- Air Dash
|
||||
- Skid Jump
|
||||
- Climb
|
||||
|
||||
NOTE: Having Move Shuffle and Standard Logic Difficulty will guarantee that one of the four Move items will be immediately accessible
|
||||
|
||||
WARNING: Combining Move Shuffle and Hard Logic Difficulty can require very difficult tricks
|
||||
"""
|
||||
display_name = "Move Shuffle"
|
||||
@@ -75,7 +77,9 @@ class Carsanity(Toggle):
|
||||
class BadelineChaserSource(Choice):
|
||||
"""
|
||||
What type of action causes more Badeline Chasers to start spawning
|
||||
|
||||
Locations: The number of locations you've checked contributes to Badeline Chasers
|
||||
|
||||
Strawberries: The number of Strawberry items you've received contributes to Badeline Chasers
|
||||
"""
|
||||
display_name = "Badeline Chaser Source"
|
||||
@@ -86,7 +90,9 @@ class BadelineChaserSource(Choice):
|
||||
class BadelineChaserFrequency(Range):
|
||||
"""
|
||||
How many of the `Badeline Chaser Source` actions must occur to make each Badeline Chaser start spawning
|
||||
|
||||
NOTE: Choosing `0` disables Badeline Chasers entirely
|
||||
|
||||
WARNING: Turning on Badeline Chasers alongside Move Shuffle could result in extremely difficult situations
|
||||
"""
|
||||
display_name = "Badeline Chaser Frequency"
|
||||
@@ -104,6 +110,24 @@ class BadelineChaserSpeed(Range):
|
||||
default = 3
|
||||
|
||||
|
||||
celeste_64_option_groups = [
|
||||
OptionGroup("Goal Options", [
|
||||
TotalStrawberries,
|
||||
StrawberriesRequiredPercentage,
|
||||
]),
|
||||
OptionGroup("Sanity Options", [
|
||||
Friendsanity,
|
||||
Signsanity,
|
||||
Carsanity,
|
||||
]),
|
||||
OptionGroup("Badeline Chasers", [
|
||||
BadelineChaserSource,
|
||||
BadelineChaserFrequency,
|
||||
BadelineChaserSpeed,
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Celeste64Options(PerGameCommonOptions):
|
||||
death_link: DeathLink
|
||||
|
||||
@@ -7,7 +7,7 @@ from .Items import Celeste64Item, unlockable_item_data_table, move_item_data_tab
|
||||
from .Locations import Celeste64Location, strawberry_location_data_table, friend_location_data_table,\
|
||||
sign_location_data_table, car_location_data_table, location_table
|
||||
from .Names import ItemName, LocationName
|
||||
from .Options import Celeste64Options
|
||||
from .Options import Celeste64Options, celeste_64_option_groups
|
||||
|
||||
|
||||
class Celeste64WebWorld(WebWorld):
|
||||
@@ -24,6 +24,8 @@ class Celeste64WebWorld(WebWorld):
|
||||
|
||||
tutorials = [setup_en]
|
||||
|
||||
option_groups = celeste_64_option_groups
|
||||
|
||||
|
||||
class Celeste64World(World):
|
||||
"""Relive the magic of Celeste Mountain alongside Madeline in this small, heartfelt 3D platformer.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
import typing
|
||||
|
||||
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList, PerGameCommonOptions
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
"""
|
||||
Determines the goal of the seed
|
||||
|
||||
Knautilus: Scuttle the Knautilus in Krematoa and defeat Baron K. Roolenstein
|
||||
|
||||
Banana Bird Hunt: Find a certain number of Banana Birds and rescue their mother
|
||||
"""
|
||||
display_name = "Goal"
|
||||
@@ -26,6 +28,7 @@ class IncludeTradeSequence(Toggle):
|
||||
class DKCoinsForGyrocopter(Range):
|
||||
"""
|
||||
How many DK Coins are needed to unlock the Gyrocopter
|
||||
|
||||
Note: Achieving this number before unlocking the Turbo Ski will cause the game to grant you a
|
||||
one-time upgrade to the next non-unlocked boat, until you return to Funky. Logic does not assume
|
||||
that you will use this.
|
||||
@@ -93,6 +96,7 @@ class LevelShuffle(Toggle):
|
||||
class Difficulty(Choice):
|
||||
"""
|
||||
Which Difficulty Level to use
|
||||
|
||||
NORML: The Normal Difficulty
|
||||
HARDR: Many DK Barrels are removed
|
||||
TUFST: Most DK Barrels and all Midway Barrels are removed
|
||||
@@ -159,19 +163,40 @@ class StartingLifeCount(Range):
|
||||
default = 5
|
||||
|
||||
|
||||
dkc3_option_groups = [
|
||||
OptionGroup("Goal Options", [
|
||||
Goal,
|
||||
KrematoaBonusCoinCost,
|
||||
PercentageOfExtraBonusCoins,
|
||||
NumberOfBananaBirds,
|
||||
PercentageOfBananaBirds,
|
||||
]),
|
||||
OptionGroup("Aesthetics", [
|
||||
Autosave,
|
||||
MERRY,
|
||||
MusicShuffle,
|
||||
KongPaletteSwap,
|
||||
StartingLifeCount,
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class DKC3Options(PerGameCommonOptions):
|
||||
#death_link: DeathLink # Disabled
|
||||
goal: Goal
|
||||
#include_trade_sequence: IncludeTradeSequence # Disabled
|
||||
dk_coins_for_gyrocopter: DKCoinsForGyrocopter
|
||||
|
||||
goal: Goal
|
||||
krematoa_bonus_coin_cost: KrematoaBonusCoinCost
|
||||
percentage_of_extra_bonus_coins: PercentageOfExtraBonusCoins
|
||||
number_of_banana_birds: NumberOfBananaBirds
|
||||
percentage_of_banana_birds: PercentageOfBananaBirds
|
||||
|
||||
dk_coins_for_gyrocopter: DKCoinsForGyrocopter
|
||||
kongsanity: KONGsanity
|
||||
level_shuffle: LevelShuffle
|
||||
difficulty: Difficulty
|
||||
|
||||
autosave: Autosave
|
||||
merry: MERRY
|
||||
music_shuffle: MusicShuffle
|
||||
|
||||
@@ -4,20 +4,21 @@ import typing
|
||||
import math
|
||||
import threading
|
||||
|
||||
import settings
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from Options import PerGameCommonOptions
|
||||
from .Items import DKC3Item, ItemData, item_table, inventory_table, junk_table
|
||||
from .Locations import DKC3Location, all_locations, setup_locations
|
||||
from .Options import DKC3Options
|
||||
from .Regions import create_regions, connect_regions
|
||||
from .Levels import level_list
|
||||
from .Rules import set_rules
|
||||
from .Names import ItemName, LocationName
|
||||
from .Client import DKC3SNIClient
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from .Rom import LocalRom, patch_rom, get_base_rom_path, DKC3DeltaPatch
|
||||
import Patch
|
||||
import settings
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
|
||||
from .Client import DKC3SNIClient
|
||||
from .Items import DKC3Item, ItemData, item_table, inventory_table, junk_table
|
||||
from .Levels import level_list
|
||||
from .Locations import DKC3Location, all_locations, setup_locations
|
||||
from .Names import ItemName, LocationName
|
||||
from .Options import DKC3Options, dkc3_option_groups
|
||||
from .Regions import create_regions, connect_regions
|
||||
from .Rom import LocalRom, patch_rom, get_base_rom_path, DKC3DeltaPatch
|
||||
from .Rules import set_rules
|
||||
|
||||
|
||||
class DK3Settings(settings.Group):
|
||||
@@ -41,9 +42,11 @@ class DKC3Web(WebWorld):
|
||||
"setup/en",
|
||||
["PoryGone"]
|
||||
)
|
||||
|
||||
|
||||
tutorials = [setup_en]
|
||||
|
||||
option_groups = dkc3_option_groups
|
||||
|
||||
|
||||
class DKC3World(World):
|
||||
"""
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
from dataclasses import dataclass
|
||||
from Options import Choice, Removed, Toggle, DefaultOnToggle, DeathLink, PerGameCommonOptions
|
||||
|
||||
|
||||
class PartyShuffle(Toggle):
|
||||
"""Shuffles party members into the pool.
|
||||
Note that enabling this can potentially increase both the difficulty and length of a run."""
|
||||
"""
|
||||
Shuffles party members into the item pool.
|
||||
|
||||
Note that enabling this can significantly increase both the difficulty and length of a run.
|
||||
"""
|
||||
display_name = "Shuffle Party Members"
|
||||
|
||||
|
||||
class GestureShuffle(Choice):
|
||||
"""Choose where gestures will appear in the item pool."""
|
||||
"""
|
||||
Choose where gestures will appear in the item pool.
|
||||
"""
|
||||
display_name = "Shuffle Gestures"
|
||||
option_anywhere = 0
|
||||
option_tvs_only = 1
|
||||
option_default_locations = 2
|
||||
default = 0
|
||||
|
||||
|
||||
class MedallionShuffle(Toggle):
|
||||
"""Shuffles red medallions into the pool."""
|
||||
"""
|
||||
Shuffles red medallions into the item pool.
|
||||
"""
|
||||
display_name = "Shuffle Red Medallions"
|
||||
|
||||
|
||||
class StartLocation(Choice):
|
||||
"""Select the starting location from 1 of 4 positions."""
|
||||
"""
|
||||
Select the starting location from 1 of 4 positions.
|
||||
"""
|
||||
display_name = "Start Location"
|
||||
option_waynehouse = 0
|
||||
option_viewaxs_edifice = 1
|
||||
@@ -35,14 +48,23 @@ class StartLocation(Choice):
|
||||
return "TV Island"
|
||||
return super().get_option_name(value)
|
||||
|
||||
|
||||
class ExtraLogic(DefaultOnToggle):
|
||||
"""Include some extra items in logic (CHARGE UP, 1x PAPER CUP) to prevent the game from becoming too difficult."""
|
||||
"""
|
||||
Include some extra items in logic (CHARGE UP, 1x PAPER CUP) to prevent the game from becoming too difficult.
|
||||
"""
|
||||
display_name = "Extra Items in Logic"
|
||||
|
||||
|
||||
class Hylics2DeathLink(DeathLink):
|
||||
"""When you die, everyone dies. The reverse is also true.
|
||||
"""
|
||||
When you die, everyone dies. The reverse is also true.
|
||||
|
||||
Note that this also includes death by using the PERISH gesture.
|
||||
Can be toggled via in-game console command "/deathlink"."""
|
||||
|
||||
Can be toggled via in-game console command "/deathlink".
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Hylics2Options(PerGameCommonOptions):
|
||||
|
||||
@@ -48,10 +48,6 @@ class MLSSClient(BizHawkClient):
|
||||
rom_name_bytes = await bizhawk.read(ctx.bizhawk_ctx, [(0xA0, 14, "ROM")])
|
||||
rom_name = bytes([byte for byte in rom_name_bytes[0] if byte != 0]).decode("UTF-8")
|
||||
if not rom_name.startswith("MARIO&LUIGIUA8"):
|
||||
logger.info(
|
||||
"ERROR: You have opened a game that is not Mario & Luigi Superstar Saga. "
|
||||
"Please make sure you are opening the correct ROM."
|
||||
)
|
||||
return False
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
"""
|
||||
Determines the goal of the seed
|
||||
|
||||
Biolizard: Finish Cannon's Core and defeat the Biolizard and Finalhazard
|
||||
|
||||
Chaos Emerald Hunt: Find the Seven Chaos Emeralds and reach Green Hill Zone
|
||||
|
||||
Finalhazard Chaos Emerald Hunt: Find the Seven Chaos Emeralds and reach Green Hill Zone, then defeat Finalhazard
|
||||
|
||||
Grand Prix: Win every race in Kart Race Mode (all standard levels are disabled)
|
||||
|
||||
Boss Rush: Beat all of the bosses in the Boss Rush, ending with Finalhazard
|
||||
|
||||
Cannon's Core Boss Rush: Beat Cannon's Core, then beat all of the bosses in the Boss Rush, ending with Finalhazard
|
||||
|
||||
Boss Rush Chaos Emerald Hunt: Find the Seven Chaos Emeralds, then beat all of the bosses in the Boss Rush, ending with Finalhazard
|
||||
|
||||
Chaos Chao: Raise a Chaos Chao to win
|
||||
"""
|
||||
display_name = "Goal"
|
||||
@@ -46,9 +54,13 @@ class MissionShuffle(Toggle):
|
||||
class BossRushShuffle(Choice):
|
||||
"""
|
||||
Determines how bosses in Boss Rush Mode are shuffled
|
||||
|
||||
Vanilla: Bosses appear in the Vanilla ordering
|
||||
|
||||
Shuffled: The same bosses appear, but in a random order
|
||||
|
||||
Chaos: Each boss is randomly chosen separately (one will always be King Boom Boo)
|
||||
|
||||
Singularity: One boss is chosen and placed in every slot (one will always be replaced with King Boom Boo)
|
||||
"""
|
||||
display_name = "Boss Rush Shuffle"
|
||||
@@ -196,9 +208,13 @@ class Keysanity(Toggle):
|
||||
class Whistlesanity(Choice):
|
||||
"""
|
||||
Determines whether whistling at various spots grants checks
|
||||
|
||||
None: No Whistle Spots grant checks
|
||||
|
||||
Pipes: Whistling at Pipes grants checks (97 Locations)
|
||||
|
||||
Hidden: Whistling at Hidden Whistle Spots grants checks (32 Locations)
|
||||
|
||||
Both: Whistling at both Pipes and Hidden Whistle Spots grants checks (129 Locations)
|
||||
"""
|
||||
display_name = "Whistlesanity"
|
||||
@@ -228,8 +244,9 @@ class Omosanity(Toggle):
|
||||
class Animalsanity(Toggle):
|
||||
"""
|
||||
Determines whether unique counts of animals grant checks.
|
||||
ALL animals must be collected in a single run of a mission to get all checks.
|
||||
(421 Locations)
|
||||
|
||||
ALL animals must be collected in a single run of a mission to get all checks.
|
||||
"""
|
||||
display_name = "Animalsanity"
|
||||
|
||||
@@ -237,8 +254,11 @@ class Animalsanity(Toggle):
|
||||
class KartRaceChecks(Choice):
|
||||
"""
|
||||
Determines whether Kart Race Mode grants checks
|
||||
|
||||
None: No Kart Races grant checks
|
||||
|
||||
Mini: Each Kart Race difficulty must be beaten only once
|
||||
|
||||
Full: Every Character must separately beat each Kart Race difficulty
|
||||
"""
|
||||
display_name = "Kart Race Checks"
|
||||
@@ -271,8 +291,11 @@ class NumberOfLevelGates(Range):
|
||||
class LevelGateDistribution(Choice):
|
||||
"""
|
||||
Determines how levels are distributed between level gate regions
|
||||
|
||||
Early: Earlier regions will have more levels than later regions
|
||||
|
||||
Even: Levels will be evenly distributed between all regions
|
||||
|
||||
Late: Later regions will have more levels than earlier regions
|
||||
"""
|
||||
display_name = "Level Gate Distribution"
|
||||
@@ -296,7 +319,9 @@ class LevelGateCosts(Choice):
|
||||
class MaximumEmblemCap(Range):
|
||||
"""
|
||||
Determines the maximum number of emblems that can be in the item pool.
|
||||
|
||||
If fewer available locations exist in the pool than this number, the number of available locations will be used instead.
|
||||
|
||||
Gate and Cannon's Core costs will be calculated based off of that number.
|
||||
"""
|
||||
display_name = "Max Emblem Cap"
|
||||
@@ -321,9 +346,13 @@ class RequiredRank(Choice):
|
||||
class ChaoRaceDifficulty(Choice):
|
||||
"""
|
||||
Determines the number of Chao Race difficulty levels included. Easier difficulty settings means fewer Chao Race checks
|
||||
|
||||
None: No Chao Races have checks
|
||||
|
||||
Beginner: Beginner Races
|
||||
|
||||
Intermediate: Beginner, Challenge, Hero, and Dark Races
|
||||
|
||||
Expert: Beginner, Challenge, Hero, Dark and Jewel Races
|
||||
"""
|
||||
display_name = "Chao Race Difficulty"
|
||||
@@ -350,9 +379,10 @@ class ChaoKarateDifficulty(Choice):
|
||||
class ChaoStadiumChecks(Choice):
|
||||
"""
|
||||
Determines which Chao Stadium activities grant checks
|
||||
|
||||
All: Each individual race and karate fight grants a check
|
||||
Prize: Only the races which grant Chao Toys grant checks (final race of each Beginner and Jewel cup, 4th, 8th, and
|
||||
12th Challenge Races, 2nd and 4th Hero and Dark Races, final fight of each Karate difficulty)
|
||||
|
||||
Prize: Only the races which grant Chao Toys grant checks (final race of each Beginner and Jewel cup, 4th, 8th, and 12th Challenge Races, 2nd and 4th Hero and Dark Races, final fight of each Karate difficulty)
|
||||
"""
|
||||
display_name = "Chao Stadium Checks"
|
||||
option_all = 0
|
||||
@@ -374,6 +404,7 @@ class ChaoStats(Range):
|
||||
class ChaoStatsFrequency(Range):
|
||||
"""
|
||||
Determines how many levels in each Chao Stat grant checks (up to the maximum set in the `chao_stats` option)
|
||||
|
||||
`1` means every level is included, `2` means every other level is included, `3` means every third, and so on
|
||||
"""
|
||||
display_name = "Chao Stats Frequency"
|
||||
@@ -408,8 +439,11 @@ class ChaoKindergarten(Choice):
|
||||
"""
|
||||
Determines whether learning the lessons from the Kindergarten Classroom grants checks
|
||||
(WARNING: VERY SLOW)
|
||||
|
||||
None: No Kindergarten classes have checks
|
||||
|
||||
Basics: One class from each category (Drawing, Dance, Song, and Instrument) is a check (4 Locations)
|
||||
|
||||
Full: Every class is a check (23 Locations)
|
||||
"""
|
||||
display_name = "Chao Kindergarten Checks"
|
||||
@@ -443,8 +477,8 @@ class BlackMarketUnlockCosts(Choice):
|
||||
class BlackMarketPriceMultiplier(Range):
|
||||
"""
|
||||
Determines how many rings the Black Market items cost
|
||||
The base ring costs of items in the Black Market range from 50-100,
|
||||
and are then multiplied by this value
|
||||
|
||||
The base ring costs of items in the Black Market range from 50-100, and are then multiplied by this value
|
||||
"""
|
||||
display_name = "Black Market Price Multiplier"
|
||||
range_start = 0
|
||||
@@ -469,7 +503,9 @@ class ChaoEntranceRandomization(Toggle):
|
||||
class RequiredCannonsCoreMissions(Choice):
|
||||
"""
|
||||
Determines how many Cannon's Core missions must be completed (for Biolizard or Cannon's Core goals)
|
||||
|
||||
First: Only the first mission must be completed
|
||||
|
||||
All Active: All active Cannon's Core missions must be completed
|
||||
"""
|
||||
display_name = "Required Cannon's Core Missions"
|
||||
@@ -665,8 +701,11 @@ class CannonsCoreMission5(DefaultOnToggle):
|
||||
class RingLoss(Choice):
|
||||
"""
|
||||
How taking damage is handled
|
||||
|
||||
Classic: You lose all of your rings when hit
|
||||
|
||||
Modern: You lose 20 rings when hit
|
||||
|
||||
OHKO: You die immediately when hit (NOTE: Some Hard Logic tricks may require damage boosts!)
|
||||
"""
|
||||
display_name = "Ring Loss"
|
||||
@@ -693,9 +732,13 @@ class RingLink(Toggle):
|
||||
class SADXMusic(Choice):
|
||||
"""
|
||||
Whether the randomizer will include Sonic Adventure DX Music in the music pool
|
||||
|
||||
SA2B: Only SA2B music will be played
|
||||
|
||||
SADX: Only SADX music will be played
|
||||
|
||||
Both: Both SA2B and SADX music will be played
|
||||
|
||||
NOTE: This option requires the player to own a PC copy of SADX and to follow the addition steps in the setup guide.
|
||||
"""
|
||||
display_name = "SADX Music"
|
||||
@@ -715,9 +758,13 @@ class SADXMusic(Choice):
|
||||
class MusicShuffle(Choice):
|
||||
"""
|
||||
What type of Music Shuffle is used
|
||||
|
||||
None: No music is shuffled.
|
||||
|
||||
Levels: Level music is shuffled.
|
||||
|
||||
Full: Level, Menu, and Additional music is shuffled.
|
||||
|
||||
Singularity: Level, Menu, and Additional music is all replaced with a single random song.
|
||||
"""
|
||||
display_name = "Music Shuffle Type"
|
||||
@@ -731,10 +778,15 @@ class MusicShuffle(Choice):
|
||||
class VoiceShuffle(Choice):
|
||||
"""
|
||||
What type of Voice Shuffle is used
|
||||
|
||||
None: No voices are shuffled.
|
||||
|
||||
Shuffled: Voices are shuffled.
|
||||
|
||||
Rude: Voices are shuffled, but some are replaced with rude words.
|
||||
|
||||
Chao: All voices are replaced with chao sounds.
|
||||
|
||||
Singularity: All voices are replaced with a single random voice.
|
||||
"""
|
||||
display_name = "Voice Shuffle Type"
|
||||
@@ -768,7 +820,9 @@ class Narrator(Choice):
|
||||
class LogicDifficulty(Choice):
|
||||
"""
|
||||
What set of Upgrade Requirement logic to use
|
||||
|
||||
Standard: The logic assumes the "intended" usage of Upgrades to progress through levels
|
||||
|
||||
Hard: Some simple skips or sequence breaks may be required
|
||||
"""
|
||||
display_name = "Logic Difficulty"
|
||||
@@ -777,96 +831,195 @@ class LogicDifficulty(Choice):
|
||||
default = 0
|
||||
|
||||
|
||||
sa2b_options: typing.Dict[str, type(Option)] = {
|
||||
"goal": Goal,
|
||||
sa2b_option_groups = [
|
||||
OptionGroup("General Options", [
|
||||
Goal,
|
||||
BossRushShuffle,
|
||||
LogicDifficulty,
|
||||
RequiredRank,
|
||||
MaximumEmblemCap,
|
||||
RingLoss,
|
||||
]),
|
||||
OptionGroup("Stages", [
|
||||
MissionShuffle,
|
||||
EmblemPercentageForCannonsCore,
|
||||
RequiredCannonsCoreMissions,
|
||||
NumberOfLevelGates,
|
||||
LevelGateCosts,
|
||||
LevelGateDistribution,
|
||||
]),
|
||||
OptionGroup("Sanity Options", [
|
||||
Keysanity,
|
||||
Whistlesanity,
|
||||
Beetlesanity,
|
||||
Omosanity,
|
||||
Animalsanity,
|
||||
KartRaceChecks,
|
||||
]),
|
||||
OptionGroup("Chao", [
|
||||
BlackMarketSlots,
|
||||
BlackMarketUnlockCosts,
|
||||
BlackMarketPriceMultiplier,
|
||||
ChaoRaceDifficulty,
|
||||
ChaoKarateDifficulty,
|
||||
ChaoStadiumChecks,
|
||||
ChaoAnimalParts,
|
||||
ChaoStats,
|
||||
ChaoStatsFrequency,
|
||||
ChaoStatsStamina,
|
||||
ChaoStatsHidden,
|
||||
ChaoKindergarten,
|
||||
ShuffleStartingChaoEggs,
|
||||
ChaoEntranceRandomization,
|
||||
]),
|
||||
OptionGroup("Junk and Traps", [
|
||||
JunkFillPercentage,
|
||||
TrapFillPercentage,
|
||||
OmochaoTrapWeight,
|
||||
TimestopTrapWeight,
|
||||
ConfusionTrapWeight,
|
||||
TinyTrapWeight,
|
||||
GravityTrapWeight,
|
||||
ExpositionTrapWeight,
|
||||
IceTrapWeight,
|
||||
SlowTrapWeight,
|
||||
CutsceneTrapWeight,
|
||||
ReverseTrapWeight,
|
||||
PongTrapWeight,
|
||||
MinigameTrapDifficulty,
|
||||
]),
|
||||
OptionGroup("Speed Missions", [
|
||||
SpeedMissionCount,
|
||||
SpeedMission2,
|
||||
SpeedMission3,
|
||||
SpeedMission4,
|
||||
SpeedMission5,
|
||||
]),
|
||||
OptionGroup("Mech Missions", [
|
||||
MechMissionCount,
|
||||
MechMission2,
|
||||
MechMission3,
|
||||
MechMission4,
|
||||
MechMission5,
|
||||
]),
|
||||
OptionGroup("Hunt Missions", [
|
||||
HuntMissionCount,
|
||||
HuntMission2,
|
||||
HuntMission3,
|
||||
HuntMission4,
|
||||
HuntMission5,
|
||||
]),
|
||||
OptionGroup("Kart Missions", [
|
||||
KartMissionCount,
|
||||
KartMission2,
|
||||
KartMission3,
|
||||
KartMission4,
|
||||
KartMission5,
|
||||
]),
|
||||
OptionGroup("Cannon's Core Missions", [
|
||||
CannonsCoreMissionCount,
|
||||
CannonsCoreMission2,
|
||||
CannonsCoreMission3,
|
||||
CannonsCoreMission4,
|
||||
CannonsCoreMission5,
|
||||
]),
|
||||
OptionGroup("Aesthetics", [
|
||||
SADXMusic,
|
||||
MusicShuffle,
|
||||
VoiceShuffle,
|
||||
Narrator,
|
||||
]),
|
||||
]
|
||||
|
||||
"mission_shuffle": MissionShuffle,
|
||||
"boss_rush_shuffle": BossRushShuffle,
|
||||
|
||||
"keysanity": Keysanity,
|
||||
"whistlesanity": Whistlesanity,
|
||||
"beetlesanity": Beetlesanity,
|
||||
"omosanity": Omosanity,
|
||||
"animalsanity": Animalsanity,
|
||||
"kart_race_checks": KartRaceChecks,
|
||||
@dataclass
|
||||
class SA2BOptions(PerGameCommonOptions):
|
||||
goal: Goal
|
||||
boss_rush_shuffle: BossRushShuffle
|
||||
logic_difficulty: LogicDifficulty
|
||||
required_rank: RequiredRank
|
||||
max_emblem_cap: MaximumEmblemCap
|
||||
ring_loss: RingLoss
|
||||
|
||||
"logic_difficulty": LogicDifficulty,
|
||||
"required_rank": RequiredRank,
|
||||
"required_cannons_core_missions": RequiredCannonsCoreMissions,
|
||||
mission_shuffle: MissionShuffle
|
||||
required_cannons_core_missions: RequiredCannonsCoreMissions
|
||||
emblem_percentage_for_cannons_core: EmblemPercentageForCannonsCore
|
||||
number_of_level_gates: NumberOfLevelGates
|
||||
level_gate_distribution: LevelGateDistribution
|
||||
level_gate_costs: LevelGateCosts
|
||||
|
||||
"emblem_percentage_for_cannons_core": EmblemPercentageForCannonsCore,
|
||||
"number_of_level_gates": NumberOfLevelGates,
|
||||
"level_gate_distribution": LevelGateDistribution,
|
||||
"level_gate_costs": LevelGateCosts,
|
||||
"max_emblem_cap": MaximumEmblemCap,
|
||||
keysanity: Keysanity
|
||||
whistlesanity: Whistlesanity
|
||||
beetlesanity: Beetlesanity
|
||||
omosanity: Omosanity
|
||||
animalsanity: Animalsanity
|
||||
kart_race_checks: KartRaceChecks
|
||||
|
||||
"chao_race_difficulty": ChaoRaceDifficulty,
|
||||
"chao_karate_difficulty": ChaoKarateDifficulty,
|
||||
"chao_stadium_checks": ChaoStadiumChecks,
|
||||
"chao_stats": ChaoStats,
|
||||
"chao_stats_frequency": ChaoStatsFrequency,
|
||||
"chao_stats_stamina": ChaoStatsStamina,
|
||||
"chao_stats_hidden": ChaoStatsHidden,
|
||||
"chao_animal_parts": ChaoAnimalParts,
|
||||
"chao_kindergarten": ChaoKindergarten,
|
||||
"black_market_slots": BlackMarketSlots,
|
||||
"black_market_unlock_costs": BlackMarketUnlockCosts,
|
||||
"black_market_price_multiplier": BlackMarketPriceMultiplier,
|
||||
"shuffle_starting_chao_eggs": ShuffleStartingChaoEggs,
|
||||
"chao_entrance_randomization": ChaoEntranceRandomization,
|
||||
black_market_slots: BlackMarketSlots
|
||||
black_market_unlock_costs: BlackMarketUnlockCosts
|
||||
black_market_price_multiplier: BlackMarketPriceMultiplier
|
||||
chao_race_difficulty: ChaoRaceDifficulty
|
||||
chao_karate_difficulty: ChaoKarateDifficulty
|
||||
chao_stadium_checks: ChaoStadiumChecks
|
||||
chao_animal_parts: ChaoAnimalParts
|
||||
chao_stats: ChaoStats
|
||||
chao_stats_frequency: ChaoStatsFrequency
|
||||
chao_stats_stamina: ChaoStatsStamina
|
||||
chao_stats_hidden: ChaoStatsHidden
|
||||
chao_kindergarten: ChaoKindergarten
|
||||
shuffle_starting_chao_eggs: ShuffleStartingChaoEggs
|
||||
chao_entrance_randomization: ChaoEntranceRandomization
|
||||
|
||||
"junk_fill_percentage": JunkFillPercentage,
|
||||
"trap_fill_percentage": TrapFillPercentage,
|
||||
"omochao_trap_weight": OmochaoTrapWeight,
|
||||
"timestop_trap_weight": TimestopTrapWeight,
|
||||
"confusion_trap_weight": ConfusionTrapWeight,
|
||||
"tiny_trap_weight": TinyTrapWeight,
|
||||
"gravity_trap_weight": GravityTrapWeight,
|
||||
"exposition_trap_weight": ExpositionTrapWeight,
|
||||
#"darkness_trap_weight": DarknessTrapWeight,
|
||||
"ice_trap_weight": IceTrapWeight,
|
||||
"slow_trap_weight": SlowTrapWeight,
|
||||
"cutscene_trap_weight": CutsceneTrapWeight,
|
||||
"reverse_trap_weight": ReverseTrapWeight,
|
||||
"pong_trap_weight": PongTrapWeight,
|
||||
"minigame_trap_difficulty": MinigameTrapDifficulty,
|
||||
junk_fill_percentage: JunkFillPercentage
|
||||
trap_fill_percentage: TrapFillPercentage
|
||||
omochao_trap_weight: OmochaoTrapWeight
|
||||
timestop_trap_weight: TimestopTrapWeight
|
||||
confusion_trap_weight: ConfusionTrapWeight
|
||||
tiny_trap_weight: TinyTrapWeight
|
||||
gravity_trap_weight: GravityTrapWeight
|
||||
exposition_trap_weight: ExpositionTrapWeight
|
||||
#darkness_trap_weight: DarknessTrapWeight
|
||||
ice_trap_weight: IceTrapWeight
|
||||
slow_trap_weight: SlowTrapWeight
|
||||
cutscene_trap_weight: CutsceneTrapWeight
|
||||
reverse_trap_weight: ReverseTrapWeight
|
||||
pong_trap_weight: PongTrapWeight
|
||||
minigame_trap_difficulty: MinigameTrapDifficulty
|
||||
|
||||
"sadx_music": SADXMusic,
|
||||
"music_shuffle": MusicShuffle,
|
||||
"voice_shuffle": VoiceShuffle,
|
||||
"narrator": Narrator,
|
||||
"ring_loss": RingLoss,
|
||||
sadx_music: SADXMusic
|
||||
music_shuffle: MusicShuffle
|
||||
voice_shuffle: VoiceShuffle
|
||||
narrator: Narrator
|
||||
|
||||
"speed_mission_count": SpeedMissionCount,
|
||||
"speed_mission_2": SpeedMission2,
|
||||
"speed_mission_3": SpeedMission3,
|
||||
"speed_mission_4": SpeedMission4,
|
||||
"speed_mission_5": SpeedMission5,
|
||||
speed_mission_count: SpeedMissionCount
|
||||
speed_mission_2: SpeedMission2
|
||||
speed_mission_3: SpeedMission3
|
||||
speed_mission_4: SpeedMission4
|
||||
speed_mission_5: SpeedMission5
|
||||
|
||||
"mech_mission_count": MechMissionCount,
|
||||
"mech_mission_2": MechMission2,
|
||||
"mech_mission_3": MechMission3,
|
||||
"mech_mission_4": MechMission4,
|
||||
"mech_mission_5": MechMission5,
|
||||
mech_mission_count: MechMissionCount
|
||||
mech_mission_2: MechMission2
|
||||
mech_mission_3: MechMission3
|
||||
mech_mission_4: MechMission4
|
||||
mech_mission_5: MechMission5
|
||||
|
||||
"hunt_mission_count": HuntMissionCount,
|
||||
"hunt_mission_2": HuntMission2,
|
||||
"hunt_mission_3": HuntMission3,
|
||||
"hunt_mission_4": HuntMission4,
|
||||
"hunt_mission_5": HuntMission5,
|
||||
hunt_mission_count: HuntMissionCount
|
||||
hunt_mission_2: HuntMission2
|
||||
hunt_mission_3: HuntMission3
|
||||
hunt_mission_4: HuntMission4
|
||||
hunt_mission_5: HuntMission5
|
||||
|
||||
"kart_mission_count": KartMissionCount,
|
||||
"kart_mission_2": KartMission2,
|
||||
"kart_mission_3": KartMission3,
|
||||
"kart_mission_4": KartMission4,
|
||||
"kart_mission_5": KartMission5,
|
||||
kart_mission_count: KartMissionCount
|
||||
kart_mission_2: KartMission2
|
||||
kart_mission_3: KartMission3
|
||||
kart_mission_4: KartMission4
|
||||
kart_mission_5: KartMission5
|
||||
|
||||
"cannons_core_mission_count": CannonsCoreMissionCount,
|
||||
"cannons_core_mission_2": CannonsCoreMission2,
|
||||
"cannons_core_mission_3": CannonsCoreMission3,
|
||||
"cannons_core_mission_4": CannonsCoreMission4,
|
||||
"cannons_core_mission_5": CannonsCoreMission5,
|
||||
cannons_core_mission_count: CannonsCoreMissionCount
|
||||
cannons_core_mission_2: CannonsCoreMission2
|
||||
cannons_core_mission_3: CannonsCoreMission3
|
||||
cannons_core_mission_4: CannonsCoreMission4
|
||||
cannons_core_mission_5: CannonsCoreMission5
|
||||
|
||||
"ring_link": RingLink,
|
||||
"death_link": DeathLink,
|
||||
}
|
||||
ring_link: RingLink
|
||||
death_link: DeathLink
|
||||
|
||||
@@ -3,20 +3,20 @@ import math
|
||||
import logging
|
||||
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
|
||||
from .AestheticData import chao_name_conversion, sample_chao_names, totally_real_item_names, \
|
||||
all_exits, all_destinations, multi_rooms, single_rooms, room_to_exits_map, exit_to_room_map, valid_kindergarten_exits
|
||||
from .GateBosses import get_gate_bosses, get_boss_rush_bosses, get_boss_name
|
||||
from .Items import SA2BItem, ItemData, item_table, upgrades_table, emeralds_table, junk_table, trap_table, item_groups, \
|
||||
eggs_table, fruits_table, seeds_table, hats_table, animals_table, chaos_drives_table
|
||||
from .Locations import SA2BLocation, all_locations, setup_locations, chao_animal_event_location_table, black_market_location_table
|
||||
from .Options import sa2b_options
|
||||
from .Missions import get_mission_table, get_mission_count_table, get_first_and_last_cannons_core_missions
|
||||
from .Names import ItemName, LocationName
|
||||
from .Options import SA2BOptions, sa2b_option_groups
|
||||
from .Regions import create_regions, shuffleable_regions, connect_regions, LevelGate, gate_0_whitelist_regions, \
|
||||
gate_0_blacklist_regions
|
||||
from .Rules import set_rules
|
||||
from .Names import ItemName, LocationName
|
||||
from .AestheticData import chao_name_conversion, sample_chao_names, totally_real_item_names, \
|
||||
all_exits, all_destinations, multi_rooms, single_rooms, room_to_exits_map, exit_to_room_map, valid_kindergarten_exits
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from .GateBosses import get_gate_bosses, get_boss_rush_bosses, get_boss_name
|
||||
from .Missions import get_mission_table, get_mission_count_table, get_first_and_last_cannons_core_missions
|
||||
import Patch
|
||||
|
||||
|
||||
class SA2BWeb(WebWorld):
|
||||
@@ -30,8 +30,9 @@ class SA2BWeb(WebWorld):
|
||||
"setup/en",
|
||||
["RaspberrySpaceJam", "PoryGone", "Entiss"]
|
||||
)
|
||||
|
||||
|
||||
tutorials = [setup_en]
|
||||
option_groups = sa2b_option_groups
|
||||
|
||||
|
||||
def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range: int, multiworld: MultiWorld):
|
||||
@@ -54,7 +55,8 @@ class SA2BWorld(World):
|
||||
Sonic Adventure 2 Battle is an action platforming game. Play as Sonic, Tails, Knuckles, Shadow, Rouge, and Eggman across 31 stages and prevent the destruction of the earth.
|
||||
"""
|
||||
game: str = "Sonic Adventure 2 Battle"
|
||||
option_definitions = sa2b_options
|
||||
options_dataclass = SA2BOptions
|
||||
options: SA2BOptions
|
||||
topology_present = False
|
||||
data_version = 7
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, PerGameCommonOptions
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
"""
|
||||
Determines the goal of the seed
|
||||
|
||||
Bowser: Defeat Koopalings, reach Bowser's Castle and defeat Bowser
|
||||
|
||||
Yoshi Egg Hunt: Find a certain number of Yoshi Eggs
|
||||
"""
|
||||
display_name = "Goal"
|
||||
@@ -28,7 +30,9 @@ class BossesRequired(Range):
|
||||
class NumberOfYoshiEggs(Range):
|
||||
"""
|
||||
Maximum possible number of Yoshi Eggs that will be in the item pool
|
||||
|
||||
If fewer available locations exist in the pool than this number, the number of available locations will be used instead.
|
||||
|
||||
Required Percentage of Yoshi Eggs will be calculated based off of that number.
|
||||
"""
|
||||
display_name = "Max Number of Yoshi Eggs"
|
||||
@@ -64,7 +68,9 @@ class MoonChecks(Toggle):
|
||||
class Hidden1UpChecks(Toggle):
|
||||
"""
|
||||
Whether collecting a hidden 1-Up mushroom in a level will grant a check
|
||||
|
||||
These checks are considered cryptic as there's no actual indicator that they're in their respective places
|
||||
|
||||
Enable this option at your own risk
|
||||
"""
|
||||
display_name = "Hidden 1-Up Checks"
|
||||
@@ -80,7 +86,9 @@ class BonusBlockChecks(Toggle):
|
||||
class Blocksanity(Toggle):
|
||||
"""
|
||||
Whether hitting a block with an item or coin inside will grant a check
|
||||
|
||||
Note that some blocks are excluded due to how the option and the game works!
|
||||
|
||||
Exclusion list:
|
||||
* Blocks in Top Secret Area & Front Door/Bowser Castle
|
||||
* Blocks that are unreachable unless you glitch your way in
|
||||
@@ -91,10 +99,15 @@ class Blocksanity(Toggle):
|
||||
class BowserCastleDoors(Choice):
|
||||
"""
|
||||
How the doors of Bowser's Castle behave
|
||||
|
||||
Vanilla: Front and Back Doors behave as vanilla
|
||||
|
||||
Fast: Both doors behave as the Back Door
|
||||
|
||||
Slow: Both doors behave as the Front Door
|
||||
|
||||
"Front Door" rooms depend on the `bowser_castle_rooms` option
|
||||
|
||||
"Back Door" only requires going through the dark hallway to Bowser
|
||||
"""
|
||||
display_name = "Bowser Castle Doors"
|
||||
@@ -107,10 +120,15 @@ class BowserCastleDoors(Choice):
|
||||
class BowserCastleRooms(Choice):
|
||||
"""
|
||||
How the rooms of Bowser's Castle Front Door behave
|
||||
|
||||
Vanilla: You can choose which rooms to enter, as in vanilla
|
||||
|
||||
Random Two Room: Two random rooms are chosen
|
||||
|
||||
Random Five Room: Five random rooms are chosen
|
||||
|
||||
Gauntlet: All eight rooms must be cleared
|
||||
|
||||
Labyrinth: Which room leads to Bowser?
|
||||
"""
|
||||
display_name = "Bowser Castle Rooms"
|
||||
@@ -125,9 +143,13 @@ class BowserCastleRooms(Choice):
|
||||
class BossShuffle(Choice):
|
||||
"""
|
||||
How bosses are shuffled
|
||||
|
||||
None: Bosses are not shuffled
|
||||
|
||||
Simple: Four Reznors and the seven Koopalings are shuffled around
|
||||
|
||||
Full: Each boss location gets a fully random boss
|
||||
|
||||
Singularity: One or two bosses are chosen and placed at every boss location
|
||||
"""
|
||||
display_name = "Boss Shuffle"
|
||||
@@ -148,6 +170,7 @@ class LevelShuffle(Toggle):
|
||||
class ExcludeSpecialZone(Toggle):
|
||||
"""
|
||||
If active, this option will prevent any progression items from being placed in Special Zone levels.
|
||||
|
||||
Additionally, if Level Shuffle is active, Special Zone levels will not be shuffled away from their vanilla tiles.
|
||||
"""
|
||||
display_name = "Exclude Special Zone"
|
||||
@@ -155,9 +178,10 @@ class ExcludeSpecialZone(Toggle):
|
||||
|
||||
class SwapDonutGhostHouseExits(Toggle):
|
||||
"""
|
||||
If enabled, this option will swap which overworld direction the two exits of the level at the Donut Ghost House
|
||||
overworld tile go:
|
||||
If enabled, this option will swap which overworld direction the two exits of the level at the Donut Ghost House overworld tile go:
|
||||
|
||||
False: Normal Exit goes up, Secret Exit goes right.
|
||||
|
||||
True: Normal Exit goes right, Secret Exit goes up.
|
||||
"""
|
||||
display_name = "Swap Donut GH Exits"
|
||||
@@ -258,6 +282,7 @@ class Autosave(DefaultOnToggle):
|
||||
class EarlyClimb(Toggle):
|
||||
"""
|
||||
Force Climb to appear early in the seed as a local item.
|
||||
|
||||
This is particularly useful to prevent BK when Level Shuffle is disabled
|
||||
"""
|
||||
display_name = "Early Climb"
|
||||
@@ -277,9 +302,13 @@ class OverworldSpeed(Choice):
|
||||
class MusicShuffle(Choice):
|
||||
"""
|
||||
Music shuffle type
|
||||
|
||||
None: No Music is shuffled
|
||||
|
||||
Consistent: Each music track is consistently shuffled throughout the game
|
||||
|
||||
Full: Each individual level has a random music track
|
||||
|
||||
Singularity: The entire game uses one song for overworld and one song for levels
|
||||
"""
|
||||
display_name = "Music Shuffle"
|
||||
@@ -293,9 +322,13 @@ class MusicShuffle(Choice):
|
||||
class SFXShuffle(Choice):
|
||||
"""
|
||||
Shuffles almost every instance of sound effect playback
|
||||
|
||||
Archipelago elements that play sound effects aren't randomized
|
||||
|
||||
None: No SFX are shuffled
|
||||
|
||||
Full: Each individual SFX call has a random SFX
|
||||
|
||||
Singularity: The entire game uses one SFX for every SFX call
|
||||
"""
|
||||
display_name = "Sound Effect Shuffle"
|
||||
@@ -324,8 +357,11 @@ class MarioPalette(Choice):
|
||||
class LevelPaletteShuffle(Choice):
|
||||
"""
|
||||
Whether to shuffle level palettes
|
||||
|
||||
Off: Do not shuffle palettes
|
||||
|
||||
On Legacy: Uses only the palette sets from the original game
|
||||
|
||||
On Curated: Uses custom, hand-crafted palette sets
|
||||
"""
|
||||
display_name = "Level Palette Shuffle"
|
||||
@@ -338,8 +374,11 @@ class LevelPaletteShuffle(Choice):
|
||||
class OverworldPaletteShuffle(Choice):
|
||||
"""
|
||||
Whether to shuffle overworld palettes
|
||||
|
||||
Off: Do not shuffle palettes
|
||||
|
||||
On Legacy: Uses only the palette sets from the original game
|
||||
|
||||
On Curated: Uses custom, hand-crafted palette sets
|
||||
"""
|
||||
display_name = "Overworld Palette Shuffle"
|
||||
@@ -359,6 +398,52 @@ class StartingLifeCount(Range):
|
||||
default = 5
|
||||
|
||||
|
||||
smw_option_groups = [
|
||||
OptionGroup("Goal Options", [
|
||||
Goal,
|
||||
BossesRequired,
|
||||
NumberOfYoshiEggs,
|
||||
PercentageOfYoshiEggs,
|
||||
]),
|
||||
OptionGroup("Sanity Options", [
|
||||
DragonCoinChecks,
|
||||
MoonChecks,
|
||||
Hidden1UpChecks,
|
||||
BonusBlockChecks,
|
||||
Blocksanity,
|
||||
]),
|
||||
OptionGroup("Level Shuffling", [
|
||||
LevelShuffle,
|
||||
ExcludeSpecialZone,
|
||||
BowserCastleDoors,
|
||||
BowserCastleRooms,
|
||||
BossShuffle,
|
||||
SwapDonutGhostHouseExits,
|
||||
]),
|
||||
OptionGroup("Junk and Traps", [
|
||||
JunkFillPercentage,
|
||||
TrapFillPercentage,
|
||||
IceTrapWeight,
|
||||
StunTrapWeight,
|
||||
LiteratureTrapWeight,
|
||||
TimerTrapWeight,
|
||||
ReverseTrapWeight,
|
||||
ThwimpTrapWeight,
|
||||
]),
|
||||
OptionGroup("Aesthetics", [
|
||||
DisplayReceivedItemPopups,
|
||||
Autosave,
|
||||
OverworldSpeed,
|
||||
MusicShuffle,
|
||||
SFXShuffle,
|
||||
MarioPalette,
|
||||
LevelPaletteShuffle,
|
||||
OverworldPaletteShuffle,
|
||||
StartingLifeCount,
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SMWOptions(PerGameCommonOptions):
|
||||
death_link: DeathLink
|
||||
|
||||
57
worlds/smw/Presets.py
Normal file
57
worlds/smw/Presets.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
all_random = {
|
||||
"goal": "random",
|
||||
"bosses_required": "random",
|
||||
"max_yoshi_egg_cap": "random",
|
||||
"percentage_of_yoshi_eggs": "random",
|
||||
"dragon_coin_checks": "random",
|
||||
"moon_checks": "random",
|
||||
"hidden_1up_checks": "random",
|
||||
"bonus_block_checks": "random",
|
||||
"blocksanity": "random",
|
||||
"bowser_castle_doors": "random",
|
||||
"bowser_castle_rooms": "random",
|
||||
"level_shuffle": "random",
|
||||
"exclude_special_zone": "random",
|
||||
"boss_shuffle": "random",
|
||||
"swap_donut_gh_exits": "random",
|
||||
"display_received_item_popups": "random",
|
||||
"junk_fill_percentage": "random",
|
||||
"trap_fill_percentage": "random",
|
||||
"ice_trap_weight": "random",
|
||||
"stun_trap_weight": "random",
|
||||
"literature_trap_weight": "random",
|
||||
"timer_trap_weight": "random",
|
||||
"reverse_trap_weight": "random",
|
||||
"thwimp_trap_weight": "random",
|
||||
"autosave": "random",
|
||||
"early_climb": "random",
|
||||
"overworld_speed": "random",
|
||||
"music_shuffle": "random",
|
||||
"sfx_shuffle": "random",
|
||||
"mario_palette": "random",
|
||||
"level_palette_shuffle": "random",
|
||||
"overworld_palette_shuffle": "random",
|
||||
"starting_life_count": "random",
|
||||
}
|
||||
|
||||
allsanity = {
|
||||
"dragon_coin_checks": True,
|
||||
"moon_checks": True,
|
||||
"hidden_1up_checks": True,
|
||||
"bonus_block_checks": True,
|
||||
"blocksanity": True,
|
||||
"level_shuffle": True,
|
||||
"boss_shuffle": "full",
|
||||
"music_shuffle": "full",
|
||||
"sfx_shuffle": "full",
|
||||
"mario_palette": "random",
|
||||
"level_palette_shuffle": "on_curated",
|
||||
"overworld_palette_shuffle": "on_curated",
|
||||
}
|
||||
|
||||
smw_options_presets: Dict[str, Dict[str, Any]] = {
|
||||
"All Random": all_random,
|
||||
"Allsanity": allsanity,
|
||||
}
|
||||
@@ -6,17 +6,19 @@ import settings
|
||||
import threading
|
||||
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from .Items import SMWItem, ItemData, item_table, junk_table
|
||||
from .Locations import SMWLocation, all_locations, setup_locations, special_zone_level_names, special_zone_dragon_coin_names, special_zone_hidden_1up_names, special_zone_blocksanity_names
|
||||
from .Options import SMWOptions
|
||||
from .Regions import create_regions, connect_regions
|
||||
from .Levels import full_level_list, generate_level_list, location_id_to_level_id
|
||||
from .Rules import set_rules
|
||||
from worlds.generic.Rules import add_rule, exclusion_rules
|
||||
from .Names import ItemName, LocationName
|
||||
from .Client import SMWSNIClient
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from worlds.generic.Rules import add_rule, exclusion_rules
|
||||
|
||||
from .Client import SMWSNIClient
|
||||
from .Items import SMWItem, ItemData, item_table, junk_table
|
||||
from .Levels import full_level_list, generate_level_list, location_id_to_level_id
|
||||
from .Locations import SMWLocation, all_locations, setup_locations, special_zone_level_names, special_zone_dragon_coin_names, special_zone_hidden_1up_names, special_zone_blocksanity_names
|
||||
from .Names import ItemName, LocationName
|
||||
from .Options import SMWOptions, smw_option_groups
|
||||
from .Presets import smw_options_presets
|
||||
from .Regions import create_regions, connect_regions
|
||||
from .Rom import LocalRom, patch_rom, get_base_rom_path, SMWDeltaPatch
|
||||
from .Rules import set_rules
|
||||
|
||||
|
||||
class SMWSettings(settings.Group):
|
||||
@@ -40,9 +42,12 @@ class SMWWeb(WebWorld):
|
||||
"setup/en",
|
||||
["PoryGone"]
|
||||
)
|
||||
|
||||
|
||||
tutorials = [setup_en]
|
||||
|
||||
option_groups = smw_option_groups
|
||||
options_presets = smw_options_presets
|
||||
|
||||
|
||||
class SMWWorld(World):
|
||||
"""
|
||||
|
||||
@@ -8,7 +8,7 @@ from .er_rules import set_er_location_rules
|
||||
from .regions import tunic_regions
|
||||
from .er_scripts import create_er_regions
|
||||
from .er_data import portal_mapping
|
||||
from .options import TunicOptions, EntranceRando
|
||||
from .options import TunicOptions, EntranceRando, tunic_option_groups
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from worlds.generic import PlandoConnection
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
@@ -27,6 +27,7 @@ class TunicWeb(WebWorld):
|
||||
]
|
||||
theme = "grassFlowers"
|
||||
game = "TUNIC"
|
||||
option_groups = tunic_option_groups
|
||||
|
||||
|
||||
class TunicItem(Item):
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PerGameCommonOptions
|
||||
from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PerGameCommonOptions,
|
||||
OptionGroup)
|
||||
|
||||
|
||||
class SwordProgression(DefaultOnToggle):
|
||||
"""Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons, including two new swords with increased range and attack power."""
|
||||
"""
|
||||
Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons, including two new swords with increased range and attack power.
|
||||
"""
|
||||
internal_name = "sword_progression"
|
||||
display_name = "Sword Progression"
|
||||
|
||||
|
||||
class StartWithSword(Toggle):
|
||||
"""Start with a sword in the player's inventory. Does not count towards Sword Progression."""
|
||||
"""
|
||||
Start with a sword in the player's inventory. Does not count towards Sword Progression.
|
||||
"""
|
||||
internal_name = "start_with_sword"
|
||||
display_name = "Start With Sword"
|
||||
|
||||
|
||||
class KeysBehindBosses(Toggle):
|
||||
"""Places the three hexagon keys behind their respective boss fight in your world."""
|
||||
"""
|
||||
Places the three hexagon keys behind their respective boss fight in your world.
|
||||
"""
|
||||
internal_name = "keys_behind_bosses"
|
||||
display_name = "Keys Behind Bosses"
|
||||
|
||||
|
||||
class AbilityShuffling(Toggle):
|
||||
"""Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found.
|
||||
"""
|
||||
Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found.
|
||||
If playing Hexagon Quest, abilities are instead randomly unlocked after obtaining 25%, 50%, and 75% of the required Hexagon goal amount.
|
||||
*Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other player-facing codes.
|
||||
* Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other player-facing codes.
|
||||
"""
|
||||
internal_name = "ability_shuffling"
|
||||
display_name = "Shuffle Abilities"
|
||||
@@ -37,9 +45,9 @@ class LogicRules(Choice):
|
||||
No Major Glitches: Sneaky Laurels zips, ice grapples through doors, shooting the west bell, and boss quick kills are included in logic.
|
||||
* Ice grappling through the Ziggurat door is not in logic since you will get stuck in there without Prayer.
|
||||
Unrestricted: Logic in No Major Glitches, as well as ladder storage to get to certain places early.
|
||||
*Torch is given to the player at the start of the game due to the high softlock potential with various tricks. Using the torch is not required in logic.
|
||||
*Using Ladder Storage to get to individual chests is not in logic to avoid tedium.
|
||||
*Getting knocked out of the air by enemies during Ladder Storage to reach places is not in logic, except for in Rooted Ziggurat Lower. This is so you're not punished for playing with enemy rando on.
|
||||
* Torch is given to the player at the start of the game due to the high softlock potential with various tricks. Using the torch is not required in logic.
|
||||
* Using Ladder Storage to get to individual chests is not in logic to avoid tedium.
|
||||
* Getting knocked out of the air by enemies during Ladder Storage to reach places is not in logic, except for in Rooted Ziggurat Lower. This is so you're not punished for playing with enemy rando on.
|
||||
"""
|
||||
internal_name = "logic_rules"
|
||||
display_name = "Logic Rules"
|
||||
@@ -52,21 +60,27 @@ class LogicRules(Choice):
|
||||
|
||||
|
||||
class Lanternless(Toggle):
|
||||
"""Choose whether you require the Lantern for dark areas.
|
||||
When enabled, the Lantern is marked as Useful instead of Progression."""
|
||||
"""
|
||||
Choose whether you require the Lantern for dark areas.
|
||||
When enabled, the Lantern is marked as Useful instead of Progression.
|
||||
"""
|
||||
internal_name = "lanternless"
|
||||
display_name = "Lanternless"
|
||||
|
||||
|
||||
class Maskless(Toggle):
|
||||
"""Choose whether you require the Scavenger's Mask for Lower Quarry.
|
||||
When enabled, the Scavenger's Mask is marked as Useful instead of Progression."""
|
||||
"""
|
||||
Choose whether you require the Scavenger's Mask for Lower Quarry.
|
||||
When enabled, the Scavenger's Mask is marked as Useful instead of Progression.
|
||||
"""
|
||||
internal_name = "maskless"
|
||||
display_name = "Maskless"
|
||||
|
||||
|
||||
class FoolTraps(Choice):
|
||||
"""Replaces low-to-medium value money rewards in the item pool with fool traps, which cause random negative effects to the player."""
|
||||
"""
|
||||
Replaces low-to-medium value money rewards in the item pool with fool traps, which cause random negative effects to the player.
|
||||
"""
|
||||
internal_name = "fool_traps"
|
||||
display_name = "Fool Traps"
|
||||
option_off = 0
|
||||
@@ -77,13 +91,17 @@ class FoolTraps(Choice):
|
||||
|
||||
|
||||
class HexagonQuest(Toggle):
|
||||
"""An alternate goal that shuffles Gold "Questagon" items into the item pool and allows the game to be completed after collecting the required number of them."""
|
||||
"""
|
||||
An alternate goal that shuffles Gold "Questagon" items into the item pool and allows the game to be completed after collecting the required number of them.
|
||||
"""
|
||||
internal_name = "hexagon_quest"
|
||||
display_name = "Hexagon Quest"
|
||||
|
||||
|
||||
class HexagonGoal(Range):
|
||||
"""How many Gold Questagons are required to complete the game on Hexagon Quest."""
|
||||
"""
|
||||
How many Gold Questagons are required to complete the game on Hexagon Quest.
|
||||
"""
|
||||
internal_name = "hexagon_goal"
|
||||
display_name = "Gold Hexagons Required"
|
||||
range_start = 15
|
||||
@@ -92,7 +110,9 @@ class HexagonGoal(Range):
|
||||
|
||||
|
||||
class ExtraHexagonPercentage(Range):
|
||||
"""How many extra Gold Questagons are shuffled into the item pool, taken as a percentage of the goal amount."""
|
||||
"""
|
||||
How many extra Gold Questagons are shuffled into the item pool, taken as a percentage of the goal amount.
|
||||
"""
|
||||
internal_name = "extra_hexagon_percentage"
|
||||
display_name = "Percentage of Extra Gold Hexagons"
|
||||
range_start = 0
|
||||
@@ -118,16 +138,20 @@ class EntranceRando(TextChoice):
|
||||
|
||||
|
||||
class FixedShop(Toggle):
|
||||
"""Forces the Windmill entrance to lead to a shop, and removes the remaining shops from the pool.
|
||||
"""
|
||||
Forces the Windmill entrance to lead to a shop, and removes the remaining shops from the pool.
|
||||
Adds another entrance in Rooted Ziggurat Lower to keep an even number of entrances.
|
||||
Has no effect if Entrance Rando is not enabled."""
|
||||
Has no effect if Entrance Rando is not enabled.
|
||||
"""
|
||||
internal_name = "fixed_shop"
|
||||
display_name = "Fewer Shops in Entrance Rando"
|
||||
|
||||
|
||||
class LaurelsLocation(Choice):
|
||||
"""Force the Hero's Laurels to be placed at a location in your world.
|
||||
For if you want to avoid or specify early or late Laurels."""
|
||||
"""
|
||||
Force the Hero's Laurels to be placed at a location in your world.
|
||||
For if you want to avoid or specify early or late Laurels.
|
||||
"""
|
||||
internal_name = "laurels_location"
|
||||
display_name = "Laurels Location"
|
||||
option_anywhere = 0
|
||||
@@ -138,9 +162,12 @@ class LaurelsLocation(Choice):
|
||||
|
||||
|
||||
class ShuffleLadders(Toggle):
|
||||
"""Turns several ladders in the game into items that must be found before they can be climbed on.
|
||||
"""
|
||||
Turns several ladders in the game into items that must be found before they can be climbed on.
|
||||
Adds more layers of progression to the game by blocking access to many areas early on.
|
||||
"Ladders were a mistake." —Andrew Shouldice"""
|
||||
"Ladders were a mistake."
|
||||
—Andrew Shouldice
|
||||
"""
|
||||
internal_name = "shuffle_ladders"
|
||||
display_name = "Shuffle Ladders"
|
||||
|
||||
@@ -163,3 +190,12 @@ class TunicOptions(PerGameCommonOptions):
|
||||
lanternless: Lanternless
|
||||
maskless: Maskless
|
||||
laurels_location: LaurelsLocation
|
||||
|
||||
|
||||
tunic_option_groups = [
|
||||
OptionGroup("Logic Options", [
|
||||
LogicRules,
|
||||
Lanternless,
|
||||
Maskless,
|
||||
])
|
||||
]
|
||||
|
||||
@@ -16,7 +16,7 @@ from .data.item_definition_classes import DoorItemDefinition, ItemData
|
||||
from .data.utils import get_audio_logs
|
||||
from .hints import CompactItemData, create_all_hints, make_compact_hint_data, make_laser_hints
|
||||
from .locations import WitnessPlayerLocations, static_witness_locations
|
||||
from .options import TheWitnessOptions
|
||||
from .options import TheWitnessOptions, witness_option_groups
|
||||
from .player_items import WitnessItem, WitnessPlayerItems
|
||||
from .player_logic import WitnessPlayerLogic
|
||||
from .presets import witness_option_presets
|
||||
@@ -36,6 +36,7 @@ class WitnessWebWorld(WebWorld):
|
||||
)]
|
||||
|
||||
options_presets = witness_option_presets
|
||||
option_groups = witness_option_groups
|
||||
|
||||
|
||||
class WitnessWorld(World):
|
||||
|
||||
@@ -2,7 +2,7 @@ from dataclasses import dataclass
|
||||
|
||||
from schema import And, Schema
|
||||
|
||||
from Options import Choice, DefaultOnToggle, OptionDict, PerGameCommonOptions, Range, Toggle
|
||||
from Options import Choice, DefaultOnToggle, OptionDict, OptionGroup, PerGameCommonOptions, Range, Toggle
|
||||
|
||||
from .data import static_logic as static_witness_logic
|
||||
from .data.item_definition_classes import ItemCategory, WeightedItemDefinition
|
||||
@@ -61,9 +61,9 @@ class ShuffleLasers(Choice):
|
||||
class ShuffleDoors(Choice):
|
||||
"""
|
||||
If on, opening doors, moving bridges etc. will require a "key".
|
||||
If set to "panels", the panel on the door will be locked until receiving its corresponding key.
|
||||
If set to "doors", the door will open immediately upon receiving its key. Door panels are added as location checks.
|
||||
"Mixed" includes all doors from "doors", and all control panels (bridges, elevators etc.) from "panels".
|
||||
- Panels: The panel on the door will be locked until receiving its corresponding key.
|
||||
- Doors: The door will open immediately upon receiving its key. Door panels are added as location checks.
|
||||
- Mixed: Includes all doors from "doors", and all control panels (bridges, elevators etc.) from "panels".
|
||||
"""
|
||||
display_name = "Shuffle Doors"
|
||||
option_off = 0
|
||||
@@ -74,8 +74,10 @@ class ShuffleDoors(Choice):
|
||||
|
||||
class DoorGroupings(Choice):
|
||||
"""
|
||||
If set to "none", there will be one key for each door, potentially resulting in upwards of 120 keys being added to the item pool.
|
||||
If set to "regional", all doors in the same general region will open at once with a single key, reducing the amount of door items and complexity.
|
||||
Controls how door items are grouped.
|
||||
|
||||
- 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
|
||||
@@ -108,8 +110,8 @@ class ShuffleVaultBoxes(Toggle):
|
||||
class ShuffleEnvironmentalPuzzles(Choice):
|
||||
"""
|
||||
Adds Environmental/Obelisk Puzzles into the location pool.
|
||||
If set to "individual", every Environmental Puzzle sends an item.
|
||||
If set to "Obelisk Sides", completing every puzzle on one side of an Obelisk sends an item.
|
||||
- Individual: Every Environmental Puzzle sends an item.
|
||||
- Obelisk Sides: Completing every puzzle on one side of an Obelisk sends an item.
|
||||
|
||||
Note: In Obelisk Sides, any EPs excluded through another option will be pre-completed on their Obelisk.
|
||||
"""
|
||||
@@ -129,9 +131,9 @@ class ShuffleDog(Toggle):
|
||||
class EnvironmentalPuzzlesDifficulty(Choice):
|
||||
"""
|
||||
When "Shuffle Environmental Puzzles" is on, this setting governs which EPs are eligible for the location pool.
|
||||
If set to "eclipse", every EP in the game is eligible, including the 1-hour-long "Theater Eclipse EP".
|
||||
If set to "tedious", Theater Eclipse EP is excluded from the location pool.
|
||||
If set to "normal", several other difficult or long EPs are excluded as well.
|
||||
- Eclipse: Every EP in the game is eligible, including the 1-hour-long "Theater Eclipse EP".
|
||||
- Tedious Theater Eclipse EP is excluded from the location pool.
|
||||
- Normal: several other difficult or long EPs are excluded as well.
|
||||
"""
|
||||
display_name = "Environmental Puzzles Difficulty"
|
||||
option_normal = 0
|
||||
@@ -159,10 +161,10 @@ class ShufflePostgame(Toggle):
|
||||
class VictoryCondition(Choice):
|
||||
"""
|
||||
Set the victory condition for this world.
|
||||
Elevator: Start the elevator at the bottom of the mountain (requires Mountain Lasers).
|
||||
Challenge: Beat the secret Challenge (requires Challenge Lasers).
|
||||
Mountain Box Short: Input the short solution to the Mountaintop Box (requires Mountain Lasers).
|
||||
Mountain Box Long: Input the long solution to the Mountaintop Box (requires Challenge Lasers).
|
||||
- Elevator: Start the elevator at the bottom of the mountain (requires Mountain Lasers).
|
||||
- Challenge: Beat the secret Challenge (requires Challenge Lasers).
|
||||
- Mountain Box Short: Input the short solution to the Mountaintop Box (requires Mountain Lasers).
|
||||
- Mountain Box Long: Input the long solution to the Mountaintop Box (requires Challenge Lasers).
|
||||
|
||||
It is important to note that while the Mountain Box requires Desert Laser to be redirected in Town for that laser
|
||||
to count, the laser locks on the Elevator and Challenge Timer panels do not.
|
||||
@@ -332,3 +334,45 @@ class TheWitnessOptions(PerGameCommonOptions):
|
||||
laser_hints: LaserHints
|
||||
death_link: DeathLink
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
|
||||
|
||||
witness_option_groups = [
|
||||
OptionGroup("Puzzles & Goal", [
|
||||
PuzzleRandomization,
|
||||
VictoryCondition,
|
||||
MountainLasers,
|
||||
ChallengeLasers,
|
||||
]),
|
||||
OptionGroup("Locations", [
|
||||
ShuffleDiscardedPanels,
|
||||
ShuffleVaultBoxes,
|
||||
ShuffleEnvironmentalPuzzles,
|
||||
EnvironmentalPuzzlesDifficulty,
|
||||
ShufflePostgame,
|
||||
DisableNonRandomizedPuzzles,
|
||||
]),
|
||||
OptionGroup("Progression Items", [
|
||||
ShuffleSymbols,
|
||||
ShuffleDoors,
|
||||
DoorGroupings,
|
||||
ShuffleLasers,
|
||||
ShuffleBoat,
|
||||
ObeliskKeys,
|
||||
]),
|
||||
OptionGroup("Filler Items", [
|
||||
PuzzleSkipAmount,
|
||||
TrapPercentage,
|
||||
TrapWeights
|
||||
]),
|
||||
OptionGroup("Hints", [
|
||||
HintAmount,
|
||||
AreaHintPercentage,
|
||||
LaserHints
|
||||
]),
|
||||
OptionGroup("Misc", [
|
||||
EarlyCaves,
|
||||
ElevatorsComeToYou,
|
||||
DeathLink,
|
||||
DeathLinkAmnesty,
|
||||
])
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user