Add setting for self-looping doors

This commit is contained in:
Catobat
2023-08-02 02:23:19 +02:00
parent 1817cf3824
commit 7197a23b45
14 changed files with 54 additions and 10 deletions

View File

@@ -145,6 +145,7 @@ class World(object):
set_player_attr('colorizepots', True)
set_player_attr('pot_pool', {})
set_player_attr('decoupledoors', False)
set_player_attr('door_self_loops', False)
set_player_attr('door_type_mode', 'original')
set_player_attr('trap_door_mode', 'optional')
set_player_attr('key_logic_algorithm', 'default')
@@ -2472,6 +2473,7 @@ class Spoiler(object):
'trap_door_mode': self.world.trap_door_mode,
'key_logic': self.world.key_logic_algorithm,
'decoupledoors': self.world.decoupledoors,
'door_self_loops': self.world.door_self_loops,
'dungeon_counters': self.world.dungeon_counters,
'item_pool': self.world.difficulty,
'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"Key Logic Algorithm: {self.metadata['key_logic'][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"Dungeon Counters: {self.metadata['dungeon_counters'][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,
'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}
# byte 6: CCCC CPAA (crystals ganon, pyramid, access
# byte 6: ?CCC CPAA (crystals ganon, pyramid, access
access_mode = {"items": 0, "locations": 1, "none": 2}
# 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]]),
((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),
((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.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]
cgt = (settings[5] & 0xf8) >> 3
cgt = (settings[5] & 0x78) >> 3
args.crystals_gt[p] = "random" if cgt == 8 else cgt
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.openpyramid[p] = True if settings[6] & 0x4 else False

3
CLI.py
View File

@@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False):
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx',
'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)
if player == 1:
setattr(ret, name, {1: value})
@@ -218,6 +218,7 @@ def parse_settings():
'trap_door_mode': 'optional',
'key_logic_algorithm': 'default',
'decoupledoors': False,
'door_self_loops': False,
'experimental': False,
'dungeon_counters': 'default',
'mixed_travel': 'prevent',

View File

@@ -115,6 +115,7 @@ def main(args, seed=None, fish=None):
world.trap_door_mode = args.trap_door_mode.copy()
world.key_logic_algorithm = args.key_logic_algorithm.copy()
world.decoupledoors = args.decoupledoors.copy()
world.door_self_loops = args.door_self_loops.copy()
world.experimental = args.experimental.copy()
world.dungeon_counters = args.dungeon_counters.copy()
world.fish = fish
@@ -487,6 +488,7 @@ def copy_world(world):
ret.beemizer = world.beemizer.copy()
ret.intensity = world.intensity.copy()
ret.decoupledoors = world.decoupledoors.copy()
ret.door_self_loops = world.door_self_loops.copy()
ret.experimental = world.experimental.copy()
ret.shopsanity = world.shopsanity.copy()
ret.dropshuffle = world.dropshuffle.copy()

View File

@@ -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.
CLI `--decoupledoors`
CLI: `--decoupledoors`
### Self-Looping Spiral Stairs
If enabled, spiral stairs are allowed to lead to themselves.
CLI: `--door_self_loops`
### Pottery

View File

@@ -31,6 +31,9 @@
partial: 0
strict: 0
decoupledoors: off
door_self_loops:
on: 1
off: 1
dropshuffle:
on: 1
off: 1

View File

@@ -31,6 +31,9 @@ key_logic_algorithm:
decoupledoors:
off: 9 # more strict
on: 1
door_self_loops:
on: 1
off: 1
dropshuffle:
on: 1
off: 1

View File

@@ -212,6 +212,10 @@
"action": "store_true",
"type": "bool"
},
"door_self_loops": {
"action": "store_true",
"type": "bool"
},
"experimental": {
"action": "store_true",
"type": "bool"

View File

@@ -253,6 +253,7 @@
"strict: Ensure small keys are available"
],
"decoupledoors" : [ "Door entrances and exits are decoupled" ],
"door_self_loops" : [ "Spiral stairs are allowed to self-loop" ],
"experimental": [ "Enable experimental features. (default: %(default)s)" ],
"dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ],
"crystals_ganon": [

View File

@@ -58,6 +58,7 @@
"randomizer.dungeon.smallkeyshuffle.universal": "Universal",
"randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.decoupledoors": "Decouple Doors",
"randomizer.dungeon.door_self_loops": "Allow Self-Looping Spiral Stairs",
"randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle",
"randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla",

View File

@@ -66,6 +66,12 @@
}
},
"decoupledoors": {
"type": "checkbox",
"config": {
"padx": [20,0]
}
},
"door_self_loops": {
"type": "checkbox",
"config": {
"padx": [20,0],

View File

@@ -113,6 +113,7 @@ class CustomSettings(object):
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.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.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])
@@ -232,6 +233,7 @@ class CustomSettings(object):
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]['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]['mode'] = world.mode[p]
settings_dict[p]['swords'] = world.swords[p]

View File

@@ -106,6 +106,7 @@ SETTINGSTOPROCESS = {
"door_type_mode": "door_type_mode",
"trap_door_mode": "trap_door_mode",
"decoupledoors": "decoupledoors",
"door_self_loops": "door_self_loops",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters",
"mixed_travel": "mixed_travel",

View File

@@ -22,7 +22,7 @@ def generate_dungeon(builder, entrance_region_names, split_dungeon, world, playe
queue = collections.deque(proposed_map.items())
while len(queue) > 0:
a, b = queue.popleft()
if world.decoupledoors[player]:
if a == b or world.decoupledoors[player]:
connect_doors_one_way(a, b)
else:
connect_doors(a, b)
@@ -128,14 +128,14 @@ def create_random_proposal(doors_to_connect, world, player):
next_hook = random.choice(hooks_left)
primary_door = random.choice(primary_bucket[next_hook])
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],
primary_door, secondary_door, world, player)):
secondary_door = random.choice(secondary_bucket[opp_hook])
proposal[primary_door] = secondary_door
primary_bucket[next_hook].remove(primary_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
primary_bucket[opp_hook].remove(secondary_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]
else:
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
if not world.decoupledoors[player]:
proposed_map[old_target] = old_attempt

View File

@@ -87,6 +87,7 @@ def roll_settings(weights):
ret.trap_door_mode = get_choice('trap_door_mode')
ret.key_logic_algorithm = get_choice('key_logic_algorithm')
ret.decoupledoors = get_choice('decoupledoors') == 'on'
ret.door_self_loops = get_choice('door_self_loops') == 'on'
ret.experimental = get_choice('experimental') == 'on'
ret.collection_rate = get_choice('collection_rate') == 'on'