Merge Unstable into EdgeWork
This commit is contained in:
@@ -220,6 +220,7 @@ jobs:
|
||||
body: ${{ steps.release_notes.outputs.body }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||
# upload linux archive asset
|
||||
- name: Upload Linux Archive Asset
|
||||
id: upload-linux-asset
|
||||
@@ -231,6 +232,7 @@ jobs:
|
||||
asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz
|
||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||
# upload macos archive asset
|
||||
- name: Upload MacOS Archive Asset
|
||||
id: upload-macos-asset
|
||||
@@ -242,6 +244,7 @@ jobs:
|
||||
asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz
|
||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||
# upload windows archive asset
|
||||
- name: Upload Windows Archive Asset
|
||||
id: upload-windows-asset
|
||||
@@ -253,3 +256,4 @@ jobs:
|
||||
asset_path: ../deploy/windows/ALttPDoorRandomizer.zip
|
||||
asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip
|
||||
asset_content_type: application/zip
|
||||
if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease')
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.idea
|
||||
.vscode
|
||||
*_Spoiler.txt
|
||||
*.bmbp
|
||||
*.log
|
||||
*_Spoiler.json
|
||||
*.pyc
|
||||
*.sfc
|
||||
*.srm
|
||||
@@ -18,6 +21,9 @@ EnemizerCLI/
|
||||
RaceRom.py
|
||||
upx/
|
||||
weights/
|
||||
/MultiMystery/
|
||||
/Players/
|
||||
/QUsb2Snes/
|
||||
|
||||
resources/user/*
|
||||
!resources/user/.gitkeep
|
||||
|
||||
+47
-1
@@ -1031,6 +1031,44 @@ class Direction(Enum):
|
||||
Down = 5
|
||||
|
||||
|
||||
@unique
|
||||
class Hook(Enum):
|
||||
North = 0
|
||||
West = 1
|
||||
South = 2
|
||||
East = 3
|
||||
Stairs = 4
|
||||
NEdge = 5
|
||||
SEdge = 6
|
||||
WEdge = 7
|
||||
EEdge = 8
|
||||
|
||||
|
||||
hook_dir_map = {
|
||||
Direction.North: Hook.North,
|
||||
Direction.South: Hook.South,
|
||||
Direction.West: Hook.West,
|
||||
Direction.East: Hook.East,
|
||||
}
|
||||
|
||||
edge_map = {
|
||||
Direction.North: Hook.NEdge,
|
||||
Direction.South: Hook.SEdge,
|
||||
Direction.West: Hook.WEdge,
|
||||
Direction.East: Hook.EEdge,
|
||||
}
|
||||
|
||||
|
||||
def hook_from_door(door):
|
||||
if door.type == DoorType.SpiralStairs:
|
||||
return Hook.Stairs
|
||||
if door.type == DoorType.Normal:
|
||||
return hook_dir_map[door.direction]
|
||||
if door.type == DoorType.Open:
|
||||
return edge_map[door.direction]
|
||||
return None
|
||||
|
||||
|
||||
class Polarity:
|
||||
def __init__(self):
|
||||
self.vector = [0, 0, 0]
|
||||
@@ -1306,6 +1344,7 @@ class Sector(object):
|
||||
self.branch_factor = None
|
||||
self.dead_end_cnt = None
|
||||
self.entrance_sector = None
|
||||
self.destination_entrance = False
|
||||
self.equations = None
|
||||
|
||||
def region_set(self):
|
||||
@@ -1327,6 +1366,13 @@ class Sector(object):
|
||||
magnitude[idx] = magnitude[idx] + 1
|
||||
return magnitude
|
||||
|
||||
def hook_magnitude(self):
|
||||
magnitude = [0] * len(Hook)
|
||||
for door in self.outstanding_doors:
|
||||
idx = hook_from_door(door).value
|
||||
magnitude[idx] = magnitude[idx] + 1
|
||||
return magnitude
|
||||
|
||||
def outflow(self):
|
||||
outflow = 0
|
||||
for door in self.outstanding_doors:
|
||||
@@ -1349,7 +1395,7 @@ class Sector(object):
|
||||
self.branch_factor -= cnt_dead - 1
|
||||
for region in self.regions:
|
||||
for ent in region.entrances:
|
||||
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
|
||||
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or ent.parent_region.name == 'Sewer Drop':
|
||||
# same sector as another entrance
|
||||
if region.name not in ['Skull Pot Circle', 'Skull Back Drop', 'Desert East Lobby', 'Desert West Lobby']:
|
||||
self.branch_factor += 1
|
||||
|
||||
@@ -123,6 +123,7 @@ def parse_settings():
|
||||
"accessibility": "items",
|
||||
"algorithm": "balanced",
|
||||
|
||||
# Shuffle Ganon defaults to TRUE
|
||||
"openpyramid": False,
|
||||
"shuffleganon": True,
|
||||
"shuffle": "vanilla",
|
||||
@@ -146,7 +147,9 @@ def parse_settings():
|
||||
"multi": 1,
|
||||
"names": "",
|
||||
|
||||
# Hints default to TRUE
|
||||
"hints": True,
|
||||
"no_hints": False,
|
||||
"disablemusic": False,
|
||||
"quickswap": False,
|
||||
"heartcolor": "red",
|
||||
@@ -156,10 +159,11 @@ def parse_settings():
|
||||
"ow_palettes": "default",
|
||||
"uw_palettes": "default",
|
||||
|
||||
# Spoiler defaults to FALSE
|
||||
# Playthrough defaults to TRUE
|
||||
# ROM defaults to TRUE
|
||||
"create_spoiler": False,
|
||||
"skip_playthrough": False,
|
||||
"calc_playthrough": True,
|
||||
"suppress_rom": False,
|
||||
"create_rom": True,
|
||||
"usestartinventory": False,
|
||||
"custom": False,
|
||||
@@ -309,9 +313,9 @@ def get_args_priority(settings_args, gui_args, cli_args):
|
||||
else:
|
||||
newArgs[key] = args[key]
|
||||
|
||||
newArgs[key] = update_deprecated_args(newArgs[key])
|
||||
else:
|
||||
newArgs[key] = args[key]
|
||||
newArgs[key] = update_deprecated_args(newArgs[key])
|
||||
|
||||
args = newArgs
|
||||
|
||||
|
||||
+22
-22
@@ -409,11 +409,6 @@ def determine_entrance_list(world, player):
|
||||
return entrance_map, potential_entrances, connections
|
||||
|
||||
|
||||
# todo: kill drop exceptions
|
||||
def drop_exception(name):
|
||||
return name in ['Skull Pot Circle', 'Skull Back Drop']
|
||||
|
||||
|
||||
def add_shuffled_entrances(sectors, region_list, entrance_list):
|
||||
for sector in sectors:
|
||||
for region in sector.regions:
|
||||
@@ -431,8 +426,6 @@ def find_enabled_origins(sectors, enabled, entrance_list, entrance_map, key):
|
||||
if key not in entrance_map.keys():
|
||||
key = ' '.join(key.split(' ')[:-1])
|
||||
entrance_map[key].append(region.name)
|
||||
if drop_exception(region.name): # only because they have unique regions
|
||||
entrance_list.append(region.name)
|
||||
|
||||
|
||||
def remove_drop_origins(entrance_list):
|
||||
@@ -504,7 +497,7 @@ def cross_dungeon(world, player):
|
||||
all_sectors = []
|
||||
for key in dungeon_regions.keys():
|
||||
all_sectors.extend(convert_to_sectors(dungeon_regions[key], world, player))
|
||||
dungeon_builders = create_dungeon_builders(all_sectors, world, player)
|
||||
dungeon_builders = create_dungeon_builders(all_sectors, connections_tuple, world, player)
|
||||
for builder in dungeon_builders.values():
|
||||
builder.entrance_list = list(entrances_map[builder.name])
|
||||
dungeon_obj = world.get_dungeon(builder.name, player)
|
||||
@@ -524,26 +517,16 @@ def cross_dungeon(world, player):
|
||||
check_required_paths(paths, world, player)
|
||||
|
||||
hc = world.get_dungeon('Hyrule Castle', player)
|
||||
del hc.dungeon_items[0] # removes map
|
||||
hc.dungeon_items.append(ItemFactory('Compass (Escape)', player))
|
||||
at = world.get_dungeon('Agahnims Tower', player)
|
||||
at.dungeon_items.append(ItemFactory('Compass (Agahnims Tower)', player))
|
||||
gt = world.get_dungeon('Ganons Tower', player)
|
||||
del gt.dungeon_items[0] # removes map
|
||||
at.dungeon_items.append(ItemFactory('Map (Agahnims Tower)', player))
|
||||
|
||||
assign_cross_keys(dungeon_builders, world, player)
|
||||
all_dungeon_items = [y for x in world.dungeons if x.player == player for y in x.all_items]
|
||||
target_items = 34 if world.retro[player] else 63
|
||||
d_items = target_items - len(all_dungeon_items)
|
||||
if d_items > 0:
|
||||
if d_items >= 1: # restore HC map
|
||||
world.get_dungeon('Hyrule Castle', player).dungeon_items.append(ItemFactory('Map (Escape)', player))
|
||||
if d_items >= 2: # restore GT map
|
||||
world.get_dungeon('Ganons Tower', player).dungeon_items.append(ItemFactory('Map (Ganons Tower)', player))
|
||||
if d_items > 2:
|
||||
world.pool_adjustment[player] = d_items - 2
|
||||
elif d_items < 0:
|
||||
world.pool_adjustment[player] = d_items
|
||||
world.pool_adjustment[player] = d_items
|
||||
smooth_door_pairs(world, player)
|
||||
|
||||
# Re-assign dungeon bosses
|
||||
@@ -1127,7 +1110,7 @@ def stateful_door(door, kind):
|
||||
|
||||
|
||||
def random_door_type(door, partner, world, player, type_a, type_b, room_a, room_b):
|
||||
r_kind = random.choices([DoorKind.Normal, DoorKind.Bombable, DoorKind.Dashable], [5, 2, 3], k=1)[0]
|
||||
r_kind = random.choices([DoorKind.Normal, DoorKind.Bombable, DoorKind.Dashable], [15, 4, 6], k=1)[0]
|
||||
if r_kind != DoorKind.Normal:
|
||||
if door.type == DoorType.Normal:
|
||||
add_pair(door, partner, world, player)
|
||||
@@ -1301,7 +1284,7 @@ def check_if_regions_visited(state, check_paths):
|
||||
|
||||
def check_for_pinball_fix(state, bad_region, world, player):
|
||||
pinball_region = world.get_region('Skull Pinball', player)
|
||||
if bad_region.name == 'Skull 2 West Lobby' and state.visited_at_all(pinball_region): #revisit this for entrance shuffle
|
||||
if bad_region.name == 'Skull 2 West Lobby' and state.visited_at_all(pinball_region): # revisit this for entrance shuffle
|
||||
door = world.get_door('Skull Pinball WS', player)
|
||||
room = world.get_room(door.roomIndex, player)
|
||||
if room.doorList[door.doorListPos][1] == DoorKind.Trap:
|
||||
@@ -1319,6 +1302,7 @@ class DROptions(Flag):
|
||||
NoOptions = 0x00
|
||||
Eternal_Mini_Bosses = 0x01 # If on, GT minibosses marked as defeated when they try to spawn a heart
|
||||
Town_Portal = 0x02 # If on, Players will start with mirror scroll
|
||||
Map_Info = 0x04
|
||||
Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required
|
||||
|
||||
# DATA GOES DOWN HERE
|
||||
@@ -2081,3 +2065,19 @@ compass_data = {
|
||||
'Turtle Rock': (0x11F, 0xcb, 0x15e, 0, 0x18),
|
||||
'Ganons Tower': (0x13A, 0xcc, 0x170, 2, 0x1a)
|
||||
}
|
||||
|
||||
# For compass boss indicator
|
||||
boss_indicator = {
|
||||
'Eastern Palace': (0x04, 'Eastern Boss SE'),
|
||||
'Desert Palace': (0x06, 'Desert Boss SW'),
|
||||
'Agahnims Tower': (0x08, 'Tower Agahnim 1 SW'),
|
||||
'Swamp Palace': (0x0a, 'Swamp Boss SW'),
|
||||
'Palace of Darkness': (0x0c, 'PoD Boss SE'),
|
||||
'Misery Mire': (0x0e, 'Mire Boss SW'),
|
||||
'Skull Woods': (0x10, 'Skull Spike Corner SW'),
|
||||
'Ice Palace': (0x12, 'Ice Antechamber NE'),
|
||||
'Tower of Hera': (0x14, 'Hera Boss Down Stairs'),
|
||||
'Thieves Town': (0x16, 'Thieves Boss SE'),
|
||||
'Turtle Rock': (0x18, 'TR Boss SW'),
|
||||
'Ganons Tower': (0x1a, 'GT Agahnim 2 SW')
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Basement Ledge Drop Down', Lgcl),
|
||||
create_door(player, 'PoD Stalfos Basement Warp', Warp),
|
||||
create_door(player, 'PoD Arena Main SW', Nrml).dir(So, 0x2a, Left, High).pos(4),
|
||||
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5).kill(),
|
||||
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5),
|
||||
create_door(player, 'PoD Arena Main NW', Nrml).dir(No, 0x2a, Left, High).small_key().pos(1),
|
||||
create_door(player, 'PoD Arena Main NE', Nrml).dir(No, 0x2a, Right, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'PoD Arena Main Crystal Path', Lgcl),
|
||||
|
||||
+915
-176
File diff suppressed because it is too large
Load Diff
+10
-2
@@ -9,9 +9,10 @@ import shlex
|
||||
import sys
|
||||
|
||||
from source.classes.BabelFish import BabelFish
|
||||
import source.classes.diags as diagnostics
|
||||
|
||||
from CLI import parse_cli, get_args_priority
|
||||
from Main import main, EnemizerError
|
||||
from Main import main, EnemizerError, __version__
|
||||
from Rom import get_sprite_from_name
|
||||
from Utils import is_bundled, close_console
|
||||
from Fill import FillError
|
||||
@@ -19,6 +20,13 @@ from Fill import FillError
|
||||
def start():
|
||||
args = parse_cli(None)
|
||||
|
||||
# print diagnostics
|
||||
# usage: py DungeonRandomizer.py --diags
|
||||
if args.diags:
|
||||
diags = diagnostics.output(__version__)
|
||||
print("\n".join(diags))
|
||||
sys.exit(0)
|
||||
|
||||
if is_bundled() and len(sys.argv) == 1:
|
||||
# for the bundled builds, if we have no arguments, the user
|
||||
# probably wants the gui. Users of the bundled build who want the command line
|
||||
@@ -67,7 +75,7 @@ def start():
|
||||
logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err)
|
||||
seed = random.randint(0, 999999999)
|
||||
for fail in failures:
|
||||
logger.info('%s seed failed with: %s', fail[1], fail[0])
|
||||
logger.info('%s\tseed failed with: %s', fail[1], fail[0])
|
||||
fail_rate = 100 * len(failures) / args.count
|
||||
success_rate = 100 * (args.count - len(failures)) / args.count
|
||||
fail_rate = str(fail_rate).split('.')
|
||||
|
||||
@@ -5,6 +5,8 @@ import sys
|
||||
block_cipher = None
|
||||
console = True
|
||||
|
||||
BINARY_SLUG = "DungeonRandomizer"
|
||||
|
||||
def recurse_for_py_files(names_so_far):
|
||||
returnvalue = []
|
||||
for name in os.listdir(os.path.join(*names_so_far)):
|
||||
@@ -28,7 +30,7 @@ binaries = []
|
||||
#if sys.platform.find("windows"):
|
||||
# binaries.append(("ucrtbase.dll","."))
|
||||
|
||||
a = Analysis(['DungeonRandomizer.py'],
|
||||
a = Analysis([f"./{BINARY_SLUG}.py"],
|
||||
pathex=[],
|
||||
binaries=binaries,
|
||||
datas=[],
|
||||
@@ -56,7 +58,7 @@ exe = EXE(pyz,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='DungeonRandomizer',
|
||||
name=BINARY_SLUG,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
@@ -7,6 +7,7 @@ console = True
|
||||
|
||||
if sys.platform.find("mac") or sys.platform.find("osx"):
|
||||
console = False
|
||||
BINARY_SLUG = "Gui"
|
||||
|
||||
def recurse_for_py_files(names_so_far):
|
||||
returnvalue = []
|
||||
@@ -31,7 +32,7 @@ binaries = []
|
||||
#if sys.platform.find("windows"):
|
||||
# binaries.append(("ucrtbase.dll","."))
|
||||
|
||||
a = Analysis(['DungeonRandomizer.py'],
|
||||
a = Analysis([f"./{BINARY_SLUG}.py"],
|
||||
pathex=[],
|
||||
binaries=binaries,
|
||||
datas=[],
|
||||
@@ -59,7 +60,7 @@ exe = EXE(pyz,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='Gui',
|
||||
name=BINARY_SLUG,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
+4
-3
@@ -1,13 +1,14 @@
|
||||
import queue
|
||||
import os
|
||||
import threading
|
||||
import tkinter as tk
|
||||
|
||||
from Utils import local_path
|
||||
|
||||
def set_icon(window):
|
||||
er16 = tk.PhotoImage(file=local_path('data/ER16.gif'))
|
||||
er32 = tk.PhotoImage(file=local_path('data/ER32.gif'))
|
||||
er48 = tk.PhotoImage(file=local_path('data/ER32.gif'))
|
||||
er16 = tk.PhotoImage(file=local_path(os.path.join("data","ER16.gif")))
|
||||
er32 = tk.PhotoImage(file=local_path(os.path.join("data","ER32.gif")))
|
||||
er48 = tk.PhotoImage(file=local_path(os.path.join("data","ER48.gif")))
|
||||
window.tk.call('wm', 'iconphoto', window._w, er16, er32, er48) # pylint: disable=protected-access
|
||||
|
||||
# Although tkinter is intended to be thread safe, there are many reports of issues
|
||||
|
||||
@@ -62,8 +62,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
|
||||
'Progressive Glove': (True, False, None, 0x61, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'),
|
||||
'Silver Arrows': (True, False, None, 0x58, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'),
|
||||
'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01], None, None, None, None, None, None, None),
|
||||
'Red Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02], None, None, None, None, None, None, None),
|
||||
'Blue Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03], None, None, None, None, None, None, None),
|
||||
'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02], None, None, None, None, None, None, None),
|
||||
'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03], None, None, None, None, None, None, None),
|
||||
'Triforce': (True, False, None, 0x6A, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'),
|
||||
'Power Star': (True, False, None, 0x6B, 'a small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'),
|
||||
'Triforce Piece': (True, False, None, 0x6C, 'a small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce Piece'),
|
||||
@@ -130,6 +130,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
|
||||
'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 'A small key to Agahnim', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Castle Tower'),
|
||||
'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 'A big key to Agahnim', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'),
|
||||
'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 'Now you can find Aga1!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Castle Tower'),
|
||||
'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'),
|
||||
'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 'A small key to darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'),
|
||||
'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 'A big key to darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'),
|
||||
'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 'Now you can find Helmasaur King!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'),
|
||||
|
||||
@@ -19,16 +19,17 @@ from Doors import create_doors
|
||||
from DoorShuffle import link_doors
|
||||
from RoomData import create_rooms
|
||||
from Rules import set_rules
|
||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive, dungeon_regions
|
||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
||||
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||
from Utils import output_path, parse_player_names, print_wiki_doors_by_region, print_wiki_doors_by_room
|
||||
from Utils import output_path, parse_player_names
|
||||
|
||||
__version__ = '0.1.0-dev'
|
||||
__version__ = '0.1.0.0-u'
|
||||
|
||||
class EnemizerError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def main(args, seed=None, fish=None):
|
||||
if args.outputpath:
|
||||
os.makedirs(args.outputpath, exist_ok=True)
|
||||
@@ -186,20 +187,18 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
rom_names = []
|
||||
jsonout = {}
|
||||
enemized = False
|
||||
if not args.suppress_rom:
|
||||
logger.info(world.fish.translate("cli","cli","patching.rom"))
|
||||
for team in range(world.teams):
|
||||
for player in range(1, world.players + 1):
|
||||
sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit'
|
||||
enemized = False
|
||||
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none'
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
or args.shufflepots[player] or sprite_random_on_hit)
|
||||
|
||||
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
|
||||
|
||||
patch_rom(world, rom, player, team, use_enemizer)
|
||||
|
||||
if use_enemizer and (args.enemizercli or not args.jsonout):
|
||||
if args.rom and not(os.path.isfile(args.rom)):
|
||||
raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom)
|
||||
@@ -242,13 +241,44 @@ def main(args, seed=None, fish=None):
|
||||
outfilepname += f'_P{player}'
|
||||
if world.players > 1 or world.teams > 1:
|
||||
outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" if world.player_names[player][team] != 'Player %d' % player else ''
|
||||
outfilesuffix = ('_%s_%s-%s-%s-%s%s_%s_%s-%s%s%s%s%s' % (world.logic[player], world.difficulty[player], world.difficulty_adjustments[player],
|
||||
world.mode[player], world.goal[player],
|
||||
"" if world.timer in ['none', 'display'] else "-" + world.timer,
|
||||
world.shuffle[player], world.doorShuffle[player], world.algorithm, mcsb_name,
|
||||
"-retro" if world.retro[player] else "",
|
||||
"-prog_" + world.progressive if world.progressive in ['off', 'random'] else "",
|
||||
"-nohints" if not world.hints[player] else "")) if not args.outputname else ''
|
||||
outfilestuffs = {
|
||||
"logic": world.logic[player], # 0
|
||||
"difficulty": world.difficulty[player], # 1
|
||||
"difficulty_adjustments": world.difficulty_adjustments[player], # 2
|
||||
"mode": world.mode[player], # 3
|
||||
"goal": world.goal[player], # 4
|
||||
"timer": str(world.timer), # 5
|
||||
"shuffle": world.shuffle[player], # 6
|
||||
"doorShuffle": world.doorShuffle[player], # 7
|
||||
"algorithm": world.algorithm, # 8
|
||||
"mscb": mcsb_name, # 9
|
||||
"retro": world.retro[player], # A
|
||||
"progressive": world.progressive, # B
|
||||
"hints": 'True' if world.hints[player] else 'False' # C
|
||||
}
|
||||
# 0 1 2 3 4 5 6 7 8 9 A B C
|
||||
outfilesuffix = ('_%s_%s-%s-%s-%s%s_%s_%s-%s%s%s%s%s' % (
|
||||
# 0 1 2 3 4 5 6 7 8 9 A B C
|
||||
# _noglitches_normal-normal-open-ganon-ohko_simple_basic-balanced-keysanity-retro-prog_swords-nohints
|
||||
# _noglitches_normal-normal-open-ganon _simple_basic-balanced-keysanity-retro
|
||||
# _noglitches_normal-normal-open-ganon _simple_basic-balanced-keysanity -prog_swords
|
||||
# _noglitches_normal-normal-open-ganon _simple_basic-balanced-keysanity -nohints
|
||||
outfilestuffs["logic"], # 0
|
||||
|
||||
outfilestuffs["difficulty"], # 1
|
||||
outfilestuffs["difficulty_adjustments"], # 2
|
||||
outfilestuffs["mode"], # 3
|
||||
outfilestuffs["goal"], # 4
|
||||
"" if outfilestuffs["timer"] in ['False', 'none', 'display'] else "-" + outfilestuffs["timer"], # 5
|
||||
|
||||
outfilestuffs["shuffle"], # 6
|
||||
outfilestuffs["doorShuffle"], # 7
|
||||
outfilestuffs["algorithm"], # 8
|
||||
outfilestuffs["mscb"], # 9
|
||||
|
||||
"-retro" if outfilestuffs["retro"] == "True" else "", # A
|
||||
"-prog_" + outfilestuffs["progressive"] if outfilestuffs["progressive"] in ['off', 'random'] else "", # B
|
||||
"-nohints" if not outfilestuffs["hints"] == "True" else "")) if not args.outputname else '' # C
|
||||
rom.write_to_file(output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.sfc'))
|
||||
|
||||
if world.players > 1:
|
||||
@@ -272,7 +302,11 @@ def main(args, seed=None, fish=None):
|
||||
print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()}))
|
||||
elif args.create_spoiler:
|
||||
logger.info(world.fish.translate("cli","cli","patching.spoiler"))
|
||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||
if args.jsonout:
|
||||
with open(output_path('%s_Spoiler.json' % outfilebase), 'w') as outfile:
|
||||
outfile.write(world.spoiler.to_json())
|
||||
else:
|
||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||
|
||||
YES = world.fish.translate("cli","cli","yes")
|
||||
NO = world.fish.translate("cli","cli","no")
|
||||
@@ -282,6 +316,7 @@ def main(args, seed=None, fish=None):
|
||||
logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO))
|
||||
logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO))
|
||||
logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO))
|
||||
logger.info(world.fish.translate("cli","cli","used.enemizer") % (YES if enemized else NO))
|
||||
logger.info(world.fish.translate("cli","cli","seed") + ": %d", world.seed)
|
||||
logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start)
|
||||
|
||||
|
||||
+6
-1
@@ -7,6 +7,7 @@ import re
|
||||
|
||||
from DungeonRandomizer import parse_cli
|
||||
from Main import main as DRMain
|
||||
from source.classes.BabelFish import BabelFish
|
||||
|
||||
def parse_yaml(txt):
|
||||
def strip(s):
|
||||
@@ -100,7 +101,7 @@ def main():
|
||||
loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[erargs.loglevel]
|
||||
logging.basicConfig(format='%(message)s', level=loglevel)
|
||||
|
||||
DRMain(erargs, seed)
|
||||
DRMain(erargs, seed, BabelFish())
|
||||
|
||||
def get_weights(path):
|
||||
try:
|
||||
@@ -150,6 +151,10 @@ def roll_settings(weights):
|
||||
ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
|
||||
ret.experimental = get_choice('experimental') == 'on'
|
||||
|
||||
ret.dungeon_counters = get_choice('dungeon_counters')
|
||||
if ret.dungeon_counters == 'default':
|
||||
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
|
||||
|
||||
goal = get_choice('goals')
|
||||
ret.goal = {'ganon': 'ganon',
|
||||
'fast_ganon': 'crystals',
|
||||
|
||||
+6
-4
@@ -1,6 +1,8 @@
|
||||
# Features
|
||||
# New Features
|
||||
|
||||
## Door Randomizer
|
||||
* None yet
|
||||
|
||||
* Native GUI executables
|
||||
* Native Dungeon Randomizer CLI executables
|
||||
# Bug Fixes
|
||||
|
||||
* Crossed Dungeon generation improvements
|
||||
* Fix for Animated Tiles in crossed dungeon
|
||||
@@ -10,7 +10,7 @@ import sys
|
||||
import subprocess
|
||||
|
||||
from BaseClasses import CollectionState, ShopType, Region, Location, DoorType
|
||||
from DoorShuffle import compass_data, DROptions
|
||||
from DoorShuffle import compass_data, DROptions, boss_indicator
|
||||
from Dungeons import dungeon_music_addresses
|
||||
from Regions import location_table
|
||||
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
|
||||
@@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = '5e01caffabb4509a0987ef2f2f0bcd56'
|
||||
RANDOMIZERBASEHASH = '6042de1d3a63417efe4397b69c626b88'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -162,7 +162,7 @@ def read_rom(stream):
|
||||
|
||||
def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, random_sprite_on_hit):
|
||||
baserom_path = os.path.abspath(baserom_path)
|
||||
basepatch_path = os.path.abspath(local_path('data/base2current.json'))
|
||||
basepatch_path = os.path.abspath(local_path(os.path.join("data","base2current.json")))
|
||||
enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json")
|
||||
randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json'))
|
||||
options_path = os.path.abspath(output_path('enemizer_options.json'))
|
||||
@@ -305,7 +305,7 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, r
|
||||
_sprite_table = {}
|
||||
def _populate_sprite_table():
|
||||
if not _sprite_table:
|
||||
for dir in [local_path('data/sprites/official'), local_path('data/sprites/unofficial')]:
|
||||
for dir in [local_path(os.path.join("data","sprites","official")), local_path(os.path.join("data","sprites","unofficial"))]:
|
||||
for file in os.listdir(dir):
|
||||
filepath = os.path.join(dir, file)
|
||||
if not os.path.isfile(filepath):
|
||||
@@ -387,7 +387,7 @@ class Sprite(object):
|
||||
|
||||
@staticmethod
|
||||
def default_link_sprite():
|
||||
return Sprite(local_path('data/default.zspr'))
|
||||
return get_sprite_from_name('Link')
|
||||
|
||||
def decode8(self, pos):
|
||||
arr = [[0 for _ in range(8)] for _ in range(8)]
|
||||
@@ -594,10 +594,22 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
if world.mode[player] == 'inverted':
|
||||
patch_shuffled_dark_sanc(world, rom, player)
|
||||
|
||||
# setup dr option flags based on experimental, etc.
|
||||
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
||||
if world.experimental[player]:
|
||||
dr_flags |= DROptions.Map_Info
|
||||
|
||||
# patch doors
|
||||
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' or not world.experimental[player] else DROptions.Town_Portal
|
||||
if world.doorShuffle[player] == 'crossed':
|
||||
rom.write_byte(0x139004, 2)
|
||||
for name, layout in world.key_layout[player].items():
|
||||
offset = compass_data[name][4]//2
|
||||
rom.write_byte(0x13f01c+offset, layout.max_chests + layout.max_drops)
|
||||
rom.write_byte(0x13f02a+offset, layout.max_chests)
|
||||
builder = world.dungeon_layouts[player][name]
|
||||
bk_status = 1 if builder.bk_required else 0
|
||||
bk_status = 2 if builder.bk_provided else bk_status
|
||||
rom.write_byte(0x13f038+offset*2, bk_status)
|
||||
rom.write_byte(0x151f1, 2)
|
||||
rom.write_byte(0x15270, 2)
|
||||
rom.write_byte(0x1597b, 2)
|
||||
@@ -622,6 +634,13 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
if builder.pre_open_stonewall:
|
||||
if builder.pre_open_stonewall.name == 'Desert Wall Slide NW':
|
||||
dr_flags |= DROptions.Open_Desert_Wall
|
||||
for name, pair in boss_indicator.items():
|
||||
dungeon_id, boss_door = pair
|
||||
opposite_door = world.get_door(boss_door, player).dest
|
||||
if opposite_door.roomIndex > -1:
|
||||
dungeon_name = opposite_door.entrance.parent_region.dungeon.name
|
||||
dungeon_id = boss_indicator[dungeon_name][0]
|
||||
rom.write_byte(0x13f000+dungeon_id, opposite_door.roomIndex)
|
||||
rom.write_byte(0x139006, dr_flags.value)
|
||||
if dr_flags & DROptions.Town_Portal and world.mode[player] == 'inverted':
|
||||
rom.write_byte(0x139008, 1)
|
||||
@@ -1048,6 +1067,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
'Big Key (Desert Palace)': (0x367, 0x10), 'Compass (Desert Palace)': (0x365, 0x10), 'Map (Desert Palace)': (0x369, 0x10),
|
||||
'Big Key (Tower of Hera)': (0x366, 0x20), 'Compass (Tower of Hera)': (0x364, 0x20), 'Map (Tower of Hera)': (0x368, 0x20),
|
||||
'Big Key (Escape)': (0x367, 0xC0), 'Compass (Escape)': (0x365, 0xC0), 'Map (Escape)': (0x369, 0xC0),
|
||||
'Big Key (Agahnims Tower)': (0x367, 0x08), 'Compass (Agahnims Tower)': (0x365, 0x08), 'Map (Agahnims Tower)': (0x369, 0x08),
|
||||
'Big Key (Palace of Darkness)': (0x367, 0x02), 'Compass (Palace of Darkness)': (0x365, 0x02), 'Map (Palace of Darkness)': (0x369, 0x02),
|
||||
'Big Key (Thieves Town)': (0x366, 0x10), 'Compass (Thieves Town)': (0x364, 0x10), 'Map (Thieves Town)': (0x368, 0x10),
|
||||
'Big Key (Skull Woods)': (0x366, 0x80), 'Compass (Skull Woods)': (0x364, 0x80), 'Map (Skull Woods)': (0x368, 0x80),
|
||||
@@ -1173,8 +1193,8 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
|
||||
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] else 0x00)
|
||||
| (0x02 if world.bigkeyshuffle[player] else 0x00)
|
||||
| (0x04 if world.compassshuffle[player] else 0x00)
|
||||
| (0x08 if world.mapshuffle[player] else 0x00))) # free roaming items in menu
|
||||
| (0x04 if world.mapshuffle[player] else 0x00)
|
||||
| (0x08 if world.compassshuffle[player] else 0x00))) # free roaming items in menu
|
||||
|
||||
# Map reveals
|
||||
reveal_bytes = {
|
||||
@@ -1232,6 +1252,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_bytes(0x180185, [0,0,0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0,0,0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0,0,0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
bow_max, bomb_max, magic_max = 0, 0, 0
|
||||
if world.mode[player] == 'standard':
|
||||
if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']:
|
||||
rom.write_byte(0x18004E, 1) # Escape Fill (arrows)
|
||||
@@ -1239,16 +1260,25 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_bytes(0x180185, [0,0,70]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0,0,10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0,0,10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
bow_max = 70
|
||||
elif uncle_location.item is not None and uncle_location.item.name in ['Bombs (10)']:
|
||||
rom.write_byte(0x18004E, 2) # Escape Fill (bombs)
|
||||
rom.write_bytes(0x180185, [0,50,0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0,3,0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0,3,0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
bomb_max = 50
|
||||
elif uncle_location.item is not None and uncle_location.item.name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']:
|
||||
rom.write_byte(0x18004E, 4) # Escape Fill (magic)
|
||||
rom.write_bytes(0x180185, [0x80,0,0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0x20,0,0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0x20,0,0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
magic_max = 0x80
|
||||
if world.doorShuffle[player] == 'crossed':
|
||||
# Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180185, [max(0x20, magic_max), max(3, bomb_max), max(10, bow_max)])
|
||||
rom.write_bytes(0x180188, [0x20, 3, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0x20, 3, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
|
||||
|
||||
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
||||
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00)
|
||||
|
||||
@@ -35,8 +35,6 @@ def is_bundled():
|
||||
return getattr(sys, 'frozen', False)
|
||||
|
||||
def local_path(path):
|
||||
return path
|
||||
|
||||
if local_path.cached_path is not None:
|
||||
return os.path.join(local_path.cached_path, path)
|
||||
|
||||
@@ -269,34 +267,72 @@ def print_wiki_doors_by_region(d_regions, world, player):
|
||||
f.write(toprint)
|
||||
|
||||
def update_deprecated_args(args):
|
||||
argVars = vars(args)
|
||||
truthy = [ 1, True, "True", "true" ]
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "suppress_rom" in argVars:
|
||||
args.create_rom = args.suppress_rom not in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "create_rom" in argVars:
|
||||
args.suppress_rom = not args.create_rom in truthy
|
||||
if args:
|
||||
argVars = vars(args)
|
||||
truthy = [ 1, True, "True", "true" ]
|
||||
# Hints default to TRUE
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "no_hints" in argVars:
|
||||
src = "no_hints"
|
||||
if isinstance(argVars["hints"],dict):
|
||||
tmp = {}
|
||||
for idx in range(1,len(argVars["hints"]) + 1):
|
||||
tmp[idx] = argVars[src] not in truthy # tmp = !src
|
||||
args.hints = tmp # dest = tmp
|
||||
else:
|
||||
args.hints = args.no_hints not in truthy # dest = !src
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "hints" in argVars:
|
||||
src = "hints"
|
||||
if isinstance(argVars["hints"],dict):
|
||||
tmp = {}
|
||||
for idx in range(1,len(argVars["hints"]) + 1):
|
||||
tmp[idx] = argVars[src] not in truthy # tmp = !src
|
||||
args.no_hints = tmp # dest = tmp
|
||||
else:
|
||||
args.no_hints = args.hints not in truthy # dest = !src
|
||||
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "no_shuffleganon" in argVars:
|
||||
args.shuffleganon = not args.no_shuffleganon in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "shuffleganon" in argVars:
|
||||
args.no_shuffleganon = not args.shuffleganon in truthy
|
||||
# Spoiler defaults to FALSE
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "create_spoiler" in argVars:
|
||||
args.suppress_spoiler = not args.create_spoiler in truthy
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "suppress_spoiler" in argVars:
|
||||
args.create_spoiler = not args.suppress_spoiler in truthy
|
||||
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "skip_playthrough" in argVars:
|
||||
args.calc_playthrough = not args.skip_playthrough in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "calc_playthrough" in argVars:
|
||||
args.skip_playthrough = not args.calc_playthrough in truthy
|
||||
# ROM defaults to TRUE
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "suppress_rom" in argVars:
|
||||
args.create_rom = not args.suppress_rom in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "create_rom" in argVars:
|
||||
args.suppress_rom = not args.create_rom in truthy
|
||||
|
||||
# Shuffle Ganon defaults to TRUE
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "no_shuffleganon" in argVars:
|
||||
args.shuffleganon = not args.no_shuffleganon in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "shuffleganon" in argVars:
|
||||
args.no_shuffleganon = not args.shuffleganon in truthy
|
||||
|
||||
# Playthrough defaults to TRUE
|
||||
# Don't do: Yes
|
||||
# Do: No
|
||||
if "skip_playthrough" in argVars:
|
||||
args.calc_playthrough = not args.skip_playthrough in truthy
|
||||
# Don't do: No
|
||||
# Do: Yes
|
||||
if "calc_playthrough" in argVars:
|
||||
args.skip_playthrough = not args.calc_playthrough in truthy
|
||||
|
||||
return args
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ incsrc keydoors.asm
|
||||
incsrc overrides.asm
|
||||
incsrc edges.asm
|
||||
incsrc math.asm
|
||||
incsrc hudadditions.asm
|
||||
warnpc $279000
|
||||
|
||||
; Data Section
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
org $279500
|
||||
TilesetTable:
|
||||
; 0 1 2 3 4 5 6 7 8 9 a b c d e f --Offset Ruler
|
||||
db $13,$04,$04,$06,$0d,$ff,$08,$05,$06,$07,$07,$07,$0e,$0e,$0b,$ff
|
||||
db $13,$04,$04,$0d,$0d,$0d,$08,$05,$06,$07,$07,$07,$0e,$0e,$0b,$0b
|
||||
db $04,$04,$04,$0d,$0d,$ff,$08,$05,$08,$09,$07,$07,$06,$ff,$0b,$06
|
||||
db $04,$05,$04,$12,$08,$08,$08,$08,$08,$09,$07,$07,$06,$0e,$0b,$0b
|
||||
db $04,$04,$04,$12,$0a,$0a,$08,$ff,$ff,$09,$07,$07,$0e,$0e,$0b,$0b
|
||||
db $04,$04,$04,$12,$08,$01,$09,$09,$09,$09,$07,$0e,$0e,$0e,$0b,$0b
|
||||
db $04,$04,$04,$12,$0a,$0a,$08,$09,$09,$ff,$07,$0e,$0e,$0e,$0b,$ff
|
||||
db $04,$04,$04,$12,$12,$12,$08,$05,$ff,$ff,$ff,$0e,$0e,$0e,$0b,$0b
|
||||
db $04,$04,$04,$12,$12,$12,$ff,$05,$ff,$05,$ff,$0e,$0e,$0e,$0b,$ff
|
||||
db $0c,$0c,$0c,$0c,$ff,$0e,$0e,$0c,$0c,$05,$ff,$0e,$0e,$0e,$0b,$0b
|
||||
db $0c,$0c,$0c,$0c,$0d,$0e,$0e,$05,$05,$05,$05,$0a,$0a,$ff,$0b,$0b
|
||||
db $04,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$05,$05,$05,$0a,$0a,$ff,$0b,$0b
|
||||
db $04,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$05,$05,$ff,$0a,$0a,$ff,$0b,$ff
|
||||
db $04,$0c,$0c,$ff,$ff,$0d,$0d,$ff,$05,$05,$05,$0a,$0a,$ff,$0b,$06
|
||||
db $04,$06,$06,$06,$06,$06,$06,$06,$06,$ff,$06,$06,$ff,$06,$06,$06
|
||||
db $06,$06,$03,$03,$03,$03,$ff,$ff,$06,$06,$06,$06,$ff,$06,$06,$06
|
||||
|
||||
org $279700
|
||||
KeyDoorOffset:
|
||||
; 0 1 2 3 4 5 6 7 8 9 a b c d e f --Offset Ruler
|
||||
@@ -545,3 +565,20 @@ db $58,$50,$30, $98,$50,$70 ; TT Ambush
|
||||
db $58,$50,$30 ; TT Nook
|
||||
MultDivInfo: ; (1placeholder, 1, 2, 3, 4, 5, 6, 10, 20)
|
||||
db $01, $01, $02, $03, $04, $05, $06, $0a, $14
|
||||
|
||||
|
||||
; dungeon tables
|
||||
; HC HC EP DP AT SP PD MM SW IP TH TT TR GT
|
||||
org $27f000
|
||||
CompassBossIndicator:
|
||||
dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
|
||||
TotalKeys: ;27f01c
|
||||
db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08
|
||||
ChestKeys: ;27f02a
|
||||
db $01, $01, $00, $01, $02, $01, $06, $03, $03, $02, $01, $01, $04, $04
|
||||
BigKeyStatus: ;27f038 (status 2 indicate BnC guard)
|
||||
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001
|
||||
DungeonReminderTable: ;27f054
|
||||
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C
|
||||
;27f070
|
||||
|
||||
|
||||
+30
-2
@@ -38,7 +38,7 @@ org $029396 ; <- 11384 - Bank02.asm : 3641 (LDA $01C322, X)
|
||||
jsl StraightStairLayerFix
|
||||
|
||||
; Graphics fix
|
||||
org $02895d
|
||||
org $02895d ; Bank 02 line 1812 (JSL Dungeon_LoadRoom : JSL Dungeon_InitStarTileChr : JSL $00D6F9 : INC $B0)
|
||||
Splicer:
|
||||
jsl GfxFixer
|
||||
lda $b1 : beq .done
|
||||
@@ -46,10 +46,14 @@ rts
|
||||
nop #5
|
||||
.done
|
||||
|
||||
org $00fda4
|
||||
org $00d377 ;Bank 00 line 3185
|
||||
DecompDungAnimatedTiles:
|
||||
org $00fda4 ;Bank 00 line 8882
|
||||
Dungeon_InitStarTileCh:
|
||||
org $00d6ae ;(PC: 56ae)
|
||||
LoadTransAuxGfx:
|
||||
org $00d739 ;
|
||||
LoadTransAuxGfx_Alt:
|
||||
org $00df5a ;(PC: 5f5a)
|
||||
PrepTransAuxGfx:
|
||||
org $0ffd65 ;(PC: 07fd65)
|
||||
@@ -86,7 +90,31 @@ org $2081f2
|
||||
jsl MirrorCheckOverride2
|
||||
org $20825c
|
||||
jsl MirrorCheckOverride2
|
||||
org $07a955 ; <- Bank07.asm : around 6564 (JP is a bit different) (STZ $05FC : STZ $05FD)
|
||||
jsl BlockEraseFix
|
||||
nop #2
|
||||
|
||||
org $02b82a
|
||||
jsl FixShopCode
|
||||
|
||||
org $1ddeea ; <- Bank1D.asm : 286 (JSL Sprite_LoadProperties)
|
||||
jsl VitreousKeyReset
|
||||
|
||||
org $1ed024 ; f5024 sprite_guruguru_bar.asm : 27 (LDA $040C : CMP.b #$12 : INY #2
|
||||
jsl GuruguruFix : bra .next
|
||||
nop #3
|
||||
.next
|
||||
|
||||
; also rando's hooks.asm line 1360
|
||||
org $a0ee11 ; <- 6FC4C - headsup_display.asm : 836 (LDA $7EF36E : AND.w #$00FF : ADD.w #$0007 : AND.w #$FFF8 : TAX)
|
||||
jsl DrHudOverride
|
||||
org $098638 ; rando's hooks.asm line 2192
|
||||
jsl CountChestKeys
|
||||
org $06D192 ; rando's hooks.asm line 457
|
||||
jsl CountAbsorbedKeys
|
||||
; rando's hooks.asm line 1020
|
||||
org $05FC7E ; <- 2FC7E - sprite_dash_item.asm : 118 (LDA $7EF36F : INC A : STA $7EF36F)
|
||||
jsl CountBonkItem
|
||||
|
||||
; These two, if enabled together, have implications for vanilla BK doors in IP/Hera/Mire
|
||||
; IPBJ is common enough to consider not doing this. Mire is not a concern for vanilla - maybe glitched modes
|
||||
|
||||
+14
-1
@@ -1,11 +1,24 @@
|
||||
GfxFixer:
|
||||
{
|
||||
lda $b1 : bne .stage2
|
||||
lda DRMode : bne +
|
||||
jsl LoadRoomHook ;this is the code we overwrote
|
||||
jsl Dungeon_InitStarTileCh
|
||||
jsl LoadTransAuxGfx_Alt
|
||||
inc $b0
|
||||
rtl
|
||||
+ lda $b1 : bne .stage2
|
||||
jsl LoadRoomHook ; this is the rando version - let's only call this guy once - may fix star tiles and slower loads
|
||||
jsl Dungeon_InitStarTileCh
|
||||
jsl LoadTransAuxGfx
|
||||
jsl Dungeon_LoadCustomTileAttr
|
||||
jsl PrepTransAuxGfx
|
||||
lda DRMode : cmp #$02 : bne + ; only do this in crossed mode
|
||||
ldx $a0 : lda TilesetTable, x
|
||||
cmp $0aa1 : beq + ; already eq no need to decomp
|
||||
sta $0aa1
|
||||
tax : lda $02802e, x : tay
|
||||
jsl DecompDungAnimatedTiles
|
||||
+
|
||||
lda #$09 : sta $17 : sta $0710
|
||||
jsl Palette_SpriteAux3
|
||||
jsl Palette_SpriteAux2
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
DrHudOverride:
|
||||
{
|
||||
jsl.l NewDrawHud
|
||||
jsr HudAdditions
|
||||
rtl
|
||||
}
|
||||
|
||||
HudAdditions:
|
||||
{
|
||||
ldx $040c : cpx #$ff : bne + : rts : +
|
||||
lda DRMode : bne + : rts : +
|
||||
phb : phk : plb
|
||||
lda $7ef364 : and.l $0098c0, x : beq +
|
||||
lda CompassBossIndicator, x : and #$00ff : cmp $a0 : bne +
|
||||
lda $1a : and #$0010 : beq +
|
||||
lda #$345e : sta $7ec790 : bra .next
|
||||
+ lda #$207f : sta $7ec790
|
||||
.next lda DRMode : and #$0002 : bne + : plb : rts : +
|
||||
lda $7ef36d : and #$00ff : beq +
|
||||
lda DungeonReminderTable, x : bra .reminder
|
||||
+ lda #$207f
|
||||
.reminder sta $7ec702
|
||||
+ lda DRFlags : and #$0004 : beq .restore
|
||||
lda $7ef368 : and.l $0098c0, x : beq .restore
|
||||
|
||||
lda #$2811 : sta $7ec740
|
||||
lda $7ef366 : and.l $0098c0, x : bne .check
|
||||
lda BigKeyStatus, x : bne + ; change this, if bk status changes to one byte
|
||||
lda #$2574 : bra ++
|
||||
+ cmp #$0002 : bne +
|
||||
lda #$2420 : bra ++
|
||||
+ lda #$207f : bra ++
|
||||
.check lda #$2826
|
||||
++ sta $7ec742
|
||||
txa : lsr : tax
|
||||
|
||||
lda $7ef4e0, x : jsr ConvertToDisplay : sta $7ec7a2
|
||||
lda #$2830 : sta $7ec7a4
|
||||
lda ChestKeys, x : jsr ConvertToDisplay : sta $7ec7a6
|
||||
|
||||
lda #$2871 : sta $7ec780
|
||||
lda TotalKeys, x
|
||||
sep #$20 : !sub $7ef4b0, x : rep #$20
|
||||
jsr ConvertToDisplay : sta $7ec782
|
||||
|
||||
.restore
|
||||
plb : rts
|
||||
}
|
||||
|
||||
ConvertToDisplay:
|
||||
and #$00ff : cmp #$000a : !blt +
|
||||
!add #$2553 : rts
|
||||
+ !add #$2490 : rts
|
||||
|
||||
|
||||
CountChestKeys:
|
||||
jsl ItemDowngradeFix
|
||||
jsr CountChest
|
||||
rtl
|
||||
|
||||
CountChest:
|
||||
lda !MULTIWORLD_ITEM_PLAYER_ID : bne .end
|
||||
cpy #$24 : beq +
|
||||
cpy #$a0 : !blt .end
|
||||
cpy #$ae : !bge .end
|
||||
pha : phx
|
||||
tya : and #$0f : bne ++
|
||||
inc a
|
||||
++ tax : bra .count
|
||||
+ pha : phx
|
||||
lda $040c : lsr : tax
|
||||
.count
|
||||
lda $7ef4b0, x : inc : sta $7ef4b0, x
|
||||
lda $7ef4e0, x : inc : sta $7ef4e0, x
|
||||
.restore plx : pla
|
||||
.end rts
|
||||
|
||||
CountAbsorbedKeys:
|
||||
jsl IncrementSmallKeysNoPrimary : phx
|
||||
lda $040c : cmp #$ff : beq +
|
||||
lsr : tax
|
||||
lda $7ef4b0, x : inc : sta $7ef4b0, x
|
||||
+ plx : rtl
|
||||
|
||||
CountBonkItem:
|
||||
jsl GiveBonkItem
|
||||
lda $a0 ; check room ID - only bonk keys in 2 rooms so we're just checking the lower byte
|
||||
cmp #115 : bne + ; Desert Bonk Key
|
||||
lda.l BonkKey_Desert
|
||||
bra ++
|
||||
+ : cmp #140 : bne + ; GTower Bonk Key
|
||||
lda.l BonkKey_GTower
|
||||
bra ++
|
||||
+ lda.b #$24 ; default to small key
|
||||
++
|
||||
phy : tay : jsr CountChest : ply
|
||||
rtl
|
||||
+3
-2
@@ -12,8 +12,9 @@
|
||||
CheckIfDoorsOpen: {
|
||||
jsr TrapDoorFixer ; see normal.asm
|
||||
; note we are 16bit mode right now
|
||||
lda $040c : cmp #$00ff : bne .gtg
|
||||
lda $a0 : dec : tax : and #$000f ; hijacked code
|
||||
lda DRMode : beq +
|
||||
lda $040c : cmp #$00ff : bne .gtg
|
||||
+ lda $a0 : dec : tax : and #$000f ; hijacked code
|
||||
sec : rtl ; set carry to indicate normal behavior
|
||||
|
||||
.gtg
|
||||
|
||||
@@ -51,3 +51,26 @@ MirrorCheckOverride:
|
||||
|
||||
MirrorCheckOverride2:
|
||||
lda $7ef353 : and #$02 : rtl
|
||||
|
||||
|
||||
BlockEraseFix:
|
||||
lda $7ef353 : and #$02 : beq +
|
||||
stz $05fc : stz $05fd
|
||||
+ rtl
|
||||
|
||||
FixShopCode:
|
||||
cpx #$300 : !bge +
|
||||
sta $7ef000, x
|
||||
+ rtl
|
||||
|
||||
VitreousKeyReset:
|
||||
lda DRMode : beq +
|
||||
stz $0cba, x
|
||||
+ jsl $0db818 ;restore old code
|
||||
rtl
|
||||
|
||||
GuruguruFix:
|
||||
lda $a0 : cmp #$df : !bge +
|
||||
and #$0f : cmp #$0e : !blt +
|
||||
iny #2
|
||||
+ rtl
|
||||
@@ -7,6 +7,7 @@ RecordStairType: {
|
||||
}
|
||||
|
||||
SpiralWarp: {
|
||||
lda DRMode : beq .abort ; abort if not DR
|
||||
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
|
||||
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
|
||||
cmp #$5f : beq .gtg
|
||||
@@ -66,6 +67,16 @@ SpiralWarp: {
|
||||
ldy #$01 : jsr SetCamera
|
||||
|
||||
stz $045e ; clear the staircase flag
|
||||
|
||||
; animated tiles fix
|
||||
lda DRMode : cmp #$02 : bne + ; only do this in crossed mode
|
||||
ldx $a0 : lda TilesetTable, x
|
||||
cmp $0aa1 : beq + ; already eq no need to decomp
|
||||
sta $0aa1
|
||||
tax : lda $02802e, x : tay
|
||||
jsl DecompDungAnimatedTiles
|
||||
+
|
||||
|
||||
ply : plx : plb ; pull the stuff we pushed
|
||||
lda $a2 : and #$0f ; this is the code we are hijacking
|
||||
rtl
|
||||
|
||||
+4
-1
@@ -3,6 +3,9 @@ import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# Spec file
|
||||
SPEC_FILE = os.path.join("DungeonRandomizer.spec")
|
||||
|
||||
# Destination is current dir
|
||||
DEST_DIRECTORY = '.'
|
||||
|
||||
@@ -16,7 +19,7 @@ if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform.
|
||||
shutil.rmtree("build")
|
||||
|
||||
# Run pyinstaller for DungeonRandomizer
|
||||
subprocess.run(" ".join(["pyinstaller DungeonRandomizer.spec ",
|
||||
subprocess.run(" ".join([f"pyinstaller {SPEC_FILE} ",
|
||||
upx_string,
|
||||
"-y ",
|
||||
"--onefile ",
|
||||
|
||||
+4
-1
@@ -3,6 +3,9 @@ import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# Spec file
|
||||
SPEC_FILE = os.path.join("Gui.spec")
|
||||
|
||||
# Destination is current dir
|
||||
DEST_DIRECTORY = '.'
|
||||
|
||||
@@ -16,7 +19,7 @@ if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform.
|
||||
shutil.rmtree("build")
|
||||
|
||||
# Run pyinstaller for Gui
|
||||
subprocess.run(" ".join(["pyinstaller Gui.spec ",
|
||||
subprocess.run(" ".join([f"pyinstaller {SPEC_FILE} ",
|
||||
upx_string,
|
||||
"-y ",
|
||||
"--onefile ",
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
aioconsole==0.1.15
|
||||
colorama==0.4.3
|
||||
websockets==8.1
|
||||
@@ -1,9 +1,18 @@
|
||||
{
|
||||
"lang": {},
|
||||
"diags": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"create_spoiler": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"suppress_spoiler": {
|
||||
"action": "store_false",
|
||||
"dest": "create_spoiler",
|
||||
"help": "suppress"
|
||||
},
|
||||
"logic": {
|
||||
"choices": [
|
||||
"noglitches",
|
||||
@@ -195,9 +204,14 @@
|
||||
]
|
||||
},
|
||||
"hints": {
|
||||
"action": "store_true",
|
||||
"action": "store_false",
|
||||
"type": "bool"
|
||||
},
|
||||
"no_hints": {
|
||||
"action": "store_true",
|
||||
"dest": "hints",
|
||||
"help": "suppress"
|
||||
},
|
||||
"heartbeep": {
|
||||
"choices": [
|
||||
"normal",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"made.rom": "Patched ROM: %s",
|
||||
"made.playthrough": "Printed Playthrough: %s",
|
||||
"made.spoiler": "Printed Spoiler: %s",
|
||||
"used.enemizer": "Enemized: %s",
|
||||
"done": "Done. Enjoy.",
|
||||
"total.time": "Total Time: %s",
|
||||
"finished.run": "Finished run",
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"randomizer.gameoptions.sprite.unchanged": "(unchanged)",
|
||||
|
||||
|
||||
"randomizer.generation.spoiler": "Create Spoiler Log",
|
||||
"randomizer.generation.createspoiler": "Create Spoiler Log",
|
||||
"randomizer.generation.createrom": "Create Patched ROM",
|
||||
"randomizer.generation.calcplaythrough": "Calculate Playthrough",
|
||||
"randomizer.generation.usestartinventory": "Use Starting Inventory",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"checkboxes": {
|
||||
"spoiler": { "type": "checkbox" },
|
||||
"createspoiler": { "type": "checkbox" },
|
||||
"createrom": { "type": "checkbox" },
|
||||
"calcplaythrough": { "type": "checkbox" },
|
||||
"usestartinventory": { "type": "checkbox" },
|
||||
|
||||
@@ -23,7 +23,7 @@ def prepare_env():
|
||||
|
||||
# get app version
|
||||
APP_VERSION = ""
|
||||
APP_VERSION_FILE = "./resources/app/meta/manifests/app_version.txt"
|
||||
APP_VERSION_FILE = os.path.join(".","resources","app","meta","manifests","app_version.txt")
|
||||
if os.path.isfile(APP_VERSION_FILE):
|
||||
with open(APP_VERSION_FILE,"r") as f:
|
||||
APP_VERSION = f.readlines()[0].strip()
|
||||
|
||||
@@ -6,7 +6,7 @@ from shutil import unpack_archive
|
||||
|
||||
# only do stuff if we don't have a UPX folder
|
||||
|
||||
if not os.path.isdir("./upx"):
|
||||
if not os.path.isdir(os.path.join(".","upx")):
|
||||
# get env vars
|
||||
env = common.prepare_env()
|
||||
# set up download url
|
||||
@@ -25,7 +25,7 @@ if not os.path.isdir("./upx"):
|
||||
|
||||
print("Getting UPX: " + UPX_FILE)
|
||||
|
||||
with open("./" + UPX_FILE,"wb") as upx:
|
||||
with open(os.path.join(".",UPX_FILE),"wb") as upx:
|
||||
UPX_REQ = urllib.request.Request(
|
||||
UPX_URL,
|
||||
data=None
|
||||
@@ -34,9 +34,9 @@ if not os.path.isdir("./upx"):
|
||||
UPX_DATA = UPX_REQ.read()
|
||||
upx.write(UPX_DATA)
|
||||
|
||||
unpack_archive(UPX_FILE,"./")
|
||||
unpack_archive(UPX_FILE,os.path.join("."))
|
||||
|
||||
os.rename("./" + UPX_SLUG,"./upx")
|
||||
os.remove("./" + UPX_FILE)
|
||||
os.rename(os.path.join(".",UPX_SLUG),os.path.join(".","upx"))
|
||||
os.remove(os.path.join(".",UPX_FILE))
|
||||
|
||||
print("UPX should " + ("not " if not os.path.isdir("./upx") else "") + "be available.")
|
||||
print("UPX should " + ("not " if not os.path.isdir(os.path.join(".","upx")) else "") + "be available.")
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import subprocess # do stuff at the shell level
|
||||
import os
|
||||
|
||||
def git_clean():
|
||||
def git_clean(clean_ignored=True, clean_user=False):
|
||||
excludes = [
|
||||
".vscode", # vscode IDE files
|
||||
".idea", # idea IDE files
|
||||
"*.json", # keep JSON files for that one time I just nuked all that I was working on, oops
|
||||
"*app*version.*" # keep appversion files
|
||||
"*app*version.*", # keep appversion files
|
||||
"EnemizerCLI" # keep EnemizerCLI files
|
||||
]
|
||||
|
||||
if not clean_user:
|
||||
excludes.append(os.path.join("resources","user*")) # keep user resources
|
||||
|
||||
excludes = ['--exclude={0}'.format(exclude) for exclude in excludes]
|
||||
|
||||
# d: directories, f: files, x: ignored files
|
||||
switches = "df" + ("x" if clean_ignored else "")
|
||||
|
||||
# clean the git slate
|
||||
subprocess.check_call([
|
||||
"git", # run a git command
|
||||
"clean", # clean command
|
||||
"-dfx", # d: directories, f: files, x: ignored files
|
||||
"-" + switches,
|
||||
*excludes])
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from git_clean import git_clean
|
||||
|
||||
git_clean(clean_user=True)
|
||||
@@ -32,12 +32,13 @@ class SpriteSelector(object):
|
||||
webbrowser.open("http://alttpr.com/sprite_preview")
|
||||
|
||||
def open_unofficial_sprite_dir(_evt):
|
||||
if not os.path.isdir(self.unofficial_sprite_dir):
|
||||
os.makedirs(self.unofficial_sprite_dir)
|
||||
open_file(self.unofficial_sprite_dir)
|
||||
|
||||
# Open SpriteSomething directory for Link sprites
|
||||
def open_spritesomething_listing(_evt):
|
||||
webbrowser.open("https://artheau.github.io/SpriteSomething/?mode=zelda3/link")
|
||||
# webbrowser.open("https://artheau.github.io/SpriteSomething/resources/app/snes/zelda3/link/sprites.html")
|
||||
webbrowser.open("https://artheau.github.io/SpriteSomething/resources/app/snes/zelda3/link/sprites.html")
|
||||
|
||||
official_frametitle = Frame(self.window)
|
||||
official_title_text = Label(official_frametitle, text="Official Sprites")
|
||||
@@ -57,8 +58,8 @@ class SpriteSelector(object):
|
||||
spritesomething_title_link.pack(side=LEFT)
|
||||
spritesomething_title_link.bind("<Button-1>", open_spritesomething_listing)
|
||||
|
||||
self.icon_section(official_frametitle, self.official_sprite_dir+'/*', 'Official sprites not found. Click "Update official sprites" to download them.')
|
||||
self.icon_section(unofficial_frametitle, self.unofficial_sprite_dir+'/*', 'Put sprites in the unofficial sprites folder (see open link above) to have them appear here.')
|
||||
self.icon_section(official_frametitle, os.path.join(self.official_sprite_dir,"*"), 'Official sprites not found. Click "Update official sprites" to download them.')
|
||||
self.icon_section(unofficial_frametitle, os.path.join(self.unofficial_sprite_dir,"*"), 'Put sprites in the unofficial sprites folder (see open link above) to have them appear here.')
|
||||
|
||||
frame = Frame(self.window)
|
||||
frame.pack(side=BOTTOM, fill=X, pady=5)
|
||||
@@ -150,10 +151,10 @@ class SpriteSelector(object):
|
||||
|
||||
try:
|
||||
task.update_status("Determining needed sprites")
|
||||
current_sprites = [os.path.basename(file) for file in glob(self.official_sprite_dir+'/*')]
|
||||
current_sprites = [os.path.basename(file) for file in glob(os.path.join(self.official_sprite_dir,"*"))]
|
||||
official_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path)) for sprite in sprites_arr]
|
||||
needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in official_sprites if filename not in current_sprites]
|
||||
bundled_sprites = [os.path.basename(file) for file in glob(self.local_official_sprite_dir+'/*')]
|
||||
bundled_sprites = [os.path.basename(file) for file in glob(os.path.join(self.unofficial_sprite_dir,"*"))]
|
||||
# todo: eventually use the above list to avoid downloading any sprites that we already have cached in the bundle.
|
||||
|
||||
official_filenames = [filename for (_, filename) in official_sprites]
|
||||
@@ -230,23 +231,23 @@ class SpriteSelector(object):
|
||||
|
||||
@property
|
||||
def official_sprite_dir(self):
|
||||
if is_bundled():
|
||||
return output_path("sprites/official")
|
||||
# if is_bundled():
|
||||
# return output_path(os.path.join("sprites","official"))
|
||||
return self.local_official_sprite_dir
|
||||
|
||||
@property
|
||||
def local_official_sprite_dir(self):
|
||||
return local_path("data/sprites/official")
|
||||
return local_path(os.path.join("data","sprites","official"))
|
||||
|
||||
@property
|
||||
def unofficial_sprite_dir(self):
|
||||
if is_bundled():
|
||||
return output_path("sprites/unofficial")
|
||||
# if is_bundled():
|
||||
# return output_path(os.path.join("sprites","unofficial"))
|
||||
return self.local_unofficial_sprite_dir
|
||||
|
||||
@property
|
||||
def local_unofficial_sprite_dir(self):
|
||||
return local_path("data/sprites/unofficial")
|
||||
return local_path(os.path.join("data","sprites","unofficial"))
|
||||
|
||||
|
||||
def get_image_for_sprite(sprite):
|
||||
|
||||
@@ -101,7 +101,7 @@ SETTINGSTOPROCESS = {
|
||||
"uwpalettes": "uw_palettes"
|
||||
},
|
||||
"generation": {
|
||||
"spoiler": "create_spoiler",
|
||||
"createspoiler": "create_spoiler",
|
||||
"createrom": "create_rom",
|
||||
"calcplaythrough": "calc_playthrough",
|
||||
"usestartinventory": "usestartinventory",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import platform, sys, os, subprocess
|
||||
import pkg_resources
|
||||
from datetime import datetime
|
||||
|
||||
def diagpad(str):
|
||||
return str.ljust(len("ALttP Door Randomizer Version") + 5,'.')
|
||||
|
||||
def output(APP_VERSION):
|
||||
lines = [
|
||||
"ALttP Door Randomizer Diagnostics",
|
||||
"=================================",
|
||||
diagpad("UTC Time") + str(datetime.utcnow())[:19],
|
||||
diagpad("ALttP Door Randomizer Version") + APP_VERSION,
|
||||
diagpad("Python Version") + platform.python_version()
|
||||
]
|
||||
lines.append(diagpad("OS Version") + "%s %s" % (platform.system(), platform.release()))
|
||||
if hasattr(sys, "executable"):
|
||||
lines.append(diagpad("Executable") + sys.executable)
|
||||
lines.append(diagpad("Build Date") + platform.python_build()[1])
|
||||
lines.append(diagpad("Compiler") + platform.python_compiler())
|
||||
if hasattr(sys, "api_version"):
|
||||
lines.append(diagpad("Python API") + str(sys.api_version))
|
||||
if hasattr(os, "sep"):
|
||||
lines.append(diagpad("Filepath Separator") + os.sep)
|
||||
if hasattr(os, "pathsep"):
|
||||
lines.append(diagpad("Path Env Separator") + os.pathsep)
|
||||
lines.append("")
|
||||
lines.append("Packages")
|
||||
lines.append("--------")
|
||||
'''
|
||||
#this breaks when run from the .exe
|
||||
reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
|
||||
installed_packages = [r.decode() for r in reqs.split()]
|
||||
for pkg in installed_packages:
|
||||
pkg = pkg.split("==")
|
||||
lines.append(diagpad(pkg[0]) + pkg[1])
|
||||
'''
|
||||
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.sort()
|
||||
for pkg in installed_packages:
|
||||
pkg = pkg.split(' ')
|
||||
lines.append(diagpad(pkg[0]) + pkg[1])
|
||||
|
||||
return lines
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise AssertionError(f"Called main() on utility library {__file__}")
|
||||
@@ -104,7 +104,7 @@ def adjust_page(top, parent, settings):
|
||||
arg = options[option]
|
||||
setattr(guiargs, arg, self.widgets[option].storageVar.get())
|
||||
guiargs.rom = self.romVar2.get()
|
||||
guiargs.baserom = top.pages["randomizer"].pages["generation"].romVar.get()
|
||||
guiargs.baserom = top.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.get()
|
||||
guiargs.sprite = self.sprite
|
||||
try:
|
||||
adjust(args=guiargs)
|
||||
|
||||
@@ -115,6 +115,7 @@ def bottom_frame(self, parent, args=None):
|
||||
made = {}
|
||||
for k in [ "rom", "playthrough", "spoiler" ]:
|
||||
made[k] = parent.fish.translate("cli","cli","made." + k)
|
||||
made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer")
|
||||
for k in made:
|
||||
v = made[k]
|
||||
pattern = "([\w]+)(:)([\s]+)(.*)"
|
||||
@@ -123,6 +124,7 @@ def bottom_frame(self, parent, args=None):
|
||||
successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n"
|
||||
successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n"
|
||||
successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n"
|
||||
successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n"
|
||||
# FIXME: English
|
||||
successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user