Starting inventory updates

Logic fix for skull woods star tile logic
Standard logic improvement
This commit is contained in:
aerinon
2023-01-12 15:32:49 -07:00
parent 4d1b5f58c0
commit 22dfeeecca
12 changed files with 225 additions and 82 deletions

View File

@@ -2287,6 +2287,9 @@ class Item(object):
def __unicode__(self):
return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})'
def __eq__(self, other):
return self.name == other.name and self.player == other.player
# have 6 address that need to be filled
class Crystal(Item):

View File

@@ -2107,7 +2107,7 @@ def find_trappable_candidates(builder, world, player):
if ext.door and ext.door.type in [DoorType.Interior, DoorType.Normal]]
for d in filtered_doors:
# I only support the first 3 due to the trapFlag right now
if 0 <= d.doorListPos < 3 and not d.entranceFlag:
if 0 <= d.doorListPos < 3 and not d.entranceFlag and d.name != 'Skull Small Hall WS':
room = world.get_room(d.roomIndex, player)
kind = room.kind(d)
if d.type == DoorType.Interior:

View File

@@ -286,6 +286,7 @@ def generate_itempool(world, player):
for _ in range(0, amt):
pool.append('Rupees (20)')
start_inventory = list(world.precollected_items)
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
@@ -435,6 +436,22 @@ def generate_itempool(world, player):
if world.pottery[player] not in ['none', 'keys'] and not skip_pool_adjustments:
add_pot_contents(world, player)
# modfiy based on start inventory, if any
modify_pool_for_start_inventory(start_inventory, world, player)
# increase pool if not enough items
ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if '- Prize' not in x.name)
pool_size = count_player_dungeon_item_pool(world, player)
pool_size += sum(1 for x in world.itempool if x.player == player)
if pool_size < ttl_locations:
retro_bow = world.bow_mode[player].startswith('retro')
amount_to_add = ttl_locations - pool_size
filler_additions = random.choices(list(filler_items.keys()), filler_items.values(), k=amount_to_add)
for item in filler_additions:
item_name = 'Rupees (5)' if retro_bow and item == 'Arrows (10)' else item
world.itempool.append(ItemFactory(item_name, player))
take_any_locations = [
'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut',
@@ -962,14 +979,60 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt
pool.extend(['Small Key (Universal)'])
else:
pool.extend(['Small Key (Universal)'])
modify_pool_for_start_inventory(pool, world, player)
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
def modify_pool_for_start_inventory(pool, world, player):
for item in world.precollected_items:
item_alternates = {
# Bows
'Progressive Bow (Alt)': ('Progressive Bow', 1),
'Bow': ('Progressive Bow', 1),
'Silver Arrows': ('Progressive Bow', 2),
# Gloves
'Power Glove': ('Progressive Glove', 1),
'Titans Mitts': ('Progressive Glove', 2),
# Swords
'Sword and Shield': ('Progressive Sword', 1), # could find a way to also remove a shield, but mostly not impactful
'Fighter Sword': ('Progressive Sword', 1),
'Master Sword': ('Progressive Sword', 2),
'Tempered Sword': ('Progressive Sword', 3),
'Golden Sword': ('Progressive Sword', 4),
# Shields
'Blue Shield': ('Progressive Shield', 1),
'Red Shield': ('Progressive Shield', 2),
'Mirror Shield': ('Progressive Shield', 3),
# Armors
'Blue Mail': ('Progressive Armor', 1),
'Red Mail': ('Progressive Armor', 2),
'Magic Upgrade (1/4)': ('Magic Upgrade (1/2)', 2),
'Ocarina': ('Ocarina (Activated)', 1),
'Ocarina (Activated)': ('Ocarina', 1),
'Boss Heart Container': ('Sanctuary Heart Container', 1),
'Sanctuary Heart Container': ('Boss Heart Container', 1),
'Power Star': ('Triforce Piece', 1)
}
def modify_pool_for_start_inventory(start_inventory, world, player):
# skips custom item pools - these shouldn't be adjusted
if (world.customizer and world.customizer.get_item_pool()) or world.custom:
return
for item in start_inventory:
if item.player == player:
pool.remove(item.name)
if item in world.itempool:
world.itempool.remove(item)
elif item.name in item_alternates:
alt = item_alternates[item.name]
i = alt[1]
while i > 0:
alt_item = ItemFactory([alt[0]], player)[0]
if alt_item in world.itempool:
world.itempool.remove(alt_item)
i = i-1
elif 'Bottle' in item.name:
bottle_item = next((x for x in world.itempool if 'Bottle' in item.name and x.player == player), None)
if bottle_item is not None:
world.itempool.remove(bottle_item)
if item.dungeon:
d = world.get_dungeon(item.dungeon, item.player)
match = next((i for i in d.all_items if i.name == item.name), None)
@@ -1085,6 +1148,22 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
# print("Placing " + str(nothings) + " Nothings")
pool.extend(['Nothing'] * nothings)
start_inventory = [x for x in world.precollected_items if x.player == player]
if not start_inventory:
if world.logic[player] in ['owglitches', 'nologic']:
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
if world.swords[player] == 'assured':
precollected_items.append('Progressive Sword')
if 'Progressive Sword' in pool:
pool.remove('Progressive Sword')
pool.append('Rupees (50)')
elif 'Fighter Sword' in pool:
pool.remove('Fighter Sword')
pool.append('Rupees (50)')
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
@@ -1206,12 +1285,20 @@ def make_customizer_pool(world, player):
if pieces < t:
pool.extend(['Triforce Piece'] * (t - pieces))
ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if '- Prize' not in x.name)
pool_size = len(get_player_dungeon_item_pool(world, player)) + len(pool)
if pool_size < ttl_locations:
amount_to_add = ttl_locations - pool_size
pool.extend(random.choices(list(filler_items.keys()), filler_items.values(), k=amount_to_add))
if not world.customizer.get_start_inventory():
if world.logic[player] in ['owglitches', 'nologic']:
precollected_items.append('Pegasus Boots')
if 'Pegasus Boots' in pool:
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
if world.swords[player] == 'assured':
precollected_items.append('Progressive Sword')
if 'Progressive Sword' in pool:
pool.remove('Progressive Sword')
pool.append('Rupees (50)')
elif 'Fighter Sword' in pool:
pool.remove('Fighter Sword')
pool.append('Rupees (50)')
return pool, placed_items, precollected_items, clock_mode, 1
@@ -1227,9 +1314,9 @@ filler_items = {
}
def get_player_dungeon_item_pool(world, player):
return [item for dungeon in world.dungeons for item in dungeon.all_items
if dungeon.player == player and item.location is None]
def count_player_dungeon_item_pool(world, player):
return sum(1 for dungeon in world.dungeons for item in dungeon.all_items
if dungeon.player == player and item.location is None and is_dungeon_item(item.name, world, player))
# location pool doesn't support larger values at this time

View File

@@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
from source.tools.BPS import create_bps_from_data
from source.classes.CustomSettings import CustomSettings
__version__ = '1.2.0.3-u'
__version__ = '1.2.0.4-u'
from source.classes.BabelFish import BabelFish

View File

@@ -108,7 +108,11 @@ These are now independent of retro mode and have three options: None, Random, an
* Bonk Fairy (Dark)
# Bug Fixes and Notes
* 1.2.0.4-u
* Starting inventory fixes if item not present in the item pool.
* Support for Assured sword setting and OWG Boots when using a custom item pool. (Customizer or GUI)
* Logic fix for the skull woods star tile that lets you into the X pot room. Now accounts for small key or big key door there blocking the way from the star tile. A trap door is not allowed there.
* Standard logic improvement that requires a path from Zelda to the start so that you cannot get softlocked by rescuing Zelda. Standard mirror scroll change may need to be reverted if impossible seed are still generated.
* 1.2.0.3-u
* Starting inventory taken into account with default item pool. (Custom pools must do this themselves)
* Fast ROM update

16
Rom.py
View File

@@ -2221,7 +2221,7 @@ def write_strings(rom, world, player, team):
hint_candidates = []
for name, district in world.districts[player].items():
hint_type = 'foolish'
choice_set = set()
choices = []
item_count, item_type = 0, 'useful'
for loc_name in district.locations:
location_item = world.get_location(loc_name, player).item
@@ -2231,34 +2231,32 @@ def write_strings(rom, world, player, team):
itm_type = 'useful' if useful_item_for_hint(location_item, world) else 'vital'
hint_type = 'path'
if item_type == itm_type:
choice_set.add(location_item)
choices.append(location_item)
item_count += 1
elif itm_type == 'vital':
item_type = 'vital'
item_count = 1
choice_set.clear()
choice_set.add(location_item)
choices.clear()
choices.append(location_item)
if hint_type == 'foolish':
if district.dungeons and world.shuffle[player] != 'vanilla':
choice_set.update(district.dungeons)
choices.extend(district.dungeons)
hint_type = 'dungeon_path'
elif district.access_points and world.shuffle[player] not in ['vanilla', 'dungeonssimple',
'dungeonsfull']:
choice_set.update([x.hint_text for x in district.access_points])
choices.extend([x.hint_text for x in district.access_points])
hint_type = 'connector'
if hint_type == 'foolish':
hint_candidates.append((hint_type, f'{name} is a foolish choice'))
elif hint_type == 'dungeon_path':
choices = sorted(list(choice_set))
dungeon_choice = random.choice(choices) # prefer required dungeons...
hint_candidates.append((hint_type, f'{name} is on the path to {dungeon_choice}'))
elif hint_type == 'connector':
choices = sorted(list(choice_set))
access_point = random.choice(choices) # prefer required access...
hint_candidates.append((hint_type, f'{name} can reach {access_point}'))
elif hint_type == 'path':
if item_count == 1:
the_item = text_for_item(next(iter(choice_set)), world, player, team)
the_item = text_for_item(next(iter(choices)), world, player, team)
hint_candidates.append((hint_type, f'{name} conceals only {the_item}'))
else:
hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items'))

View File

@@ -124,6 +124,10 @@ def or_rule(rule1, rule2):
return lambda state: rule1(state) or rule2(state)
def and_rule(rule1, rule2):
return lambda state: rule1(state) and rule2(state)
def add_lamp_requirement(spot, player):
add_rule(spot, lambda state: state.has('Lamp', player, state.world.lamps_needed_for_dark_rooms))
@@ -277,8 +281,22 @@ def global_rules(world, player):
set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Skull Torch Room WN', player), lambda state: state.has('Fire Rod', player))
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player))
set_rule(world.get_entrance('Skull 2 West Lobby Pits', player), lambda state: state.has_Boots(player) or state.has('Hidden Pits', player))
set_rule(world.get_entrance('Skull 2 West Lobby Ledge Pits', player), lambda state: state.has('Hidden Pits', player))
hidden_pits_door = world.get_door('Skull Small Hall WS', player)
def hidden_pits_rule(state):
return state.has('Hidden Pits', player)
if hidden_pits_door.bigKey:
key_logic = world.key_logic[player][hidden_pits_door.entrance.parent_region.dungeon.name]
hidden_pits_rule = and_rule(hidden_pits_rule, create_rule(key_logic.bk_name, player))
elif hidden_pits_door.smallKey:
d_name = hidden_pits_door.entrance.parent_region.dungeon.name
hidden_pits_rule = and_rule(hidden_pits_rule, eval_small_key_door('Skull Small Hall WS', d_name, player))
set_rule(world.get_entrance('Skull 2 West Lobby Pits', player), lambda state: state.has_Boots(player)
or hidden_pits_rule(state))
set_rule(world.get_entrance('Skull 2 West Lobby Ledge Pits', player), hidden_pits_rule)
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player))

View File

@@ -2,6 +2,7 @@ meta:
players: 1
race: true
settings:
1:
shopsanity: true
pseudoboots: true
goal: crystals

View File

@@ -2,6 +2,7 @@ meta:
players: 1
race: true
settings:
1:
shopsanity: true
pseudoboots: true
goal: crystals

View File

@@ -2,6 +2,7 @@ meta:
players: 1
race: true
settings:
1:
shopsanity: true
pseudoboots: true
goal: crystals

View File

@@ -319,6 +319,8 @@ def determine_paths_for_dungeon(world, player, all_regions, name):
if world.mode[player] == 'standard' and name == 'Hyrule Castle Dungeon':
paths.append('Hyrule Dungeon Cellblock')
paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room'))
entrance = next(x for x in world.dungeon_portals[player] if x.name == 'Hyrule Castle South')
paths.append(('Hyrule Dungeon Cellblock', entrance.door.entrance.parent_region.name))
if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town':
paths.append('Thieves Attic Window')
elif 'Thieves Attic Window' in all_r_names:

View File

@@ -0,0 +1,28 @@
meta:
players: 1
race: true
settings:
1:
shopsanity: true
pseudoboots: true
goal: crystals
crystals_gt: random
keysanity: true
door_shuffle: crossed
intensity: 3
door_type_mode: big
pottery: keys
dropshuffle: true
experimental: true
dungeon_counters: 'on'
hints: true
msu_resume: true
collection_rate: true
quickswap: true
start_inventory:
1:
- Pegasus Boots
- Ocarina (Activated)
- Magic Mirror
- Boss Heart Container
- Blue Mail