Files
dockipelago/worlds/spyro2/Options.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

507 lines
21 KiB
Python

import typing
from dataclasses import dataclass
from Options import Toggle, DefaultOnToggle, Option, Range, Choice, ItemDict, DeathLink, PerGameCommonOptions, OptionGroup
class GoalOptions():
RIPTO = 0
SIXTY_FOUR_ORB = 3
HUNDRED_PERCENT = 4
TEN_TOKENS = 5
ALL_SKILLPOINTS = 6
EPILOGUE = 7
ORB_HUNT = 8
class LevelLockOptions():
VANILLA = 0
KEYS = 1
ORBS = 2
class MoneybagsOptions():
VANILLA = 0
PRICE_SHUFFLE = 1
MONEYBAGSSANITY = 2
class GemsanityOptions():
OFF = 0
PARTIAL = 1
FULL = 2
FULL_GLOBAL = 3
class SparxUpgradeOptions():
OFF = 0
BLUE = 1
GREEN = 2
SPARXLESS = 3
TRUE_SPARXLESS = 4
class AbilityOptions():
VANILLA = 0
IN_POOL = 1
OFF = 2
START_WITH = 3
class BomboOptions():
VANILLA = 0
THIRD_ONLY = 1
FIRST_ONLY = 2
FIRST_ONLY_NO_ATTACK = 3
class PortalTextColorOptions():
DEFAULT = 0
RED = 1
GREEN = 2
BLUE = 3
PINK = 4
WHITE = 5
class RandomizeGemColorOptions():
DEFAULT = 0
SHUFFLE = 1
RANDOM = 2
TRUE_RANDOM = 3
class GoalOption(Choice):
"""Lets the user choose the completion goal.
Ripto - Collect enough orbs to open the arena, and beat Ripto. The game marks you as having defeated Ripto during the ensuing cutscene.
64 Orb - Collect 64 orbs and beat Ripto.
100 Percent - Collect all talismans, orbs, and gems and beat Ripto. In Open World mode, no talismans are required.
10 Tokens - Collect 8000 gems and 55 orbs to unlock the theme park and collect all 10 tokens in Dragon Shores.
All Skillpoints - Collect all 16 skill points in the game. Excluded locations are still required for this goal.
Epilogue - Unlock the full epilogue by collecting all 16 skill points and defeating Ripto. Excluded locations are still required for this goal."""
display_name = "Completion Goal"
default = GoalOptions.RIPTO
option_ripto = GoalOptions.RIPTO
option_64_orb = GoalOptions.SIXTY_FOUR_ORB
option_100_percent = GoalOptions.HUNDRED_PERCENT
option_10_tokens = GoalOptions.TEN_TOKENS
option_all_skillpoints = GoalOptions.ALL_SKILLPOINTS
option_epilogue = GoalOptions.EPILOGUE
# TODO: Allow this once edge cases are handled.
# option_orb_hunt = GoalOptions.ORB_HUNT
class OrbHuntRequirement(Range):
"""Determines how many orbs are needed to complete the Orb Hunt goal. Has no effect if any other goal is chosen."""
display_name = "Orbs Required for Orb Hunt"
range_start = 2
range_end = 64
default = 40
class RiptoDoorOrbs(Range):
"""Determines how many orbs are required to unlock the door to Ripto.
NOTE: Due to limitations of Spyro 2, if you connect to Archipelago while in Winter Tundra, the game will default
to 40 orbs until you exit Winter Tundra and return."""
display_name = "Orbs to Unlock Ripto"
range_start = 0
range_end = 64
default = 40
class TotalAvailableOrbs(Range):
"""Determines the number of orbs available.
NOTE: If fewer than the amount required for the Ripto door ar an Orb Hunt goal, more will be added.
Other orb requirements throughout the game will be adjusted according to the number available."""
display_name = "Orbs Available"
range_start = 2
range_end = 64
default = 64
class GuaranteedItemsOption(ItemDict):
"""Guarantees that the specified items will be in the item pool"""
display_name = "Guaranteed Items"
class EnableOpenWorld(Toggle):
"""If on, Crush and Gulp do not require talismans."""
display_name = "Enable Open World"
class LevelLockOption(Choice):
"""Determines the rules for unlocking each level. Glimmer, Homeworlds, and bosses are bosses always have their
vanilla requirements.
Vanilla: Levels are available if you meet the vanilla requirements.
Keys: Levels are unlocked by finding "Unlock" items. Note: you may need to increase the number of locations (for example,
by adding gem checks) to add keys.
"""
display_name = "Level Locks"
default = LevelLockOptions.VANILLA
option_vanilla = LevelLockOptions.VANILLA
option_keys = LevelLockOptions.KEYS
# option_orbs = LevelLockOptions.ORBS
class StartingLevelCount(Range):
"""Determines how many level unlocks the player starts with.
The player always has access to Glimmer, homeworlds, and boss fights.
NOTE: Only has an effect when level locks are on."""
display_name = "Starting Level Unlocks"
range_start = 0
range_end = 22
default = 8
class StartWithAbilitiesAndWarps(Toggle):
"""If on in Open World mode, the player will start with swim, climb, headbash, and access to all 3 homeworlds.
NOTE: Only has an effect in Open World mode."""
display_name = "Open World Start With Abilities and Warps"
class Enable25PctGemChecksOption(Toggle):
"""Adds checks for getting 25% of the gems in a level"""
display_name = "Enable 25% Gem Checks"
class Enable50PctGemChecksOption(Toggle):
"""Adds checks for getting 50% of the gems in a level"""
display_name = "Enable 50% Gem Checks"
class Enable75PctGemChecksOption(Toggle):
"""Adds checks for getting 75% of the gems in a level"""
display_name = "Enable 75% Gem Checks"
class EnableGemChecksOption(Toggle):
"""Adds checks for getting all gems in a level"""
display_name = "Enable 100% Gem Checks"
class EnableTotalGemChecksOption(Toggle):
"""Adds checks for every 500 gems you collect total.
NOTE: Gems currently paid to Moneybags do not count towards your total.
Logic assumes you pay Moneybags everywhere you can so you cannot be locked out of checks."""
display_name = "Enable Total Gem Count Checks"
class MaxTotalGemCheckOption(Range):
"""Sets the highest number of total gems that can be required for Total Gem Count checks.
This has no effect if Enable Total Gem Count Checks is disabled."""
display_name = "Max for Total Gem Count Checks"
range_start = 500
range_end = 10000
default = 4000
class EnableSkillpointChecksOption(Toggle):
"""Adds checks for getting skill points"""
display_name = "Enable Skillpoint Checks"
class EnableLifeBottleChecksOption(Toggle):
"""Adds checks for getting life bottles"""
display_name = "Enable Life Bottle Checks"
class EnableSpiritParticleChecksOption(Toggle):
"""Adds checks for getting all spirit particles in a level.
Some minigame enemies are counted as spirit particles, like Draclets in Crystal Glacier.
NOTE: Some enemies will only release spirit particles while the camera is on them."""
display_name = "Enable Spirit Particle Checks"
class EnableGemsanityOption(Choice):
"""Adds checks for each individual gem.
WARNING: To avoid logic issues, this setting is meant for Moneybagssanity only. If Moneybagssanity is off,
all Moneybags prices will be set to 0 in game.
WARNING: Both full and full global require the host to edit allow_full_gemsanity
in their yaml file.
Off: Individual gems are not checks.
Partial: Every gem has a chance to be a check, but only 200 will be (chosen at random). For every level with loose
gems (not speedways), 8 items giving 50 gems for that level will be added to the pool.
Full: All gems are checks. Gem items will be shuffled only within your world.
Full Global: All gems are checks. Gem items can be anywhere."""
display_name = "Enable Gemsanity"
default = GemsanityOptions.OFF
option_off = GemsanityOptions.OFF
option_partial = GemsanityOptions.PARTIAL
option_full = GemsanityOptions.FULL
option_full_global = GemsanityOptions.FULL_GLOBAL
class MoneybagsSettings(Choice):
"""Determines settings for Moneybags unlocks.
Vanilla - Pay Moneybags to progress as usual
COMING SOON (tm): Price Shuffle - The prices Moneybags charges are randomized, while still allowing for beatable seeds.
Moneybagssanity - You cannot pay Moneybags at all and must find unlock items to progress. Glimmer Bridge
is excluded to avoid issues with early game randomization."""
display_name = "Moneybags Settings"
default = MoneybagsOptions.VANILLA
option_vanilla = MoneybagsOptions.VANILLA
# TODO: Implement.
# option_price_shuffle = MoneybagsOptions.PRICE_SHUFFLE
option_moneybagssanity = MoneybagsOptions.MONEYBAGSSANITY
class EnableDeathLink(DeathLink):
"""If enabled, Spyro will die when a DeathLink is received and will send them on his death.
This is a beta feature and does not fully support all edge cases yet - not every death will trigger a DeathLink,
and not every received DeathLink will kill Spyro."""
display_name = "DeathLink"
class EnableFillerExtraLives(DefaultOnToggle):
"""Allows filler items to include extra lives"""
display_name = "Enable Extra Lives Filler"
class EnableFillerDestructiveSpyro(Toggle):
"""Allows filler items to include temporarily powering up Spyro so anything destructible he touches is destroyed.
Affects enemies, strong chests, breakable walls, etc."""
display_name = "Enable Temporary Destructive Spyro Filler"
# Likely possible but check for side effects.
#class EnableFillerInvincibility(Toggle):
# """Allows filler items to include temporary invincibility"""
# display_name = "Enable Temporary Invincibility Filler"
class EnableFillerColorChange(Toggle):
"""Allows filler items to include changing Spyro's color"""
display_name = "Enable Changing Spyro's Color Filler"
class EnableFillerBigHeadMode(Toggle):
"""Allows filler items to include turning on Big Head Mode and Flat Spyro Mode"""
display_name = "Enable Big Head and Flat Spyro Filler"
class EnableFillerHealSparx(Toggle):
"""Allows filler items to include healing Sparx."""
display_name = "Enable healing Sparx Filler"
class TrapFillerPercent(Range):
"""Determines the percentage of filler items that will be traps."""
display_name = "Trap Percentage of Filler"
range_start = 0
range_end = 100
default = 0
class EnableTrapDamageSparx(Toggle):
"""Allows filler items to include damaging Sparx. Cannot directly kill Spyro."""
display_name = "Enable Hurting Sparx Trap"
class EnableTrapSparxless(Toggle):
"""Allows filler items to include removing Sparx."""
display_name = "Enable Sparxless Trap"
class EnableTrapInvisible(Toggle):
"""Allows filler items to turn Spyro invisible briefly.
NOTE: Duckstation must be run in Interpreter mode for this to have any effect."""
display_name = "Enable Invisibility Trap"
class EnableTrapRemappedController(Toggle):
"""Allows filler items to "remap" your controller briefly.
NOTE: Duckstation must be run in Interpreter mode for this to have any effect."""
display_name = "Enable Remapped Controller"
class EnableProgressiveSparxHealth(Choice):
"""Start the game with lower max health and add items to the pool to increase your max health.
Off - The game behaves normally.
Blue - Your max health starts at blue Sparx, and 1 upgrade is added to the pool.
Green - Your max health starts at green Sparx, and 2 upgrades are added to the pool.
Sparxless - Your max health starts at no Sparx, and 3 upgrades are added to the pool.
True Sparxless - Your max health is permanently Sparxless. No upgrades are added to the pool."""
display_name = "Enable Progressive Sparx Health Upgrades"
default = SparxUpgradeOptions.OFF
option_off = SparxUpgradeOptions.OFF
option_blue = SparxUpgradeOptions.BLUE
option_green = SparxUpgradeOptions.GREEN
option_sparxless = SparxUpgradeOptions.SPARXLESS
option_true_sparxless = SparxUpgradeOptions.TRUE_SPARXLESS
class ProgressiveSparxHealthLogic(Toggle):
"""Ensures that sufficient max Sparx health is in logic before various required checks.
Entering Aquaria Towers and Crush logically requires green Sparx. Entering Skelos Badlands and Gulp
logically requires blue Sparx, and entering Ripto logically requires gold Sparx.
Note: This does nothing unless Enable Progressive Sparx Health Upgrades is set to blue, green, or Sparxless,"""
display_name = "Enable Progressive Sparx Health Logic"
class DoubleJumpAbility(Choice):
"""By default, Spyro 2 supports a double jump ability, where jumping then pressing square without letting go of jump
gains extra height. This allows many sequence breaks within the game.
Note that most skips possible with double jump can be done in other, harder ways.
This option affects how the game plays, but does not directly impact logic.
The logic impact of the tricks below may be impacted by whether you can double jump.
NOTE: Duckstation must be run in Interpreter mode for this to have any effect.
Vanilla - Double Jump behaves normally.
In Pool - Adds Double Jump to the item pool. The ability is disabled until you acquire the item.
Off - Double Jump is disabled."""
display_name = "Double Jump Ability"
default = AbilityOptions.VANILLA
option_vanilla = AbilityOptions.VANILLA
option_in_pool = AbilityOptions.IN_POOL
option_off = AbilityOptions.OFF
class FireballAbility(Choice):
"""Spyro 2 has a permanent fireball powerup in Dragon Shores, which makes gameplay much easier.
With glitches, it is possible to get here without 100% completion.
Exiting a 100% save file and entering a new game without resetting Duckstation also carries it over to the new save.
This option affects how the game plays, not game logic.
Vanilla - The fireball powerup in Dragon Shores behaves normally.
In Pool - Adds Permanent Fireball to the item pool. The Dragon Shores powerup does not work.
Off - Permanent Fireball is disabled. The Dragon Shores powerup does not work.
Start With - You begin the game with fireball, as in New Game Plus."""
display_name = "Permanent Fireball Ability"
default = AbilityOptions.VANILLA
option_vanilla = AbilityOptions.VANILLA
option_in_pool = AbilityOptions.IN_POOL
option_off = AbilityOptions.OFF
option_start_with = AbilityOptions.START_WITH
class ColossusStartingGoals(Range):
"""Determines how many goals you start with in both Colossus orb challenges."""
display_name = "Colossus Starting Goals"
range_start = 0
range_end = 4
default = 0
class IdolEasyFish(Toggle):
"""Makes it so red fish behave the same as other types of fish in Idol Springs."""
display_name = "Idol Easy Fish"
class HurricosEasyLightningOrbs(Toggle):
"""Makes it so lightning thieves do not steal the orbs in Hurricos."""
display_name = "Hurricos Easy Lightning Orbs"
class BreezeRequiredGears(Range):
"""Determines how many gears you must collect to complete the trolley orb."""
display_name = "Breeze Required Gears"
range_start = 1
range_end = 50
default = 50
class ScorchBomboSettings(Choice):
"""Determines how the Bombo orb works in Scorch.
NOTE: This is a beta option. Multiple Bombos may appear, and interacting with the wrong one may softlock Bombo.
Vanilla - Bombo behaves as normal.
First Only - Complete the first (shortest) path to complete the orb.
Attackless First Only - Complete the first (shortest) path to complete the orb. Bombo will not attack."""
display_name = "Scorch Bombo Settings"
default = BomboOptions.VANILLA
option_vanilla = BomboOptions.VANILLA
option_first_only = BomboOptions.FIRST_ONLY
option_attackless_first_only = BomboOptions.FIRST_ONLY_NO_ATTACK
class FractureRequireHeadbash(DefaultOnToggle):
"""Determines if Hunter requires headbash to start Earthshaper Bash.
Without headbash, this orb can be completed with fireball or the Fracture Easy Earthshapers setting.
This does not change the orb's logic or change how it plays."""
display_name = "Fracture Require Headbash"
class FractureEasyEarthshapers(Toggle):
"""Removes the 7 earthshapers from the Alchemist area and reduces the maximum number of spirit particles in the level
accordingly.
Removes the headbash requirement from the Fracture Hills all spirit particles check.
The second orb still requires headbash, unless Fracture Require Headbash is disabled."""
display_name = "Fracture Easy Earthshapers"
class MagmaSpyroStartingPopcorn(Range):
"""Determines how many popcorn crystals you start with in each Hunter orb challenge."""
display_name = "Magma Spyro Starting Popcorn"
range_start = 0
range_end = 9
default = 0
class MagmaHunterStartingPopcorn(Range):
"""Determines how many popcorn crystals Hunter starts with in each Hunter orb challenge."""
display_name = "Magma Hunter Starting Popcorn"
range_start = 0
range_end = 9
default = 0
class ShadyRequireHeadbash(DefaultOnToggle):
"""Determines if Free Hippos in Shady Oasis requires headbash to start.
Without headbash, this orb can be completed with fireball.
This does not change the orb's logic or change how it plays."""
display_name = "Shady Require Headbash"
class EasyGulp(Toggle):
"""If turned on, Spyro does double damage to Gulp."""
display_name = "Easy Gulp"
class PortalAndGemCollectionColor(Choice):
"""Changes the color of the number that appears when gems are collected,
as well as the text on portals."""
display_name = "Portal and Gem Collection Text Color"
default = PortalTextColorOptions.DEFAULT
option_default = PortalTextColorOptions.DEFAULT
option_red = PortalTextColorOptions.RED
option_green = PortalTextColorOptions.GREEN
option_blue = PortalTextColorOptions.BLUE
option_pink = PortalTextColorOptions.PINK
option_white = PortalTextColorOptions.WHITE
class GemColor(Choice):
"""Changes the color of gem types (and some other items in game).
Default: No changes.
Shuffle: Mixes up the colors of gem types.
Random Choice: Gem colors are randomly selected from a curated set of options.
True Random: Gem colors are completely random. This probably won't look great.
"""
display_name = "Gem Color"
default = RandomizeGemColorOptions.DEFAULT
option_default = RandomizeGemColorOptions.DEFAULT
option_shuffle = RandomizeGemColorOptions.SHUFFLE
option_random_choice = RandomizeGemColorOptions.RANDOM
option_true_random = RandomizeGemColorOptions.TRUE_RANDOM
@dataclass
class Spyro2Option(PerGameCommonOptions):
goal: GoalOption
guaranteed_items: GuaranteedItemsOption
# TODO: Enable.
# orb_hunt_requirement: OrbHuntRequirement
ripto_door_orbs: RiptoDoorOrbs
# TODO: Handle edge cases and enable.
# available_orbs: TotalAvailableOrbs
enable_open_world: EnableOpenWorld
open_world_ability_and_warp_unlocks: StartWithAbilitiesAndWarps
level_lock_options: LevelLockOption
level_unlocks: StartingLevelCount
enable_25_pct_gem_checks: Enable25PctGemChecksOption
enable_50_pct_gem_checks: Enable50PctGemChecksOption
enable_75_pct_gem_checks: Enable75PctGemChecksOption
enable_gem_checks: EnableGemChecksOption
enable_total_gem_checks: EnableTotalGemChecksOption
max_total_gem_checks: MaxTotalGemCheckOption
enable_skillpoint_checks: EnableSkillpointChecksOption
enable_life_bottle_checks: EnableLifeBottleChecksOption
enable_spirit_particle_checks: EnableSpiritParticleChecksOption
enable_gemsanity: EnableGemsanityOption
moneybags_settings: MoneybagsSettings
death_link: EnableDeathLink
enable_filler_extra_lives: EnableFillerExtraLives
enable_destructive_spyro_filler: EnableFillerDestructiveSpyro
enable_filler_color_change: EnableFillerColorChange
enable_filler_big_head_mode: EnableFillerBigHeadMode
enable_filler_heal_sparx: EnableFillerHealSparx
trap_filler_percent: TrapFillerPercent
enable_trap_damage_sparx: EnableTrapDamageSparx
enable_trap_sparxless: EnableTrapSparxless
enable_trap_invisibility: EnableTrapInvisible
enable_progressive_sparx_health: EnableProgressiveSparxHealth
enable_progressive_sparx_logic: ProgressiveSparxHealthLogic
double_jump_ability: DoubleJumpAbility
permanent_fireball_ability: FireballAbility
colossus_starting_goals: ColossusStartingGoals
idol_easy_fish: IdolEasyFish
hurricos_easy_lightning_orbs: HurricosEasyLightningOrbs
breeze_required_gears: BreezeRequiredGears
scorch_bombo_settings: ScorchBomboSettings
fracture_require_headbash: FractureRequireHeadbash
fracture_easy_earthshapers: FractureEasyEarthshapers
magma_spyro_starting_popcorn: MagmaSpyroStartingPopcorn
magma_hunter_starting_popcorn: MagmaHunterStartingPopcorn
shady_require_headbash: ShadyRequireHeadbash
easy_gulp: EasyGulp
portal_gem_collection_color: PortalAndGemCollectionColor
gem_color: GemColor
# Group logic/trick options together, especially for the local WebHost.
spyro_options_groups = [
OptionGroup(
"Difficulty",
[
ColossusStartingGoals,
IdolEasyFish,
HurricosEasyLightningOrbs,
BreezeRequiredGears,
ScorchBomboSettings,
FractureRequireHeadbash,
FractureEasyEarthshapers,
MagmaSpyroStartingPopcorn,
MagmaHunterStartingPopcorn,
ShadyRequireHeadbash,
EasyGulp
],
True
),
OptionGroup(
"Cosmetics",
[
PortalAndGemCollectionColor,
GemColor
],
True
),
]