New goals and rom update

This commit is contained in:
aerinon
2022-12-01 14:10:10 -07:00
parent 1c91eef29a
commit becba348b9
14 changed files with 74 additions and 28 deletions

View File

@@ -155,7 +155,9 @@ class World(object):
def finish_init(self):
for player in range(1, self.players + 1):
if self.mode[player] == 'retro':
self.mode[player] == 'open'
self.mode[player] = 'open'
if self.goal[player] == 'completionist':
self.accessibility[player] = 'locations'
def get_name_string_for_object(self, obj):
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
@@ -1035,6 +1037,10 @@ class CollectionState(object):
def item_count(self, item, player):
return self.prog_items[item, player]
def everything(self, player):
return (len([x for x in self.locations_checked if x.player == player])
>= len(self.world.get_filled_locations(player)))
def has_crystals(self, count, player):
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
@@ -2587,7 +2593,7 @@ class Spoiler(object):
outfile.write('Mode: %s\n' % self.metadata['mode'][player])
outfile.write('Swords: %s\n' % self.metadata['weapons'][player])
outfile.write('Goal: %s\n' % self.metadata['goal'][player])
if self.metadata['goal'][player] in ['triforcehunt', 'trinity']:
if self.metadata['goal'][player] in ['triforcehunt', 'trinity', 'ganonhunt']:
outfile.write('Triforce Pieces Required: %s\n' % self.metadata['triforcegoal'][player])
outfile.write('Triforce Pieces Total: %s\n' % self.metadata['triforcepool'][player])
outfile.write('Crystals required for GT: %s\n' % (str(self.world.crystals_gt_orig[player])))
@@ -2867,7 +2873,8 @@ 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}
goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5,
'ganonhunt': 6, 'completionist': 7}
diff_mode = {"normal": 0, "hard": 1, "expert": 2}
func_mode = {"normal": 0, "hard": 1, "expert": 2}

View File

@@ -400,7 +400,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
else:
max_trash = gt_count
scaled_trash = math.floor(max_trash * scale_factor)
if world.goal[player] in ['triforcehunt', 'trinity']:
if world.goal[player] in ['triforcehunt', 'trinity', 'ganonhunt']:
gftower_trash_count = random.randint(scaled_trash, max_trash)
else:
gftower_trash_count = random.randint(0, scaled_trash)

View File

@@ -181,8 +181,12 @@ 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']
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']):
if (world.difficulty[player] not in ['normal', 'hard', 'expert']
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals',
'ganonhunt', 'completionist']
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')
if world.timer in ['ohko', 'timed-ohko']:
@@ -344,7 +348,7 @@ def generate_itempool(world, player):
world.clock_mode = clock_mode
goal = world.goal[player]
if goal in ['triforcehunt', 'trinity']:
if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
g, t = set_default_triforce(goal, world.treasure_hunt_count[player], world.treasure_hunt_total[player])
world.treasure_hunt_count[player], world.treasure_hunt_total[player] = g, t
world.treasure_hunt_icon[player] = 'Triforce Piece'
@@ -817,15 +821,15 @@ def add_pot_contents(world, player):
world.itempool.append(ItemFactory(item, player))
def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, bombbag,
door_shuffle, logic, flute_activated):
def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords,
bombbag, door_shuffle, logic, flute_activated):
pool = []
placed_items = {}
precollected_items = []
clock_mode = None
if goal in ['triforcehunt', 'trinity']:
if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
if treasure_hunt_total == 0:
treasure_hunt_total = 30 if goal == 'triforcehunt' else 10
treasure_hunt_total = 30 if goal in ['triforcehunt', 'ganonhunt'] else 10
# triforce pieces max out
triforcepool = ['Triforce Piece'] * min(treasure_hunt_total, max_goal)
@@ -928,7 +932,7 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt
elif timer == 'timed-ohko':
pool.extend(diff.timedohko)
clock_mode = 'countdown-ohko'
if goal in ['triforcehunt', 'trinity']:
if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
pool.extend(triforcepool)
for extra in diff.extras:
@@ -987,7 +991,7 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
customitemarray["triforce"] = total_items_to_place
# Triforce Pieces
if goal in ['triforcehunt', 'trinity']:
if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
g, t = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"])
customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = g, t
@@ -1025,8 +1029,8 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], max_goal), 1)
treasure_hunt_icon = 'Triforce Piece'
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
if ((customitemarray["triforcepieces"] < treasure_hunt_count) and (goal in ['triforcehunt', 'trinity'])
and (customitemarray["triforce"] == 0)):
if ((customitemarray["triforcepieces"] < treasure_hunt_count)
and (goal in ['triforcehunt', 'trinity', 'ganonhunt']) and (customitemarray["triforce"] == 0)):
extrapieces = treasure_hunt_count - customitemarray["triforcepieces"]
pool.extend(['Triforce Piece'] * extrapieces)
itemtotal = itemtotal + extrapieces
@@ -1214,7 +1218,7 @@ def get_player_dungeon_item_pool(world, player):
# location pool doesn't support larger values at this time
def set_default_triforce(goal, custom_goal, custom_total):
triforce_goal, triforce_total = 0, 0
if goal == 'triforcehunt':
if goal in ['triforcehunt', 'ganonhunt']:
triforce_goal, triforce_total = 20, 30
elif goal == 'trinity':
triforce_goal, triforce_total = 8, 10

View File

@@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
from source.tools.BPS import create_bps_from_data
from source.classes.CustomSettings import CustomSettings
__version__ = '1.2.0.0-u'
__version__ = '1.2.0.1-u'
from source.classes.BabelFish import BabelFish

View File

@@ -53,7 +53,16 @@ This is similar to insanity mode in ER where door entrances and exits are not pa
## Customizer
Please see [Customizer documentation](docs/Customizer.md) on how to create custom seeds.
Please see [Customizer documentation](docs/Customizer.md) on how to create custom seeds.
## New Goals
### Triforce Hunt + Ganon
Collect the requisite triforce pieces, then defeat Ganon. (Aga2 not required). Use `ganonhunt` on CLI
### Completionist
All dungeons not enough for you? You have to obtain every item in the game too. This option turns on the collection rate counter and forces accessibility to be 100% locations. Finish by defeating Ganon.
## Standard Generation Change
@@ -100,10 +109,10 @@ These are now independent of retro mode and have three options: None, Random, an
# Bug Fixes and Notes
None yet
* 1.2.0.1-u
* Fixed the issue when defeating Agahnim and standing in the doorway can cause door state to linger.
# Known Issues
* Standing in the doorway when defeating Aga 1 and being teleported to the Dark World will not clear door state. It may cause issues requiring a Save & Quit to fix.
* Decoupled doors can lead to situations where you aren't logically supposed to go back through a door without a big key or small key, but you can if you press the correct direction back through the door first. There are some transitions where you may get stuck without a bomb. These problems are planned to be fixed.
* Logic getting to Skull X room may be wrong if a trap door, big key door, or bombable wall. A bomb jump to get to those pot may be required if you don't have boots to bonk across.
* Logic getting to Skull X room may be wrong if a trap door, big key door, or bombable wall is shuffled there. A bomb jump to get to those pot may be required if you don't have boots to bonk across.

15
Rom.py
View File

@@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'b6fcbc0d61faffa178135545f18fadbd'
RANDOMIZERBASEHASH = 'fb7f9a0d501ba9ecd0a31066f9a0a973'
class JsonRom(object):
@@ -730,7 +730,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
if world.doorShuffle[player] not in ['vanilla', 'basic']:
dr_flags |= DROptions.Map_Info
if world.collection_rate[player] and world.goal[player] not in ['triforcehunt', 'trinity']:
if ((world.collection_rate[player] or world.goal[player] == 'completionist')
and world.goal[player] not in ['triforcehunt', 'trinity', 'ganonhunt']):
dr_flags |= DROptions.Debug
if world.doorShuffle[player] not in ['vanilla', 'basic'] and world.logic[player] != 'nologic'\
and world.mixed_travel[player] == 'prevent':
@@ -1275,7 +1276,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# set up goals for treasure hunt
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28])
if world.goal[player] in ['triforcehunt', 'trinity']:
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)
@@ -1340,6 +1341,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
elif world.goal[player] in ['crystals', 'trinity']:
rom.write_byte(0x18003E, 0x04) # make ganon invincible until all crystals
elif world.goal[player] in ['ganonhunt']:
rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected
elif world.goal[player] in ['completionist']:
rom.write_byte(0x18003E, 0x09) # make ganon invincible until everything is collected
else:
rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected
@@ -2362,6 +2367,10 @@ def write_strings(rom, world, player, team):
trinity_crystal_text = ('%d crystal to beat Ganon.' if world.crystals_needed_for_ganon[player] == 1 else '%d crystals to beat Ganon.') % world.crystals_needed_for_ganon[player]
tt['sign_ganon'] = 'Three ways to victory! %s Get to it!' % trinity_crystal_text
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] == '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!'

View File

@@ -60,6 +60,10 @@ def set_rules(world, player):
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
elif world.goal[player] in ['triforcehunt', 'trinity']:
add_rule(world.get_location('Murahdahla', 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] == '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] == 'completionist':
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
if world.mode[player] != 'inverted':
set_big_bomb_rules(world, player)

Binary file not shown.

View File

@@ -82,6 +82,8 @@
pedestal: 2
triforce-hunt: 2
trinity: 2
ganonhunt: 2
completionist: 1
triforce_goal_min: 10
triforce_goal_max: 30
triforce_pool_min: 20

View File

@@ -67,7 +67,9 @@
"dungeons",
"triforcehunt",
"trinity",
"crystals"
"crystals",
"ganonhunt",
"completionist"
]
},
"difficulty": {

View File

@@ -107,7 +107,10 @@
"Triforce Hunt: Places 30 Triforce Pieces in the world, collect",
" 20 of them to beat the game.",
"Trinity: Can beat the game by defeating Ganon, pulling",
" Pedestal, or delivering Triforce Pieces."
" Pedestal, or delivering Triforce Pieces.",
"Ganon Hunt: Places 30 Triforce Pieces in the world, collect",
" 20 of them then defeat Ganon.",
"Completionist: Find everything then defeat Ganon."
],
"difficulty": [
"Select game difficulty. Affects available itempool. (default: %(default)s)",

View File

@@ -248,6 +248,8 @@
"randomizer.item.goal.triforcehunt": "Triforce Hunt",
"randomizer.item.goal.trinity": "Trinity",
"randomizer.item.goal.crystals": "Crystals",
"randomizer.item.goal.ganonhunt": "Triforce Hunt + Ganon",
"randomizer.item.goal.completionist": "Completionist",
"randomizer.item.crystals_gt": "Crystals to open GT",
"randomizer.item.crystals_gt.0": "0",

View File

@@ -36,7 +36,9 @@
"dungeons",
"triforcehunt",
"trinity",
"crystals"
"crystals",
"ganonhunt",
"completionist"
]
},
"crystals_gt": {

View File

@@ -113,7 +113,9 @@ def roll_settings(weights):
'dungeons': 'dungeons',
'pedestal': 'pedestal',
'triforce-hunt': 'triforcehunt',
'trinity': 'trinity'
'trinity': 'trinity',
'ganonhunt': 'ganonhunt',
'completionist': 'completionist'
}[goal]
ret.openpyramid = goal in ['fast_ganon', 'trinity'] if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False