Replaced bomb/rupee logic with pseudo locations/items
This commit is contained in:
157
BaseClasses.py
157
BaseClasses.py
@@ -1087,150 +1087,14 @@ class CollectionState(object):
|
|||||||
return self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))
|
return self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))
|
||||||
|
|
||||||
def can_farm_rupees(self, player):
|
def can_farm_rupees(self, player):
|
||||||
tree_pulls = ['Lost Woods East Area',
|
return self.has('Farmable Rupees', player)
|
||||||
'Snitch Lady (East)',
|
|
||||||
'Turtle Rock Area',
|
|
||||||
'Pyramid Area',
|
|
||||||
'Hype Cave Area',
|
|
||||||
'Dark South Pass Area',
|
|
||||||
'Bumper Cave Area']
|
|
||||||
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
|
|
||||||
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace Area']
|
|
||||||
|
|
||||||
rupee_farms = ['Archery Game', '50 Rupee Cave', '20 Rupee Cave']
|
|
||||||
|
|
||||||
bush_crabs = ['Lost Woods East Area', 'Mountain Entry Area']
|
|
||||||
pre_aga_bush_crabs = ['Lumberjack Area', 'South Pass Area']
|
|
||||||
rock_crabs = ['Desert Pass Area']
|
|
||||||
|
|
||||||
def can_reach_non_bunny(regionname):
|
|
||||||
region = self.world.get_region(regionname, player)
|
|
||||||
return region.can_reach(self) and ((self.world.mode[player] != 'inverted' and region.is_light_world) or (self.world.mode[player] == 'inverted' and region.is_dark_world) or self.has_Pearl(player))
|
|
||||||
|
|
||||||
for region in rupee_farms if self.world.pottery[player] in ['none', 'keys', 'dungeon'] else ['Archery Game']:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# tree pulls
|
|
||||||
if self.can_kill_most_things(player) and any(i in [0xda, 0xdb] for i in self.world.prizes[player]['pull']):
|
|
||||||
for region in tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if not self.has_beaten_aga(player):
|
|
||||||
for region in pre_aga_tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
for region in post_aga_tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# bush crabs (final item isn't considered)
|
|
||||||
if self.world.enemy_shuffle[player] == 'none':
|
|
||||||
if self.world.prizes[player]['crab'][0] in [0xda, 0xdb]:
|
|
||||||
for region in bush_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if not self.has_beaten_aga(player):
|
|
||||||
for region in pre_aga_bush_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if self.can_lift_rocks(player) and self.world.prizes[player]['crab'][0] in [0xda, 0xdb]:
|
|
||||||
for region in rock_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def can_farm_bombs(self, player):
|
def can_farm_bombs(self, player):
|
||||||
if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player):
|
if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
bush_bombs = ['Flute Boy Approach Area',
|
if self.has('Farmable Bombs', player):
|
||||||
'Kakariko Area',
|
return True
|
||||||
'Village of Outcasts Area',
|
|
||||||
'Forgotten Forest Area',
|
|
||||||
'Bat Cave Ledge',
|
|
||||||
'East Dark Death Mountain (Bottom)']
|
|
||||||
rock_bombs = ['Links House Area',
|
|
||||||
'Dark Chapel Area',
|
|
||||||
'Wooden Bridge Area',
|
|
||||||
'Ice Cave Area',
|
|
||||||
'Eastern Nook Area',
|
|
||||||
'West Death Mountain (Bottom)',
|
|
||||||
'Kakariko Fortune Area',
|
|
||||||
'Skull Woods Forest',
|
|
||||||
'Catfish Area',
|
|
||||||
'Dark Fortune Area',
|
|
||||||
'Qirn Jump Area',
|
|
||||||
'Shield Shop Area',
|
|
||||||
'Palace of Darkness Nook Area',
|
|
||||||
'Swamp Nook Area',
|
|
||||||
'Dark South Pass Area']
|
|
||||||
bonk_bombs = ['Kakariko Fortune Area', 'Dark Graveyard Area'] #TODO: Flute Boy Approach Area and Bonk Rock Ledge are available post-Aga
|
|
||||||
bomb_caves = ['Graveyard Cave', 'Light World Bomb Hut']
|
|
||||||
|
|
||||||
tree_pulls = ['Lost Woods East Area',
|
|
||||||
'Snitch Lady (East)',
|
|
||||||
'Turtle Rock Area',
|
|
||||||
'Pyramid Area',
|
|
||||||
'Hype Cave Area',
|
|
||||||
'Dark South Pass Area',
|
|
||||||
'Bumper Cave Area']
|
|
||||||
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
|
|
||||||
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace Area']
|
|
||||||
|
|
||||||
bush_crabs = ['Lost Woods East Area', 'Mountain Entry Area']
|
|
||||||
pre_aga_bush_crabs = ['Lumberjack Area', 'South Pass Area']
|
|
||||||
rock_crabs = ['Desert Pass Area']
|
|
||||||
|
|
||||||
def can_reach_non_bunny(regionname):
|
|
||||||
region = self.world.get_region(regionname, player)
|
|
||||||
return region.can_reach(self) and ((self.world.mode[player] != 'inverted' and region.is_light_world) or (self.world.mode[player] == 'inverted' and region.is_dark_world) or self.has_Pearl(player))
|
|
||||||
|
|
||||||
# bomb pickups
|
|
||||||
for region in bush_bombs + (bomb_caves if self.world.pottery[player] in ['none', 'keys', 'dungeon'] else []):
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
if self.can_lift_rocks(player):
|
|
||||||
for region in rock_bombs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
if not self.world.shuffle_bonk_drops[player] and self.can_collect_bonkdrops(player):
|
|
||||||
for region in bonk_bombs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# tree pulls
|
|
||||||
if self.can_kill_most_things(player) and any(i in [0xdc, 0xdd, 0xde] for i in self.world.prizes[player]['pull']):
|
|
||||||
for region in tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if not self.has_beaten_aga(player):
|
|
||||||
for region in pre_aga_tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
for region in post_aga_tree_pulls:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# bush crabs (final item isn't considered)
|
|
||||||
if self.world.enemy_shuffle[player] == 'none':
|
|
||||||
if self.world.prizes[player]['crab'][0] in [0xdc, 0xdd, 0xde]:
|
|
||||||
for region in bush_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if not self.has_beaten_aga(player):
|
|
||||||
for region in pre_aga_bush_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
if self.can_lift_rocks(player) and self.world.prizes[player]['crab'][0] in [0xdc, 0xdd, 0xde]:
|
|
||||||
for region in rock_crabs:
|
|
||||||
if can_reach_non_bunny(region):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# stun prize
|
# stun prize
|
||||||
if self.can_stun_enemies(player) and self.world.prizes[player]['stun'] in [0xdc, 0xdd, 0xde]:
|
if self.can_stun_enemies(player) and self.world.prizes[player]['stun'] in [0xdc, 0xdd, 0xde]:
|
||||||
@@ -1239,6 +1103,7 @@ class CollectionState(object):
|
|||||||
# bomb purchases
|
# bomb purchases
|
||||||
if self.can_farm_rupees(player) and (self.can_buy_unlimited('Bombs (10)', player) or self.can_reach('Big Bomb Shop', None, player)):
|
if self.can_farm_rupees(player) and (self.can_buy_unlimited('Bombs (10)', player) or self.can_reach('Big Bomb Shop', None, player)):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def item_count(self, item, player):
|
def item_count(self, item, player):
|
||||||
@@ -1391,7 +1256,7 @@ class CollectionState(object):
|
|||||||
if self.has_Pearl(player):
|
if self.has_Pearl(player):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return region.is_light_world if self.world.mode[player] != 'inverted' else region.is_dark_world
|
return not region.can_cause_bunny(player)
|
||||||
|
|
||||||
def can_reach_light_world(self, player):
|
def can_reach_light_world(self, player):
|
||||||
if True in [i.is_light_world for i in self.reachable_regions[player]]:
|
if True in [i.is_light_world for i in self.reachable_regions[player]]:
|
||||||
@@ -1635,6 +1500,12 @@ class Region(object):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def can_cause_bunny(self, player):
|
||||||
|
if 'Moon Pearl' in list(map(str, [i for i in self.world.precollected_items if i.player == player])):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.is_dark_world if self.world.mode[player] != 'inverted' else self.is_light_world
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
|
|
||||||
@@ -1830,6 +1701,9 @@ class Entrance(object):
|
|||||||
|
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
def can_cause_bunny(self, player):
|
||||||
|
return self.parent_region.can_cause_bunny(player)
|
||||||
|
|
||||||
def connect(self, region, addresses=None, target=None, vanilla=None):
|
def connect(self, region, addresses=None, target=None, vanilla=None):
|
||||||
self.connected_region = region
|
self.connected_region = region
|
||||||
self.target = target
|
self.target = target
|
||||||
@@ -2681,6 +2555,9 @@ class Location(object):
|
|||||||
name += f' ({world.get_player_names(self.player)})'
|
name += f' ({world.get_player_names(self.player)})'
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def can_cause_bunny(self, player):
|
||||||
|
return self.parent_region.can_cause_bunny(player)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
|
|
||||||
|
|||||||
105
ItemList.py
105
ItemList.py
@@ -505,6 +505,111 @@ def create_dynamic_shop_locations(world, player):
|
|||||||
loc.locked = True
|
loc.locked = True
|
||||||
|
|
||||||
|
|
||||||
|
def create_farm_locations(world, player):
|
||||||
|
bush_bombs = ['Flute Boy Approach Area',
|
||||||
|
'Kakariko Area',
|
||||||
|
'Village of Outcasts Area',
|
||||||
|
'Forgotten Forest Area',
|
||||||
|
'Bat Cave Ledge',
|
||||||
|
'East Dark Death Mountain (Bottom)']
|
||||||
|
rock_bombs = ['Links House Area',
|
||||||
|
'Dark Chapel Area',
|
||||||
|
'Wooden Bridge Area',
|
||||||
|
'Ice Cave Area',
|
||||||
|
'Eastern Nook Area',
|
||||||
|
'West Death Mountain (Bottom)',
|
||||||
|
'Kakariko Fortune Area',
|
||||||
|
'Skull Woods Forest',
|
||||||
|
'Catfish Area',
|
||||||
|
'Dark Fortune Area',
|
||||||
|
'Qirn Jump Area',
|
||||||
|
'Shield Shop Area',
|
||||||
|
'Palace of Darkness Nook Area',
|
||||||
|
'Swamp Nook Area',
|
||||||
|
'Dark South Pass Area']
|
||||||
|
bonk_bombs = ['Kakariko Fortune Area', 'Dark Graveyard Area'] #TODO: Flute Boy Approach Area and Bonk Rock Ledge are available post-Aga
|
||||||
|
bomb_caves = ['Graveyard Cave', 'Light World Bomb Hut']
|
||||||
|
|
||||||
|
rupee_caves = ['50 Rupee Cave', '20 Rupee Cave']
|
||||||
|
rupee_games = ['Archery Game']
|
||||||
|
|
||||||
|
tree_pulls = ['Lost Woods East Area',
|
||||||
|
'Snitch Lady (East)',
|
||||||
|
'Turtle Rock Area',
|
||||||
|
'Pyramid Area',
|
||||||
|
'Hype Cave Area',
|
||||||
|
'Dark South Pass Area',
|
||||||
|
'Bumper Cave Area']
|
||||||
|
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
|
||||||
|
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace Area']
|
||||||
|
|
||||||
|
bush_crabs = ['Lost Woods East Area', 'Mountain Entry Area']
|
||||||
|
pre_aga_bush_crabs = ['Lumberjack Area', 'South Pass Area']
|
||||||
|
rock_crabs = ['Desert Pass Area']
|
||||||
|
|
||||||
|
# NOTE: Altho pre-Aga locations cannot technically be guaranteed by the player, the
|
||||||
|
# goal here is just to ensure access to early rupees/bombs to get the player started,
|
||||||
|
# and hopefully access to more permanent farm locations
|
||||||
|
|
||||||
|
def create_and_fill_location(region_name, loc_description, item_name):
|
||||||
|
region = world.get_region(region_name, player)
|
||||||
|
loc = Location(player, f'{region_name} {loc_description}', 0, region)
|
||||||
|
loc.type = LocationType.Logical
|
||||||
|
loc.parent_region = region
|
||||||
|
loc.event = True
|
||||||
|
loc.locked = True
|
||||||
|
loc.address = None
|
||||||
|
|
||||||
|
world.push_item(loc, ItemFactory(item_name, player), False)
|
||||||
|
|
||||||
|
region.locations.append(loc)
|
||||||
|
world.dynamic_locations.append(loc)
|
||||||
|
|
||||||
|
return loc
|
||||||
|
|
||||||
|
from Rules import set_rule, add_rule, add_bunny_rule
|
||||||
|
for region in bush_bombs:
|
||||||
|
loc = create_and_fill_location(region, 'Bush Drop', 'Farmable Bombs')
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
for region in rock_bombs:
|
||||||
|
loc = create_and_fill_location(region, 'Rock Drop', 'Farmable Bombs')
|
||||||
|
set_rule(loc, lambda state: state.can_lift_rocks(player))
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
if not world.shuffle_bonk_drops[player]:
|
||||||
|
for region in bonk_bombs:
|
||||||
|
loc = create_and_fill_location(region, 'Bonk Drop', 'Farmable Bombs')
|
||||||
|
set_rule(loc, lambda state: state.can_collect_bonkdrops(player))
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
if world.pottery[player] in ['none', 'keys', 'dungeon']:
|
||||||
|
for region in bomb_caves + rupee_caves:
|
||||||
|
loc = create_and_fill_location(region, 'Pot Drop', 'Farmable Rupees' if region in rupee_caves else 'Farmable Bombs')
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
for region in rupee_games:
|
||||||
|
loc = create_and_fill_location(region, 'Prize', 'Farmable Rupees')
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
if any(i in [0xda, 0xdb, 0xdc, 0xdd, 0xde] for i in world.prizes[player]['pull']):
|
||||||
|
rupee_farm = any(i in [0xda, 0xdb] for i in world.prizes[player]['pull'])
|
||||||
|
for region in tree_pulls + pre_aga_tree_pulls + post_aga_tree_pulls:
|
||||||
|
loc = create_and_fill_location(region, 'Tree Pull', 'Farmable Rupees' if rupee_farm else 'Farmable Bombs')
|
||||||
|
set_rule(loc, lambda state: state.can_kill_most_things(player))
|
||||||
|
if region in pre_aga_tree_pulls:
|
||||||
|
add_rule(loc, lambda state: not state.has_beaten_aga(player))
|
||||||
|
elif region in post_aga_tree_pulls:
|
||||||
|
add_rule(loc, lambda state: state.has_beaten_aga(player))
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
if world.enemy_shuffle[player] == 'none' and any(i in [0xda, 0xdb, 0xdc, 0xdd, 0xde] for i in world.prizes[player]['crab']):
|
||||||
|
rupee_farm = any(i in [0xda, 0xdb] for i in world.prizes[player]['crab'])
|
||||||
|
for region in bush_crabs + pre_aga_bush_crabs + rock_crabs:
|
||||||
|
loc = create_and_fill_location(region, 'Crab Drop', 'Farmable Rupees' if rupee_farm else 'Farmable Bombs')
|
||||||
|
if region in pre_aga_bush_crabs:
|
||||||
|
set_rule(loc, lambda state: not state.has_beaten_aga(player))
|
||||||
|
elif region in rock_crabs:
|
||||||
|
set_rule(loc, lambda state: state.can_lift_rocks(player))
|
||||||
|
add_bunny_rule(loc, player)
|
||||||
|
|
||||||
|
world.clear_location_cache()
|
||||||
|
|
||||||
|
|
||||||
def create_dynamic_bonkdrop_locations(world, player):
|
def create_dynamic_bonkdrop_locations(world, player):
|
||||||
from Regions import bonk_prize_table
|
from Regions import bonk_prize_table
|
||||||
for bonk_location, (_, _, _, _, region_name, hint_text) in bonk_prize_table.items():
|
for bonk_location, (_, _, _, _, region_name, hint_text) in bonk_prize_table.items():
|
||||||
|
|||||||
2
Items.py
2
Items.py
@@ -198,4 +198,6 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
|
|||||||
'Hidden Pits': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
'Hidden Pits': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||||
'Zelda Herself': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
'Zelda Herself': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||||
'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||||
|
'Farmable Bombs': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||||
|
'Farmable Rupees': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||||
}
|
}
|
||||||
|
|||||||
9
Main.py
9
Main.py
@@ -26,7 +26,7 @@ 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, ensure_good_pots
|
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, set_prize_drops
|
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops
|
||||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, create_farm_locations
|
||||||
from Utils import output_path, parse_player_names
|
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
|
||||||
@@ -212,6 +212,10 @@ def main(args, seed=None, fish=None):
|
|||||||
mark_dark_world_regions(world, player)
|
mark_dark_world_regions(world, player)
|
||||||
logger.info(world.fish.translate("cli", "cli", "generating.itempool"))
|
logger.info(world.fish.translate("cli", "cli", "generating.itempool"))
|
||||||
|
|
||||||
|
for player in range(1, world.players + 1):
|
||||||
|
set_prize_drops(world, player)
|
||||||
|
create_farm_locations(world, player)
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
generate_itempool(world, player)
|
generate_itempool(world, player)
|
||||||
|
|
||||||
@@ -229,9 +233,6 @@ def main(args, seed=None, fish=None):
|
|||||||
else:
|
else:
|
||||||
lock_shop_locations(world, player)
|
lock_shop_locations(world, player)
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
|
||||||
set_prize_drops(world, player)
|
|
||||||
|
|
||||||
massage_item_pool(world)
|
massage_item_pool(world)
|
||||||
logger.info(world.fish.translate("cli", "cli", "placing.dungeon.prizes"))
|
logger.info(world.fish.translate("cli", "cli", "placing.dungeon.prizes"))
|
||||||
|
|
||||||
|
|||||||
3
Rules.py
3
Rules.py
@@ -135,7 +135,8 @@ def add_rule(spot, rule, combine='and'):
|
|||||||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||||
|
|
||||||
def add_bunny_rule(spot, player):
|
def add_bunny_rule(spot, player):
|
||||||
add_rule(spot, lambda state: state.is_not_bunny(spot.parent_region, player))
|
if spot.can_cause_bunny(player):
|
||||||
|
add_rule(spot, lambda state: state.has_Pearl(player))
|
||||||
|
|
||||||
|
|
||||||
def or_rule(rule1, rule2):
|
def or_rule(rule1, rule2):
|
||||||
|
|||||||
Reference in New Issue
Block a user