mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-08 16:43:48 -07:00
Compare commits
1 Commits
NewSoupVi-
...
NewSoupVi-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c59264dee8 |
@@ -150,26 +150,6 @@ sample_chao_names = [
|
||||
"Hubert",
|
||||
"Corvus",
|
||||
"Nigel",
|
||||
"Benjamin",
|
||||
"Gooey",
|
||||
"Maddy",
|
||||
"AFGNCAAP",
|
||||
"Reinhardt",
|
||||
"Claire",
|
||||
"Yoshi",
|
||||
"Peasley",
|
||||
"Faux",
|
||||
"Naija",
|
||||
"Kaiba",
|
||||
"Hat Kid",
|
||||
"TzTokJad",
|
||||
"Sora",
|
||||
"WoodMan",
|
||||
"Yachty",
|
||||
"Grieve",
|
||||
"Portia",
|
||||
"Graves",
|
||||
"Kaycee",
|
||||
]
|
||||
|
||||
totally_real_item_names = [
|
||||
@@ -260,35 +240,6 @@ totally_real_item_names = [
|
||||
"Ladder",
|
||||
|
||||
"Visible Dots",
|
||||
|
||||
"CooCoo",
|
||||
|
||||
"Blueberry",
|
||||
|
||||
"Ear of Luigi",
|
||||
|
||||
"Mega Nut",
|
||||
|
||||
"DUELIST ALLIANCE",
|
||||
"DUEL OVERLOAD",
|
||||
"POWER OF THE ELEMENTS",
|
||||
"S:P Little Knight",
|
||||
"Red-Eyes Dark Dragoon",
|
||||
|
||||
"Fire Hat",
|
||||
|
||||
"Area: Taverly",
|
||||
"Area: Meiyerditch",
|
||||
"Fire Cape",
|
||||
|
||||
"Donald Zeta Flare",
|
||||
|
||||
"Category One of a Kind",
|
||||
"Category Fuller House",
|
||||
|
||||
"Passive Camoflage",
|
||||
|
||||
"Earth Card",
|
||||
]
|
||||
|
||||
all_exits = [
|
||||
|
||||
@@ -1,82 +1,6 @@
|
||||
# Sonic Adventure 2 Battle - Changelog
|
||||
|
||||
|
||||
## v2.4 - Minigame Madness
|
||||
|
||||
### Features:
|
||||
- New Goal
|
||||
- Minigame Madness
|
||||
- Win a certain number of each type of Minigame Trap, then defeat the Finalhazard to win!
|
||||
- How many of each Minigame are required can be set by an Option
|
||||
- When the required amount of a Minigame has been received, that Minigame can be replayed in the Chao World Lobby
|
||||
- New optional Location Checks
|
||||
- Bigsanity
|
||||
- Go fishing with Big in each stage for a Location Check
|
||||
- Itemboxsanity
|
||||
- Either Extra Life Boxes or All Item Boxes
|
||||
- New Items
|
||||
- New Traps
|
||||
- Literature Trap
|
||||
- Controller Drift Trap
|
||||
- Poison Trap
|
||||
- Bee Trap
|
||||
- New Minigame Traps
|
||||
- Breakout Trap
|
||||
- Fishing Trap
|
||||
- Trivia Trap
|
||||
- Pokemon Trivia Trap
|
||||
- Pokemon Count Trap
|
||||
- Number Sequence Trap
|
||||
- Light Up Path Trap
|
||||
- Pinball Trap
|
||||
- Math Quiz Trap
|
||||
- Snake Trap
|
||||
- Input Sequence Trap
|
||||
- Trap Link
|
||||
- When you receive a trap, you send a copy of it to every other player with Trap Link enabled
|
||||
- Boss Gate Plando
|
||||
- Expert Logic Difficulty
|
||||
- Use at your own risk. This difficulty requires complete mastery of SA2.
|
||||
- Missions can now be enabled and disabled per-character, instead of just per-style
|
||||
- Minigame Difficulty can now be set to "Chaos", which selects a new difficulty randomly per-trap received
|
||||
|
||||
### Quality of Life:
|
||||
- Gate Stages and Mission Orders are now displayed in the spoiler log
|
||||
- Additional play stats are saved and displayed with the randomizer credits
|
||||
- Stage Locations progress UI now displays in multiple pages when Itemboxsanity is enabled
|
||||
- Current stage mission order and progress are now shown when paused in-level
|
||||
- Chaos Emeralds are now shown when paused in-level
|
||||
- Location Name Groups were created
|
||||
- Moved SA2B to the new Options system
|
||||
- Option Presets were created
|
||||
- Error Messages are more obvious
|
||||
|
||||
### Bug Fixes:
|
||||
- Added missing `Dry Lagoon - 12 Animals` location
|
||||
- Flying Dog boss should no longer crash when you have done at least 3 Intermediate Kart Races
|
||||
- Invincibility can no longer be received in the King Boom Boo fight, preventing a crash
|
||||
- Chaos Emeralds should no longer disproportionately end up in Cannon's Core or the final Level Gate
|
||||
- Going into submenus from the pause menu should no longer reset traps
|
||||
- `Sonic - Magic Gloves` are now plural
|
||||
- Junk items will no longer cause a crash when in a falling state
|
||||
- Chao Garden:
|
||||
- Prevent races from occasionally becoming uncompletable when using the "Prize Only" option
|
||||
- Properly allow Hero Chao to participate in Dark Races
|
||||
- Don't allow the Chao Garden to send locations when connected to an invalid server
|
||||
- Prevent the Chao Garden from resetting your life count
|
||||
- Fix Chao World Entrance Shuffle causing inaccessible Neutral Garden
|
||||
- Fix pressing the 'B' button to take you to the proper location in Chao World Entrance Shuffle
|
||||
- Prevent Chao Karate progress icon overflow
|
||||
- Prevent changing Chao Timescale while paused or while a Minigame is active
|
||||
- Logic Fixes:
|
||||
- `Mission Street - Chao Key 1` (Hard Logic) now requires no upgrades
|
||||
- `Mission Street - Chao Key 2` (Hard Logic) now requires no upgrades
|
||||
- `Crazy Gadget - Hidden 1` (Standard Logic) now requires `Sonic - Bounce Bracelet` instead of `Sonic - Light Shoes`
|
||||
- `Lost Colony - Hidden 1` (Standard Logic) now requires `Eggman - Jet Engine`
|
||||
- `Mad Space - Gold Beetle` (Standard Logic) now only requires `Rouge - Iron Boots`
|
||||
- `Cosmic Wall - Gold Beetle` (Standard and Hard Logic) now only requires `Eggman - Jet Engine`
|
||||
|
||||
|
||||
## v2.3 - The Chao Update
|
||||
|
||||
### Features:
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from worlds.AutoWorld import World
|
||||
|
||||
from .Names import LocationName
|
||||
from .Options import GateBossPlando
|
||||
|
||||
|
||||
speed_characters_1 = "sonic vs shadow 1"
|
||||
speed_characters_2 = "sonic vs shadow 2"
|
||||
mech_characters_1 = "tails vs eggman 1"
|
||||
mech_characters_2 = "tails vs eggman 2"
|
||||
hunt_characters_1 = "knuckles vs rouge 1"
|
||||
big_foot = "big foot"
|
||||
hot_shot = "hot shot"
|
||||
flying_dog = "flying dog"
|
||||
egg_golem_sonic = "egg golem (sonic)"
|
||||
egg_golem_eggman = "egg golem (eggman)"
|
||||
king_boom_boo = "king boom boo"
|
||||
speed_characters_1 = "Sonic vs Shadow 1"
|
||||
speed_characters_2 = "Sonic vs Shadow 2"
|
||||
mech_characters_1 = "Tails vs Eggman 1"
|
||||
mech_characters_2 = "Tails vs Eggman 2"
|
||||
hunt_characters_1 = "Knuckles vs Rouge 1"
|
||||
big_foot = "F-6t BIG FOOT"
|
||||
hot_shot = "B-3x HOT SHOT"
|
||||
flying_dog = "R-1/A FLYING DOG"
|
||||
egg_golem_sonic = "Egg Golem (Sonic)"
|
||||
egg_golem_eggman = "Egg Golem (Eggman)"
|
||||
king_boom_boo = "King Boom Boo"
|
||||
|
||||
gate_bosses_no_requirements_table = {
|
||||
speed_characters_1: 0,
|
||||
@@ -50,83 +45,44 @@ all_gate_bosses_table = {
|
||||
}
|
||||
|
||||
|
||||
boss_id_to_name = {
|
||||
0: "Sonic vs Shadow 1",
|
||||
1: "Sonic vs Shadow 2",
|
||||
2: "Tails vs Eggman 1",
|
||||
3: "Tails vs Eggman 2",
|
||||
4: "Knuckles vs Rouge 1",
|
||||
5: "F-6t BIG FOOT",
|
||||
6: "B-3x HOT SHOT",
|
||||
7: "R-1/A FLYING DOG",
|
||||
8: "Egg Golem (Sonic)",
|
||||
9: "Egg Golem (Eggman)",
|
||||
10: "King Boom Boo",
|
||||
11: "Sonic vs Shadow 1",
|
||||
12: "Sonic vs Shadow 2",
|
||||
13: "Tails vs Eggman 1",
|
||||
14: "Tails vs Eggman 2",
|
||||
15: "Knuckles vs Rouge 1",
|
||||
}
|
||||
|
||||
def get_boss_name(boss: int):
|
||||
return boss_id_to_name[boss]
|
||||
for key, value in gate_bosses_no_requirements_table.items():
|
||||
if value == boss:
|
||||
return key
|
||||
for key, value in gate_bosses_with_requirements_table.items():
|
||||
if value == boss:
|
||||
return key
|
||||
for key, value in extra_boss_rush_bosses_table.items():
|
||||
if value == boss:
|
||||
return key
|
||||
|
||||
|
||||
def boss_has_requirement(boss: int):
|
||||
return boss >= len(gate_bosses_no_requirements_table)
|
||||
|
||||
|
||||
def get_gate_bosses(world: World):
|
||||
def get_gate_bosses(multiworld: MultiWorld, world: World):
|
||||
selected_bosses: typing.List[int] = []
|
||||
boss_gates: typing.List[int] = []
|
||||
available_bosses: typing.List[str] = list(gate_bosses_no_requirements_table.keys())
|
||||
world.random.shuffle(available_bosses)
|
||||
|
||||
gate_boss_plando: typing.Union[int, str] = world.options.gate_boss_plando.value
|
||||
plando_bosses = ["None", "None", "None", "None", "None"]
|
||||
if isinstance(gate_boss_plando, str):
|
||||
# boss plando
|
||||
options = gate_boss_plando.split(";")
|
||||
gate_boss_plando = GateBossPlando.options[options.pop()]
|
||||
for option in options:
|
||||
if "-" in option:
|
||||
loc, boss = option.split("-")
|
||||
boss_num = LocationName.boss_gate_names[loc]
|
||||
|
||||
if boss_num >= world.options.number_of_level_gates.value:
|
||||
# Don't reject bosses plando'd into gate bosses that won't exist
|
||||
pass
|
||||
|
||||
if boss in plando_bosses:
|
||||
# TODO: Raise error here. Duplicates not allowed
|
||||
pass
|
||||
|
||||
plando_bosses[boss_num] = boss
|
||||
|
||||
if boss in available_bosses:
|
||||
available_bosses.remove(boss)
|
||||
multiworld.random.shuffle(available_bosses)
|
||||
halfway = False
|
||||
|
||||
for x in range(world.options.number_of_level_gates):
|
||||
if ("king boom boo" not in selected_bosses) and ("king boom boo" not in available_bosses) and ((x + 1) / world.options.number_of_level_gates) > 0.5:
|
||||
if (not halfway) and ((x + 1) / world.options.number_of_level_gates) > 0.5:
|
||||
available_bosses.extend(gate_bosses_with_requirements_table)
|
||||
world.random.shuffle(available_bosses)
|
||||
|
||||
chosen_boss = available_bosses[0]
|
||||
if plando_bosses[x] != "None":
|
||||
available_bosses.append(plando_bosses[x])
|
||||
chosen_boss = plando_bosses[x]
|
||||
|
||||
selected_bosses.append(all_gate_bosses_table[chosen_boss])
|
||||
multiworld.random.shuffle(available_bosses)
|
||||
halfway = True
|
||||
selected_bosses.append(all_gate_bosses_table[available_bosses[0]])
|
||||
boss_gates.append(x + 1)
|
||||
available_bosses.remove(chosen_boss)
|
||||
available_bosses.remove(available_bosses[0])
|
||||
|
||||
bosses: typing.Dict[int, int] = dict(zip(boss_gates, selected_bosses))
|
||||
|
||||
return bosses
|
||||
|
||||
|
||||
def get_boss_rush_bosses(world: World):
|
||||
def get_boss_rush_bosses(multiworld: MultiWorld, world: World):
|
||||
|
||||
if world.options.boss_rush_shuffle == 0:
|
||||
boss_list_o = list(range(0, 16))
|
||||
@@ -136,21 +92,21 @@ def get_boss_rush_bosses(world: World):
|
||||
elif world.options.boss_rush_shuffle == 1:
|
||||
boss_list_o = list(range(0, 16))
|
||||
boss_list_s = boss_list_o.copy()
|
||||
world.random.shuffle(boss_list_s)
|
||||
multiworld.random.shuffle(boss_list_s)
|
||||
|
||||
return dict(zip(boss_list_o, boss_list_s))
|
||||
elif world.options.boss_rush_shuffle == 2:
|
||||
boss_list_o = list(range(0, 16))
|
||||
boss_list_s = [world.random.choice(boss_list_o) for i in range(0, 16)]
|
||||
boss_list_s = [multiworld.random.choice(boss_list_o) for i in range(0, 16)]
|
||||
if 10 not in boss_list_s:
|
||||
boss_list_s[world.random.randint(0, 15)] = 10
|
||||
boss_list_s[multiworld.random.randint(0, 15)] = 10
|
||||
|
||||
return dict(zip(boss_list_o, boss_list_s))
|
||||
elif world.options.boss_rush_shuffle == 3:
|
||||
boss_list_o = list(range(0, 16))
|
||||
boss_list_s = [world.random.choice(boss_list_o)] * len(boss_list_o)
|
||||
boss_list_s = [multiworld.random.choice(boss_list_o)] * len(boss_list_o)
|
||||
if 10 not in boss_list_s:
|
||||
boss_list_s[world.random.randint(0, 15)] = 10
|
||||
boss_list_s[multiworld.random.randint(0, 15)] = 10
|
||||
|
||||
return dict(zip(boss_list_o, boss_list_s))
|
||||
else:
|
||||
|
||||
@@ -2,6 +2,7 @@ import typing
|
||||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from .Names import ItemName
|
||||
from worlds.alttp import ALTTPWorld
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
@@ -13,7 +14,7 @@ class ItemData(typing.NamedTuple):
|
||||
|
||||
|
||||
class SA2BItem(Item):
|
||||
game: str = "Sonic Adventure 2 Battle"
|
||||
game: str = "Sonic Adventure 2: Battle"
|
||||
|
||||
def __init__(self, name, classification: ItemClassification, code: int = None, player: int = None):
|
||||
super(SA2BItem, self).__init__(name, classification, code, player)
|
||||
@@ -72,36 +73,19 @@ junk_table = {
|
||||
}
|
||||
|
||||
trap_table = {
|
||||
ItemName.omochao_trap: ItemData(0xFF0030, False, True),
|
||||
ItemName.timestop_trap: ItemData(0xFF0031, False, True),
|
||||
ItemName.confuse_trap: ItemData(0xFF0032, False, True),
|
||||
ItemName.tiny_trap: ItemData(0xFF0033, False, True),
|
||||
ItemName.gravity_trap: ItemData(0xFF0034, False, True),
|
||||
ItemName.exposition_trap: ItemData(0xFF0035, False, True),
|
||||
#ItemName.darkness_trap: ItemData(0xFF0036, False, True),
|
||||
ItemName.ice_trap: ItemData(0xFF0037, False, True),
|
||||
ItemName.slow_trap: ItemData(0xFF0038, False, True),
|
||||
ItemName.cutscene_trap: ItemData(0xFF0039, False, True),
|
||||
ItemName.reverse_trap: ItemData(0xFF003A, False, True),
|
||||
ItemName.literature_trap: ItemData(0xFF003B, False, True),
|
||||
ItemName.controller_drift_trap: ItemData(0xFF003C, False, True),
|
||||
ItemName.poison_trap: ItemData(0xFF003D, False, True),
|
||||
ItemName.bee_trap: ItemData(0xFF003E, False, True),
|
||||
}
|
||||
ItemName.omochao_trap: ItemData(0xFF0030, False, True),
|
||||
ItemName.timestop_trap: ItemData(0xFF0031, False, True),
|
||||
ItemName.confuse_trap: ItemData(0xFF0032, False, True),
|
||||
ItemName.tiny_trap: ItemData(0xFF0033, False, True),
|
||||
ItemName.gravity_trap: ItemData(0xFF0034, False, True),
|
||||
ItemName.exposition_trap: ItemData(0xFF0035, False, True),
|
||||
#ItemName.darkness_trap: ItemData(0xFF0036, False, True),
|
||||
ItemName.ice_trap: ItemData(0xFF0037, False, True),
|
||||
ItemName.slow_trap: ItemData(0xFF0038, False, True),
|
||||
ItemName.cutscene_trap: ItemData(0xFF0039, False, True),
|
||||
ItemName.reverse_trap: ItemData(0xFF003A, False, True),
|
||||
|
||||
minigame_trap_table = {
|
||||
ItemName.pong_trap: ItemData(0xFF0050, False, True),
|
||||
ItemName.breakout_trap: ItemData(0xFF0051, False, True),
|
||||
ItemName.fishing_trap: ItemData(0xFF0052, False, True),
|
||||
ItemName.trivia_trap: ItemData(0xFF0053, False, True),
|
||||
ItemName.pokemon_trivia_trap: ItemData(0xFF0054, False, True),
|
||||
ItemName.pokemon_count_trap: ItemData(0xFF0055, False, True),
|
||||
ItemName.number_sequence_trap: ItemData(0xFF0056, False, True),
|
||||
ItemName.light_up_path_trap: ItemData(0xFF0057, False, True),
|
||||
ItemName.pinball_trap: ItemData(0xFF0058, False, True),
|
||||
ItemName.math_quiz_trap: ItemData(0xFF0059, False, True),
|
||||
ItemName.snake_trap: ItemData(0xFF005A, False, True),
|
||||
ItemName.input_sequence_trap: ItemData(0xFF005B, False, True),
|
||||
ItemName.pong_trap: ItemData(0xFF0050, False, True),
|
||||
}
|
||||
|
||||
emeralds_table = {
|
||||
@@ -251,7 +235,7 @@ chaos_drives_table = {
|
||||
}
|
||||
|
||||
event_table = {
|
||||
ItemName.maria: ItemData(None, True),
|
||||
ItemName.maria: ItemData(0xFF001D, True),
|
||||
}
|
||||
|
||||
# Complete item table.
|
||||
@@ -260,7 +244,6 @@ item_table = {
|
||||
**upgrades_table,
|
||||
**junk_table,
|
||||
**trap_table,
|
||||
**minigame_trap_table,
|
||||
**emeralds_table,
|
||||
**eggs_table,
|
||||
**fruits_table,
|
||||
@@ -268,6 +251,7 @@ item_table = {
|
||||
**hats_table,
|
||||
**animals_table,
|
||||
**chaos_drives_table,
|
||||
**event_table,
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
|
||||
@@ -279,12 +263,7 @@ item_groups: typing.Dict[str, str] = {
|
||||
"Seeds": list(seeds_table.keys()),
|
||||
"Hats": list(hats_table.keys()),
|
||||
"Traps": list(trap_table.keys()),
|
||||
"Minigames": list(minigame_trap_table.keys()),
|
||||
}
|
||||
|
||||
try:
|
||||
from worlds.alttp import ALTTPWorld
|
||||
ALTTPWorld.pedestal_credit_texts[item_table[ItemName.sonic_light_shoes].code] = "and the Soap Shoes"
|
||||
ALTTPWorld.pedestal_credit_texts[item_table[ItemName.shadow_air_shoes].code] = "and the Soap Shoes"
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
ALTTPWorld.pedestal_credit_texts[item_table[ItemName.sonic_light_shoes].code] = "and the Soap Shoes"
|
||||
ALTTPWorld.pedestal_credit_texts[item_table[ItemName.shadow_air_shoes].code] = "and the Soap Shoes"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -119,14 +119,11 @@ mission_orders: typing.List[typing.List[int]] = [
|
||||
[4, 5, 3, 2, 1],
|
||||
]
|
||||
|
||||
### 0: Sonic
|
||||
### 1: Tails
|
||||
### 2: Knuckles
|
||||
### 3: Shadow
|
||||
### 4: Eggman
|
||||
### 5: Rouge
|
||||
### 6: Kart
|
||||
### 7: Cannon's Core
|
||||
### 0: Speed
|
||||
### 1: Mech
|
||||
### 2: Hunt
|
||||
### 3: Kart
|
||||
### 4: Cannon's Core
|
||||
level_styles: typing.List[int] = [
|
||||
0,
|
||||
2,
|
||||
@@ -136,7 +133,7 @@ level_styles: typing.List[int] = [
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
6,
|
||||
3,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
@@ -145,22 +142,22 @@ level_styles: typing.List[int] = [
|
||||
0,
|
||||
0,
|
||||
|
||||
4,
|
||||
5,
|
||||
4,
|
||||
3,
|
||||
5,
|
||||
4,
|
||||
4,
|
||||
5,
|
||||
3,
|
||||
6,
|
||||
3,
|
||||
5,
|
||||
4,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
0,
|
||||
|
||||
7,
|
||||
4,
|
||||
]
|
||||
|
||||
stage_name_prefixes: typing.List[str] = [
|
||||
@@ -204,33 +201,21 @@ def get_mission_count_table(multiworld: MultiWorld, world: World, player: int):
|
||||
for level in range(31):
|
||||
mission_count_table[level] = 0
|
||||
else:
|
||||
sonic_active_missions = 1
|
||||
tails_active_missions = 1
|
||||
knuckles_active_missions = 1
|
||||
shadow_active_missions = 1
|
||||
eggman_active_missions = 1
|
||||
rouge_active_missions = 1
|
||||
speed_active_missions = 1
|
||||
mech_active_missions = 1
|
||||
hunt_active_missions = 1
|
||||
kart_active_missions = 1
|
||||
cannons_core_active_missions = 1
|
||||
|
||||
for i in range(2,6):
|
||||
if getattr(world.options, "sonic_mission_" + str(i), None):
|
||||
sonic_active_missions += 1
|
||||
if getattr(world.options, "speed_mission_" + str(i), None):
|
||||
speed_active_missions += 1
|
||||
|
||||
if getattr(world.options, "tails_mission_" + str(i), None):
|
||||
tails_active_missions += 1
|
||||
if getattr(world.options, "mech_mission_" + str(i), None):
|
||||
mech_active_missions += 1
|
||||
|
||||
if getattr(world.options, "knuckles_mission_" + str(i), None):
|
||||
knuckles_active_missions += 1
|
||||
|
||||
if getattr(world.options, "shadow_mission_" + str(i), None):
|
||||
shadow_active_missions += 1
|
||||
|
||||
if getattr(world.options, "eggman_mission_" + str(i), None):
|
||||
eggman_active_missions += 1
|
||||
|
||||
if getattr(world.options, "rouge_mission_" + str(i), None):
|
||||
rouge_active_missions += 1
|
||||
if getattr(world.options, "hunt_mission_" + str(i), None):
|
||||
hunt_active_missions += 1
|
||||
|
||||
if getattr(world.options, "kart_mission_" + str(i), None):
|
||||
kart_active_missions += 1
|
||||
@@ -238,22 +223,16 @@ def get_mission_count_table(multiworld: MultiWorld, world: World, player: int):
|
||||
if getattr(world.options, "cannons_core_mission_" + str(i), None):
|
||||
cannons_core_active_missions += 1
|
||||
|
||||
sonic_active_missions = min(sonic_active_missions, world.options.sonic_mission_count.value)
|
||||
tails_active_missions = min(tails_active_missions, world.options.tails_mission_count.value)
|
||||
knuckles_active_missions = min(knuckles_active_missions, world.options.knuckles_mission_count.value)
|
||||
shadow_active_missions = min(shadow_active_missions, world.options.sonic_mission_count.value)
|
||||
eggman_active_missions = min(eggman_active_missions, world.options.eggman_mission_count.value)
|
||||
rouge_active_missions = min(rouge_active_missions, world.options.rouge_mission_count.value)
|
||||
speed_active_missions = min(speed_active_missions, world.options.speed_mission_count.value)
|
||||
mech_active_missions = min(mech_active_missions, world.options.mech_mission_count.value)
|
||||
hunt_active_missions = min(hunt_active_missions, world.options.hunt_mission_count.value)
|
||||
kart_active_missions = min(kart_active_missions, world.options.kart_mission_count.value)
|
||||
cannons_core_active_missions = min(cannons_core_active_missions, world.options.cannons_core_mission_count.value)
|
||||
|
||||
active_missions: typing.List[typing.List[int]] = [
|
||||
sonic_active_missions,
|
||||
tails_active_missions,
|
||||
knuckles_active_missions,
|
||||
shadow_active_missions,
|
||||
eggman_active_missions,
|
||||
rouge_active_missions,
|
||||
speed_active_missions,
|
||||
mech_active_missions,
|
||||
hunt_active_missions,
|
||||
kart_active_missions,
|
||||
cannons_core_active_missions
|
||||
]
|
||||
@@ -273,34 +252,22 @@ def get_mission_table(multiworld: MultiWorld, world: World, player: int):
|
||||
for level in range(31):
|
||||
mission_table[level] = 0
|
||||
else:
|
||||
sonic_active_missions: typing.List[int] = [1]
|
||||
tails_active_missions: typing.List[int] = [1]
|
||||
knuckles_active_missions: typing.List[int] = [1]
|
||||
shadow_active_missions: typing.List[int] = [1]
|
||||
eggman_active_missions: typing.List[int] = [1]
|
||||
rouge_active_missions: typing.List[int] = [1]
|
||||
speed_active_missions: typing.List[int] = [1]
|
||||
mech_active_missions: typing.List[int] = [1]
|
||||
hunt_active_missions: typing.List[int] = [1]
|
||||
kart_active_missions: typing.List[int] = [1]
|
||||
cannons_core_active_missions: typing.List[int] = [1]
|
||||
|
||||
# Add included missions
|
||||
for i in range(2,6):
|
||||
if getattr(world.options, "sonic_mission_" + str(i), None):
|
||||
sonic_active_missions.append(i)
|
||||
if getattr(world.options, "speed_mission_" + str(i), None):
|
||||
speed_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "tails_mission_" + str(i), None):
|
||||
tails_active_missions.append(i)
|
||||
if getattr(world.options, "mech_mission_" + str(i), None):
|
||||
mech_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "knuckles_mission_" + str(i), None):
|
||||
knuckles_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "shadow_mission_" + str(i), None):
|
||||
shadow_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "eggman_mission_" + str(i), None):
|
||||
eggman_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "rouge_mission_" + str(i), None):
|
||||
rouge_active_missions.append(i)
|
||||
if getattr(world.options, "hunt_mission_" + str(i), None):
|
||||
hunt_active_missions.append(i)
|
||||
|
||||
if getattr(world.options, "kart_mission_" + str(i), None):
|
||||
kart_active_missions.append(i)
|
||||
@@ -309,12 +276,9 @@ def get_mission_table(multiworld: MultiWorld, world: World, player: int):
|
||||
cannons_core_active_missions.append(i)
|
||||
|
||||
active_missions: typing.List[typing.List[int]] = [
|
||||
sonic_active_missions,
|
||||
tails_active_missions,
|
||||
knuckles_active_missions,
|
||||
shadow_active_missions,
|
||||
eggman_active_missions,
|
||||
rouge_active_missions,
|
||||
speed_active_missions,
|
||||
mech_active_missions,
|
||||
hunt_active_missions,
|
||||
kart_active_missions,
|
||||
cannons_core_active_missions
|
||||
]
|
||||
@@ -364,60 +328,13 @@ def get_mission_table(multiworld: MultiWorld, world: World, player: int):
|
||||
|
||||
|
||||
def get_first_and_last_cannons_core_missions(mission_map: typing.Dict[int, int], mission_count_map: typing.Dict[int, int]):
|
||||
mission_count = mission_count_map[30]
|
||||
mission_order: typing.List[int] = mission_orders[mission_map[30]]
|
||||
stage_prefix: str = stage_name_prefixes[30]
|
||||
mission_count = mission_count_map[30]
|
||||
mission_order: typing.List[int] = mission_orders[mission_map[30]]
|
||||
stage_prefix: str = stage_name_prefixes[30]
|
||||
|
||||
first_mission_number = mission_order[0]
|
||||
last_mission_number = mission_order[mission_count - 1]
|
||||
first_location_name: str = stage_prefix + str(first_mission_number)
|
||||
last_location_name: str = stage_prefix + str(last_mission_number)
|
||||
first_mission_number = mission_order[0]
|
||||
last_mission_number = mission_order[mission_count - 1]
|
||||
first_location_name: str = stage_prefix + str(first_mission_number)
|
||||
last_location_name: str = stage_prefix + str(last_mission_number)
|
||||
|
||||
return first_location_name, last_location_name
|
||||
|
||||
|
||||
def print_mission_orders_to_spoiler(mission_map: typing.Dict[int, int],
|
||||
mission_count_map: typing.Dict[int, int],
|
||||
shuffled_region_list: typing.Dict[int, int],
|
||||
levels_per_gate: typing.Dict[int, int],
|
||||
player_name: str,
|
||||
spoiler_handle: typing.TextIO):
|
||||
spoiler_handle.write("\n")
|
||||
header_text = "SA2 Mission Orders for {}:\n"
|
||||
header_text = header_text.format(player_name)
|
||||
spoiler_handle.write(header_text)
|
||||
|
||||
level_index = 0
|
||||
for gate_idx in range(len(levels_per_gate)):
|
||||
gate_len = levels_per_gate[gate_idx]
|
||||
gate_levels = shuffled_region_list[int(level_index):int(level_index+gate_len)]
|
||||
gate_levels.sort()
|
||||
|
||||
gate_text = "Gate {}:\n"
|
||||
gate_text = gate_text.format(gate_idx)
|
||||
spoiler_handle.write(gate_text)
|
||||
|
||||
for i in range(len(gate_levels)):
|
||||
stage = gate_levels[i]
|
||||
mission_count = mission_count_map[stage]
|
||||
mission_order: typing.List[int] = mission_orders[mission_map[stage]]
|
||||
stage_prefix: str = stage_name_prefixes[stage]
|
||||
|
||||
for mission in range(mission_count):
|
||||
stage_prefix += str(mission_order[mission]) + " "
|
||||
|
||||
spoiler_handle.write(stage_prefix)
|
||||
spoiler_handle.write("\n")
|
||||
|
||||
level_index += gate_len
|
||||
spoiler_handle.write("\n")
|
||||
|
||||
mission_count = mission_count_map[30]
|
||||
mission_order: typing.List[int] = mission_orders[mission_map[30]]
|
||||
stage_prefix: str = stage_name_prefixes[30]
|
||||
|
||||
for mission in range(mission_count):
|
||||
stage_prefix += str(mission_order[mission]) + " "
|
||||
|
||||
spoiler_handle.write(stage_prefix)
|
||||
spoiler_handle.write("\n\n")
|
||||
return first_location_name, last_location_name
|
||||
|
||||
@@ -5,7 +5,7 @@ emblem = "Emblem"
|
||||
market_token = "Chao Coin"
|
||||
|
||||
# Upgrade Definitions
|
||||
sonic_gloves = "Sonic - Magic Gloves"
|
||||
sonic_gloves = "Sonic - Magic Glove"
|
||||
sonic_light_shoes = "Sonic - Light Shoes"
|
||||
sonic_ancient_light = "Sonic - Ancient Light"
|
||||
sonic_bounce_bracelet = "Sonic - Bounce Bracelet"
|
||||
@@ -51,34 +51,19 @@ invincibility = "Invincibility"
|
||||
|
||||
|
||||
# Traps
|
||||
omochao_trap = "OmoTrap"
|
||||
timestop_trap = "Chaos Control Trap"
|
||||
confuse_trap = "Confusion Trap"
|
||||
tiny_trap = "Tiny Trap"
|
||||
gravity_trap = "Gravity Trap"
|
||||
exposition_trap = "Exposition Trap"
|
||||
darkness_trap = "Darkness Trap"
|
||||
ice_trap = "Ice Trap"
|
||||
slow_trap = "Slow Trap"
|
||||
cutscene_trap = "Cutscene Trap"
|
||||
reverse_trap = "Reverse Trap"
|
||||
literature_trap = "Literature Trap"
|
||||
controller_drift_trap = "Controller Drift Trap"
|
||||
poison_trap = "Poison Trap"
|
||||
bee_trap = "Bee Trap"
|
||||
omochao_trap = "OmoTrap"
|
||||
timestop_trap = "Chaos Control Trap"
|
||||
confuse_trap = "Confusion Trap"
|
||||
tiny_trap = "Tiny Trap"
|
||||
gravity_trap = "Gravity Trap"
|
||||
exposition_trap = "Exposition Trap"
|
||||
darkness_trap = "Darkness Trap"
|
||||
ice_trap = "Ice Trap"
|
||||
slow_trap = "Slow Trap"
|
||||
cutscene_trap = "Cutscene Trap"
|
||||
reverse_trap = "Reverse Trap"
|
||||
|
||||
pong_trap = "Pong Trap"
|
||||
breakout_trap = "Breakout Trap"
|
||||
fishing_trap = "Fishing Trap"
|
||||
trivia_trap = "Trivia Trap"
|
||||
pokemon_trivia_trap = "Pokemon Trivia Trap"
|
||||
pokemon_count_trap = "Pokemon Count Trap"
|
||||
number_sequence_trap = "Number Sequence Trap"
|
||||
light_up_path_trap = "Light Up Path Trap"
|
||||
pinball_trap = "Pinball Trap"
|
||||
math_quiz_trap = "Math Quiz Trap"
|
||||
snake_trap = "Snake Trap"
|
||||
input_sequence_trap = "Input Sequence Trap"
|
||||
pong_trap = "Pong Trap"
|
||||
|
||||
|
||||
# Chaos Emeralds
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import Choice, Range, Option, OptionGroup, Toggle, DeathLink, DefaultOnToggle, PerGameCommonOptions, PlandoBosses
|
||||
|
||||
from .Names import LocationName
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
@@ -24,8 +22,6 @@ class Goal(Choice):
|
||||
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
|
||||
|
||||
Minigame Madness: Win a certain amount of each Minigame Trap, then defeat Finalhazard
|
||||
"""
|
||||
display_name = "Goal"
|
||||
option_biolizard = 0
|
||||
@@ -36,7 +32,6 @@ class Goal(Choice):
|
||||
option_cannons_core_boss_rush = 5
|
||||
option_boss_rush_chaos_emerald_hunt = 6
|
||||
option_chaos_chao = 7
|
||||
option_minigame_madness = 8
|
||||
default = 0
|
||||
|
||||
@classmethod
|
||||
@@ -76,66 +71,6 @@ class BossRushShuffle(Choice):
|
||||
default = 0
|
||||
|
||||
|
||||
class GateBossPlando(PlandoBosses):
|
||||
"""
|
||||
Possible Locations:
|
||||
"Gate 1 Boss"
|
||||
"Gate 2 Boss"
|
||||
"Gate 3 Boss"
|
||||
"Gate 4 Boss"
|
||||
"Gate 5 Boss"
|
||||
|
||||
Possible Bosses:
|
||||
"Sonic vs Shadow 1"
|
||||
"Sonic vs Shadow 2"
|
||||
"Tails vs Eggman 1"
|
||||
"Tails vs Eggman 2"
|
||||
"Knuckles vs Rouge 1"
|
||||
"BIG FOOT"
|
||||
"HOT SHOT"
|
||||
"FLYING DOG"
|
||||
"Egg Golem (Sonic)"
|
||||
"Egg Golem (Eggman)"
|
||||
"King Boom Boo"
|
||||
"""
|
||||
bosses = frozenset(LocationName.boss_names.keys())
|
||||
|
||||
locations = frozenset(LocationName.boss_gate_names.keys())
|
||||
|
||||
duplicate_bosses = False
|
||||
|
||||
@classmethod
|
||||
def can_place_boss(cls, boss: str, location: str) -> bool:
|
||||
return True
|
||||
|
||||
display_name = "Boss Shuffle"
|
||||
option_plando = 0
|
||||
|
||||
|
||||
class MinigameMadnessRequirement(Range):
|
||||
"""
|
||||
Determines how many of each Minigame Trap must be won (for Minigame Madness goal)
|
||||
|
||||
Receiving this many of a Minigame Trap will allow you to replay that minigame at-will in the Chao World lobby
|
||||
"""
|
||||
display_name = "Minigame Madness Trap Requirement"
|
||||
range_start = 1
|
||||
range_end = 10
|
||||
default = 3
|
||||
|
||||
|
||||
class MinigameMadnessMinimum(Range):
|
||||
"""
|
||||
Determines the minimum number of each Minigame Trap that are created (for Minigame Madness goal)
|
||||
|
||||
At least this many of each trap will be created as "Progression Traps", regardless of other trap option selections
|
||||
"""
|
||||
display_name = "Minigame Madness Trap Minimum"
|
||||
range_start = 1
|
||||
range_end = 10
|
||||
default = 5
|
||||
|
||||
|
||||
class BaseTrapWeight(Choice):
|
||||
"""
|
||||
Base Class for Trap Weights
|
||||
@@ -224,34 +159,6 @@ class ReverseTrapWeight(BaseTrapWeight):
|
||||
display_name = "Reverse Trap Weight"
|
||||
|
||||
|
||||
class LiteratureTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to read
|
||||
"""
|
||||
display_name = "Literature Trap Weight"
|
||||
|
||||
|
||||
class ControllerDriftTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which causes your control sticks to drift
|
||||
"""
|
||||
display_name = "Controller Drift Trap Weight"
|
||||
|
||||
|
||||
class PoisonTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which causes you to lose rings over time
|
||||
"""
|
||||
display_name = "Poison Trap Weight"
|
||||
|
||||
|
||||
class BeeTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which spawns a swarm of bees
|
||||
"""
|
||||
display_name = "Bee Trap Weight"
|
||||
|
||||
|
||||
class PongTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Pong minigame
|
||||
@@ -259,106 +166,14 @@ class PongTrapWeight(BaseTrapWeight):
|
||||
display_name = "Pong Trap Weight"
|
||||
|
||||
|
||||
class BreakoutTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Breakout minigame
|
||||
"""
|
||||
display_name = "Breakout Trap Weight"
|
||||
|
||||
|
||||
class FishingTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Fishing minigame
|
||||
"""
|
||||
display_name = "Fishing Trap Weight"
|
||||
|
||||
|
||||
class TriviaTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Trivia minigame
|
||||
"""
|
||||
display_name = "Trivia Trap Weight"
|
||||
|
||||
|
||||
class PokemonTriviaTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Pokemon Trivia minigame
|
||||
"""
|
||||
display_name = "Pokemon Trivia Trap Weight"
|
||||
|
||||
|
||||
class PokemonCountTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Pokemon Count minigame
|
||||
"""
|
||||
display_name = "Pokemon Count Trap Weight"
|
||||
|
||||
|
||||
class NumberSequenceTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Number Sequence minigame
|
||||
"""
|
||||
display_name = "Number Sequence Trap Weight"
|
||||
|
||||
|
||||
class LightUpPathTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Light Up Path minigame
|
||||
"""
|
||||
display_name = "Light Up Path Trap Weight"
|
||||
|
||||
|
||||
class PinballTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Pinball minigame
|
||||
"""
|
||||
display_name = "Pinball Trap Weight"
|
||||
|
||||
|
||||
class MathQuizTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to solve a math problem
|
||||
"""
|
||||
display_name = "Math Quiz Trap Weight"
|
||||
|
||||
|
||||
class SnakeTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to play a Snake minigame
|
||||
"""
|
||||
display_name = "Snake Trap Weight"
|
||||
|
||||
|
||||
class InputSequenceTrapWeight(BaseTrapWeight):
|
||||
"""
|
||||
Likelihood of receiving a trap which forces you to press a sequence of inputs
|
||||
"""
|
||||
display_name = "Input Sequence Trap Weight"
|
||||
|
||||
|
||||
class MinigameTrapDifficulty(Choice):
|
||||
"""
|
||||
How difficult any Minigame-style traps are
|
||||
Chaos causes the difficulty to be random per-minigame
|
||||
"""
|
||||
display_name = "Minigame Trap Difficulty"
|
||||
option_easy = 0
|
||||
option_medium = 1
|
||||
option_hard = 2
|
||||
option_chaos = 3
|
||||
default = 1
|
||||
|
||||
|
||||
class BigFishingDifficulty(Choice):
|
||||
"""
|
||||
How difficult Big's Fishing Minigames are
|
||||
Chaos causes the difficulty to be random per-minigame
|
||||
"""
|
||||
display_name = "Big Fishing Difficulty"
|
||||
option_easy = 0
|
||||
option_medium = 1
|
||||
option_hard = 2
|
||||
option_chaos = 3
|
||||
default = 1
|
||||
|
||||
|
||||
@@ -382,7 +197,7 @@ class TrapFillPercentage(Range):
|
||||
default = 0
|
||||
|
||||
|
||||
class Keysanity(DefaultOnToggle):
|
||||
class Keysanity(Toggle):
|
||||
"""
|
||||
Determines whether picking up Chao Keys grants checks
|
||||
(86 Locations)
|
||||
@@ -410,7 +225,7 @@ class Whistlesanity(Choice):
|
||||
default = 0
|
||||
|
||||
|
||||
class Beetlesanity(DefaultOnToggle):
|
||||
class Beetlesanity(Toggle):
|
||||
"""
|
||||
Determines whether destroying Gold Beetles grants checks
|
||||
(27 Locations)
|
||||
@@ -429,35 +244,13 @@ class Omosanity(Toggle):
|
||||
class Animalsanity(Toggle):
|
||||
"""
|
||||
Determines whether unique counts of animals grant checks.
|
||||
(422 Locations)
|
||||
(421 Locations)
|
||||
|
||||
ALL animals must be collected in a single run of a mission to get all checks.
|
||||
"""
|
||||
display_name = "Animalsanity"
|
||||
|
||||
|
||||
class ItemBoxsanity(Choice):
|
||||
"""
|
||||
Determines whether collecting Item Boxes grants checks
|
||||
None: No Item Boxes grant checks
|
||||
Extra Lives: Extra Life Boxes grant checks (94 Locations)
|
||||
All: All Item Boxes grant checks (502 Locations Total)
|
||||
"""
|
||||
display_name = "Itemboxsanity"
|
||||
option_none = 0
|
||||
option_extra_lives = 1
|
||||
option_all = 2
|
||||
default = 0
|
||||
|
||||
|
||||
class Bigsanity(Toggle):
|
||||
"""
|
||||
Determines whether helping Big fish grants checks.
|
||||
(32 Locations)
|
||||
"""
|
||||
display_name = "Bigsanity"
|
||||
|
||||
|
||||
class KartRaceChecks(Choice):
|
||||
"""
|
||||
Determines whether Kart Race Mode grants checks
|
||||
@@ -520,7 +313,7 @@ class LevelGateCosts(Choice):
|
||||
option_low = 0
|
||||
option_medium = 1
|
||||
option_high = 2
|
||||
default = 0
|
||||
default = 2
|
||||
|
||||
|
||||
class MaximumEmblemCap(Range):
|
||||
@@ -730,214 +523,109 @@ class BaseMissionCount(Range):
|
||||
default = 2
|
||||
|
||||
|
||||
class SonicMissionCount(BaseMissionCount):
|
||||
class SpeedMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Sonic stages
|
||||
The number of active missions to include for Sonic and Shadow stages
|
||||
"""
|
||||
display_name = "Sonic Mission Count"
|
||||
display_name = "Speed Mission Count"
|
||||
|
||||
|
||||
class SonicMission2(DefaultOnToggle):
|
||||
class SpeedMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Sonic 100 rings missions should be included
|
||||
Determines if the Sonic and Shadow 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Sonic Mission 2"
|
||||
display_name = "Speed Mission 2"
|
||||
|
||||
|
||||
class SonicMission3(DefaultOnToggle):
|
||||
class SpeedMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Sonic lost chao missions should be included
|
||||
Determines if the Sonic and Shadow lost chao missions should be included
|
||||
"""
|
||||
display_name = "Sonic Mission 3"
|
||||
display_name = "Speed Mission 3"
|
||||
|
||||
|
||||
class SonicMission4(DefaultOnToggle):
|
||||
class SpeedMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Sonic time trial missions should be included
|
||||
Determines if the Sonic and Shadow time trial missions should be included
|
||||
"""
|
||||
display_name = "Sonic Mission 4"
|
||||
display_name = "Speed Mission 4"
|
||||
|
||||
|
||||
class SonicMission5(DefaultOnToggle):
|
||||
class SpeedMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Sonic hard missions should be included
|
||||
Determines if the Sonic and Shadow hard missions should be included
|
||||
"""
|
||||
display_name = "Sonic Mission 5"
|
||||
display_name = "Speed Mission 5"
|
||||
|
||||
|
||||
class ShadowMissionCount(BaseMissionCount):
|
||||
class MechMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Shadow stages
|
||||
The number of active missions to include for Tails and Eggman stages
|
||||
"""
|
||||
display_name = "Shadow Mission Count"
|
||||
display_name = "Mech Mission Count"
|
||||
|
||||
|
||||
class ShadowMission2(DefaultOnToggle):
|
||||
class MechMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Shadow 100 rings missions should be included
|
||||
Determines if the Tails and Eggman 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Shadow Mission 2"
|
||||
display_name = "Mech Mission 2"
|
||||
|
||||
|
||||
class ShadowMission3(DefaultOnToggle):
|
||||
class MechMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Shadow lost chao missions should be included
|
||||
Determines if the Tails and Eggman lost chao missions should be included
|
||||
"""
|
||||
display_name = "Shadow Mission 3"
|
||||
display_name = "Mech Mission 3"
|
||||
|
||||
|
||||
class ShadowMission4(DefaultOnToggle):
|
||||
class MechMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Shadow time trial missions should be included
|
||||
Determines if the Tails and Eggman time trial missions should be included
|
||||
"""
|
||||
display_name = "Shadow Mission 4"
|
||||
display_name = "Mech Mission 4"
|
||||
|
||||
|
||||
class ShadowMission5(DefaultOnToggle):
|
||||
class MechMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Shadow hard missions should be included
|
||||
Determines if the Tails and Eggman hard missions should be included
|
||||
"""
|
||||
display_name = "Shadow Mission 5"
|
||||
display_name = "Mech Mission 5"
|
||||
|
||||
|
||||
class TailsMissionCount(BaseMissionCount):
|
||||
class HuntMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Tails stages
|
||||
The number of active missions to include for Knuckles and Rouge stages
|
||||
"""
|
||||
display_name = "Tails Mission Count"
|
||||
display_name = "Hunt Mission Count"
|
||||
|
||||
|
||||
class TailsMission2(DefaultOnToggle):
|
||||
class HuntMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Tails 100 rings missions should be included
|
||||
Determines if the Knuckles and Rouge 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Tails Mission 2"
|
||||
display_name = "Hunt Mission 2"
|
||||
|
||||
|
||||
class TailsMission3(DefaultOnToggle):
|
||||
class HuntMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Tails lost chao missions should be included
|
||||
Determines if the Knuckles and Rouge lost chao missions should be included
|
||||
"""
|
||||
display_name = "Tails Mission 3"
|
||||
display_name = "Hunt Mission 3"
|
||||
|
||||
|
||||
class TailsMission4(DefaultOnToggle):
|
||||
class HuntMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Tails time trial missions should be included
|
||||
Determines if the Knuckles and Rouge time trial missions should be included
|
||||
"""
|
||||
display_name = "Tails Mission 4"
|
||||
display_name = "Hunt Mission 4"
|
||||
|
||||
|
||||
class TailsMission5(DefaultOnToggle):
|
||||
class HuntMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Tails hard missions should be included
|
||||
Determines if the Knuckles and Rouge hard missions should be included
|
||||
"""
|
||||
display_name = "Tails Mission 5"
|
||||
|
||||
|
||||
class EggmanMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Eggman stages
|
||||
"""
|
||||
display_name = "Eggman Mission Count"
|
||||
|
||||
|
||||
class EggmanMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Eggman 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Eggman Mission 2"
|
||||
|
||||
|
||||
class EggmanMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Eggman lost chao missions should be included
|
||||
"""
|
||||
display_name = "Eggman Mission 3"
|
||||
|
||||
|
||||
class EggmanMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Eggman time trial missions should be included
|
||||
"""
|
||||
display_name = "Eggman Mission 4"
|
||||
|
||||
|
||||
class EggmanMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Eggman hard missions should be included
|
||||
"""
|
||||
display_name = "Eggman Mission 5"
|
||||
|
||||
|
||||
class KnucklesMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Knuckles stages
|
||||
"""
|
||||
display_name = "Knuckles Mission Count"
|
||||
|
||||
|
||||
class KnucklesMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Knuckles 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Knuckles Mission 2"
|
||||
|
||||
|
||||
class KnucklesMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Knuckles lost chao missions should be included
|
||||
"""
|
||||
display_name = "Knuckles Mission 3"
|
||||
|
||||
|
||||
class KnucklesMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Knuckles time trial missions should be included
|
||||
"""
|
||||
display_name = "Knuckles Mission 4"
|
||||
|
||||
|
||||
class KnucklesMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Knuckles hard missions should be included
|
||||
"""
|
||||
display_name = "Knuckles Mission 5"
|
||||
|
||||
|
||||
class RougeMissionCount(BaseMissionCount):
|
||||
"""
|
||||
The number of active missions to include for Rouge stages
|
||||
"""
|
||||
display_name = "Rouge Mission Count"
|
||||
|
||||
|
||||
class RougeMission2(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Rouge 100 rings missions should be included
|
||||
"""
|
||||
display_name = "Rouge Mission 2"
|
||||
|
||||
|
||||
class RougeMission3(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Rouge lost chao missions should be included
|
||||
"""
|
||||
display_name = "Rouge Mission 3"
|
||||
|
||||
|
||||
class RougeMission4(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Rouge time trial missions should be included
|
||||
"""
|
||||
display_name = "Rouge Mission 4"
|
||||
|
||||
|
||||
class RougeMission5(DefaultOnToggle):
|
||||
"""
|
||||
Determines if the Rouge hard missions should be included
|
||||
"""
|
||||
display_name = "Rouge Mission 5"
|
||||
display_name = "Hunt Mission 5"
|
||||
|
||||
|
||||
class KartMissionCount(BaseMissionCount):
|
||||
@@ -1018,7 +706,7 @@ class RingLoss(Choice):
|
||||
|
||||
Modern: You lose 20 rings when hit
|
||||
|
||||
OHKO: You die immediately when hit (NOTE: Some Hard or Expert Logic tricks may require damage boosts!)
|
||||
OHKO: You die immediately when hit (NOTE: Some Hard Logic tricks may require damage boosts!)
|
||||
"""
|
||||
display_name = "Ring Loss"
|
||||
option_classic = 0
|
||||
@@ -1041,16 +729,6 @@ class RingLink(Toggle):
|
||||
display_name = "Ring Link"
|
||||
|
||||
|
||||
class TrapLink(Toggle):
|
||||
"""
|
||||
Whether your received traps are linked to other players
|
||||
|
||||
You will also receive any linked traps from other players with Trap Link enabled,
|
||||
if you have a weight above "none" set for that trap
|
||||
"""
|
||||
display_name = "Trap Link"
|
||||
|
||||
|
||||
class SADXMusic(Choice):
|
||||
"""
|
||||
Whether the randomizer will include Sonic Adventure DX Music in the music pool
|
||||
@@ -1145,14 +823,11 @@ class LogicDifficulty(Choice):
|
||||
|
||||
Standard: The logic assumes the "intended" usage of Upgrades to progress through levels
|
||||
|
||||
Hard: Some simple skips or sequence breaks may be required, but no out-of-bounds
|
||||
|
||||
Expert: If it is humanly possible, it may be required
|
||||
Hard: Some simple skips or sequence breaks may be required
|
||||
"""
|
||||
display_name = "Logic Difficulty"
|
||||
option_standard = 0
|
||||
option_hard = 1
|
||||
option_expert = 2
|
||||
default = 0
|
||||
|
||||
|
||||
@@ -1160,8 +835,6 @@ sa2b_option_groups = [
|
||||
OptionGroup("General Options", [
|
||||
Goal,
|
||||
BossRushShuffle,
|
||||
MinigameMadnessRequirement,
|
||||
MinigameMadnessMinimum,
|
||||
LogicDifficulty,
|
||||
RequiredRank,
|
||||
MaximumEmblemCap,
|
||||
@@ -1181,8 +854,6 @@ sa2b_option_groups = [
|
||||
Beetlesanity,
|
||||
Omosanity,
|
||||
Animalsanity,
|
||||
ItemBoxsanity,
|
||||
Bigsanity,
|
||||
KartRaceChecks,
|
||||
]),
|
||||
OptionGroup("Chao", [
|
||||
@@ -1214,68 +885,29 @@ sa2b_option_groups = [
|
||||
SlowTrapWeight,
|
||||
CutsceneTrapWeight,
|
||||
ReverseTrapWeight,
|
||||
LiteratureTrapWeight,
|
||||
ControllerDriftTrapWeight,
|
||||
PoisonTrapWeight,
|
||||
BeeTrapWeight,
|
||||
]),
|
||||
OptionGroup("Minigames", [
|
||||
PongTrapWeight,
|
||||
BreakoutTrapWeight,
|
||||
FishingTrapWeight,
|
||||
TriviaTrapWeight,
|
||||
PokemonTriviaTrapWeight,
|
||||
PokemonCountTrapWeight,
|
||||
NumberSequenceTrapWeight,
|
||||
LightUpPathTrapWeight,
|
||||
PinballTrapWeight,
|
||||
MathQuizTrapWeight,
|
||||
SnakeTrapWeight,
|
||||
InputSequenceTrapWeight,
|
||||
MinigameTrapDifficulty,
|
||||
BigFishingDifficulty,
|
||||
]),
|
||||
OptionGroup("Sonic Missions", [
|
||||
SonicMissionCount,
|
||||
SonicMission2,
|
||||
SonicMission3,
|
||||
SonicMission4,
|
||||
SonicMission5,
|
||||
OptionGroup("Speed Missions", [
|
||||
SpeedMissionCount,
|
||||
SpeedMission2,
|
||||
SpeedMission3,
|
||||
SpeedMission4,
|
||||
SpeedMission5,
|
||||
]),
|
||||
OptionGroup("Shadow Missions", [
|
||||
ShadowMissionCount,
|
||||
ShadowMission2,
|
||||
ShadowMission3,
|
||||
ShadowMission4,
|
||||
ShadowMission5,
|
||||
OptionGroup("Mech Missions", [
|
||||
MechMissionCount,
|
||||
MechMission2,
|
||||
MechMission3,
|
||||
MechMission4,
|
||||
MechMission5,
|
||||
]),
|
||||
OptionGroup("Tails Missions", [
|
||||
TailsMissionCount,
|
||||
TailsMission2,
|
||||
TailsMission3,
|
||||
TailsMission4,
|
||||
TailsMission5,
|
||||
]),
|
||||
OptionGroup("Eggman Missions", [
|
||||
EggmanMissionCount,
|
||||
EggmanMission2,
|
||||
EggmanMission3,
|
||||
EggmanMission4,
|
||||
EggmanMission5,
|
||||
]),
|
||||
OptionGroup("Knuckles Missions", [
|
||||
KnucklesMissionCount,
|
||||
KnucklesMission2,
|
||||
KnucklesMission3,
|
||||
KnucklesMission4,
|
||||
KnucklesMission5,
|
||||
]),
|
||||
OptionGroup("Rouge Missions", [
|
||||
RougeMissionCount,
|
||||
RougeMission2,
|
||||
RougeMission3,
|
||||
RougeMission4,
|
||||
RougeMission5,
|
||||
OptionGroup("Hunt Missions", [
|
||||
HuntMissionCount,
|
||||
HuntMission2,
|
||||
HuntMission3,
|
||||
HuntMission4,
|
||||
HuntMission5,
|
||||
]),
|
||||
OptionGroup("Kart Missions", [
|
||||
KartMissionCount,
|
||||
@@ -1299,13 +931,11 @@ sa2b_option_groups = [
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SA2BOptions(PerGameCommonOptions):
|
||||
goal: Goal
|
||||
boss_rush_shuffle: BossRushShuffle
|
||||
minigame_madness_requirement: MinigameMadnessRequirement
|
||||
minigame_madness_minimum: MinigameMadnessMinimum
|
||||
gate_boss_plando: GateBossPlando
|
||||
logic_difficulty: LogicDifficulty
|
||||
required_rank: RequiredRank
|
||||
max_emblem_cap: MaximumEmblemCap
|
||||
@@ -1323,8 +953,6 @@ class SA2BOptions(PerGameCommonOptions):
|
||||
beetlesanity: Beetlesanity
|
||||
omosanity: Omosanity
|
||||
animalsanity: Animalsanity
|
||||
itemboxsanity: ItemBoxsanity
|
||||
bigsanity: Bigsanity
|
||||
kart_race_checks: KartRaceChecks
|
||||
|
||||
black_market_slots: BlackMarketSlots
|
||||
@@ -1355,65 +983,31 @@ class SA2BOptions(PerGameCommonOptions):
|
||||
slow_trap_weight: SlowTrapWeight
|
||||
cutscene_trap_weight: CutsceneTrapWeight
|
||||
reverse_trap_weight: ReverseTrapWeight
|
||||
literature_trap_weight: LiteratureTrapWeight
|
||||
controller_drift_trap_weight: ControllerDriftTrapWeight
|
||||
poison_trap_weight: PoisonTrapWeight
|
||||
bee_trap_weight: BeeTrapWeight
|
||||
pong_trap_weight: PongTrapWeight
|
||||
breakout_trap_weight: BreakoutTrapWeight
|
||||
fishing_trap_weight: FishingTrapWeight
|
||||
trivia_trap_weight: TriviaTrapWeight
|
||||
pokemon_trivia_trap_weight: PokemonTriviaTrapWeight
|
||||
pokemon_count_trap_weight: PokemonCountTrapWeight
|
||||
number_sequence_trap_weight: NumberSequenceTrapWeight
|
||||
light_up_path_trap_weight: LightUpPathTrapWeight
|
||||
pinball_trap_weight: PinballTrapWeight
|
||||
math_quiz_trap_weight: MathQuizTrapWeight
|
||||
snake_trap_weight: SnakeTrapWeight
|
||||
input_sequence_trap_weight: InputSequenceTrapWeight
|
||||
minigame_trap_difficulty: MinigameTrapDifficulty
|
||||
big_fishing_difficulty: BigFishingDifficulty
|
||||
|
||||
sadx_music: SADXMusic
|
||||
music_shuffle: MusicShuffle
|
||||
voice_shuffle: VoiceShuffle
|
||||
narrator: Narrator
|
||||
|
||||
sonic_mission_count: SonicMissionCount
|
||||
sonic_mission_2: SonicMission2
|
||||
sonic_mission_3: SonicMission3
|
||||
sonic_mission_4: SonicMission4
|
||||
sonic_mission_5: SonicMission5
|
||||
speed_mission_count: SpeedMissionCount
|
||||
speed_mission_2: SpeedMission2
|
||||
speed_mission_3: SpeedMission3
|
||||
speed_mission_4: SpeedMission4
|
||||
speed_mission_5: SpeedMission5
|
||||
|
||||
shadow_mission_count: ShadowMissionCount
|
||||
shadow_mission_2: ShadowMission2
|
||||
shadow_mission_3: ShadowMission3
|
||||
shadow_mission_4: ShadowMission4
|
||||
shadow_mission_5: ShadowMission5
|
||||
mech_mission_count: MechMissionCount
|
||||
mech_mission_2: MechMission2
|
||||
mech_mission_3: MechMission3
|
||||
mech_mission_4: MechMission4
|
||||
mech_mission_5: MechMission5
|
||||
|
||||
tails_mission_count: TailsMissionCount
|
||||
tails_mission_2: TailsMission2
|
||||
tails_mission_3: TailsMission3
|
||||
tails_mission_4: TailsMission4
|
||||
tails_mission_5: TailsMission5
|
||||
|
||||
eggman_mission_count: EggmanMissionCount
|
||||
eggman_mission_2: EggmanMission2
|
||||
eggman_mission_3: EggmanMission3
|
||||
eggman_mission_4: EggmanMission4
|
||||
eggman_mission_5: EggmanMission5
|
||||
|
||||
knuckles_mission_count: KnucklesMissionCount
|
||||
knuckles_mission_2: KnucklesMission2
|
||||
knuckles_mission_3: KnucklesMission3
|
||||
knuckles_mission_4: KnucklesMission4
|
||||
knuckles_mission_5: KnucklesMission5
|
||||
|
||||
rouge_mission_count: RougeMissionCount
|
||||
rouge_mission_2: RougeMission2
|
||||
rouge_mission_3: RougeMission3
|
||||
rouge_mission_4: RougeMission4
|
||||
rouge_mission_5: RougeMission5
|
||||
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
|
||||
@@ -1428,5 +1022,4 @@ class SA2BOptions(PerGameCommonOptions):
|
||||
cannons_core_mission_5: CannonsCoreMission5
|
||||
|
||||
ring_link: RingLink
|
||||
trap_link: TrapLink
|
||||
death_link: DeathLink
|
||||
|
||||
@@ -1,502 +0,0 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from .Options import *
|
||||
|
||||
minsanity = {
|
||||
"goal": Goal.option_chaos_chao,
|
||||
"max_emblem_cap": MaximumEmblemCap.range_start,
|
||||
|
||||
"keysanity": False,
|
||||
"whistlesanity": Whistlesanity.option_none,
|
||||
"beetlesanity": False,
|
||||
"omosanity": False,
|
||||
"animalsanity": False,
|
||||
"itemboxsanity": ItemBoxsanity.option_none,
|
||||
"bigsanity": False,
|
||||
"kart_race_checks": KartRaceChecks.option_none,
|
||||
|
||||
"junk_fill_percentage": 0,
|
||||
|
||||
"sonic_mission_count": BaseMissionCount.range_start,
|
||||
"sonic_mission_2": False,
|
||||
"sonic_mission_3": False,
|
||||
"sonic_mission_4": False,
|
||||
"sonic_mission_5": False,
|
||||
|
||||
"shadow_mission_count": BaseMissionCount.range_start,
|
||||
"shadow_mission_2": False,
|
||||
"shadow_mission_3": False,
|
||||
"shadow_mission_4": False,
|
||||
"shadow_mission_5": False,
|
||||
|
||||
"tails_mission_count": BaseMissionCount.range_start,
|
||||
"tails_mission_2": False,
|
||||
"tails_mission_3": False,
|
||||
"tails_mission_4": False,
|
||||
"tails_mission_5": False,
|
||||
|
||||
"eggman_mission_count": BaseMissionCount.range_start,
|
||||
"eggman_mission_2": False,
|
||||
"eggman_mission_3": False,
|
||||
"eggman_mission_4": False,
|
||||
"eggman_mission_5": False,
|
||||
|
||||
"knuckles_mission_count": BaseMissionCount.range_start,
|
||||
"knuckles_mission_2": False,
|
||||
"knuckles_mission_3": False,
|
||||
"knuckles_mission_4": False,
|
||||
"knuckles_mission_5": False,
|
||||
|
||||
"rouge_mission_count": BaseMissionCount.range_start,
|
||||
"rouge_mission_2": False,
|
||||
"rouge_mission_3": False,
|
||||
"rouge_mission_4": False,
|
||||
"rouge_mission_5": False,
|
||||
|
||||
"kart_mission_count": BaseMissionCount.range_start,
|
||||
"kart_mission_2": False,
|
||||
"kart_mission_3": False,
|
||||
"kart_mission_4": False,
|
||||
"kart_mission_5": False,
|
||||
|
||||
"cannons_core_mission_count": BaseMissionCount.range_start,
|
||||
"cannons_core_mission_2": False,
|
||||
"cannons_core_mission_3": False,
|
||||
"cannons_core_mission_4": False,
|
||||
"cannons_core_mission_5": False,
|
||||
}
|
||||
|
||||
chao_centric = {
|
||||
"goal": Goal.option_chaos_chao,
|
||||
|
||||
"keysanity": False,
|
||||
"whistlesanity": Whistlesanity.option_none,
|
||||
"beetlesanity": False,
|
||||
"omosanity": False,
|
||||
"animalsanity": False,
|
||||
"itemboxsanity": ItemBoxsanity.option_none,
|
||||
"bigsanity": False,
|
||||
"kart_race_checks": KartRaceChecks.option_none,
|
||||
|
||||
"black_market_slots": BlackMarketSlots.range_end,
|
||||
"black_market_unlock_costs": BlackMarketUnlockCosts.option_high,
|
||||
"chao_race_difficulty": ChaoRaceDifficulty.option_expert,
|
||||
"chao_karate_difficulty": ChaoKarateDifficulty.option_super,
|
||||
"chao_stadium_checks": ChaoStadiumChecks.option_all,
|
||||
"chao_animal_parts": True,
|
||||
"chao_stats": ChaoStats.range_end,
|
||||
"chao_stats_frequency": 1,
|
||||
"chao_stats_stamina": True,
|
||||
"chao_stats_hidden": True,
|
||||
"chao_kindergarten": ChaoKindergarten.option_full,
|
||||
|
||||
"junk_fill_percentage": 50,
|
||||
|
||||
"sonic_mission_count": BaseMissionCount.range_start,
|
||||
"sonic_mission_2": False,
|
||||
"sonic_mission_3": False,
|
||||
"sonic_mission_4": False,
|
||||
"sonic_mission_5": False,
|
||||
|
||||
"shadow_mission_count": BaseMissionCount.range_start,
|
||||
"shadow_mission_2": False,
|
||||
"shadow_mission_3": False,
|
||||
"shadow_mission_4": False,
|
||||
"shadow_mission_5": False,
|
||||
|
||||
"tails_mission_count": BaseMissionCount.range_start,
|
||||
"tails_mission_2": False,
|
||||
"tails_mission_3": False,
|
||||
"tails_mission_4": False,
|
||||
"tails_mission_5": False,
|
||||
|
||||
"eggman_mission_count": BaseMissionCount.range_start,
|
||||
"eggman_mission_2": False,
|
||||
"eggman_mission_3": False,
|
||||
"eggman_mission_4": False,
|
||||
"eggman_mission_5": False,
|
||||
|
||||
"knuckles_mission_count": BaseMissionCount.range_start,
|
||||
"knuckles_mission_2": False,
|
||||
"knuckles_mission_3": False,
|
||||
"knuckles_mission_4": False,
|
||||
"knuckles_mission_5": False,
|
||||
|
||||
"rouge_mission_count": BaseMissionCount.range_start,
|
||||
"rouge_mission_2": False,
|
||||
"rouge_mission_3": False,
|
||||
"rouge_mission_4": False,
|
||||
"rouge_mission_5": False,
|
||||
|
||||
"kart_mission_count": BaseMissionCount.range_start,
|
||||
"kart_mission_2": False,
|
||||
"kart_mission_3": False,
|
||||
"kart_mission_4": False,
|
||||
"kart_mission_5": False,
|
||||
|
||||
"cannons_core_mission_count": BaseMissionCount.range_start,
|
||||
"cannons_core_mission_2": False,
|
||||
"cannons_core_mission_3": False,
|
||||
"cannons_core_mission_4": False,
|
||||
"cannons_core_mission_5": False,
|
||||
}
|
||||
|
||||
allsanity_no_chao = {
|
||||
"goal": Goal.option_cannons_core_boss_rush,
|
||||
"boss_rush_shuffle": BossRushShuffle.option_chaos,
|
||||
"minigame_madness_requirement": MinigameMadnessRequirement.range_end,
|
||||
"minigame_madness_minimum": MinigameMadnessMinimum.range_end,
|
||||
"max_emblem_cap": MaximumEmblemCap.range_end,
|
||||
|
||||
"mission_shuffle": True,
|
||||
"required_cannons_core_missions": RequiredCannonsCoreMissions.option_all_active,
|
||||
"emblem_percentage_for_cannons_core": EmblemPercentageForCannonsCore.range_end,
|
||||
"number_of_level_gates": NumberOfLevelGates.range_end,
|
||||
"level_gate_costs": LevelGateCosts.option_high,
|
||||
|
||||
"keysanity": True,
|
||||
"whistlesanity": Whistlesanity.option_both,
|
||||
"beetlesanity": True,
|
||||
"omosanity": True,
|
||||
"animalsanity": True,
|
||||
"itemboxsanity": ItemBoxsanity.option_all,
|
||||
"bigsanity": True,
|
||||
"kart_race_checks": KartRaceChecks.option_full,
|
||||
|
||||
"junk_fill_percentage": 25,
|
||||
"trap_fill_percentage": 25,
|
||||
"omochao_trap_weight": BaseTrapWeight.option_high,
|
||||
"timestop_trap_weight": BaseTrapWeight.option_high,
|
||||
"confusion_trap_weight": BaseTrapWeight.option_high,
|
||||
"tiny_trap_weight": BaseTrapWeight.option_high,
|
||||
"gravity_trap_weight": BaseTrapWeight.option_high,
|
||||
"exposition_trap_weight": BaseTrapWeight.option_high,
|
||||
"ice_trap_weight": BaseTrapWeight.option_high,
|
||||
"slow_trap_weight": BaseTrapWeight.option_high,
|
||||
"cutscene_trap_weight": BaseTrapWeight.option_high,
|
||||
"reverse_trap_weight": BaseTrapWeight.option_high,
|
||||
"literature_trap_weight": BaseTrapWeight.option_high,
|
||||
"controller_drift_trap_weight": BaseTrapWeight.option_high,
|
||||
"poison_trap_weight": BaseTrapWeight.option_high,
|
||||
"bee_trap_weight": BaseTrapWeight.option_high,
|
||||
"pong_trap_weight": BaseTrapWeight.option_high,
|
||||
"breakout_trap_weight": BaseTrapWeight.option_high,
|
||||
"fishing_trap_weight": BaseTrapWeight.option_high,
|
||||
"trivia_trap_weight": BaseTrapWeight.option_high,
|
||||
"pokemon_trivia_trap_weight": BaseTrapWeight.option_high,
|
||||
"pokemon_count_trap_weight": BaseTrapWeight.option_high,
|
||||
"number_sequence_trap_weight": BaseTrapWeight.option_high,
|
||||
"light_up_path_trap_weight": BaseTrapWeight.option_high,
|
||||
"pinball_trap_weight": BaseTrapWeight.option_high,
|
||||
"math_quiz_trap_weight": BaseTrapWeight.option_high,
|
||||
"snake_trap_weight": BaseTrapWeight.option_high,
|
||||
"input_sequence_trap_weight": BaseTrapWeight.option_high,
|
||||
"minigame_trap_difficulty": MinigameTrapDifficulty.option_chaos,
|
||||
"big_fishing_difficulty": BigFishingDifficulty.option_chaos,
|
||||
|
||||
"music_shuffle": MusicShuffle.option_full,
|
||||
"voice_shuffle": VoiceShuffle.option_shuffled,
|
||||
|
||||
"sonic_mission_count": BaseMissionCount.range_end,
|
||||
"sonic_mission_2": True,
|
||||
"sonic_mission_3": True,
|
||||
"sonic_mission_4": True,
|
||||
"sonic_mission_5": True,
|
||||
|
||||
"shadow_mission_count": BaseMissionCount.range_end,
|
||||
"shadow_mission_2": True,
|
||||
"shadow_mission_3": True,
|
||||
"shadow_mission_4": True,
|
||||
"shadow_mission_5": True,
|
||||
|
||||
"tails_mission_count": BaseMissionCount.range_end,
|
||||
"tails_mission_2": True,
|
||||
"tails_mission_3": True,
|
||||
"tails_mission_4": True,
|
||||
"tails_mission_5": True,
|
||||
|
||||
"eggman_mission_count": BaseMissionCount.range_end,
|
||||
"eggman_mission_2": True,
|
||||
"eggman_mission_3": True,
|
||||
"eggman_mission_4": True,
|
||||
"eggman_mission_5": True,
|
||||
|
||||
"knuckles_mission_count": BaseMissionCount.range_end,
|
||||
"knuckles_mission_2": True,
|
||||
"knuckles_mission_3": True,
|
||||
"knuckles_mission_4": True,
|
||||
"knuckles_mission_5": True,
|
||||
|
||||
"rouge_mission_count": BaseMissionCount.range_end,
|
||||
"rouge_mission_2": True,
|
||||
"rouge_mission_3": True,
|
||||
"rouge_mission_4": True,
|
||||
"rouge_mission_5": True,
|
||||
|
||||
"kart_mission_count": BaseMissionCount.range_end,
|
||||
"kart_mission_2": True,
|
||||
"kart_mission_3": True,
|
||||
"kart_mission_4": True,
|
||||
"kart_mission_5": True,
|
||||
|
||||
"cannons_core_mission_count": BaseMissionCount.range_end,
|
||||
"cannons_core_mission_2": True,
|
||||
"cannons_core_mission_3": True,
|
||||
"cannons_core_mission_4": True,
|
||||
"cannons_core_mission_5": True,
|
||||
}
|
||||
|
||||
allsanity = {
|
||||
"goal": Goal.option_cannons_core_boss_rush,
|
||||
"boss_rush_shuffle": BossRushShuffle.option_chaos,
|
||||
"minigame_madness_requirement": MinigameMadnessRequirement.range_end,
|
||||
"minigame_madness_minimum": MinigameMadnessMinimum.range_end,
|
||||
"max_emblem_cap": MaximumEmblemCap.range_end,
|
||||
|
||||
"mission_shuffle": True,
|
||||
"required_cannons_core_missions": RequiredCannonsCoreMissions.option_all_active,
|
||||
"emblem_percentage_for_cannons_core": EmblemPercentageForCannonsCore.range_end,
|
||||
"number_of_level_gates": NumberOfLevelGates.range_end,
|
||||
"level_gate_costs": LevelGateCosts.option_high,
|
||||
|
||||
"keysanity": True,
|
||||
"whistlesanity": Whistlesanity.option_both,
|
||||
"beetlesanity": True,
|
||||
"omosanity": True,
|
||||
"animalsanity": True,
|
||||
"itemboxsanity": ItemBoxsanity.option_all,
|
||||
"bigsanity": True,
|
||||
"kart_race_checks": KartRaceChecks.option_full,
|
||||
|
||||
"black_market_slots": BlackMarketSlots.range_end,
|
||||
"black_market_unlock_costs": BlackMarketUnlockCosts.option_high,
|
||||
"chao_race_difficulty": ChaoRaceDifficulty.option_expert,
|
||||
"chao_karate_difficulty": ChaoKarateDifficulty.option_super,
|
||||
"chao_stadium_checks": ChaoStadiumChecks.option_all,
|
||||
"chao_animal_parts": True,
|
||||
"chao_stats": ChaoStats.range_end,
|
||||
"chao_stats_frequency": 1,
|
||||
"chao_stats_stamina": True,
|
||||
"chao_stats_hidden": True,
|
||||
"chao_kindergarten": ChaoKindergarten.option_full,
|
||||
|
||||
"junk_fill_percentage": 25,
|
||||
"trap_fill_percentage": 25,
|
||||
"omochao_trap_weight": BaseTrapWeight.option_high,
|
||||
"timestop_trap_weight": BaseTrapWeight.option_high,
|
||||
"confusion_trap_weight": BaseTrapWeight.option_high,
|
||||
"tiny_trap_weight": BaseTrapWeight.option_high,
|
||||
"gravity_trap_weight": BaseTrapWeight.option_high,
|
||||
"exposition_trap_weight": BaseTrapWeight.option_high,
|
||||
"ice_trap_weight": BaseTrapWeight.option_high,
|
||||
"slow_trap_weight": BaseTrapWeight.option_high,
|
||||
"cutscene_trap_weight": BaseTrapWeight.option_high,
|
||||
"reverse_trap_weight": BaseTrapWeight.option_high,
|
||||
"literature_trap_weight": BaseTrapWeight.option_high,
|
||||
"controller_drift_trap_weight": BaseTrapWeight.option_high,
|
||||
"poison_trap_weight": BaseTrapWeight.option_high,
|
||||
"bee_trap_weight": BaseTrapWeight.option_high,
|
||||
"pong_trap_weight": BaseTrapWeight.option_high,
|
||||
"breakout_trap_weight": BaseTrapWeight.option_high,
|
||||
"fishing_trap_weight": BaseTrapWeight.option_high,
|
||||
"trivia_trap_weight": BaseTrapWeight.option_high,
|
||||
"pokemon_trivia_trap_weight": BaseTrapWeight.option_high,
|
||||
"pokemon_count_trap_weight": BaseTrapWeight.option_high,
|
||||
"number_sequence_trap_weight": BaseTrapWeight.option_high,
|
||||
"light_up_path_trap_weight": BaseTrapWeight.option_high,
|
||||
"pinball_trap_weight": BaseTrapWeight.option_high,
|
||||
"math_quiz_trap_weight": BaseTrapWeight.option_high,
|
||||
"snake_trap_weight": BaseTrapWeight.option_high,
|
||||
"input_sequence_trap_weight": BaseTrapWeight.option_high,
|
||||
"minigame_trap_difficulty": MinigameTrapDifficulty.option_chaos,
|
||||
"big_fishing_difficulty": BigFishingDifficulty.option_chaos,
|
||||
|
||||
"music_shuffle": MusicShuffle.option_full,
|
||||
"voice_shuffle": VoiceShuffle.option_shuffled,
|
||||
|
||||
"sonic_mission_count": BaseMissionCount.range_end,
|
||||
"sonic_mission_2": True,
|
||||
"sonic_mission_3": True,
|
||||
"sonic_mission_4": True,
|
||||
"sonic_mission_5": True,
|
||||
|
||||
"shadow_mission_count": BaseMissionCount.range_end,
|
||||
"shadow_mission_2": True,
|
||||
"shadow_mission_3": True,
|
||||
"shadow_mission_4": True,
|
||||
"shadow_mission_5": True,
|
||||
|
||||
"tails_mission_count": BaseMissionCount.range_end,
|
||||
"tails_mission_2": True,
|
||||
"tails_mission_3": True,
|
||||
"tails_mission_4": True,
|
||||
"tails_mission_5": True,
|
||||
|
||||
"eggman_mission_count": BaseMissionCount.range_end,
|
||||
"eggman_mission_2": True,
|
||||
"eggman_mission_3": True,
|
||||
"eggman_mission_4": True,
|
||||
"eggman_mission_5": True,
|
||||
|
||||
"knuckles_mission_count": BaseMissionCount.range_end,
|
||||
"knuckles_mission_2": True,
|
||||
"knuckles_mission_3": True,
|
||||
"knuckles_mission_4": True,
|
||||
"knuckles_mission_5": True,
|
||||
|
||||
"rouge_mission_count": BaseMissionCount.range_end,
|
||||
"rouge_mission_2": True,
|
||||
"rouge_mission_3": True,
|
||||
"rouge_mission_4": True,
|
||||
"rouge_mission_5": True,
|
||||
|
||||
"kart_mission_count": BaseMissionCount.range_end,
|
||||
"kart_mission_2": True,
|
||||
"kart_mission_3": True,
|
||||
"kart_mission_4": True,
|
||||
"kart_mission_5": True,
|
||||
|
||||
"cannons_core_mission_count": BaseMissionCount.range_end,
|
||||
"cannons_core_mission_2": True,
|
||||
"cannons_core_mission_3": True,
|
||||
"cannons_core_mission_4": True,
|
||||
"cannons_core_mission_5": True,
|
||||
}
|
||||
|
||||
all_random = {
|
||||
"goal": "random",
|
||||
"boss_rush_shuffle": "random",
|
||||
"minigame_madness_requirement": "random",
|
||||
"minigame_madness_minimum": "random",
|
||||
"logic_difficulty": "random",
|
||||
"required_rank": "random",
|
||||
"max_emblem_cap": "random",
|
||||
"ring_loss": "random",
|
||||
|
||||
"mission_shuffle": "random",
|
||||
"required_cannons_core_missions": "random",
|
||||
"emblem_percentage_for_cannons_core": "random",
|
||||
"number_of_level_gates": "random",
|
||||
"level_gate_distribution": "random",
|
||||
"level_gate_costs": "random",
|
||||
|
||||
"keysanity": "random",
|
||||
"whistlesanity": "random",
|
||||
"beetlesanity": "random",
|
||||
"omosanity": "random",
|
||||
"animalsanity": "random",
|
||||
"itemboxsanity": "random",
|
||||
"bigsanity": "random",
|
||||
"kart_race_checks": "random",
|
||||
|
||||
"black_market_slots": "random",
|
||||
"black_market_unlock_costs": "random",
|
||||
"black_market_price_multiplier": "random",
|
||||
"chao_race_difficulty": "random",
|
||||
"chao_karate_difficulty": "random",
|
||||
"chao_stadium_checks": "random",
|
||||
"chao_animal_parts": "random",
|
||||
"chao_stats": "random",
|
||||
"chao_stats_frequency": "random",
|
||||
"chao_stats_stamina": "random",
|
||||
"chao_stats_hidden": "random",
|
||||
"chao_kindergarten": "random",
|
||||
"shuffle_starting_chao_eggs": "random",
|
||||
"chao_entrance_randomization": "random",
|
||||
|
||||
"junk_fill_percentage": "random",
|
||||
"trap_fill_percentage": "random",
|
||||
"omochao_trap_weight": "random",
|
||||
"timestop_trap_weight": "random",
|
||||
"confusion_trap_weight": "random",
|
||||
"tiny_trap_weight": "random",
|
||||
"gravity_trap_weight": "random",
|
||||
"exposition_trap_weight": "random",
|
||||
"ice_trap_weight": "random",
|
||||
"slow_trap_weight": "random",
|
||||
"cutscene_trap_weight": "random",
|
||||
"reverse_trap_weight": "random",
|
||||
"literature_trap_weight": "random",
|
||||
"controller_drift_trap_weight": "random",
|
||||
"poison_trap_weight": "random",
|
||||
"bee_trap_weight": "random",
|
||||
"pong_trap_weight": "random",
|
||||
"breakout_trap_weight": "random",
|
||||
"fishing_trap_weight": "random",
|
||||
"trivia_trap_weight": "random",
|
||||
"pokemon_trivia_trap_weight": "random",
|
||||
"pokemon_count_trap_weight": "random",
|
||||
"number_sequence_trap_weight": "random",
|
||||
"light_up_path_trap_weight": "random",
|
||||
"pinball_trap_weight": "random",
|
||||
"math_quiz_trap_weight": "random",
|
||||
"snake_trap_weight": "random",
|
||||
"input_sequence_trap_weight": "random",
|
||||
"minigame_trap_difficulty": "random",
|
||||
"big_fishing_difficulty": "random",
|
||||
|
||||
"sadx_music": "random",
|
||||
"music_shuffle": "random",
|
||||
"voice_shuffle": "random",
|
||||
"narrator": "random",
|
||||
|
||||
"sonic_mission_count": "random",
|
||||
"sonic_mission_2": "random",
|
||||
"sonic_mission_3": "random",
|
||||
"sonic_mission_4": "random",
|
||||
"sonic_mission_5": "random",
|
||||
|
||||
"shadow_mission_count": "random",
|
||||
"shadow_mission_2": "random",
|
||||
"shadow_mission_3": "random",
|
||||
"shadow_mission_4": "random",
|
||||
"shadow_mission_5": "random",
|
||||
|
||||
"tails_mission_count": "random",
|
||||
"tails_mission_2": "random",
|
||||
"tails_mission_3": "random",
|
||||
"tails_mission_4": "random",
|
||||
"tails_mission_5": "random",
|
||||
|
||||
"eggman_mission_count": "random",
|
||||
"eggman_mission_2": "random",
|
||||
"eggman_mission_3": "random",
|
||||
"eggman_mission_4": "random",
|
||||
"eggman_mission_5": "random",
|
||||
|
||||
"knuckles_mission_count": "random",
|
||||
"knuckles_mission_2": "random",
|
||||
"knuckles_mission_3": "random",
|
||||
"knuckles_mission_4": "random",
|
||||
"knuckles_mission_5": "random",
|
||||
|
||||
"rouge_mission_count": "random",
|
||||
"rouge_mission_2": "random",
|
||||
"rouge_mission_3": "random",
|
||||
"rouge_mission_4": "random",
|
||||
"rouge_mission_5": "random",
|
||||
|
||||
"kart_mission_count": "random",
|
||||
"kart_mission_2": "random",
|
||||
"kart_mission_3": "random",
|
||||
"kart_mission_4": "random",
|
||||
"kart_mission_5": "random",
|
||||
|
||||
"cannons_core_mission_count": "random",
|
||||
"cannons_core_mission_2": "random",
|
||||
"cannons_core_mission_3": "random",
|
||||
"cannons_core_mission_4": "random",
|
||||
"cannons_core_mission_5": "random",
|
||||
|
||||
"ring_link": "random",
|
||||
"trap_link": "random",
|
||||
"death_link": "random",
|
||||
}
|
||||
|
||||
sa2b_options_presets: Dict[str, Dict[str, Any]] = {
|
||||
"Minsanity": minsanity,
|
||||
"Chao-centric": chao_centric,
|
||||
"Allsanity No Chao": allsanity_no_chao,
|
||||
"Allsanity": allsanity,
|
||||
"All Random": all_random,
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
2115
worlds/sa2b/Rules.py
2115
worlds/sa2b/Rules.py
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,12 @@ 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, minigame_trap_table, item_groups, \
|
||||
eggs_table, fruits_table, seeds_table, hats_table, animals_table, chaos_drives_table, event_table
|
||||
from .Locations import SA2BLocation, all_locations, location_groups, setup_locations, chao_animal_event_location_table, black_market_location_table
|
||||
from .Missions import get_mission_table, get_mission_count_table, get_first_and_last_cannons_core_missions, print_mission_orders_to_spoiler
|
||||
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 .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 .Presets import sa2b_options_presets
|
||||
from .Regions import create_regions, shuffleable_regions, connect_regions, LevelGate, gate_0_whitelist_regions, \
|
||||
gate_0_blacklist_regions
|
||||
from .Rules import set_rules
|
||||
@@ -34,7 +33,6 @@ class SA2BWeb(WebWorld):
|
||||
|
||||
tutorials = [setup_en]
|
||||
option_groups = sa2b_option_groups
|
||||
options_presets = sa2b_options_presets
|
||||
|
||||
|
||||
def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range: int, multiworld: MultiWorld):
|
||||
@@ -62,14 +60,11 @@ class SA2BWorld(World):
|
||||
topology_present = False
|
||||
|
||||
item_name_groups = item_groups
|
||||
location_name_groups = location_groups
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = all_locations
|
||||
|
||||
location_table: typing.Dict[str, int]
|
||||
|
||||
shuffled_region_list: typing.List[int]
|
||||
levels_per_gate: typing.List[int]
|
||||
mission_map: typing.Dict[int, int]
|
||||
mission_count_map: typing.Dict[int, int]
|
||||
emblems_for_cannons_core: int
|
||||
@@ -83,7 +78,7 @@ class SA2BWorld(World):
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
return {
|
||||
"ModVersion": 204,
|
||||
"ModVersion": 203,
|
||||
"Goal": self.options.goal.value,
|
||||
"MusicMap": self.generate_music_data(),
|
||||
"VoiceMap": self.generate_voice_data(),
|
||||
@@ -94,20 +89,14 @@ class SA2BWorld(World):
|
||||
"MusicShuffle": self.options.music_shuffle.value,
|
||||
"Narrator": self.options.narrator.value,
|
||||
"MinigameTrapDifficulty": self.options.minigame_trap_difficulty.value,
|
||||
"BigFishingDifficulty": self.options.big_fishing_difficulty.value,
|
||||
"RingLoss": self.options.ring_loss.value,
|
||||
"RingLink": self.options.ring_link.value,
|
||||
"TrapLink": self.options.trap_link.value,
|
||||
"RequiredRank": self.options.required_rank.value,
|
||||
"MinigameMadnessAmount": self.options.minigame_madness_requirement.value,
|
||||
"LogicDifficulty": self.options.logic_difficulty.value,
|
||||
"ChaoKeys": self.options.keysanity.value,
|
||||
"Whistlesanity": self.options.whistlesanity.value,
|
||||
"GoldBeetles": self.options.beetlesanity.value,
|
||||
"OmochaoChecks": self.options.omosanity.value,
|
||||
"AnimalChecks": self.options.animalsanity.value,
|
||||
"ItemBoxChecks": self.options.itemboxsanity.value,
|
||||
"BigChecks": self.options.bigsanity.value,
|
||||
"KartRaceChecks": self.options.kart_race_checks.value,
|
||||
"ChaoStadiumChecks": self.options.chao_stadium_checks.value,
|
||||
"ChaoRaceDifficulty": self.options.chao_race_difficulty.value,
|
||||
@@ -133,7 +122,6 @@ class SA2BWorld(World):
|
||||
"GateCosts": self.gate_costs,
|
||||
"GateBosses": self.gate_bosses,
|
||||
"BossRushMap": self.boss_rush_map,
|
||||
"ActiveTraps": self.output_active_traps(),
|
||||
"PlayerNum": self.player,
|
||||
}
|
||||
|
||||
@@ -163,42 +151,12 @@ class SA2BWorld(World):
|
||||
|
||||
valid_trap_weights = self.options.exposition_trap_weight.value + \
|
||||
self.options.reverse_trap_weight.value + \
|
||||
self.options.literature_trap_weight.value + \
|
||||
self.options.controller_drift_trap_weight.value + \
|
||||
self.options.poison_trap_weight.value + \
|
||||
self.options.bee_trap_weight.value + \
|
||||
self.options.pong_trap_weight.value + \
|
||||
self.options.breakout_trap_weight.value + \
|
||||
self.options.fishing_trap_weight.value + \
|
||||
self.options.trivia_trap_weight.value + \
|
||||
self.options.pokemon_trivia_trap_weight.value + \
|
||||
self.options.pokemon_count_trap_weight.value + \
|
||||
self.options.number_sequence_trap_weight.value + \
|
||||
self.options.light_up_path_trap_weight.value + \
|
||||
self.options.pinball_trap_weight.value + \
|
||||
self.options.math_quiz_trap_weight.value + \
|
||||
self.options.snake_trap_weight.value + \
|
||||
self.options.input_sequence_trap_weight.value
|
||||
self.options.pong_trap_weight.value
|
||||
|
||||
if valid_trap_weights == 0:
|
||||
self.options.exposition_trap_weight.value = 4
|
||||
self.options.reverse_trap_weight.value = 4
|
||||
self.options.literature_trap_weight.value = 4
|
||||
self.options.controller_drift_trap_weight.value = 4
|
||||
self.options.poison_trap_weight.value = 4
|
||||
self.options.bee_trap_weight.value = 4
|
||||
self.options.pong_trap_weight.value = 4
|
||||
self.options.breakout_trap_weight.value = 4
|
||||
self.options.fishing_trap_weight.value = 4
|
||||
self.options.trivia_trap_weight.value = 4
|
||||
self.options.pokemon_trivia_trap_weight.value = 4
|
||||
self.options.pokemon_count_trap_weight.value = 4
|
||||
self.options.number_sequence_trap_weight.value = 4
|
||||
self.options.light_up_path_trap_weight.value = 4
|
||||
self.options.pinball_trap_weight.value = 4
|
||||
self.options.math_quiz_trap_weight.value = 4
|
||||
self.options.snake_trap_weight.value = 4
|
||||
self.options.input_sequence_trap_weight.value = 4
|
||||
|
||||
if self.options.kart_race_checks.value == 0:
|
||||
self.options.kart_race_checks.value = 2
|
||||
@@ -206,8 +164,8 @@ class SA2BWorld(World):
|
||||
self.gate_bosses = {}
|
||||
self.boss_rush_map = {}
|
||||
else:
|
||||
self.gate_bosses = get_gate_bosses(self)
|
||||
self.boss_rush_map = get_boss_rush_bosses(self)
|
||||
self.gate_bosses = get_gate_bosses(self.multiworld, self)
|
||||
self.boss_rush_map = get_boss_rush_bosses(self.multiworld, self)
|
||||
|
||||
def create_regions(self):
|
||||
self.mission_map = get_mission_table(self.multiworld, self, self.player)
|
||||
@@ -219,7 +177,7 @@ class SA2BWorld(World):
|
||||
# Not Generate Basic
|
||||
self.black_market_costs = dict()
|
||||
|
||||
if self.options.goal.value in [0, 2, 4, 5, 6, 8]:
|
||||
if self.options.goal.value in [0, 2, 4, 5, 6]:
|
||||
self.multiworld.get_location(LocationName.finalhazard, self.player).place_locked_item(self.create_item(ItemName.maria))
|
||||
elif self.options.goal.value == 1:
|
||||
self.multiworld.get_location(LocationName.green_hill, self.player).place_locked_item(self.create_item(ItemName.maria))
|
||||
@@ -244,7 +202,7 @@ class SA2BWorld(World):
|
||||
if self.options.goal.value != 3:
|
||||
# Fill item pool with all required items
|
||||
for item in {**upgrades_table}:
|
||||
itempool += [self.create_item(item, None, self.options.goal.value)]
|
||||
itempool += [self.create_item(item, False, self.options.goal.value)]
|
||||
|
||||
if self.options.goal.value in [1, 2, 6]:
|
||||
# Some flavor of Chaos Emerald Hunt
|
||||
@@ -254,25 +212,6 @@ class SA2BWorld(World):
|
||||
# Black Market
|
||||
itempool += [self.create_item(ItemName.market_token) for _ in range(self.options.black_market_slots.value)]
|
||||
|
||||
if self.options.goal.value in [8]:
|
||||
available_locations: int = total_required_locations - len(itempool) - self.options.number_of_level_gates.value
|
||||
|
||||
while (self.options.minigame_madness_requirement.value * len(minigame_trap_table)) > available_locations:
|
||||
self.options.minigame_madness_requirement.value -= 1
|
||||
|
||||
while (self.options.minigame_madness_minimum.value * len(minigame_trap_table)) > available_locations:
|
||||
self.options.minigame_madness_minimum.value -= 1
|
||||
|
||||
traps_to_create: int = max(self.options.minigame_madness_minimum.value, self.options.minigame_madness_requirement.value)
|
||||
|
||||
# Minigame Madness
|
||||
for item in {**minigame_trap_table}:
|
||||
for i in range(traps_to_create):
|
||||
classification: ItemClassification = ItemClassification.trap
|
||||
if i < self.options.minigame_madness_requirement.value:
|
||||
classification |= ItemClassification.progression
|
||||
itempool.append(self.create_item(item, classification))
|
||||
|
||||
black_market_unlock_mult = 1.0
|
||||
if self.options.black_market_unlock_costs.value == 0:
|
||||
black_market_unlock_mult = 0.5
|
||||
@@ -296,12 +235,12 @@ class SA2BWorld(World):
|
||||
elif self.options.level_gate_costs.value == 1:
|
||||
gate_cost_mult = 0.8
|
||||
|
||||
self.shuffled_region_list = list(range(30))
|
||||
shuffled_region_list = list(range(30))
|
||||
emblem_requirement_list = list()
|
||||
self.multiworld.random.shuffle(self.shuffled_region_list)
|
||||
self.levels_per_gate = self.get_levels_per_gate()
|
||||
self.multiworld.random.shuffle(shuffled_region_list)
|
||||
levels_per_gate = self.get_levels_per_gate()
|
||||
|
||||
check_for_impossible_shuffle(self.shuffled_region_list, math.ceil(self.levels_per_gate[0]), self.multiworld)
|
||||
check_for_impossible_shuffle(shuffled_region_list, math.ceil(levels_per_gate[0]), self.multiworld)
|
||||
levels_added_to_gate = 0
|
||||
total_levels_added = 0
|
||||
current_gate = 0
|
||||
@@ -311,11 +250,11 @@ class SA2BWorld(World):
|
||||
gates = list()
|
||||
gates.append(LevelGate(0))
|
||||
for i in range(30):
|
||||
gates[current_gate].gate_levels.append(self.shuffled_region_list[i])
|
||||
gates[current_gate].gate_levels.append(shuffled_region_list[i])
|
||||
emblem_requirement_list.append(current_gate_emblems)
|
||||
levels_added_to_gate += 1
|
||||
total_levels_added += 1
|
||||
if levels_added_to_gate >= self.levels_per_gate[current_gate]:
|
||||
if levels_added_to_gate >= levels_per_gate[current_gate]:
|
||||
current_gate += 1
|
||||
if current_gate > self.options.number_of_level_gates.value:
|
||||
current_gate = self.options.number_of_level_gates.value
|
||||
@@ -326,19 +265,18 @@ class SA2BWorld(World):
|
||||
self.gate_costs[current_gate] = current_gate_emblems
|
||||
levels_added_to_gate = 0
|
||||
|
||||
self.region_emblem_map = dict(zip(self.shuffled_region_list, emblem_requirement_list))
|
||||
self.region_emblem_map = dict(zip(shuffled_region_list, emblem_requirement_list))
|
||||
|
||||
first_cannons_core_mission, final_cannons_core_mission = get_first_and_last_cannons_core_missions(self.mission_map, self.mission_count_map)
|
||||
|
||||
connect_regions(self.multiworld, self, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses, self.boss_rush_map, first_cannons_core_mission, final_cannons_core_mission)
|
||||
|
||||
max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core)
|
||||
max_required_emblems = min(int(max_required_emblems * 1.1), total_emblem_count)
|
||||
itempool += [self.create_item(ItemName.emblem) for _ in range(max_required_emblems)]
|
||||
|
||||
non_required_emblems = (total_emblem_count - max_required_emblems)
|
||||
junk_count = math.floor(non_required_emblems * (self.options.junk_fill_percentage.value / 100.0))
|
||||
itempool += [self.create_item(ItemName.emblem, ItemClassification.filler) for _ in range(non_required_emblems - junk_count)]
|
||||
itempool += [self.create_item(ItemName.emblem, True) for _ in range(non_required_emblems - junk_count)]
|
||||
|
||||
# Carve Traps out of junk_count
|
||||
trap_weights = []
|
||||
@@ -353,22 +291,7 @@ class SA2BWorld(World):
|
||||
trap_weights += ([ItemName.slow_trap] * self.options.slow_trap_weight.value)
|
||||
trap_weights += ([ItemName.cutscene_trap] * self.options.cutscene_trap_weight.value)
|
||||
trap_weights += ([ItemName.reverse_trap] * self.options.reverse_trap_weight.value)
|
||||
trap_weights += ([ItemName.literature_trap] * self.options.literature_trap_weight.value)
|
||||
trap_weights += ([ItemName.controller_drift_trap] * self.options.controller_drift_trap_weight.value)
|
||||
trap_weights += ([ItemName.poison_trap] * self.options.poison_trap_weight.value)
|
||||
trap_weights += ([ItemName.bee_trap] * self.options.bee_trap_weight.value)
|
||||
trap_weights += ([ItemName.pong_trap] * self.options.pong_trap_weight.value)
|
||||
trap_weights += ([ItemName.breakout_trap] * self.options.breakout_trap_weight.value)
|
||||
trap_weights += ([ItemName.fishing_trap] * self.options.fishing_trap_weight.value)
|
||||
trap_weights += ([ItemName.trivia_trap] * self.options.trivia_trap_weight.value)
|
||||
trap_weights += ([ItemName.pokemon_trivia_trap] * self.options.pokemon_trivia_trap_weight.value)
|
||||
trap_weights += ([ItemName.pokemon_count_trap] * self.options.pokemon_count_trap_weight.value)
|
||||
trap_weights += ([ItemName.number_sequence_trap] * self.options.number_sequence_trap_weight.value)
|
||||
trap_weights += ([ItemName.light_up_path_trap] * self.options.light_up_path_trap_weight.value)
|
||||
trap_weights += ([ItemName.pinball_trap] * self.options.pinball_trap_weight.value)
|
||||
trap_weights += ([ItemName.math_quiz_trap] * self.options.math_quiz_trap_weight.value)
|
||||
trap_weights += ([ItemName.snake_trap] * self.options.snake_trap_weight.value)
|
||||
trap_weights += ([ItemName.input_sequence_trap] * self.options.input_sequence_trap_weight.value)
|
||||
|
||||
junk_count += extra_junk_count
|
||||
trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.options.trap_fill_percentage.value / 100.0))
|
||||
@@ -424,15 +347,11 @@ class SA2BWorld(World):
|
||||
|
||||
|
||||
|
||||
def create_item(self, name: str, force_classification=None, goal=0) -> Item:
|
||||
data = None
|
||||
if name in event_table:
|
||||
data = event_table[name]
|
||||
else:
|
||||
data = item_table[name]
|
||||
def create_item(self, name: str, force_non_progression=False, goal=0) -> Item:
|
||||
data = item_table[name]
|
||||
|
||||
if force_classification is not None:
|
||||
classification = force_classification
|
||||
if force_non_progression:
|
||||
classification = ItemClassification.filler
|
||||
elif name == ItemName.emblem or \
|
||||
name in emeralds_table.keys() or \
|
||||
(name == ItemName.knuckles_shovel_claws and goal in [4, 5]):
|
||||
@@ -461,16 +380,9 @@ class SA2BWorld(World):
|
||||
set_rules(self.multiworld, self, self.player, self.gate_bosses, self.boss_rush_map, self.mission_map, self.mission_count_map, self.black_market_costs)
|
||||
|
||||
def write_spoiler(self, spoiler_handle: typing.TextIO):
|
||||
print_mission_orders_to_spoiler(self.mission_map,
|
||||
self.mission_count_map,
|
||||
self.shuffled_region_list,
|
||||
self.levels_per_gate,
|
||||
self.multiworld.player_name[self.player],
|
||||
spoiler_handle)
|
||||
|
||||
if self.options.number_of_level_gates.value > 0 or self.options.goal.value in [4, 5, 6]:
|
||||
spoiler_handle.write("\n")
|
||||
header_text = "SA2 Bosses for {}:\n"
|
||||
header_text = "Sonic Adventure 2 Bosses for {}:\n"
|
||||
header_text = header_text.format(self.multiworld.player_name[self.player])
|
||||
spoiler_handle.write(header_text)
|
||||
|
||||
@@ -523,20 +435,20 @@ class SA2BWorld(World):
|
||||
continue
|
||||
level_region = exit.connected_region
|
||||
for location in level_region.locations:
|
||||
if location.address != None:
|
||||
er_hint_data[location.address] = gate_name
|
||||
er_hint_data[location.address] = gate_name
|
||||
|
||||
for i in range(self.options.black_market_slots.value):
|
||||
location = self.multiworld.get_location(LocationName.chao_black_market_base + str(i + 1), self.player)
|
||||
er_hint_data[location.address] = str(self.black_market_costs[i]) + " " + str(ItemName.market_token)
|
||||
|
||||
|
||||
hint_data[self.player] = er_hint_data
|
||||
|
||||
@classmethod
|
||||
def stage_fill_hook(cls, multiworld: MultiWorld, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
if multiworld.get_game_players("Sonic Adventure 2 Battle"):
|
||||
progitempool.sort(
|
||||
key=lambda item: 0 if ("Emblem" in item.name and item.game == "Sonic Adventure 2 Battle") else 1)
|
||||
key=lambda item: 0 if (item.name != 'Emblem') else 1)
|
||||
|
||||
def get_levels_per_gate(self) -> list:
|
||||
levels_per_gate = list()
|
||||
@@ -574,39 +486,6 @@ class SA2BWorld(World):
|
||||
|
||||
return levels_per_gate
|
||||
|
||||
def output_active_traps(self) -> typing.Dict[int, int]:
|
||||
trap_data = {}
|
||||
|
||||
trap_data[0x30] = self.options.omochao_trap_weight.value
|
||||
trap_data[0x31] = self.options.timestop_trap_weight.value
|
||||
trap_data[0x32] = self.options.confusion_trap_weight.value
|
||||
trap_data[0x33] = self.options.tiny_trap_weight.value
|
||||
trap_data[0x34] = self.options.gravity_trap_weight.value
|
||||
trap_data[0x35] = self.options.exposition_trap_weight.value
|
||||
trap_data[0x37] = self.options.ice_trap_weight.value
|
||||
trap_data[0x38] = self.options.slow_trap_weight.value
|
||||
trap_data[0x39] = self.options.cutscene_trap_weight.value
|
||||
trap_data[0x3A] = self.options.reverse_trap_weight.value
|
||||
trap_data[0x3B] = self.options.literature_trap_weight.value
|
||||
trap_data[0x3C] = self.options.controller_drift_trap_weight.value
|
||||
trap_data[0x3D] = self.options.poison_trap_weight.value
|
||||
trap_data[0x3E] = self.options.bee_trap_weight.value
|
||||
|
||||
trap_data[0x50] = self.options.pong_trap_weight.value
|
||||
trap_data[0x51] = self.options.breakout_trap_weight.value
|
||||
trap_data[0x52] = self.options.fishing_trap_weight.value
|
||||
trap_data[0x53] = self.options.trivia_trap_weight.value
|
||||
trap_data[0x54] = self.options.pokemon_trivia_trap_weight.value
|
||||
trap_data[0x55] = self.options.pokemon_count_trap_weight.value
|
||||
trap_data[0x56] = self.options.number_sequence_trap_weight.value
|
||||
trap_data[0x57] = self.options.light_up_path_trap_weight.value
|
||||
trap_data[0x58] = self.options.pinball_trap_weight.value
|
||||
trap_data[0x59] = self.options.math_quiz_trap_weight.value
|
||||
trap_data[0x5A] = self.options.snake_trap_weight.value
|
||||
trap_data[0x5B] = self.options.input_sequence_trap_weight.value
|
||||
|
||||
return trap_data
|
||||
|
||||
def any_chao_locations_active(self) -> bool:
|
||||
if self.options.chao_race_difficulty.value > 0 or \
|
||||
self.options.chao_karate_difficulty.value > 0 or \
|
||||
@@ -807,6 +686,7 @@ class SA2BWorld(World):
|
||||
exit_choice = self.random.choice(valid_kindergarten_exits)
|
||||
exit_room = exit_to_room_map[exit_choice]
|
||||
all_exits_copy.remove(exit_choice)
|
||||
multi_rooms_copy.remove(exit_room)
|
||||
|
||||
destination = 0x06
|
||||
single_rooms_copy.remove(destination)
|
||||
@@ -843,8 +723,7 @@ class SA2BWorld(World):
|
||||
|
||||
er_layout[exit_choice] = destination
|
||||
|
||||
possible_reverse_exits = [exit for exit in room_to_exits_map[destination] if exit in all_exits_copy]
|
||||
reverse_exit = self.random.choice(possible_reverse_exits)
|
||||
reverse_exit = self.random.choice(room_to_exits_map[destination])
|
||||
|
||||
er_layout[reverse_exit] = exit_room
|
||||
|
||||
|
||||
@@ -214,67 +214,67 @@ class StardewValleyWorld(World):
|
||||
def setup_victory(self):
|
||||
if self.options.goal == Goal.option_community_center:
|
||||
self.create_event_location(location_table[GoalName.community_center],
|
||||
self.logic.goal.can_complete_community_center(),
|
||||
self.logic.bundle.can_complete_community_center,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_grandpa_evaluation:
|
||||
self.create_event_location(location_table[GoalName.grandpa_evaluation],
|
||||
self.logic.goal.can_finish_grandpa_evaluation(),
|
||||
self.logic.can_finish_grandpa_evaluation(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_bottom_of_the_mines:
|
||||
self.create_event_location(location_table[GoalName.bottom_of_the_mines],
|
||||
self.logic.goal.can_complete_bottom_of_the_mines(),
|
||||
True_(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_cryptic_note:
|
||||
self.create_event_location(location_table[GoalName.cryptic_note],
|
||||
self.logic.goal.can_complete_cryptic_note(),
|
||||
self.logic.quest.can_complete_quest("Cryptic Note"),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_master_angler:
|
||||
self.create_event_location(location_table[GoalName.master_angler],
|
||||
self.logic.goal.can_complete_master_angler(),
|
||||
self.logic.fishing.can_catch_every_fish_for_fishsanity(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_complete_collection:
|
||||
self.create_event_location(location_table[GoalName.complete_museum],
|
||||
self.logic.goal.can_complete_complete_collection(),
|
||||
self.logic.museum.can_complete_museum(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_full_house:
|
||||
self.create_event_location(location_table[GoalName.full_house],
|
||||
self.logic.goal.can_complete_full_house(),
|
||||
(self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce()),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_greatest_walnut_hunter:
|
||||
self.create_event_location(location_table[GoalName.greatest_walnut_hunter],
|
||||
self.logic.goal.can_complete_greatest_walnut_hunter(),
|
||||
self.logic.walnut.has_walnut(130),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_protector_of_the_valley:
|
||||
self.create_event_location(location_table[GoalName.protector_of_the_valley],
|
||||
self.logic.goal.can_complete_protector_of_the_valley(),
|
||||
self.logic.monster.can_complete_all_monster_slaying_goals(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_full_shipment:
|
||||
self.create_event_location(location_table[GoalName.full_shipment],
|
||||
self.logic.goal.can_complete_full_shipment(self.get_all_location_names()),
|
||||
self.logic.shipping.can_ship_everything_in_slot(self.get_all_location_names()),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_gourmet_chef:
|
||||
self.create_event_location(location_table[GoalName.gourmet_chef],
|
||||
self.logic.goal.can_complete_gourmet_chef(),
|
||||
self.logic.cooking.can_cook_everything,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_craft_master:
|
||||
self.create_event_location(location_table[GoalName.craft_master],
|
||||
self.logic.goal.can_complete_craft_master(),
|
||||
self.logic.crafting.can_craft_everything,
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_legend:
|
||||
self.create_event_location(location_table[GoalName.legend],
|
||||
self.logic.goal.can_complete_legend(),
|
||||
self.logic.money.can_have_earned_total(10_000_000),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_mystery_of_the_stardrops:
|
||||
self.create_event_location(location_table[GoalName.mystery_of_the_stardrops],
|
||||
self.logic.goal.can_complete_mystery_of_the_stardrop(),
|
||||
self.logic.has_all_stardrops(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_allsanity:
|
||||
self.create_event_location(location_table[GoalName.allsanity],
|
||||
self.logic.goal.can_complete_allsanity(),
|
||||
HasProgressionPercent(self.player, 100),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_perfection:
|
||||
self.create_event_location(location_table[GoalName.perfection],
|
||||
self.logic.goal.can_complete_perfection(),
|
||||
HasProgressionPercent(self.player, 100),
|
||||
Event.victory)
|
||||
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has(Event.victory, self.player)
|
||||
|
||||
@@ -13,9 +13,12 @@ from .relationship_logic import RelationshipLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from ..data.recipe_data import RecipeSource, StarterSource, ShopSource, SkillSource, FriendshipSource, \
|
||||
QueenOfSauceSource, CookingRecipe, ShopFriendshipSource
|
||||
QueenOfSauceSource, CookingRecipe, ShopFriendshipSource, \
|
||||
all_cooking_recipes_by_name
|
||||
from ..data.recipe_source import CutsceneSource, ShopTradeSource
|
||||
from ..locations import locations_by_tag, LocationTags
|
||||
from ..options import Chefsanity
|
||||
from ..options import ExcludeGingerIsland
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.region_names import LogicRegion
|
||||
from ..strings.skill_names import Skill
|
||||
@@ -89,3 +92,17 @@ BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]]
|
||||
@cache_self1
|
||||
def received_recipe(self, meal_name: str):
|
||||
return self.logic.received(f"{meal_name} Recipe")
|
||||
|
||||
@cached_property
|
||||
def can_cook_everything(self) -> StardewRule:
|
||||
cooksanity_prefix = "Cook "
|
||||
all_recipes_names = []
|
||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
for location in locations_by_tag[LocationTags.COOKSANITY]:
|
||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||
continue
|
||||
if location.mod_name and location.mod_name not in self.options.mods:
|
||||
continue
|
||||
all_recipes_names.append(location.name[len(cooksanity_prefix):])
|
||||
all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
|
||||
return self.logic.and_(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from functools import cached_property
|
||||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
@@ -11,10 +12,11 @@ from .relationship_logic import RelationshipLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .special_order_logic import SpecialOrderLogicMixin
|
||||
from .. import options
|
||||
from ..data.craftable_data import CraftingRecipe
|
||||
from ..data.craftable_data import CraftingRecipe, all_crafting_recipes_by_name
|
||||
from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
|
||||
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource
|
||||
from ..options import Craftsanity, SpecialOrderLocations
|
||||
from ..locations import locations_by_tag, LocationTags
|
||||
from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.region_names import Region
|
||||
|
||||
@@ -69,8 +71,7 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
||||
if isinstance(recipe.source, ShopSource):
|
||||
return self.logic.money.can_spend_at(recipe.source.region, recipe.source.price)
|
||||
if isinstance(recipe.source, SkillCraftsanitySource):
|
||||
return self.logic.skill.has_level(recipe.source.skill, recipe.source.level) & self.logic.skill.can_earn_level(recipe.source.skill,
|
||||
recipe.source.level)
|
||||
return self.logic.skill.has_level(recipe.source.skill, recipe.source.level) & self.logic.skill.can_earn_level(recipe.source.skill, recipe.source.level)
|
||||
if isinstance(recipe.source, SkillSource):
|
||||
return self.logic.skill.has_level(recipe.source.skill, recipe.source.level)
|
||||
if isinstance(recipe.source, MasterySource):
|
||||
@@ -94,3 +95,23 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
||||
@cache_self1
|
||||
def received_recipe(self, item_name: str):
|
||||
return self.logic.received(f"{item_name} Recipe")
|
||||
|
||||
@cached_property
|
||||
def can_craft_everything(self) -> StardewRule:
|
||||
craftsanity_prefix = "Craft "
|
||||
all_recipes_names = []
|
||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled
|
||||
for location in locations_by_tag[LocationTags.CRAFTSANITY]:
|
||||
if not location.name.startswith(craftsanity_prefix):
|
||||
continue
|
||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||
continue
|
||||
# FIXME Remove when recipes are in content packs
|
||||
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
|
||||
continue
|
||||
if location.mod_name and location.mod_name not in self.options.mods:
|
||||
continue
|
||||
all_recipes_names.append(location.name[len(craftsanity_prefix):])
|
||||
all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
|
||||
return self.logic.and_(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes))
|
||||
|
||||
@@ -29,7 +29,7 @@ class FishingLogicMixin(BaseLogicMixin):
|
||||
|
||||
|
||||
class FishingLogic(BaseLogic[Union[HasLogicMixin, FishingLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin,
|
||||
SkillLogicMixin]]):
|
||||
SkillLogicMixin]]):
|
||||
def can_fish_in_freshwater(self) -> StardewRule:
|
||||
return self.logic.skill.can_fish() & self.logic.region.can_reach_any((Region.forest, Region.town, Region.mountain))
|
||||
|
||||
@@ -97,5 +97,19 @@ SkillLogicMixin]]):
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def can_catch_every_fish_for_fishsanity(self) -> StardewRule:
|
||||
if not self.content.features.fishsanity.is_enabled:
|
||||
return self.can_catch_every_fish()
|
||||
|
||||
rules = [self.has_max_fishing()]
|
||||
|
||||
rules.extend(
|
||||
self.logic.fishing.can_catch_fish_for_fishsanity(fish)
|
||||
for fish in self.content.fishes.values()
|
||||
if self.content.features.fishsanity.is_included(fish)
|
||||
)
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def has_specific_bait(self, fish: FishItem) -> StardewRule:
|
||||
return self.can_catch_fish(fish) & self.logic.has(Machine.bait_maker)
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
import typing
|
||||
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from ..data.craftable_data import all_crafting_recipes_by_name
|
||||
from ..data.recipe_data import all_cooking_recipes_by_name
|
||||
from ..locations import LocationTags, locations_by_tag
|
||||
from ..mods.mod_data import ModNames
|
||||
from ..options import options
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.building_names import Building
|
||||
from ..strings.quest_names import Quest
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.wallet_item_names import Wallet
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .logic import StardewLogic
|
||||
else:
|
||||
StardewLogic = object
|
||||
|
||||
|
||||
class GoalLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.goal = GoalLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class GoalLogic(BaseLogic[StardewLogic]):
|
||||
|
||||
def can_complete_community_center(self) -> StardewRule:
|
||||
return self.logic.bundle.can_complete_community_center
|
||||
|
||||
def can_finish_grandpa_evaluation(self) -> StardewRule:
|
||||
# https://stardewvalleywiki.com/Grandpa
|
||||
rules_worth_a_point = [
|
||||
self.logic.money.can_have_earned_total(50_000),
|
||||
self.logic.money.can_have_earned_total(100_000),
|
||||
self.logic.money.can_have_earned_total(200_000),
|
||||
self.logic.money.can_have_earned_total(300_000),
|
||||
self.logic.money.can_have_earned_total(500_000),
|
||||
self.logic.money.can_have_earned_total(1_000_000), # first point
|
||||
self.logic.money.can_have_earned_total(1_000_000), # second point
|
||||
self.logic.skill.has_total_level(30),
|
||||
self.logic.skill.has_total_level(50),
|
||||
self.logic.museum.can_complete_museum(),
|
||||
# Catching every fish not expected
|
||||
# Shipping every item not expected
|
||||
self.logic.relationship.can_get_married() & self.logic.building.has_house(2),
|
||||
self.logic.relationship.has_hearts_with_n(5, 8), # 5 Friends
|
||||
self.logic.relationship.has_hearts_with_n(10, 8), # 10 friends
|
||||
self.logic.pet.has_pet_hearts(5), # Max Pet
|
||||
self.logic.bundle.can_complete_community_center, # 1 point for Community Center Completion
|
||||
self.logic.bundle.can_complete_community_center, # Ceremony first point
|
||||
self.logic.bundle.can_complete_community_center, # Ceremony second point
|
||||
self.logic.received(Wallet.skull_key),
|
||||
self.logic.wallet.has_rusty_key(),
|
||||
]
|
||||
return self.logic.count(12, *rules_worth_a_point)
|
||||
|
||||
def can_complete_bottom_of_the_mines(self) -> StardewRule:
|
||||
# The location is in the bottom of the mines region, so no actual rule is required
|
||||
return self.logic.true_
|
||||
|
||||
def can_complete_cryptic_note(self) -> StardewRule:
|
||||
return self.logic.quest.can_complete_quest(Quest.cryptic_note)
|
||||
|
||||
def can_complete_master_angler(self) -> StardewRule:
|
||||
if not self.content.features.fishsanity.is_enabled:
|
||||
return self.logic.fishing.can_catch_every_fish()
|
||||
|
||||
rules = [self.logic.fishing.has_max_fishing()]
|
||||
|
||||
rules.extend(
|
||||
self.logic.fishing.can_catch_fish_for_fishsanity(fish)
|
||||
for fish in self.content.fishes.values()
|
||||
if self.content.features.fishsanity.is_included(fish)
|
||||
)
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def can_complete_complete_collection(self) -> StardewRule:
|
||||
return self.logic.museum.can_complete_museum()
|
||||
|
||||
def can_complete_full_house(self) -> StardewRule:
|
||||
return self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce()
|
||||
|
||||
def can_complete_greatest_walnut_hunter(self) -> StardewRule:
|
||||
return self.logic.walnut.has_walnut(130)
|
||||
|
||||
def can_complete_protector_of_the_valley(self) -> StardewRule:
|
||||
return self.logic.monster.can_complete_all_monster_slaying_goals()
|
||||
|
||||
def can_complete_full_shipment(self, all_location_names_in_slot: list[str]) -> StardewRule:
|
||||
if self.options.shipsanity == options.Shipsanity.option_none:
|
||||
return self.logic.shipping.can_ship_everything()
|
||||
|
||||
rules = [self.logic.building.has_building(Building.shipping_bin)]
|
||||
|
||||
for shipsanity_location in locations_by_tag[LocationTags.SHIPSANITY]:
|
||||
if shipsanity_location.name not in all_location_names_in_slot:
|
||||
continue
|
||||
rules.append(self.logic.region.can_reach_location(shipsanity_location.name))
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def can_complete_gourmet_chef(self) -> StardewRule:
|
||||
cooksanity_prefix = "Cook "
|
||||
all_recipes_names = []
|
||||
exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
|
||||
for location in locations_by_tag[LocationTags.COOKSANITY]:
|
||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||
continue
|
||||
if location.mod_name and location.mod_name not in self.options.mods:
|
||||
continue
|
||||
all_recipes_names.append(location.name[len(cooksanity_prefix):])
|
||||
all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
|
||||
return self.logic.and_(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes))
|
||||
|
||||
def can_complete_craft_master(self) -> StardewRule:
|
||||
craftsanity_prefix = "Craft "
|
||||
all_recipes_names = []
|
||||
exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
|
||||
exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled
|
||||
for location in locations_by_tag[LocationTags.CRAFTSANITY]:
|
||||
if not location.name.startswith(craftsanity_prefix):
|
||||
continue
|
||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||
continue
|
||||
# FIXME Remove when recipes are in content packs
|
||||
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
|
||||
continue
|
||||
if location.mod_name and location.mod_name not in self.options.mods:
|
||||
continue
|
||||
all_recipes_names.append(location.name[len(craftsanity_prefix):])
|
||||
all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
|
||||
return self.logic.and_(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes))
|
||||
|
||||
def can_complete_legend(self) -> StardewRule:
|
||||
return self.logic.money.can_have_earned_total(10_000_000)
|
||||
|
||||
def can_complete_mystery_of_the_stardrop(self) -> StardewRule:
|
||||
other_rules = []
|
||||
number_of_stardrops_to_receive = 0
|
||||
number_of_stardrops_to_receive += 1 # The Mines level 100
|
||||
number_of_stardrops_to_receive += 1 # Old Master Cannoli
|
||||
number_of_stardrops_to_receive += 1 # Museum Stardrop
|
||||
number_of_stardrops_to_receive += 1 # Krobus Stardrop
|
||||
|
||||
# Master Angler Stardrop
|
||||
if self.content.features.fishsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.logic.fishing.can_catch_every_fish())
|
||||
|
||||
if self.options.festival_locations == options.FestivalLocations.option_disabled: # Fair Stardrop
|
||||
other_rules.append(self.logic.season.has(Season.fall))
|
||||
else:
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
||||
# Spouse Stardrop
|
||||
if self.content.features.friendsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.logic.relationship.has_hearts_with_any_bachelor(13))
|
||||
|
||||
if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
||||
return self.logic.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules, allow_empty=True)
|
||||
|
||||
def can_complete_allsanity(self) -> StardewRule:
|
||||
return self.logic.has_progress_percent(100)
|
||||
|
||||
def can_complete_perfection(self) -> StardewRule:
|
||||
return self.logic.has_progress_percent(100)
|
||||
@@ -1,5 +1,5 @@
|
||||
from .base_logic import BaseLogic
|
||||
from ..stardew_rule import StardewRule, And, Or, Has, Count, true_, false_, HasProgressionPercent
|
||||
from ..stardew_rule import StardewRule, And, Or, Has, Count, true_, false_
|
||||
|
||||
|
||||
class HasLogicMixin(BaseLogic[None]):
|
||||
@@ -23,12 +23,6 @@ class HasLogicMixin(BaseLogic[None]):
|
||||
def has_n(self, *items: str, count: int):
|
||||
return self.count(count, *(self.has(item) for item in items))
|
||||
|
||||
def has_progress_percent(self, percent: int):
|
||||
assert percent >= 0, "Can't have a negative progress percent"
|
||||
assert percent <= 100, "Can't have a progress percent over 100"
|
||||
|
||||
return HasProgressionPercent(self.player, percent)
|
||||
|
||||
@staticmethod
|
||||
def count(count: int, *rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a Count conditions without rules"
|
||||
@@ -53,14 +47,8 @@ class HasLogicMixin(BaseLogic[None]):
|
||||
return Count(rules, count)
|
||||
|
||||
@staticmethod
|
||||
def and_(*rules: StardewRule, allow_empty: bool = False) -> StardewRule:
|
||||
"""
|
||||
:param rules: The rules to combine
|
||||
:param allow_empty: If True, return true_ when no rules are given. Otherwise, raise an error.
|
||||
"""
|
||||
if not rules:
|
||||
assert allow_empty, "Can't create a And conditions without rules"
|
||||
return true_
|
||||
def and_(*rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a And conditions without rules"
|
||||
|
||||
if len(rules) == 1:
|
||||
return rules[0]
|
||||
@@ -68,14 +56,8 @@ class HasLogicMixin(BaseLogic[None]):
|
||||
return And(*rules)
|
||||
|
||||
@staticmethod
|
||||
def or_(*rules: StardewRule, allow_empty: bool = False) -> StardewRule:
|
||||
"""
|
||||
:param rules: The rules to combine
|
||||
:param allow_empty: If True, return false_ when no rules are given. Otherwise, raise an error.
|
||||
"""
|
||||
if not rules:
|
||||
assert allow_empty, "Can't create a Or conditions without rules"
|
||||
return false_
|
||||
def or_(*rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a Or conditions without rules"
|
||||
|
||||
if len(rules) == 1:
|
||||
return rules[0]
|
||||
|
||||
@@ -19,7 +19,6 @@ from .farming_logic import FarmingLogicMixin
|
||||
from .festival_logic import FestivalLogicMixin
|
||||
from .fishing_logic import FishingLogicMixin
|
||||
from .gift_logic import GiftLogicMixin
|
||||
from .goal_logic import GoalLogicMixin
|
||||
from .grind_logic import GrindLogicMixin
|
||||
from .harvesting_logic import HarvestingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
@@ -51,7 +50,8 @@ from ..data.museum_data import all_museum_items
|
||||
from ..data.recipe_data import all_cooking_recipes
|
||||
from ..mods.logic.magic_logic import MagicLogicMixin
|
||||
from ..mods.logic.mod_logic import ModLogicMixin
|
||||
from ..options import ExcludeGingerIsland, StardewValleyOptions
|
||||
from ..mods.mod_data import ModNames
|
||||
from ..options import ExcludeGingerIsland, FestivalLocations, StardewValleyOptions
|
||||
from ..stardew_rule import False_, True_, StardewRule
|
||||
from ..strings.animal_names import Animal
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
@@ -93,7 +93,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
|
||||
CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, QualityLogicMixin,
|
||||
SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin,
|
||||
SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin, HarvestingLogicMixin, SourceLogicMixin,
|
||||
RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin, GoalLogicMixin):
|
||||
RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin):
|
||||
player: int
|
||||
options: StardewValleyOptions
|
||||
content: StardewContent
|
||||
@@ -375,11 +375,71 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
|
||||
def can_smelt(self, item: str) -> StardewRule:
|
||||
return self.has(Machine.furnace) & self.has(item)
|
||||
|
||||
def can_finish_grandpa_evaluation(self) -> StardewRule:
|
||||
# https://stardewvalleywiki.com/Grandpa
|
||||
rules_worth_a_point = [
|
||||
self.money.can_have_earned_total(50000), # 50 000g
|
||||
self.money.can_have_earned_total(100000), # 100 000g
|
||||
self.money.can_have_earned_total(200000), # 200 000g
|
||||
self.money.can_have_earned_total(300000), # 300 000g
|
||||
self.money.can_have_earned_total(500000), # 500 000g
|
||||
self.money.can_have_earned_total(1000000), # 1 000 000g first point
|
||||
self.money.can_have_earned_total(1000000), # 1 000 000g second point
|
||||
self.skill.has_total_level(30), # Total Skills: 30
|
||||
self.skill.has_total_level(50), # Total Skills: 50
|
||||
self.museum.can_complete_museum(), # Completing the museum for a point
|
||||
# Catching every fish not expected
|
||||
# Shipping every item not expected
|
||||
self.relationship.can_get_married() & self.building.has_house(2),
|
||||
self.relationship.has_hearts_with_n(5, 8), # 5 Friends
|
||||
self.relationship.has_hearts_with_n(10, 8), # 10 friends
|
||||
self.pet.has_pet_hearts(5), # Max Pet
|
||||
self.bundle.can_complete_community_center, # Community Center Completion
|
||||
self.bundle.can_complete_community_center, # CC Ceremony first point
|
||||
self.bundle.can_complete_community_center, # CC Ceremony second point
|
||||
self.received(Wallet.skull_key), # Skull Key obtained
|
||||
self.wallet.has_rusty_key(), # Rusty key obtained
|
||||
]
|
||||
return self.count(12, *rules_worth_a_point)
|
||||
|
||||
def has_island_trader(self) -> StardewRule:
|
||||
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
|
||||
return False_()
|
||||
return self.region.can_reach(Region.island_trader)
|
||||
|
||||
def has_all_stardrops(self) -> StardewRule:
|
||||
other_rules = []
|
||||
number_of_stardrops_to_receive = 0
|
||||
number_of_stardrops_to_receive += 1 # The Mines level 100
|
||||
number_of_stardrops_to_receive += 1 # Old Master Cannoli
|
||||
number_of_stardrops_to_receive += 1 # Museum Stardrop
|
||||
number_of_stardrops_to_receive += 1 # Krobus Stardrop
|
||||
|
||||
# Master Angler Stardrop
|
||||
if self.content.features.fishsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.fishing.can_catch_every_fish())
|
||||
|
||||
if self.options.festival_locations == FestivalLocations.option_disabled: # Fair Stardrop
|
||||
other_rules.append(self.season.has(Season.fall))
|
||||
else:
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
||||
# Spouse Stardrop
|
||||
if self.content.features.friendsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.relationship.has_hearts_with_any_bachelor(13))
|
||||
|
||||
if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
||||
if not other_rules:
|
||||
return self.received("Stardrop", number_of_stardrops_to_receive)
|
||||
|
||||
return self.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules)
|
||||
|
||||
def has_abandoned_jojamart(self) -> StardewRule:
|
||||
return self.received(CommunityUpgrade.movie_theater, 1)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from functools import cached_property
|
||||
from typing import Union
|
||||
from typing import Union, List
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
@@ -8,7 +8,7 @@ from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from ..locations import LocationTags, locations_by_tag
|
||||
from ..options import ExcludeGingerIsland
|
||||
from ..options import ExcludeGingerIsland, Shipsanity
|
||||
from ..options import SpecialOrderLocations
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.building_names import Building
|
||||
@@ -45,3 +45,15 @@ class ShippingLogic(BaseLogic[Union[ReceivedLogicMixin, ShippingLogicMixin, Buil
|
||||
continue
|
||||
all_items_to_ship.append(location.name[len(shipsanity_prefix):])
|
||||
return self.logic.building.has_building(Building.shipping_bin) & self.logic.has_all(*all_items_to_ship)
|
||||
|
||||
def can_ship_everything_in_slot(self, all_location_names_in_slot: List[str]) -> StardewRule:
|
||||
if self.options.shipsanity == Shipsanity.option_none:
|
||||
return self.logic.shipping.can_ship_everything()
|
||||
|
||||
rules = [self.logic.building.has_building(Building.shipping_bin)]
|
||||
|
||||
for shipsanity_location in locations_by_tag[LocationTags.SHIPSANITY]:
|
||||
if shipsanity_location.name not in all_location_names_in_slot:
|
||||
continue
|
||||
rules.append(self.logic.region.can_reach_location(shipsanity_location.name))
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
@@ -63,7 +63,7 @@ class WitnessWorld(World):
|
||||
item_name_groups = static_witness_items.ITEM_GROUPS
|
||||
location_name_groups = static_witness_locations.AREA_LOCATION_GROUPS
|
||||
|
||||
required_client_version = (0, 6, 0)
|
||||
required_client_version = (0, 5, 1)
|
||||
|
||||
player_logic: WitnessPlayerLogic
|
||||
player_locations: WitnessPlayerLocations
|
||||
|
||||
@@ -262,6 +262,10 @@ def is_easter_time() -> bool:
|
||||
# Thus, we just take a range from the earliest to latest possible easter dates.
|
||||
|
||||
today = date.today()
|
||||
|
||||
if today < date(2025, 3, 31): # Don't go live early if 0.6.0 RC3 happens, with a little leeway
|
||||
return False
|
||||
|
||||
earliest_easter_day = date(today.year, 3, 20) # Earliest possible is 3/22 + 2 day buffer for Good Friday
|
||||
last_easter_day = date(today.year, 4, 26) # Latest possible is 4/25 + 1 day buffer for Easter Monday
|
||||
|
||||
|
||||
Reference in New Issue
Block a user