Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
@@ -1687,12 +1687,12 @@ class Entrance(object):
|
|||||||
|
|
||||||
def can_reach(self, state):
|
def can_reach(self, state):
|
||||||
# Destination Pickup OW Only No Ledges Can S&Q Allow Mirror
|
# Destination Pickup OW Only No Ledges Can S&Q Allow Mirror
|
||||||
multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False, True),
|
multi_step_locations = { 'Pyramid Crack': ('Pick Up Big Bomb', True, True, False, True),
|
||||||
'Missing Smith': ('Frog', True, False, True, True),
|
'Missing Smith': ('Get Frog', True, False, True, True),
|
||||||
'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True),
|
'Middle Aged Man': ('Pick Up Purple Chest', True, False, True, True),
|
||||||
'Dark Palace Button':('Kiki', True, False, False, False),
|
'Dark Palace Button': ('Pick Up Kiki', True, False, False, False),
|
||||||
'Old Man Drop Off': ('Lost Old Man', True, False, False, False),
|
'Old Man Drop Off': ('Escort Old Man', True, False, False, False),
|
||||||
'Revealing Light': ('Suspicious Maiden', False, False, False, False)
|
'Revealing Light': ('Maiden Rescued', False, False, False, False)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.name in multi_step_locations:
|
if self.name in multi_step_locations:
|
||||||
@@ -1701,7 +1701,10 @@ class Entrance(object):
|
|||||||
multi_step_loc = multi_step_locations[self.name]
|
multi_step_loc = multi_step_locations[self.name]
|
||||||
if world.shuffle_followers[self.player]:
|
if world.shuffle_followers[self.player]:
|
||||||
multi_step_loc = (multi_step_loc[0], self.name == 'Pyramid Crack', multi_step_loc[2], True, True)
|
multi_step_loc = (multi_step_loc[0], self.name == 'Pyramid Crack', multi_step_loc[2], True, True)
|
||||||
step_location = world.get_location(multi_step_loc[0], self.player)
|
step_location = world.find_items(multi_step_loc[0], self.player)
|
||||||
|
if len(step_location) == 0:
|
||||||
|
return False
|
||||||
|
step_location = step_location[0]
|
||||||
if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_loc[1], multi_step_loc[2], multi_step_loc[3], multi_step_loc[4]) and self.access_rule(state):
|
if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_loc[1], multi_step_loc[2], multi_step_loc[3], multi_step_loc[4]) and self.access_rule(state):
|
||||||
if not self in state.path:
|
if not self in state.path:
|
||||||
path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None))
|
path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None))
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.6.1.8
|
||||||
|
- Fixed follower placement and logic
|
||||||
|
- Fixed error with HC Courtyard Tree Pull
|
||||||
|
|
||||||
## 0.6.1.7
|
## 0.6.1.7
|
||||||
- \~Merged in DR v1.5.2~
|
- \~Merged in DR v1.5.2~
|
||||||
- Reverted key count update
|
- Reverted key count update
|
||||||
|
|||||||
29
ItemList.py
29
ItemList.py
@@ -1695,9 +1695,11 @@ def set_event_item(world, player, location_name, item_name=None):
|
|||||||
|
|
||||||
def shuffle_event_items(world, player):
|
def shuffle_event_items(world, player):
|
||||||
if world.shuffle_followers[player]:
|
if world.shuffle_followers[player]:
|
||||||
|
all_state = world.get_all_state(keys=True)
|
||||||
available_quests = follower_quests.copy()
|
available_quests = follower_quests.copy()
|
||||||
available_pickups = [quests[0] for quests in available_quests.values()]
|
available_pickups = [quests[0] for quests in available_quests.values()]
|
||||||
|
|
||||||
|
# finalize customizer followers first
|
||||||
for loc_name in follower_quests.keys():
|
for loc_name in follower_quests.keys():
|
||||||
loc = world.get_location(loc_name, player)
|
loc = world.get_location(loc_name, player)
|
||||||
if loc.item:
|
if loc.item:
|
||||||
@@ -1705,7 +1707,6 @@ def shuffle_event_items(world, player):
|
|||||||
available_quests.pop(loc_name)
|
available_quests.pop(loc_name)
|
||||||
available_pickups.remove(loc.item.name)
|
available_pickups.remove(loc.item.name)
|
||||||
|
|
||||||
|
|
||||||
if world.mode[player] == 'standard' and 'Zelda Herself' in available_pickups:
|
if world.mode[player] == 'standard' and 'Zelda Herself' in available_pickups:
|
||||||
zelda_dropoff = 'Zelda Pickup'
|
zelda_dropoff = 'Zelda Pickup'
|
||||||
if world.default_zelda_region[player] == 'Thieves Blind\'s Cell':
|
if world.default_zelda_region[player] == 'Thieves Blind\'s Cell':
|
||||||
@@ -1715,20 +1716,22 @@ def shuffle_event_items(world, player):
|
|||||||
available_pickups.remove(zelda_pickup)
|
available_pickups.remove(zelda_pickup)
|
||||||
set_event_item(world, player, zelda_dropoff, zelda_pickup)
|
set_event_item(world, player, zelda_dropoff, zelda_pickup)
|
||||||
|
|
||||||
random.shuffle(available_pickups)
|
# randomize the follower pickups, but ensure that the last items are the unrestrictive ones
|
||||||
|
unrestrictive_pickups = [p for p in ['Zelda Herself', 'Sign Vandalized'] if p in available_pickups]
|
||||||
|
restrictive_pickups = [p for p in available_pickups if p not in unrestrictive_pickups]
|
||||||
|
random.shuffle(restrictive_pickups)
|
||||||
|
random.shuffle(unrestrictive_pickups)
|
||||||
|
available_pickups = restrictive_pickups + unrestrictive_pickups
|
||||||
|
|
||||||
restricted_pickups = { 'Get Frog': 'Dark Blacksmith Ruins'}
|
pickup_items = ItemFactory(available_pickups, player)
|
||||||
for pickup in restricted_pickups:
|
follower_locations = [world.get_location(loc_name, player) for loc_name in available_quests.keys()]
|
||||||
restricted_quests = [q for q in available_quests.keys() if q not in restricted_pickups[pickup]]
|
random.shuffle(follower_locations)
|
||||||
random.shuffle(restricted_quests)
|
|
||||||
quest = restricted_quests.pop()
|
|
||||||
available_quests.pop(quest)
|
|
||||||
available_pickups.remove(pickup)
|
|
||||||
set_event_item(world, player, quest, pickup)
|
|
||||||
|
|
||||||
for pickup in available_pickups:
|
fill_restrictive(world, all_state, follower_locations, pickup_items, single_player_placement=True)
|
||||||
quest, _ = available_quests.popitem()
|
for loc_name in available_quests.keys():
|
||||||
set_event_item(world, player, quest, pickup)
|
loc = world.get_location(loc_name, player)
|
||||||
|
if loc.item:
|
||||||
|
set_event_item(world, player, loc_name)
|
||||||
|
|
||||||
|
|
||||||
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):
|
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):
|
||||||
|
|||||||
@@ -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.6.1.7'
|
version_number = '0.6.1.8'
|
||||||
# branch indicator is intentionally different across branches
|
# branch indicator is intentionally different across branches
|
||||||
version_branch = ''
|
version_branch = ''
|
||||||
|
|
||||||
|
|||||||
4
Rules.py
4
Rules.py
@@ -1806,7 +1806,9 @@ def standard_rules(world, player):
|
|||||||
add_rule(world.get_location('Central Bonk Rocks Tree', player), lambda state: state.has('Zelda Delivered', player))
|
add_rule(world.get_location('Central Bonk Rocks Tree', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
|
|
||||||
if not world.is_premature_copied_world:
|
if not world.is_premature_copied_world:
|
||||||
add_rule(world.get_location('Hyrule Castle Courtyard Tree Pull', player), lambda state: state.has('Zelda Delivered', player))
|
loc = world.get_location_unsafe('Hyrule Castle Courtyard Tree Pull', player)
|
||||||
|
if loc:
|
||||||
|
add_rule(loc, lambda state: state.has('Zelda Delivered', player))
|
||||||
|
|
||||||
# don't allow bombs to get past here before zelda is rescued
|
# don't allow bombs to get past here before zelda is rescued
|
||||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
||||||
|
|||||||
@@ -2116,8 +2116,13 @@ class EnemyTable:
|
|||||||
self.room_map = defaultdict(list)
|
self.room_map = defaultdict(list)
|
||||||
self.special_bitmasks = None
|
self.special_bitmasks = None
|
||||||
|
|
||||||
def write_sprite_data_to_rom(self, rom):
|
def write_sprite_data_to_rom(self, rom, pointer_table):
|
||||||
pointer_address = snes_to_pc(0x09D62E)
|
ow_data_end = pointer_table['ow_sprites'][0] + pointer_table['ow_sprites'][1]
|
||||||
|
if ow_data_end > pointer_table['uw_sprites'][2]:
|
||||||
|
# moving pointer table down
|
||||||
|
pointer_table['uw_sprites'][2] = ow_data_end
|
||||||
|
rom.write_bytes(snes_to_pc(pointer_table['uw_sprites'][3]), int16_as_bytes(ow_data_end & 0xFFFF))
|
||||||
|
pointer_address = snes_to_pc(pointer_table['uw_sprites'][2])
|
||||||
data_pointer = snes_to_pc(0x288000)
|
data_pointer = snes_to_pc(0x288000)
|
||||||
empty_pointer = pc_to_snes(data_pointer) & 0xFFFF
|
empty_pointer = pc_to_snes(data_pointer) & 0xFFFF
|
||||||
rom.write_bytes(data_pointer, [0x00, 0xff])
|
rom.write_bytes(data_pointer, [0x00, 0xff])
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ class DataTables:
|
|||||||
self.ow_enemy_table = None
|
self.ow_enemy_table = None
|
||||||
self.pot_secret_table = None
|
self.pot_secret_table = None
|
||||||
self.overworld_sprite_sheets = None
|
self.overworld_sprite_sheets = None
|
||||||
|
self.pointer_addresses = {
|
||||||
|
# table: [data_start, data_size, pointer_table, references]
|
||||||
|
'ow_sprites': [ 0x09CB41, None, (0x09C881, 0x09C901, 0x09CA21), None ],
|
||||||
|
'uw_sprites': [ 0x09D92E, None, 0x09D62E, 0x09C298 ],
|
||||||
|
}
|
||||||
|
|
||||||
# associated data
|
# associated data
|
||||||
self.sprite_requirements = None
|
self.sprite_requirements = None
|
||||||
@@ -77,13 +82,6 @@ class DataTables:
|
|||||||
# bank 0A uses 372A bytes
|
# bank 0A uses 372A bytes
|
||||||
# bank 1F uses 77CE bytes: total is about a bank and a half
|
# bank 1F uses 77CE bytes: total is about a bank and a half
|
||||||
# probably should reuse bank 1F if writing all the rooms out
|
# probably should reuse bank 1F if writing all the rooms out
|
||||||
for sheet in self.sprite_sheets.values():
|
|
||||||
sheet.write_to_rom(rom, snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3
|
|
||||||
if self.uw_enemy_table.size() > 0x2800:
|
|
||||||
raise Exception('Sprite table is too big for current area')
|
|
||||||
self.uw_enemy_table.write_sprite_data_to_rom(rom)
|
|
||||||
self.uw_enemy_table.check_special_bitmasks_size()
|
|
||||||
self.uw_enemy_table.write_special_bitmask_table(rom)
|
|
||||||
for area_id, sheet in self.overworld_sprite_sheets.items():
|
for area_id, sheet in self.overworld_sprite_sheets.items():
|
||||||
if area_id in [0x80, 0x81]:
|
if area_id in [0x80, 0x81]:
|
||||||
offset = area_id - 0x80 # 02E575 for special areas?
|
offset = area_id - 0x80 # 02E575 for special areas?
|
||||||
@@ -95,7 +93,14 @@ class DataTables:
|
|||||||
# _00FAC1 is LW post-aga
|
# _00FAC1 is LW post-aga
|
||||||
# _00FB01 is DW
|
# _00FB01 is DW
|
||||||
# _00FA41 is rain state
|
# _00FA41 is rain state
|
||||||
|
for sheet in self.sprite_sheets.values():
|
||||||
|
sheet.write_to_rom(rom, snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3
|
||||||
self.write_ow_sprite_data_to_rom(rom)
|
self.write_ow_sprite_data_to_rom(rom)
|
||||||
|
if self.uw_enemy_table.size() > 0x2800:
|
||||||
|
raise Exception('Sprite table is too big for current area')
|
||||||
|
self.uw_enemy_table.write_sprite_data_to_rom(rom, self.pointer_addresses)
|
||||||
|
self.uw_enemy_table.check_special_bitmasks_size()
|
||||||
|
self.uw_enemy_table.write_special_bitmask_table(rom)
|
||||||
for sprite, stats in self.enemy_stats.items():
|
for sprite, stats in self.enemy_stats.items():
|
||||||
# write health to rom
|
# write health to rom
|
||||||
if stats.health is not None:
|
if stats.health is not None:
|
||||||
@@ -133,13 +138,13 @@ class DataTables:
|
|||||||
|
|
||||||
def write_ow_sprite_data_to_rom(self, rom):
|
def write_ow_sprite_data_to_rom(self, rom):
|
||||||
# calculate how big this table is going to be?
|
# calculate how big this table is going to be?
|
||||||
# bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1
|
bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1
|
||||||
|
self.pointer_addresses['ow_sprites'][1] = bytes
|
||||||
# ending_byte = 0x09CB3B + bytes
|
# ending_byte = 0x09CB3B + bytes
|
||||||
max_per_state = {0: 0x40, 1: 0x90, 2: 0x8B} # dropped max on state 2 to steal space for a couple extra sprites (Murahdahla, extra tutorial guard)
|
max_per_state = {0: 0x40, 1: 0x90, 2: 0x90}
|
||||||
|
|
||||||
pointer_address = snes_to_pc(0x09C881)
|
pointer_address = snes_to_pc(self.pointer_addresses['ow_sprites'][2][0])
|
||||||
# currently borrowed 10 bytes, used 9 (2xMurah + TutorialGuard)
|
data_pointer = snes_to_pc(self.pointer_addresses['ow_sprites'][0])
|
||||||
data_pointer = snes_to_pc(0x09CB38) # was originally 0x09CB41 - stealing space for a couple extra sprites (Murahdahla, extra tutorial guard)
|
|
||||||
empty_pointer = pc_to_snes(data_pointer) & 0xFFFF
|
empty_pointer = pc_to_snes(data_pointer) & 0xFFFF
|
||||||
rom.write_byte(data_pointer, 0xff)
|
rom.write_byte(data_pointer, 0xff)
|
||||||
cached_dark_world = {}
|
cached_dark_world = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user