Merge branch 'DoorDevUnstable' into Synthesis
# Conflicts: # Bosses.py # Main.py # Rom.py # data/base2current.bps
This commit is contained in:
@@ -223,12 +223,15 @@ def place_bosses(world, player):
|
|||||||
for u, level in used_bosses:
|
for u, level in used_bosses:
|
||||||
if not level:
|
if not level:
|
||||||
bosses.remove(u)
|
bosses.remove(u)
|
||||||
|
gt_bosses = []
|
||||||
|
|
||||||
for [loc, level] in boss_locations:
|
for [loc, level] in boss_locations:
|
||||||
loc_text = loc + (' ('+level+')' if level else '')
|
loc_text = loc + (' ('+level+')' if level else '')
|
||||||
try:
|
try:
|
||||||
if level:
|
if level:
|
||||||
boss = random.choice([b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)])
|
boss = random.choice([b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)
|
||||||
|
and b not in gt_bosses])
|
||||||
|
gt_bosses.append(boss)
|
||||||
else:
|
else:
|
||||||
boss = random.choice([b for b in bosses if can_place_boss(world, player, b, loc, level)])
|
boss = random.choice([b for b in bosses if can_place_boss(world, player, b, loc, level)])
|
||||||
bosses.remove(boss)
|
bosses.remove(boss)
|
||||||
|
|||||||
85
Fill.py
85
Fill.py
@@ -3,6 +3,7 @@ import collections
|
|||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
from contextlib import suppress
|
||||||
|
|
||||||
from BaseClasses import CollectionState, FillError, LocationType
|
from BaseClasses import CollectionState, FillError, LocationType
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
@@ -35,17 +36,6 @@ def dungeon_tracking(world):
|
|||||||
|
|
||||||
def fill_dungeons_restrictive(world, shuffled_locations):
|
def fill_dungeons_restrictive(world, shuffled_locations):
|
||||||
dungeon_tracking(world)
|
dungeon_tracking(world)
|
||||||
all_state_base = world.get_all_state()
|
|
||||||
|
|
||||||
# for player in range(1, world.players + 1):
|
|
||||||
# pinball_room = world.get_location('Skull Woods - Pinball Room', player)
|
|
||||||
# if world.retro[player]:
|
|
||||||
# world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False)
|
|
||||||
# else:
|
|
||||||
# world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False)
|
|
||||||
# pinball_room.event = True
|
|
||||||
# pinball_room.locked = True
|
|
||||||
# shuffled_locations.remove(pinball_room)
|
|
||||||
|
|
||||||
# with shuffled dungeon items they are distributed as part of the normal item pool
|
# with shuffled dungeon items they are distributed as part of the normal item pool
|
||||||
for item in world.get_items():
|
for item in world.get_items():
|
||||||
@@ -55,17 +45,28 @@ def fill_dungeons_restrictive(world, shuffled_locations):
|
|||||||
item.priority = True
|
item.priority = True
|
||||||
|
|
||||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
|
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
|
||||||
|
bigs, smalls, others = [], [], []
|
||||||
|
for i in dungeon_items:
|
||||||
|
(bigs if i.bigkey else smalls if i.smallkey else others).append(i)
|
||||||
|
|
||||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
def fill(base_state, items, key_pool):
|
||||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
fill_restrictive(world, base_state, shuffled_locations, items, key_pool, True)
|
||||||
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
|
|
||||||
|
|
||||||
fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items,
|
all_state_base = world.get_all_state()
|
||||||
keys_in_itempool={player: not world.keyshuffle[player] for player in range(1, world.players+1)},
|
big_state_base = all_state_base.copy()
|
||||||
single_player_placement=True)
|
for x in smalls + others:
|
||||||
|
big_state_base.collect(x, True)
|
||||||
|
fill(big_state_base, bigs, smalls)
|
||||||
|
random.shuffle(shuffled_locations)
|
||||||
|
small_state_base = all_state_base.copy()
|
||||||
|
for x in others:
|
||||||
|
small_state_base.collect(x, True)
|
||||||
|
fill(small_state_base, smalls, smalls)
|
||||||
|
random.shuffle(shuffled_locations)
|
||||||
|
fill(all_state_base, others, None)
|
||||||
|
|
||||||
|
|
||||||
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=None, single_player_placement=False,
|
def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False,
|
||||||
vanilla=False):
|
vanilla=False):
|
||||||
def sweep_from_pool():
|
def sweep_from_pool():
|
||||||
new_state = base_state.copy()
|
new_state = base_state.copy()
|
||||||
@@ -101,8 +102,7 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=No
|
|||||||
item_locations = filter_locations(item_to_place, locations, world, vanilla)
|
item_locations = filter_locations(item_to_place, locations, world, vanilla)
|
||||||
for location in item_locations:
|
for location in item_locations:
|
||||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, maximum_exploration_state,
|
spot_to_fill = verify_spot_to_fill(location, item_to_place, maximum_exploration_state,
|
||||||
single_player_placement, perform_access_check, itempool,
|
single_player_placement, perform_access_check, key_pool, world)
|
||||||
keys_in_itempool, world)
|
|
||||||
if spot_to_fill:
|
if spot_to_fill:
|
||||||
break
|
break
|
||||||
if spot_to_fill is None:
|
if spot_to_fill is None:
|
||||||
@@ -111,7 +111,7 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=No
|
|||||||
continue
|
continue
|
||||||
spot_to_fill = recovery_placement(item_to_place, locations, world, maximum_exploration_state,
|
spot_to_fill = recovery_placement(item_to_place, locations, world, maximum_exploration_state,
|
||||||
base_state, itempool, perform_access_check, item_locations,
|
base_state, itempool, perform_access_check, item_locations,
|
||||||
keys_in_itempool, single_player_placement)
|
key_pool, single_player_placement)
|
||||||
if spot_to_fill is None:
|
if spot_to_fill is None:
|
||||||
# we filled all reachable spots. Maybe the game can be beaten anyway?
|
# we filled all reachable spots. Maybe the game can be beaten anyway?
|
||||||
unplaced_items.insert(0, item_to_place)
|
unplaced_items.insert(0, item_to_place)
|
||||||
@@ -123,6 +123,10 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=No
|
|||||||
raise FillError('No more spots to place %s' % item_to_place)
|
raise FillError('No more spots to place %s' % item_to_place)
|
||||||
|
|
||||||
world.push_item(spot_to_fill, item_to_place, False)
|
world.push_item(spot_to_fill, item_to_place, False)
|
||||||
|
# todo: remove key item from key_pool
|
||||||
|
if item_to_place.smallkey:
|
||||||
|
with suppress(ValueError):
|
||||||
|
key_pool.remove(item_to_place)
|
||||||
track_outside_keys(item_to_place, spot_to_fill, world)
|
track_outside_keys(item_to_place, spot_to_fill, world)
|
||||||
track_dungeon_items(item_to_place, spot_to_fill, world)
|
track_dungeon_items(item_to_place, spot_to_fill, world)
|
||||||
locations.remove(spot_to_fill)
|
locations.remove(spot_to_fill)
|
||||||
@@ -132,7 +136,7 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=No
|
|||||||
|
|
||||||
|
|
||||||
def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_placement, perform_access_check,
|
def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_placement, perform_access_check,
|
||||||
itempool, keys_in_itempool, world):
|
key_pool, world):
|
||||||
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there
|
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there
|
||||||
location.item = item_to_place
|
location.item = item_to_place
|
||||||
test_state = max_exp_state.copy()
|
test_state = max_exp_state.copy()
|
||||||
@@ -141,8 +145,7 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
|
|||||||
test_state = max_exp_state
|
test_state = max_exp_state
|
||||||
if not single_player_placement or location.player == item_to_place.player:
|
if not single_player_placement or location.player == item_to_place.player:
|
||||||
if location.can_fill(test_state, item_to_place, perform_access_check):
|
if location.can_fill(test_state, item_to_place, perform_access_check):
|
||||||
test_pool = itempool if (keys_in_itempool and keys_in_itempool[item_to_place.player]) else world.itempool
|
if valid_key_placement(item_to_place, location, key_pool, world):
|
||||||
if valid_key_placement(item_to_place, location, test_pool, world):
|
|
||||||
if item_to_place.crystal or valid_dungeon_placement(item_to_place, location, world):
|
if item_to_place.crystal or valid_dungeon_placement(item_to_place, location, world):
|
||||||
return location
|
return location
|
||||||
if item_to_place.smallkey or item_to_place.bigkey:
|
if item_to_place.smallkey or item_to_place.bigkey:
|
||||||
@@ -150,7 +153,7 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def valid_key_placement(item, location, itempool, world):
|
def valid_key_placement(item, location, key_pool, world):
|
||||||
if not valid_reserved_placement(item, location, world):
|
if not valid_reserved_placement(item, location, world):
|
||||||
return False
|
return False
|
||||||
if ((not item.smallkey and not item.bigkey) or item.player != location.player
|
if ((not item.smallkey and not item.bigkey) or item.player != location.player
|
||||||
@@ -161,7 +164,7 @@ def valid_key_placement(item, location, itempool, world):
|
|||||||
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
|
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
|
||||||
return True
|
return True
|
||||||
key_logic = world.key_logic[item.player][dungeon.name]
|
key_logic = world.key_logic[item.player][dungeon.name]
|
||||||
unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name and x.player == item.player])
|
unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player])
|
||||||
prize_loc = None
|
prize_loc = None
|
||||||
if key_logic.prize_location:
|
if key_logic.prize_location:
|
||||||
prize_loc = world.get_location(key_logic.prize_location, location.player)
|
prize_loc = world.get_location(key_logic.prize_location, location.player)
|
||||||
@@ -216,16 +219,16 @@ def is_dungeon_item(item, world):
|
|||||||
|
|
||||||
|
|
||||||
def recovery_placement(item_to_place, locations, world, state, base_state, itempool, perform_access_check, attempted,
|
def recovery_placement(item_to_place, locations, world, state, base_state, itempool, perform_access_check, attempted,
|
||||||
keys_in_itempool=None, single_player_placement=False):
|
key_pool=None, single_player_placement=False):
|
||||||
logging.getLogger('').debug(f'Could not place {item_to_place} attempting recovery')
|
logging.getLogger('').debug(f'Could not place {item_to_place} attempting recovery')
|
||||||
if world.algorithm in ['balanced', 'equitable']:
|
if world.algorithm in ['balanced', 'equitable']:
|
||||||
return last_ditch_placement(item_to_place, locations, world, state, base_state, itempool, keys_in_itempool,
|
return last_ditch_placement(item_to_place, locations, world, state, base_state, itempool, key_pool,
|
||||||
single_player_placement)
|
single_player_placement)
|
||||||
elif world.algorithm == 'vanilla_fill':
|
elif world.algorithm == 'vanilla_fill':
|
||||||
if item_to_place.type == 'Crystal':
|
if item_to_place.type == 'Crystal':
|
||||||
possible_swaps = [x for x in state.locations_checked if x.item.type == 'Crystal']
|
possible_swaps = [x for x in state.locations_checked if x.item.type == 'Crystal']
|
||||||
return try_possible_swaps(possible_swaps, item_to_place, locations, world, base_state, itempool,
|
return try_possible_swaps(possible_swaps, item_to_place, locations, world, base_state, itempool,
|
||||||
keys_in_itempool, single_player_placement)
|
key_pool, single_player_placement)
|
||||||
else:
|
else:
|
||||||
i, config = 0, world.item_pool_config
|
i, config = 0, world.item_pool_config
|
||||||
tried = set(attempted)
|
tried = set(attempted)
|
||||||
@@ -235,7 +238,7 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp
|
|||||||
other_locs = [x for x in locations if x.name in fallback_locations]
|
other_locs = [x for x in locations if x.name in fallback_locations]
|
||||||
for location in other_locs:
|
for location in other_locs:
|
||||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
||||||
perform_access_check, itempool, keys_in_itempool, world)
|
perform_access_check, key_pool, world)
|
||||||
if spot_to_fill:
|
if spot_to_fill:
|
||||||
return spot_to_fill
|
return spot_to_fill
|
||||||
i += 1
|
i += 1
|
||||||
@@ -244,14 +247,14 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp
|
|||||||
other_locations = vanilla_fallback(item_to_place, locations, world)
|
other_locations = vanilla_fallback(item_to_place, locations, world)
|
||||||
for location in other_locations:
|
for location in other_locations:
|
||||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
||||||
perform_access_check, itempool, keys_in_itempool, world)
|
perform_access_check, key_pool, world)
|
||||||
if spot_to_fill:
|
if spot_to_fill:
|
||||||
return spot_to_fill
|
return spot_to_fill
|
||||||
tried.update(other_locations)
|
tried.update(other_locations)
|
||||||
other_locations = [x for x in locations if x not in tried]
|
other_locations = [x for x in locations if x not in tried]
|
||||||
for location in other_locations:
|
for location in other_locations:
|
||||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
||||||
perform_access_check, itempool, keys_in_itempool, world)
|
perform_access_check, key_pool, world)
|
||||||
if spot_to_fill:
|
if spot_to_fill:
|
||||||
return spot_to_fill
|
return spot_to_fill
|
||||||
return None
|
return None
|
||||||
@@ -259,14 +262,14 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp
|
|||||||
other_locations = [x for x in locations if x not in attempted]
|
other_locations = [x for x in locations if x not in attempted]
|
||||||
for location in other_locations:
|
for location in other_locations:
|
||||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
spot_to_fill = verify_spot_to_fill(location, item_to_place, state, single_player_placement,
|
||||||
perform_access_check, itempool, keys_in_itempool, world)
|
perform_access_check, key_pool, world)
|
||||||
if spot_to_fill:
|
if spot_to_fill:
|
||||||
return spot_to_fill
|
return spot_to_fill
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def last_ditch_placement(item_to_place, locations, world, state, base_state, itempool,
|
def last_ditch_placement(item_to_place, locations, world, state, base_state, itempool,
|
||||||
keys_in_itempool=None, single_player_placement=False):
|
key_pool=None, single_player_placement=False):
|
||||||
def location_preference(loc):
|
def location_preference(loc):
|
||||||
if not loc.item.advancement:
|
if not loc.item.advancement:
|
||||||
return 1
|
return 1
|
||||||
@@ -284,21 +287,21 @@ def last_ditch_placement(item_to_place, locations, world, state, base_state, ite
|
|||||||
if x.item.type not in ['Event', 'Crystal'] and not x.forced_item]
|
if x.item.type not in ['Event', 'Crystal'] and not x.forced_item]
|
||||||
swap_locations = sorted(possible_swaps, key=location_preference)
|
swap_locations = sorted(possible_swaps, key=location_preference)
|
||||||
return try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool,
|
return try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool,
|
||||||
keys_in_itempool, single_player_placement)
|
key_pool, single_player_placement)
|
||||||
|
|
||||||
|
|
||||||
def try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool,
|
def try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool,
|
||||||
keys_in_itempool=None, single_player_placement=False):
|
key_pool=None, single_player_placement=False):
|
||||||
for location in swap_locations:
|
for location in swap_locations:
|
||||||
old_item = location.item
|
old_item = location.item
|
||||||
new_pool = list(itempool) + [old_item]
|
new_pool = list(itempool) + [old_item]
|
||||||
new_spot = find_spot_for_item(item_to_place, [location], world, base_state, new_pool,
|
new_spot = find_spot_for_item(item_to_place, [location], world, base_state, new_pool,
|
||||||
keys_in_itempool, single_player_placement)
|
key_pool, single_player_placement)
|
||||||
if new_spot:
|
if new_spot:
|
||||||
restore_item = new_spot.item
|
restore_item = new_spot.item
|
||||||
new_spot.item = item_to_place
|
new_spot.item = item_to_place
|
||||||
swap_spot = find_spot_for_item(old_item, locations, world, base_state, itempool,
|
swap_spot = find_spot_for_item(old_item, locations, world, base_state, itempool,
|
||||||
keys_in_itempool, single_player_placement)
|
key_pool, single_player_placement)
|
||||||
if swap_spot:
|
if swap_spot:
|
||||||
logging.getLogger('').debug(f'Swapping {old_item} for {item_to_place}')
|
logging.getLogger('').debug(f'Swapping {old_item} for {item_to_place}')
|
||||||
world.push_item(swap_spot, old_item, False)
|
world.push_item(swap_spot, old_item, False)
|
||||||
@@ -414,13 +417,13 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
|||||||
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
|
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
|
||||||
# todo: crossed
|
# todo: crossed
|
||||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
|
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
|
||||||
keys_in_pool = {player: world.keyshuffle[player] or world.algorithm != 'balanced' for player in range(1, world.players + 1)}
|
key_pool = [x for x in progitempool if x.smallkey]
|
||||||
|
|
||||||
# sort maps and compasses to the back -- this may not be viable in equitable & ambrosia
|
# sort maps and compasses to the back -- this may not be viable in equitable & ambrosia
|
||||||
progitempool.sort(key=lambda item: 0 if item.map or item.compass else 1)
|
progitempool.sort(key=lambda item: 0 if item.map or item.compass else 1)
|
||||||
if world.algorithm == 'vanilla_fill':
|
if world.algorithm == 'vanilla_fill':
|
||||||
fill_restrictive(world, world.state, fill_locations, progitempool, keys_in_pool, vanilla=True)
|
fill_restrictive(world, world.state, fill_locations, progitempool, key_pool, vanilla=True)
|
||||||
fill_restrictive(world, world.state, fill_locations, progitempool, keys_in_pool)
|
fill_restrictive(world, world.state, fill_locations, progitempool, key_pool)
|
||||||
random.shuffle(fill_locations)
|
random.shuffle(fill_locations)
|
||||||
if world.algorithm == 'balanced':
|
if world.algorithm == 'balanced':
|
||||||
fast_fill(world, prioitempool, fill_locations)
|
fast_fill(world, prioitempool, fill_locations)
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -33,7 +33,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__ = '1.0.1.0-x'
|
__version__ = '1.0.1.1-x'
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
|
|
||||||
|
|||||||
@@ -787,12 +787,13 @@ vanilla_pots = {
|
|||||||
Pot(230, 27, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF5E, [0xCF, 0xDF, 0xFA]))],
|
Pot(230, 27, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF5E, [0xCF, 0xDF, 0xFA]))],
|
||||||
0x108: [Pot(166, 19, PotItem.Chicken, 'Chicken House', obj=RoomObject(0x03EFA9, [0x4F, 0x9F, 0xFA]))],
|
0x108: [Pot(166, 19, PotItem.Chicken, 'Chicken House', obj=RoomObject(0x03EFA9, [0x4F, 0x9F, 0xFA]))],
|
||||||
0x10C: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy', obj=RoomObject(0x03F329, [0xB3, 0x73, 0xFA]))],
|
0x10C: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy', obj=RoomObject(0x03F329, [0xB3, 0x73, 0xFA]))],
|
||||||
0x114: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x23, 0xFA])),
|
# note: these addresses got moved thanks to waterfall fairy edit
|
||||||
Pot(96, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x23, 0xFA])),
|
0x114: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79A, [0xBB, 0x23, 0xFA])),
|
||||||
Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x2B, 0xFA])),
|
Pot(96, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79D, [0xC3, 0x23, 0xFA])),
|
||||||
Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x2B, 0xFA])),
|
Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x2B, 0xFA])),
|
||||||
Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint', obj=RoomObject(0x03F7AC, [0xBB, 0x53, 0xFA])),
|
Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x2B, 0xFA])),
|
||||||
Pot(96, 10, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7AF, [0xC3, 0x53, 0xFA]))],
|
Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x53, 0xFA])),
|
||||||
|
Pot(96, 10, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x53, 0xFA]))],
|
||||||
0x117: [Pot(138, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB2, [0x17, 0x1F, 0xFA])), # 0x38A -> 38A
|
0x117: [Pot(138, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB2, [0x17, 0x1F, 0xFA])), # 0x38A -> 38A
|
||||||
Pot(142, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB8, [0x1F, 0x1F, 0xFA])),
|
Pot(142, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB8, [0x1F, 0x1F, 0xFA])),
|
||||||
Pot(166, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC1, [0x4F, 0x1F, 0xFA])),
|
Pot(166, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC1, [0x4F, 0x1F, 0xFA])),
|
||||||
|
|||||||
@@ -207,6 +207,12 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
|||||||
|
|
||||||
#### Unstable
|
#### Unstable
|
||||||
|
|
||||||
|
* 1.0.1.1
|
||||||
|
* Fixed the pots in Mire Storyteller/ Dark Desert Hint to be colorized when they should be
|
||||||
|
* Certain pot items no longer reload when reloading the supertile (matches original pot behavior better)
|
||||||
|
* Changed the key distribution that made small keys placement more random when keys are in their own dungeon
|
||||||
|
* Unique boss shuffle no longer allows repeat bosses in GT (e.g. only one Trinexx in GT, so exactly 3 bosses are repeated in the seed. This is a difference process than full which does affect the probability distribution.)
|
||||||
|
* Removed text color in hints due to vanilla bug
|
||||||
* 1.0.1.0
|
* 1.0.1.0
|
||||||
* Large features
|
* Large features
|
||||||
* New pottery modes - see notes above
|
* New pottery modes - see notes above
|
||||||
@@ -230,7 +236,6 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
|||||||
* Refactored spoiler to generate in stages for better error collection. A meta file will be generated additionally for mystery seeds. Some random settings moved later in the spoiler to have the meta section at the top not spoil certain things. (GT/Ganon requirements.) Thanks to codemann and OWR for most of this work.
|
* Refactored spoiler to generate in stages for better error collection. A meta file will be generated additionally for mystery seeds. Some random settings moved later in the spoiler to have the meta section at the top not spoil certain things. (GT/Ganon requirements.) Thanks to codemann and OWR for most of this work.
|
||||||
* Updated tourney winners (included Doors Async League winners)
|
* Updated tourney winners (included Doors Async League winners)
|
||||||
* Some textual changes for hints (capitalization standardization)
|
* Some textual changes for hints (capitalization standardization)
|
||||||
* Item will be highlighted in red if experimental is on. This will likely be removed.
|
|
||||||
* Reworked GT Trash Fill. Base rate is 0-75% of locations fill with 7 crystals entrance requirements. Triforce hunt is 75%-100% of locations. The 75% number will decrease based on the crystal entrance requirement. Dungeon_only algorithm caps it based on how many items need to be placed in dungeons. Cross dungeon shuffle will now work with the trash fill.
|
* Reworked GT Trash Fill. Base rate is 0-75% of locations fill with 7 crystals entrance requirements. Triforce hunt is 75%-100% of locations. The 75% number will decrease based on the crystal entrance requirement. Dungeon_only algorithm caps it based on how many items need to be placed in dungeons. Cross dungeon shuffle will now work with the trash fill.
|
||||||
* Expanded Mystery logic options (e.g. owglitches)
|
* Expanded Mystery logic options (e.g. owglitches)
|
||||||
* Updated indicators on keysanity menu for overworld map option
|
* Updated indicators on keysanity menu for overworld map option
|
||||||
@@ -259,7 +264,7 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
|||||||
* Fixed a bug with shopsanity + district algorithm where pre-placed potions messed up the placeholder count
|
* Fixed a bug with shopsanity + district algorithm where pre-placed potions messed up the placeholder count
|
||||||
* Fixed usestartinventory flag (can be use on a per player basis)
|
* Fixed usestartinventory flag (can be use on a per player basis)
|
||||||
* Sprite selector fix for systems with SSL issues
|
* Sprite selector fix for systems with SSL issues
|
||||||
* Fix for Standard ER where locations in rain state could be in logic
|
* Fix for Standard ER where locations in rain state could be in logic
|
||||||
* 1.0.0.3
|
* 1.0.0.3
|
||||||
* overworld_map=map mode fixed. Location of dungeons with maps are not shown until map is retrieved. (Dungeon that do not have map like Castle Tower are simply never shown)
|
* overworld_map=map mode fixed. Location of dungeons with maps are not shown until map is retrieved. (Dungeon that do not have map like Castle Tower are simply never shown)
|
||||||
* Aga2 completion on overworld_map now tied to boss defeat flag instead of pyramid hole being opened (fast ganon fix)
|
* Aga2 completion on overworld_map now tied to boss defeat flag instead of pyramid hole being opened (fast ganon fix)
|
||||||
|
|||||||
33
Rom.py
33
Rom.py
@@ -663,18 +663,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
if world.mapshuffle[player]:
|
if world.mapshuffle[player]:
|
||||||
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
|
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
|
||||||
|
|
||||||
if world.pottery[player] not in ['none']:
|
|
||||||
rom.write_bytes(snes_to_pc(0x1F8375), int32_as_bytes(0x2A8000))
|
|
||||||
# make hammer pegs use different tiles
|
|
||||||
Room0127.write_to_rom(snes_to_pc(0x2A8000), rom)
|
|
||||||
|
|
||||||
if world.pot_contents[player]:
|
|
||||||
colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery']
|
|
||||||
and (world.colorizepots[player]
|
|
||||||
or world.pottery[player] in ['reduced', 'clustered']))
|
|
||||||
if world.pot_contents[player].size() > 0x2800:
|
|
||||||
raise Exception('Pot table is too big for current area')
|
|
||||||
world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots)
|
|
||||||
# fix for swamp drains if necessary
|
# fix for swamp drains if necessary
|
||||||
swamp1location = world.get_location('Swamp Palace - Trench 1 Pot Key', player)
|
swamp1location = world.get_location('Swamp Palace - Trench 1 Pot Key', player)
|
||||||
if not swamp1location.pot.indicator:
|
if not swamp1location.pot.indicator:
|
||||||
@@ -1549,6 +1537,19 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
if room.player == player and room.modified:
|
if room.player == player and room.modified:
|
||||||
rom.write_bytes(room.address(), room.rom_data())
|
rom.write_bytes(room.address(), room.rom_data())
|
||||||
|
|
||||||
|
if world.pottery[player] not in ['none']:
|
||||||
|
rom.write_bytes(snes_to_pc(0x1F8375), int32_as_bytes(0x2A8000))
|
||||||
|
# make hammer pegs use different tiles
|
||||||
|
Room0127.write_to_rom(snes_to_pc(0x2A8000), rom)
|
||||||
|
|
||||||
|
if world.pot_contents[player]:
|
||||||
|
colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery']
|
||||||
|
and (world.colorizepots[player]
|
||||||
|
or world.pottery[player] in ['reduced', 'clustered']))
|
||||||
|
if world.pot_contents[player].size() > 0x2800:
|
||||||
|
raise Exception('Pot table is too big for current area')
|
||||||
|
world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots)
|
||||||
|
|
||||||
write_strings(rom, world, player, team)
|
write_strings(rom, world, player, team)
|
||||||
|
|
||||||
# write initial sram
|
# write initial sram
|
||||||
@@ -1982,8 +1983,6 @@ def write_strings(rom, world, player, team):
|
|||||||
else:
|
else:
|
||||||
if isinstance(dest, Region) and dest.type == RegionType.Dungeon and dest.dungeon:
|
if isinstance(dest, Region) and dest.type == RegionType.Dungeon and dest.dungeon:
|
||||||
hint = dest.dungeon.name
|
hint = dest.dungeon.name
|
||||||
elif isinstance(dest, Item) and world.experimental[player]:
|
|
||||||
hint = f'{{C:RED}}{dest.hint_text}{{C:WHITE}}' if dest.hint_text else 'something'
|
|
||||||
else:
|
else:
|
||||||
hint = dest.hint_text if dest.hint_text else "something"
|
hint = dest.hint_text if dest.hint_text else "something"
|
||||||
if dest.player != player:
|
if dest.player != player:
|
||||||
@@ -2162,8 +2161,7 @@ def write_strings(rom, world, player, team):
|
|||||||
if this_location:
|
if this_location:
|
||||||
item_name = this_location[0].item.hint_text
|
item_name = this_location[0].item.hint_text
|
||||||
item_name = item_name[0].upper() + item_name[1:]
|
item_name = item_name[0].upper() + item_name[1:]
|
||||||
item_format = f'{{C:RED}}{item_name}{{C:WHITE}}' if world.experimental[player] else item_name
|
this_hint = f'{item_name} can be found {hint_text(this_location[0])}.'
|
||||||
this_hint = f'{item_format} can be found {hint_text(this_location[0])}.'
|
|
||||||
tt[hint_locations.pop(0)] = this_hint
|
tt[hint_locations.pop(0)] = this_hint
|
||||||
hint_count -= 1
|
hint_count -= 1
|
||||||
|
|
||||||
@@ -2217,8 +2215,7 @@ def write_strings(rom, world, player, team):
|
|||||||
elif hint_type == 'path':
|
elif hint_type == 'path':
|
||||||
if item_count == 1:
|
if item_count == 1:
|
||||||
the_item = text_for_item(next(iter(choice_set)), world, player, team)
|
the_item = text_for_item(next(iter(choice_set)), world, player, team)
|
||||||
item_format = f'{{C:RED}}{the_item}{{C:WHITE}}' if world.experimental[player] else the_item
|
hint_candidates.append((hint_type, f'{name} conceals only {the_item}'))
|
||||||
hint_candidates.append((hint_type, f'{name} conceals only {item_format}'))
|
|
||||||
else:
|
else:
|
||||||
hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items'))
|
hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items'))
|
||||||
district_hints = min(len(hint_candidates), len(hint_locations))
|
district_hints = min(len(hint_candidates), len(hint_locations))
|
||||||
|
|||||||
Reference in New Issue
Block a user