Bosshunt mode
This commit is contained in:
@@ -74,6 +74,8 @@ class World(object):
|
||||
self.dark_rooms = {}
|
||||
self.damage_challenge = {}
|
||||
self.shuffle_damage_table = {}
|
||||
self.bosses_ganon = {}
|
||||
self.bosshunt_include_agas = {}
|
||||
self.ganon_item = {}
|
||||
self.ganon_item_orig = {}
|
||||
self.custom = custom
|
||||
@@ -168,6 +170,8 @@ class World(object):
|
||||
set_player_attr('escape_assist', [])
|
||||
set_player_attr('crystals_needed_for_ganon', 7)
|
||||
set_player_attr('crystals_needed_for_gt', 7)
|
||||
set_player_attr('bosses_ganon', 8)
|
||||
set_player_attr('bosshunt_include_agas', False)
|
||||
set_player_attr('ganon_item', 'silver')
|
||||
set_player_attr('crystals_ganon_orig', {})
|
||||
set_player_attr('crystals_gt_orig', {})
|
||||
@@ -356,7 +360,7 @@ class World(object):
|
||||
else:
|
||||
if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district']:
|
||||
return False
|
||||
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt']:
|
||||
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt', 'bosshunt']:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -3118,6 +3122,8 @@ class Spoiler(object):
|
||||
'beemizer': self.world.beemizer,
|
||||
'gt_crystals': self.world.crystals_needed_for_gt,
|
||||
'ganon_crystals': self.world.crystals_needed_for_ganon,
|
||||
'ganon_bosses': self.world.bosses_ganon,
|
||||
'bosshunt_include_agas': self.world.bosshunt_include_agas,
|
||||
'ganon_item': self.world.ganon_item,
|
||||
'open_pyramid': self.world.open_pyramid,
|
||||
'accessibility': self.world.accessibility,
|
||||
@@ -3340,6 +3346,10 @@ class Spoiler(object):
|
||||
if custom['ganongoal'] and 'requirements' in custom['ganongoal']:
|
||||
outfile.write('Ganon Requirement:'.ljust(line_width) + 'custom\n')
|
||||
outfile.write(' %s\n' % custom['ganongoal']['goaltext'])
|
||||
elif self.metadata['goal'][player] == 'bosshunt':
|
||||
outfile.write('Ganon Requirement:'.ljust(line_width) + '%s bosses%s\n' %
|
||||
(str(self.world.bosses_ganon[player]),
|
||||
' (including both Agahnims)' if self.world.bosshunt_include_agas[player] else ''))
|
||||
else:
|
||||
outfile.write('Ganon Requirement:'.ljust(line_width) + '%s crystals\n' % str(self.world.crystals_ganon_orig[player]))
|
||||
if custom['pedgoal'] and 'requirements' in custom['pedgoal']:
|
||||
@@ -3749,8 +3759,9 @@ world_mode = {"open": 0, "standard": 1, "inverted": 2}
|
||||
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
|
||||
|
||||
# byte 2: GGGD DFFH (goal, diff, item_func, hints)
|
||||
goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5,
|
||||
'ganonhunt': 6, 'completionist': 7, 'sanctuary': 1}
|
||||
goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3,
|
||||
'crystals': 4, 'trinity': 5, 'ganonhunt': 6, 'completionist': 7,
|
||||
'sanctuary': 1, 'bosshunt': 6}
|
||||
diff_mode = {"normal": 0, "hard": 1, "expert": 2}
|
||||
func_mode = {"normal": 0, "hard": 1, "expert": 2}
|
||||
|
||||
|
||||
4
CLI.py
4
CLI.py
@@ -134,7 +134,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle',
|
||||
'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle',
|
||||
'flute_mode', 'bow_mode', 'take_any', 'boots_hint', 'shuffle_followers',
|
||||
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'ganon_item', 'openpyramid',
|
||||
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'bosses_ganon', 'bosshunt_include_agas', 'ganon_item', 'openpyramid',
|
||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'prizeshuffle', 'showloot', 'showmap', 'startinventory',
|
||||
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
|
||||
'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
||||
@@ -178,6 +178,8 @@ def parse_settings():
|
||||
"goal": "ganon",
|
||||
"crystals_gt": "7",
|
||||
"crystals_ganon": "7",
|
||||
"bosses_ganon": "8",
|
||||
"bosshunt_include_agas": False,
|
||||
"ganon_item": "silver",
|
||||
"swords": "random",
|
||||
"flute_mode": "normal",
|
||||
|
||||
@@ -218,12 +218,14 @@ def get_custom_array_key(item):
|
||||
|
||||
def generate_itempool(world, player):
|
||||
if (world.difficulty[player] not in ['normal', 'hard', 'expert']
|
||||
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals',
|
||||
'ganonhunt', 'completionist', 'sanctuary']
|
||||
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons',
|
||||
'triforcehunt', 'trinity', 'crystals',
|
||||
'ganonhunt', 'completionist', 'sanctuary',
|
||||
'bosshunt']
|
||||
or world.mode[player] not in ['open', 'standard', 'inverted']
|
||||
or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']
|
||||
or world.progressive not in ['on', 'off', 'random']):
|
||||
raise NotImplementedError('Not supported yet')
|
||||
raise NotImplementedError('Not supported yet')
|
||||
|
||||
if world.timer in ['ohko', 'timed-ohko']:
|
||||
world.can_take_damage[player] = False
|
||||
|
||||
20
Main.py
20
Main.py
@@ -80,7 +80,7 @@ def random_ganon_item(sword_mode):
|
||||
|
||||
def main(args, seed=None, fish=None):
|
||||
check_python_version()
|
||||
|
||||
|
||||
if args.print_template_yaml:
|
||||
return export_yaml(args, fish)
|
||||
|
||||
@@ -124,14 +124,14 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
from OverworldShuffle import __version__ as ORVersion
|
||||
logger.info(
|
||||
world.fish.translate("cli","cli","app.title") + "\n",
|
||||
ORVersion,
|
||||
"%s (%s)" % (world.seed, str(args.outputname)) if str(args.outputname).startswith('M') else world.seed,
|
||||
Settings.make_code(world, 1) if world.players == 1 else ''
|
||||
world.fish.translate("cli","cli","app.title") + "\n",
|
||||
ORVersion,
|
||||
"%s (%s)" % (world.seed, str(args.outputname)) if str(args.outputname).startswith('M') else world.seed,
|
||||
Settings.make_code(world, 1) if world.players == 1 else ''
|
||||
)
|
||||
|
||||
for k,v in {"DR":__version__,"OR":ORVersion}.items():
|
||||
logger.info((k + ' Version:').ljust(16) + '%s' % v)
|
||||
logger.info((k + ' Version:').ljust(16) + '%s' % v)
|
||||
|
||||
parsed_names = parse_player_names(args.names, world.players, args.teams)
|
||||
world.teams = len(parsed_names)
|
||||
@@ -478,6 +478,8 @@ def init_world(args, fish):
|
||||
world.crystals_ganon_orig = args.crystals_ganon.copy()
|
||||
world.crystals_gt_orig = args.crystals_gt.copy()
|
||||
world.ganon_item_orig = args.ganon_item.copy()
|
||||
world.bosses_ganon = {player: int(args.bosses_ganon[player]) for player in range(1, world.players + 1)}
|
||||
world.bosshunt_include_agas = args.bosshunt_include_agas.copy()
|
||||
world.owTerrain = args.ow_terrain.copy()
|
||||
world.owKeepSimilar = args.ow_keepsimilar.copy()
|
||||
world.owWhirlpoolShuffle = args.ow_whirlpool.copy()
|
||||
@@ -525,7 +527,7 @@ def init_world(args, fish):
|
||||
world.money_balance = args.money_balance.copy()
|
||||
|
||||
# custom settings - these haven't been promoted to full settings yet
|
||||
in_progress_settings = ['force_enemy', 'free_lamp_cone']
|
||||
in_progress_settings = ['force_enemy']
|
||||
for player in range(1, world.players + 1):
|
||||
for setting in in_progress_settings:
|
||||
if world.customizer and world.customizer.has_setting(player, setting):
|
||||
@@ -795,6 +797,8 @@ def copy_world(world):
|
||||
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
|
||||
ret.bosses_ganon = world.bosses_ganon.copy()
|
||||
ret.bosshunt_include_agas = world.bosshunt_include_agas.copy()
|
||||
ret.ganon_item = world.ganon_item.copy()
|
||||
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
||||
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
||||
@@ -1024,6 +1028,8 @@ def copy_world_premature(world, player, create_flute_exits=True):
|
||||
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
|
||||
ret.bosses_ganon = world.bosses_ganon.copy()
|
||||
ret.bosshunt_include_agas = world.bosshunt_include_agas.copy()
|
||||
ret.ganon_item = world.ganon_item.copy()
|
||||
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
||||
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
||||
|
||||
47
Rom.py
47
Rom.py
@@ -44,7 +44,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = '015d196d606fe1870a37df38ec335835'
|
||||
RANDOMIZERBASEHASH = '0a1b516a3ecde44603b6b2dca2b93616'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -1211,7 +1211,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28])
|
||||
if world.goal[player] in ['triforcehunt', 'trinity', 'ganonhunt']:
|
||||
rom.write_bytes(0x180167, int16_as_bytes(world.treasure_hunt_count[player]))
|
||||
rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
|
||||
rom.write_byte(0x180194, 1) # Must turn in triforce pieces (instant win not enabled)
|
||||
|
||||
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
|
||||
|
||||
@@ -1285,6 +1285,8 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
# 08: Goal items collected (ie. Triforce Pieces)
|
||||
# 09: Max collection rate
|
||||
# 0A: Custom goal
|
||||
# 0B: Reserved for Bingo
|
||||
# 0C: All bosses (prize bosses + aga1 + aga2)
|
||||
|
||||
def get_goal_bytes(type):
|
||||
goal_bytes = []
|
||||
@@ -1339,6 +1341,11 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
ganon_goal += [0x02, world.crystals_needed_for_ganon[player]]
|
||||
elif world.goal[player] in ['ganonhunt']:
|
||||
ganon_goal += [0x88] # triforce pieces
|
||||
elif world.goal[player] in ['bosshunt']:
|
||||
if world.bosshunt_include_agas[player]:
|
||||
ganon_goal += [0x0C, world.bosses_ganon[player]] # total bosses
|
||||
else:
|
||||
ganon_goal += [0x05, world.bosses_ganon[player]] # prize bosses
|
||||
elif world.goal[player] in ['completionist']:
|
||||
ganon_goal += [0x81, 0x82, 0x06, 0x07, 0x89] # AD and max collection rate
|
||||
else:
|
||||
@@ -1482,7 +1489,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
rom.write_byte(loot_icons + crystal_id, crystal_category)
|
||||
|
||||
pendant_ids = [0x37, 0x38, 0x39]
|
||||
if world.goal[player] in ['pedestal', 'dungeons']:
|
||||
if world.goal[player] in ['pedestal', 'dungeons', 'trinity']:
|
||||
pendant_category = 0x0C
|
||||
else:
|
||||
pendant_category = 0x06
|
||||
@@ -1635,12 +1642,28 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
# b - Big Key
|
||||
# a - Small Key
|
||||
#
|
||||
dungeon_items_menu = 0x00
|
||||
|
||||
if world.doorShuffle[player] not in ['vanilla', 'basic']:
|
||||
dungeon_items_menu |= 0x0F
|
||||
|
||||
if world.keyshuffle[player] not in ['none', 'universal']:
|
||||
dungeon_items_menu |= 0x01
|
||||
|
||||
if world.bigkeyshuffle[player] != 'none':
|
||||
dungeon_items_menu |= 0x02
|
||||
|
||||
enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby']
|
||||
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] not in ['none', 'universal'] else 0x00)
|
||||
| (0x02 if world.bigkeyshuffle[player] != 'none' else 0x00)
|
||||
| (0x04 if world.mapshuffle[player] != 'none' or enable_menu_map_check else 0x00)
|
||||
| (0x08 if world.compassshuffle[player] != 'none' else 0x00) # free roaming items in menu
|
||||
| (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon
|
||||
if world.mapshuffle[player] != 'none' or enable_menu_map_check:
|
||||
dungeon_items_menu |= 0x04
|
||||
|
||||
if world.compassshuffle[player] != 'none':
|
||||
dungeon_items_menu |= 0x08
|
||||
|
||||
if world.logic[player] == 'nologic' or world.goal[player] == 'bosshunt':
|
||||
dungeon_items_menu |= 0x10
|
||||
|
||||
rom.write_byte(0x180045, dungeon_items_menu)
|
||||
|
||||
def get_reveal_bytes(itemName):
|
||||
if world.prizeshuffle[player] != 'wild':
|
||||
@@ -2746,12 +2769,18 @@ def write_strings(rom, world, player, team):
|
||||
tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! You can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % int(world.treasure_hunt_count[player])
|
||||
elif world.goal[player] == 'ganonhunt':
|
||||
tt['sign_ganon'] = 'Go find the Triforce pieces to beat Ganon'
|
||||
elif world.goal[player] == 'bosshunt':
|
||||
bosshunt_count = '%d guardian%s of %sdungeons' % \
|
||||
(world.bosses_ganon[player],
|
||||
'' if world.bosses_ganon[player] == 1 else 's',
|
||||
'' if world.bosshunt_include_agas[player] else 'prize ')
|
||||
tt['sign_ganon'] = 'To beat Ganon you must defeat %s.' % bosshunt_count
|
||||
elif world.goal[player] == 'completionist':
|
||||
tt['sign_ganon'] = 'Ganon only respects those who have done everything'
|
||||
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_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
|
||||
|
||||
|
||||
def get_custom_goal_text(type):
|
||||
goal_text = world.custom_goals[player][type]['goaltext']
|
||||
placeholder_count = goal_text.count('%d')
|
||||
|
||||
9
Rules.py
9
Rules.py
@@ -80,6 +80,15 @@ def set_rules(world, player):
|
||||
add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||
elif world.goal[player] == 'ganonhunt':
|
||||
add_rule(world.get_location('Ganon', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= int(state.world.treasure_hunt_count[player]))
|
||||
elif world.goal[player] == 'bosshunt':
|
||||
if world.bosshunt_include_agas[player]:
|
||||
add_rule(world.get_location('Ganon', player), lambda state:
|
||||
state.item_count('Beat Agahnim 1', player) +
|
||||
state.item_count('Beat Agahnim 2', player) +
|
||||
state.item_count('Beat Boss', player) >= world.bosses_ganon[player])
|
||||
else:
|
||||
add_rule(world.get_location('Ganon', player), lambda state:
|
||||
state.item_count('Beat Boss', player) >= world.bosses_ganon[player])
|
||||
elif world.goal[player] == 'completionist':
|
||||
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
|
||||
|
||||
|
||||
Binary file not shown.
@@ -72,6 +72,7 @@
|
||||
"trinity",
|
||||
"crystals",
|
||||
"ganonhunt",
|
||||
"bosshunt",
|
||||
"completionist",
|
||||
"sanctuary"
|
||||
]
|
||||
@@ -299,6 +300,27 @@
|
||||
"random"
|
||||
]
|
||||
},
|
||||
"bosses_ganon": {
|
||||
"choices": [
|
||||
"12",
|
||||
"11",
|
||||
"10",
|
||||
"9",
|
||||
"8",
|
||||
"7",
|
||||
"6",
|
||||
"5",
|
||||
"4",
|
||||
"3",
|
||||
"2",
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"bosshunt_include_agas": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"crystals_gt": {
|
||||
"choices": [
|
||||
"7",
|
||||
|
||||
Reference in New Issue
Block a user