Add setting for self-looping doors
This commit is contained in:
@@ -145,6 +145,7 @@ class World(object):
|
|||||||
set_player_attr('colorizepots', True)
|
set_player_attr('colorizepots', True)
|
||||||
set_player_attr('pot_pool', {})
|
set_player_attr('pot_pool', {})
|
||||||
set_player_attr('decoupledoors', False)
|
set_player_attr('decoupledoors', False)
|
||||||
|
set_player_attr('door_self_loops', False)
|
||||||
set_player_attr('door_type_mode', 'original')
|
set_player_attr('door_type_mode', 'original')
|
||||||
set_player_attr('trap_door_mode', 'optional')
|
set_player_attr('trap_door_mode', 'optional')
|
||||||
set_player_attr('key_logic_algorithm', 'default')
|
set_player_attr('key_logic_algorithm', 'default')
|
||||||
@@ -2472,6 +2473,7 @@ class Spoiler(object):
|
|||||||
'trap_door_mode': self.world.trap_door_mode,
|
'trap_door_mode': self.world.trap_door_mode,
|
||||||
'key_logic': self.world.key_logic_algorithm,
|
'key_logic': self.world.key_logic_algorithm,
|
||||||
'decoupledoors': self.world.decoupledoors,
|
'decoupledoors': self.world.decoupledoors,
|
||||||
|
'door_self_loops': self.world.door_self_loops,
|
||||||
'dungeon_counters': self.world.dungeon_counters,
|
'dungeon_counters': self.world.dungeon_counters,
|
||||||
'item_pool': self.world.difficulty,
|
'item_pool': self.world.difficulty,
|
||||||
'item_functionality': self.world.difficulty_adjustments,
|
'item_functionality': self.world.difficulty_adjustments,
|
||||||
@@ -2682,6 +2684,7 @@ class Spoiler(object):
|
|||||||
outfile.write(f"Trap Door Mode: {self.metadata['trap_door_mode'][player]}\n")
|
outfile.write(f"Trap Door Mode: {self.metadata['trap_door_mode'][player]}\n")
|
||||||
outfile.write(f"Key Logic Algorithm: {self.metadata['key_logic'][player]}\n")
|
outfile.write(f"Key Logic Algorithm: {self.metadata['key_logic'][player]}\n")
|
||||||
outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n")
|
outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n")
|
||||||
|
outfile.write(f"Spiral Stairs can self-loop: {yn(self.metadata['door_self_loops'][player])}\n")
|
||||||
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
|
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
|
||||||
outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n")
|
outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n")
|
||||||
outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n")
|
outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n")
|
||||||
@@ -2947,10 +2950,10 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2}
|
|||||||
pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7,
|
pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7,
|
||||||
'clustered': 8, 'nonempty': 9}
|
'clustered': 8, 'nonempty': 9}
|
||||||
|
|
||||||
# byte 5: CCCC CTTX (crystals gt, ctr2, experimental)
|
# byte 5: SCCC CTTX (self-loop doors, crystals gt, ctr2, experimental)
|
||||||
counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3}
|
counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3}
|
||||||
|
|
||||||
# byte 6: CCCC CPAA (crystals ganon, pyramid, access
|
# byte 6: ?CCC CPAA (crystals ganon, pyramid, access
|
||||||
access_mode = {"items": 0, "locations": 1, "none": 2}
|
access_mode = {"items": 0, "locations": 1, "none": 2}
|
||||||
|
|
||||||
# byte 7: B?MC DDEE (big, ?, maps, compass, door_type, enemies)
|
# byte 7: B?MC DDEE (big, ?, maps, compass, door_type, enemies)
|
||||||
@@ -3008,7 +3011,8 @@ class Settings(object):
|
|||||||
|
|
||||||
(0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]),
|
(0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]),
|
||||||
|
|
||||||
((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3)
|
(0x80 if w.door_self_loops[p] else 0)
|
||||||
|
| ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3)
|
||||||
| (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0),
|
| (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0),
|
||||||
|
|
||||||
((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3)
|
((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3)
|
||||||
@@ -3067,12 +3071,13 @@ class Settings(object):
|
|||||||
args.dropshuffle[p] = True if settings[4] & 0x10 else False
|
args.dropshuffle[p] = True if settings[4] & 0x10 else False
|
||||||
args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F]
|
args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F]
|
||||||
|
|
||||||
|
args.door_self_loops[p] = True if settings[5] & 0x80 else False
|
||||||
args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1]
|
args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1]
|
||||||
cgt = (settings[5] & 0xf8) >> 3
|
cgt = (settings[5] & 0x78) >> 3
|
||||||
args.crystals_gt[p] = "random" if cgt == 8 else cgt
|
args.crystals_gt[p] = "random" if cgt == 8 else cgt
|
||||||
args.experimental[p] = True if settings[5] & 0x1 else False
|
args.experimental[p] = True if settings[5] & 0x1 else False
|
||||||
|
|
||||||
cgan = (settings[6] & 0xf8) >> 3
|
cgan = (settings[6] & 0x78) >> 3
|
||||||
args.crystals_ganon[p] = "random" if cgan == 8 else cgan
|
args.crystals_ganon[p] = "random" if cgan == 8 else cgan
|
||||||
args.openpyramid[p] = True if settings[6] & 0x4 else False
|
args.openpyramid[p] = True if settings[6] & 0x4 else False
|
||||||
|
|
||||||
|
|||||||
3
CLI.py
3
CLI.py
@@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
|
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
|
||||||
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx',
|
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx',
|
||||||
'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
|
'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
|
||||||
'trap_door_mode', 'key_logic_algorithm']:
|
'trap_door_mode', 'key_logic_algorithm', 'door_self_loops']:
|
||||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||||
if player == 1:
|
if player == 1:
|
||||||
setattr(ret, name, {1: value})
|
setattr(ret, name, {1: value})
|
||||||
@@ -218,6 +218,7 @@ def parse_settings():
|
|||||||
'trap_door_mode': 'optional',
|
'trap_door_mode': 'optional',
|
||||||
'key_logic_algorithm': 'default',
|
'key_logic_algorithm': 'default',
|
||||||
'decoupledoors': False,
|
'decoupledoors': False,
|
||||||
|
'door_self_loops': False,
|
||||||
'experimental': False,
|
'experimental': False,
|
||||||
'dungeon_counters': 'default',
|
'dungeon_counters': 'default',
|
||||||
'mixed_travel': 'prevent',
|
'mixed_travel': 'prevent',
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -115,6 +115,7 @@ def main(args, seed=None, fish=None):
|
|||||||
world.trap_door_mode = args.trap_door_mode.copy()
|
world.trap_door_mode = args.trap_door_mode.copy()
|
||||||
world.key_logic_algorithm = args.key_logic_algorithm.copy()
|
world.key_logic_algorithm = args.key_logic_algorithm.copy()
|
||||||
world.decoupledoors = args.decoupledoors.copy()
|
world.decoupledoors = args.decoupledoors.copy()
|
||||||
|
world.door_self_loops = args.door_self_loops.copy()
|
||||||
world.experimental = args.experimental.copy()
|
world.experimental = args.experimental.copy()
|
||||||
world.dungeon_counters = args.dungeon_counters.copy()
|
world.dungeon_counters = args.dungeon_counters.copy()
|
||||||
world.fish = fish
|
world.fish = fish
|
||||||
@@ -487,6 +488,7 @@ def copy_world(world):
|
|||||||
ret.beemizer = world.beemizer.copy()
|
ret.beemizer = world.beemizer.copy()
|
||||||
ret.intensity = world.intensity.copy()
|
ret.intensity = world.intensity.copy()
|
||||||
ret.decoupledoors = world.decoupledoors.copy()
|
ret.decoupledoors = world.decoupledoors.copy()
|
||||||
|
ret.door_self_loops = world.door_self_loops.copy()
|
||||||
ret.experimental = world.experimental.copy()
|
ret.experimental = world.experimental.copy()
|
||||||
ret.shopsanity = world.shopsanity.copy()
|
ret.shopsanity = world.shopsanity.copy()
|
||||||
ret.dropshuffle = world.dropshuffle.copy()
|
ret.dropshuffle = world.dropshuffle.copy()
|
||||||
|
|||||||
@@ -174,7 +174,13 @@ CLI: `--key_logic [default|partial|strict]`
|
|||||||
|
|
||||||
This is similar to insanity mode in ER where door entrances and exits are not paired anymore. Tends to remove more logic from dungeons as many rooms will not be required to traverse to explore. Hope you like transitions.
|
This is similar to insanity mode in ER where door entrances and exits are not paired anymore. Tends to remove more logic from dungeons as many rooms will not be required to traverse to explore. Hope you like transitions.
|
||||||
|
|
||||||
CLI `--decoupledoors`
|
CLI: `--decoupledoors`
|
||||||
|
|
||||||
|
### Self-Looping Spiral Stairs
|
||||||
|
|
||||||
|
If enabled, spiral stairs are allowed to lead to themselves.
|
||||||
|
|
||||||
|
CLI: `--door_self_loops`
|
||||||
|
|
||||||
### Pottery
|
### Pottery
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
partial: 0
|
partial: 0
|
||||||
strict: 0
|
strict: 0
|
||||||
decoupledoors: off
|
decoupledoors: off
|
||||||
|
door_self_loops:
|
||||||
|
on: 1
|
||||||
|
off: 1
|
||||||
dropshuffle:
|
dropshuffle:
|
||||||
on: 1
|
on: 1
|
||||||
off: 1
|
off: 1
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ key_logic_algorithm:
|
|||||||
decoupledoors:
|
decoupledoors:
|
||||||
off: 9 # more strict
|
off: 9 # more strict
|
||||||
on: 1
|
on: 1
|
||||||
|
door_self_loops:
|
||||||
|
on: 1
|
||||||
|
off: 1
|
||||||
dropshuffle:
|
dropshuffle:
|
||||||
on: 1
|
on: 1
|
||||||
off: 1
|
off: 1
|
||||||
|
|||||||
@@ -212,6 +212,10 @@
|
|||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
},
|
},
|
||||||
|
"door_self_loops": {
|
||||||
|
"action": "store_true",
|
||||||
|
"type": "bool"
|
||||||
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
|
|||||||
@@ -253,6 +253,7 @@
|
|||||||
"strict: Ensure small keys are available"
|
"strict: Ensure small keys are available"
|
||||||
],
|
],
|
||||||
"decoupledoors" : [ "Door entrances and exits are decoupled" ],
|
"decoupledoors" : [ "Door entrances and exits are decoupled" ],
|
||||||
|
"door_self_loops" : [ "Spiral stairs are allowed to self-loop" ],
|
||||||
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
|
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
|
||||||
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
|
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
|
||||||
"crystals_ganon": [
|
"crystals_ganon": [
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
"randomizer.dungeon.smallkeyshuffle.universal": "Universal",
|
"randomizer.dungeon.smallkeyshuffle.universal": "Universal",
|
||||||
"randomizer.dungeon.bigkeyshuffle": "Big Keys",
|
"randomizer.dungeon.bigkeyshuffle": "Big Keys",
|
||||||
"randomizer.dungeon.decoupledoors": "Decouple Doors",
|
"randomizer.dungeon.decoupledoors": "Decouple Doors",
|
||||||
|
"randomizer.dungeon.door_self_loops": "Allow Self-Looping Spiral Stairs",
|
||||||
|
|
||||||
"randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle",
|
"randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle",
|
||||||
"randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla",
|
"randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla",
|
||||||
|
|||||||
@@ -66,6 +66,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decoupledoors": {
|
"decoupledoors": {
|
||||||
|
"type": "checkbox",
|
||||||
|
"config": {
|
||||||
|
"padx": [20,0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"door_self_loops": {
|
||||||
"type": "checkbox",
|
"type": "checkbox",
|
||||||
"config": {
|
"config": {
|
||||||
"padx": [20,0],
|
"padx": [20,0],
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ class CustomSettings(object):
|
|||||||
args.trap_door_mode[p] = get_setting(settings['trap_door_mode'], args.trap_door_mode[p])
|
args.trap_door_mode[p] = get_setting(settings['trap_door_mode'], args.trap_door_mode[p])
|
||||||
args.key_logic_algorithm[p] = get_setting(settings['key_logic_algorithm'], args.key_logic_algorithm[p])
|
args.key_logic_algorithm[p] = get_setting(settings['key_logic_algorithm'], args.key_logic_algorithm[p])
|
||||||
args.decoupledoors[p] = get_setting(settings['decoupledoors'], args.decoupledoors[p])
|
args.decoupledoors[p] = get_setting(settings['decoupledoors'], args.decoupledoors[p])
|
||||||
|
args.door_self_loops[p] = get_setting(settings['door_self_loops'], args.door_self_loops[p])
|
||||||
args.dungeon_counters[p] = get_setting(settings['dungeon_counters'], args.dungeon_counters[p])
|
args.dungeon_counters[p] = get_setting(settings['dungeon_counters'], args.dungeon_counters[p])
|
||||||
args.crystals_gt[p] = get_setting(settings['crystals_gt'], args.crystals_gt[p])
|
args.crystals_gt[p] = get_setting(settings['crystals_gt'], args.crystals_gt[p])
|
||||||
args.crystals_ganon[p] = get_setting(settings['crystals_ganon'], args.crystals_ganon[p])
|
args.crystals_ganon[p] = get_setting(settings['crystals_ganon'], args.crystals_ganon[p])
|
||||||
@@ -232,6 +233,7 @@ class CustomSettings(object):
|
|||||||
settings_dict[p]['trap_door_mode'] = world.trap_door_mode[p]
|
settings_dict[p]['trap_door_mode'] = world.trap_door_mode[p]
|
||||||
settings_dict[p]['key_logic_algorithm'] = world.key_logic_algorithm[p]
|
settings_dict[p]['key_logic_algorithm'] = world.key_logic_algorithm[p]
|
||||||
settings_dict[p]['decoupledoors'] = world.decoupledoors[p]
|
settings_dict[p]['decoupledoors'] = world.decoupledoors[p]
|
||||||
|
settings_dict[p]['door_self_loops'] = world.door_self_loops[p]
|
||||||
settings_dict[p]['logic'] = world.logic[p]
|
settings_dict[p]['logic'] = world.logic[p]
|
||||||
settings_dict[p]['mode'] = world.mode[p]
|
settings_dict[p]['mode'] = world.mode[p]
|
||||||
settings_dict[p]['swords'] = world.swords[p]
|
settings_dict[p]['swords'] = world.swords[p]
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ SETTINGSTOPROCESS = {
|
|||||||
"door_type_mode": "door_type_mode",
|
"door_type_mode": "door_type_mode",
|
||||||
"trap_door_mode": "trap_door_mode",
|
"trap_door_mode": "trap_door_mode",
|
||||||
"decoupledoors": "decoupledoors",
|
"decoupledoors": "decoupledoors",
|
||||||
|
"door_self_loops": "door_self_loops",
|
||||||
"experimental": "experimental",
|
"experimental": "experimental",
|
||||||
"dungeon_counters": "dungeon_counters",
|
"dungeon_counters": "dungeon_counters",
|
||||||
"mixed_travel": "mixed_travel",
|
"mixed_travel": "mixed_travel",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ def generate_dungeon(builder, entrance_region_names, split_dungeon, world, playe
|
|||||||
queue = collections.deque(proposed_map.items())
|
queue = collections.deque(proposed_map.items())
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
a, b = queue.popleft()
|
a, b = queue.popleft()
|
||||||
if world.decoupledoors[player]:
|
if a == b or world.decoupledoors[player]:
|
||||||
connect_doors_one_way(a, b)
|
connect_doors_one_way(a, b)
|
||||||
else:
|
else:
|
||||||
connect_doors(a, b)
|
connect_doors(a, b)
|
||||||
@@ -128,14 +128,14 @@ def create_random_proposal(doors_to_connect, world, player):
|
|||||||
next_hook = random.choice(hooks_left)
|
next_hook = random.choice(hooks_left)
|
||||||
primary_door = random.choice(primary_bucket[next_hook])
|
primary_door = random.choice(primary_bucket[next_hook])
|
||||||
opp_hook, secondary_door = type_map[next_hook], None
|
opp_hook, secondary_door = type_map[next_hook], None
|
||||||
while (secondary_door is None or secondary_door == primary_door
|
while (secondary_door is None or (secondary_door == primary_door and not world.door_self_loops[player])
|
||||||
or decouple_check(primary_bucket[next_hook], secondary_bucket[opp_hook],
|
or decouple_check(primary_bucket[next_hook], secondary_bucket[opp_hook],
|
||||||
primary_door, secondary_door, world, player)):
|
primary_door, secondary_door, world, player)):
|
||||||
secondary_door = random.choice(secondary_bucket[opp_hook])
|
secondary_door = random.choice(secondary_bucket[opp_hook])
|
||||||
proposal[primary_door] = secondary_door
|
proposal[primary_door] = secondary_door
|
||||||
primary_bucket[next_hook].remove(primary_door)
|
primary_bucket[next_hook].remove(primary_door)
|
||||||
secondary_bucket[opp_hook].remove(secondary_door)
|
secondary_bucket[opp_hook].remove(secondary_door)
|
||||||
if not world.decoupledoors[player]:
|
if primary_door != secondary_door and not world.decoupledoors[player]:
|
||||||
proposal[secondary_door] = primary_door
|
proposal[secondary_door] = primary_door
|
||||||
primary_bucket[opp_hook].remove(secondary_door)
|
primary_bucket[opp_hook].remove(secondary_door)
|
||||||
secondary_bucket[next_hook].remove(primary_door)
|
secondary_bucket[next_hook].remove(primary_door)
|
||||||
@@ -205,6 +205,14 @@ def modify_proposal(proposed_map, explored_state, doors_to_connect, hash_code_se
|
|||||||
old_attempt = proposed_map[new_door]
|
old_attempt = proposed_map[new_door]
|
||||||
else:
|
else:
|
||||||
old_attempt = next(x for x in proposed_map if proposed_map[x] == new_door)
|
old_attempt = next(x for x in proposed_map if proposed_map[x] == new_door)
|
||||||
|
# ensure nothing gets messed up when something loops with itself
|
||||||
|
if attempt == old_target and old_attempt == new_door:
|
||||||
|
old_attempt = new_door
|
||||||
|
old_target = attempt
|
||||||
|
elif attempt == old_target:
|
||||||
|
old_target = old_attempt
|
||||||
|
elif old_attempt == new_door:
|
||||||
|
old_attempt = old_target
|
||||||
proposed_map[old_attempt] = old_target
|
proposed_map[old_attempt] = old_target
|
||||||
if not world.decoupledoors[player]:
|
if not world.decoupledoors[player]:
|
||||||
proposed_map[old_target] = old_attempt
|
proposed_map[old_target] = old_attempt
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ def roll_settings(weights):
|
|||||||
ret.trap_door_mode = get_choice('trap_door_mode')
|
ret.trap_door_mode = get_choice('trap_door_mode')
|
||||||
ret.key_logic_algorithm = get_choice('key_logic_algorithm')
|
ret.key_logic_algorithm = get_choice('key_logic_algorithm')
|
||||||
ret.decoupledoors = get_choice('decoupledoors') == 'on'
|
ret.decoupledoors = get_choice('decoupledoors') == 'on'
|
||||||
|
ret.door_self_loops = get_choice('door_self_loops') == 'on'
|
||||||
ret.experimental = get_choice('experimental') == 'on'
|
ret.experimental = get_choice('experimental') == 'on'
|
||||||
ret.collection_rate = get_choice('collection_rate') == 'on'
|
ret.collection_rate = get_choice('collection_rate') == 'on'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user