diff --git a/CHANGELOG.md b/CHANGELOG.md index f782574c..522421f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.3.0.2 +- \~Merged in DR v1.2.0.12~ + - Fixed some door landing issues +- Added Cold Fairy Statue as a new Bonk Location in Bonk Drop Shuffle +- Added Customizer support for enabling OWR Options +- Removed `Arrows (5)` item from Item Table, replaces with `Arrows (10)` + ## 0.3.0.1 - \~Merged in DR v1.2.0.10~ - Fixed some door landing issues diff --git a/Fill.py b/Fill.py index 4507ddeb..d549e969 100644 --- a/Fill.py +++ b/Fill.py @@ -538,7 +538,7 @@ def ensure_good_pots(world, write_skips=False): and (loc.type != LocationType.Pot or loc.item.player != loc.player)): loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) if (loc.item.name in {'Arrows (5)'} - and (loc.type not in [LocationType.Pot, LocationType.Bonk] 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) # # can be placed here by multiworld balancing or shop balancing # # change it to something normal for the player it got swapped to diff --git a/ItemList.py b/ItemList.py index 81826c70..956b8153 100644 --- a/ItemList.py +++ b/ItemList.py @@ -392,21 +392,7 @@ def generate_itempool(world, player): for i in range(4): next(adv_heart_pieces).advancement = True - beeweights = {'0': {None: 100}, - '1': {None: 75, 'trap': 25}, - '2': {None: 40, 'trap': 40, 'bee': 20}, - '3': {'trap': 50, 'bee': 50}, - '4': {'trap': 100}} - def beemizer(item): - if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type: - choice = random.choices(list(beeweights[world.beemizer[item.player]].keys()), weights=list(beeweights[world.beemizer[item.player]].values()))[0] - return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player) - return item - - if not skip_pool_adjustments: - world.itempool += [beemizer(item) for item in items] - else: - world.itempool += items + world.itempool += items # shuffle medallions mm_medallion, tr_medallion = None, None @@ -463,6 +449,20 @@ def generate_itempool(world, player): # modfiy based on start inventory, if any modify_pool_for_start_inventory(start_inventory, world, player) + beeweights = {'0': {None: 100}, + '1': {None: 75, 'trap': 25}, + '2': {None: 40, 'trap': 40, 'bee': 20}, + '3': {'trap': 50, 'bee': 50}, + '4': {'trap': 100}} + def beemizer(item): + if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type: + choice = random.choices(list(beeweights[world.beemizer[item.player]].keys()), weights=list(beeweights[world.beemizer[item.player]].values()))[0] + return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player) + return item + + if not skip_pool_adjustments: + world.itempool = [beemizer(item) for item in world.itempool] + # 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) @@ -1104,6 +1104,8 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt pool.remove('Fighter Sword') pool.extend(['Rupees (50)']) + #TODO: Remove test placements + #place_item('Purple Chest', 'Magic Mirror') if timer in ['timed', 'timed-countdown']: pool.extend(diff.timedother) clock_mode = 'stopwatch' if timer == 'timed' else 'countdown' diff --git a/Items.py b/Items.py index 6bde89ef..1b7a041d 100644 --- a/Items.py +++ b/Items.py @@ -80,7 +80,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), - 'Arrows (5)': (False, False, None, 0xB5, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), + 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), 'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), 'Chicken': (False, False, None, 0xB3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), @@ -175,6 +175,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Green Potion': (False, False, None, 0x2F, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a green potion'), 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), + 'Good Bee': (False, False, None, 0xB5, 10, 'I will sting your foes a lot', 'and the cold buddy', 'the beekeeper kid', 'cold insect for sale', 'shroom pollenation', 'bottle boy has cold bee again', 'a good bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), 'Apples': (False, False, None, 0xB1, 30, 'Just a few pieces of fruit!', 'and the juicy fruit', 'the fruity kid', 'the fruit stand', 'expired fruit', 'bottle boy has fruit again', 'an apple hoard'), 'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), diff --git a/Main.py b/Main.py index 00762aa2..08cd7ff0 100644 --- a/Main.py +++ b/Main.py @@ -36,7 +36,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.10u' +__version__ = '1.2.0.12u' from source.classes.BabelFish import BabelFish diff --git a/OverworldShuffle.py b/OverworldShuffle.py index ae01be3a..f4eb366b 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -7,7 +7,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType from OverworldGlitchRules import create_owg_connections from Utils import bidict -version_number = '0.3.0.1' +version_number = '0.3.0.2' # branch indicator is intentionally different across branches version_branch = '' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6cb841e6..317db6b2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -108,6 +108,16 @@ These are now independent of retro mode and have three options: None, Random, an * Bonk Fairy (Dark) # Bug Fixes and Notes +* 1.2.0.12u + * Fix for mirror portal in inverted + * Yet another fix for blocked door in Standard ER +* 1.2.0.11u + * Fixed an issue with lower layer doors in Standard + * Fix for doors in cave state (will no longer be vanilla) + * Added a logic rule for th murderdactyl near bumper ledge for OHKO purposes + * Enemizer alteration for Hovers and normal enemies in shallow water + * Fix for beemizer including modes with an increased item pool + * Fix for district algorithm * 1.2.0.10u * Fixed overrun issues with edge transitions * Better support for customized start_inventory with dungeon items diff --git a/Regions.py b/Regions.py index 0bbca8b0..4f8e85de 100644 --- a/Regions.py +++ b/Regions.py @@ -688,6 +688,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Thieves Big Chest Nook', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest'], ['Thieves Big Chest Nook ES Edge']), create_dungeon_region(player, 'Thieves Hallway', 'Thieves\' Town', ['Thieves\' Town - Hallway Pot Key'], ['Thieves Hallway SE', 'Thieves Hallway NE', 'Thieves Hallway WN', 'Thieves Hallway WS']), create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', ['Revealing Light', 'Thieves\' Town - Boss', 'Thieves\' Town - Prize'], ['Thieves Boss SE']), + #create_dungeon_region(player, 'Thieves Boss', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize'], ['Revealing Light', 'Thieves Boss SE']), + #create_dungeon_region(player, 'Thieves Revealing Light', 'Thieves\' Town', ['Revealing Light'], ['Thieves Boss Room']), create_dungeon_region(player, 'Thieves Pot Alcove Mid', 'Thieves\' Town', None, ['Thieves Pot Alcove Mid ES', 'Thieves Pot Alcove Mid WS']), create_dungeon_region(player, 'Thieves Pot Alcove Bottom', 'Thieves\' Town', None, ['Thieves Pot Alcove Bottom SW']), create_dungeon_region(player, 'Thieves Pot Alcove Top', 'Thieves\' Town', None, ['Thieves Pot Alcove Top NW']), @@ -1304,7 +1306,8 @@ bonk_prize_table = { 'Dark Tree Line Tree 2': (0x26, 0x10, False, '', 'Dark Tree Line Area', 'in a tree'), 'Dark Tree Line Tree 3': (0x27, 0x08, False, '', 'Dark Tree Line Area', 'in a tree'), 'Dark Tree Line Tree 4': (0x28, 0x04, False, '', 'Dark Tree Line Area', 'in a tree'), - 'Hype Cave Statue': (0x29, 0x10, False, '', 'Hype Cave Area', 'encased in stone') + 'Hype Cave Statue': (0x29, 0x10, False, '', 'Hype Cave Area', 'encased in stone'), + 'Cold Fairy Statue': (0x2a, 0x02, False, '', 'Good Bee Cave', 'encased in stone') } bonk_table_by_location_id = {0x2ABB00+(data[0]*6)+3: name for name, data in bonk_prize_table.items()} diff --git a/Rom.py b/Rom.py index 639363f7..3a5c689c 100644 --- a/Rom.py +++ b/Rom.py @@ -38,7 +38,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '7043e64e74b2452367de7cc146873524' +RANDOMIZERBASEHASH = '4458a348e3040d79d0e28d572579fcb5' class JsonRom(object): @@ -686,7 +686,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): flute_spots = default_flute_connections else: flute_spots = world.owflutespots[player] - owFlags |= 0x100 + owFlags |= 0x0100 for o in range(0, len(flute_spots)): owslot = flute_spots[o] @@ -740,12 +740,12 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): owMode = 2 if world.owKeepSimilar[player] and (world.owShuffle[player] != 'vanilla' or world.owCrossed[player] in ['limited', 'chaos']): - owMode |= 0x100 + owMode |= 0x0100 if world.owCrossed[player] != 'none' and (world.owCrossed[player] != 'polar' or world.owMixed[player]): - owMode |= 0x200 + owMode |= 0x0200 world.fix_fake_world[player] = True if world.owMixed[player]: - owMode |= 0x400 + owMode |= 0x0400 # patches map data specific for OW Shuffle #inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM @@ -793,7 +793,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # for prize, address in zip(bonk_prizes, bonk_addresses): # rom.write_byte(address, prize) - owFlags |= 0x200 + owFlags |= 0x0200 # setting spriteID to D8, a placeholder sprite we use to inform ROM to spawn a dynamic item #for address in bonk_addresses: @@ -803,6 +803,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(snes_to_pc(0x09AE32), 0xD8) rom.write_byte(snes_to_pc(0x09AE35), 0xD8) + rom.write_byte(snes_to_pc(0x06918E), 0x80) # skip good bee bottle check + write_int16(rom, 0x150002, owMode) write_int16(rom, 0x150004, owFlags) @@ -1649,6 +1651,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if world.shuffle_bonk_drops[player]: # warning, this temporary patch might cause fairies to respawn differently?, limiting this to bonk drop mode only rom.write_byte(snes_to_pc(0x0DB808), 0x03) # patch fairies sprites to not permadeath like enemies + rom.write_byte(snes_to_pc(0x1DF6D8), 0) # allows sprites to travel across water / same flag as write_enemizer_tweaks # allow smith into multi-entrance caves in appropriate shuffles if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): @@ -1695,6 +1698,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): raise Exception('Pot table is too big for current area') world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) + write_enemizer_tweaks(rom, world, player) write_strings(rom, world, player, team) # write initial sram @@ -1785,6 +1789,11 @@ def write_custom_shops(rom, world, player): rom.write_bytes(0x184900, items_data) +def write_enemizer_tweaks(rom, world, player): + if world.enemy_shuffle[player] != 'none': + rom.write_byte(snes_to_pc(0x1DF6D8), 0) # lets enemies walk on water instead of clipping into infinity? + rom.write_byte(snes_to_pc(0x0DB6B3), 0x82) # hovers don't need water necessarily? + def hud_format_text(text): output = bytes() for char in text.lower(): diff --git a/Rules.py b/Rules.py index 8676cb96..1cb91819 100644 --- a/Rules.py +++ b/Rules.py @@ -862,7 +862,9 @@ def default_rules(world, player): from Regions import bonk_prize_table for location_name, (_, _, aga_required, _, _, _) in bonk_prize_table.items(): loc = world.get_location(location_name, player) - if not aga_required: + if location_name == 'Cold Fairy Statue': + set_rule(loc, lambda state: state.can_use_bombs(player) and state.can_collect_bonkdrops(player)) + elif not aga_required: set_rule(loc, lambda state: state.can_collect_bonkdrops(player)) else: set_rule(loc, lambda state: state.can_collect_bonkdrops(player) and state.has_beaten_aga(player)) @@ -921,6 +923,8 @@ def default_rules(world, player): set_rule(world.get_entrance('Desert Pass Rocks (South)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Skull Woods Bush Rock (West)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Skull Woods Bush Rock (East)', player), lambda state: state.can_lift_rocks(player)) + # this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off? + set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: (state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))) set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Skull Woods Pass Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) @@ -1157,6 +1161,7 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (East)', player), player) add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) add_bunny_rule(world.get_entrance('East Dark Death Mountain Bushes', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Ledge Drop', player), player) add_bunny_rule(world.get_entrance('Bumper Cave Entrance Rock', player), player) add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (West)', player), player) add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (East)', player), player) diff --git a/Tables.py b/Tables.py index a30f24f0..52f96488 100644 --- a/Tables.py +++ b/Tables.py @@ -130,6 +130,7 @@ bonk_prize_lookup = { 'Chicken': (0x0b, 0, None), 'Bee Trap': (0x79, 6, None), 'Apples': (0xac, 8, None), + 'Good Bee': (0xb2, 1, None), 'Small Heart': (0xd8, 2, None), 'Rupee (1)': (0xd9, 0, None), 'Rupees (5)': (0xda, 3, None), # TODO: add in murahdahla tree rupee diff --git a/asm/owrando.asm b/asm/owrando.asm index 09fe010d..3f3a7be0 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -157,6 +157,14 @@ and #$7f : eor #$40 : nop #2 org $06AD4C jsl.l OWBonkDrops : nop #4 +org $1EDE6F +jsl.l OWBonkGoodBeeDrop : bra + +GoldBee_SpawnSelf_SetProperties: +phb : lda.b #$1E : pha : plb ; switch to bank 1E + jsr GoldBee_SpawnSelf+12 +plb : rtl +nop #3 ++ ;Code org $aa8800 @@ -395,6 +403,112 @@ LoadMapDarkOrMixed: dw $0400+$0210 ; bottom right } +OWBonkGoodBeeDrop: +{ + LDA.l OWFlags+1 : AND.b #$02 : BNE .shuffled + .vanilla ; what we wrote over + STZ.w $0DD0,X + LDA.l BottleContentsOne : ORA.l BottleContentsTwo + ORA.l BottleContentsThree : ORA.l BottleContentsFour + RTL + .shuffled + PHY : TXY + LDA.l RoomDataWRAM[$0120].high : AND.b #$02 : PHA : BNE + ; check if collected + LDA.b #$1B : STA $12F ; JSL Sound_SetSfx3PanLong ; seems that when you bonk, there is a pending bonk sfx, so we clear that out and replace with reveal secret sfx + + + LDA.l OWBonkPrizeData+(42*6+4) : BEQ + ; multiworld item + LDA.l OWBonkPrizeData+(42*6+3) + JMP .spawn_item + + + + .determine_type ; S = Collected, FlagBitmask, X (row + 2) + LDA.l OWBonkPrizeData+(42*6+3) ; A = item id + CMP.b #$B0 : BNE + + LDA.b #$79 : JMP .sprite_transform ; transform to bees + + CMP.b #$42 : BNE + + JSL.l Sprite_TransmuteToBomb ; transform a heart to bomb, vanilla behavior + JMP .mark_collected + + CMP.b #$34 : BNE + + LDA.b #$D9 : JMP .sprite_transform ; transform to single rupee + + CMP.b #$35 : BNE + + LDA.b #$DA : JMP .sprite_transform ; transform to blue rupee + + CMP.b #$36 : BNE + + LDA.b #$DB : BRA .sprite_transform ; transform to red rupee + + CMP.b #$27 : BNE + + LDA.b #$DC : BRA .sprite_transform ; transform to 1 bomb + + CMP.b #$28 : BNE + + LDA.b #$DD : BRA .sprite_transform ; transform to 4 bombs + + CMP.b #$31 : BNE + + LDA.b #$DE : BRA .sprite_transform ; transform to 8 bombs + + CMP.b #$45 : BNE + + LDA.b #$DF : BRA .sprite_transform ; transform to small magic + + CMP.b #$B4 : BNE + + LDA.b #$E0 : BRA .sprite_transform ; transform to big magic + + CMP.b #$B5 : BNE + + LDA.b #$79 : JSL.l OWBonkSpritePrep + JSL.l GoldBee_SpawnSelf_SetProperties ; transform to good bee + BRA .mark_collected + + CMP.b #$44 : BNE + + LDA.b #$E2 : BRA .sprite_transform ; transform to 10 arrows + + CMP.b #$B1 : BNE + + LDA.b #$AC : BRA .sprite_transform ; transform to apples + + CMP.b #$B2 : BNE + + LDA.b #$E3 : BRA .sprite_transform ; transform to fairy + + CMP.b #$B3 : BNE .spawn_item + INX : INX : LDA.l OWBonkPrizeData+(42*6+5) + CLC : ADC.b #$08 : PHA + LDA.w $0D00,Y : SEC : SBC.b 1,S : STA.w $0D00,Y + LDA.w $0D20,Y : SBC.b #$00 : STA.w $0D20,Y : PLX + LDA.b #$0B : SEC ; BRA .sprite_transform ; transform to chicken + + .sprite_transform + JSL.l OWBonkSpritePrep + + .mark_collected ; S = Collected + PLA : BNE .return + LDA.l RoomDataWRAM[$0120].high : ORA.b #$02 : STA.l RoomDataWRAM[$0120].high + + REP #$20 + LDA.l TotalItemCounter : INC : STA.l TotalItemCounter + SEP #$20 + BRA .return + + ; spawn itemget item + .spawn_item ; A = item id ; Y = bonk sprite slot ; S = Collected + PLX : BEQ + : LDA.b #$00 : STA.w $0DD0,Y : BRA .return + + LDA.l OWBonkPrizeData+(42*6+4) : STA.l !MULTIWORLD_SPRITEITEM_PLAYER_ID + + LDA.b #$01 : STA !REDRAW + + LDA.b #$EB : STA.l $7FFE00 + JSL Sprite_SpawnDynamically+15 ; +15 to skip finding a new slot, use existing sprite + + ; affects the rate the item moves in the Y/X direction + LDA.b #$00 : STA.w $0D40,Y + LDA.b #$0A : STA.w $0D50,Y + + LDA.b #$20 : STA.w $0F80,Y ; amount of force (gives height to the arch) + LDA.b #$FF : STA.w $0B58,Y ; stun timer + LDA.b #$30 : STA.w $0F10,Y ; aux delay timer 4 ?? dunno what that means + + LDA.b #$00 : STA.w $0F20,Y ; layer the sprite is on + + ; sets OW event bitmask flag, uses free RAM + LDA.l OWBonkPrizeData+(42*6+2) : STA.w $0ED0,Y + + ; determines the initial spawn point of item + LDA.w $0D00,Y : SEC : SBC.l OWBonkPrizeData+(42*6+5) : STA.w $0D00,Y + LDA.w $0D20,Y : SBC #$00 : STA.w $0D20,Y + + LDA.b #$01 : STA !REDRAW : STA !FORCE_HEART_SPAWN + + .return + PLY + LDA #$08 ; makes original good bee not spawn + RTL + nop #20 +} + ; Y = sprite slot index of bonk sprite OWBonkDrops: { @@ -439,7 +553,7 @@ OWBonkDrops: + CMP.b #$34 : BNE + LDA.b #$D9 : CLC : JMP .sprite_transform ; transform to single rupee + CMP.b #$35 : BNE + - LDA.b #$DA : CLC : BRA .sprite_transform ; transform to blue rupee + LDA.b #$DA : CLC : JMP .sprite_transform ; transform to blue rupee + CMP.b #$36 : BNE + LDA.b #$DB : CLC : BRA .sprite_transform ; transform to red rupee + CMP.b #$27 : BNE + @@ -453,7 +567,9 @@ OWBonkDrops: + CMP.b #$B4 : BNE + LDA.b #$E0 : CLC : BRA .sprite_transform ; transform to big magic + CMP.b #$B5 : BNE + - LDA.b #$E1 : CLC : BRA .sprite_transform ; transform to 5 arrows + LDA.b #$79 : JSL.l OWBonkSpritePrep + JSL.l GoldBee_SpawnSelf_SetProperties ; transform to good bee + BRA .mark_collected + CMP.b #$44 : BNE + LDA.b #$E2 : CLC : BRA .sprite_transform ; transform to 10 arrows + CMP.b #$B1 : BNE + @@ -468,14 +584,7 @@ OWBonkDrops: LDA.b #$0B : SEC ; BRA .sprite_transform ; transform to chicken .sprite_transform - STA.w $0E20,Y - TYX : JSL.l Sprite_LoadProperties - BEQ + - ; these are sprite properties that make it fall out of the tree to the east - LDA #$30 : STA $0F80,Y ; amount of force (related to speed) - LDA #$10 : STA $0D50,Y ; eastward rate of speed - LDA #$FF : STA $0B58,Y ; expiration timer - + + JSL.l OWBonkSpritePrep .mark_collected ; S = Collected, FlagBitmask, X (row + 2) PLA : BNE + ; S = FlagBitmask, X (row + 2) @@ -495,8 +604,7 @@ OWBonkDrops: LDA.b #$01 : STA !REDRAW - LDA.b #$EB - STA.l $7FFE00 + LDA.b #$EB : STA.l $7FFE00 JSL Sprite_SpawnDynamically+15 ; +15 to skip finding a new slot, use existing sprite ; affects the rate the item moves in the Y/X direction @@ -525,6 +633,19 @@ OWBonkDrops: PLA : PLA : PLB : RTL } +; A = SpriteID, Y = Sprite Slot Index, X = free/overwritten +OWBonkSpritePrep: +{ + STA.w $0E20,Y + TYX : JSL.l Sprite_LoadProperties + BEQ + + ; these are sprite properties that make it fall out of the tree to the east + LDA #$30 : STA $0F80,Y ; amount of force (related to speed) + LDA #$10 : STA $0D50,Y ; eastward rate of speed + LDA #$FF : STA $0B58,Y ; expiration timer + + RTL +} + org $aa9000 OWDetectEdgeTransition: { @@ -1539,6 +1660,7 @@ db $6e, $8c, $10, $35, $00, $10 db $6e, $90, $08, $b0, $00, $10 db $6e, $a4, $04, $b1, $00, $10 db $74, $4e, $10, $b1, $00, $1c +db $ff, $00, $02, $b5, $00, $08 ; temporary fix - murahdahla replaces one of the bonk tree prizes ; so we copy the sprite table here and update the pointer diff --git a/data/base2current.bps b/data/base2current.bps index ba53c4d4..f12897a4 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/docs/customizer_example.yaml b/docs/customizer_example.yaml index 188c17d6..cd70004c 100644 --- a/docs/customizer_example.yaml +++ b/docs/customizer_example.yaml @@ -17,6 +17,13 @@ settings: shopsanity: true shuffle: crossed shufflelinks: true + ow_shuffle: parallel + ow_terrain: true + ow_crossed: grouped + ow_keepsimilar: true + ow_mixed: true + ow_whirlpool: true + ow_fluteshuffle: balanced shufflebosses: unique item_pool: 1: diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 8108712e..6d297347 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -72,7 +72,13 @@ class CustomSettings(object): args.mystery = True else: settings = defaultdict(lambda: None, player_setting) + args.ow_shuffle[p] = get_setting(settings['ow_shuffle'], args.ow_shuffle[p]) + args.ow_terrain[p] = get_setting(settings['ow_terrain'], args.ow_terrain[p]) + args.ow_crossed[p] = get_setting(settings['ow_crossed'], args.ow_crossed[p]) + args.ow_keepsimilar[p] = get_setting(settings['ow_keepsimilar'], args.ow_keepsimilar[p]) args.ow_mixed[p] = get_setting(settings['ow_mixed'], args.ow_mixed[p]) + args.ow_whirlpool[p] = get_setting(settings['ow_whirlpool'], args.ow_whirlpool[p]) + args.ow_fluteshuffle[p] = get_setting(settings['ow_fluteshuffle'], args.ow_fluteshuffle[p]) args.shuffle[p] = get_setting(settings['shuffle'], args.shuffle[p]) args.door_shuffle[p] = get_setting(settings['door_shuffle'], args.door_shuffle[p]) args.logic[p] = get_setting(settings['logic'], args.logic[p]) @@ -130,8 +136,8 @@ class CustomSettings(object): args.mapshuffle[p] = True args.compassshuffle[p] = True - args.shufflebosses[p] = get_setting(settings['shufflebosses'], args.shufflebosses[p]) - args.shuffleenemies[p] = get_setting(settings['shuffleenemies'], args.shuffleenemies[p]) + args.shufflebosses[p] = get_setting(settings['boss_shuffle'], args.shufflebosses[p]) + args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p]) args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p]) args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p]) args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p]) diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 8364ad58..1ed455b3 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -426,7 +426,8 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion return filtered if len(filtered) > 0 else locations if world.algorithm == 'district': config = world.item_pool_config - if item_to_place == 'Placeholder' or item_to_place.name in config.item_pool[item_to_place.player]: + if ((isinstance(item_to_place,str) and item_to_place == 'Placeholder') + or item_to_place.name in config.item_pool[item_to_place.player]): restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name in restricted and l.player in restricted[l.name]] return filtered if len(filtered) > 0 else locations @@ -820,7 +821,7 @@ trash_items = { 'Nothing': -1, 'Bee Trap': 0, 'Rupee (1)': 1, 'Rupees (5)': 1, 'Small Heart': 1, 'Bee': 1, 'Arrows (5)': 1, 'Chicken': 1, 'Single Bomb': 1, - 'Rupees (20)': 2, 'Small Magic': 2, + 'Rupees (20)': 2, 'Small Magic': 2, 'Good Bee': 2, 'Bombs (3)': 3, 'Arrows (10)': 3, 'Bombs (10)': 3, 'Apples': 3, 'Fairy': 4, 'Big Magic': 4, 'Red Potion': 4, 'Blue Shield': 4, 'Rupees (50)': 4, 'Rupees (100)': 4, 'Rupees (300)': 5, diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 349af41c..3515fa47 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -444,17 +444,19 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe random.shuffle(hole_entrances) if not cross_world and 'Sanctuary Grave' in holes_to_shuffle: hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player) + chosen_entrance = None if hc.connected_region and hc.connected_region.type == RegionType.DarkWorld: chosen_entrance = next(entrance for entrance in hole_entrances if entrance[0] in DW_Entrances) if not chosen_entrance: chosen_entrance = next(entrance for entrance in hole_entrances if entrance[0] in LW_Entrances) - hole_entrances.remove(chosen_entrance) - sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') - hole_targets.remove(sanc_interior) - connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit - connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole - remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]]) - remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) + if chosen_entrance: + hole_entrances.remove(chosen_entrance) + sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') + hole_targets.remove(sanc_interior) + connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit + connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole + remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]]) + remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) random.shuffle(hole_targets) for entrance, drop in hole_entrances: @@ -1132,6 +1134,9 @@ def do_vanilla_connect(pool_def, avail): elif pool_def['condition'] == 'takeany': if avail.world.take_any[avail.player] == 'fixed': return + elif pool_def['condition'] == 'bonk': + if avail.world.shuffle_bonk_drops[avail.player]: + return defaults = {**default_connections, **(inverted_default_connections if avail.inverted != avail.world.is_tile_swapped(0x1b, avail.player) else open_default_connections)} for entrance in pool_def['entrances']: if entrance in avail.entrances: @@ -1548,7 +1553,7 @@ modes = { 'condition': '', 'entrances': ['Dark Desert Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', - 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', 'Long Fairy Cave', + 'East Dark World Hint', 'Kakariko Gamble Game', 'Long Fairy Cave', 'Bush Covered House', 'Fortune Teller (Light)', 'Lost Woods Gamble', 'Lake Hylia Fortune Teller', 'Lake Hylia Fairy', 'Bonk Fairy (Light)', 'Inverted Dark Sanctuary'], }, @@ -1572,7 +1577,11 @@ modes = { 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Desert Hint'] - + }, + 'fixed_bonk': { + 'special': 'vanilla', + 'condition': 'bonk', + 'entrances': ['Good Bee Cave'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps 'entrances': ['Mimic Cave', 'Spike Cave', 'Mire Shed', 'Dark World Hammer Peg Cave', 'Chest Game', @@ -1580,7 +1589,7 @@ modes = { 'Ice Rod Cave', 'Dam', 'Bonk Rock Cave', 'Library', 'Potion Shop', 'Mini Moldorm Cave', 'Checkerboard Cave', 'Graveyard Cave', 'Cave 45', 'Sick Kids House', 'Blacksmiths Hut', 'Sahasrahlas Hut', 'Aginahs Cave', 'Chicken House', 'Kings Grave', 'Blinds Hideout', - 'Waterfall of Wishing', 'Cave Shop (Dark Death Mountain)', + 'Waterfall of Wishing', 'Cave Shop (Dark Death Mountain)', 'Good Bee Cave', 'Dark World Potion Shop', 'Dark World Lumberjack Shop', 'Dark World Shop', 'Red Shield Shop', 'Kakariko Shop', 'Capacity Upgrade', 'Cave Shop (Lake Hylia)', 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', @@ -1635,7 +1644,7 @@ modes = { 'condition': '', 'entrances': ['Dark Desert Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', - 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', 'Long Fairy Cave', + 'East Dark World Hint', 'Kakariko Gamble Game', 'Long Fairy Cave', 'Bush Covered House', 'Fortune Teller (Light)', 'Lost Woods Gamble', 'Lake Hylia Fortune Teller', 'Lake Hylia Fairy', 'Bonk Fairy (Light)', 'Inverted Dark Sanctuary'], }, @@ -1659,7 +1668,11 @@ modes = { 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Desert Hint'] - + }, + 'fixed_bonk': { + 'special': 'vanilla', + 'condition': 'bonk', + 'entrances': ['Good Bee Cave'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps 'entrances': ['Mimic Cave', 'Spike Cave', 'Mire Shed', 'Dark World Hammer Peg Cave', 'Chest Game', @@ -1667,7 +1680,7 @@ modes = { 'Ice Rod Cave', 'Dam', 'Bonk Rock Cave', 'Library', 'Potion Shop', 'Mini Moldorm Cave', 'Checkerboard Cave', 'Graveyard Cave', 'Cave 45', 'Sick Kids House', 'Blacksmiths Hut', 'Sahasrahlas Hut', 'Aginahs Cave', 'Chicken House', 'Kings Grave', 'Blinds Hideout', - 'Waterfall of Wishing', 'Cave Shop (Dark Death Mountain)', + 'Waterfall of Wishing', 'Cave Shop (Dark Death Mountain)', 'Good Bee Cave', 'Dark World Potion Shop', 'Dark World Lumberjack Shop', 'Dark World Shop', 'Red Shield Shop', 'Kakariko Shop', 'Capacity Upgrade', 'Cave Shop (Lake Hylia)', 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)',