GK Version 1.0.0 #1
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,8 +32,6 @@ weights/
|
|||||||
/output/
|
/output/
|
||||||
/enemizer/
|
/enemizer/
|
||||||
|
|
||||||
base2current.json
|
|
||||||
|
|
||||||
resources/user/*
|
resources/user/*
|
||||||
!resources/user/.gitkeep
|
!resources/user/.gitkeep
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from Tables import (
|
|||||||
spiral_offset_table,
|
spiral_offset_table,
|
||||||
)
|
)
|
||||||
from Utils import int16_as_bytes
|
from Utils import int16_as_bytes
|
||||||
|
from Versions import DRVersion, GKVersion, ORVersion
|
||||||
|
|
||||||
|
|
||||||
class World(object):
|
class World(object):
|
||||||
@@ -79,6 +80,8 @@ class World(object):
|
|||||||
self.dark_rooms = {}
|
self.dark_rooms = {}
|
||||||
self.damage_challenge = {}
|
self.damage_challenge = {}
|
||||||
self.shuffle_damage_table = {}
|
self.shuffle_damage_table = {}
|
||||||
|
self.bosses_ganon = {}
|
||||||
|
self.bosshunt_include_agas = {}
|
||||||
self.ganon_item = {}
|
self.ganon_item = {}
|
||||||
self.ganon_item_orig = {}
|
self.ganon_item_orig = {}
|
||||||
self.custom = custom
|
self.custom = custom
|
||||||
@@ -154,6 +157,8 @@ class World(object):
|
|||||||
set_player_attr('keyshuffle', 'none')
|
set_player_attr('keyshuffle', 'none')
|
||||||
set_player_attr('bigkeyshuffle', 'none')
|
set_player_attr('bigkeyshuffle', 'none')
|
||||||
set_player_attr('prizeshuffle', 'none')
|
set_player_attr('prizeshuffle', 'none')
|
||||||
|
set_player_attr('showloot', 'never')
|
||||||
|
set_player_attr('showmap', 'map')
|
||||||
set_player_attr('restrict_boss_items', 'none')
|
set_player_attr('restrict_boss_items', 'none')
|
||||||
set_player_attr('bombbag', False)
|
set_player_attr('bombbag', False)
|
||||||
set_player_attr('flute_mode', 'normal')
|
set_player_attr('flute_mode', 'normal')
|
||||||
@@ -171,6 +176,8 @@ class World(object):
|
|||||||
set_player_attr('escape_assist', [])
|
set_player_attr('escape_assist', [])
|
||||||
set_player_attr('crystals_needed_for_ganon', 7)
|
set_player_attr('crystals_needed_for_ganon', 7)
|
||||||
set_player_attr('crystals_needed_for_gt', 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('ganon_item', 'silver')
|
||||||
set_player_attr('crystals_ganon_orig', {})
|
set_player_attr('crystals_ganon_orig', {})
|
||||||
set_player_attr('crystals_gt_orig', {})
|
set_player_attr('crystals_gt_orig', {})
|
||||||
@@ -359,7 +366,7 @@ class World(object):
|
|||||||
else:
|
else:
|
||||||
if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district']:
|
if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district']:
|
||||||
return False
|
return False
|
||||||
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt']:
|
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt', 'bosshunt']:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@@ -3077,12 +3084,9 @@ class Spoiler(object):
|
|||||||
self.doorTypes[(doorNames, player)] = OrderedDict([('player', player), ('doorNames', doorNames), ('type', type)])
|
self.doorTypes[(doorNames, player)] = OrderedDict([('player', player), ('doorNames', doorNames), ('type', type)])
|
||||||
|
|
||||||
def parse_meta(self):
|
def parse_meta(self):
|
||||||
from Main import __version__ as ERVersion
|
|
||||||
from OverworldShuffle import __version__ as ORVersion
|
|
||||||
|
|
||||||
self.startinventory = list(map(str, self.world.precollected_items))
|
self.startinventory = list(map(str, self.world.precollected_items))
|
||||||
self.metadata = {'version': ERVersion,
|
self.metadata = {'version': GKVersion,
|
||||||
'versions': {'Door':ERVersion, 'Overworld':ORVersion},
|
'versions': {'Door': DRVersion, 'Overworld': ORVersion},
|
||||||
'logic': self.world.logic,
|
'logic': self.world.logic,
|
||||||
'mode': self.world.mode,
|
'mode': self.world.mode,
|
||||||
'bombbag': self.world.bombbag,
|
'bombbag': self.world.bombbag,
|
||||||
@@ -3121,6 +3125,8 @@ class Spoiler(object):
|
|||||||
'beemizer': self.world.beemizer,
|
'beemizer': self.world.beemizer,
|
||||||
'gt_crystals': self.world.crystals_needed_for_gt,
|
'gt_crystals': self.world.crystals_needed_for_gt,
|
||||||
'ganon_crystals': self.world.crystals_needed_for_ganon,
|
'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,
|
'ganon_item': self.world.ganon_item,
|
||||||
'open_pyramid': self.world.open_pyramid,
|
'open_pyramid': self.world.open_pyramid,
|
||||||
'accessibility': self.world.accessibility,
|
'accessibility': self.world.accessibility,
|
||||||
@@ -3131,6 +3137,8 @@ class Spoiler(object):
|
|||||||
'keyshuffle': self.world.keyshuffle,
|
'keyshuffle': self.world.keyshuffle,
|
||||||
'bigkeyshuffle': self.world.bigkeyshuffle,
|
'bigkeyshuffle': self.world.bigkeyshuffle,
|
||||||
'prizeshuffle': self.world.prizeshuffle,
|
'prizeshuffle': self.world.prizeshuffle,
|
||||||
|
'showloot': self.world.showloot,
|
||||||
|
'showmap': self.world.showmap,
|
||||||
'boss_shuffle': self.world.boss_shuffle,
|
'boss_shuffle': self.world.boss_shuffle,
|
||||||
'enemy_shuffle': self.world.enemy_shuffle,
|
'enemy_shuffle': self.world.enemy_shuffle,
|
||||||
'enemy_health': self.world.enemy_health,
|
'enemy_health': self.world.enemy_health,
|
||||||
@@ -3295,7 +3303,7 @@ class Spoiler(object):
|
|||||||
self.parse_meta()
|
self.parse_meta()
|
||||||
with open(filename, 'w') as outfile:
|
with open(filename, 'w') as outfile:
|
||||||
line_width = 35
|
line_width = 35
|
||||||
outfile.write('ALttP Overworld Randomizer - Seed: %s\n\n' % (self.world.seed))
|
outfile.write('ALttP GwaaKiwi Randomizer - Seed: %s\n\n' % (self.world.seed))
|
||||||
for k,v in self.metadata["versions"].items():
|
for k,v in self.metadata["versions"].items():
|
||||||
outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v)
|
outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v)
|
||||||
for player in range(1, self.world.players + 1):
|
for player in range(1, self.world.players + 1):
|
||||||
@@ -3310,7 +3318,7 @@ class Spoiler(object):
|
|||||||
self.parse_meta()
|
self.parse_meta()
|
||||||
with open(filename, 'w') as outfile:
|
with open(filename, 'w') as outfile:
|
||||||
line_width = 35
|
line_width = 35
|
||||||
outfile.write('ALttP Overworld Randomizer - Seed: %s\n\n' % (self.world.seed))
|
outfile.write('ALttP GwaaKiwi Randomizer - Seed: %s\n\n' % (self.world.seed))
|
||||||
for k,v in self.metadata["versions"].items():
|
for k,v in self.metadata["versions"].items():
|
||||||
outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v)
|
outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v)
|
||||||
if self.metadata['user_notes']:
|
if self.metadata['user_notes']:
|
||||||
@@ -3341,6 +3349,10 @@ class Spoiler(object):
|
|||||||
if custom['ganongoal'] and 'requirements' in custom['ganongoal']:
|
if custom['ganongoal'] and 'requirements' in custom['ganongoal']:
|
||||||
outfile.write('Ganon Requirement:'.ljust(line_width) + 'custom\n')
|
outfile.write('Ganon Requirement:'.ljust(line_width) + 'custom\n')
|
||||||
outfile.write(' %s\n' % custom['ganongoal']['goaltext'])
|
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:
|
else:
|
||||||
outfile.write('Ganon Requirement:'.ljust(line_width) + '%s crystals\n' % str(self.world.crystals_ganon_orig[player]))
|
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']:
|
if custom['pedgoal'] and 'requirements' in custom['pedgoal']:
|
||||||
@@ -3395,6 +3407,8 @@ class Spoiler(object):
|
|||||||
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
|
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
|
||||||
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['bigkeyshuffle'][player])
|
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['bigkeyshuffle'][player])
|
||||||
outfile.write('Prize Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['prizeshuffle'][player])
|
outfile.write('Prize Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['prizeshuffle'][player])
|
||||||
|
outfile.write('Show Value of Checks:'.ljust(line_width) + '%s\n' % self.metadata['showloot'][player])
|
||||||
|
outfile.write('Show Map:'.ljust(line_width) + '%s\n' % self.metadata['showmap'][player])
|
||||||
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
|
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
|
||||||
outfile.write('\n')
|
outfile.write('\n')
|
||||||
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
|
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
|
||||||
@@ -3748,8 +3762,9 @@ world_mode = {"open": 0, "standard": 1, "inverted": 2}
|
|||||||
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
|
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
|
||||||
|
|
||||||
# byte 2: GGGD DFFH (goal, diff, item_func, hints)
|
# 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,
|
||||||
'ganonhunt': 6, 'completionist': 7, 'sanctuary': 1}
|
'crystals': 4, 'trinity': 5, 'ganonhunt': 6, 'completionist': 7,
|
||||||
|
'sanctuary': 1, 'bosshunt': 6}
|
||||||
diff_mode = {"normal": 0, "hard": 1, "expert": 2}
|
diff_mode = {"normal": 0, "hard": 1, "expert": 2}
|
||||||
func_mode = {"normal": 0, "hard": 1, "expert": 2}
|
func_mode = {"normal": 0, "hard": 1, "expert": 2}
|
||||||
|
|
||||||
|
|||||||
10
CLI.py
10
CLI.py
@@ -106,7 +106,7 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
ret = parser.parse_args(argv)
|
ret = parser.parse_args(argv)
|
||||||
|
|
||||||
if ret.keysanity:
|
if ret.keysanity:
|
||||||
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = 'wild' * 4
|
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = ['wild'] * 4
|
||||||
|
|
||||||
if ret.keydropshuffle:
|
if ret.keydropshuffle:
|
||||||
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
|
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
|
||||||
@@ -133,8 +133,8 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle',
|
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle',
|
||||||
'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle',
|
'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle',
|
||||||
'flute_mode', 'bow_mode', 'take_any', 'boots_hint', 'shuffle_followers',
|
'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', 'startinventory',
|
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'prizeshuffle', 'showloot', 'showmap', 'startinventory',
|
||||||
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
|
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
|
||||||
'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
||||||
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
|
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
|
||||||
@@ -177,6 +177,8 @@ def parse_settings():
|
|||||||
"goal": "ganon",
|
"goal": "ganon",
|
||||||
"crystals_gt": "7",
|
"crystals_gt": "7",
|
||||||
"crystals_ganon": "7",
|
"crystals_ganon": "7",
|
||||||
|
"bosses_ganon": "8",
|
||||||
|
"bosshunt_include_agas": False,
|
||||||
"ganon_item": "silver",
|
"ganon_item": "silver",
|
||||||
"swords": "random",
|
"swords": "random",
|
||||||
"flute_mode": "normal",
|
"flute_mode": "normal",
|
||||||
@@ -234,6 +236,8 @@ def parse_settings():
|
|||||||
"keyshuffle": "none",
|
"keyshuffle": "none",
|
||||||
"bigkeyshuffle": "none",
|
"bigkeyshuffle": "none",
|
||||||
"prizeshuffle": "none",
|
"prizeshuffle": "none",
|
||||||
|
"showloot": "never",
|
||||||
|
"showmap": "map",
|
||||||
"keysanity": False,
|
"keysanity": False,
|
||||||
"door_shuffle": "vanilla",
|
"door_shuffle": "vanilla",
|
||||||
"intensity": 3,
|
"intensity": 3,
|
||||||
|
|||||||
@@ -2340,7 +2340,7 @@ def parallel_full_neutralization(dungeon_map, polarized_sectors, global_pole):
|
|||||||
increment_depth = True
|
increment_depth = True
|
||||||
current_depth = last_depth + 1 if increment_depth else last_depth
|
current_depth = last_depth + 1 if increment_depth else last_depth
|
||||||
finished = all([(x.polarity()+sum_polarity(solution_list[x])).is_neutral() for x in builders])
|
finished = all([(x.polarity()+sum_polarity(solution_list[x])).is_neutral() for x in builders])
|
||||||
logging.getLogger('').info(f'-Balanced solution found in {time.process_time()-start}')
|
logging.getLogger('').debug(f'-Balanced solution found in {time.process_time()-start}')
|
||||||
for builder, sectors in solution_list.items():
|
for builder, sectors in solution_list.items():
|
||||||
for sector in sectors:
|
for sector in sectors:
|
||||||
assign_sector(sector, builder, polarized_sectors, global_pole)
|
assign_sector(sector, builder, polarized_sectors, global_pole)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import RaceRandom as random
|
|||||||
import source.classes.diags as diagnostics
|
import source.classes.diags as diagnostics
|
||||||
from CLI import get_args_priority, parse_cli
|
from CLI import get_args_priority, parse_cli
|
||||||
from Fill import FillError
|
from Fill import FillError
|
||||||
from Main import EnemizerError, __version__, main
|
from Main import EnemizerError, main
|
||||||
from Rom import get_sprite_from_name
|
from Rom import get_sprite_from_name
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
from Utils import close_console, is_bundled
|
from Utils import close_console, is_bundled
|
||||||
@@ -80,7 +80,7 @@ def start():
|
|||||||
break
|
break
|
||||||
except (FillError, EnemizerError, Exception, RuntimeError) as err:
|
except (FillError, EnemizerError, Exception, RuntimeError) as err:
|
||||||
failures.append((err, seed))
|
failures.append((err, seed))
|
||||||
logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err)
|
logger.exception('Attempt %d - %s: %s', trynum, fish.translate("cli","cli","generation.failed"), err)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
seed = random.randint(0, 999999999)
|
seed = random.randint(0, 999999999)
|
||||||
|
|
||||||
|
|||||||
16
Dungeons.py
16
Dungeons.py
@@ -13,7 +13,19 @@ def create_dungeons(world, player):
|
|||||||
dungeon.world = world
|
dungeon.world = world
|
||||||
return dungeon
|
return dungeon
|
||||||
|
|
||||||
ES = make_dungeon('Hyrule Castle', 1, None, hyrule_castle_regions, None, [ItemFactory('Small Key (Escape)', player)], [ItemFactory('Map (Escape)', player)])
|
hc_dungeon_items = ['Map (Escape)']
|
||||||
|
at_dungeon_items = []
|
||||||
|
|
||||||
|
if world.showloot[player] == 'compass':
|
||||||
|
if world.dropshuffle[player] == 'underworld' or world.pottery[player] in ['dungeon', 'reduced', 'clustered', 'nonempty', 'lottery']:
|
||||||
|
hc_dungeon_items.append('Compass (Escape)')
|
||||||
|
at_dungeon_items.append('Compass (Agahnims Tower)')
|
||||||
|
elif world.compassshuffle[player] == 'wild':
|
||||||
|
hc_dungeon_items.append('Compass (Escape)')
|
||||||
|
if world.keyshuffle[player] == 'wild':
|
||||||
|
at_dungeon_items.append('Compass (Agahnims Tower)')
|
||||||
|
|
||||||
|
ES = make_dungeon('Hyrule Castle', 1, None, hyrule_castle_regions, None, [ItemFactory('Small Key (Escape)', player)], ItemFactory(hc_dungeon_items, player))
|
||||||
EP = make_dungeon('Eastern Palace', 2, 'Armos Knights', eastern_regions, ItemFactory('Big Key (Eastern Palace)', player), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
|
EP = make_dungeon('Eastern Palace', 2, 'Armos Knights', eastern_regions, ItemFactory('Big Key (Eastern Palace)', player), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
|
||||||
DP = make_dungeon('Desert Palace', 3, 'Lanmolas', desert_regions, ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
|
DP = make_dungeon('Desert Palace', 3, 'Lanmolas', desert_regions, ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
|
||||||
ToH = make_dungeon('Tower of Hera', 10, 'Moldorm', hera_regions, ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
|
ToH = make_dungeon('Tower of Hera', 10, 'Moldorm', hera_regions, ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
|
||||||
@@ -24,7 +36,7 @@ def create_dungeons(world, player):
|
|||||||
IP = make_dungeon('Ice Palace', 9, 'Kholdstare', ice_regions, ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
|
IP = make_dungeon('Ice Palace', 9, 'Kholdstare', ice_regions, ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
|
||||||
MM = make_dungeon('Misery Mire', 7, 'Vitreous', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
MM = make_dungeon('Misery Mire', 7, 'Vitreous', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
||||||
TR = make_dungeon('Turtle Rock', 12, 'Trinexx', tr_regions, ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
TR = make_dungeon('Turtle Rock', 12, 'Trinexx', tr_regions, ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
||||||
AT = make_dungeon('Agahnims Tower', 4, 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
AT = make_dungeon('Agahnims Tower', 4, 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), ItemFactory(at_dungeon_items, player))
|
||||||
GT = make_dungeon('Ganons Tower', 13, 'Agahnim2', gt_regions, ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
|
GT = make_dungeon('Ganons Tower', 13, 'Agahnim2', gt_regions, ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
|
||||||
|
|
||||||
GT.bosses['bottom'] = BossFactory('Armos Knights', player)
|
GT.bosses['bottom'] = BossFactory('Armos Knights', player)
|
||||||
|
|||||||
2
Fill.py
2
Fill.py
@@ -799,7 +799,7 @@ def sell_potions(world, player):
|
|||||||
if shop.region.name in shop_to_location_table and shop.region.name != 'Capacity Upgrade':
|
if shop.region.name in shop_to_location_table and shop.region.name != 'Capacity Upgrade':
|
||||||
loc_choices += [world.get_location(loc, player) for loc in shop_to_location_table[shop.region.name]]
|
loc_choices += [world.get_location(loc, player) for loc in shop_to_location_table[shop.region.name]]
|
||||||
locations = [loc for loc in loc_choices if not loc.item]
|
locations = [loc for loc in loc_choices if not loc.item]
|
||||||
for potion in ['Green Potion', 'Blue Potion', 'Red Potion']:
|
for potion in ['Green Potion', 'Blue Potion', 'Red Potion', 'Bee']:
|
||||||
location = random.choice(filter_locations(ItemFactory(potion, player), locations, world, potion=True))
|
location = random.choice(filter_locations(ItemFactory(potion, player), locations, world, potion=True))
|
||||||
locations.remove(location)
|
locations.remove(location)
|
||||||
p_item = next((item for item in world.itempool if item.name == potion and item.player == player), None)
|
p_item = next((item for item in world.itempool if item.name == potion and item.player == player), None)
|
||||||
|
|||||||
7
Gui.py
7
Gui.py
@@ -22,8 +22,6 @@ from tkinter import (
|
|||||||
from CLI import get_args_priority
|
from CLI import get_args_priority
|
||||||
from DungeonRandomizer import parse_cli
|
from DungeonRandomizer import parse_cli
|
||||||
from GuiUtils import set_icon
|
from GuiUtils import set_icon
|
||||||
from Main import __version__ as ESVersion
|
|
||||||
from OverworldShuffle import __version__ as ORVersion
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
from source.classes.Empty import Empty
|
from source.classes.Empty import Empty
|
||||||
from source.gui.adjust.overview import adjust_page
|
from source.gui.adjust.overview import adjust_page
|
||||||
@@ -40,13 +38,14 @@ from source.gui.randomize.generation import generation_page
|
|||||||
from source.gui.randomize.item import item_page
|
from source.gui.randomize.item import item_page
|
||||||
from source.gui.randomize.overworld import overworld_page
|
from source.gui.randomize.overworld import overworld_page
|
||||||
from source.gui.startinventory.overview import startinventory_page
|
from source.gui.startinventory.overview import startinventory_page
|
||||||
|
from Versions import DRVersion, GKVersion, ORVersion
|
||||||
|
|
||||||
|
|
||||||
def check_python_version(fish):
|
def check_python_version(fish):
|
||||||
import sys
|
import sys
|
||||||
version = sys.version_info
|
version = sys.version_info
|
||||||
if version.major < 3 or version.minor < 7:
|
if version.major < 3 or version.minor < 7:
|
||||||
messagebox.showinfo("Overworld Shuffle %s (DR %s)" % (ORVersion, ESVersion), fish.translate("cli","cli","old.python.version") % sys.version)
|
messagebox.showinfo("GwaaKiwi Randomizer %s (OR %s, DR %s)" % (GKVersion, ORVersion, DRVersion), fish.translate("cli","cli","old.python.version") % sys.version)
|
||||||
|
|
||||||
|
|
||||||
# Save settings to file
|
# Save settings to file
|
||||||
@@ -95,7 +94,7 @@ def guiMain(args=None):
|
|||||||
mainWindow = Tk()
|
mainWindow = Tk()
|
||||||
self = mainWindow
|
self = mainWindow
|
||||||
|
|
||||||
mainWindow.wm_title("Overworld Shuffle %s (DR %s)" % (ORVersion, ESVersion))
|
mainWindow.wm_title("GwaaKiwi Randomizer %s (OR %s, DR %s)" % (GKVersion, ORVersion, DRVersion))
|
||||||
mainWindow.protocol("WM_DELETE_WINDOW", guiExit) # intercept when user clicks the X
|
mainWindow.protocol("WM_DELETE_WINDOW", guiExit) # intercept when user clicks the X
|
||||||
|
|
||||||
# set program icon
|
# set program icon
|
||||||
|
|||||||
10
ItemList.py
10
ItemList.py
@@ -236,8 +236,10 @@ def get_custom_array_key(item):
|
|||||||
|
|
||||||
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']
|
||||||
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals',
|
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons',
|
||||||
'ganonhunt', 'completionist', 'sanctuary']
|
'triforcehunt', 'trinity', 'crystals',
|
||||||
|
'ganonhunt', 'completionist', 'sanctuary',
|
||||||
|
'bosshunt']
|
||||||
or world.mode[player] not in ['open', 'standard', 'inverted']
|
or world.mode[player] not in ['open', 'standard', 'inverted']
|
||||||
or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']
|
or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']
|
||||||
or world.progressive not in ['on', 'off', 'random']):
|
or world.progressive not in ['on', 'off', 'random']):
|
||||||
@@ -379,7 +381,7 @@ def generate_itempool(world, player):
|
|||||||
|
|
||||||
items = ItemFactory(pool, player)
|
items = ItemFactory(pool, player)
|
||||||
if world.shopsanity[player]:
|
if world.shopsanity[player]:
|
||||||
for potion in ['Green Potion', 'Blue Potion', 'Red Potion']:
|
for potion in ['Green Potion', 'Blue Potion', 'Red Potion', 'Bee']:
|
||||||
p_item = next(item for item in items if item.name == potion and item.player == player)
|
p_item = next(item for item in items if item.name == potion and item.player == player)
|
||||||
p_item.priority = True # don't beemize one of each potion
|
p_item.priority = True # don't beemize one of each potion
|
||||||
|
|
||||||
@@ -1520,7 +1522,7 @@ def make_customizer_pool(world, player):
|
|||||||
guaranteed_items.append('Ocarina (Activated)')
|
guaranteed_items.append('Ocarina (Activated)')
|
||||||
missing_items = []
|
missing_items = []
|
||||||
if world.shopsanity[player]:
|
if world.shopsanity[player]:
|
||||||
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])
|
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion', 'Bee'])
|
||||||
if world.keyshuffle[player] == 'universal':
|
if world.keyshuffle[player] == 'universal':
|
||||||
guaranteed_items.append('Small Key (Universal)')
|
guaranteed_items.append('Small Key (Universal)')
|
||||||
for item in guaranteed_items:
|
for item in guaranteed_items:
|
||||||
|
|||||||
36
Main.py
36
Main.py
@@ -74,6 +74,7 @@ from Rom import (
|
|||||||
)
|
)
|
||||||
from RoomData import create_rooms
|
from RoomData import create_rooms
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
|
from source.classes.BabelFish import BabelFish
|
||||||
from source.classes.CustomSettings import CustomSettings
|
from source.classes.CustomSettings import CustomSettings
|
||||||
from source.enemizer.DamageTables import DamageTable
|
from source.enemizer.DamageTables import DamageTable
|
||||||
from source.enemizer.Enemizer import randomize_enemies
|
from source.enemizer.Enemizer import randomize_enemies
|
||||||
@@ -92,12 +93,7 @@ from UnderworldGlitchRules import (
|
|||||||
create_hmg_entrances_regions,
|
create_hmg_entrances_regions,
|
||||||
)
|
)
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
from Versions import DRVersion, GKVersion, ORVersion
|
||||||
version_number = '1.5.0'
|
|
||||||
version_branch = '-u'
|
|
||||||
__version__ = f'{version_number}{version_branch}'
|
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
|
||||||
|
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
@@ -175,15 +171,13 @@ def main(args, seed=None, fish=None):
|
|||||||
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
|
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
|
||||||
world.finish_init()
|
world.finish_init()
|
||||||
|
|
||||||
from OverworldShuffle import __version__ as ORVersion
|
|
||||||
logger.info(
|
logger.info(
|
||||||
world.fish.translate("cli","cli","app.title") + "\n",
|
world.fish.translate("cli","cli","app.title") + "\n",
|
||||||
ORVersion,
|
GKVersion,
|
||||||
"%s (%s)" % (world.seed, str(args.outputname)) if str(args.outputname).startswith('M') else world.seed,
|
"%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():
|
for k,v in {"GK": GKVersion, "OR": ORVersion, "DR": DRVersion}.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)
|
parsed_names = parse_player_names(args.names, world.players, args.teams)
|
||||||
@@ -195,7 +189,7 @@ def main(args, seed=None, fish=None):
|
|||||||
world.player_names[player].append(name)
|
world.player_names[player].append(name)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
|
|
||||||
outfilebase = f'OR_{args.outputname if args.outputname else world.seed}'
|
outfilebase = f'GK_{args.outputname if args.outputname else world.seed}'
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
||||||
@@ -474,15 +468,13 @@ def export_yaml(args, fish):
|
|||||||
if args.seed and int(args.seed) > 0:
|
if args.seed and int(args.seed) > 0:
|
||||||
world.seed = int(args.seed)
|
world.seed = int(args.seed)
|
||||||
|
|
||||||
from OverworldShuffle import __version__ as ORVersion
|
|
||||||
logger.info(
|
logger.info(
|
||||||
world.fish.translate("cli","cli","app.title") + "\n",
|
world.fish.translate("cli","cli","app.title") + "\n",
|
||||||
ORVersion,
|
GKVersion,
|
||||||
"(%s)" % outfilebase,
|
"(%s)" % outfilebase,
|
||||||
Settings.make_code(world, 1) if world.players == 1 else ''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for k,v in {"DR":__version__,"OR":ORVersion}.items():
|
for k,v in {"GK": GKVersion, "OR": ORVersion, "DR": DRVersion}.items():
|
||||||
logger.info((k + ' Version:').ljust(16) + '%s' % v)
|
logger.info((k + ' Version:').ljust(16) + '%s' % v)
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
@@ -523,12 +515,16 @@ def init_world(args, fish):
|
|||||||
world.keyshuffle = args.keyshuffle.copy()
|
world.keyshuffle = args.keyshuffle.copy()
|
||||||
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
||||||
world.prizeshuffle = args.prizeshuffle.copy()
|
world.prizeshuffle = args.prizeshuffle.copy()
|
||||||
|
world.showloot = args.showloot.copy()
|
||||||
|
world.showmap = args.showmap.copy()
|
||||||
world.bombbag = args.bombbag.copy()
|
world.bombbag = args.bombbag.copy()
|
||||||
world.flute_mode = args.flute_mode.copy()
|
world.flute_mode = args.flute_mode.copy()
|
||||||
world.bow_mode = args.bow_mode.copy()
|
world.bow_mode = args.bow_mode.copy()
|
||||||
world.crystals_ganon_orig = args.crystals_ganon.copy()
|
world.crystals_ganon_orig = args.crystals_ganon.copy()
|
||||||
world.crystals_gt_orig = args.crystals_gt.copy()
|
world.crystals_gt_orig = args.crystals_gt.copy()
|
||||||
world.ganon_item_orig = args.ganon_item.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.owTerrain = args.ow_terrain.copy()
|
||||||
world.owKeepSimilar = args.ow_keepsimilar.copy()
|
world.owKeepSimilar = args.ow_keepsimilar.copy()
|
||||||
world.owWhirlpoolShuffle = args.ow_whirlpool.copy()
|
world.owWhirlpoolShuffle = args.ow_whirlpool.copy()
|
||||||
@@ -576,7 +572,7 @@ def init_world(args, fish):
|
|||||||
world.money_balance = args.money_balance.copy()
|
world.money_balance = args.money_balance.copy()
|
||||||
|
|
||||||
# custom settings - these haven't been promoted to full settings yet
|
# 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 player in range(1, world.players + 1):
|
||||||
for setting in in_progress_settings:
|
for setting in in_progress_settings:
|
||||||
if world.customizer and world.customizer.has_setting(player, setting):
|
if world.customizer and world.customizer.has_setting(player, setting):
|
||||||
@@ -838,12 +834,16 @@ def copy_world(world):
|
|||||||
ret.keyshuffle = world.keyshuffle.copy()
|
ret.keyshuffle = world.keyshuffle.copy()
|
||||||
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
||||||
ret.prizeshuffle = world.prizeshuffle.copy()
|
ret.prizeshuffle = world.prizeshuffle.copy()
|
||||||
|
ret.showloot = world.showloot.copy()
|
||||||
|
ret.showmap = world.showmap.copy()
|
||||||
ret.bombbag = world.bombbag.copy()
|
ret.bombbag = world.bombbag.copy()
|
||||||
ret.flute_mode = world.flute_mode.copy()
|
ret.flute_mode = world.flute_mode.copy()
|
||||||
ret.bow_mode = world.bow_mode.copy()
|
ret.bow_mode = world.bow_mode.copy()
|
||||||
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
||||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.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.ganon_item = world.ganon_item.copy()
|
||||||
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
||||||
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
||||||
@@ -1065,12 +1065,16 @@ def copy_world_premature(world, player, create_flute_exits=True):
|
|||||||
ret.keyshuffle = world.keyshuffle.copy()
|
ret.keyshuffle = world.keyshuffle.copy()
|
||||||
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
||||||
ret.prizeshuffle = world.prizeshuffle.copy()
|
ret.prizeshuffle = world.prizeshuffle.copy()
|
||||||
|
ret.showloot = world.showloot.copy()
|
||||||
|
ret.showmap = world.showmap.copy()
|
||||||
ret.bombbag = world.bombbag.copy()
|
ret.bombbag = world.bombbag.copy()
|
||||||
ret.flute_mode = world.flute_mode.copy()
|
ret.flute_mode = world.flute_mode.copy()
|
||||||
ret.bow_mode = world.bow_mode.copy()
|
ret.bow_mode = world.bow_mode.copy()
|
||||||
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
ret.free_lamp_cone = world.free_lamp_cone.copy()
|
||||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.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.ganon_item = world.ganon_item.copy()
|
||||||
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
||||||
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
||||||
|
|||||||
@@ -27,12 +27,6 @@ from Regions import mark_light_dark_world_regions
|
|||||||
from source.overworld.EntranceShuffle2 import connect_simple
|
from source.overworld.EntranceShuffle2 import connect_simple
|
||||||
from Utils import bidict
|
from Utils import bidict
|
||||||
|
|
||||||
version_number = '0.6.1.7'
|
|
||||||
# branch indicator is intentionally different across branches
|
|
||||||
version_branch = ''
|
|
||||||
|
|
||||||
__version__ = '%s%s' % (version_number, version_branch)
|
|
||||||
|
|
||||||
parallel_links_new = None # needs to be globally available, reset every new generation/player
|
parallel_links_new = None # needs to be globally available, reset every new generation/player
|
||||||
|
|
||||||
def link_overworld(world, player):
|
def link_overworld(world, player):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ from source.overworld.EntranceShuffle2 import (
|
|||||||
link_entrances_new,
|
link_entrances_new,
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = '0.2-dev'
|
PlandoVersion = '0.2-dev'
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
start_time = time.process_time()
|
start_time = time.process_time()
|
||||||
@@ -47,7 +47,7 @@ def main(args):
|
|||||||
|
|
||||||
random.seed(world.seed)
|
random.seed(world.seed)
|
||||||
|
|
||||||
logger.info('ALttP Plandomizer Version %s - Seed: %s\n\n', __version__, args.plando)
|
logger.info('ALttP Plandomizer Version %s - Seed: %s\n\n', PlandoVersion, args.plando)
|
||||||
|
|
||||||
world.difficulty_requirements[1] = difficulties[world.difficulty[1]]
|
world.difficulty_requirements[1] = difficulties[world.difficulty[1]]
|
||||||
|
|
||||||
|
|||||||
131
Rom.py
131
Rom.py
@@ -81,9 +81,10 @@ from Text import (
|
|||||||
text_addresses,
|
text_addresses,
|
||||||
)
|
)
|
||||||
from Utils import int16_as_bytes, int32_as_bytes, local_path, snes_to_pc
|
from Utils import int16_as_bytes, int32_as_bytes, local_path, snes_to_pc
|
||||||
|
from Versions import DRVersion, GKVersion, ORVersion
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '76dc2d00e5dd5b925ad01574b327d364'
|
RANDOMIZERBASEHASH = '2647cc28bca3675152576dd1f5ea0bab'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -201,6 +202,7 @@ class LocalRom(object):
|
|||||||
with open(local_path('data/base2current.bps'), 'rb') as stream:
|
with open(local_path('data/base2current.bps'), 'rb') as stream:
|
||||||
bps.apply.apply_to_bytearrays(bps.io.read_bps(stream), orig_buffer, self.buffer)
|
bps.apply.apply_to_bytearrays(bps.io.read_bps(stream), orig_buffer, self.buffer)
|
||||||
|
|
||||||
|
if not os.getenv("SKIP_BASEROM_CHECK", False):
|
||||||
# verify md5
|
# verify md5
|
||||||
patchedmd5 = hashlib.md5()
|
patchedmd5 = hashlib.md5()
|
||||||
patchedmd5.update(self.buffer)
|
patchedmd5.update(self.buffer)
|
||||||
@@ -1225,7 +1227,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])
|
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']:
|
if world.goal[player] in ['triforcehunt', 'trinity', 'ganonhunt']:
|
||||||
rom.write_bytes(0x180167, int16_as_bytes(world.treasure_hunt_count[player]))
|
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
|
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
|
||||||
|
|
||||||
@@ -1254,7 +1256,6 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) # Set spike cave and MM spike room Cape usage
|
rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) # Set spike cave and MM spike room Cape usage
|
||||||
rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest
|
rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest
|
||||||
rom.write_byte(0x50599, 0x00) # disable below ganon chest
|
rom.write_byte(0x50599, 0x00) # disable below ganon chest
|
||||||
rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest
|
|
||||||
if world.is_pyramid_open(player):
|
if world.is_pyramid_open(player):
|
||||||
rom.initial_sram.pre_open_pyramid_hole()
|
rom.initial_sram.pre_open_pyramid_hole()
|
||||||
rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped(player) else 0x00) # AT/GT swapped
|
rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped(player) else 0x00) # AT/GT swapped
|
||||||
@@ -1300,6 +1301,8 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
# 08: Goal items collected (ie. Triforce Pieces)
|
# 08: Goal items collected (ie. Triforce Pieces)
|
||||||
# 09: Max collection rate
|
# 09: Max collection rate
|
||||||
# 0A: Custom goal
|
# 0A: Custom goal
|
||||||
|
# 0B: Reserved for Bingo
|
||||||
|
# 0C: All bosses (prize bosses + aga1 + aga2)
|
||||||
|
|
||||||
def get_goal_bytes(type):
|
def get_goal_bytes(type):
|
||||||
goal_bytes = []
|
goal_bytes = []
|
||||||
@@ -1354,6 +1357,11 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
ganon_goal += [0x02, world.crystals_needed_for_ganon[player]]
|
ganon_goal += [0x02, world.crystals_needed_for_ganon[player]]
|
||||||
elif world.goal[player] in ['ganonhunt']:
|
elif world.goal[player] in ['ganonhunt']:
|
||||||
ganon_goal += [0x88] # triforce pieces
|
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']:
|
elif world.goal[player] in ['completionist']:
|
||||||
ganon_goal += [0x81, 0x82, 0x06, 0x07, 0x89] # AD and max collection rate
|
ganon_goal += [0x81, 0x82, 0x06, 0x07, 0x89] # AD and max collection rate
|
||||||
else:
|
else:
|
||||||
@@ -1455,6 +1463,60 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']):
|
or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']):
|
||||||
rom.write_byte(0x18003A, 0x01) # show key counts on map pickup
|
rom.write_byte(0x18003A, 0x01) # show key counts on map pickup
|
||||||
|
|
||||||
|
loot_source = 0x09
|
||||||
|
if world.prizeshuffle[player] != 'none':
|
||||||
|
loot_source |= 0x10
|
||||||
|
if world.pottery[player] not in ['none', 'cave']:
|
||||||
|
loot_source |= 0x02
|
||||||
|
if world.dropshuffle[player] != 'none':
|
||||||
|
loot_source |= 0x04
|
||||||
|
rom.write_byte(0x1CFF10, loot_source)
|
||||||
|
|
||||||
|
if world.showloot[player] == 'never':
|
||||||
|
rom.write_bytes(0x1CFF08, [0x00, 0x00, 0x00, 0x00])
|
||||||
|
rom.write_byte(0x1CFF11, 0x00)
|
||||||
|
elif world.showloot[player] == 'presence':
|
||||||
|
rom.write_bytes(0x1CFF08, [0x01, 0x00, 0x00, 0x00])
|
||||||
|
rom.write_byte(0x1CFF11, 0x00)
|
||||||
|
elif world.showloot[player] == 'compass':
|
||||||
|
rom.write_bytes(0x1CFF08, [0x01, 0x00, 0x02, 0x00])
|
||||||
|
rom.write_byte(0x1CFF11, 0x01)
|
||||||
|
elif world.showloot[player] == 'always':
|
||||||
|
rom.write_bytes(0x1CFF08, [0x02, 0x00, 0x00, 0x00])
|
||||||
|
rom.write_byte(0x1CFF11, 0x00)
|
||||||
|
|
||||||
|
if world.showmap[player] == 'visited':
|
||||||
|
rom.write_bytes(0x1CFF00, [0x01, 0x00, 0x00, 0x05])
|
||||||
|
elif world.showmap[player] == 'map':
|
||||||
|
rom.write_bytes(0x1CFF00, [0x01, 0x05, 0x00, 0x05])
|
||||||
|
elif world.showmap[player] == 'always':
|
||||||
|
rom.write_bytes(0x1CFF00, [0x05, 0x00, 0x00, 0x00])
|
||||||
|
|
||||||
|
loot_icons = 0x1CF900
|
||||||
|
if world.bombbag[player]:
|
||||||
|
rom.write_byte(loot_icons + 0x52, 0x0B) # bomb bag is major
|
||||||
|
|
||||||
|
triforce_piece_ids = [0x6B, 0x6C]
|
||||||
|
if world.treasure_hunt_count[player] > 20:
|
||||||
|
for triforce_piece_id in triforce_piece_ids:
|
||||||
|
rom.write_byte(loot_icons + triforce_piece_id, 0x04)
|
||||||
|
|
||||||
|
crystal_ids = [0x20, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6]
|
||||||
|
if world.goal[player] in ['ganon', 'dungeons', 'crystals', 'trinity']:
|
||||||
|
crystal_category = 0x0D
|
||||||
|
else:
|
||||||
|
crystal_category = 0x06
|
||||||
|
for crystal_id in crystal_ids:
|
||||||
|
rom.write_byte(loot_icons + crystal_id, crystal_category)
|
||||||
|
|
||||||
|
pendant_ids = [0x37, 0x38, 0x39]
|
||||||
|
if world.goal[player] in ['pedestal', 'dungeons', 'trinity']:
|
||||||
|
pendant_category = 0x0C
|
||||||
|
else:
|
||||||
|
pendant_category = 0x06
|
||||||
|
for pendant_id in pendant_ids:
|
||||||
|
rom.write_byte(loot_icons + pendant_id, pendant_category)
|
||||||
|
|
||||||
# compasses showing dungeon count
|
# compasses showing dungeon count
|
||||||
compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'nearby'] else 0x00
|
compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'nearby'] else 0x00
|
||||||
if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off':
|
if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off':
|
||||||
@@ -1601,12 +1663,28 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
# b - Big Key
|
# b - Big Key
|
||||||
# a - Small 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']
|
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)
|
if world.mapshuffle[player] != 'none' or enable_menu_map_check:
|
||||||
| (0x02 if world.bigkeyshuffle[player] != 'none' else 0x00)
|
dungeon_items_menu |= 0x04
|
||||||
| (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
|
if world.compassshuffle[player] != 'none':
|
||||||
| (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon
|
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):
|
def get_reveal_bytes(itemName):
|
||||||
if world.prizeshuffle[player] != 'wild':
|
if world.prizeshuffle[player] != 'wild':
|
||||||
@@ -1815,30 +1893,29 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
|||||||
|
|
||||||
# set rom name
|
# set rom name
|
||||||
# 21 bytes
|
# 21 bytes
|
||||||
from Main import __version__
|
|
||||||
from OverworldShuffle import __version__ as ORVersion
|
|
||||||
if rom_header:
|
if rom_header:
|
||||||
if len(rom_header) > 21:
|
if len(rom_header) > 21:
|
||||||
raise Exception('ROM header too long. Max 21 bytes, found %d bytes.' % len(rom_header))
|
raise Exception('ROM header too long. Max 21 bytes, found %d bytes.' % len(rom_header))
|
||||||
elif '|' in rom_header:
|
|
||||||
gen, seedstring = rom_header.split('|', 1)
|
|
||||||
gen = f'{gen:<3}'
|
|
||||||
seedstring = f'{int(seedstring):09}' if seedstring.isdigit() else seedstring[:9]
|
|
||||||
rom.name = bytearray(f'OR{gen}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
|
||||||
elif len(rom_header) <= 9:
|
|
||||||
seedstring = f'{int(rom_header):09}' if rom_header.isdigit() else rom_header
|
|
||||||
rom.name = bytearray(f'OR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
|
||||||
else:
|
|
||||||
rom.name = bytearray(rom_header, 'utf8')[:21]
|
|
||||||
else:
|
|
||||||
seedstring = f'{world.seed:09}' if isinstance(world.seed, int) else world.seed
|
|
||||||
rom.name = bytearray(f'OR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21]
|
|
||||||
|
|
||||||
|
if world.players > 1 and len(rom_header) <= 12:
|
||||||
|
rom.name = bytearray(f"GK_{team + 1}_{player}_{rom_header}", 'utf8')
|
||||||
|
elif len(rom_header) <= 18:
|
||||||
|
rom.name = bytearray(f"GK_{rom_header}", 'utf8')
|
||||||
|
else:
|
||||||
|
rom.name = bytearray(rom_header, 'utf8')
|
||||||
|
else:
|
||||||
|
if world.players > 1:
|
||||||
|
rom.name = bytearray(f'GK_{team + 1}_{player}_{world.seed}', 'utf8')
|
||||||
|
else:
|
||||||
|
rom.name = bytearray(f'GK_{world.seed}', 'utf8')
|
||||||
|
|
||||||
|
rom.name = rom.name[:21]
|
||||||
rom.name.extend([0] * (21 - len(rom.name)))
|
rom.name.extend([0] * (21 - len(rom.name)))
|
||||||
rom.write_bytes(0x7FC0, rom.name)
|
rom.write_bytes(0x7FC0, rom.name)
|
||||||
|
|
||||||
rom.write_bytes(0x138010, bytearray(__version__, 'utf8'))
|
rom.write_bytes(0x138010, bytearray(DRVersion, 'utf8'))
|
||||||
rom.write_bytes(0x150010, bytearray(ORVersion, 'utf8'))
|
rom.write_bytes(0x150010, bytearray(ORVersion, 'utf8'))
|
||||||
|
rom.write_bytes(0x1CEEF0, bytearray(GKVersion, 'utf8'))
|
||||||
|
|
||||||
# set player names
|
# set player names
|
||||||
for p in range(1, min(world.players, 255) + 1):
|
for p in range(1, min(world.players, 255) + 1):
|
||||||
@@ -2712,6 +2789,12 @@ 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])
|
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':
|
elif world.goal[player] == 'ganonhunt':
|
||||||
tt['sign_ganon'] = 'Go find the Triforce pieces to beat Ganon'
|
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':
|
elif world.goal[player] == 'completionist':
|
||||||
tt['sign_ganon'] = 'Ganon only respects those who have done everything'
|
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'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)]
|
||||||
|
|||||||
9
Rules.py
9
Rules.py
@@ -90,6 +90,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))
|
add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||||
elif world.goal[player] == 'ganonhunt':
|
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]))
|
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':
|
elif world.goal[player] == 'completionist':
|
||||||
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
|
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
|
||||||
|
|
||||||
|
|||||||
4
Text.py
4
Text.py
@@ -1781,7 +1781,7 @@ class TextTable(object):
|
|||||||
text['mastersword_pedestal_translated'] = CompressedTextMapper.convert("A test of strength: If you have 3 pendants, I'm yours.")
|
text['mastersword_pedestal_translated'] = CompressedTextMapper.convert("A test of strength: If you have 3 pendants, I'm yours.")
|
||||||
text['telepathic_tile_spectacle_rock'] = CompressedTextMapper.convert("{NOBORDER}\n{NOBORDER}\nUse the Mirror, or the Hookshot and Hammer, to get to Tower of Hera!")
|
text['telepathic_tile_spectacle_rock'] = CompressedTextMapper.convert("{NOBORDER}\n{NOBORDER}\nUse the Mirror, or the Hookshot and Hammer, to get to Tower of Hera!")
|
||||||
text['telepathic_tile_swamp_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nDrain the floodgate to raise the water here!")
|
text['telepathic_tile_swamp_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nDrain the floodgate to raise the water here!")
|
||||||
text['telepathic_tile_thieves_town_upstairs'] = CompressedTextMapper.convert("{NOBORDER}\nBlind hate's bright light.")
|
text['telepathic_tile_thieves_town_upstairs'] = CompressedTextMapper.convert("{NOBORDER}\nBlind hates bright light.")
|
||||||
text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!")
|
text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!")
|
||||||
text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^")
|
text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^")
|
||||||
text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.")
|
text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.")
|
||||||
@@ -2015,7 +2015,7 @@ class TextTable(object):
|
|||||||
text['thief_desert_rupee_cave'] = CompressedTextMapper.convert("So you, like, busted down my door, and are being a jerk by talking to me? Normally I would be angry and make you pay for it, but I bet you're just going to break all my pots and steal my 50 rupees.")
|
text['thief_desert_rupee_cave'] = CompressedTextMapper.convert("So you, like, busted down my door, and are being a jerk by talking to me? Normally I would be angry and make you pay for it, but I bet you're just going to break all my pots and steal my 50 rupees.")
|
||||||
text['thief_ice_rupee_cave'] = CompressedTextMapper.convert("I'm a rupee pot farmer. One day I will take over the world with my skillz. Have you met my brother in the desert? He's way richer than I am.")
|
text['thief_ice_rupee_cave'] = CompressedTextMapper.convert("I'm a rupee pot farmer. One day I will take over the world with my skillz. Have you met my brother in the desert? He's way richer than I am.")
|
||||||
text['telepathic_tile_south_east_darkworld_cave'] = CompressedTextMapper.convert("~~ dev cave ~~\n no farming\n required")
|
text['telepathic_tile_south_east_darkworld_cave'] = CompressedTextMapper.convert("~~ dev cave ~~\n no farming\n required")
|
||||||
text['cukeman'] = CompressedTextMapper.convert("Did you hear that Veetorp beat ajneb174 in a 1 on 1 race at AGDQ?")
|
text['cukeman'] = CompressedTextMapper.convert("Trans rights!")
|
||||||
text['cukeman_2'] = CompressedTextMapper.convert("You found Shabadoo, huh?\nNiiiiice.")
|
text['cukeman_2'] = CompressedTextMapper.convert("You found Shabadoo, huh?\nNiiiiice.")
|
||||||
text['potion_shop_no_cash'] = CompressedTextMapper.convert("Yo! I'm not running a charity here.")
|
text['potion_shop_no_cash'] = CompressedTextMapper.convert("Yo! I'm not running a charity here.")
|
||||||
text['kakariko_powdered_chicken'] = CompressedTextMapper.convert("Smallhacker…\n\n\nWas hiding, you found me!\n\n\nOkay, you can leave now.")
|
text['kakariko_powdered_chicken'] = CompressedTextMapper.convert("Smallhacker…\n\n\nWas hiding, you found me!\n\n\nOkay, you can leave now.")
|
||||||
|
|||||||
28
Utils.py
28
Utils.py
@@ -8,6 +8,7 @@ import urllib.parse
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from hashlib import md5
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from math import factorial
|
from math import factorial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -102,31 +103,13 @@ def close_console():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def make_new_base2current(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', new_rom='working.sfc'):
|
def get_new_romhash(new_rom='working.sfc'):
|
||||||
import hashlib
|
|
||||||
import json
|
|
||||||
from collections import OrderedDict
|
|
||||||
with open(old_rom, 'rb') as stream:
|
|
||||||
old_rom_data = bytearray(stream.read())
|
|
||||||
with open(new_rom, 'rb') as stream:
|
with open(new_rom, 'rb') as stream:
|
||||||
new_rom_data = bytearray(stream.read())
|
new_rom_data = bytearray(stream.read())
|
||||||
# extend to 2 mb
|
|
||||||
old_rom_data.extend(bytearray([0x00] * (2097152 - len(old_rom_data))))
|
|
||||||
|
|
||||||
out_data = OrderedDict()
|
basemd5 = md5()
|
||||||
for idx, old in enumerate(old_rom_data):
|
|
||||||
new = new_rom_data[idx]
|
|
||||||
if old != new:
|
|
||||||
out_data[idx] = [int(new)]
|
|
||||||
for offset in reversed(list(out_data.keys())):
|
|
||||||
if offset - 1 in out_data:
|
|
||||||
out_data[offset-1].extend(out_data.pop(offset))
|
|
||||||
with open('data/base2current.json', 'wt') as outfile:
|
|
||||||
json.dump([{key: value} for key, value in out_data.items()], outfile, separators=(",", ":"))
|
|
||||||
|
|
||||||
basemd5 = hashlib.md5()
|
|
||||||
basemd5.update(new_rom_data)
|
basemd5.update(new_rom_data)
|
||||||
return "New Rom Hash: " + basemd5.hexdigest()
|
return basemd5.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def kth_combination(k, l, r):
|
def kth_combination(k, l, r):
|
||||||
@@ -787,12 +770,11 @@ class bidict(dict):
|
|||||||
class HexInt(int): pass
|
class HexInt(int): pass
|
||||||
|
|
||||||
def hex_representer(dumper, data):
|
def hex_representer(dumper, data):
|
||||||
import yaml
|
|
||||||
return yaml.ScalarNode('tag:yaml.org,2002:int', f"{data:#0{4}x}")
|
return yaml.ScalarNode('tag:yaml.org,2002:int', f"{data:#0{4}x}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(make_new_base2current())
|
print("New Rom Hash:", get_new_romhash())
|
||||||
# read_entrance_data(old_rom=sys.argv[1])
|
# read_entrance_data(old_rom=sys.argv[1])
|
||||||
# room_palette_data(old_rom=sys.argv[1])
|
# room_palette_data(old_rom=sys.argv[1])
|
||||||
# extract_data_from_us_rom(sys.argv[1])
|
# extract_data_from_us_rom(sys.argv[1])
|
||||||
|
|||||||
3
Versions.py
Normal file
3
Versions.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
GKVersion = '1.0.0'
|
||||||
|
ORVersion = '0.6.1.7'
|
||||||
|
DRVersion = '1.5.0-u'
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from OverworldShuffle import __version__ as OWVersion
|
|
||||||
|
|
||||||
with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f:
|
|
||||||
f.write(OWVersion)
|
|
||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "alttpr-python"
|
name = "alttpr-python"
|
||||||
version = "0.2.0"
|
version = "1.0.0"
|
||||||
description = "Python ALttP Randomizer"
|
description = "Python ALttP Randomizer"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"trinity",
|
"trinity",
|
||||||
"crystals",
|
"crystals",
|
||||||
"ganonhunt",
|
"ganonhunt",
|
||||||
|
"bosshunt",
|
||||||
"completionist",
|
"completionist",
|
||||||
"sanctuary"
|
"sanctuary"
|
||||||
]
|
]
|
||||||
@@ -299,6 +300,27 @@
|
|||||||
"random"
|
"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": {
|
"crystals_gt": {
|
||||||
"choices": [
|
"choices": [
|
||||||
"7",
|
"7",
|
||||||
@@ -431,6 +453,21 @@
|
|||||||
"wild"
|
"wild"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"showloot": {
|
||||||
|
"choices": [
|
||||||
|
"never",
|
||||||
|
"presence",
|
||||||
|
"compass",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"showmap": {
|
||||||
|
"choices": [
|
||||||
|
"visited",
|
||||||
|
"map",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
},
|
||||||
"keysanity": {
|
"keysanity": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"cli": {
|
"cli": {
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"app.title": "ALttP Overworld Randomizer Version %s : --seed %s --code %s",
|
"app.title": "ALttP GwaaKiwi Randomizer Version %s : --seed %s",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"seed": "Seed",
|
"seed": "Seed",
|
||||||
"player": "Player",
|
"player": "Player",
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from OverworldShuffle import __version__
|
|
||||||
|
|
||||||
OWR_VERSION = __version__
|
|
||||||
|
|
||||||
def write_appversion():
|
|
||||||
APP_VERSION = OWR_VERSION
|
|
||||||
if "-" in APP_VERSION:
|
|
||||||
APP_VERSION = APP_VERSION[:APP_VERSION.find("-")]
|
|
||||||
APP_VERSION_FILE = os.path.join(".","resources","app","meta","manifests","app_version.txt")
|
|
||||||
with open(APP_VERSION_FILE,"w") as f:
|
|
||||||
f.seek(0)
|
|
||||||
f.truncate()
|
|
||||||
f.write(APP_VERSION)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
write_appversion()
|
|
||||||
@@ -9,27 +9,21 @@ except ModuleNotFoundError as e:
|
|||||||
pass
|
pass
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from Main import __version__
|
from Versions import DRVersion, GKVersion, ORVersion
|
||||||
|
|
||||||
DR_VERSION = __version__
|
|
||||||
|
|
||||||
from OverworldShuffle import __version__
|
|
||||||
|
|
||||||
OWR_VERSION = __version__
|
|
||||||
|
|
||||||
PROJECT_NAME = "ALttP Overworld Randomizer"
|
|
||||||
|
|
||||||
def diagpad(str):
|
def diagpad(str):
|
||||||
return str.ljust(len(f"{PROJECT_NAME} Version") + 5,'.')
|
return str.ljust(40, '.')
|
||||||
|
|
||||||
def output():
|
def output():
|
||||||
lines = [
|
lines = [
|
||||||
f"{PROJECT_NAME} Diagnostics",
|
"ALttP GwaaKiwi Randomizer Diagnostics",
|
||||||
"=================================",
|
"=====================================",
|
||||||
diagpad("UTC Time") + str(datetime.datetime.now(datetime.UTC))[:19],
|
diagpad("UTC Time") + str(datetime.datetime.now(datetime.UTC))[:19],
|
||||||
diagpad("ALttP Door Randomizer Version") + DR_VERSION,
|
diagpad("ALttP Door Randomizer Version") + DRVersion,
|
||||||
diagpad(f"{PROJECT_NAME} Version") + OWR_VERSION,
|
diagpad("ALttP Overworld Randomizer Version") + ORVersion,
|
||||||
diagpad("Python Version") + platform.python_version()
|
diagpad("ALttP GwaaKiwi Randomizer Version") + GKVersion,
|
||||||
|
diagpad("Python Version") + platform.python_version(),
|
||||||
]
|
]
|
||||||
lines.append(diagpad("OS Version") + "%s %s" % (platform.system(), platform.release()))
|
lines.append(diagpad("OS Version") + "%s %s" % (platform.system(), platform.release()))
|
||||||
if hasattr(sys, "executable"):
|
if hasattr(sys, "executable"):
|
||||||
@@ -43,18 +37,17 @@ def output():
|
|||||||
if hasattr(os, "pathsep"):
|
if hasattr(os, "pathsep"):
|
||||||
lines.append(diagpad("Path Env Separator") + os.pathsep)
|
lines.append(diagpad("Path Env Separator") + os.pathsep)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
lines.append("Packages")
|
lines.append("Packages")
|
||||||
lines.append("--------")
|
lines.append("--------")
|
||||||
'''
|
|
||||||
#this breaks when run from the .exe
|
|
||||||
reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
|
reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
|
||||||
installed_packages = [r.decode() for r in reqs.split()]
|
installed_packages = [r.decode() for r in reqs.split()]
|
||||||
for pkg in installed_packages:
|
for pkg in installed_packages:
|
||||||
pkg = pkg.split("==")
|
pkg = pkg.split("==")
|
||||||
lines.append(diagpad(pkg[0]) + pkg[1])
|
lines.append(diagpad(pkg[0]) + pkg[1])
|
||||||
'''
|
|
||||||
installed_packages = []
|
installed_packages = []
|
||||||
installed_packages = [str(d) for d in pkg_resources.working_set] #this doesn't work from the .exe either, but it doesn't crash the program
|
installed_packages = [str(d) for d in pkg_resources.working_set]
|
||||||
installed_packages.sort()
|
installed_packages.sort()
|
||||||
for pkg in installed_packages:
|
for pkg in installed_packages:
|
||||||
pkg = pkg.split(' ')
|
pkg = pkg.split(' ')
|
||||||
|
|||||||
@@ -471,10 +471,6 @@ vanilla_sheets = [
|
|||||||
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
|
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
|
||||||
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
|
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00),
|
||||||
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x08), (0x5D, 0x49, 0x00, 0x52), (0x55, 0x49, 0x42, 0x43),
|
(0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x08), (0x5D, 0x49, 0x00, 0x52), (0x55, 0x49, 0x42, 0x43),
|
||||||
(0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50),
|
|
||||||
(0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50),
|
|
||||||
(0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x56, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50),
|
|
||||||
(0x61, 0x56, 0x33, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
required_boss_sheets = {EnemySprite.ArmosKnight: 9, EnemySprite.Lanmolas: 11, EnemySprite.Moldorm: 12,
|
required_boss_sheets = {EnemySprite.ArmosKnight: 9, EnemySprite.Lanmolas: 11, EnemySprite.Moldorm: 12,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ from tkinter import (
|
|||||||
|
|
||||||
import source.classes.diags as diagnostics
|
import source.classes.diags as diagnostics
|
||||||
import source.gui.widgets as widgets
|
import source.gui.widgets as widgets
|
||||||
from Main import __version__
|
|
||||||
from source.classes.Empty import Empty
|
from source.classes.Empty import Empty
|
||||||
|
from Versions import DRVersion
|
||||||
|
|
||||||
|
|
||||||
def generation_page(parent,settings):
|
def generation_page(parent,settings):
|
||||||
@@ -167,7 +167,7 @@ def generation_page(parent,settings):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
diag = Tk()
|
diag = Tk()
|
||||||
diag.title("Door Shuffle " + __version__)
|
diag.title("Door Shuffle " + DRVersion)
|
||||||
diag.geometry(str(dims["window"]["width"]) + 'x' + str(dims["window"]["height"]))
|
diag.geometry(str(dims["window"]["width"]) + 'x' + str(dims["window"]["height"]))
|
||||||
text = Text(diag, width=dims["textarea.characters"]["width"], height=dims["textarea.characters"]["height"])
|
text = Text(diag, width=dims["textarea.characters"]["width"], height=dims["textarea.characters"]["height"])
|
||||||
text.pack()
|
text.pack()
|
||||||
|
|||||||
5
uv.lock
generated
5
uv.lock
generated
@@ -11,6 +11,7 @@ resolution-markers = [
|
|||||||
name = "aenum"
|
name = "aenum"
|
||||||
version = "3.1.16"
|
version = "3.1.16"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/09/7a/61ed58e8be9e30c3fe518899cc78c284896d246d51381bab59b5db11e1f3/aenum-3.1.16.tar.gz", hash = "sha256:bfaf9589bdb418ee3a986d85750c7318d9d2839c1b1a1d6fe8fc53ec201cf140", size = 137693, upload-time = "2026-01-12T22:34:38.819Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl", hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z" },
|
{ url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl", hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z" },
|
||||||
]
|
]
|
||||||
@@ -41,8 +42,8 @@ wheels = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alttpoverworldrandomizer"
|
name = "alttpr-python"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aenum" },
|
{ name = "aenum" },
|
||||||
|
|||||||
Reference in New Issue
Block a user