forked from mirror/Archipelago
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
249 lines
7.6 KiB
Python
249 lines
7.6 KiB
Python
from __future__ import annotations
|
|
|
|
from Options import (PerGameCommonOptions, Range, Choice, OptionSet, OptionDict, DeathLinkMixin, Toggle,
|
|
OptionCounter, Visibility, TextChoice)
|
|
from dataclasses import dataclass
|
|
from schema import Schema, And, Use, Optional, Or
|
|
from typing import Any
|
|
import random
|
|
from .aesthetics import palette_addresses
|
|
|
|
|
|
subgame_mapping = {
|
|
0: "Spring Breeze",
|
|
1: "Dyna Blade",
|
|
2: "Gourmet Race",
|
|
3: "The Great Cave Offensive",
|
|
4: "Revenge of Meta Knight",
|
|
5: "Milky Way Wishes",
|
|
6: "The Arena"
|
|
}
|
|
|
|
|
|
class RequiredSubgameCompletions(Range):
|
|
"""
|
|
How many subgames must be completed for the game to be considered complete.
|
|
"""
|
|
display_name = "Required Subgame Completions"
|
|
range_start = 1
|
|
range_end = 7
|
|
default = 6
|
|
|
|
|
|
class RequiredSubgames(OptionSet):
|
|
"""
|
|
Which subgames are required to be completed for the game to be considered complete.
|
|
"""
|
|
display_name = "Required Subgames"
|
|
valid_keys = frozenset(subgame_mapping.values())
|
|
default = {"Milky Way Wishes"}
|
|
|
|
|
|
class StartingSubgame(Choice):
|
|
"""
|
|
The subgame that will be unlocked by default.
|
|
"""
|
|
display_name = "Starting Subgame"
|
|
option_spring_breeze = 0
|
|
option_dyna_blade = 1
|
|
option_gourmet_race = 2
|
|
option_the_great_cave_offensive = 3
|
|
option_revenge_of_meta_knight = 4
|
|
option_milky_way_wishes = 5
|
|
option_the_arena = 6
|
|
default = 0
|
|
|
|
|
|
class IncludedSubgames(OptionSet):
|
|
"""
|
|
Which subgames should be included as locations.
|
|
"""
|
|
display_name = "Included Subgames"
|
|
valid_keys = frozenset(subgame_mapping.values())
|
|
default = sorted(valid_keys)
|
|
|
|
|
|
class TheGreatCaveOffensiveRequiredGold(Range):
|
|
"""
|
|
Required amount of gold that is needed in order to complete The Great Cave Offensive
|
|
"""
|
|
display_name = "The Great Cave Offensive Required Gold"
|
|
range_start = 2500000
|
|
range_end = 9999990
|
|
default = range_end
|
|
|
|
|
|
class TheGreatCaveOffensiveGoldThresholds(OptionCounter):
|
|
"""
|
|
What percent of the required gold is required before allowing access to
|
|
Crystal/Old Tower/Garden areas in The Great Cave Offensive
|
|
"""
|
|
display_name = "The Great Cave Offensive Gold Thresholds"
|
|
valid_keys = ("Crystal", "Old Tower", "Garden")
|
|
schema = Schema({
|
|
area: And(int, lambda i: 0 <= i <= 100, error="Value must be between 0 and 100")
|
|
for area in ["Crystal", "Old Tower", "Garden"]
|
|
})
|
|
min = 0
|
|
max = 100
|
|
default = {
|
|
"Crystal": 25,
|
|
"Old Tower": 50,
|
|
"Garden": 75
|
|
}
|
|
|
|
|
|
class TheGreatCaveOffensiveExcessGold(Range):
|
|
"""
|
|
How much of the excess gold should be kept within the multiworld.
|
|
"""
|
|
display_name = "The Great Cave Offensive Excess Gold"
|
|
range_start = 0
|
|
range_end = 100
|
|
|
|
|
|
class MilkyWayWishesMode(Choice):
|
|
"""
|
|
Determines how Marx is unlocked in Milky Way Wishes.
|
|
Local: Marx is unlocked after completing the 7 main planets
|
|
(Floria, Aqualiss, Skyhigh, Hotbeat, Cavios, Mecheye, Halfmoon)
|
|
Multiworld: Marx is unlocked after receiving 7 Rainbow Stars scattered across the multiworld
|
|
"""
|
|
display_name = "Milky Way Wishes Mode"
|
|
option_local = 0
|
|
option_multiworld = 1
|
|
default = 0
|
|
|
|
|
|
class Consumables(OptionSet):
|
|
"""
|
|
Adds the specified consumables to the location pool. Options are Maxim Tomato, 1-Up,
|
|
and Invincibility Candy.
|
|
"""
|
|
display_name = "Consumable Checks"
|
|
valid_keys = {"Maxim Tomato", "1-Up", "Invincibility Candy", "Arena Maxim Tomato"}
|
|
|
|
default = frozenset()
|
|
|
|
|
|
class Essences(Toggle):
|
|
"""
|
|
Adds Copy Essence pedestals to the location pool.
|
|
"""
|
|
display_name = "Essence-sanity"
|
|
|
|
|
|
class KirbyFlavorPreset(TextChoice, OptionDict):
|
|
"""
|
|
The color of Kirby, from a list of presets.
|
|
Can also accept a valid preset defined in `kirby_flavors`, 1/2 direct hex colors (#RRGGBB|#RRGGBB), a single hex
|
|
color and a method to derive a second color (#RRGGBB|complement/analogous/intensify), or a dict taking each Copy
|
|
Ability (and Kirby) as a key and one of the former options as the value.
|
|
|
|
kirby_flavor_preset:
|
|
kirby: #99EEDD|analogous
|
|
Copy: bubblegum
|
|
Cutter: #AAEEDD
|
|
"""
|
|
display_name = "Kirby Flavor"
|
|
valid_keys = sorted(palette_addresses.keys())
|
|
# primary validation, perform secondary validation in generate_early
|
|
schema = Schema(Or(str, int, {
|
|
Optional(And(str, Use(str.title), lambda s: s in palette_addresses)): str
|
|
}))
|
|
default = 0
|
|
option_default = 0
|
|
option_bubblegum = 1
|
|
option_cherry = 2
|
|
option_blueberry = 3
|
|
option_lemon = 4
|
|
option_kiwi = 5
|
|
option_grape = 6
|
|
option_chocolate = 7
|
|
option_marshmallow = 8
|
|
option_licorice = 9
|
|
option_watermelon = 10
|
|
option_orange = 11
|
|
option_lime = 12
|
|
option_lavender = 13
|
|
option_miku = 14
|
|
|
|
def __init__(self, value: int | str | dict[str, Any]) -> None:
|
|
self.value: int | str | dict[str, Any] = value
|
|
|
|
@classmethod
|
|
def parse_weighted_option(cls, value: dict[str, int]) -> str:
|
|
return random.choices(list(value.keys()), weights=list(value.values()), k=1)[0]
|
|
|
|
@classmethod
|
|
def from_any(cls, value: Any) -> TextChoice | OptionDict:
|
|
if isinstance(value, dict):
|
|
if any(key not in cls.valid_keys for key in value.keys()):
|
|
# We have to assume that this is a weighted option
|
|
val = cls.parse_weighted_option(value)
|
|
return super().from_any(val)
|
|
for key in value.keys():
|
|
if value[key].lower() == "random":
|
|
value[key] = random.choice(list([key for key, val in cls.options.items() if val >= 0]))
|
|
return cls(value)
|
|
else:
|
|
return super().from_any(value)
|
|
|
|
def verify_keys(self) -> None:
|
|
if isinstance(self.value, dict):
|
|
super().verify_keys()
|
|
|
|
@classmethod
|
|
def get_option_name(cls, value: int | dict[str, str]) -> str:
|
|
if isinstance(value, int):
|
|
return cls.name_lookup[value].replace("_", " ").title()
|
|
elif isinstance(value, str):
|
|
return value
|
|
else:
|
|
return ", ".join(f"{key}: {v}" for key, v in value.items())
|
|
|
|
|
|
class KirbyFlavor(OptionDict):
|
|
"""
|
|
Define custom colors for Kirby. To use a custom color, set the preset to the name defined and then define a dict of keys from "1" to
|
|
"8", with their values being an HTML hex color.
|
|
"""
|
|
display_name = "Custom Kirby Flavors"
|
|
schema = Schema(
|
|
{
|
|
Optional(str): {
|
|
f"{x}": And(str, Use(str.lower), lambda x: all(y in "abcdef0123456789#" for y in x))
|
|
for x in range(1, 9)
|
|
}
|
|
}
|
|
)
|
|
default = {
|
|
"default_kirby": {
|
|
"1": "F8F8F8",
|
|
"2": "F0E0E8",
|
|
"3": "E8D0D0",
|
|
"4": "F0A0B8",
|
|
"5": "C8A0A8",
|
|
"6": "A85048",
|
|
"7": "E02018",
|
|
"8": "E85048",
|
|
}
|
|
}
|
|
visibility = Visibility.template | Visibility.spoiler # never supported on guis
|
|
|
|
|
|
@dataclass
|
|
class KSSOptions(PerGameCommonOptions, DeathLinkMixin):
|
|
required_subgame_completions: RequiredSubgameCompletions
|
|
required_subgames: RequiredSubgames
|
|
starting_subgame: StartingSubgame
|
|
included_subgames: IncludedSubgames
|
|
consumables: Consumables
|
|
essences: Essences
|
|
the_great_cave_offensive_required_gold: TheGreatCaveOffensiveRequiredGold
|
|
the_great_cave_offensive_excess_gold: TheGreatCaveOffensiveExcessGold
|
|
the_great_cave_offensive_gold_thresholds: TheGreatCaveOffensiveGoldThresholds
|
|
milky_way_wishes_mode: MilkyWayWishesMode
|
|
kirby_flavor_preset: KirbyFlavorPreset
|
|
kirby_flavors: KirbyFlavor
|
|
|