Merge branch 'fouton_vanilla' into beta
This commit is contained in:
@@ -1519,7 +1519,10 @@ class CollectionState(object):
|
||||
if not item:
|
||||
return
|
||||
changed = False
|
||||
if item.name.startswith('Progressive '):
|
||||
if item.name == "Sword and Shield":
|
||||
self.prog_items["Fighter Sword", item.player] += 1
|
||||
self.prog_items["Blue Shield", item.player] += 1
|
||||
elif item.name.startswith('Progressive '):
|
||||
if 'Sword' in item.name:
|
||||
if self.has('Golden Sword', item.player):
|
||||
pass
|
||||
@@ -2895,6 +2898,10 @@ class Item(object):
|
||||
def compass(self):
|
||||
return self.type == 'Compass'
|
||||
|
||||
@property
|
||||
def event(self):
|
||||
return self.type == 'Event'
|
||||
|
||||
@property
|
||||
def dungeon(self):
|
||||
if not self.smallkey and not self.bigkey and not self.map and not self.compass:
|
||||
|
||||
12
CLI.py
12
CLI.py
@@ -89,6 +89,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli", "help", "count")), type=int)
|
||||
parser.add_argument('--tries', default=defval(int(settings["tries"]) if settings["tries"] != "" and settings["tries"] is not None else 1), help="\n".join(fish.translate("cli", "help", "tries")), type=int)
|
||||
parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS)
|
||||
parser.add_argument('--skip_money_balance', action="store_true", help=argparse.SUPPRESS)
|
||||
|
||||
# included for backwards compatibility
|
||||
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
|
||||
@@ -106,7 +107,14 @@ def parse_cli(argv, no_defaults=False):
|
||||
ret = parser.parse_args(argv)
|
||||
|
||||
if ret.keysanity:
|
||||
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = ['wild'] * 4
|
||||
if ret.mapshuffle == "none":
|
||||
ret.mapshuffle = "wild"
|
||||
if ret.compassshuffle == "none":
|
||||
ret.compassshuffle = "wild"
|
||||
if ret.keyshuffle == "none":
|
||||
ret.keyshuffle = "wild"
|
||||
if ret.bigkeyshuffle == "none":
|
||||
ret.bigkeyshuffle = "wild"
|
||||
|
||||
if ret.keydropshuffle:
|
||||
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
|
||||
@@ -178,7 +186,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
'decoupledoors', 'door_type_mode', 'bonk_drops',
|
||||
'trap_door_mode', 'key_logic_algorithm',
|
||||
'door_self_loops', 'any_enemy_logic', 'aga_randomness',
|
||||
'money_balance']:
|
||||
'money_balance', 'patches']:
|
||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||
if player == 1:
|
||||
setattr(ret, name, {1: value})
|
||||
|
||||
5
Fill.py
5
Fill.py
@@ -252,6 +252,7 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
|
||||
or (world.algorithm == 'vanilla_fill' and item_to_place.is_near_dungeon_item(world)))) \
|
||||
or valid_dungeon_placement(item_to_place, location, world):
|
||||
return location
|
||||
|
||||
if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize:
|
||||
location.item = None
|
||||
location.event = False
|
||||
@@ -311,7 +312,9 @@ def valid_dungeon_placement(item, location, world):
|
||||
dungeon = check_dungeon
|
||||
if dungeon:
|
||||
layout = world.dungeon_layouts[location.player][dungeon.name]
|
||||
if not is_dungeon_item(item, world) or item.player != location.player:
|
||||
if item.event:
|
||||
return True
|
||||
elif not is_dungeon_item(item, world) or item.player != location.player:
|
||||
if item.prize and item.is_near_dungeon_item(world):
|
||||
return item.dungeon_object == dungeon and layout.free_items > 0
|
||||
return layout.free_items > 0
|
||||
|
||||
24
ItemList.py
24
ItemList.py
@@ -372,6 +372,13 @@ def generate_itempool(world, player):
|
||||
world.push_precollected(ItemFactory(item, player))
|
||||
|
||||
if world.mode[player] == 'standard' and not world.state.has_blunt_weapon(player):
|
||||
if world.customizer:
|
||||
placements = world.customizer.get_placements()
|
||||
if placements:
|
||||
custom_uncle = placements.get(player, {}).get("Link's Uncle")
|
||||
if custom_uncle:
|
||||
placed_items["Link's Uncle"] = custom_uncle
|
||||
|
||||
if "Link's Uncle" not in placed_items:
|
||||
found_sword = False
|
||||
found_bow = False
|
||||
@@ -1669,12 +1676,15 @@ def fill_specific_items(world):
|
||||
item_to_place, event_flag = get_item_and_event_flag(item, world, player,
|
||||
dungeon_pool, prize_set, prize_pool)
|
||||
if item_to_place:
|
||||
world.push_item(loc, item_to_place, False)
|
||||
loc.locked = True
|
||||
track_outside_keys(item_to_place, loc, world)
|
||||
track_dungeon_items(item_to_place, loc, world)
|
||||
loc.event = (event_flag or item_to_place.advancement
|
||||
or item_to_place.bigkey or item_to_place.smallkey)
|
||||
if not loc.item:
|
||||
world.push_item(loc, item_to_place, False)
|
||||
loc.locked = True
|
||||
track_outside_keys(item_to_place, loc, world)
|
||||
track_dungeon_items(item_to_place, loc, world)
|
||||
loc.event = (event_flag or item_to_place.advancement
|
||||
or item_to_place.bigkey or item_to_place.smallkey)
|
||||
elif loc.item != item_to_place:
|
||||
logging.getLogger('').warning("Failed to place item %s at location %s because it already contained %s", item_to_place, loc.name, loc.item)
|
||||
else:
|
||||
raise Exception(f'Did not find "{item}" in item pool to place at "{location}"')
|
||||
advanced_placements = world.customizer.get_advanced_placements()
|
||||
@@ -1802,7 +1812,7 @@ def shuffle_event_items(world, player):
|
||||
continue
|
||||
break
|
||||
else:
|
||||
raise FillError(f'Unable to place followers: {", ".join(list(map(lambda d: d.hint_text, follower_locations)))}')
|
||||
raise FillError(f'Unable to place followers: {", ".join(list(map(lambda f: f.name, pickup_items)))}')
|
||||
|
||||
|
||||
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):
|
||||
|
||||
2
Items.py
2
Items.py
@@ -55,7 +55,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class
|
||||
'Master Sword': (True, False, 'Sword', 0x50, 100, 'Master Sword!\nEvil\'s bane!', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'),
|
||||
'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'Tempered Sword!\nMore slashy!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'),
|
||||
'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'Fighter Sword!\nStarter level slashy!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'),
|
||||
'Sword and Shield': (True, False, 'Sword', 0x00, 'Sword and Shield!\nUncle sword ahoy!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'),
|
||||
'Sword and Shield': (True, False, 'Sword', 0x00, 50, 'Sword and Shield!\nUncle sword ahoy!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'),
|
||||
'Golden Sword': (True, False, 'Sword', 0x03, 200, 'Golden Sword!\nBest slashy!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'),
|
||||
'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'Sword!\nA better sword for your time!', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'),
|
||||
'Progressive Glove': (True, False, None, 0x61, 150, 'Glove!\nLift more than you can now!', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'),
|
||||
|
||||
7
Main.py
7
Main.py
@@ -67,6 +67,7 @@ from Rom import (
|
||||
JsonRom,
|
||||
LocalRom,
|
||||
apply_rom_settings,
|
||||
apply_rom_patches,
|
||||
get_hash_string,
|
||||
patch_race_rom,
|
||||
patch_rom,
|
||||
@@ -335,7 +336,7 @@ def main(args, seed=None, fish=None):
|
||||
for player in range(1, world.players+1):
|
||||
if world.shopsanity[player]:
|
||||
customize_shops(world, player)
|
||||
if args.algorithm in ['balanced', 'equitable']:
|
||||
if not args.skip_money_balance and args.algorithm in ['balanced', 'equitable']:
|
||||
balance_money_progression(world)
|
||||
ensure_good_items(world, True)
|
||||
|
||||
@@ -364,6 +365,8 @@ def main(args, seed=None, fish=None):
|
||||
rom_names.append((player, team, list(rom.name)))
|
||||
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
|
||||
|
||||
if args.patches[player]:
|
||||
apply_rom_patches(rom, map(lambda arg: arg.strip(), args.patches[player].split(",")))
|
||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
||||
args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.triforce_gfx[player],
|
||||
args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player],
|
||||
@@ -572,7 +575,7 @@ def init_world(args, fish):
|
||||
world.collection_rate = args.collection_rate.copy()
|
||||
world.colorizepots = args.colorizepots.copy()
|
||||
world.aga_randomness = args.aga_randomness.copy()
|
||||
world.money_balance = args.money_balance.copy()
|
||||
world.money_balance = {player: int(args.money_balance[player]) for player in range(1, world.players + 1)}
|
||||
|
||||
# custom settings - these haven't been promoted to full settings yet
|
||||
in_progress_settings = ['force_enemy']
|
||||
|
||||
62
Rom.py
62
Rom.py
@@ -994,7 +994,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
set_inverted_mode(world, player, rom, inverted_buffer)
|
||||
|
||||
uncle_location = world.get_location('Link\'s Uncle', player)
|
||||
if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword']:
|
||||
if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword', 'Sword and Shield']:
|
||||
# disable sword sprite from uncle
|
||||
rom.write_bytes(0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
@@ -1779,14 +1779,15 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
|
||||
rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00)
|
||||
rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00)
|
||||
|
||||
# remove shield from uncle
|
||||
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
||||
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||
if uncle_location.item is None or uncle_location.item.name not in ['Sword and Shield']:
|
||||
# remove shield from uncle
|
||||
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
||||
rom.write_bytes(0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
||||
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||
|
||||
write_int16(rom, 0x180183, 0) # Escape fill rupee bow
|
||||
# Uncle / Zelda / Mantle respawn refills (magic, bombs, arrows)
|
||||
@@ -2074,6 +2075,49 @@ def hud_format_text(text):
|
||||
output += b'\x7f\x00'
|
||||
return output[:32]
|
||||
|
||||
def read_bytes(f, count):
|
||||
values = f.read(count)
|
||||
if len(values) < count:
|
||||
raise EOFError
|
||||
return values
|
||||
|
||||
def apply_rom_patches(rom, patches):
|
||||
for patch in patches:
|
||||
if not os.path.exists(f"patches/{patch}.ips"):
|
||||
logging.getLogger('').warning("Patch %s not found -- skipping", patch)
|
||||
continue
|
||||
|
||||
with open(f"patches/{patch}.ips", "rb") as f:
|
||||
byte_changes = []
|
||||
try:
|
||||
header = read_bytes(f, 5)
|
||||
if header != b"PATCH":
|
||||
logging.getLogger('').warning("Patch %s invalid -- skipping", patch)
|
||||
continue
|
||||
|
||||
while True:
|
||||
address = read_bytes(f, 3)
|
||||
if address == b"EOF":
|
||||
break
|
||||
|
||||
address = int.from_bytes(address, byteorder="big")
|
||||
length = int.from_bytes(read_bytes(f, 2), byteorder="big")
|
||||
|
||||
if length > 0:
|
||||
byte_changes.append((address, list(f.read(length))))
|
||||
else:
|
||||
length = int.from_bytes(read_bytes(f, 2), byteorder="big")
|
||||
value = read_bytes(f, 1)
|
||||
byte_changes.append((address, length * list(value)))
|
||||
|
||||
for address, values in byte_changes:
|
||||
rom.write_bytes(address, values)
|
||||
|
||||
logging.getLogger("").info("Patch %s applied successfully", patch)
|
||||
|
||||
except EOFError:
|
||||
logging.getLogger('').warning("Patch %s invalid -- skipping", patch)
|
||||
continue
|
||||
|
||||
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, triforce_gfx,
|
||||
ow_palettes, uw_palettes, reduce_flashing, shuffle_sfx,
|
||||
|
||||
BIN
patches/all_starting.ips
Normal file
BIN
patches/all_starting.ips
Normal file
Binary file not shown.
BIN
patches/no_dungeon_item_popups.ips
Normal file
BIN
patches/no_dungeon_item_popups.ips
Normal file
Binary file not shown.
BIN
patches/no_escape_assist.ips
Normal file
BIN
patches/no_escape_assist.ips
Normal file
Binary file not shown.
BIN
patches/pod_key_swap.ips
Normal file
BIN
patches/pod_key_swap.ips
Normal file
Binary file not shown.
@@ -754,6 +754,10 @@
|
||||
"type": "bool"
|
||||
},
|
||||
"money_balance": {},
|
||||
"patches": {
|
||||
"type": "str",
|
||||
"help": "suppress"
|
||||
},
|
||||
"settingsonload": {
|
||||
"choices": [
|
||||
"default",
|
||||
|
||||
Reference in New Issue
Block a user