Bomblogic added

This commit is contained in:
StructuralMike
2021-07-23 20:35:48 +02:00
parent bfd5c5ceeb
commit 051a47e0cd
13 changed files with 54 additions and 26 deletions

View File

@@ -114,6 +114,7 @@ class World(object):
set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', False)
set_player_attr('bigkeyshuffle', False)
set_player_attr('bomblogic', False)
set_player_attr('difficulty_requirements', None)
set_player_attr('boss_shuffle', 'none')
set_player_attr('enemy_shuffle', 'none')
@@ -686,8 +687,7 @@ class CollectionState(object):
# In the future, this can be used to check if the player starts without bombs
def can_use_bombs(self, player):
StartingBombs = True
return StartingBombs or self.has('Bomb Upgrade (+10)', player)
return (not self.world.bomblogic[player] or self.has('Bomb Upgrade (+10)', player))
def can_hit_crystal(self, player):
return (self.can_use_bombs(player)
@@ -2013,6 +2013,7 @@ class Spoiler(object):
'logic': self.world.logic,
'mode': self.world.mode,
'retro': self.world.retro,
'bomblogic': self.world.bomblogic,
'weapons': self.world.swords,
'goal': self.world.goal,
'shuffle': self.world.shuffle,
@@ -2111,6 +2112,7 @@ class Spoiler(object):
outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No'))
outfile.write('Key Drops shuffled: %s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No'))
outfile.write(f"Shopsanity: {'Yes' if self.metadata['shopsanity'][player] else 'No'}\n")
outfile.write('Bomblogic: %s\n' % ('Yes' if self.metadata['bomblogic'][player] else 'No'))
if self.doors:
outfile.write('\n\nDoors:\n\n')
outfile.write('\n'.join(

2
CLI.py
View File

@@ -96,6 +96,7 @@ def parse_cli(argv, no_defaults=False):
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'bomblogic',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots',
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
@@ -126,6 +127,7 @@ def parse_settings():
settings = {
"lang": "en",
"retro": False,
"bomblogic": False,
"mode": "open",
"logic": "noglitches",
"goal": "ganon",

View File

@@ -37,7 +37,7 @@ Difficulty = namedtuple('Difficulty',
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
'basicshield', 'progressivearmor', 'basicarmor', 'swordless',
'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother',
'retro',
'retro', 'bomblogic',
'extras', 'progressive_sword_limit', 'progressive_shield_limit',
'progressive_armor_limit', 'progressive_bottle_limit',
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
@@ -61,6 +61,7 @@ difficulties = {
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
bomblogic = ['Bomb Upgrade (+10)'] * 2,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 4,
progressive_shield_limit = 3,
@@ -86,6 +87,7 @@ difficulties = {
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
bomblogic = ['Bomb Upgrade (+10)'] * 2,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 3,
progressive_shield_limit = 2,
@@ -111,6 +113,7 @@ difficulties = {
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
bomblogic = ['Bomb Upgrade (+10)'] * 2,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 2,
progressive_shield_limit = 1,
@@ -251,10 +254,10 @@ def generate_itempool(world, player):
# set up item pool
if world.custom:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray)
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bomblogic[player], world.customitemarray)
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player], world.logic[player])
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bomblogic[player], world.doorShuffle[player], world.logic[player])
if player in world.pool_adjustment.keys():
amt = world.pool_adjustment[player]
@@ -284,7 +287,7 @@ def generate_itempool(world, player):
if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
if item not in possible_weapons:
possible_weapons.append(item)
if item in ['Bombs (10)']:
if not world.bomblogic[player] and item in ['Bombs (10)']:
if item not in possible_weapons and world.doorShuffle[player] != 'crossed':
possible_weapons.append(item)
starting_weapon = random.choice(possible_weapons)
@@ -709,7 +712,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)'
'Rupees (100)': 100, 'Rupees (300)': 300}
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, door_shuffle, logic):
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bomblogic, door_shuffle, logic):
pool = []
placed_items = {}
precollected_items = []
@@ -756,6 +759,11 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
diff = difficulties[difficulty]
pool.extend(diff.baseitems)
if bomblogic:
pool = [item.replace('Bomb Upgrade (+5)','Rupees (5)') for item in pool]
pool = [item.replace('Bomb Upgrade (+10)','Rupees (5)') for item in pool]
pool.extend(diff.bomblogic)
# expert+ difficulties produce the same contents for
# all bottles, since only one bottle is available
if diff.same_bottle:
@@ -850,7 +858,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
pool.extend(['Small Key (Universal)'])
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray):
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bomblogic, customitemarray):
if isinstance(customitemarray,dict) and 1 in customitemarray:
customitemarray = customitemarray[1]
pool = []
@@ -966,8 +974,9 @@ def test():
for shuffle in ['full', 'insanity_legacy']:
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
for retro in [True, False]:
for bomblogic in [True, False]:
for door_shuffle in ['basic', 'crossed', 'vanilla']:
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, door_shuffle, logic)
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bomblogic, door_shuffle, logic)
count = len(out[0]) + len(out[1])
correct_count = total_items_to_place
@@ -977,7 +986,7 @@ def test():
if retro:
correct_count += 28
try:
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro))
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bomblogic))
except AssertionError as e:
print(e)

View File

@@ -81,7 +81,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'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'),
'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+10)': (True, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the blue mail'),
'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the red mail'),

View File

@@ -69,6 +69,7 @@ def main(args, seed=None, fish=None):
world.compassshuffle = args.compassshuffle.copy()
world.keyshuffle = args.keyshuffle.copy()
world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.bomblogic = args.bomblogic.copy()
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
world.crystals_ganon_orig = args.crystals_ganon.copy()
@@ -372,6 +373,7 @@ def copy_world(world):
ret.compassshuffle = world.compassshuffle.copy()
ret.keyshuffle = world.keyshuffle.copy()
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.bomblogic = world.bomblogic.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()

View File

@@ -176,6 +176,8 @@ def roll_settings(weights):
ret.retro = True
ret.retro = get_choice('retro') == 'on' # this overrides world_state if used
ret.bomblogic = get_choice('bomblogic') == 'on'
ret.hints = get_choice('hints') == 'on'
ret.swords = {'randomized': 'random',

7
Rom.py
View File

@@ -1032,7 +1032,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(0x184000, [
# original_item, limit, replacement_item, filler
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade
0x51, 0x00 if world.bomblogic[player] else 0x06, 0x31 if world.bomblogic[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bomblogic -> turns into Bombs (10)
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
0x58, 0x01, 0x36 if world.retro[player] else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode)
0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20
@@ -1169,6 +1169,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
equip[0x36C] = 0x18
equip[0x36D] = 0x18
equip[0x379] = 0x68
if world.bomblogic[player]:
starting_max_bombs = 0
else:
starting_max_bombs = 10
starting_max_arrows = 30
@@ -1461,7 +1464,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(0x180188, [0, 0, 10]) # Zelda respawn refills (magic, bombs, arrows)
rom.write_bytes(0x18018B, [0, 0, 10]) # Mantle respawn refills (magic, bombs, arrows)
bow_max, bow_small = 70, 10
elif uncle_location.item is not None and uncle_location.item.name in ['Bombs (10)']:
elif uncle_location.item is not None and uncle_location.item.name in ['Bomb Upgrade (+10)' if world.bomblogic[player] else 'Bombs (10)']:
rom.write_byte(0x18004E, 2) # Escape Fill (bombs)
rom.write_bytes(0x180185, [0, 50, 0]) # Uncle respawn refills (magic, bombs, arrows)
rom.write_bytes(0x180188, [0, 3, 0]) # Zelda respawn refills (magic, bombs, arrows)

View File

@@ -1160,7 +1160,7 @@ def standard_rules(world, player):
def bomb_escape_rule():
loc = world.get_location("Link's Uncle", player)
return loc.item and loc.item.name == 'Bombs (10)'
return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bomblogic[player] else 'Bombs (10)']
def standard_escape_rule(state):
return state.can_kill_most_things(player) or bomb_escape_rule()

View File

@@ -220,6 +220,10 @@
"type": "bool",
"help": "suppress"
},
"bomblogic": {
"action": "store_true",
"type": "bool"
},
"retro": {
"action": "store_true",
"type": "bool"

View File

@@ -263,6 +263,7 @@
"and a few other little things make this more like Zelda-1. (default: %(default)s)"
],
"pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"],
"bomblogic": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
"usestartinventory": [ "Toggle usage of Starting Inventory." ],
"custom": [ "Not supported." ],

View File

@@ -190,6 +190,7 @@
"randomizer.item.hints": "Include Helpful Hints",
"randomizer.item.retro": "Retro mode (universal keys)",
"randomizer.item.pseudoboots": "Start with Pseudo Boots",
"randomizer.item.bomblogic": "Bomblogic",
"randomizer.item.worldstate": "World State",
"randomizer.item.worldstate.standard": "Standard",

View File

@@ -1,6 +1,7 @@
{
"checkboxes": {
"retro": { "type": "checkbox" },
"bomblogic": { "type": "checkbox" },
"shopsanity": { "type": "checkbox" },
"hints": {
"type": "checkbox"

View File

@@ -57,6 +57,7 @@ SETTINGSTOPROCESS = {
"item": {
"hints": "hints",
"retro": "retro",
"bomblogic": "bomblogic",
"shopsanity": "shopsanity",
"pseudoboots": "pseudoboots",
"worldstate": "mode",