Revamp HMG logic
This commit is contained in:
32
Main.py
32
Main.py
@@ -27,7 +27,7 @@ from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dunge
|
|||||||
from Fill import dungeon_tracking
|
from Fill import dungeon_tracking
|
||||||
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
|
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
|
||||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items
|
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items
|
||||||
from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors, get_hybridmajor_connector_entrances
|
from UnderworldGlitchRules import create_hybridmajor_connections, get_hybridmajor_connection_entrances
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config, verify_item_pool_config
|
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config, verify_item_pool_config
|
||||||
@@ -222,8 +222,6 @@ def main(args, seed=None, fish=None):
|
|||||||
world.data_tables[player] = init_data_tables(world, player)
|
world.data_tables[player] = init_data_tables(world, player)
|
||||||
place_bosses(world, player)
|
place_bosses(world, player)
|
||||||
randomize_enemies(world, player)
|
randomize_enemies(world, player)
|
||||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
|
||||||
create_hybridmajor_connections(world, player)
|
|
||||||
adjust_locations(world, player)
|
adjust_locations(world, player)
|
||||||
|
|
||||||
if world.customizer and world.customizer.get_start_inventory():
|
if world.customizer and world.customizer.get_start_inventory():
|
||||||
@@ -264,8 +262,6 @@ def main(args, seed=None, fish=None):
|
|||||||
link_overworld(world, player)
|
link_overworld(world, player)
|
||||||
create_dynamic_exits(world, player)
|
create_dynamic_exits(world, player)
|
||||||
link_entrances_new(world, player)
|
link_entrances_new(world, player)
|
||||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
|
||||||
create_hybridmajor_connectors(world, player)
|
|
||||||
|
|
||||||
logger.info(world.fish.translate("cli", "cli", "shuffling.prep"))
|
logger.info(world.fish.translate("cli", "cli", "shuffling.prep"))
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
@@ -285,6 +281,8 @@ def main(args, seed=None, fish=None):
|
|||||||
logger.info(world.fish.translate("cli", "cli", "generating.itempool"))
|
logger.info(world.fish.translate("cli", "cli", "generating.itempool"))
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
|
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||||
|
create_hybridmajor_connections(world, player)
|
||||||
generate_itempool(world, player)
|
generate_itempool(world, player)
|
||||||
|
|
||||||
verify_item_pool_config(world)
|
verify_item_pool_config(world)
|
||||||
@@ -533,8 +531,6 @@ def copy_world(world):
|
|||||||
create_dungeons(ret, player)
|
create_dungeons(ret, player)
|
||||||
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
|
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
|
||||||
create_owg_connections(ret, player)
|
create_owg_connections(ret, player)
|
||||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
|
||||||
create_hybridmajor_connections(ret, player)
|
|
||||||
|
|
||||||
|
|
||||||
# there are region references here they must be migrated to preserve integrity
|
# there are region references here they must be migrated to preserve integrity
|
||||||
@@ -561,7 +557,10 @@ def copy_world(world):
|
|||||||
|
|
||||||
# connect copied world
|
# connect copied world
|
||||||
copied_locations = {(loc.name, loc.player): loc for loc in ret.get_locations()} # caches all locations
|
copied_locations = {(loc.name, loc.player): loc for loc in ret.get_locations()} # caches all locations
|
||||||
hmg_entrances = get_hybridmajor_connector_entrances()
|
|
||||||
|
# We have to skip these for now. They require both the rest of the entrances _and_ the dungeon portals to be copied first
|
||||||
|
# We will connect them later
|
||||||
|
hmg_entrances = get_hybridmajor_connection_entrances()
|
||||||
|
|
||||||
for region in world.regions:
|
for region in world.regions:
|
||||||
copied_region = ret.get_region(region.name, region.player)
|
copied_region = ret.get_region(region.name, region.player)
|
||||||
@@ -621,7 +620,22 @@ def copy_world(world):
|
|||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||||
create_hybridmajor_connectors(ret, player)
|
create_hybridmajor_connections(ret, player)
|
||||||
|
|
||||||
|
for region in world.regions:
|
||||||
|
copied_region = ret.get_region(region.name, region.player)
|
||||||
|
copied_region.is_light_world = region.is_light_world
|
||||||
|
copied_region.is_dark_world = region.is_dark_world
|
||||||
|
copied_region.dungeon = region.dungeon
|
||||||
|
copied_region.locations = [copied_locations[(location.name, location.player)] for location in region.locations]
|
||||||
|
for location in copied_region.locations:
|
||||||
|
location.parent_region = copied_region
|
||||||
|
for entrance in region.entrances:
|
||||||
|
if entrance.name not in hmg_entrances:
|
||||||
|
continue
|
||||||
|
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
|
||||||
|
|
||||||
|
for player in range(1, world.players + 1):
|
||||||
set_rules(ret, player)
|
set_rules(ret, player)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Helper functions to deliver entrance/exit/region sets to OWG rules.
|
Helper functions to deliver entrance/exit/region sets to OWG rules.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from BaseClasses import Entrance
|
from BaseClasses import Entrance, Region
|
||||||
|
|
||||||
# Cave regions that superbunny can get through - but only with a sword.
|
# Cave regions that superbunny can get through - but only with a sword.
|
||||||
sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"]
|
sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"]
|
||||||
@@ -283,10 +283,18 @@ def add_alternate_rule(entrance, rule):
|
|||||||
entrance.access_rule = lambda state: old_rule(state) or rule(state)
|
entrance.access_rule = lambda state: old_rule(state) or rule(state)
|
||||||
|
|
||||||
|
|
||||||
def create_no_logic_connections(player, world, connections):
|
def create_no_logic_connections(player, world, connections, connect_external=False):
|
||||||
for entrance, parent_region, target_region, *rule_override in connections:
|
for entrance, parent_region, target_region, *rule_override in connections:
|
||||||
parent = world.get_region(parent_region, player)
|
parent = world.get_region(parent_region, player)
|
||||||
target = world.get_region(target_region, player)
|
|
||||||
|
if isinstance(target_region, Region):
|
||||||
|
target_region = target_region.name
|
||||||
|
|
||||||
|
if connect_external and target_region.endswith(" Portal"):
|
||||||
|
target = world.get_portal(target_region[:-7], player).find_portal_entrance().parent_region
|
||||||
|
else:
|
||||||
|
target = world.get_region(target_region, player)
|
||||||
|
|
||||||
connection = Entrance(player, entrance, parent)
|
connection = Entrance(player, entrance, parent)
|
||||||
parent.exits.append(connection)
|
parent.exits.append(connection)
|
||||||
connection.connect(target)
|
connection.connect(target)
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ def link_overworld(world, player):
|
|||||||
for exitname, regionname in inverted_mandatory_connections:
|
for exitname, regionname in inverted_mandatory_connections:
|
||||||
connect_simple(world, exitname, regionname, player)
|
connect_simple(world, exitname, regionname, player)
|
||||||
|
|
||||||
if world.logic[player] in ['hybridglitches', 'nologic'] and not world.fix_palaceofdarkness_exit[player]:
|
|
||||||
connect_two_way(world, 'Palace of Darkness HMG Exit', 'Palace of Darkness HMG Entrance', player)
|
|
||||||
|
|
||||||
for forward_edge, back_edge in default_connections:
|
for forward_edge, back_edge in default_connections:
|
||||||
connect_two_way(world, forward_edge, back_edge, player)
|
connect_two_way(world, forward_edge, back_edge, player)
|
||||||
|
|
||||||
|
|||||||
15
Regions.py
15
Regions.py
@@ -191,19 +191,8 @@ def create_regions(world, player):
|
|||||||
create_dw_region(player, 'Broken Bridge Area', None, ['Broken Bridge Hammer Rock (South)', 'Broken Bridge Water Drop', 'Broken Bridge SW']),
|
create_dw_region(player, 'Broken Bridge Area', None, ['Broken Bridge Hammer Rock (South)', 'Broken Bridge Water Drop', 'Broken Bridge SW']),
|
||||||
create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']),
|
create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']),
|
||||||
create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']),
|
create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']),
|
||||||
create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water)
|
create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water),
|
||||||
]
|
create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']),
|
||||||
if world.logic[player] in ['hybridglitches', 'nologic'] and not world.fix_palaceofdarkness_exit[player]:
|
|
||||||
world.regions += [
|
|
||||||
create_dw_region(player, 'Palace of Darkness HMG Area', None, ['Palace of Darkness', 'Palace of Darkness HMG Exit']),
|
|
||||||
create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness SW', 'Palace of Darkness SE', 'Palace of Darkness HMG Entrance'])
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
world.regions += [
|
|
||||||
create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE'])
|
|
||||||
]
|
|
||||||
|
|
||||||
world.regions += [
|
|
||||||
create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']),
|
create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']),
|
||||||
create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']),
|
create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']),
|
||||||
create_dw_region(player, 'Dark Dunes Area', None, ['Dark Dunes NW', 'Dark Dunes WN', 'Dark Dunes SC']),
|
create_dw_region(player, 'Dark Dunes Area', None, ['Dark Dunes NW', 'Dark Dunes WN', 'Dark Dunes SC']),
|
||||||
|
|||||||
@@ -29,42 +29,10 @@ paradox_spots = [
|
|||||||
("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")
|
("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions
|
|
||||||
kikiskip_connectors = [
|
|
||||||
("Kiki Skip Connector", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit")
|
|
||||||
]
|
|
||||||
mirehera_connectors = [
|
|
||||||
("Mire to Hera Connector", "Mire Torches Top", "Tower of Hera Exit")
|
|
||||||
]
|
|
||||||
heraswamp_connectors = [
|
|
||||||
("Mire to Swamp Connector", "Mire Torches Top", "Swamp Palace Exit")
|
|
||||||
]
|
|
||||||
thievesdesert_connectors = [
|
|
||||||
("Thieves to Desert West Connector", "Thieves Attic", "Desert Palace Exit (West)"),
|
|
||||||
(
|
|
||||||
"Thieves to Desert South Connector",
|
|
||||||
"Thieves Attic",
|
|
||||||
"Desert Palace Exit (South)",
|
|
||||||
),
|
|
||||||
("Thieves to Desert East Connector", "Thieves Attic", "Desert Palace Exit (East)"),
|
|
||||||
]
|
|
||||||
specrock_connectors = [
|
|
||||||
(
|
|
||||||
"Spec Rock Top Connector",
|
|
||||||
"Spectacle Rock Cave (Peak)",
|
|
||||||
"Spectacle Rock Cave Exit (Top)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Spec Rock Exit Connector",
|
|
||||||
"Spectacle Rock Cave (Peak)",
|
|
||||||
"Spectacle Rock Cave Exit",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Create connections between dungeons/locations
|
# Create connections between dungeons/locations
|
||||||
def create_hybridmajor_connections(world, player):
|
def create_hybridmajor_connections(world, player):
|
||||||
|
fix_fake_worlds = world.fix_fake_world[player]
|
||||||
|
|
||||||
for spots in [
|
for spots in [
|
||||||
kikiskip_spots,
|
kikiskip_spots,
|
||||||
mirehera_spots,
|
mirehera_spots,
|
||||||
@@ -74,7 +42,7 @@ def create_hybridmajor_connections(world, player):
|
|||||||
specrock_spots,
|
specrock_spots,
|
||||||
paradox_spots,
|
paradox_spots,
|
||||||
]:
|
]:
|
||||||
create_no_logic_connections(player, world, spots)
|
create_no_logic_connections(player, world, spots, connect_external=fix_fake_worlds)
|
||||||
|
|
||||||
# Add the new Ice path (back of bomb drop to front) to the world and model it properly
|
# Add the new Ice path (back of bomb drop to front) to the world and model it properly
|
||||||
clip_door = create_door(player, "Ice Bomb Drop Clip", DoorType.Logical)
|
clip_door = create_door(player, "Ice Bomb Drop Clip", DoorType.Logical)
|
||||||
@@ -87,39 +55,20 @@ def create_hybridmajor_connections(world, player):
|
|||||||
)
|
)
|
||||||
connect_simple_door(world, "Ice Bomb Drop Clip", "Ice Bomb Drop", player)
|
connect_simple_door(world, "Ice Bomb Drop Clip", "Ice Bomb Drop", player)
|
||||||
|
|
||||||
|
def get_hybridmajor_connection_entrances():
|
||||||
# Turn dungeons into connectors
|
connections = []
|
||||||
def create_hybridmajor_connectors(world, player):
|
|
||||||
for connectors in [
|
|
||||||
kikiskip_connectors,
|
|
||||||
mirehera_connectors,
|
|
||||||
heraswamp_connectors,
|
|
||||||
thievesdesert_connectors,
|
|
||||||
specrock_connectors,
|
|
||||||
]:
|
|
||||||
new_connectors = [
|
|
||||||
(
|
|
||||||
connector[0],
|
|
||||||
connector[1],
|
|
||||||
world.get_entrance(connector[2], player).connected_region,
|
|
||||||
)
|
|
||||||
for connector in connectors
|
|
||||||
]
|
|
||||||
create_no_logic_connections(player, world, new_connectors)
|
|
||||||
|
|
||||||
|
|
||||||
def get_hybridmajor_connector_entrances():
|
|
||||||
connectors = []
|
|
||||||
for connector in (
|
for connector in (
|
||||||
kikiskip_connectors
|
kikiskip_spots
|
||||||
+ mirehera_connectors
|
+ mirehera_spots
|
||||||
+ heraswamp_connectors
|
+ heraswamp_spots
|
||||||
+ thievesdesert_connectors
|
+ icepalace_spots
|
||||||
+ specrock_connectors
|
+ thievesdesert_spots
|
||||||
):
|
+ specrock_spots
|
||||||
connectors.append(connector[0])
|
+ paradox_spots
|
||||||
return set(connectors)
|
):
|
||||||
|
connections.append(connector[0])
|
||||||
|
connections.append('Ice Bomb Drop Clip')
|
||||||
|
return set(connections)
|
||||||
|
|
||||||
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
|
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
|
||||||
# This creates a copy of the input state that has Moon Pearl.
|
# This creates a copy of the input state that has Moon Pearl.
|
||||||
@@ -151,11 +100,6 @@ def dungeon_reentry_rules(
|
|||||||
+ thievesdesert_spots
|
+ thievesdesert_spots
|
||||||
+ specrock_spots
|
+ specrock_spots
|
||||||
+ paradox_spots
|
+ paradox_spots
|
||||||
+ kikiskip_connectors
|
|
||||||
+ mirehera_connectors
|
|
||||||
+ heraswamp_connectors
|
|
||||||
+ thievesdesert_connectors
|
|
||||||
+ specrock_connectors
|
|
||||||
]
|
]
|
||||||
|
|
||||||
dungeon_entrance = [
|
dungeon_entrance = [
|
||||||
@@ -239,23 +183,12 @@ def underworld_glitches_rules(world, player):
|
|||||||
|
|
||||||
def dash_clip(state, region, player):
|
def dash_clip(state, region, player):
|
||||||
return state.can_dash_clip(region, player)
|
return state.can_dash_clip(region, player)
|
||||||
|
|
||||||
if not world.fix_palaceofdarkness_exit[player]:
|
|
||||||
Rules.set_rule(
|
|
||||||
world.get_entrance("Palace of Darkness HMG Exit", player),
|
|
||||||
lambda state: state.can_reach(world.get_entrance("Palace of Darkness", player), player),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Bomb clips
|
# Bomb clips
|
||||||
for clip in (
|
for clip in (
|
||||||
kikiskip_spots
|
kikiskip_spots
|
||||||
+ icepalace_spots
|
+ icepalace_spots
|
||||||
+ thievesdesert_spots
|
+ thievesdesert_spots
|
||||||
+ specrock_spots
|
+ specrock_spots
|
||||||
+ kikiskip_connectors
|
|
||||||
+ thievesdesert_connectors
|
|
||||||
+ specrock_connectors
|
|
||||||
):
|
):
|
||||||
region = world.get_region(clip[1], player)
|
region = world.get_region(clip[1], player)
|
||||||
Rules.set_rule(
|
Rules.set_rule(
|
||||||
@@ -279,15 +212,7 @@ def underworld_glitches_rules(world, player):
|
|||||||
spot[2],
|
spot[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
for connector in kikiskip_connectors + thievesdesert_connectors:
|
for clip in mirehera_spots:
|
||||||
dungeon_reentry_rules(
|
|
||||||
world,
|
|
||||||
player,
|
|
||||||
world.get_entrance(connector[0], player),
|
|
||||||
world.get_entrance(connector[2], player).parent_region.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
for clip in mirehera_spots + mirehera_connectors:
|
|
||||||
Rules.set_rule(
|
Rules.set_rule(
|
||||||
world.get_entrance(clip[0], player),
|
world.get_entrance(clip[0], player),
|
||||||
lambda state: mire_clip(state),
|
lambda state: mire_clip(state),
|
||||||
@@ -395,7 +320,7 @@ def underworld_glitches_rules(world, player):
|
|||||||
|
|
||||||
for desert_exit in desert_exits:
|
for desert_exit in desert_exits:
|
||||||
Rules.add_rule(
|
Rules.add_rule(
|
||||||
world.get_entrance(f"Thieves to Desert {desert_exit} Connector", player),
|
world.get_entrance(f"Thieves to Desert {desert_exit} Clip", player),
|
||||||
lambda state: state.can_dash_clip(
|
lambda state: state.can_dash_clip(
|
||||||
world.get_region("Thieves Attic", player), player
|
world.get_region("Thieves Attic", player), player
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ start_inventory:
|
|||||||
placements:
|
placements:
|
||||||
1:
|
1:
|
||||||
Desert Palace - Boss: Moon Pearl
|
Desert Palace - Boss: Moon Pearl
|
||||||
|
Desert Palace - Prize: Green Pendant
|
||||||
|
Sahasrahla: Magic Mirror
|
||||||
entrances:
|
entrances:
|
||||||
1:
|
1:
|
||||||
two-way:
|
two-way:
|
||||||
@@ -22,4 +24,6 @@ entrances:
|
|||||||
Thieves Town: Thieves Town Exit
|
Thieves Town: Thieves Town Exit
|
||||||
Hyrule Castle Entrance (East): Desert Palace Exit (South)
|
Hyrule Castle Entrance (East): Desert Palace Exit (South)
|
||||||
Hyrule Castle Entrance (West): Desert Palace Exit (North)
|
Hyrule Castle Entrance (West): Desert Palace Exit (North)
|
||||||
|
entrances:
|
||||||
|
Agahnims Tower: Pyramid Fairy
|
||||||
|
|
||||||
|
|||||||
13
test/suite/hmg/pod_mp.yaml
Normal file
13
test/suite/hmg/pod_mp.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
meta:
|
||||||
|
players: 1
|
||||||
|
settings:
|
||||||
|
1:
|
||||||
|
logic: hybridglitches
|
||||||
|
mode: open
|
||||||
|
shuffle: vanilla
|
||||||
|
start_inventory:
|
||||||
|
1:
|
||||||
|
- Pegasus Boots
|
||||||
|
placements:
|
||||||
|
1:
|
||||||
|
Palace of Darkness - Shooter Room: Moon Pearl
|
||||||
Reference in New Issue
Block a user