start moving some alttp options over to the new system
This commit is contained in:
parent
622f8f8158
commit
5ea03c71c0
|
@ -113,8 +113,6 @@ class MultiWorld():
|
|||
set_player_attr('bush_shuffle', False)
|
||||
set_player_attr('beemizer', 0)
|
||||
set_player_attr('escape_assist', [])
|
||||
set_player_attr('crystals_needed_for_ganon', 7)
|
||||
set_player_attr('crystals_needed_for_gt', 7)
|
||||
set_player_attr('open_pyramid', False)
|
||||
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
|
||||
set_player_attr('treasure_hunt_count', 0)
|
||||
|
@ -131,7 +129,6 @@ class MultiWorld():
|
|||
set_player_attr('triforce_pieces_available', 30)
|
||||
set_player_attr('triforce_pieces_required', 20)
|
||||
set_player_attr('shop_shuffle', 'off')
|
||||
set_player_attr('shop_shuffle_slots', 0)
|
||||
set_player_attr('shuffle_prizes', "g")
|
||||
set_player_attr('sprite_pool', [])
|
||||
set_player_attr('dark_room_logic', "lamp")
|
||||
|
@ -141,9 +138,6 @@ class MultiWorld():
|
|||
set_player_attr('plando_connections', [])
|
||||
set_player_attr('game', "A Link to the Past")
|
||||
set_player_attr('completion_condition', lambda state: True)
|
||||
import Options
|
||||
for hk_option in Options.hollow_knight_options:
|
||||
set_player_attr(hk_option, False)
|
||||
self.custom_data = {}
|
||||
for player in range(1, players+1):
|
||||
self.custom_data[player] = {}
|
||||
|
@ -1448,7 +1442,7 @@ class Spoiler(object):
|
|||
'triforce_pieces_available': self.world.triforce_pieces_available,
|
||||
'triforce_pieces_required': self.world.triforce_pieces_required,
|
||||
'shop_shuffle': self.world.shop_shuffle,
|
||||
'shop_shuffle_slots': self.world.shop_shuffle_slots,
|
||||
'shop_item_slots': self.world.shop_item_slots,
|
||||
'shuffle_prizes': self.world.shuffle_prizes,
|
||||
'sprite_pool': self.world.sprite_pool,
|
||||
'restrict_dungeon_item_on_boss': self.world.restrict_dungeon_item_on_boss,
|
||||
|
@ -1565,8 +1559,8 @@ class Spoiler(object):
|
|||
"f" in self.metadata["shop_shuffle"][player]))
|
||||
outfile.write('Custom Potion Shop: %s\n' %
|
||||
bool_to_text("w" in self.metadata["shop_shuffle"][player]))
|
||||
outfile.write('Shop Slots: %s\n' %
|
||||
self.metadata["shop_shuffle_slots"][player])
|
||||
outfile.write('Shop Item Slots: %s\n' %
|
||||
self.metadata["shop_item_slots"][player])
|
||||
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
|
||||
outfile.write(
|
||||
'Enemy shuffle: %s\n' % bool_to_text(self.metadata['enemy_shuffle'][player]))
|
||||
|
|
2
Gui.py
2
Gui.py
|
@ -468,7 +468,7 @@ def guiMain(args=None):
|
|||
if shopWitchShuffleVar.get():
|
||||
guiargs.shop_shuffle += "w"
|
||||
if shopPoolShuffleVar.get():
|
||||
guiargs.shop_shuffle_slots = 30
|
||||
guiargs.shop_item_slots = 30
|
||||
guiargs.shuffle_prizes = {"none": "",
|
||||
"bonk": "b",
|
||||
"general": "g",
|
||||
|
|
13
Main.py
13
Main.py
|
@ -94,8 +94,6 @@ def main(args, seed=None):
|
|||
world.compassshuffle = args.compassshuffle.copy()
|
||||
world.keyshuffle = args.keyshuffle.copy()
|
||||
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
||||
world.crystals_needed_for_ganon = args.crystals_ganon.copy()
|
||||
world.crystals_needed_for_gt = args.crystals_gt.copy()
|
||||
world.open_pyramid = args.open_pyramid.copy()
|
||||
world.boss_shuffle = args.shufflebosses.copy()
|
||||
world.enemy_shuffle = args.enemy_shuffle.copy()
|
||||
|
@ -117,7 +115,6 @@ def main(args, seed=None):
|
|||
world.triforce_pieces_available = args.triforce_pieces_available.copy()
|
||||
world.triforce_pieces_required = args.triforce_pieces_required.copy()
|
||||
world.shop_shuffle = args.shop_shuffle.copy()
|
||||
world.shop_shuffle_slots = args.shop_shuffle_slots.copy()
|
||||
world.progression_balancing = args.progression_balancing.copy()
|
||||
world.shuffle_prizes = args.shuffle_prizes.copy()
|
||||
world.sprite_pool = args.sprite_pool.copy()
|
||||
|
@ -130,12 +127,10 @@ def main(args, seed=None):
|
|||
world.required_medallions = args.required_medallions.copy()
|
||||
world.game = args.game.copy()
|
||||
import Options
|
||||
for hk_option in Options.hollow_knight_options:
|
||||
setattr(world, hk_option, getattr(args, hk_option, {}))
|
||||
for factorio_option in Options.factorio_options:
|
||||
setattr(world, factorio_option, getattr(args, factorio_option, {}))
|
||||
for minecraft_option in Options.minecraft_options:
|
||||
setattr(world, minecraft_option, getattr(args, minecraft_option, {}))
|
||||
for option_set in Options.option_sets:
|
||||
for option in option_set:
|
||||
setattr(world, option, getattr(args, option, {}))
|
||||
|
||||
world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option.
|
||||
|
||||
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)}
|
||||
|
|
14
Mystery.py
14
Mystery.py
|
@ -581,6 +581,12 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
|||
|
||||
|
||||
def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
||||
for option_name, option in Options.alttp_options.items():
|
||||
if option_name in weights:
|
||||
setattr(ret, option_name, option.from_any(get_choice(option_name, weights)))
|
||||
else:
|
||||
setattr(ret, option_name, option(option.default))
|
||||
|
||||
glitches_required = get_choice('glitches_required', weights)
|
||||
if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'minor_glitches']:
|
||||
logging.warning("Only NMG, OWG and No Logic supported")
|
||||
|
@ -632,12 +638,9 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
|||
# fast ganon + ganon at hole
|
||||
ret.open_pyramid = get_choice('open_pyramid', weights, 'goal')
|
||||
|
||||
ret.crystals_gt = Options.Crystals.from_any(get_choice('tower_open', weights)).value
|
||||
ret.crystals_ganon = Options.Crystals.from_any(get_choice('ganon_open', weights)).value
|
||||
|
||||
extra_pieces = get_choice('triforce_pieces_mode', weights, 'available')
|
||||
|
||||
ret.triforce_pieces_required = Options.TriforcePieces.from_any(get_choice('triforce_pieces_required', weights)).value
|
||||
ret.triforce_pieces_required = Options.TriforcePieces.from_any(get_choice('triforce_pieces_required', weights))
|
||||
|
||||
# sum a percentage to required
|
||||
if extra_pieces == 'percentage':
|
||||
|
@ -645,7 +648,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
|||
ret.triforce_pieces_available = int(round(ret.triforce_pieces_required * percentage, 0))
|
||||
# vanilla mode (specify how many pieces are)
|
||||
elif extra_pieces == 'available':
|
||||
ret.triforce_pieces_available = Options.TriforcePieces.from_any(get_choice('triforce_pieces_available', weights)).value
|
||||
ret.triforce_pieces_available = Options.TriforcePieces.from_any(get_choice('triforce_pieces_available', weights))
|
||||
# required pieces + fixed extra
|
||||
elif extra_pieces == 'extra':
|
||||
extra_pieces = max(0, int(get_choice('triforce_pieces_extra', weights, 10)))
|
||||
|
@ -653,7 +656,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
|||
|
||||
# change minimum to required pieces to avoid problems
|
||||
ret.triforce_pieces_available = min(max(ret.triforce_pieces_required, int(ret.triforce_pieces_available)), 90)
|
||||
ret.shop_shuffle_slots = Options.TriforcePieces.from_any(get_choice('shop_shuffle_slots', weights)).value
|
||||
|
||||
ret.shop_shuffle = get_choice('shop_shuffle', weights, '')
|
||||
if not ret.shop_shuffle:
|
||||
|
|
37
Options.py
37
Options.py
|
@ -8,8 +8,9 @@ class AssembleOptions(type):
|
|||
options = attrs["options"] = {}
|
||||
name_lookup = attrs["name_lookup"] = {}
|
||||
for base in bases:
|
||||
options.update(base.options)
|
||||
name_lookup.update(name_lookup)
|
||||
if hasattr(base, "options"):
|
||||
options.update(base.options)
|
||||
name_lookup.update(name_lookup)
|
||||
new_options = {name[7:].lower(): option_id for name, option_id in attrs.items() if
|
||||
name.startswith("option_")}
|
||||
attrs["name_lookup"].update({option_id: name for name, option_id in new_options.items()})
|
||||
|
@ -110,7 +111,7 @@ class Choice(Option):
|
|||
return cls.from_text(str(data))
|
||||
|
||||
|
||||
class Range(Option):
|
||||
class Range(Option, int):
|
||||
range_start = 0
|
||||
range_end = 1
|
||||
|
||||
|
@ -119,7 +120,7 @@ class Range(Option):
|
|||
raise Exception(f"{value} is lower than minimum {self.range_start} for option {self.__class__.__name__}")
|
||||
elif value > self.range_end:
|
||||
raise Exception(f"{value} is higher than maximum {self.range_end} for option {self.__class__.__name__}")
|
||||
self.value: int = value
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def from_text(cls, text: str) -> Range:
|
||||
|
@ -139,6 +140,9 @@ class Range(Option):
|
|||
return cls(data)
|
||||
return cls.from_text(str(data))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
|
||||
class OptionNameSet(Option):
|
||||
default = frozenset()
|
||||
|
@ -210,12 +214,20 @@ class Crystals(Range):
|
|||
range_end = 7
|
||||
|
||||
|
||||
class CrystalsTower(Crystals):
|
||||
pass
|
||||
|
||||
|
||||
class CrystalsGanon(Crystals):
|
||||
default = 7
|
||||
|
||||
|
||||
class TriforcePieces(Range):
|
||||
range_start = 1
|
||||
range_end = 90
|
||||
|
||||
|
||||
class ShopShuffleSlots(Range):
|
||||
class ShopItemSlots(Range):
|
||||
range_start = 0
|
||||
range_end = 30
|
||||
|
||||
|
@ -240,6 +252,12 @@ class Enemies(Choice):
|
|||
option_chaos = 2
|
||||
|
||||
|
||||
alttp_options: typing.Dict[str, type(Option)] = {
|
||||
"crystals_needed_for_gt": CrystalsTower,
|
||||
"crystals_needed_for_ganon": CrystalsGanon,
|
||||
"shop_item_slots": ShopItemSlots,
|
||||
}
|
||||
|
||||
mapshuffle = Toggle
|
||||
compassshuffle = Toggle
|
||||
keyshuffle = Toggle
|
||||
|
@ -268,7 +286,7 @@ RandomizeLoreTablets = Toggle
|
|||
RandomizeLifebloodCocoons = Toggle
|
||||
RandomizeFlames = Toggle
|
||||
|
||||
hollow_knight_randomize_options: typing.Dict[str, Option] = {
|
||||
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {
|
||||
"RandomizeDreamers": RandomizeDreamers,
|
||||
"RandomizeSkills": RandomizeSkills,
|
||||
"RandomizeCharms": RandomizeCharms,
|
||||
|
@ -409,6 +427,13 @@ minecraft_options: typing.Dict[str, type(Option)] = {
|
|||
"shuffle_structures": Toggle
|
||||
}
|
||||
|
||||
option_sets = (
|
||||
minecraft_options,
|
||||
factorio_options,
|
||||
alttp_options,
|
||||
hollow_knight_options
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ triforce_pieces_required: # Set to how many out of X triforce pieces you need to
|
|||
30: 0
|
||||
40: 0
|
||||
50: 0
|
||||
tower_open: # Crystals required to open GT
|
||||
crystals_needed_for_gt: # Crystals required to open GT
|
||||
0: 0
|
||||
1: 0
|
||||
2: 0
|
||||
|
@ -232,7 +232,7 @@ tower_open: # Crystals required to open GT
|
|||
random: 0
|
||||
random-low: 50 # any valid number, weighted towards the lower end
|
||||
random-high: 0
|
||||
ganon_open: # Crystals required to hurt Ganon
|
||||
crystals_needed_for_ganon: # Crystals required to hurt Ganon
|
||||
0: 0
|
||||
1: 0
|
||||
2: 0
|
||||
|
@ -317,7 +317,7 @@ beemizer: # Remove items from the global item pool and replace them with single
|
|||
4: 0 # 100% of rupees, bombs and arrows are replaced with bees, of which 90% are traps and 10% single bees
|
||||
5: 0 # 100% of rupees, bombs and arrows are replaced with bees, of which 100% are traps and 0% single bees
|
||||
### Shop Settings ###
|
||||
shop_shuffle_slots: # Maximum amount of shop slots to be filled with regular item pool items (such as Moon Pearl)
|
||||
shop_item_slots: # Maximum amount of shop slots to be filled with regular item pool items (such as Moon Pearl)
|
||||
0: 50
|
||||
5: 0
|
||||
15: 0
|
||||
|
|
|
@ -196,22 +196,6 @@ def parse_arguments(argv, no_defaults=False):
|
|||
The dungeon variants only mix up dungeons and keep the rest of
|
||||
the overworld vanilla.
|
||||
''')
|
||||
parser.add_argument('--crystals_ganon', default=defval('7'), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'],
|
||||
help='''\
|
||||
How many crystals are needed to defeat ganon. Any other
|
||||
requirements for ganon for the selected goal still apply.
|
||||
This setting does not apply when the all dungeons goal is
|
||||
selected. (default: %(default)s)
|
||||
Random: Picks a random value between 0 and 7 (inclusive).
|
||||
0-7: Number of crystals needed
|
||||
''')
|
||||
parser.add_argument('--crystals_gt', default=defval('7'), const='7', nargs='?',
|
||||
choices=['0', '1', '2', '3', '4', '5', '6', '7'],
|
||||
help='''\
|
||||
How many crystals are needed to open GT. For inverted mode
|
||||
this applies to the castle tower door instead. (default: %(default)s)
|
||||
0-7: Number of crystals needed
|
||||
''')
|
||||
parser.add_argument('--open_pyramid', default=defval('auto'), help='''\
|
||||
Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it.
|
||||
Depending on goal, you might still need to beat Agahnim 2 in order to beat ganon.
|
||||
|
@ -337,11 +321,6 @@ def parse_arguments(argv, no_defaults=False):
|
|||
u: shuffle capacity upgrades into the item pool
|
||||
w: consider witch's hut like any other shop and shuffle/randomize it too
|
||||
''')
|
||||
parser.add_argument('--shop_shuffle_slots', default=defval(0),
|
||||
type=lambda value: min(max(int(value), 1), 96),
|
||||
help='''
|
||||
Maximum amount of shop slots able to be filled by items from the item pool.
|
||||
''')
|
||||
parser.add_argument('--shuffle_prizes', default=defval('g'), choices=['', 'g', 'b', 'gb'])
|
||||
parser.add_argument('--sprite_pool', help='''\
|
||||
Specifies a colon separated list of sprites used for random/randomonevent. If not specified, the full sprite pool is used.''')
|
||||
|
@ -397,14 +376,14 @@ def parse_arguments(argv, no_defaults=False):
|
|||
playerargs = parse_arguments(shlex.split(getattr(ret, f"p{player}")), True)
|
||||
|
||||
for name in ['logic', 'mode', 'swordless', 'goal', 'difficulty', 'item_functionality',
|
||||
'shuffle', 'crystals_ganon', 'crystals_gt', 'open_pyramid', 'timer',
|
||||
'shuffle', 'open_pyramid', 'timer',
|
||||
'countdown_start_time', 'red_clock_time', 'blue_clock_time', 'green_clock_time',
|
||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
||||
'local_items', 'non_local_items', 'retro', 'accessibility', 'hints', 'beemizer',
|
||||
'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor',
|
||||
'heartbeep', "progression_balancing", "triforce_pieces_available",
|
||||
"triforce_pieces_required", "shop_shuffle", "shop_shuffle_slots",
|
||||
"triforce_pieces_required", "shop_shuffle",
|
||||
"required_medallions", "start_hints",
|
||||
"plando_items", "plando_texts", "plando_connections", "er_seeds",
|
||||
'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves',
|
||||
|
|
|
@ -885,7 +885,7 @@ def patch_rom(world, rom, player, team, enemized):
|
|||
credits_total = 216
|
||||
if world.retro[player]: # Old man cave and Take any caves will count towards collection rate.
|
||||
credits_total += 5
|
||||
if world.shop_shuffle_slots[player]: # Potion shop only counts towards collection rate if included in the shuffle.
|
||||
if world.shop_item_slots[player]: # Potion shop only counts towards collection rate if included in the shuffle.
|
||||
credits_total += 30 if 'w' in world.shop_shuffle[player] else 27
|
||||
|
||||
rom.write_byte(0x187010, credits_total) # dynamic credits
|
||||
|
@ -1705,7 +1705,7 @@ def write_custom_shops(rom, world, player):
|
|||
slot = 0 if shop.type == ShopType.TakeAny else index
|
||||
if item is None:
|
||||
break
|
||||
if world.shop_shuffle_slots[player] or shop.type == ShopType.TakeAny:
|
||||
if world.shop_item_slots[player] or shop.type == ShopType.TakeAny:
|
||||
count_shop = (shop.region.name != 'Potion Shop' or 'w' in world.shop_shuffle[player]) and \
|
||||
shop.region.name != 'Capacity Upgrade'
|
||||
rom.write_byte(0x186560 + shop.sram_offset + slot, 1 if count_shop else 0)
|
||||
|
|
|
@ -243,7 +243,7 @@ def create_shops(world, player: int):
|
|||
else:
|
||||
dynamic_shop_slots = total_dynamic_shop_slots
|
||||
|
||||
num_slots = min(dynamic_shop_slots, max(0, int(world.shop_shuffle_slots[player]))) # 0 to 30
|
||||
num_slots = min(dynamic_shop_slots, world.shop_item_slots[player])
|
||||
single_purchase_slots: List[bool] = [True] * num_slots + [False] * (dynamic_shop_slots - num_slots)
|
||||
world.random.shuffle(single_purchase_slots)
|
||||
|
||||
|
|
Loading…
Reference in New Issue