mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 20:33:22 -07:00
0.10b - New Options
This commit is contained in:
@@ -65,6 +65,10 @@ def is_location_valid(world: World, location: str) -> bool:
|
||||
if location in shop_locations and location not in world.shop_locs:
|
||||
return False
|
||||
|
||||
data = location_table.get(location) or event_locs.get(location)
|
||||
if data.region == "Time Rift - Tour" and world.multiworld.ExcludeTour[world.player].value > 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import typing
|
||||
from worlds.AutoWorld import World
|
||||
from Options import Option, Range, Toggle, DeathLink, Choice
|
||||
from Options import Option, Range, Toggle, DeathLink, Choice, OptionDict
|
||||
from .Items import get_total_time_pieces
|
||||
|
||||
|
||||
@@ -72,26 +72,22 @@ class ActRandomizer(Choice):
|
||||
default = 1
|
||||
|
||||
|
||||
class ActPlando(OptionDict):
|
||||
"""Plando acts onto other acts. For example, \"Train Rush\": \"Alpine Free Roam\""""
|
||||
display_name = "Act Plando"
|
||||
|
||||
|
||||
class ShuffleAlpineZiplines(Toggle):
|
||||
"""If enabled, Alpine's zipline paths leading to the peaks will be locked behind items."""
|
||||
display_name = "Shuffle Alpine Ziplines"
|
||||
default = 0
|
||||
|
||||
|
||||
class VanillaAlpine(Choice):
|
||||
"""If enabled, force Alpine (and optionally its finale) onto their vanilla locations in act shuffle."""
|
||||
display_name = "Vanilla Alpine Skyline"
|
||||
option_false = 0
|
||||
option_true = 1
|
||||
option_finale = 2
|
||||
class FinaleShuffle(Toggle):
|
||||
"""If enabled, chapter finales will only be shuffled amongst each other in act shuffle."""
|
||||
default = 0
|
||||
|
||||
|
||||
class NoFreeRoamFinale(Toggle):
|
||||
"""If enabled, prevent Free Roam acts from being shuffled onto chapter finales."""
|
||||
default = 1
|
||||
|
||||
|
||||
class LogicDifficulty(Choice):
|
||||
"""Choose the difficulty setting for logic."""
|
||||
display_name = "Logic Difficulty"
|
||||
@@ -221,6 +217,22 @@ class TasksanityCheckCount(Range):
|
||||
default = 18
|
||||
|
||||
|
||||
class ExcludeTour(Toggle):
|
||||
"""Removes the Tour time rift from the game. This option is recommended if you don't want to deal with
|
||||
important levels being shuffled onto the Tour time rift, or important items being shuffled onto Tour pages
|
||||
when your goal is Time's End."""
|
||||
display_name = "Exclude Tour Time Rift"
|
||||
default = 0
|
||||
|
||||
|
||||
class ShipShapeCustomTaskGoal(Range):
|
||||
"""Change the amount of tasks required to complete Ship Shape. This will not affect Cruisin' for a Bruisin'."""
|
||||
display_name = "Ship Shape Custom Task Goal"
|
||||
range_start = 5
|
||||
range_end = 30
|
||||
default = 18
|
||||
|
||||
|
||||
class EnableDLC2(Toggle):
|
||||
"""Shuffle content from Nyakuza Metro (Chapter 7) into the game.
|
||||
DO NOT ENABLE THIS OPTION IF YOU DO NOT HAVE NYAKUZA METRO DLC INSTALLED!!!"""
|
||||
@@ -238,7 +250,7 @@ class MetroMinPonCost(Range):
|
||||
|
||||
class MetroMaxPonCost(Range):
|
||||
"""The most expensive an item can be in any Nyakuza Metro shop. Includes ticket booths."""
|
||||
display_name = "Metro Shops Minimum Pon Cost"
|
||||
display_name = "Metro Shops Maximum Pon Cost"
|
||||
range_start = 10
|
||||
range_end = 800
|
||||
default = 200
|
||||
@@ -267,14 +279,6 @@ class BaseballBat(Toggle):
|
||||
default = 0
|
||||
|
||||
|
||||
class VanillaMetro(Choice):
|
||||
"""Force Nyakuza Metro (and optionally its finale) onto their vanilla locations in act shuffle."""
|
||||
display_name = "Vanilla Metro"
|
||||
option_false = 0
|
||||
option_true = 1
|
||||
option_finale = 2
|
||||
|
||||
|
||||
class ChapterCostIncrement(Range):
|
||||
"""Lower values mean chapter costs increase slower. Higher values make the cost differences more steep."""
|
||||
display_name = "Chapter Cost Increment"
|
||||
@@ -357,7 +361,7 @@ class DWExcludeAnnoyingContracts(Toggle):
|
||||
|
||||
class DWExcludeAnnoyingBonuses(Toggle):
|
||||
"""NOT IMPLEMENTED If Death Wish full completions are shuffled in, exclude particularly tedious Death Wish full completions
|
||||
from the pool"""
|
||||
from the pool. DANGER! DISABLE AT YOUR OWN RISK! THIS OPTION WHEN DISABLED CAN CREATE VERY DIFFICULT SEEDS!!!"""
|
||||
display_name = "Exclude Annoying Death Wish Full Completions"
|
||||
default = 1
|
||||
|
||||
@@ -459,9 +463,9 @@ ahit_options: typing.Dict[str, type(Option)] = {
|
||||
|
||||
"EndGoal": EndGoal,
|
||||
"ActRandomizer": ActRandomizer,
|
||||
"ActPlando": ActPlando,
|
||||
"ShuffleAlpineZiplines": ShuffleAlpineZiplines,
|
||||
"VanillaAlpine": VanillaAlpine,
|
||||
"NoFreeRoamFinale": NoFreeRoamFinale,
|
||||
"FinaleShuffle": FinaleShuffle,
|
||||
"LogicDifficulty": LogicDifficulty,
|
||||
"KnowledgeChecks": KnowledgeChecks,
|
||||
"YarnBalancePercent": YarnBalancePercent,
|
||||
@@ -480,11 +484,12 @@ ahit_options: typing.Dict[str, type(Option)] = {
|
||||
"Tasksanity": Tasksanity,
|
||||
"TasksanityTaskStep": TasksanityTaskStep,
|
||||
"TasksanityCheckCount": TasksanityCheckCount,
|
||||
"ExcludeTour": ExcludeTour,
|
||||
"ShipShapeCustomTaskGoal": ShipShapeCustomTaskGoal,
|
||||
|
||||
"EnableDeathWish": EnableDeathWish,
|
||||
"EnableDLC2": EnableDLC2,
|
||||
"BaseballBat": BaseballBat,
|
||||
"VanillaMetro": VanillaMetro,
|
||||
"MetroMinPonCost": MetroMinPonCost,
|
||||
"MetroMaxPonCost": MetroMaxPonCost,
|
||||
"NyakuzaThugMinShopItems": NyakuzaThugMinShopItems,
|
||||
@@ -534,6 +539,8 @@ slot_data_options: typing.Dict[str, type(Option)] = {
|
||||
"Tasksanity": Tasksanity,
|
||||
"TasksanityTaskStep": TasksanityTaskStep,
|
||||
"TasksanityCheckCount": TasksanityCheckCount,
|
||||
"ShipShapeCustomTaskGoal": ShipShapeCustomTaskGoal,
|
||||
"ExcludeTour": ExcludeTour,
|
||||
|
||||
"EnableDeathWish": EnableDeathWish,
|
||||
|
||||
|
||||
@@ -252,6 +252,16 @@ blacklisted_acts = {
|
||||
"Battle of the Birds - Finale A": "Award Ceremony",
|
||||
}
|
||||
|
||||
# Blacklisted act shuffle combinations to help prevent impossible layouts. Mostly for free roam acts.
|
||||
blacklisted_combos = {
|
||||
"The Illness has Spread": ["Alpine Free Roam"],
|
||||
"Rush Hour": ["Nyakuza Free Roam"],
|
||||
"Time Rift - The Owl Express": ["Alpine Free Roam", "Nyakuza Free Roam"],
|
||||
"Time Rift - The Moon": ["Alpine Free Roam", "Nyakuza Free Roam"],
|
||||
"Time Rift - Dead Bird Studio": ["Alpine Free Roam", "Nyakuza Free Roam"],
|
||||
"Time Rift - Rumbi Factory": ["Alpine Free Roam"],
|
||||
}
|
||||
|
||||
|
||||
def create_regions(world: World):
|
||||
w = world
|
||||
@@ -371,7 +381,9 @@ def create_regions(world: World):
|
||||
connect_regions(ac_act3, cruise_ship, "Cruise Ship Entrance RTB", p)
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Balcony"))
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Deep Sea"))
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Tour"))
|
||||
|
||||
if mw.ExcludeTour[world.player].value == 0:
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Tour"))
|
||||
|
||||
if mw.Tasksanity[p].value > 0:
|
||||
create_tasksanity_locations(w)
|
||||
@@ -419,9 +431,52 @@ def create_tasksanity_locations(world: World):
|
||||
ship_shape.locations.append(location)
|
||||
|
||||
|
||||
def is_valid_plando(world: World, region: str) -> bool:
|
||||
if region in blacklisted_acts.values():
|
||||
return False
|
||||
|
||||
if region not in world.multiworld.ActPlando[world.player].keys():
|
||||
return False
|
||||
|
||||
act = world.multiworld.ActPlando[world.player].get(region)
|
||||
if act in blacklisted_acts.values():
|
||||
return False
|
||||
|
||||
# Don't allow plando-ing things onto the first act that aren't completable with nothing
|
||||
is_first_act: bool = act_chapters[region] == get_first_chapter_region(world).name \
|
||||
and region in act_entrances.keys() and ("Act 1" in act_entrances[region] or "Free Roam" in act_entrances[region])
|
||||
|
||||
if is_first_act:
|
||||
if act_chapters[act] == "Subcon Forest" and world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
|
||||
return False
|
||||
|
||||
if world.multiworld.UmbrellaLogic[world.player].value > 0 \
|
||||
and (act == "Heating Up Mafia Town" or act == "Queen Vanessa's Manor"):
|
||||
return False
|
||||
|
||||
if act not in guaranteed_first_acts:
|
||||
return False
|
||||
|
||||
# Don't allow straight up impossible mappings
|
||||
if region == "The Illness has Spread" and act == "Alpine Free Roam":
|
||||
return False
|
||||
|
||||
if region == "Rush Hour" and act == "Nyakuza Free Roam":
|
||||
return False
|
||||
|
||||
if region == "Time Rift - Rumbi Factory" and act == "Nyakuza Free Roam":
|
||||
return False
|
||||
|
||||
if region == "Time Rift - The Owl Express" and act == "Murder on the Owl Express":
|
||||
return False
|
||||
|
||||
return any(a.name == world.multiworld.ActPlando[world.player].get(region) for a in
|
||||
world.multiworld.get_regions(world.player))
|
||||
|
||||
|
||||
def randomize_act_entrances(world: World):
|
||||
region_list: typing.List[Region] = get_act_regions(world)
|
||||
world.multiworld.random.shuffle(region_list)
|
||||
world.random.shuffle(region_list)
|
||||
|
||||
separate_rifts: bool = bool(world.multiworld.ActRandomizer[world.player].value == 1)
|
||||
|
||||
@@ -436,12 +491,21 @@ def randomize_act_entrances(world: World):
|
||||
region_list.remove(region)
|
||||
region_list.append(region)
|
||||
|
||||
# We want to do these first as well, since they can be blocked from being shuffled onto freeroam
|
||||
for region in region_list.copy():
|
||||
if region.name in chapter_finales or region.name == "Cheating the Race":
|
||||
if region.name in chapter_finales:
|
||||
region_list.remove(region)
|
||||
region_list.append(region)
|
||||
|
||||
for region in region_list.copy():
|
||||
if region.name in world.multiworld.ActPlando[world.player].keys():
|
||||
if is_valid_plando(world, region.name):
|
||||
region_list.remove(region)
|
||||
region_list.append(region)
|
||||
else:
|
||||
print("Disallowing act plando for",
|
||||
world.multiworld.player_name[world.player],
|
||||
"-", region.name, ":", world.multiworld.ActPlando[world.player].get(region.name))
|
||||
|
||||
# Reverse the list, so we can do what we want to do first
|
||||
region_list.reverse()
|
||||
|
||||
@@ -465,6 +529,9 @@ def randomize_act_entrances(world: World):
|
||||
and "Free Roam" not in act_entrances[region.name]:
|
||||
continue
|
||||
|
||||
if region.name in world.multiworld.ActPlando[world.player].keys() and is_valid_plando(world, region.name):
|
||||
has_guaranteed = True
|
||||
|
||||
i = 0
|
||||
|
||||
# Already mapped to something else
|
||||
@@ -481,6 +548,9 @@ def randomize_act_entrances(world: World):
|
||||
if candidate.name not in guaranteed_first_acts:
|
||||
continue
|
||||
|
||||
if candidate.name in world.multiworld.ActPlando[world.player].values():
|
||||
continue
|
||||
|
||||
# Not completable without Umbrella
|
||||
if world.multiworld.UmbrellaLogic[world.player].value > 0 \
|
||||
and (candidate.name == "Heating Up Mafia Town" or candidate.name == "Queen Vanessa's Manor"):
|
||||
@@ -495,6 +565,12 @@ def randomize_act_entrances(world: World):
|
||||
has_guaranteed = True
|
||||
break
|
||||
|
||||
if region.name in world.multiworld.ActPlando[world.player].keys() and is_valid_plando(world, region.name):
|
||||
candidate_list.clear()
|
||||
candidate_list.append(
|
||||
world.multiworld.get_region(world.multiworld.ActPlando[world.player].get(region.name), world.player))
|
||||
break
|
||||
|
||||
# Already mapped onto something else
|
||||
if candidate in shuffled_list:
|
||||
continue
|
||||
@@ -513,18 +589,11 @@ def randomize_act_entrances(world: World):
|
||||
or region.name not in purple_time_rifts and candidate.name in purple_time_rifts:
|
||||
continue
|
||||
|
||||
# Don't map Alpine to its own finale
|
||||
if region.name == "The Illness has Spread" and candidate.name == "Alpine Free Roam":
|
||||
if region.name in blacklisted_combos.keys() and candidate.name in blacklisted_combos[region.name]:
|
||||
continue
|
||||
|
||||
# Ditto for Metro
|
||||
if region.name == "Rush Hour" and candidate.name == "Nyakuza Free Roam":
|
||||
continue
|
||||
|
||||
# CTR entrance and Tour aren't a finale, but have a fuck ton of unlock requirements
|
||||
if world.multiworld.NoFreeRoamFinale[world.player].value > 0 and "Free Roam" in candidate.name:
|
||||
if region.name in chapter_finales or region.name == "Cheating the Race" \
|
||||
or world.multiworld.EndGoal[world.player].value == 1 and region.name == "Time Rift - Tour":
|
||||
if world.multiworld.FinaleShuffle[world.player].value > 0 and region.name in chapter_finales:
|
||||
if candidate.name not in chapter_finales:
|
||||
continue
|
||||
|
||||
if region.name in rift_access_regions and candidate.name in rift_access_regions[region.name]:
|
||||
@@ -532,8 +601,18 @@ def randomize_act_entrances(world: World):
|
||||
|
||||
candidate_list.append(candidate)
|
||||
|
||||
candidate: Region = candidate_list[world.multiworld.random.randint(0, len(candidate_list)-1)]
|
||||
candidate: Region
|
||||
if len(candidate_list) > 0:
|
||||
candidate = candidate_list[world.multiworld.random.randint(0, len(candidate_list)-1)]
|
||||
else:
|
||||
# plando can still break certain rules, so acts may not always end up shuffled.
|
||||
for c in region_list:
|
||||
if c not in shuffled_list:
|
||||
candidate = c
|
||||
break
|
||||
|
||||
shuffled_list.append(candidate)
|
||||
# print(region, candidate)
|
||||
|
||||
# Vanilla
|
||||
if candidate.name == region.name:
|
||||
@@ -588,21 +667,17 @@ def get_act_regions(world: World) -> typing.List[Region]:
|
||||
|
||||
|
||||
def is_act_blacklisted(world: World, name: str) -> bool:
|
||||
plando: bool = name in world.multiworld.ActPlando[world.player].keys() \
|
||||
or name in world.multiworld.ActPlando[world.player].values()
|
||||
|
||||
if name == "The Finale":
|
||||
return world.multiworld.EndGoal[world.player].value == 1
|
||||
|
||||
if name == "Alpine Free Roam":
|
||||
return world.multiworld.VanillaAlpine[world.player].value > 0
|
||||
|
||||
if name == "The Illness has Spread":
|
||||
return world.multiworld.VanillaAlpine[world.player].value == 2
|
||||
|
||||
if name == "Nyakuza Free Roam":
|
||||
return world.multiworld.VanillaMetro[world.player].value > 0
|
||||
return not plando and world.multiworld.EndGoal[world.player].value == 1
|
||||
|
||||
if name == "Rush Hour":
|
||||
return world.multiworld.EndGoal[world.player].value == 2 \
|
||||
or world.multiworld.VanillaMetro[world.player].value == 2
|
||||
return not plando and world.multiworld.EndGoal[world.player].value == 2
|
||||
|
||||
if name == "Time Rift - Tour":
|
||||
return world.multiworld.ExcludeTour[world.player].value > 0
|
||||
|
||||
return name in blacklisted_acts.values()
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ def set_rules(world: World):
|
||||
if data.hit_requirement > 0:
|
||||
if data.hit_requirement == 1:
|
||||
add_rule(location, lambda state: can_hit(state, world))
|
||||
else: # Can bypass with Dweller Mask (dweller bells)
|
||||
elif data.hit_requirement == 2: # Can bypass with Dweller Mask (dweller bells)
|
||||
add_rule(location, lambda state: can_hit(state, world) or can_use_hat(state, world, HatType.DWELLER))
|
||||
|
||||
if get_difficulty(world) >= 1:
|
||||
|
||||
Reference in New Issue
Block a user