Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -310,7 +310,7 @@ class World(object):
|
|||||||
return self.is_tile_swapped(0x03, player) and self.is_tile_swapped(0x1b, player)
|
return self.is_tile_swapped(0x03, player) and self.is_tile_swapped(0x1b, player)
|
||||||
|
|
||||||
def is_bombshop_start(self, player):
|
def is_bombshop_start(self, player):
|
||||||
return self.is_tile_swapped(0x2c, player) and (self.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not self.shufflelinks[player])
|
return self.is_tile_swapped(0x2c, player)
|
||||||
|
|
||||||
def is_pyramid_open(self, player):
|
def is_pyramid_open(self, player):
|
||||||
if self.open_pyramid[player] == 'yes':
|
if self.open_pyramid[player] == 'yes':
|
||||||
@@ -318,7 +318,7 @@ class World(object):
|
|||||||
elif self.open_pyramid[player] == 'no':
|
elif self.open_pyramid[player] == 'no':
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
if self.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district']:
|
||||||
return False
|
return False
|
||||||
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt']:
|
elif self.goal[player] in ['crystals', 'trinity', 'ganonhunt']:
|
||||||
return True
|
return True
|
||||||
@@ -3464,7 +3464,7 @@ class Pot(object):
|
|||||||
# byte 0: DDDE EEEE (DR, ER)
|
# byte 0: DDDE EEEE (DR, ER)
|
||||||
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4}
|
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4}
|
||||||
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
|
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
|
||||||
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, "swapped": 10}
|
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, "swapped": 10, "district": 11}
|
||||||
|
|
||||||
# byte 1: LLLW WSS? (logic, mode, sword)
|
# byte 1: LLLW WSS? (logic, mode, sword)
|
||||||
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
|
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
|
||||||
|
|||||||
111
Bosses.py
111
Bosses.py
@@ -4,11 +4,12 @@ import RaceRandom as random
|
|||||||
from BaseClasses import Boss, FillError
|
from BaseClasses import Boss, FillError
|
||||||
|
|
||||||
|
|
||||||
def BossFactory(boss, player):
|
def BossFactory(boss, player, on_ice=False):
|
||||||
if boss is None:
|
if boss is None:
|
||||||
return None
|
return None
|
||||||
if boss in boss_table:
|
if boss in boss_table:
|
||||||
enemizer_name, defeat_rule = boss_table[boss]
|
enemizer_name, normal_defeat_rule, ice_defeat_rule = boss_table[boss]
|
||||||
|
defeat_rule = ice_defeat_rule if on_ice else normal_defeat_rule
|
||||||
return Boss(boss, enemizer_name, defeat_rule, player)
|
return Boss(boss, enemizer_name, defeat_rule, player)
|
||||||
|
|
||||||
logging.getLogger('').error('Unknown Boss: %s', boss)
|
logging.getLogger('').error('Unknown Boss: %s', boss)
|
||||||
@@ -41,16 +42,21 @@ def MoldormDefeatRule(state, player):
|
|||||||
def HelmasaurKingDefeatRule(state, player):
|
def HelmasaurKingDefeatRule(state, player):
|
||||||
return (state.has('Hammer', player) or state.can_use_bombs(player)) and (state.has_sword(player) or state.can_shoot_arrows(player))
|
return (state.has('Hammer', player) or state.can_use_bombs(player)) and (state.has_sword(player) or state.can_shoot_arrows(player))
|
||||||
|
|
||||||
|
|
||||||
|
def IceHelmasaurKingDefeatRule(state, player):
|
||||||
|
return state.can_use_bombs(player) and (state.has_sword(player) or state.can_shoot_arrows(player))
|
||||||
|
|
||||||
|
|
||||||
def ArrghusDefeatRule(state, player):
|
def ArrghusDefeatRule(state, player):
|
||||||
if not state.has('Hookshot', player):
|
if not state.has('Hookshot', player):
|
||||||
return False
|
return False
|
||||||
# TODO: ideally we would have a check for bow and silvers, which combined with the
|
|
||||||
# hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature
|
|
||||||
# makes this complicated
|
|
||||||
if state.has_blunt_weapon(player):
|
if state.has_blunt_weapon(player):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or #assuming mostly gitting two puff with one shot
|
if state.can_shoot_arrows(player) and state.has('Silver Arrows', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or # assuming mostly getting two puffs with one shot
|
||||||
(state.has('Ice Rod', player) and state.can_use_bombs(player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16))))
|
(state.has('Ice Rod', player) and state.can_use_bombs(player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16))))
|
||||||
|
|
||||||
|
|
||||||
@@ -64,9 +70,27 @@ def MothulaDefeatRule(state, player):
|
|||||||
(state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16))
|
(state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def BlindDefeatRule(state, player):
|
def BlindDefeatRule(state, player):
|
||||||
return state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player)
|
return state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player)
|
||||||
|
|
||||||
|
|
||||||
|
def IceBlindDefeatRule(state, player):
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
# weapon
|
||||||
|
state.has_beam_sword(player) or
|
||||||
|
state.has('Cane of Somaria', player) or
|
||||||
|
(state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16))
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
# protection
|
||||||
|
state.has('Red Shield', player) or
|
||||||
|
(state.has('Cane of Byrna', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert'])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def KholdstareDefeatRule(state, player):
|
def KholdstareDefeatRule(state, player):
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
@@ -90,9 +114,39 @@ def KholdstareDefeatRule(state, player):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def IceKholdstareDefeatRule(state, player):
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
state.has('Fire Rod', player) or
|
||||||
|
(
|
||||||
|
state.has('Bombos', player) and
|
||||||
|
# FIXME: the following only actually works for the vanilla location for swordless
|
||||||
|
(state.has_sword(player) or state.world.swords[player] == 'swordless')
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
state.has_beam_sword(player) or
|
||||||
|
(state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or
|
||||||
|
# FIXME: this actually only works for the vanilla location for swordless
|
||||||
|
(
|
||||||
|
state.has('Fire Rod', player) and
|
||||||
|
state.has('Bombos', player) and
|
||||||
|
(state.has_sword(player) or state.world.swords[player] == 'swordless') and
|
||||||
|
state.can_extend_magic(player, 16)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def VitreousDefeatRule(state, player):
|
def VitreousDefeatRule(state, player):
|
||||||
return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_blunt_weapon(player)
|
return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_blunt_weapon(player)
|
||||||
|
|
||||||
|
|
||||||
|
def IceVitreousDefeatRule(state, player):
|
||||||
|
return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_beam_sword(player)
|
||||||
|
|
||||||
|
|
||||||
def TrinexxDefeatRule(state, player):
|
def TrinexxDefeatRule(state, player):
|
||||||
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
|
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
|
||||||
return False
|
return False
|
||||||
@@ -102,24 +156,36 @@ def TrinexxDefeatRule(state, player):
|
|||||||
(state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or
|
(state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or
|
||||||
(state.has_sword(player) and state.can_extend_magic(player, 32)))
|
(state.has_sword(player) and state.can_extend_magic(player, 32)))
|
||||||
|
|
||||||
|
|
||||||
|
def IceTrinexxDefeatRule(state, player):
|
||||||
|
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player) and state.has_Boots(player)):
|
||||||
|
return False
|
||||||
|
return (state.has('Golden Sword', player) or
|
||||||
|
(state.has('Tempered Sword', player) and state.can_extend_magic(player, 16)) or
|
||||||
|
((state.has('Hammer', player) or
|
||||||
|
state.has('Master Sword', player)) and state.can_extend_magic(player, 32))) # rod spam rule
|
||||||
|
|
||||||
|
|
||||||
def AgahnimDefeatRule(state, player):
|
def AgahnimDefeatRule(state, player):
|
||||||
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
||||||
|
|
||||||
|
|
||||||
boss_table = {
|
boss_table = {
|
||||||
'Armos Knights': ('Armos', ArmosKnightsDefeatRule),
|
'Armos Knights': ('Armos', ArmosKnightsDefeatRule, ArmosKnightsDefeatRule),
|
||||||
'Lanmolas': ('Lanmola', LanmolasDefeatRule),
|
'Lanmolas': ('Lanmola', LanmolasDefeatRule, LanmolasDefeatRule),
|
||||||
'Moldorm': ('Moldorm', MoldormDefeatRule),
|
'Moldorm': ('Moldorm', MoldormDefeatRule, MoldormDefeatRule),
|
||||||
'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule),
|
'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule, IceHelmasaurKingDefeatRule),
|
||||||
'Arrghus': ('Arrghus', ArrghusDefeatRule),
|
'Arrghus': ('Arrghus', ArrghusDefeatRule, ArrghusDefeatRule),
|
||||||
'Mothula': ('Mothula', MothulaDefeatRule),
|
'Mothula': ('Mothula', MothulaDefeatRule, MothulaDefeatRule),
|
||||||
'Blind': ('Blind', BlindDefeatRule),
|
'Blind': ('Blind', BlindDefeatRule, IceBlindDefeatRule),
|
||||||
'Kholdstare': ('Kholdstare', KholdstareDefeatRule),
|
'Kholdstare': ('Kholdstare', KholdstareDefeatRule, IceKholdstareDefeatRule),
|
||||||
'Vitreous': ('Vitreous', VitreousDefeatRule),
|
'Vitreous': ('Vitreous', VitreousDefeatRule, IceVitreousDefeatRule),
|
||||||
'Trinexx': ('Trinexx', TrinexxDefeatRule),
|
'Trinexx': ('Trinexx', TrinexxDefeatRule, IceTrinexxDefeatRule),
|
||||||
'Agahnim': ('Agahnim', AgahnimDefeatRule),
|
'Agahnim': ('Agahnim', AgahnimDefeatRule, AgahnimDefeatRule),
|
||||||
'Agahnim2': ('Agahnim2', AgahnimDefeatRule)
|
'Agahnim2': ('Agahnim2', AgahnimDefeatRule, AgahnimDefeatRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def can_place_boss(world, player, boss, dungeon_name, level=None):
|
def can_place_boss(world, player, boss, dungeon_name, level=None):
|
||||||
if world.swords[player] in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace':
|
if world.swords[player] in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace':
|
||||||
return False
|
return False
|
||||||
@@ -132,6 +198,11 @@ def can_place_boss(world, player, boss, dungeon_name, level=None):
|
|||||||
if boss in ["Blind"]:
|
if boss in ["Blind"]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# no Trinexx on Ice in doors without doing some health modelling
|
||||||
|
if world.doorShuffle[player] != 'vanilla' and boss == 'Trinexx':
|
||||||
|
if dungeon_name == 'Ganons Tower' and level == 'bottom':
|
||||||
|
return False
|
||||||
|
|
||||||
if dungeon_name == 'Tower of Hera' and boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]:
|
if dungeon_name == 'Tower of Hera' and boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -151,6 +222,7 @@ def place_bosses(world, player):
|
|||||||
['Tower of Hera', None],
|
['Tower of Hera', None],
|
||||||
['Skull Woods', None],
|
['Skull Woods', None],
|
||||||
['Ganons Tower', 'middle'],
|
['Ganons Tower', 'middle'],
|
||||||
|
['Ganons Tower', 'bottom'],
|
||||||
['Eastern Palace', None],
|
['Eastern Palace', None],
|
||||||
['Desert Palace', None],
|
['Desert Palace', None],
|
||||||
['Palace of Darkness', None],
|
['Palace of Darkness', None],
|
||||||
@@ -159,7 +231,6 @@ def place_bosses(world, player):
|
|||||||
['Ice Palace', None],
|
['Ice Palace', None],
|
||||||
['Misery Mire', None],
|
['Misery Mire', None],
|
||||||
['Turtle Rock', None],
|
['Turtle Rock', None],
|
||||||
['Ganons Tower', 'bottom'],
|
|
||||||
]
|
]
|
||||||
|
|
||||||
all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons
|
all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons
|
||||||
@@ -246,4 +317,4 @@ def place_boss(boss, level, loc, loc_text, world, player):
|
|||||||
loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0]
|
loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0]
|
||||||
loc_text = loc + ' (' + level + ')'
|
loc_text = loc + ' (' + level + ')'
|
||||||
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
|
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
|
||||||
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)
|
world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player, level == 'bottom')
|
||||||
|
|||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,10 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.3.4.1
|
||||||
|
- Implemented new District ER mode option
|
||||||
|
- Added alternate boss logic when in GT Ice Basement
|
||||||
|
- Updated Inverted 2.0 to start in Bomb Shop regardless of ER mode
|
||||||
|
- Fixed broken customizer features with OWR Tile Flip
|
||||||
|
- Allowing Insanity ER + Standard to decouple standard entrances
|
||||||
|
|
||||||
## 0.3.4.0
|
## 0.3.4.0
|
||||||
- \~Merged in some things from DR v1.4.0.0-v~
|
- \~Merged in DR v1.2.0.23~
|
||||||
- Improved bunny-walking algorithm
|
- Improved bunny-walking algorithm
|
||||||
- Improved multiworld balancing
|
- Improved multiworld balancing
|
||||||
- Implemented Hyrid Major Glitches logic (thanks Muffins/Espeon)
|
- \~Merged in some things from DR v1.4.0.0-v~
|
||||||
|
- Implemented Hybrid Major Glitches logic (thanks Muffins/Espeon)
|
||||||
- Added sparkles to Bonk Drop locations for better visibility
|
- Added sparkles to Bonk Drop locations for better visibility
|
||||||
- Some tweaks/improvements to Shuffle Song Instruments
|
- Some tweaks/improvements to Shuffle Song Instruments
|
||||||
- Replaced Save Settings on Exit with Settings on Load
|
- Replaced Save Settings on Exit with Settings on Load
|
||||||
|
|||||||
@@ -262,23 +262,24 @@ def vanilla_key_logic(world, player):
|
|||||||
world.key_layout[player][builder.name] = key_layout
|
world.key_layout[player][builder.name] = key_layout
|
||||||
log_key_logic(builder.name, key_layout.key_logic)
|
log_key_logic(builder.name, key_layout.key_logic)
|
||||||
# special adjustments for vanilla
|
# special adjustments for vanilla
|
||||||
if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none':
|
if world.keyshuffle[player] != 'universal':
|
||||||
# adjust hc doors
|
if world.mode[player] != 'standard' and not world.dropshuffle[player]:
|
||||||
def adjust_hc_door(door_rule):
|
# adjust hc doors
|
||||||
if door_rule.new_rules[KeyRuleType.WorstCase] == 3:
|
def adjust_hc_door(door_rule):
|
||||||
door_rule.new_rules[KeyRuleType.WorstCase] = 2
|
if door_rule.new_rules[KeyRuleType.WorstCase] == 3:
|
||||||
door_rule.small_key_num = 2
|
door_rule.new_rules[KeyRuleType.WorstCase] = 2
|
||||||
|
door_rule.small_key_num = 2
|
||||||
|
|
||||||
rules = world.key_logic[player]['Hyrule Castle'].door_rules
|
rules = world.key_logic[player]['Hyrule Castle'].door_rules
|
||||||
adjust_hc_door(rules['Sewers Secret Room Key Door S'])
|
adjust_hc_door(rules['Sewers Secret Room Key Door S'])
|
||||||
adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S'])
|
adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S'])
|
||||||
adjust_hc_door(rules['Sewers Dark Cross Key Door N'])
|
adjust_hc_door(rules['Sewers Dark Cross Key Door N'])
|
||||||
# adjust pod front door
|
# adjust pod front door
|
||||||
pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N']
|
pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N']
|
||||||
if pod_front.new_rules[KeyRuleType.WorstCase] == 6:
|
if pod_front.new_rules[KeyRuleType.WorstCase] == 6:
|
||||||
pod_front.new_rules[KeyRuleType.WorstCase] = 1
|
pod_front.new_rules[KeyRuleType.WorstCase] = 1
|
||||||
pod_front.small_key_num = 1
|
pod_front.small_key_num = 1
|
||||||
# gt logic? I'm unsure it needs adjusting
|
# gt logic? I'm unsure it needs adjusting
|
||||||
|
|
||||||
|
|
||||||
def validate_vanilla_reservation(dungeon, world, player):
|
def validate_vanilla_reservation(dungeon, world, player):
|
||||||
|
|||||||
@@ -986,7 +986,8 @@ def balance_prices(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def check_hints(world, player):
|
def check_hints(world, player):
|
||||||
if world.shuffle[player] in ['simple', 'restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity']:
|
if (world.shuffle[player] in ['simple', 'restricted', 'full', 'district', 'swapped', 'crossed', 'insanity']
|
||||||
|
or (world.shuffle[player] in ['lite', 'lean'] and world.shopsanity[player])):
|
||||||
for shop, location_list in shop_to_location_table.items():
|
for shop, location_list in shop_to_location_table.items():
|
||||||
if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']:
|
if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']:
|
||||||
continue # near the queen, near potions, and near 7 chests are fine
|
continue # near the queen, near potions, and near 7 chests are fine
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -37,7 +37,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
|
|||||||
from source.tools.BPS import create_bps_from_data
|
from source.tools.BPS import create_bps_from_data
|
||||||
from source.classes.CustomSettings import CustomSettings
|
from source.classes.CustomSettings import CustomSettings
|
||||||
|
|
||||||
version_number = '1.2.0.22'
|
version_number = '1.2.0.23'
|
||||||
version_branch = '-u'
|
version_branch = '-u'
|
||||||
__version__ = f'{version_number}{version_branch}'
|
__version__ = f'{version_number}{version_branch}'
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
|
|||||||
from OverworldGlitchRules import create_owg_connections
|
from OverworldGlitchRules import create_owg_connections
|
||||||
from Utils import bidict
|
from Utils import bidict
|
||||||
|
|
||||||
version_number = '0.3.4.0'
|
version_number = '0.3.4.1'
|
||||||
# branch indicator is intentionally different across branches
|
# branch indicator is intentionally different across branches
|
||||||
version_branch = ''
|
version_branch = ''
|
||||||
|
|
||||||
@@ -1044,10 +1044,10 @@ def shuffle_tiles(world, groups, result_list, do_grouped, forced_flips, player):
|
|||||||
raise GenerationException('Could not find valid tile flips')
|
raise GenerationException('Could not find valid tile flips')
|
||||||
|
|
||||||
# tile shuffle happens here
|
# tile shuffle happens here
|
||||||
removed = copy.deepcopy(nonflipped_groups)
|
removed = []
|
||||||
if 0 < undefined_chance < 100:
|
if 0 < undefined_chance < 100:
|
||||||
for group in [g for g in groups if g not in nonflipped_groups]:
|
for group in groups:
|
||||||
if group not in flipped_groups and random.randint(1, 100) > undefined_chance:
|
if group[0] in nonflipped_groups or (group[0] not in flipped_groups and random.randint(1, 100) > undefined_chance):
|
||||||
removed.append(group)
|
removed.append(group)
|
||||||
|
|
||||||
# save shuffled tiles to list
|
# save shuffled tiles to list
|
||||||
@@ -1072,7 +1072,7 @@ def shuffle_tiles(world, groups, result_list, do_grouped, forced_flips, player):
|
|||||||
attempts -= 1
|
attempts -= 1
|
||||||
continue
|
continue
|
||||||
# ensure sanc can be placed in LW in certain modes
|
# ensure sanc can be placed in LW in certain modes
|
||||||
if not do_grouped and world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lean', 'swapped', 'crossed', 'insanity'] and world.mode[player] != 'inverted' and (world.doorShuffle[player] != 'crossed' or world.intensity[player] < 3 or world.mode[player] == 'standard'):
|
if not do_grouped and world.shuffle[player] in ['simple', 'restricted', 'full', 'district'] and world.mode[player] != 'inverted' and (world.doorShuffle[player] != 'crossed' or world.intensity[player] < 3 or world.mode[player] == 'standard'):
|
||||||
free_dw_drops = parity[5] + (1 if world.shuffle_ganon[player] else 0)
|
free_dw_drops = parity[5] + (1 if world.shuffle_ganon[player] else 0)
|
||||||
free_drops = 6 + (1 if world.mode[player] != 'standard' else 0) + (1 if world.shuffle_ganon[player] else 0)
|
free_drops = 6 + (1 if world.mode[player] != 'standard' else 0) + (1 if world.shuffle_ganon[player] else 0)
|
||||||
if free_dw_drops == free_drops:
|
if free_dw_drops == free_drops:
|
||||||
@@ -1124,7 +1124,7 @@ def define_tile_groups(world, do_grouped, player):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# sanctuary/chapel should not be flipped if S+Q guaranteed to output on that screen
|
# sanctuary/chapel should not be flipped if S+Q guaranteed to output on that screen
|
||||||
if 0x13 in group and ((world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] \
|
if 0x13 in group and ((world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district'] \
|
||||||
and (world.mode[player] in ['standard', 'inverted'] or world.doorShuffle[player] != 'crossed' or world.intensity[player] < 3)) \
|
and (world.mode[player] in ['standard', 'inverted'] or world.doorShuffle[player] != 'crossed' or world.intensity[player] < 3)) \
|
||||||
or (world.shuffle[player] in ['lite', 'lean'] and world.mode[player] == 'inverted')):
|
or (world.shuffle[player] in ['lite', 'lean'] and world.mode[player] == 'inverted')):
|
||||||
return False
|
return False
|
||||||
@@ -1138,24 +1138,31 @@ def define_tile_groups(world, do_grouped, player):
|
|||||||
groups.append([0x80])
|
groups.append([0x80])
|
||||||
groups.append([0x81])
|
groups.append([0x81])
|
||||||
|
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple']:
|
# hyrule castle and sanctuary connector
|
||||||
merge_groups([[0x03, 0x0a], [0x28, 0x29]])
|
if world.shuffle[player] in ['vanilla', 'district'] or (world.mode[player] == 'standard' and world.shuffle[player] in ['dungeonssimple', 'dungeonsfull']):
|
||||||
|
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite']:
|
|
||||||
merge_groups([[0x13, 0x14]])
|
|
||||||
|
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'simple', 'restricted']:
|
|
||||||
merge_groups([[0x05, 0x07]])
|
|
||||||
|
|
||||||
if world.shuffle[player] == 'vanilla' or (world.mode[player] == 'standard' and world.shuffle[player] in ['dungeonssimple', 'dungeonsfull']):
|
|
||||||
merge_groups([[0x13, 0x14, 0x1b]])
|
merge_groups([[0x13, 0x14, 0x1b]])
|
||||||
|
|
||||||
|
# sanctuary and grave connector
|
||||||
|
if world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite']:
|
||||||
|
merge_groups([[0x13, 0x14]])
|
||||||
|
|
||||||
|
# cross-screen connector
|
||||||
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'district']:
|
||||||
|
merge_groups([[0x03, 0x0a], [0x28, 0x29]])
|
||||||
|
|
||||||
|
# turtle rock connector
|
||||||
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'simple', 'restricted', 'district']:
|
||||||
|
merge_groups([[0x05, 0x07]])
|
||||||
|
|
||||||
|
# all non-parallel screens
|
||||||
if world.owShuffle[player] == 'vanilla' and (world.owCrossed[player] == 'none' or do_grouped):
|
if world.owShuffle[player] == 'vanilla' and (world.owCrossed[player] == 'none' or do_grouped):
|
||||||
merge_groups([[0x00, 0x2d, 0x80], [0x0f, 0x81], [0x1a, 0x1b], [0x28, 0x29], [0x30, 0x3a]])
|
merge_groups([[0x00, 0x2d, 0x80], [0x0f, 0x81], [0x1a, 0x1b], [0x28, 0x29], [0x30, 0x3a]])
|
||||||
|
|
||||||
|
# special case: non-parallel keep similar
|
||||||
if world.owShuffle[player] == 'parallel' and world.owKeepSimilar[player] and (world.owCrossed[player] == 'none' or do_grouped):
|
if world.owShuffle[player] == 'parallel' and world.owKeepSimilar[player] and (world.owCrossed[player] == 'none' or do_grouped):
|
||||||
merge_groups([[0x28, 0x29]])
|
merge_groups([[0x28, 0x29]])
|
||||||
|
|
||||||
|
# whirlpool screens
|
||||||
if not world.owWhirlpoolShuffle[player] and (world.owCrossed[player] == 'none' or do_grouped):
|
if not world.owWhirlpoolShuffle[player] and (world.owCrossed[player] == 'none' or do_grouped):
|
||||||
merge_groups([[0x0f, 0x35], [0x12, 0x15, 0x33, 0x3f]])
|
merge_groups([[0x0f, 0x35], [0x12, 0x15, 0x33, 0x3f]])
|
||||||
|
|
||||||
@@ -1562,7 +1569,7 @@ def validate_layout(world, player):
|
|||||||
for dest_region in entrance_connectors[region_name]:
|
for dest_region in entrance_connectors[region_name]:
|
||||||
if dest_region not in explored_regions:
|
if dest_region not in explored_regions:
|
||||||
explore_region(dest_region)
|
explore_region(dest_region)
|
||||||
if world.shuffle[player] not in ['insanity'] and region_name in sane_connectors:
|
if world.shuffle[player] not in ['district', 'insanity'] and region_name in sane_connectors:
|
||||||
for dest_region in sane_connectors[region_name]:
|
for dest_region in sane_connectors[region_name]:
|
||||||
if dest_region not in explored_regions:
|
if dest_region not in explored_regions:
|
||||||
explore_region(dest_region)
|
explore_region(dest_region)
|
||||||
@@ -1619,11 +1626,13 @@ def validate_layout(world, player):
|
|||||||
break
|
break
|
||||||
# check if entrances in region could be used to access region
|
# check if entrances in region could be used to access region
|
||||||
if world.shuffle[player] != 'vanilla':
|
if world.shuffle[player] != 'vanilla':
|
||||||
|
# TODO: For District ER, we need to check if there is a dropdown or connector that is able to connect
|
||||||
for entrance in [e for e in unreachable_regions[region_name].exits if e.spot_type == 'Entrance']:
|
for entrance in [e for e in unreachable_regions[region_name].exits if e.spot_type == 'Entrance']:
|
||||||
if (entrance.name == 'Links House' and (world.mode[player] == 'inverted' or not world.shufflelinks[player] or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \
|
if (entrance.name == 'Links House' and ((not world.is_bombshop_start(player) and not world.shufflelinks[player]) or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \
|
||||||
or (entrance.name == 'Big Bomb Shop' and (world.mode[player] != 'inverted' or not world.shufflelinks[player] or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \
|
or (entrance.name == 'Big Bomb Shop' and ((world.is_bombshop_start(player) and not world.shufflelinks[player]) or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \
|
||||||
or (entrance.name == 'Ganons Tower' and (world.mode[player] != 'inverted' and not world.shuffle_ganon[player])) \
|
or (entrance.name == 'Ganons Tower' and (not world.is_atgt_swapped(player) and not world.shuffle_ganon[player])) \
|
||||||
or (entrance.name in ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] and world.shuffle[player] not in ['insanity']) \
|
or (entrance.name == 'Agahnims Tower' and (world.is_atgt_swapped(player) and not world.shuffle_ganon[player])) \
|
||||||
|
or (entrance.name in ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] and world.shuffle[player] not in ['district', 'insanity']) \
|
||||||
or (entrance.name == 'Tavern North' and not world.shuffletavern[player]):
|
or (entrance.name == 'Tavern North' and not world.shuffletavern[player]):
|
||||||
continue # these are fixed entrances and cannot be used for gaining access to region
|
continue # these are fixed entrances and cannot be used for gaining access to region
|
||||||
if entrance.name not in drop_entrances \
|
if entrance.name not in drop_entrances \
|
||||||
|
|||||||
@@ -111,9 +111,6 @@ These are now independent of retro mode and have three options: None, Random, an
|
|||||||
|
|
||||||
* 1.4.0.1v
|
* 1.4.0.1v
|
||||||
* Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door
|
* Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door
|
||||||
* 1.4.0.0v
|
|
||||||
* Generation: fix for bunny walk logic taking up too much memory
|
|
||||||
* Key Logic: Partial is now the new default
|
|
||||||
* 1.3.0.9v
|
* 1.3.0.9v
|
||||||
* Ganonhunt: playthrough no longer collects crystals
|
* Ganonhunt: playthrough no longer collects crystals
|
||||||
* Vanilla Fill: Uncle weapon is always a sword, medallions for Mire/TR will be vanilla
|
* Vanilla Fill: Uncle weapon is always a sword, medallions for Mire/TR will be vanilla
|
||||||
@@ -123,7 +120,10 @@ These are now independent of retro mode and have three options: None, Random, an
|
|||||||
* MW Progression Balancing: Change to be percentage based instead of raw count. (80% threshold)
|
* MW Progression Balancing: Change to be percentage based instead of raw count. (80% threshold)
|
||||||
* Take anys: Good Bee cave chosen as take any should no longer prevent generation
|
* Take anys: Good Bee cave chosen as take any should no longer prevent generation
|
||||||
* Money balancing: Fixed generation issue
|
* Money balancing: Fixed generation issue
|
||||||
1.2.0.22u
|
1.2.0.23u
|
||||||
|
* Generation: fix for bunny walk logic taking up too much memory
|
||||||
|
* Key Logic: Partial is now the new default
|
||||||
|
* 1.2.0.22u
|
||||||
* Flute can't be activated in rain state (except glitched modes) (Thanks codemann!)
|
* Flute can't be activated in rain state (except glitched modes) (Thanks codemann!)
|
||||||
* ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used)
|
* ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used)
|
||||||
* Logic issues:
|
* Logic issues:
|
||||||
|
|||||||
26
Rom.py
26
Rom.py
@@ -799,7 +799,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
|
|
||||||
owFlags |= 0x0200
|
owFlags |= 0x0200
|
||||||
|
|
||||||
# setting spriteID to D8, a placeholder sprite we use to inform ROM to spawn a dynamic item
|
# setting spriteID to D9, a placeholder sprite we use to inform ROM to spawn a dynamic item
|
||||||
#for address in bonk_addresses:
|
#for address in bonk_addresses:
|
||||||
for address in [b for b in bonk_addresses if b != 0x4D0AE]: # temp fix for screen 1A murahdahla sprite replacement
|
for address in [b for b in bonk_addresses if b != 0x4D0AE]: # temp fix for screen 1A murahdahla sprite replacement
|
||||||
rom.write_byte(address, 0xD9)
|
rom.write_byte(address, 0xD9)
|
||||||
@@ -1549,7 +1549,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
# b - Big Key
|
# b - Big Key
|
||||||
# a - Small Key
|
# a - Small Key
|
||||||
#
|
#
|
||||||
enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'none'
|
enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla'
|
||||||
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
|
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
|
||||||
| (0x02 if world.bigkeyshuffle[player] else 0x00)
|
| (0x02 if world.bigkeyshuffle[player] else 0x00)
|
||||||
| (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00)
|
| (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00)
|
||||||
@@ -1667,7 +1667,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
# rom.write_byte(snes_to_pc(0x0DB730), 0x08) # allows chickens to travel across water
|
# rom.write_byte(snes_to_pc(0x0DB730), 0x08) # allows chickens to travel across water
|
||||||
|
|
||||||
# allow smith into multi-entrance caves in appropriate shuffles
|
# allow smith into multi-entrance caves in appropriate shuffles
|
||||||
if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
|
if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
|
||||||
rom.write_byte(0x18004C, 0x01)
|
rom.write_byte(0x18004C, 0x01)
|
||||||
|
|
||||||
# set correct flag for hera basement item
|
# set correct flag for hera basement item
|
||||||
@@ -2142,7 +2142,7 @@ def write_strings(rom, world, player, team):
|
|||||||
tt.removeUnwantedText()
|
tt.removeUnwantedText()
|
||||||
|
|
||||||
# Let's keep this guy's text accurate to the shuffle setting.
|
# Let's keep this guy's text accurate to the shuffle setting.
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']:
|
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'lite', 'lean']:
|
||||||
tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
|
tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
|
||||||
tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
|
tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
|
||||||
|
|
||||||
@@ -2193,7 +2193,7 @@ def write_strings(rom, world, player, team):
|
|||||||
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
|
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
|
||||||
if world.shuffle[player] not in ['lite', 'lean']:
|
if world.shuffle[player] not in ['lite', 'lean']:
|
||||||
entrances_to_hint.update(InconvenientOtherEntrances)
|
entrances_to_hint.update(InconvenientOtherEntrances)
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'swapped']:
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'district']:
|
||||||
hint_count = 0
|
hint_count = 0
|
||||||
elif world.shuffle[player] in ['simple', 'restricted']:
|
elif world.shuffle[player] in ['simple', 'restricted']:
|
||||||
hint_count = 2
|
hint_count = 2
|
||||||
@@ -2227,7 +2227,7 @@ def write_strings(rom, world, player, team):
|
|||||||
entrances_to_hint.update(OtherEntrances)
|
entrances_to_hint.update(OtherEntrances)
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'})
|
entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'})
|
||||||
if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean']:
|
if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'district']:
|
||||||
if world.shufflelinks[player]:
|
if world.shufflelinks[player]:
|
||||||
entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'})
|
entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'})
|
||||||
entrances_to_hint.update({'Links House': 'The hero\'s old residence'})
|
entrances_to_hint.update({'Links House': 'The hero\'s old residence'})
|
||||||
@@ -2244,7 +2244,7 @@ def write_strings(rom, world, player, team):
|
|||||||
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
|
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
|
||||||
else:
|
else:
|
||||||
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
|
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
|
||||||
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 0
|
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 0
|
||||||
hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0
|
hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0
|
||||||
for entrance in all_entrances:
|
for entrance in all_entrances:
|
||||||
if entrance.name in entrances_to_hint:
|
if entrance.name in entrances_to_hint:
|
||||||
@@ -2263,7 +2263,7 @@ def write_strings(rom, world, player, team):
|
|||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||||
locations_to_hint.extend(InconvenientVanillaLocations)
|
locations_to_hint.extend(InconvenientVanillaLocations)
|
||||||
random.shuffle(locations_to_hint)
|
random.shuffle(locations_to_hint)
|
||||||
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 5
|
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 5
|
||||||
hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
||||||
del locations_to_hint[hint_count:]
|
del locations_to_hint[hint_count:]
|
||||||
for location in locations_to_hint:
|
for location in locations_to_hint:
|
||||||
@@ -2338,7 +2338,7 @@ def write_strings(rom, world, player, team):
|
|||||||
if world.bigkeyshuffle[player]:
|
if world.bigkeyshuffle[player]:
|
||||||
items_to_hint.extend(BigKeys)
|
items_to_hint.extend(BigKeys)
|
||||||
random.shuffle(items_to_hint)
|
random.shuffle(items_to_hint)
|
||||||
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 8
|
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 8
|
||||||
hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
||||||
hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] != 'none' or world.owMixed[player] else 0
|
hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] != 'none' or world.owMixed[player] else 0
|
||||||
while hint_count > 0 and len(items_to_hint) > 0:
|
while hint_count > 0 and len(items_to_hint) > 0:
|
||||||
@@ -2382,11 +2382,11 @@ def write_strings(rom, world, player, team):
|
|||||||
choices.clear()
|
choices.clear()
|
||||||
choices.append(location_item)
|
choices.append(location_item)
|
||||||
if hint_type == 'foolish':
|
if hint_type == 'foolish':
|
||||||
if district.dungeons and world.shuffle[player] != 'vanilla':
|
if district.dungeons and world.shuffle[player] not in ['vanilla', 'district']:
|
||||||
choices.extend(district.dungeons)
|
choices.extend(district.dungeons)
|
||||||
hint_type = 'dungeon_path'
|
hint_type = 'dungeon_path'
|
||||||
elif district.access_points and world.shuffle[player] not in ['vanilla', 'dungeonssimple',
|
elif district.access_points and world.shuffle[player] not in ['vanilla', 'dungeonssimple',
|
||||||
'dungeonsfull']:
|
'dungeonsfull', 'district']:
|
||||||
choices.extend([x.hint_text for x in district.access_points])
|
choices.extend([x.hint_text for x in district.access_points])
|
||||||
hint_type = 'connector'
|
hint_type = 'connector'
|
||||||
if hint_type == 'foolish':
|
if hint_type == 'foolish':
|
||||||
@@ -2732,10 +2732,6 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
|
|||||||
write_int16s(rom, snes_to_pc(0x1BB810), [0x00BE, 0x00C0, 0x013E]) # update pyramid hole entrance
|
write_int16s(rom, snes_to_pc(0x1BB810), [0x00BE, 0x00C0, 0x013E]) # update pyramid hole entrance
|
||||||
write_int16s(rom, snes_to_pc(0x1BB836), [0x001B, 0x001B, 0x001B])
|
write_int16s(rom, snes_to_pc(0x1BB836), [0x001B, 0x001B, 0x001B])
|
||||||
|
|
||||||
write_int16(rom, snes_to_pc(0x308300), 0x0140) # add extra pyramid hole
|
|
||||||
write_int16(rom, snes_to_pc(0x308320), 0x001B)
|
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
|
||||||
rom.write_byte(snes_to_pc(0x308340), 0x7B)
|
|
||||||
|
|
||||||
rom.write_byte(snes_to_pc(0x00DB9D), 0x1A) # make retreat bat gfx available in HC area
|
rom.write_byte(snes_to_pc(0x00DB9D), 0x1A) # make retreat bat gfx available in HC area
|
||||||
rom.write_byte(snes_to_pc(0x00DC09), 0x1A)
|
rom.write_byte(snes_to_pc(0x00DC09), 0x1A)
|
||||||
|
|||||||
20
Rules.py
20
Rules.py
@@ -1067,7 +1067,7 @@ def ow_inverted_rules(world, player):
|
|||||||
else:
|
else:
|
||||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player)) # barrier gets removed after killing agahnim, rule for that added later
|
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player)) # barrier gets removed after killing agahnim, rule for that added later
|
||||||
set_rule(world.get_entrance('GT Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
set_rule(world.get_entrance('GT Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||||
set_rule(world.get_entrance('GT Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'))
|
set_rule(world.get_entrance('GT Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity'))
|
||||||
|
|
||||||
if world.is_tile_swapped(0x03, player):
|
if world.is_tile_swapped(0x03, player):
|
||||||
set_rule(world.get_entrance('Spectacle Rock Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player))
|
set_rule(world.get_entrance('Spectacle Rock Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player))
|
||||||
@@ -1742,16 +1742,19 @@ def set_bunny_rules(world, player, inverted):
|
|||||||
# for each such entrance a new option is added that consist of:
|
# for each such entrance a new option is added that consist of:
|
||||||
# a) being able to reach it, and
|
# a) being able to reach it, and
|
||||||
# b) being able to access all entrances from there to `region`
|
# b) being able to access all entrances from there to `region`
|
||||||
queue = deque([(region, [], {region})])
|
queue = deque([(region, [], {region}, [region])])
|
||||||
seen_sets = set([frozenset({region})])
|
seen_sets = set([frozenset({region})])
|
||||||
while queue:
|
while queue:
|
||||||
(current, path, seen) = queue.popleft()
|
(current, path, seen, region_path) = queue.popleft()
|
||||||
for entrance in current.entrances:
|
for entrance in current.entrances:
|
||||||
|
if entrance.door and entrance.door.blocked:
|
||||||
|
continue
|
||||||
new_region = entrance.parent_region
|
new_region = entrance.parent_region
|
||||||
new_seen = seen.union({new_region})
|
new_seen = seen.union({new_region})
|
||||||
if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets:
|
if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets:
|
||||||
continue
|
continue
|
||||||
new_path = path + [entrance.access_rule]
|
new_path = path + [entrance.access_rule]
|
||||||
|
new_region_path = region_path + [new_region]
|
||||||
seen_sets.add(frozenset(new_seen))
|
seen_sets.add(frozenset(new_seen))
|
||||||
if not is_link(new_region):
|
if not is_link(new_region):
|
||||||
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||||
@@ -1796,7 +1799,7 @@ def set_bunny_rules(world, player, inverted):
|
|||||||
continue
|
continue
|
||||||
if is_bunny(new_region):
|
if is_bunny(new_region):
|
||||||
# todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense?
|
# todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense?
|
||||||
queue.append((new_region, new_path, new_seen))
|
queue.append((new_region, new_path, new_seen, new_region_path))
|
||||||
else:
|
else:
|
||||||
# we have reached pure light world, so we have a new possible option
|
# we have reached pure light world, so we have a new possible option
|
||||||
possible_options.append(path_to_access_rule(new_path, entrance))
|
possible_options.append(path_to_access_rule(new_path, entrance))
|
||||||
@@ -1838,10 +1841,11 @@ def set_bunny_rules(world, player, inverted):
|
|||||||
continue
|
continue
|
||||||
add_rule(location, get_rule_to_add(region, location))
|
add_rule(location, get_rule_to_add(region, location))
|
||||||
|
|
||||||
for ent_name in bunny_pocket_entrances:
|
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||||
bunny_exit = world.get_entrance(ent_name, player)
|
for ent_name in bunny_pocket_entrances:
|
||||||
if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region) and not can_bunny_pocket_to(world, ent_name, player):
|
bunny_exit = world.get_entrance(ent_name, player)
|
||||||
add_rule(bunny_exit, lambda state: state.has_Pearl(player))
|
if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region) and not can_bunny_pocket_to(world, ent_name, player):
|
||||||
|
add_rule(bunny_exit, lambda state: state.has_Pearl(player))
|
||||||
|
|
||||||
|
|
||||||
drop_dungeon_entrances = {
|
drop_dungeon_entrances = {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ def main(args=None):
|
|||||||
test("Full ", "--shuffle full")
|
test("Full ", "--shuffle full")
|
||||||
test("Lite ", "--shuffle lite")
|
test("Lite ", "--shuffle lite")
|
||||||
test("Lean ", "--shuffle lean")
|
test("Lean ", "--shuffle lean")
|
||||||
|
test("District ", "--shuffle district")
|
||||||
test("Swapped ", "--shuffle swapped")
|
test("Swapped ", "--shuffle swapped")
|
||||||
test("Crossed ", "--shuffle crossed")
|
test("Crossed ", "--shuffle crossed")
|
||||||
test("Insanity ", "--shuffle insanity")
|
test("Insanity ", "--shuffle insanity")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ ALL_SETTINGS = {
|
|||||||
'mode': ['open', 'standard', 'inverted'],
|
'mode': ['open', 'standard', 'inverted'],
|
||||||
'goal': ['ganon', 'pedestal', 'triforcehunt', 'trinity', 'crystals', 'dungeons'],
|
'goal': ['ganon', 'pedestal', 'triforcehunt', 'trinity', 'crystals', 'dungeons'],
|
||||||
'swords': ['random', 'swordless', 'assured'],
|
'swords': ['random', 'swordless', 'assured'],
|
||||||
'shuffle': ['vanilla','simple','restricted','full','dungeonssimple','dungeonsfull','lite','lean','swapped','crossed','insanity'],
|
'shuffle': ['vanilla','simple','restricted','full','dungeonssimple','dungeonsfull','lite','lean','district','swapped','crossed','insanity'],
|
||||||
'shufflelinks': [True, False],
|
'shufflelinks': [True, False],
|
||||||
'shuffleganon': [True, False],
|
'shuffleganon': [True, False],
|
||||||
'door_shuffle': ['vanilla', 'basic', 'crossed'],
|
'door_shuffle': ['vanilla', 'basic', 'crossed'],
|
||||||
@@ -39,7 +39,7 @@ SETTINGS = {
|
|||||||
'goal': ['ganon'],
|
'goal': ['ganon'],
|
||||||
'swords': ['random'],
|
'swords': ['random'],
|
||||||
'shuffle': ['vanilla',
|
'shuffle': ['vanilla',
|
||||||
'dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'
|
'dungeonssimple', 'dungeonsfull', 'simple', 'restricted', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity'
|
||||||
],
|
],
|
||||||
'shufflelinks': [True, False],
|
'shufflelinks': [True, False],
|
||||||
'shuffleganon': [True, False],
|
'shuffleganon': [True, False],
|
||||||
|
|||||||
@@ -209,6 +209,7 @@
|
|||||||
"full",
|
"full",
|
||||||
"lite",
|
"lite",
|
||||||
"lean",
|
"lean",
|
||||||
|
"district",
|
||||||
"swapped",
|
"swapped",
|
||||||
"crossed",
|
"crossed",
|
||||||
"insanity",
|
"insanity",
|
||||||
|
|||||||
@@ -212,6 +212,7 @@
|
|||||||
" item locations are shuffled in separate pools. Non-item",
|
" item locations are shuffled in separate pools. Non-item",
|
||||||
" locations remain vanilla. Connectors are same-world.",
|
" locations remain vanilla. Connectors are same-world.",
|
||||||
"Lean: Same as Lite, except connectors can travel cross worlds.",
|
"Lean: Same as Lite, except connectors can travel cross worlds.",
|
||||||
|
"District: Entrances are shuffled within their overworld districts.",
|
||||||
"Crossed: Mix cave and dungeon entrances freely while allowing",
|
"Crossed: Mix cave and dungeon entrances freely while allowing",
|
||||||
" caves to cross between worlds.",
|
" caves to cross between worlds.",
|
||||||
"Swapped: Same as Crossed, but entrances switch places in pairs.",
|
"Swapped: Same as Crossed, but entrances switch places in pairs.",
|
||||||
|
|||||||
@@ -181,6 +181,7 @@
|
|||||||
"randomizer.entrance.entranceshuffle.restricted": "Restricted",
|
"randomizer.entrance.entranceshuffle.restricted": "Restricted",
|
||||||
"randomizer.entrance.entranceshuffle.full": "Full",
|
"randomizer.entrance.entranceshuffle.full": "Full",
|
||||||
"randomizer.entrance.entranceshuffle.lean": "Lean",
|
"randomizer.entrance.entranceshuffle.lean": "Lean",
|
||||||
|
"randomizer.entrance.entranceshuffle.district": "District",
|
||||||
"randomizer.entrance.entranceshuffle.swapped": "Swapped",
|
"randomizer.entrance.entranceshuffle.swapped": "Swapped",
|
||||||
"randomizer.entrance.entranceshuffle.crossed": "Crossed",
|
"randomizer.entrance.entranceshuffle.crossed": "Crossed",
|
||||||
"randomizer.entrance.entranceshuffle.insanity": "Insanity",
|
"randomizer.entrance.entranceshuffle.insanity": "Insanity",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"full",
|
"full",
|
||||||
"lite",
|
"lite",
|
||||||
"lean",
|
"lean",
|
||||||
|
"district",
|
||||||
"swapped",
|
"swapped",
|
||||||
"crossed",
|
"crossed",
|
||||||
"insanity",
|
"insanity",
|
||||||
|
|||||||
@@ -129,11 +129,11 @@ def create_item_pool_config(world):
|
|||||||
groups = LocationGroup('Major').locs(init_set)
|
groups = LocationGroup('Major').locs(init_set)
|
||||||
if world.bigkeyshuffle[player]:
|
if world.bigkeyshuffle[player]:
|
||||||
groups.locations.extend(mode_grouping['Big Keys'])
|
groups.locations.extend(mode_grouping['Big Keys'])
|
||||||
if world.dropshuffle[player] != 'none':
|
if world.dropshuffle[player]:
|
||||||
groups.locations.extend(mode_grouping['Big Key Drops'])
|
groups.locations.extend(mode_grouping['Big Key Drops'])
|
||||||
if world.keyshuffle[player] != 'none':
|
if world.keyshuffle[player] != 'none':
|
||||||
groups.locations.extend(mode_grouping['Small Keys'])
|
groups.locations.extend(mode_grouping['Small Keys'])
|
||||||
if world.dropshuffle[player] != 'none':
|
if world.dropshuffle[player]:
|
||||||
groups.locations.extend(mode_grouping['Key Drops'])
|
groups.locations.extend(mode_grouping['Key Drops'])
|
||||||
if world.pottery[player] not in ['none', 'cave']:
|
if world.pottery[player] not in ['none', 'cave']:
|
||||||
groups.locations.extend(mode_grouping['Pot Keys'])
|
groups.locations.extend(mode_grouping['Pot Keys'])
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class EntrancePool(object):
|
|||||||
self.inverted = False
|
self.inverted = False
|
||||||
self.coupled = True
|
self.coupled = True
|
||||||
self.swapped = False
|
self.swapped = False
|
||||||
|
self.assumed_loose_caves = False
|
||||||
|
self.keep_drops_together = True
|
||||||
self.default_map = {}
|
self.default_map = {}
|
||||||
self.one_way_map = {}
|
self.one_way_map = {}
|
||||||
self.skull_handled = False
|
self.skull_handled = False
|
||||||
@@ -54,6 +56,7 @@ def link_entrances_new(world, player):
|
|||||||
avail_pool.entrances = set(i_drop_map.keys()).union(i_entrance_map.keys()).union(i_single_ent_map.keys())
|
avail_pool.entrances = set(i_drop_map.keys()).union(i_entrance_map.keys()).union(i_single_ent_map.keys())
|
||||||
avail_pool.exits = set(i_entrance_map.values()).union(i_drop_map.values()).union(i_single_ent_map.values())
|
avail_pool.exits = set(i_entrance_map.values()).union(i_drop_map.values()).union(i_single_ent_map.values())
|
||||||
avail_pool.inverted = world.mode[player] == 'inverted'
|
avail_pool.inverted = world.mode[player] == 'inverted'
|
||||||
|
avail_pool.assumed_loose_caves = world.shuffle[player] == 'district'
|
||||||
inverted_substitution(avail_pool, avail_pool.entrances, True, True)
|
inverted_substitution(avail_pool, avail_pool.entrances, True, True)
|
||||||
inverted_substitution(avail_pool, avail_pool.exits, False, True)
|
inverted_substitution(avail_pool, avail_pool.exits, False, True)
|
||||||
avail_pool.original_entrances.update(avail_pool.entrances)
|
avail_pool.original_entrances.update(avail_pool.entrances)
|
||||||
@@ -93,6 +96,8 @@ def link_entrances_new(world, player):
|
|||||||
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
|
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
|
||||||
mode_cfg = copy.deepcopy(modes[mode])
|
mode_cfg = copy.deepcopy(modes[mode])
|
||||||
avail_pool.swapped = mode_cfg['undefined'] == 'swap'
|
avail_pool.swapped = mode_cfg['undefined'] == 'swap'
|
||||||
|
avail_pool.keep_drops_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
|
||||||
|
avail_pool.coupled = mode_cfg['decoupled'] != 'on' if 'decoupled' in mode_cfg else True
|
||||||
if avail_pool.is_standard():
|
if avail_pool.is_standard():
|
||||||
do_standard_connections(avail_pool)
|
do_standard_connections(avail_pool)
|
||||||
pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {}
|
pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {}
|
||||||
@@ -106,8 +111,7 @@ def link_entrances_new(world, player):
|
|||||||
connect_random(holes, targets, avail_pool)
|
connect_random(holes, targets, avail_pool)
|
||||||
elif special_shuffle == 'normal_drops':
|
elif special_shuffle == 'normal_drops':
|
||||||
cross_world = mode_cfg['cross_world'] == 'on' if 'cross_world' in mode_cfg else False
|
cross_world = mode_cfg['cross_world'] == 'on' if 'cross_world' in mode_cfg else False
|
||||||
keep_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
|
do_holes_and_linked_drops(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, cross_world)
|
||||||
do_holes_and_linked_drops(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, cross_world, keep_together)
|
|
||||||
elif special_shuffle == 'fixed_shuffle':
|
elif special_shuffle == 'fixed_shuffle':
|
||||||
do_fixed_shuffle(avail_pool, pool['entrances'])
|
do_fixed_shuffle(avail_pool, pool['entrances'])
|
||||||
elif special_shuffle == 'same_world':
|
elif special_shuffle == 'same_world':
|
||||||
@@ -126,10 +130,18 @@ def link_entrances_new(world, player):
|
|||||||
do_limited_shuffle_exclude_drops(pool, avail_pool, False)
|
do_limited_shuffle_exclude_drops(pool, avail_pool, False)
|
||||||
elif special_shuffle == 'vanilla':
|
elif special_shuffle == 'vanilla':
|
||||||
do_vanilla_connect(pool, avail_pool)
|
do_vanilla_connect(pool, avail_pool)
|
||||||
|
elif special_shuffle == 'district':
|
||||||
|
drops = []
|
||||||
|
world_limiter = LW_Entrances if pool['condition'] == 'lightworld' else DW_Entrances
|
||||||
|
entrances = [e for e in pool['entrances'] if e in world_limiter]
|
||||||
|
if 'drops' in pool:
|
||||||
|
drops = [e for e in pool['drops'] if combine_linked_drop_map[e] in world_limiter]
|
||||||
|
entrances, exits = find_entrances_and_exits(avail_pool, entrances+drops)
|
||||||
|
do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
|
||||||
elif special_shuffle == 'skull':
|
elif special_shuffle == 'skull':
|
||||||
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
||||||
rem_ent = None
|
rem_ent = None
|
||||||
if avail_pool.world.shuffle[avail_pool.player] in ['dungeons-simple', 'simple', 'restricted'] \
|
if avail_pool.world.shuffle[avail_pool.player] in ['dungeonssimple', 'simple', 'restricted'] \
|
||||||
and not avail_pool.world.is_tile_swapped(0x00, avail_pool.player):
|
and not avail_pool.world.is_tile_swapped(0x00, avail_pool.player):
|
||||||
rem_ent = random.choice(['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)'])
|
rem_ent = random.choice(['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)'])
|
||||||
entrances.remove(rem_ent)
|
entrances.remove(rem_ent)
|
||||||
@@ -149,6 +161,8 @@ def link_entrances_new(world, player):
|
|||||||
do_vanilla_connections(avail_pool)
|
do_vanilla_connections(avail_pool)
|
||||||
elif undefined_behavior in ['shuffle', 'swap']:
|
elif undefined_behavior in ['shuffle', 'swap']:
|
||||||
do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg)
|
do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg)
|
||||||
|
elif undefined_behavior == 'error':
|
||||||
|
assert len(avail_pool.entrances)+len(avail_pool.exits) == 0, 'Not all entrances were placed in their districts'
|
||||||
|
|
||||||
# afterward
|
# afterward
|
||||||
|
|
||||||
@@ -191,10 +205,8 @@ def do_vanilla_connections(avail_pool):
|
|||||||
|
|
||||||
def do_main_shuffle(entrances, exits, avail, mode_def):
|
def do_main_shuffle(entrances, exits, avail, mode_def):
|
||||||
cross_world = mode_def['cross_world'] == 'on' if 'cross_world' in mode_def else False
|
cross_world = mode_def['cross_world'] == 'on' if 'cross_world' in mode_def else False
|
||||||
avail.coupled = mode_def['decoupled'] != 'on' if 'decoupled' in mode_def else True
|
|
||||||
# drops and holes
|
# drops and holes
|
||||||
keep_together = mode_def['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_def else True
|
do_holes_and_linked_drops(entrances, exits, avail, cross_world)
|
||||||
do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_together)
|
|
||||||
|
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
avail.decoupled_entrances.extend(entrances)
|
avail.decoupled_entrances.extend(entrances)
|
||||||
@@ -310,7 +322,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
return not avail.is_standard() or x != 'Bonk Fairy (Light)'
|
return not avail.is_standard() or x != 'Bonk Fairy (Light)'
|
||||||
|
|
||||||
# old man S&Q cave
|
# old man S&Q cave
|
||||||
if not cross_world:
|
if not cross_world and not avail.assumed_loose_caves:
|
||||||
#TODO: Add Swapped ER support for this
|
#TODO: Add Swapped ER support for this
|
||||||
# OM Cave entrance in lw/dw if cross_world off
|
# OM Cave entrance in lw/dw if cross_world off
|
||||||
if 'Old Man Cave Exit (West)' in rem_exits:
|
if 'Old Man Cave Exit (West)' in rem_exits:
|
||||||
@@ -382,7 +394,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
def do_old_man_cave_exit(entrances, exits, avail, cross_world):
|
def do_old_man_cave_exit(entrances, exits, avail, cross_world):
|
||||||
if 'Old Man Cave Exit (East)' in exits:
|
if 'Old Man Cave Exit (East)' in exits:
|
||||||
from EntranceShuffle import build_accessible_region_list
|
from EntranceShuffle import build_accessible_region_list
|
||||||
if not avail.world.is_tile_swapped(0x03, avail.player):
|
if not avail.world.is_tile_swapped(0x03, avail.player) or avail.world.shuffle[avail.player] == 'district':
|
||||||
region_name = 'West Death Mountain (Top)'
|
region_name = 'West Death Mountain (Top)'
|
||||||
else:
|
else:
|
||||||
region_name = 'West Dark Death Mountain (Top)'
|
region_name = 'West Dark Death Mountain (Top)'
|
||||||
@@ -420,11 +432,14 @@ def do_blacksmith(entrances, exits, avail):
|
|||||||
if avail.world.logic[avail.player] in ['noglitches', 'minorglitches'] and (avail.world.is_tile_swapped(0x29, avail.player) == avail.inverted):
|
if avail.world.logic[avail.player] in ['noglitches', 'minorglitches'] and (avail.world.is_tile_swapped(0x29, avail.player) == avail.inverted):
|
||||||
assumed_inventory.append('Titans Mitts')
|
assumed_inventory.append('Titans Mitts')
|
||||||
|
|
||||||
|
blacksmith_options = list()
|
||||||
if not avail.world.is_bombshop_start(avail.player):
|
if not avail.world.is_bombshop_start(avail.player):
|
||||||
links_region = avail.world.get_entrance('Links House Exit', avail.player).connected_region.name
|
links_region = avail.world.get_entrance('Links House Exit', avail.player).connected_region
|
||||||
else:
|
else:
|
||||||
links_region = avail.world.get_entrance('Big Bomb Shop Exit', avail.player).connected_region.name
|
links_region = avail.world.get_entrance('Big Bomb Shop Exit', avail.player).connected_region
|
||||||
blacksmith_options = list(get_accessible_entrances(links_region, avail, assumed_inventory, False, True, True))
|
if links_region is not None:
|
||||||
|
links_region = links_region.name
|
||||||
|
blacksmith_options = list(get_accessible_entrances(links_region, avail, assumed_inventory, False, True, True))
|
||||||
|
|
||||||
if avail.inverted:
|
if avail.inverted:
|
||||||
dark_sanc = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player).connected_region.name
|
dark_sanc = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player).connected_region.name
|
||||||
@@ -440,6 +455,9 @@ def do_blacksmith(entrances, exits, avail):
|
|||||||
blacksmith_options = [e for e in blacksmith_options if e not in Forbidden_Swap_Entrances]
|
blacksmith_options = [e for e in blacksmith_options if e not in Forbidden_Swap_Entrances]
|
||||||
blacksmith_options = [x for x in blacksmith_options if x in entrances]
|
blacksmith_options = [x for x in blacksmith_options if x in entrances]
|
||||||
|
|
||||||
|
if avail.world.shuffle[avail.player] == 'district' and not len(blacksmith_options):
|
||||||
|
blacksmith_options = [e for e in entrances if e not in Forbidden_Swap_Entrances or not avail.swapped]
|
||||||
|
|
||||||
assert len(blacksmith_options), 'No available entrances left to place Blacksmith'
|
assert len(blacksmith_options), 'No available entrances left to place Blacksmith'
|
||||||
blacksmith_choice = random.choice(blacksmith_options)
|
blacksmith_choice = random.choice(blacksmith_options)
|
||||||
connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail)
|
connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail)
|
||||||
@@ -454,11 +472,21 @@ def do_blacksmith(entrances, exits, avail):
|
|||||||
|
|
||||||
|
|
||||||
def do_standard_connections(avail):
|
def do_standard_connections(avail):
|
||||||
connect_two_way('Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', avail)
|
std_exits = ['Hyrule Castle Exit (South)', 'Hyrule Castle Secret Entrance Exit']
|
||||||
# cannot move uncle cave
|
if not avail.keep_drops_together:
|
||||||
connect_two_way('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', avail)
|
random.shuffle(std_exits)
|
||||||
connect_entrance('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', avail)
|
|
||||||
connect_two_way('Links House', 'Links House Exit', avail)
|
connect_two_way('Links House', 'Links House Exit', avail)
|
||||||
|
connect_entrance('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', avail)
|
||||||
|
if avail.coupled:
|
||||||
|
connect_two_way('Hyrule Castle Entrance (South)', std_exits[0], avail)
|
||||||
|
# cannot move uncle cave
|
||||||
|
connect_two_way('Hyrule Castle Secret Entrance Stairs', std_exits[1], avail)
|
||||||
|
else:
|
||||||
|
connect_entrance('Hyrule Castle Entrance (South)', std_exits[0], avail)
|
||||||
|
connect_entrance('Hyrule Castle Secret Entrance Stairs', std_exits[1], avail)
|
||||||
|
random.shuffle(std_exits)
|
||||||
|
connect_exit(std_exits[0], 'Hyrule Castle Entrance (South)', avail)
|
||||||
|
connect_exit(std_exits[1], 'Hyrule Castle Secret Entrance Stairs', avail)
|
||||||
|
|
||||||
|
|
||||||
def remove_from_list(t_list, removals):
|
def remove_from_list(t_list, removals):
|
||||||
@@ -466,7 +494,7 @@ def remove_from_list(t_list, removals):
|
|||||||
t_list.remove(r)
|
t_list.remove(r)
|
||||||
|
|
||||||
|
|
||||||
def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_together):
|
def do_holes_and_linked_drops(entrances, exits, avail, cross_world):
|
||||||
holes_to_shuffle = [x for x in entrances if x in drop_map]
|
holes_to_shuffle = [x for x in entrances if x in drop_map]
|
||||||
|
|
||||||
if not avail.world.shuffle_ganon:
|
if not avail.world.shuffle_ganon:
|
||||||
@@ -483,7 +511,7 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
|
|||||||
remove_from_list(entrances, ['Pyramid Hole', 'Pyramid Entrance'])
|
remove_from_list(entrances, ['Pyramid Hole', 'Pyramid Entrance'])
|
||||||
remove_from_list(exits, ['Pyramid', 'Pyramid Exit'])
|
remove_from_list(exits, ['Pyramid', 'Pyramid Exit'])
|
||||||
|
|
||||||
if not keep_together:
|
if not avail.keep_drops_together:
|
||||||
targets = [avail.one_way_map[x] for x in holes_to_shuffle]
|
targets = [avail.one_way_map[x] for x in holes_to_shuffle]
|
||||||
connect_random(holes_to_shuffle, targets, avail)
|
connect_random(holes_to_shuffle, targets, avail)
|
||||||
remove_from_list(entrances, holes_to_shuffle)
|
remove_from_list(entrances, holes_to_shuffle)
|
||||||
@@ -567,6 +595,8 @@ def do_dark_sanc(entrances, exits, avail):
|
|||||||
forbidden.extend(Forbidden_Swap_Entrances)
|
forbidden.extend(Forbidden_Swap_Entrances)
|
||||||
if not avail.world.is_bombshop_start(avail.player):
|
if not avail.world.is_bombshop_start(avail.player):
|
||||||
forbidden.append('Links House')
|
forbidden.append('Links House')
|
||||||
|
else:
|
||||||
|
forbidden.append('Big Bomb Shop')
|
||||||
if avail.world.owShuffle[avail.player] == 'vanilla':
|
if avail.world.owShuffle[avail.player] == 'vanilla':
|
||||||
choices = [e for e in avail.world.districts[avail.player]['Northwest Dark World'].entrances if e not in forbidden and e in entrances]
|
choices = [e for e in avail.world.districts[avail.player]['Northwest Dark World'].entrances if e not in forbidden and e in entrances]
|
||||||
else:
|
else:
|
||||||
@@ -602,7 +632,7 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
forbidden.append('Mimic Cave')
|
forbidden.append('Mimic Cave')
|
||||||
if avail.world.is_bombshop_start(avail.player) and (avail.inverted == avail.world.is_tile_swapped(0x03, avail.player)):
|
if avail.world.is_bombshop_start(avail.player) and (avail.inverted == avail.world.is_tile_swapped(0x03, avail.player)):
|
||||||
forbidden.extend(['Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)'])
|
forbidden.extend(['Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)'])
|
||||||
if avail.inverted:
|
if avail.inverted and avail.world.shuffle[avail.player] != 'district':
|
||||||
dark_sanc_region = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player).connected_region.name
|
dark_sanc_region = avail.world.get_entrance('Dark Sanctuary Hint Exit', avail.player).connected_region.name
|
||||||
forbidden.extend(get_nearby_entrances(avail, dark_sanc_region))
|
forbidden.extend(get_nearby_entrances(avail, dark_sanc_region))
|
||||||
else:
|
else:
|
||||||
@@ -644,7 +674,7 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
sanc_spawn_can_be_dark = (not avail.inverted and avail.world.doorShuffle[avail.player] in ['partitioned', 'crossed']
|
sanc_spawn_can_be_dark = (not avail.inverted and avail.world.doorShuffle[avail.player] in ['partitioned', 'crossed']
|
||||||
and avail.world.intensity[avail.player] >= 3)
|
and avail.world.intensity[avail.player] >= 3)
|
||||||
|
|
||||||
if cross_world and not sanc_spawn_can_be_dark:
|
if (cross_world and not sanc_spawn_can_be_dark) or avail.world.shuffle[avail.player] == 'district':
|
||||||
possible = [e for e in entrance_pool if e not in forbidden]
|
possible = [e for e in entrance_pool if e not in forbidden]
|
||||||
else:
|
else:
|
||||||
world_list = LW_Entrances if not avail.inverted else DW_Entrances
|
world_list = LW_Entrances if not avail.inverted else DW_Entrances
|
||||||
@@ -676,7 +706,7 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
return
|
return
|
||||||
if avail.world.shuffle[avail.player] in ['lite', 'lean']:
|
if avail.world.shuffle[avail.player] in ['lite', 'lean']:
|
||||||
rem_exits = [e for e in avail.exits if e in Connector_Exit_Set and e not in Dungeon_Exit_Set]
|
rem_exits = [e for e in avail.exits if e in Connector_Exit_Set and e not in Dungeon_Exit_Set]
|
||||||
multi_exit_caves = figure_out_connectors(rem_exits)
|
multi_exit_caves = figure_out_connectors(rem_exits, avail)
|
||||||
if cross_world:
|
if cross_world:
|
||||||
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List]
|
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List]
|
||||||
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots]
|
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots]
|
||||||
@@ -685,7 +715,7 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List and e in world_list]
|
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List and e in world_list]
|
||||||
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots and e in world_list]
|
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots and e in world_list]
|
||||||
else:
|
else:
|
||||||
multi_exit_caves = figure_out_connectors(exits)
|
multi_exit_caves = figure_out_connectors(exits, avail)
|
||||||
entrance_pool = entrances if avail.coupled else avail.decoupled_entrances
|
entrance_pool = entrances if avail.coupled else avail.decoupled_entrances
|
||||||
if cross_world:
|
if cross_world:
|
||||||
possible_dm_exits = [e for e in entrances if e in LH_DM_Connector_List]
|
possible_dm_exits = [e for e in entrances if e in LH_DM_Connector_List]
|
||||||
@@ -832,12 +862,22 @@ def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_wo
|
|||||||
return found_entrances
|
return found_entrances
|
||||||
|
|
||||||
|
|
||||||
def figure_out_connectors(exits):
|
def figure_out_connectors(exits, avail):
|
||||||
multi_exit_caves = []
|
multi_exit_caves = []
|
||||||
for item in Connector_List:
|
cave_list = list(Connector_List)
|
||||||
|
if avail.assumed_loose_caves:
|
||||||
|
sw_list = ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']
|
||||||
|
random.shuffle(sw_list)
|
||||||
|
cave_list.extend([sw_list])
|
||||||
|
cave_list.extend([[entrance_map[e]] for e in linked_drop_map.values() if 'Inverted ' not in e])
|
||||||
|
for item in cave_list:
|
||||||
if all(x in exits for x in item):
|
if all(x in exits for x in item):
|
||||||
remove_from_list(exits, item)
|
remove_from_list(exits, item)
|
||||||
multi_exit_caves.append(list(item))
|
multi_exit_caves.append(list(item))
|
||||||
|
elif avail.assumed_loose_caves and any(x in exits for x in item):
|
||||||
|
remaining = [i for i in item if i in exits]
|
||||||
|
remove_from_list(exits, remaining)
|
||||||
|
multi_exit_caves.append(list(remaining))
|
||||||
return multi_exit_caves
|
return multi_exit_caves
|
||||||
|
|
||||||
|
|
||||||
@@ -1015,7 +1055,7 @@ def figure_out_must_exits_same_world(entrances, exits, avail):
|
|||||||
|
|
||||||
for x in entrances:
|
for x in entrances:
|
||||||
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
|
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
|
||||||
multi_exit_caves = figure_out_connectors(exits)
|
multi_exit_caves = figure_out_connectors(exits, avail)
|
||||||
|
|
||||||
must_exit_lw, must_exit_dw = must_exits_helper(avail)
|
must_exit_lw, must_exit_dw = must_exits_helper(avail)
|
||||||
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
|
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
|
||||||
@@ -1025,7 +1065,7 @@ def figure_out_must_exits_same_world(entrances, exits, avail):
|
|||||||
|
|
||||||
|
|
||||||
def figure_out_must_exits_cross_world(entrances, exits, avail):
|
def figure_out_must_exits_cross_world(entrances, exits, avail):
|
||||||
multi_exit_caves = figure_out_connectors(exits)
|
multi_exit_caves = figure_out_connectors(exits, avail)
|
||||||
|
|
||||||
must_exit_lw, must_exit_dw = must_exits_helper(avail)
|
must_exit_lw, must_exit_dw = must_exits_helper(avail)
|
||||||
must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances)
|
must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances)
|
||||||
@@ -1148,9 +1188,12 @@ def do_fixed_shuffle(avail, entrance_list):
|
|||||||
new_x = 'Agahnims Tower Exit'
|
new_x = 'Agahnims Tower Exit'
|
||||||
elif x == 'Agahnims Tower Exit':
|
elif x == 'Agahnims Tower Exit':
|
||||||
new_x = 'Ganons Tower Exit'
|
new_x = 'Ganons Tower Exit'
|
||||||
|
if avail.world.is_bombshop_start(avail.player):
|
||||||
|
if x == 'Links House Exit':
|
||||||
|
new_x = 'Big Bomb Shop'
|
||||||
|
elif x == 'Big Bomb Shop':
|
||||||
|
new_x = 'Links House Exit'
|
||||||
lw_exits.add(new_x)
|
lw_exits.add(new_x)
|
||||||
if avail.world.shufflelinks[avail.player] or avail.world.shuffle[avail.player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
|
||||||
lw_exits.update({'Big Bomb Shop'} if avail.world.is_bombshop_start(avail.player) else {'Links House Exit'})
|
|
||||||
filtered_choices = {i: opt for i, opt in choices.items() if all(t in lw_exits for t in opt[2])}
|
filtered_choices = {i: opt for i, opt in choices.items() if all(t in lw_exits for t in opt[2])}
|
||||||
_, choice = random.choice(list(filtered_choices.items()))
|
_, choice = random.choice(list(filtered_choices.items()))
|
||||||
else:
|
else:
|
||||||
@@ -1361,7 +1404,8 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
# find multi exit cave
|
# find multi exit cave
|
||||||
candidates = []
|
candidates = []
|
||||||
for candidate in cave_options:
|
for candidate in cave_options:
|
||||||
if not isinstance(candidate, str) and len(candidate) > 1 and (candidate in used_caves
|
allow_single = avail.assumed_loose_caves or len(candidate) > 1
|
||||||
|
if not isinstance(candidate, str) and allow_single and (candidate in used_caves
|
||||||
or len(candidate) < len(entrances) - required_entrances):
|
or len(candidate) < len(entrances) - required_entrances):
|
||||||
if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped
|
if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped
|
||||||
candidates.append(candidate)
|
candidates.append(candidate)
|
||||||
@@ -1395,6 +1439,10 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
if entrance in invalid_connections:
|
if entrance in invalid_connections:
|
||||||
for exit2 in invalid_connections[entrance]:
|
for exit2 in invalid_connections[entrance]:
|
||||||
invalid_connections[exit2] = invalid_connections[exit2].union(invalid_connections[exit]).union(invalid_cave_connections[tuple(cave)])
|
invalid_connections[exit2] = invalid_connections[exit2].union(invalid_connections[exit]).union(invalid_cave_connections[tuple(cave)])
|
||||||
|
elif len(cave) == 1 and avail.assumed_loose_caves:
|
||||||
|
#TODO: keep track of caves we use for must exits that are unaccounted here
|
||||||
|
# the other exits of the cave should NOT be used to satisfy must-exit later
|
||||||
|
pass
|
||||||
elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit
|
elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit
|
||||||
cave_entrances = []
|
cave_entrances = []
|
||||||
for cave_exit in rnd_cave[:-1]:
|
for cave_exit in rnd_cave[:-1]:
|
||||||
@@ -1519,14 +1567,12 @@ def find_entrances_and_exits(avail_pool, entrance_pool):
|
|||||||
entrances, targets = [], []
|
entrances, targets = [], []
|
||||||
inverted_substitution(avail_pool, entrance_pool, True)
|
inverted_substitution(avail_pool, entrance_pool, True)
|
||||||
for item in entrance_pool:
|
for item in entrance_pool:
|
||||||
if item == 'Ganons Tower' and not avail_pool.world.shuffle_ganon[avail_pool.player]:
|
|
||||||
continue
|
|
||||||
if item in avail_pool.entrances:
|
if item in avail_pool.entrances:
|
||||||
entrances.append(item)
|
entrances.append(item)
|
||||||
if item in entrance_map and entrance_map[item] in avail_pool.exits:
|
if item in avail_pool.default_map and avail_pool.default_map[item] in avail_pool.exits:
|
||||||
targets.append(entrance_map[item])
|
targets.append(avail_pool.default_map[item])
|
||||||
elif item in single_entrance_map and single_entrance_map[item] in avail_pool.exits:
|
elif item in avail_pool.one_way_map and avail_pool.one_way_map[item] in avail_pool.exits:
|
||||||
targets.append(single_entrance_map[item])
|
targets.append(avail_pool.one_way_map[item])
|
||||||
return entrances, targets
|
return entrances, targets
|
||||||
|
|
||||||
|
|
||||||
@@ -2064,6 +2110,157 @@ modes = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'district': {
|
||||||
|
'undefined': 'error',
|
||||||
|
'keep_drops_together': 'off',
|
||||||
|
'cross_world': 'off',
|
||||||
|
'pools': {
|
||||||
|
'northwest_hyrule': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'drops': ['Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', 'North Fairy Cave Drop',
|
||||||
|
|
||||||
|
'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)',
|
||||||
|
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'],
|
||||||
|
'entrances': ['Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Sanctuary', 'North Fairy Cave',
|
||||||
|
'Lost Woods Gamble', 'Lumberjack House', 'Old Man Cave (West)', 'Death Mountain Return Cave (West)',
|
||||||
|
'Fortune Teller (Light)', 'Bonk Rock Cave', 'Graveyard Cave', 'Kings Grave',
|
||||||
|
|
||||||
|
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||||
|
'Skull Woods Second Section Door (West)', 'Skull Woods Final Section', 'Dark Lumberjack Shop',
|
||||||
|
'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint',
|
||||||
|
'Red Shield Shop']
|
||||||
|
},
|
||||||
|
'northwest_dark_world': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'darkworld',
|
||||||
|
'drops': ['Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)',
|
||||||
|
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole',
|
||||||
|
|
||||||
|
'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', 'North Fairy Cave Drop',
|
||||||
|
'Kakariko Well Drop', 'Bat Cave Drop'],
|
||||||
|
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||||
|
'Skull Woods Second Section Door (West)', 'Skull Woods Final Section', 'Dark Lumberjack Shop',
|
||||||
|
'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint',
|
||||||
|
'Chest Game', 'Thieves Town', 'C-Shaped House', 'Dark World Shop', 'Brewery',
|
||||||
|
'Red Shield Shop', 'Hammer Peg Cave',
|
||||||
|
|
||||||
|
'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Sanctuary', 'North Fairy Cave',
|
||||||
|
'Kakariko Well Cave', 'Bat Cave Cave', 'Lost Woods Gamble', 'Lumberjack House', 'Fortune Teller (Light)',
|
||||||
|
'Old Man Cave (West)', 'Death Mountain Return Cave (West)', 'Bonk Rock Cave', 'Graveyard Cave',
|
||||||
|
'Kings Grave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)', 'Snitch Lady (West)',
|
||||||
|
'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Bush Covered House', 'Light World Bomb Hut',
|
||||||
|
'Kakariko Shop', 'Tavern North', 'Tavern (Front)', 'Blacksmiths Hut']
|
||||||
|
},
|
||||||
|
'central_hyrule': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'drops': ['Hyrule Castle Secret Entrance Drop', 'Inverted Pyramid Hole',
|
||||||
|
|
||||||
|
'Pyramid Hole'],
|
||||||
|
'entrances': ['Hyrule Castle Secret Entrance Stairs', 'Inverted Pyramid Entrance', 'Agahnims Tower',
|
||||||
|
'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (South)',
|
||||||
|
'Bonk Fairy (Light)', 'Links House', 'Cave 45', 'Light Hype Fairy', 'Dam',
|
||||||
|
|
||||||
|
'Pyramid Entrance', 'Pyramid Fairy', 'Bonk Fairy (Dark)', 'Big Bomb Shop', 'Hype Cave', 'Swamp Palace']
|
||||||
|
},
|
||||||
|
'kakariko': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'drops': ['Kakariko Well Drop', 'Bat Cave Drop'],
|
||||||
|
'entrances': ['Kakariko Well Cave', 'Bat Cave Cave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)',
|
||||||
|
'Snitch Lady (West)', 'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Bush Covered House',
|
||||||
|
'Light World Bomb Hut', 'Kakariko Shop', 'Tavern North', 'Tavern (Front)', 'Blacksmiths Hut',
|
||||||
|
'Two Brothers House (West)', 'Two Brothers House (East)', 'Library', 'Kakariko Gamble Game',
|
||||||
|
|
||||||
|
'Chest Game', 'Thieves Town', 'C-Shaped House', 'Dark World Shop', 'Brewery',
|
||||||
|
'Hammer Peg Cave', 'Archery Game']
|
||||||
|
},
|
||||||
|
'eastern_hyrule': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'entrances': ['Waterfall of Wishing', 'Potion Shop', 'Sahasrahlas Hut', 'Eastern Palace', 'Lake Hylia Fairy',
|
||||||
|
'Long Fairy Cave',
|
||||||
|
|
||||||
|
'Dark Potion Shop', 'Palace of Darkness Hint', 'Palace of Darkness', 'Dark Lake Hylia Fairy',
|
||||||
|
'East Dark World Hint']
|
||||||
|
},
|
||||||
|
'lake_hylia': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'entrances': ['Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Capacity Upgrade', 'Mini Moldorm Cave',
|
||||||
|
'Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave',
|
||||||
|
|
||||||
|
'Dark Lake Hylia Shop', 'Ice Palace', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint',
|
||||||
|
'Dark Lake Hylia Ledge Spike Cave']
|
||||||
|
},
|
||||||
|
'desert': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'entrances': ['Desert Palace Entrance (North)', 'Desert Palace Entrance (West)', 'Desert Palace Entrance (South)',
|
||||||
|
'Desert Palace Entrance (East)', 'Checkerboard Cave', 'Aginahs Cave', 'Desert Fairy', '50 Rupee Cave',
|
||||||
|
|
||||||
|
'Mire Shed', 'Misery Mire', 'Mire Fairy', 'Mire Hint']
|
||||||
|
},
|
||||||
|
'death_mountain': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'lightworld',
|
||||||
|
'entrances': ['Tower of Hera', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave',
|
||||||
|
'Death Mountain Return Cave (East)', 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)',
|
||||||
|
'Spiral Cave', 'Spiral Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Fairy Ascension Cave (Bottom)',
|
||||||
|
'Mimic Cave', 'Hookshot Fairy', 'Paradox Cave (Top)', 'Paradox Cave (Middle)', 'Paradox Cave (Bottom)',
|
||||||
|
|
||||||
|
'Ganons Tower', 'Dark Death Mountain Fairy', 'Spike Cave', 'Superbunny Cave (Bottom)', 'Superbunny Cave (Top)',
|
||||||
|
'Dark Death Mountain Shop', 'Hookshot Cave', 'Hookshot Cave Back Entrance',
|
||||||
|
'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock']
|
||||||
|
},
|
||||||
|
'dark_death_mountain': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'darkworld',
|
||||||
|
'entrances': ['Ganons Tower', 'Dark Death Mountain Fairy', 'Spike Cave', 'Superbunny Cave (Bottom)', 'Superbunny Cave (Top)',
|
||||||
|
'Dark Death Mountain Shop', 'Hookshot Cave', 'Hookshot Cave Back Entrance',
|
||||||
|
'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock',
|
||||||
|
|
||||||
|
'Tower of Hera', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave',
|
||||||
|
'Death Mountain Return Cave (East)', 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)',
|
||||||
|
'Spiral Cave', 'Spiral Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Fairy Ascension Cave (Bottom)',
|
||||||
|
'Mimic Cave', 'Hookshot Fairy', 'Paradox Cave (Top)', 'Paradox Cave (Middle)', 'Paradox Cave (Bottom)']
|
||||||
|
},
|
||||||
|
'south_dark_world': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'darkworld',
|
||||||
|
'entrances': ['Archery Game', 'Bonk Fairy (Dark)', 'Big Bomb Shop', 'Hype Cave', 'Dark Lake Hylia Shop', 'Ice Palace',
|
||||||
|
'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave',
|
||||||
|
'Swamp Palace',
|
||||||
|
|
||||||
|
'Two Brothers House (West)', 'Two Brothers House (East)', 'Library', 'Kakariko Gamble Game',
|
||||||
|
'Bonk Fairy (Light)', 'Links House', 'Cave 45', 'Desert Fairy', '50 Rupee Cave', 'Dam',
|
||||||
|
'Light Hype Fairy', 'Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Capacity Upgrade',
|
||||||
|
'Mini Moldorm Cave', 'Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave']
|
||||||
|
},
|
||||||
|
'east_dark_world': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'darkworld',
|
||||||
|
'drops': ['Pyramid Hole',
|
||||||
|
|
||||||
|
'Hyrule Castle Secret Entrance Drop', 'Inverted Pyramid Hole'],
|
||||||
|
'entrances': ['Pyramid Entrance', 'Pyramid Fairy', 'Dark Potion Shop', 'Palace of Darkness Hint', 'Palace of Darkness',
|
||||||
|
'Dark Lake Hylia Fairy', 'East Dark World Hint',
|
||||||
|
|
||||||
|
'Hyrule Castle Secret Entrance Stairs', 'Inverted Pyramid Entrance', 'Waterfall of Wishing', 'Potion Shop',
|
||||||
|
'Agahnims Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)',
|
||||||
|
'Hyrule Castle Entrance (South)', 'Sahasrahlas Hut', 'Eastern Palace', 'Lake Hylia Fairy', 'Long Fairy Cave']
|
||||||
|
},
|
||||||
|
'mire': {
|
||||||
|
'special': 'district',
|
||||||
|
'condition': 'darkworld',
|
||||||
|
'entrances': ['Mire Shed', 'Misery Mire', 'Mire Fairy', 'Mire Hint',
|
||||||
|
|
||||||
|
'Desert Palace Entrance (North)', 'Desert Palace Entrance (West)', 'Desert Palace Entrance (South)',
|
||||||
|
'Desert Palace Entrance (East)', 'Checkerboard Cave', 'Aginahs Cave']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
'swapped': {
|
'swapped': {
|
||||||
'undefined': 'swap',
|
'undefined': 'swap',
|
||||||
'keep_drops_together': 'on',
|
'keep_drops_together': 'on',
|
||||||
@@ -2125,7 +2322,7 @@ drop_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
linked_drop_map = {
|
linked_drop_map = {
|
||||||
'Hyrule Castle Secret Entrance Drop': 'Hyrule Castle Secret Entrance Stairs',
|
'Hyrule Castle Secret Entrance Drop': 'Hyrule Castle Secret Entrance Stairs',
|
||||||
'Kakariko Well Drop': 'Kakariko Well Cave',
|
'Kakariko Well Drop': 'Kakariko Well Cave',
|
||||||
'Bat Cave Drop': 'Bat Cave Cave',
|
'Bat Cave Drop': 'Bat Cave Cave',
|
||||||
'North Fairy Cave Drop': 'North Fairy Cave',
|
'North Fairy Cave Drop': 'North Fairy Cave',
|
||||||
@@ -2136,6 +2333,13 @@ linked_drop_map = {
|
|||||||
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance'
|
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw_linked_drop_map = {
|
||||||
|
'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (West)',
|
||||||
|
'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door',
|
||||||
|
'Skull Woods First Section Hole (West)': 'Skull Woods First Section Door',
|
||||||
|
'Skull Woods First Section Hole (East)': 'Skull Woods First Section Door'
|
||||||
|
}
|
||||||
|
|
||||||
entrance_map = {
|
entrance_map = {
|
||||||
'Desert Palace Entrance (South)': 'Desert Palace Exit (South)',
|
'Desert Palace Entrance (South)': 'Desert Palace Exit (South)',
|
||||||
'Desert Palace Entrance (West)': 'Desert Palace Exit (West)',
|
'Desert Palace Entrance (West)': 'Desert Palace Exit (West)',
|
||||||
@@ -2169,7 +2373,7 @@ entrance_map = {
|
|||||||
'Links House': 'Links House Exit',
|
'Links House': 'Links House Exit',
|
||||||
|
|
||||||
|
|
||||||
'Hyrule Castle Secret Entrance Stairs': 'Hyrule Castle Secret Entrance Exit',
|
'Hyrule Castle Secret Entrance Stairs': 'Hyrule Castle Secret Entrance Exit',
|
||||||
'Kakariko Well Cave': 'Kakariko Well Exit',
|
'Kakariko Well Cave': 'Kakariko Well Exit',
|
||||||
'Bat Cave Cave': 'Bat Cave Exit',
|
'Bat Cave Cave': 'Bat Cave Exit',
|
||||||
'North Fairy Cave': 'North Fairy Cave Exit',
|
'North Fairy Cave': 'North Fairy Cave Exit',
|
||||||
@@ -2244,6 +2448,7 @@ single_entrance_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
combine_map = {**entrance_map, **single_entrance_map, **drop_map}
|
combine_map = {**entrance_map, **single_entrance_map, **drop_map}
|
||||||
|
combine_linked_drop_map = {**linked_drop_map, **sw_linked_drop_map}
|
||||||
|
|
||||||
LW_Entrances = []
|
LW_Entrances = []
|
||||||
DW_Entrances = []
|
DW_Entrances = []
|
||||||
|
|||||||
Reference in New Issue
Block a user