Add ganon triforce hunt (#117)
* Add ganon triforce hunt * Add self to license * Correction of help message for Local Ganon Triforce Hunt. * if 'triforcehunt in world.goal[player]:
This commit is contained in:
parent
46038830c3
commit
545bb8023c
|
@ -518,6 +518,9 @@ class CollectionState(object):
|
||||||
def item_count(self, item, player: int) -> int:
|
def item_count(self, item, player: int) -> int:
|
||||||
return self.prog_items[item, player]
|
return self.prog_items[item, player]
|
||||||
|
|
||||||
|
def has_triforce_pieces(self, count: int, player: int) -> bool:
|
||||||
|
return self.item_count('Triforce Piece', player) + self.item_count('Power Star', player) >= count
|
||||||
|
|
||||||
def has_crystals(self, count: int, player: int) -> bool:
|
def has_crystals(self, count: int, player: int) -> bool:
|
||||||
crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
|
crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
|
||||||
return len([crystal for crystal in crystals if self.has(crystal, player)]) >= count
|
return len([crystal for crystal in crystals if self.has(crystal, player)]) >= count
|
||||||
|
|
|
@ -67,7 +67,7 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
Vanilla: Swords are in vanilla locations.
|
Vanilla: Swords are in vanilla locations.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--goal', default=defval('ganon'), const='ganon', nargs='?',
|
parser.add_argument('--goal', default=defval('ganon'), const='ganon', nargs='?',
|
||||||
choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt', 'crystals'],
|
choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt', 'ganontriforcehunt', 'localganontriforcehunt', 'crystals'],
|
||||||
help='''\
|
help='''\
|
||||||
Select completion goal. (default: %(default)s)
|
Select completion goal. (default: %(default)s)
|
||||||
Ganon: Collect all crystals, beat Agahnim 2 then
|
Ganon: Collect all crystals, beat Agahnim 2 then
|
||||||
|
@ -79,7 +79,11 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
||||||
20 of them to beat the game.
|
20 of them to beat the game.
|
||||||
Local Triforce Hunt: Places 30 Triforce Pieces in your world, collect
|
Local Triforce Hunt: Places 30 Triforce Pieces in your world, collect
|
||||||
20 of them to beat the game.
|
20 of them to beat the game.
|
||||||
|
Ganon Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
||||||
|
20 of them, then defeat Ganon.
|
||||||
|
Local Ganon Triforce Hunt: Places 30 Triforce Pieces in your world,
|
||||||
|
collect 20 of them, then defeat Ganon.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--triforce_pieces_available', default=defval(30),
|
parser.add_argument('--triforce_pieces_available', default=defval(30),
|
||||||
type=lambda value: min(max(int(value), 1), 90),
|
type=lambda value: min(max(int(value), 1), 90),
|
||||||
|
|
4
Fill.py
4
Fill.py
|
@ -244,8 +244,8 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
gftower_trash_count = (
|
gftower_trash_count = (
|
||||||
random.randint(15, 50) if world.goal[player] in {'triforcehunt', 'localtriforcehunt'} else random.randint(0,
|
random.randint(15, 50) if 'triforcehunt' in world.goal[player]
|
||||||
15))
|
else random.randint(0, 15))
|
||||||
|
|
||||||
gtower_locations = [location for location in fill_locations if
|
gtower_locations = [location for location in fill_locations if
|
||||||
'Ganons Tower' in location.name and location.player == player]
|
'Ganons Tower' in location.name and location.player == player]
|
||||||
|
|
2
Gui.py
2
Gui.py
|
@ -240,7 +240,7 @@ def guiMain(args=None):
|
||||||
goalVar = StringVar()
|
goalVar = StringVar()
|
||||||
goalVar.set('ganon')
|
goalVar.set('ganon')
|
||||||
goalOptionMenu = OptionMenu(goalFrame, goalVar, 'ganon', 'pedestal', 'dungeons', 'triforcehunt',
|
goalOptionMenu = OptionMenu(goalFrame, goalVar, 'ganon', 'pedestal', 'dungeons', 'triforcehunt',
|
||||||
'localtriforcehunt', 'crystals')
|
'localtriforcehunt', 'ganontriforcehunt', 'localganontriforcehunt', 'crystals')
|
||||||
goalOptionMenu.pack(side=RIGHT)
|
goalOptionMenu.pack(side=RIGHT)
|
||||||
goalLabel = Label(goalFrame, text='Game goal')
|
goalLabel = Label(goalFrame, text='Game goal')
|
||||||
goalLabel.pack(side=LEFT)
|
goalLabel.pack(side=LEFT)
|
||||||
|
|
14
ItemList.py
14
ItemList.py
|
@ -124,7 +124,7 @@ difficulties = {
|
||||||
def generate_itempool(world, player):
|
def generate_itempool(world, player):
|
||||||
if world.difficulty[player] not in ['normal', 'hard', 'expert']:
|
if world.difficulty[player] not in ['normal', 'hard', 'expert']:
|
||||||
raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
|
raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
|
||||||
if world.goal[player] not in {'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt', 'crystals'}:
|
if world.goal[player] not in {'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt', 'ganontriforcehunt', 'localganontriforcehunt', 'crystals'}:
|
||||||
raise NotImplementedError(f"Goal {world.goal[player]}")
|
raise NotImplementedError(f"Goal {world.goal[player]}")
|
||||||
if world.mode[player] not in {'open', 'standard', 'inverted'}:
|
if world.mode[player] not in {'open', 'standard', 'inverted'}:
|
||||||
raise NotImplementedError(f"Mode {world.mode[player]}")
|
raise NotImplementedError(f"Mode {world.mode[player]}")
|
||||||
|
@ -142,9 +142,8 @@ def generate_itempool(world, player):
|
||||||
region = world.get_region('Light World', player)
|
region = world.get_region('Light World', player)
|
||||||
|
|
||||||
loc = Location(player, "Murahdahla", parent=region)
|
loc = Location(player, "Murahdahla", parent=region)
|
||||||
loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star',
|
loc.access_rule = lambda state: state.has_triforce_pieces(state.world.treasure_hunt_count[player], player)
|
||||||
player) >= \
|
|
||||||
state.world.treasure_hunt_count[player]
|
|
||||||
region.locations.append(loc)
|
region.locations.append(loc)
|
||||||
world.dynamic_locations.append(loc)
|
world.dynamic_locations.append(loc)
|
||||||
|
|
||||||
|
@ -261,7 +260,7 @@ def generate_itempool(world, player):
|
||||||
random.shuffle(nonprogressionitems)
|
random.shuffle(nonprogressionitems)
|
||||||
|
|
||||||
triforce_pieces = world.triforce_pieces_available[player]
|
triforce_pieces = world.triforce_pieces_available[player]
|
||||||
if world.goal[player] in {'triforcehunt', 'localtriforcehunt'} and triforce_pieces > 30:
|
if 'triforcehunt' in world.goal[player] and triforce_pieces > 30:
|
||||||
progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30)
|
progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30)
|
||||||
nonprogressionitems = nonprogressionitems[(triforce_pieces-30):]
|
nonprogressionitems = nonprogressionitems[(triforce_pieces-30):]
|
||||||
|
|
||||||
|
@ -510,7 +509,7 @@ def get_pool_core(world, player: int):
|
||||||
pool.extend(diff.timedohko)
|
pool.extend(diff.timedohko)
|
||||||
extraitems -= len(diff.timedohko)
|
extraitems -= len(diff.timedohko)
|
||||||
clock_mode = 'countdown-ohko'
|
clock_mode = 'countdown-ohko'
|
||||||
if goal in {'triforcehunt', 'localtriforcehunt'}:
|
if 'triforcehunt' in goal:
|
||||||
while len(diff.triforcehunt) > world.triforce_pieces_available[player]:
|
while len(diff.triforcehunt) > world.triforce_pieces_available[player]:
|
||||||
diff.triforcehunt.pop()
|
diff.triforcehunt.pop()
|
||||||
pool.extend(diff.triforcehunt)
|
pool.extend(diff.triforcehunt)
|
||||||
|
@ -643,8 +642,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||||
treasure_hunt_count = max(min(customitemarray[67], 99), 1) #To display, count must be between 1 and 99.
|
treasure_hunt_count = max(min(customitemarray[67], 99), 1) #To display, count must be between 1 and 99.
|
||||||
treasure_hunt_icon = 'Triforce Piece'
|
treasure_hunt_icon = 'Triforce Piece'
|
||||||
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
|
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
|
||||||
if (customitemarray[66] < treasure_hunt_count) and (goal in {'triforcehunt', 'localtriforcehunt'}) and (
|
if (customitemarray[66] < treasure_hunt_count) and ('triforcehunt' in goal) and (customitemarray[68] == 0):
|
||||||
customitemarray[68] == 0):
|
|
||||||
extrapieces = treasure_hunt_count - customitemarray[66]
|
extrapieces = treasure_hunt_count - customitemarray[66]
|
||||||
pool.extend(['Triforce Piece'] * extrapieces)
|
pool.extend(['Triforce Piece'] * extrapieces)
|
||||||
itemtotal = itemtotal + extrapieces
|
itemtotal = itemtotal + extrapieces
|
||||||
|
|
1
LICENSE
1
LICENSE
|
@ -2,6 +2,7 @@ MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 LLCoolDave
|
Copyright (c) 2017 LLCoolDave
|
||||||
Copyright (c) 2020 Berserker66
|
Copyright (c) 2020 Berserker66
|
||||||
|
Copyright (c) 2020 CaitSith2
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -288,9 +288,11 @@ def roll_settings(weights):
|
||||||
'pedestal': 'pedestal',
|
'pedestal': 'pedestal',
|
||||||
'triforce_hunt': 'triforcehunt',
|
'triforce_hunt': 'triforcehunt',
|
||||||
'triforce-hunt': 'triforcehunt', # deprecated, moving all goals to `_`
|
'triforce-hunt': 'triforcehunt', # deprecated, moving all goals to `_`
|
||||||
'local_triforce_hunt': 'localtriforcehunt'
|
'local_triforce_hunt': 'localtriforcehunt',
|
||||||
|
'ganon_triforce_hunt': 'ganontriforcehunt',
|
||||||
|
'local_ganon_triforce_hunt': 'localganontriforcehunt'
|
||||||
}[goal]
|
}[goal]
|
||||||
ret.openpyramid = goal == 'fast_ganon'
|
ret.openpyramid = goal in ['fast_ganon', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt']
|
||||||
|
|
||||||
ret.crystals_gt = get_choice('tower_open', weights)
|
ret.crystals_gt = get_choice('tower_open', weights)
|
||||||
|
|
||||||
|
|
8
Rom.py
8
Rom.py
|
@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses
|
||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = 'e7eee92d3a89283f591fdf7ac66a4ab7'
|
RANDOMIZERBASEHASH = 'a567da86e8bd499256da4bba2209a3fd'
|
||||||
|
|
||||||
class LocalRom(object):
|
class LocalRom(object):
|
||||||
|
|
||||||
|
@ -1084,6 +1084,8 @@ def patch_rom(world, rom, player, team, enemized):
|
||||||
|
|
||||||
if world.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt']:
|
if world.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt']:
|
||||||
rom.write_byte(0x18003E, 0x01) # make ganon invincible
|
rom.write_byte(0x18003E, 0x01) # make ganon invincible
|
||||||
|
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||||
|
rom.write_byte(0x18003E, 0x05) # make ganon invincible until 20 triforce pieces are collected
|
||||||
elif world.goal[player] in ['dungeons']:
|
elif world.goal[player] in ['dungeons']:
|
||||||
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
|
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
|
||||||
elif world.goal[player] in ['crystals']:
|
elif world.goal[player] in ['crystals']:
|
||||||
|
@ -1778,6 +1780,10 @@ def write_strings(rom, world, player, team):
|
||||||
tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)]
|
tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)]
|
||||||
tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!'
|
tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!'
|
||||||
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
|
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
|
||||||
|
if world.goal[player] == 'ganontriforcehunt' and world.players > 1:
|
||||||
|
tt['sign_ganon'] = 'You need to find %d Triforce pieces with your friends to defeat Ganon.' % world.treasure_hunt_count[player]
|
||||||
|
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||||
|
tt['sign_ganon'] = 'You need to find %d Triforce pieces to defeat Ganon.' % world.treasure_hunt_count[player]
|
||||||
|
|
||||||
tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)]
|
tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)]
|
||||||
|
|
||||||
|
|
16
Rules.py
16
Rules.py
|
@ -143,7 +143,7 @@ def item_name(state, location, player):
|
||||||
|
|
||||||
|
|
||||||
def locality_rules(world, player):
|
def locality_rules(world, player):
|
||||||
if world.goal[player] == "localtriforcehunt":
|
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
|
||||||
world.local_items[player].add('Triforce Piece')
|
world.local_items[player].add('Triforce Piece')
|
||||||
if world.local_items[player]:
|
if world.local_items[player]:
|
||||||
for location in world.get_locations():
|
for location in world.get_locations():
|
||||||
|
@ -418,8 +418,13 @@ def global_rules(world, player):
|
||||||
'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']:
|
'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']:
|
||||||
forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player)
|
forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player)
|
||||||
|
|
||||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)
|
|
||||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||||
|
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_triforce_pieces(world.treasure_hunt_count[player], player)
|
||||||
|
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||||
|
else:
|
||||||
|
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)
|
||||||
|
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
||||||
|
|
||||||
|
|
||||||
|
@ -817,7 +822,10 @@ def swordless_rules(world, player):
|
||||||
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||||
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain
|
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain
|
||||||
set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace
|
set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace
|
||||||
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||||
|
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_triforce_pieces(world.treasure_hunt_count[player], player))
|
||||||
|
else:
|
||||||
|
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
|
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
|
||||||
|
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
|
|
Binary file not shown.
|
@ -80,6 +80,8 @@ goals:
|
||||||
pedestal: 0 # Pull the Triforce from the Master Sword pedestal
|
pedestal: 0 # Pull the Triforce from the Master Sword pedestal
|
||||||
triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout the worlds, then turn them in to Murahadala in front of Hyrule Castle
|
triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout the worlds, then turn them in to Murahadala in front of Hyrule Castle
|
||||||
local_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout your world, then turn them in to Murahadala in front of Hyrule Castle
|
local_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout your world, then turn them in to Murahadala in front of Hyrule Castle
|
||||||
|
ganon_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout the worlds, then kill Ganon
|
||||||
|
local_ganon_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout your world, then kill Ganon
|
||||||
triforce_pieces_available: # set to how many triforces pieces are available to collect in the world. 30 is the default. Max is 112, Min is 1.
|
triforce_pieces_available: # set to how many triforces pieces are available to collect in the world. 30 is the default. Max is 112, Min is 1.
|
||||||
# format "pieces: chance"
|
# format "pieces: chance"
|
||||||
25: 0
|
25: 0
|
||||||
|
|
Loading…
Reference in New Issue