start moving some alttp options over to the new system

This commit is contained in:
Fabian Dill 2021-06-08 21:58:11 +02:00
parent 622f8f8158
commit 5ea03c71c0
9 changed files with 55 additions and 60 deletions

View File

@ -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
View File

@ -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
View File

@ -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)}

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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)

View File

@ -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)