Many bugfixes
* Fixed inverted generation issues with pottery option * Moved SRAM according to SRAM standard * Removed equitable algorithm * Upped TFH goal limit to 254 * Cuccos should no longer cause trap door rooms to not open * Added double click fix for install.py * Fix for pottery item palettes near bonkable torches * Fix for multiworld progression balancing would place Nothing or Arrow items
This commit is contained in:
8
Fill.py
8
Fill.py
@@ -446,15 +446,19 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
|||||||
unfilled = [location.name for location in fill_locations]
|
unfilled = [location.name for location in fill_locations]
|
||||||
if unplaced or unfilled:
|
if unplaced or unfilled:
|
||||||
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||||
|
ensure_good_pots(world)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_good_pots(world, write_skips=False):
|
||||||
for loc in world.get_locations():
|
for loc in world.get_locations():
|
||||||
# convert Arrows 5 and Nothing when necessary
|
# convert Arrows 5 and Nothing when necessary
|
||||||
if (loc.item.name in {'Arrows (5)', 'Nothing'}
|
if (loc.item.name in {'Arrows (5)', 'Nothing'}
|
||||||
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
|
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
|
||||||
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player)
|
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player)
|
||||||
# don't write out all pots to spoiler
|
# don't write out all pots to spoiler
|
||||||
if loc.type == LocationType.Pot and loc.item.name in valid_pot_items:
|
if write_skips:
|
||||||
loc.skip = True
|
if loc.type == LocationType.Pot and loc.item.name in valid_pot_items:
|
||||||
|
loc.skip = True
|
||||||
|
|
||||||
|
|
||||||
invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)'}
|
invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)'}
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ def create_inverted_regions(world, player):
|
|||||||
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot']),
|
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot']),
|
||||||
create_cave_region(player, 'Death Mountain Return Cave (left)', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave E']),
|
create_cave_region(player, 'Death Mountain Return Cave (left)', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave E']),
|
||||||
create_cave_region(player, 'Death Mountain Return Cave (right)', 'a connector', None, ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']),
|
create_cave_region(player, 'Death Mountain Return Cave (right)', 'a connector', None, ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']),
|
||||||
|
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)', 'Bumper Cave Ledge Mirror Spot']),
|
||||||
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||||
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
|
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
|
||||||
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||||
|
|||||||
@@ -964,7 +964,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
|||||||
pool.append(thisbottle)
|
pool.append(thisbottle)
|
||||||
|
|
||||||
if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0:
|
if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0:
|
||||||
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], 99), 1) #To display, count must be between 1 and 99.
|
# To display, count must be between 1 and 254 - larger values are not yet supported
|
||||||
|
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], 254), 1)
|
||||||
treasure_hunt_icon = 'Triforce Piece'
|
treasure_hunt_icon = 'Triforce Piece'
|
||||||
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
|
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
|
||||||
if (customitemarray["triforcepieces"] < treasure_hunt_count) and (goal == 'triforcehunt') and (customitemarray["triforce"] == 0):
|
if (customitemarray["triforcepieces"] < treasure_hunt_count) and (goal == 'triforcehunt') and (customitemarray["triforce"] == 0):
|
||||||
|
|||||||
5
Main.py
5
Main.py
@@ -23,7 +23,7 @@ from DoorShuffle import link_doors, connect_portal, link_doors_prep
|
|||||||
from RoomData import create_rooms
|
from RoomData import create_rooms
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
from Dungeons import create_dungeons
|
from Dungeons import create_dungeons
|
||||||
from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive
|
from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots
|
||||||
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
|
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
|
||||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
@@ -31,7 +31,7 @@ from Utils import output_path, parse_player_names
|
|||||||
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
|
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
|
||||||
|
|
||||||
|
|
||||||
__version__ = '1.0.1.2-v'
|
__version__ = '1.0.1.3-v'
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
|
|
||||||
@@ -253,6 +253,7 @@ def main(args, seed=None, fish=None):
|
|||||||
customize_shops(world, player)
|
customize_shops(world, player)
|
||||||
if args.algorithm in ['balanced', 'equitable']:
|
if args.algorithm in ['balanced', 'equitable']:
|
||||||
balance_money_progression(world)
|
balance_money_progression(world)
|
||||||
|
ensure_good_pots(world, True)
|
||||||
|
|
||||||
outfilebase = f'DR_{args.outputname if args.outputname else world.seed}'
|
outfilebase = f'DR_{args.outputname if args.outputname else world.seed}'
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ INGAME_MODES = {0x07, 0x09, 0x0b}
|
|||||||
SAVEDATA_START = WRAM_START + 0xF000
|
SAVEDATA_START = WRAM_START + 0xF000
|
||||||
SAVEDATA_SIZE = 0x500
|
SAVEDATA_SIZE = 0x500
|
||||||
|
|
||||||
POT_ITEMS_SRAM_START = WRAM_START + 0x016600
|
POT_ITEMS_SRAM_START = WRAM_START + 0x016018
|
||||||
SPRITE_ITEMS_SRAM_START = WRAM_START + 0x016850
|
SPRITE_ITEMS_SRAM_START = WRAM_START + 0x016268
|
||||||
ITEM_SRAM_SIZE = 0x250
|
ITEM_SRAM_SIZE = 0x250
|
||||||
|
|
||||||
RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes
|
RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes
|
||||||
|
|||||||
@@ -45,10 +45,6 @@ The "Item Sorting" option or ```--algorithm``` has been updated with new placeme
|
|||||||
|
|
||||||
This one stays the same as before and is recommended for the most random distribution of items.
|
This one stays the same as before and is recommended for the most random distribution of items.
|
||||||
|
|
||||||
### Equitable
|
|
||||||
|
|
||||||
This one is currently under development and may not fill correctly. It is a new method that should allow item and key logic to interact. (Vanilla key placement in PoD is theoretically possible, but isn't yet.)
|
|
||||||
|
|
||||||
### Vanilla Fill
|
### Vanilla Fill
|
||||||
|
|
||||||
This fill attempts to place all items in their vanilla locations when possible. Obviously shuffling entrances or the dungeon interiors will often prevent items from being placed in their vanilla location. If the vanilla fill is not possible, then other locations are tried in sequence preferring "major" locations (see below), then heart piece locations, then the rest except for GT locations which are preferred last. Note the PoD small key that is normally found in the dark maze in vanilla is move to Harmless Hellway due to the placement algorithm limitation.
|
This fill attempts to place all items in their vanilla locations when possible. Obviously shuffling entrances or the dungeon interiors will often prevent items from being placed in their vanilla location. If the vanilla fill is not possible, then other locations are tried in sequence preferring "major" locations (see below), then heart piece locations, then the rest except for GT locations which are preferred last. Note the PoD small key that is normally found in the dark maze in vanilla is move to Harmless Hellway due to the placement algorithm limitation.
|
||||||
@@ -95,7 +91,7 @@ In multiworld, the districts chosen apply to all players.
|
|||||||
|
|
||||||
### CLI values:
|
### CLI values:
|
||||||
|
|
||||||
```balanced, equitable, vanilla_fill, major_only, dungeon_only, district```
|
```balanced, vanilla_fill, major_only, dungeon_only, district```
|
||||||
|
|
||||||
## New Hints
|
## New Hints
|
||||||
|
|
||||||
@@ -148,6 +144,15 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
|||||||
|
|
||||||
#### Volatile
|
#### Volatile
|
||||||
|
|
||||||
|
* 1.0.1.3
|
||||||
|
* Fixed inverted generation issues with pottery option
|
||||||
|
* Moved SRAM according to SRAM standard
|
||||||
|
* Removed equitable algorithm
|
||||||
|
* Upped TFH goal limit to 254
|
||||||
|
* Cuccos should no longer cause trap door rooms to not open
|
||||||
|
* Added double click fix for install.py
|
||||||
|
* Fix for pottery item palettes near bonkable torches
|
||||||
|
* Fix for multiworld progression balancing would place Nothing or Arrow items
|
||||||
* 1.0.1.2
|
* 1.0.1.2
|
||||||
* Fixed logic for pots in TR Hub and TR Dark Ride
|
* Fixed logic for pots in TR Hub and TR Dark Ride
|
||||||
* Fix for districting + shopsanity
|
* Fix for districting + shopsanity
|
||||||
|
|||||||
@@ -1057,7 +1057,11 @@ def create_pot_location(pot, pot_index, super_tile, world, player):
|
|||||||
if (pot.item not in [PotItem.Key, PotItem.Hole]
|
if (pot.item not in [PotItem.Key, PotItem.Hole]
|
||||||
and (pot.item != PotItem.Switch or world.potshuffle[player])):
|
and (pot.item != PotItem.Switch or world.potshuffle[player])):
|
||||||
address = pot_address(pot_index, super_tile)
|
address = pot_address(pot_index, super_tile)
|
||||||
parent = world.get_region(pot.room, player)
|
region = pot.room
|
||||||
|
if world.mode[player] == 'inverted':
|
||||||
|
if region == 'Links House':
|
||||||
|
region = 'Inverted Links House'
|
||||||
|
parent = world.get_region(region, player)
|
||||||
descriptor = 'Large Block' if pot.flags & PotFlags.Block else f'Pot #{pot_index+1}'
|
descriptor = 'Large Block' if pot.flags & PotFlags.Block else f'Pot #{pot_index+1}'
|
||||||
hint_text = ('under a block' if pot.flags & PotFlags.Block else 'in a pot')
|
hint_text = ('under a block' if pot.flags & PotFlags.Block else 'in a pot')
|
||||||
modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint',
|
modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint',
|
||||||
@@ -1073,7 +1077,7 @@ def create_pot_location(pot, pot_index, super_tile, world, player):
|
|||||||
|
|
||||||
|
|
||||||
def pot_address(pot_index, super_tile):
|
def pot_address(pot_index, super_tile):
|
||||||
return 0x7f6600 + super_tile * 2 + (pot_index << 24)
|
return 0x7f6018 + super_tile * 2 + (pot_index << 24)
|
||||||
|
|
||||||
|
|
||||||
# (type, room_id, shopkeeper, custom, locked, [items])
|
# (type, room_id, shopkeeper, custom, locked, [items])
|
||||||
|
|||||||
5
Rom.py
5
Rom.py
@@ -35,7 +35,7 @@ from source.item.FillUtil import valid_pot_items
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '83bdcdbe989281b7eb36a10150314997'
|
RANDOMIZERBASEHASH = 'a25e589ca9359e73b0ed94cab8db107d'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -884,6 +884,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
rom.write_byte(0x142A51, multiClientFlags)
|
rom.write_byte(0x142A51, multiClientFlags)
|
||||||
rom.write_byte(0x142A55, ((0x1 if world.pottery[player] != 'none' else 0) # StandingItemCounterMask
|
rom.write_byte(0x142A55, ((0x1 if world.pottery[player] != 'none' else 0) # StandingItemCounterMask
|
||||||
| (0x2 if world.dropshuffle[player] else 0)))
|
| (0x2 if world.dropshuffle[player] else 0)))
|
||||||
|
if world.pottery[player] not in ['none', 'keys']:
|
||||||
|
# Cuccos should not prevent kill rooms from opening
|
||||||
|
rom.write_byte(snes_to_pc(0x0DB457), 0x40)
|
||||||
|
|
||||||
write_int16(rom, 0x187010, credits_total) # dynamic credits
|
write_int16(rom, 0x187010, credits_total) # dynamic credits
|
||||||
if credits_total != 216:
|
if credits_total != 216:
|
||||||
|
|||||||
Binary file not shown.
@@ -116,7 +116,6 @@
|
|||||||
"algorithm": {
|
"algorithm": {
|
||||||
"choices": [
|
"choices": [
|
||||||
"balanced",
|
"balanced",
|
||||||
"equitable",
|
|
||||||
"vanilla_fill",
|
"vanilla_fill",
|
||||||
"major_only",
|
"major_only",
|
||||||
"dungeon_only",
|
"dungeon_only",
|
||||||
|
|||||||
@@ -154,8 +154,6 @@
|
|||||||
"balanced: vt26 derivative that aims to strike a balance between",
|
"balanced: vt26 derivative that aims to strike a balance between",
|
||||||
" the overworld heavy vt25 and the dungeon heavy vt26",
|
" the overworld heavy vt25 and the dungeon heavy vt26",
|
||||||
" algorithm.",
|
" algorithm.",
|
||||||
"equitable: does not place dungeon items first allowing new potential",
|
|
||||||
" but mixed with the normal advancement pool",
|
|
||||||
"restricted placements: these consider all major items to be special and attempts",
|
"restricted placements: these consider all major items to be special and attempts",
|
||||||
"to place items from fixed to semi-random locations. For purposes of these shuffles, all",
|
"to place items from fixed to semi-random locations. For purposes of these shuffles, all",
|
||||||
"Y items, A items, swords (unless vanilla swords), mails, shields, heart containers and",
|
"Y items, A items, swords (unless vanilla swords), mails, shields, heart containers and",
|
||||||
|
|||||||
@@ -289,7 +289,6 @@
|
|||||||
|
|
||||||
"randomizer.item.sortingalgo": "Item Sorting",
|
"randomizer.item.sortingalgo": "Item Sorting",
|
||||||
"randomizer.item.sortingalgo.balanced": "Balanced",
|
"randomizer.item.sortingalgo.balanced": "Balanced",
|
||||||
"randomizer.item.sortingalgo.equitable": "Equitable",
|
|
||||||
"randomizer.item.sortingalgo.vanilla_fill": "Vanilla Fill",
|
"randomizer.item.sortingalgo.vanilla_fill": "Vanilla Fill",
|
||||||
"randomizer.item.sortingalgo.major_only": "Major Location Restriction",
|
"randomizer.item.sortingalgo.major_only": "Major Location Restriction",
|
||||||
"randomizer.item.sortingalgo.dungeon_only": "Dungeon Restriction",
|
"randomizer.item.sortingalgo.dungeon_only": "Dungeon Restriction",
|
||||||
|
|||||||
@@ -117,7 +117,6 @@
|
|||||||
"default": "balanced",
|
"default": "balanced",
|
||||||
"options": [
|
"options": [
|
||||||
"balanced",
|
"balanced",
|
||||||
"equitable",
|
|
||||||
"vanilla_fill",
|
"vanilla_fill",
|
||||||
"major_only",
|
"major_only",
|
||||||
"dungeon_only",
|
"dungeon_only",
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import subprocess # do stuff at the shell level
|
|||||||
|
|
||||||
env = common.prepare_env()
|
env = common.prepare_env()
|
||||||
|
|
||||||
pip_requirements = os.path.join(".","resources","app","meta","manifests","pip_requirements.txt")
|
pip_requirements = os.path.join("..","resources","app","meta","manifests","pip_requirements.txt")
|
||||||
|
if not os.path.isfile(pip_requirements):
|
||||||
|
pip_requirements = os.path.join("..","..","..","resources","app","meta","manifests","pip_requirements.txt")
|
||||||
|
|
||||||
def run_install(PY_VERSION,USER):
|
def run_install(PY_VERSION,USER):
|
||||||
# get executables
|
# get executables
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ def create_district_helper(world, player):
|
|||||||
central_lw_entrances.append('Inverted Big Bomb Shop')
|
central_lw_entrances.append('Inverted Big Bomb Shop')
|
||||||
central_lw_entrances.remove('Links House')
|
central_lw_entrances.remove('Links House')
|
||||||
south_dw_entrances.append('Inverted Links House')
|
south_dw_entrances.append('Inverted Links House')
|
||||||
voo_north_entrances.remove('Dark Sanctuary')
|
voo_north_entrances.remove('Dark Sanctuary Hint')
|
||||||
voo_north_entrances.append('Inverted Dark Sanctuary')
|
voo_north_entrances.append('Inverted Dark Sanctuary')
|
||||||
voo_north_entrances.remove('Bumper Cave (Top)')
|
voo_north_entrances.remove('Bumper Cave (Top)')
|
||||||
nw_lw_entrances.append('Bumper Cave (Top)')
|
nw_lw_entrances.append('Bumper Cave (Top)')
|
||||||
|
|||||||
Reference in New Issue
Block a user