Merge in door dev unstable

This commit is contained in:
aerinon
2020-09-17 15:23:06 -06:00
18 changed files with 107 additions and 47 deletions

View File

@@ -19,11 +19,13 @@ from RoomData import Room
class World(object):
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
self.players = players
self.teams = 1
self.shuffle = shuffle.copy()
self.doorShuffle = doorShuffle.copy()
self.intensity = {}
self.logic = logic.copy()
self.mode = mode.copy()
self.swords = swords.copy()
@@ -1843,6 +1845,7 @@ class Spoiler(object):
'goal': self.world.goal,
'shuffle': self.world.shuffle,
'door_shuffle': self.world.doorShuffle,
'intensity': self.world.intensity,
'item_pool': self.world.difficulty,
'item_functionality': self.world.difficulty_adjustments,
'gt_crystals': self.world.crystals_needed_for_gt,
@@ -1905,6 +1908,7 @@ class Spoiler(object):
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))

3
CLI.py
View File

@@ -90,7 +90,7 @@ def parse_cli(argv, no_defaults=False):
playerargs = parse_cli(shlex.split(getattr(ret,f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
@@ -141,6 +141,7 @@ def parse_settings():
"bigkeyshuffle": False,
"keysanity": False,
"door_shuffle": "basic",
"intensity": 2,
"experimental": False,
"dungeon_counters": "default",

View File

@@ -35,6 +35,12 @@ def link_doors(world, player):
for ent, ext in ladders:
connect_two_way(world, ent, ext, player)
if world.intensity[player] < 2:
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
for entrance, ext in straight_staircases:
connect_two_way(world, entrance, ext, player)
choose_portals(world, player)
if world.doorShuffle[player] == 'vanilla':
@@ -52,18 +58,8 @@ def link_doors(world, player):
connect_one_way(world, ent, ext, player)
vanilla_key_logic(world, player)
elif world.doorShuffle[player] == 'basic':
if not world.experimental[player]:
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
for entrance, ext in straight_staircases:
connect_two_way(world, entrance, ext, player)
within_dungeon(world, player)
elif world.doorShuffle[player] == 'crossed':
if not world.experimental[player]:
for entrance, ext in open_edges:
connect_two_way(world, entrance, ext, player)
for entrance, ext in straight_staircases:
connect_two_way(world, entrance, ext, player)
cross_dungeon(world, player)
else:
logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player])
@@ -1268,8 +1264,8 @@ def reassign_key_doors(builder, world, player):
dp.pair = False
if not found:
world.paired_doors[player].append(PairedDoor(d1.name, d2.name))
change_door_to_small_key(d1, world, player)
change_door_to_small_key(d2, world, player)
change_door_to_small_key(d1, world, player)
change_door_to_small_key(d2, world, player)
world.spoiler.set_door_type(d1.name+' <-> '+d2.name, 'Key Door', player)
logger.debug('Key Door: %s', d1.name+' <-> '+d2.name)
else:
@@ -1565,6 +1561,7 @@ logical_connections = [
('Desert Main Lobby Right Path', 'Desert Right Alcove'),
('Desert Left Alcove Path', 'Desert Main Lobby'),
('Desert Right Alcove Path', 'Desert Main Lobby'),
('Hera Big Chest Hook Path', 'Hera Big Chest Landing'),
('Hera Big Chest Landing Exit', 'Hera 4F'),
('PoD Pit Room Block Path N', 'PoD Pit Room Blocked'),
('PoD Pit Room Block Path S', 'PoD Pit Room'),

View File

@@ -271,6 +271,7 @@ def create_doors(world, player):
create_door(player, 'Hera 4F Down Stairs', Sprl).dir(Dn, 0x27, 0, HTH).ss(S, 0x62, 0xc0),
create_door(player, 'Hera 4F Up Stairs', Sprl).dir(Up, 0x27, 1, HTH).ss(A, 0x6b, 0x2c),
create_door(player, 'Hera 4F Holes', Hole),
create_door(player, 'Hera Big Chest Hook Path', Lgcl),
create_door(player, 'Hera Big Chest Landing Exit', Lgcl),
create_door(player, 'Hera Big Chest Landing Holes', Hole),
create_door(player, 'Hera 5F Down Stairs', Sprl).dir(Dn, 0x17, 1, HTH).ss(A, 0x62, 0x40),

13
Main.py
View File

@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties, fill_prizes
from Utils import output_path, parse_player_names
__version__ = '0.1.0.14-u'
__version__ = '0.1.0-dev'
class EnemizerError(RuntimeError):
pass
@@ -38,7 +38,9 @@ def main(args, seed=None, fish=None):
start = time.perf_counter()
# initialize the world
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
logger = logging.getLogger('')
if seed is None:
random.seed(None)
@@ -60,6 +62,7 @@ def main(args, seed=None, fish=None):
world.enemy_health = args.enemy_health.copy()
world.enemy_damage = args.enemy_damage.copy()
world.beemizer = args.beemizer.copy()
world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
world.experimental = args.experimental.copy()
world.dungeon_counters = args.dungeon_counters.copy()
world.fish = fish
@@ -329,7 +332,9 @@ def main(args, seed=None, fish=None):
def copy_world(world):
# ToDo: Not good yet
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
ret.teams = world.teams
ret.player_names = copy.deepcopy(world.player_names)
ret.remote_items = world.remote_items.copy()
@@ -364,6 +369,8 @@ def copy_world(world):
ret.enemy_health = world.enemy_health.copy()
ret.enemy_damage = world.enemy_damage.copy()
ret.beemizer = world.beemizer.copy()
ret.intensity = world.intensity.copy()
ret.experimental = world.experimental.copy()
for player in range(1, world.players + 1):
if world.mode[player] != 'inverted':

View File

@@ -149,6 +149,7 @@ def roll_settings(weights):
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
door_shuffle = get_choice('door_shuffle')
ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
ret.intensity = get_choice('intensity')
ret.experimental = get_choice('experimental') == 'on'
ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default'
@@ -166,7 +167,7 @@ def roll_settings(weights):
ret.crystals_gt = get_choice('tower_open')
ret.crystals_ganon = get_choice('ganon_open')
ret.crystals_ganon = get_choice('ganon_open')
ret.mode = get_choice('world_state')
if ret.mode == 'retro':

View File

@@ -36,6 +36,16 @@ Doors are shuffled between dungeons as well.
Doors are not shuffled.
## Intensity
#### Level 1
Normal door and spiral staircases are shuffled
#### Level 2
Same as Level 1 plus open edges and straight staircases are shuffled.
#### Level 3 (Coming soon)
Same as Level 2 plus Dungeon Lobbies are shuffled
## Map/Compass/Small Key/Big Key shuffle (aka Keysanity)
These settings allow dungeon specific items to be distributed anywhere in the world and not just in their native dungeon.
@@ -75,3 +85,9 @@ Show the help message and exit.
```
For specifying the door shuffle you want as above. (default: basic)
```
--intensity
```
For specifying the door shuffle intensity level you want as above. (default: 2)

View File

@@ -343,7 +343,7 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'Hera Beetles', 'Tower of Hera', None, ['Hera Beetles Down Stairs', 'Hera Beetles WS', 'Hera Beetles Holes']),
create_dungeon_region(player, 'Hera Startile Corner', 'Tower of Hera', None, ['Hera Startile Corner ES', 'Hera Startile Corner NW', 'Hera Startile Corner Holes']),
create_dungeon_region(player, 'Hera Startile Wide', 'Tower of Hera', None, ['Hera Startile Wide SW', 'Hera Startile Wide Up Stairs', 'Hera Startile Wide Holes']),
create_dungeon_region(player, 'Hera 4F', 'Tower of Hera', ['Tower of Hera - Compass Chest'], ['Hera 4F Down Stairs', 'Hera 4F Up Stairs', 'Hera 4F Holes']),
create_dungeon_region(player, 'Hera 4F', 'Tower of Hera', ['Tower of Hera - Compass Chest'], ['Hera 4F Down Stairs', 'Hera 4F Up Stairs', 'Hera Big Chest Hook Path', 'Hera 4F Holes']),
create_dungeon_region(player, 'Hera Big Chest Landing', 'Tower of Hera', ['Tower of Hera - Big Chest'], ['Hera Big Chest Landing Exit', 'Hera Big Chest Landing Holes']),
create_dungeon_region(player, 'Hera 5F', 'Tower of Hera', None, ['Hera 5F Down Stairs', 'Hera 5F Up Stairs', 'Hera 5F Star Hole', 'Hera 5F Pothole Chain', 'Hera 5F Normal Holes']),
create_dungeon_region(player, 'Hera Fairies', 'Tower of Hera', None, ['Hera Fairies\' Warp']),

12
Rom.py
View File

@@ -22,7 +22,7 @@ from EntranceShuffle import door_addresses, exit_ids
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '150e8e5bdb52e565fa9e2172d98c31ab'
RANDOMIZERBASEHASH = 'b9e578ef0af231041070bd9049a55646'
class JsonRom(object):
@@ -602,7 +602,7 @@ def patch_rom(world, rom, player, team, enemized):
# patch doors
if world.doorShuffle[player] == 'crossed':
rom.write_byte(0x139004, 2)
rom.write_byte(0x138002, 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)
@@ -627,7 +627,7 @@ def patch_rom(world, rom, player, team, enemized):
if room.player == player and room.palette is not None:
rom.write_byte(0x13f200+room.index, room.palette)
if world.doorShuffle[player] == 'basic':
rom.write_byte(0x139004, 1)
rom.write_byte(0x138002, 1)
for door in world.doors:
if door.dest is not None and door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs,
DoorType.Open, DoorType.StraightStairs]:
@@ -648,9 +648,11 @@ def patch_rom(world, rom, player, team, enemized):
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)
elif not opposite_door:
rom.write_byte(0x13f000+dungeon_id, 0) # no supertile preceeding boss
rom.write_byte(0x138004, dr_flags.value)
if dr_flags & DROptions.Town_Portal and world.mode[player] == 'inverted':
rom.write_byte(0x139008, 1)
rom.write_byte(0x138006, 1)
for portal in world.dungeon_portals[player]:
if not portal.default:

View File

@@ -159,6 +159,7 @@ def global_rules(world, player):
# Tower of Hera
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('Hera Big Chest Hook Path', player), lambda state: state.has('Hookshot', player))
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player))
@@ -274,7 +275,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has_Boots(player))
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Crystal Maze Cane Path', player), lambda state: state.has('Cane of Somaria', player))

View File

@@ -14,6 +14,16 @@ incsrc drhooks.asm
;Main Code
org $278000 ;138000
db $44, $52 ;DR
DRMode:
dw 0
DRFlags:
dw 0
DRScroll:
db 0
OffsetTable:
dw -8, 8
incsrc normal.asm
incsrc scroll.asm
incsrc spiral.asm
@@ -22,20 +32,6 @@ incsrc keydoors.asm
incsrc overrides.asm
incsrc edges.asm
incsrc math.asm
warnpc $279000
; Data Section
org $279000
OffsetTable:
dw -8, 8
DRMode:
dw 0
DRFlags:
dw 0
DRScroll:
db 0
org $279030
incsrc hudadditions.asm
warnpc $279700

File diff suppressed because one or more lines are too long

View File

@@ -110,6 +110,11 @@
"vanilla"
]
},
"intensity": {
"choices":[
"3", "2", "1", "random"
]
},
"experimental": {
"action": "store_true",
"type": "bool"
@@ -124,12 +129,12 @@
},
"crystals_ganon": {
"choices": [
7, 6, 5, 4, 3, 2, 1, 0, "random"
"7", "6", "5", "4", "3", "2", "1", "0", "random"
]
},
"crystals_gt": {
"choices": [
7, 6, 5, 4, 3, 2, 1, 0, "random"
"7", "6", "5", "4", "3", "2", "1", "0", "random"
]
},
"openpyramid": {

View File

@@ -198,6 +198,13 @@
"Vanilla: All doors are connected the same way they were in the",
" base game."
],
"intensity" : [
"Door Shuffle Intensity Level (default: %(default)s)",
"1: Shuffles normal doors and spiral staircases",
"2: And shuffles open edges and straight staircases",
"3: (Coming soon) And shuffles dungeon lobbies",
"random: Picks one of those at random"
],
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
"crystals_ganon": [

View File

@@ -58,6 +58,12 @@
"randomizer.dungeon.dungeondoorshuffle.basic": "Basic",
"randomizer.dungeon.dungeondoorshuffle.crossed": "Crossed",
"randomizer.dungeon.dungeonintensity": "Intensity Level",
"randomizer.dungeon.dungeonintensity.1": "1: Normal Supertile and Spiral Stairs",
"randomizer.dungeon.dungeonintensity.2": "2: Open Edges and Straight Stairs",
"randomizer.dungeon.dungeonintensity.3": "3: (Coming soon) Dungeon Lobbies",
"randomizer.dungeon.dungeonintensity.random": "Random",
"randomizer.dungeon.experimental": "Enable Experimental Features",
"randomizer.dungeon.dungeon_counters": "Dungeon Chest Counters",

View File

@@ -9,6 +9,19 @@
"crossed"
]
},
"dungeonintensity": {
"type": "selectbox",
"default": "2",
"options": [
"1",
"2",
"3",
"random"
],
"config": {
"width": 40
}
},
"experimental": { "type": "checkbox" },
"dungeon_counters": {
"type": "selectbox",

View File

@@ -87,6 +87,7 @@ SETTINGSTOPROCESS = {
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"dungeonintensity": "intensity",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters"
},

View File

@@ -33,7 +33,7 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs):
return self
# Make an OptionMenu with a label and pretty option labels
def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs):
def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs, config=None):
self = Frame(parent)
labels = options
@@ -96,7 +96,7 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt
else:
self.label.pack(side=LEFT)
self.selectbox.config(width=20)
self.selectbox.config(width=config['width'] if config and config['width'] else 20)
idx = 0
default = self.selectbox.options["values"][idx]
if managerAttrs is not None and "default" in managerAttrs:
@@ -166,7 +166,8 @@ def make_textbox(self, parent, label, storageVar, manager, managerAttrs):
return widget
# Make a generic widget
def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(), options=None):
def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(),
options=None, config=None):
widget = None
if manager is None:
manager = "pack"
@@ -184,7 +185,7 @@ def make_widget(self, type, parent, label, storageVar=None, manager=None, manage
elif type == "selectbox":
if thisStorageVar is None:
thisStorageVar = StringVar()
widget = make_selectbox(self, parent, label, options, thisStorageVar, manager, managerAttrs)
widget = make_selectbox(self, parent, label, options, thisStorageVar, manager, managerAttrs, config)
elif type == "spinbox":
if thisStorageVar is None:
thisStorageVar = StringVar()
@@ -203,13 +204,14 @@ def make_widget_from_dict(self, defn, parent):
manager = defn["manager"] if "manager" in defn else None
managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None
options = defn["options"] if "options" in defn else None
config = defn["config"] if "config" in defn else None
if managerAttrs is None and "default" in defn:
managerAttrs = {}
if "default" in defn:
managerAttrs["default"] = defn["default"]
widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options)
widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options, config)
widget.type = type
return widget