forked from mirror/Archipelago
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
573 lines
25 KiB
Python
573 lines
25 KiB
Python
# not entirely, but partially from https://github.com/icebound777/PMR-SeedGenerator/blob/main/rando_modules/logic.py
|
|
# follows examples in OoT's implementation
|
|
|
|
from collections import namedtuple
|
|
from itertools import chain
|
|
|
|
from .data.chapter_logic import get_bowser_castle_removed_locations, areas_by_chapter, \
|
|
get_locations_beyond_spirit_requirements
|
|
from .data.ItemList import taycet_items, item_table, progression_miscitems, item_groups, item_multiples_base_name
|
|
from .data.LocationsList import location_groups, location_table, missable_locations, dojo_location_order, ch8_locations
|
|
from .options import *
|
|
from .data.item_exclusion import exclude_due_to_settings, exclude_from_taycet_placement
|
|
from .modules.modify_itempool import get_randomized_itempool
|
|
from BaseClasses import ItemClassification as Ic, LocationProgressType
|
|
from .Locations import location_factory
|
|
from .data.chapter_logic import get_chapter_excluded_item_names, get_chapter_excluded_location_names
|
|
|
|
from typing import TYPE_CHECKING
|
|
if TYPE_CHECKING:
|
|
from . import PaperMarioWorld
|
|
|
|
|
|
def generate_itempool(pm_world):
|
|
world = pm_world.multiworld
|
|
player = pm_world.player
|
|
|
|
(pool, placed_items, placed_items_excluded) = get_pool_core(pm_world)
|
|
|
|
pm_world.itempool = [pm_world.create_item(item) for item in pool]
|
|
for (location_name, item) in placed_items.items():
|
|
location = world.get_location(location_name, player)
|
|
location.place_locked_item(pm_world.create_item(item, allow_arbitrary_name=True))
|
|
|
|
for (location_name, item) in placed_items_excluded.items():
|
|
location = location_factory(location_name, player)
|
|
location.place_locked_item(pm_world.create_item(item, allow_arbitrary_name=True))
|
|
pm_world.ch_excluded_locations.append(location)
|
|
|
|
|
|
def get_pool_core(world: "PaperMarioWorld"):
|
|
|
|
pool_misc_progression_items = []
|
|
pool_other_items = []
|
|
pool_progression_items = []
|
|
pool_coins_only = []
|
|
pool_illogical_consumables = []
|
|
pool_badges = []
|
|
pool = []
|
|
|
|
placed_items = {}
|
|
|
|
bc_removed_locations = []
|
|
|
|
# items and locations excluded from chapters for LCL get handled differently from normal excluded locations
|
|
ch_excluded_locations = []
|
|
ch_excluded_items = []
|
|
placed_items_excluded = {}
|
|
|
|
if world.options.spirit_requirements.value == SpiritRequirements.option_Specific_And_Limit_Chapter_Logic:
|
|
ch_excluded_locations = get_chapter_excluded_location_names(world.excluded_spirits,
|
|
world.options.letter_rewards.value)
|
|
ch_excluded_items = get_chapter_excluded_item_names(world.excluded_spirits)
|
|
|
|
# remove chapter 8 locations if star way is the goal
|
|
# otherwise remove any bowser castle locations removed by shortened or boss rush modes
|
|
if world.options.seed_goal.value == SeedGoal.option_Open_Star_Way:
|
|
ch_excluded_locations.extend(ch8_locations)
|
|
ch_excluded_items.extend(get_chapter_excluded_item_names([8]))
|
|
else:
|
|
bc_removed_locations = get_bowser_castle_removed_locations(world.options.bowser_castle_mode.value)
|
|
|
|
# Exclude locations that are either missable or are going to be considered not in logic based on settings
|
|
excluded_locations = missable_locations + get_locations_to_exclude(world, bc_removed_locations)
|
|
|
|
# remove unused items from the pool
|
|
|
|
for loc_name in location_table:
|
|
|
|
if loc_name not in ch_excluded_locations and loc_name not in bc_removed_locations:
|
|
location = world.get_location(loc_name)
|
|
else:
|
|
location = location_factory(loc_name, world.player)
|
|
|
|
if location.vanilla_item is None:
|
|
continue
|
|
|
|
item = location.vanilla_item
|
|
itemdata = item_table[item]
|
|
shuffle_item = True
|
|
|
|
# Excluded locations
|
|
if location.name in excluded_locations:
|
|
location.progress_type = LocationProgressType.EXCLUDED
|
|
|
|
# Sometimes placed items
|
|
|
|
if location.name in location_groups["OverworldCoin"]:
|
|
shuffle_item = world.options.overworld_coins.value
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["BlockCoin"]:
|
|
shuffle_item = world.options.coin_blocks.value
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["FoliageCoin"]:
|
|
shuffle_item = world.options.foliage_coins.value
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["ShopItem"]:
|
|
|
|
if location.identifier in ["DRO_01/ShopItemB", "DRO_01/ShopItemD", "DRO_01/ShopItemE"]:
|
|
shuffle_item = (world.options.random_puzzles.value and world.options.include_shops.value
|
|
and not (world.options.spirit_requirements.value ==
|
|
SpiritRequirements.option_Specific_And_Limit_Chapter_Logic and
|
|
2 in world.excluded_spirits))
|
|
else:
|
|
shuffle_item = world.options.include_shops.value
|
|
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["HiddenPanel"]:
|
|
shuffle_item = world.options.shuffle_hidden_panels.value
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["FavorReward"]:
|
|
# coins get shuffled only if other rewards are also shuffled
|
|
if location.name in location_groups["FavorCoin"]:
|
|
shuffle_item = (world.options.koot_coins.value and
|
|
(world.options.koot_favors.value != ShuffleKootFavors.option_Vanilla))
|
|
else:
|
|
shuffle_item = (world.options.koot_favors.value != ShuffleKootFavors.option_Vanilla)
|
|
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["FavorItem"]:
|
|
shuffle_item = (world.options.koot_favors.value == ShuffleKootFavors.option_Full_Shuffle)
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["LetterReward"]:
|
|
if location.name == "GR Goomba Village Goompapa Letter Reward 2":
|
|
shuffle_item = (world.options.letter_rewards.value in [ShuffleLetters.option_Final_Letter_Chain_Reward,
|
|
ShuffleLetters.option_Full_Shuffle])
|
|
elif location.name in location_groups["LetterChain"]:
|
|
shuffle_item = (world.options.letter_rewards.value == ShuffleLetters.option_Full_Shuffle)
|
|
|
|
else:
|
|
shuffle_item = (world.options.letter_rewards.value != ShuffleLetters.option_Vanilla)
|
|
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["RadioTradeEvent"]:
|
|
shuffle_item = world.options.trading_events.value
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in location_groups["DojoReward"]:
|
|
|
|
reward_num = dojo_location_order.index(location.name)
|
|
shuffle_item = (reward_num < world.options.dojo.value)
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
|
|
if location.vanilla_item == "Forest Pass":
|
|
shuffle_item = (not world.options.open_forest.value)
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
|
|
if location.name in location_groups["Partner"]:
|
|
shuffle_item = (world.options.partners.value != ShufflePartners.option_Off)
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
|
|
if location.name in location_groups["MultiCoinBlock"]:
|
|
shuffle_item = (world.options.super_multi_blocks.value > ShuffleSuperMultiBlocks.option_Off)
|
|
|
|
if location.name in location_groups["SuperBlock"]:
|
|
shuffle_item = world.options.partner_upgrades.value > PartnerUpgradeShuffle.option_Vanilla
|
|
|
|
if location.name in location_groups["Gear"]:
|
|
# hammer 1 bush is special in that it is made to not be empty even if starting with hammer
|
|
if location.name == "GR Jr. Troopa's Playground In Hammer Bush":
|
|
shuffle_item = ((world.options.gear_shuffle_mode.value != GearShuffleMode.option_Vanilla) or
|
|
(world.options.starting_hammer.value == StartingHammer.option_Hammerless))
|
|
else:
|
|
shuffle_item = (world.options.gear_shuffle_mode.value != GearShuffleMode.option_Vanilla)
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
|
|
if location.name == "SSS Star Sanctuary Gift of the Stars":
|
|
shuffle_item = world.options.shuffle_star_beam.value
|
|
|
|
if not shuffle_item:
|
|
location.disabled = True
|
|
location.show_in_spoiler = False
|
|
|
|
if location.name in ch_excluded_locations and item in ch_excluded_items:
|
|
shuffle_item = False
|
|
|
|
# add it to the proper pool, or place the item
|
|
if shuffle_item:
|
|
|
|
# hammer bush gets shuffled as a Tayce T item if shuffling gear locations and not hammerless
|
|
if (location.name == "GR Jr. Troopa's Playground In Hammer Bush" and
|
|
(world.options.gear_shuffle_mode.value == GearShuffleMode.option_Gear_Location_Shuffle) and
|
|
(world.options.starting_hammer.value != StartingHammer.option_Hammerless)):
|
|
pool_progression_items.append(world.random.choice([x for x in taycet_items
|
|
if x not in exclude_from_taycet_placement]))
|
|
|
|
# some progression items need to be in replenishable locations, we only need one of each
|
|
elif item in progression_miscitems:
|
|
if item not in pool_misc_progression_items:
|
|
pool_misc_progression_items.append(item)
|
|
else:
|
|
pool_illogical_consumables.append(item)
|
|
|
|
# progression items are shuffled; include gear and star pieces from rip cheato
|
|
elif (itemdata[1] == Ic.progression or
|
|
(location.name in location_groups["ShopItem"] and
|
|
world.options.include_shops.value and "Star Piece" in item)) and item not in ch_excluded_items:
|
|
pool_progression_items.append(item)
|
|
|
|
# split other items into their own pools; these other pools get modified before being sent elsewhere
|
|
elif itemdata[0] == "COIN":
|
|
pool_coins_only.append(item)
|
|
elif itemdata[0] == "ITEM":
|
|
pool_illogical_consumables.append(item)
|
|
elif itemdata[0] == "BADGE":
|
|
pool_badges.append(item)
|
|
else:
|
|
pool_other_items.append(item)
|
|
elif loc_name in ch_excluded_locations or loc_name in bc_removed_locations:
|
|
# keep out of logic placed items separate, remove the location and item from remaining excluded lists
|
|
placed_items_excluded[location.name] = item
|
|
|
|
# remove locations with placed items from the respective lists so we can get the item pool count correct
|
|
if location.name in bc_removed_locations:
|
|
bc_removed_locations.remove(location.name)
|
|
|
|
if location.name in ch_excluded_locations:
|
|
ch_excluded_locations.remove(location.name)
|
|
|
|
# below only applies to key items in the chapter
|
|
if item in ch_excluded_items:
|
|
ch_excluded_items.remove(item)
|
|
|
|
else:
|
|
placed_items[location.name] = item
|
|
|
|
# end of location for loop
|
|
|
|
# at this point every location's item should be either left unshuffled or added to a pool
|
|
# we want to modify these pools according to settings and make sure to have the right number of items
|
|
|
|
target_itempool_size = (
|
|
len(pool_progression_items)
|
|
+ len(pool_misc_progression_items)
|
|
+ len(pool_coins_only)
|
|
+ len(pool_illogical_consumables)
|
|
+ len(pool_badges)
|
|
+ len(pool_other_items)
|
|
- len(bc_removed_locations)
|
|
)
|
|
|
|
# add power stars
|
|
if world.options.power_star_hunt.value and world.options.total_power_stars.value > 0:
|
|
for i in range(0, world.options.total_power_stars.value):
|
|
pool_progression_items.append("Power Star")
|
|
|
|
# add 5 item pouches
|
|
if world.options.item_pouches.value:
|
|
for i in range(0, 5):
|
|
pool_other_items.append("Pouch Upgrade")
|
|
|
|
# add beta items
|
|
if world.options.beta_items.value:
|
|
pool_other_items.extend(item_groups["ItemBeta"])
|
|
for badge in item_groups["BadgeBeta"]:
|
|
pool_badges.append(get_item_multiples_base_name(badge))
|
|
|
|
# add unused badge dupes
|
|
if world.options.unused_badge_dupes.value:
|
|
for badge in item_groups["BadgeDupe"]:
|
|
if not (world.options.beta_items.value and badge in item_groups["BadgeBeta"]):
|
|
pool_badges.append(get_item_multiples_base_name(badge))
|
|
|
|
# add progressive badges
|
|
if world.options.progressive_badges.value:
|
|
# 3 copies of each progressive badge
|
|
for name in item_groups["ProgBadge"]:
|
|
for i in range(0, 3):
|
|
pool_badges.append(name)
|
|
|
|
# add normal boots
|
|
if world.options.starting_boots.value == StartingBoots.option_Jumpless:
|
|
pool_progression_items.append("Progressive Boots")
|
|
|
|
# add two of each partner upgrade item, remove the generic ones
|
|
if world.options.partner_upgrades.value != PartnerUpgradeShuffle.option_Vanilla:
|
|
for i in range(0, 2):
|
|
for upgrade in item_groups["PartnerUpgrade"]:
|
|
if upgrade == "Partner Upgrade":
|
|
for _ in range(0, 8):
|
|
pool_other_items.remove(upgrade)
|
|
else:
|
|
pool_other_items.append(upgrade)
|
|
|
|
# add traps
|
|
max_traps = 0
|
|
match world.options.item_traps.value:
|
|
case ItemTraps.option_Sparse:
|
|
max_traps = 15
|
|
case ItemTraps.option_Moderate:
|
|
max_traps = 35
|
|
case ItemTraps.option_Plenty:
|
|
max_traps = 80
|
|
case _:
|
|
max_traps = 0
|
|
|
|
pool_other_items.extend(["Damage Trap"] * max_traps)
|
|
|
|
# adjust item pools based on settings
|
|
items_to_remove_from_pools = get_items_to_exclude(world)
|
|
|
|
while items_to_remove_from_pools:
|
|
item = items_to_remove_from_pools.pop()
|
|
if item in pool_progression_items:
|
|
pool_progression_items.remove(item)
|
|
continue
|
|
if item in pool_misc_progression_items:
|
|
pool_misc_progression_items.remove(item)
|
|
continue
|
|
if item in pool_badges:
|
|
pool_badges.remove(item)
|
|
continue
|
|
if item in pool_other_items:
|
|
pool_other_items.remove(item)
|
|
continue
|
|
|
|
# If we have set a badge pool limit and exceed that, remove random badges
|
|
# until that condition is satisfied
|
|
if len(pool_badges) > world.options.badge_pool_limit.value:
|
|
world.random.shuffle(pool_badges)
|
|
while len(pool_badges) > world.options.badge_pool_limit.value:
|
|
pool_badges.pop()
|
|
|
|
# If the item pool is the wrong size now, fix it by filling up or clearing out items
|
|
cur_itempool_size = (
|
|
len(pool_progression_items)
|
|
+ len(pool_misc_progression_items)
|
|
+ len(pool_coins_only)
|
|
+ len(pool_illogical_consumables)
|
|
+ len(pool_badges)
|
|
+ len(pool_other_items)
|
|
)
|
|
|
|
# add random tayce t items if we need to add items for some reason
|
|
while target_itempool_size > cur_itempool_size:
|
|
pool_illogical_consumables.append(world.random.choice([x for x in taycet_items
|
|
if x not in exclude_from_taycet_placement]))
|
|
cur_itempool_size += 1
|
|
|
|
# remove coins first, then consumables if we need to keep going
|
|
if target_itempool_size < cur_itempool_size:
|
|
world.random.shuffle(pool_illogical_consumables)
|
|
while target_itempool_size < cur_itempool_size:
|
|
if len(pool_coins_only) > 20 or len(pool_illogical_consumables) == 0:
|
|
trashable_items = pool_coins_only
|
|
else:
|
|
trashable_items = pool_illogical_consumables
|
|
if trashable_items:
|
|
trashable_items.pop()
|
|
cur_itempool_size -= 1
|
|
else:
|
|
raise ValueError(f"Paper Mario: {world.player} ({world.multiworld.player_name[world.player]}) has too "
|
|
f"large of an item pool for the number of locations; consider increasing the number "
|
|
f"of checks available or reducing the badge or power star pools.")
|
|
|
|
# Re-join the non-required items into one array
|
|
pool_other_items.extend(pool_coins_only)
|
|
pool_other_items.extend(pool_illogical_consumables)
|
|
pool_other_items.extend(pool_badges)
|
|
|
|
# Randomize consumables if needed
|
|
pool_other_items = get_randomized_itempool(
|
|
pool_other_items,
|
|
world.options.consumable_item_pool.value,
|
|
world.options.consumable_item_quality.value,
|
|
world.options.beta_items.value,
|
|
world.random
|
|
)
|
|
|
|
if ch_excluded_locations:
|
|
world.random.shuffle(ch_excluded_locations)
|
|
|
|
# shuffle items but sort to put useful items in front so that filler items go to out of logic locations first
|
|
world.random.shuffle(pool_other_items)
|
|
pool_other_items.sort(key=lambda item: 1 if item_table[item][1] == Ic.filler else 0)
|
|
|
|
# save some filler items for the excluded locations; not the chapter ones, but from get_locations_to_exclude
|
|
for _ in excluded_locations:
|
|
pool.append(pool_other_items.pop())
|
|
|
|
for loc in ch_excluded_locations:
|
|
placed_items_excluded[loc] = pool_other_items.pop()
|
|
|
|
# now we have the full pool
|
|
|
|
pool.extend(pool_progression_items)
|
|
pool.extend(pool_other_items)
|
|
pool.extend(pool_misc_progression_items)
|
|
|
|
return pool, placed_items, placed_items_excluded
|
|
|
|
|
|
def get_items_to_exclude(world: "PaperMarioWorld") -> list:
|
|
"""
|
|
Returns a list of items that should not be placed or given to Mario at the
|
|
start.
|
|
"""
|
|
excluded_items = []
|
|
|
|
if world.options.dojo.value:
|
|
for item_name in exclude_due_to_settings.get("do_randomize_dojo"):
|
|
excluded_items.append(item_name)
|
|
|
|
if world.options.start_with_goombario.value:
|
|
excluded_items.append("Goombario")
|
|
if world.options.start_with_kooper.value:
|
|
excluded_items.append("Kooper")
|
|
if world.options.start_with_bombette.value:
|
|
excluded_items.append("Bombette")
|
|
if world.options.start_with_parakarry.value:
|
|
excluded_items.append("Parakarry")
|
|
if world.options.start_with_bow.value:
|
|
excluded_items.append("Bow")
|
|
if world.options.start_with_watt.value:
|
|
excluded_items.append("Watt")
|
|
if world.options.start_with_sushie.value:
|
|
excluded_items.append("Sushie")
|
|
if world.options.start_with_lakilester.value:
|
|
excluded_items.append("Lakilester")
|
|
|
|
if world.options.open_blue_house.value:
|
|
for item_name in exclude_due_to_settings.get("startwith_bluehouse_open"):
|
|
excluded_items.append(item_name)
|
|
|
|
if world.options.open_forest.value:
|
|
for item_name in exclude_due_to_settings.get("startwith_forest_open"):
|
|
excluded_items.append(item_name)
|
|
|
|
if world.options.magical_seeds.value < 4:
|
|
for item_name in exclude_due_to_settings.get("magical_seeds_required").get(world.options.magical_seeds.value):
|
|
excluded_items.append(item_name)
|
|
|
|
if world.options.bowser_castle_mode.value > BowserCastleMode.option_Vanilla:
|
|
for item_name in exclude_due_to_settings.get("shorten_bowsers_castle"):
|
|
excluded_items.append(item_name)
|
|
if world.options.bowser_castle_mode.value == BowserCastleMode.option_Boss_Rush:
|
|
for item_name in exclude_due_to_settings.get("boss_rush"):
|
|
excluded_items.append(item_name)
|
|
if world.options.always_speedy_spin.value:
|
|
for item_name in exclude_due_to_settings.get("always_speedyspin"):
|
|
excluded_items.append(item_name)
|
|
if world.options.always_ispy.value:
|
|
for item_name in exclude_due_to_settings.get("always_ispy"):
|
|
excluded_items.append(item_name)
|
|
if world.options.always_peekaboo.value:
|
|
for item_name in exclude_due_to_settings.get("always_peekaboo"):
|
|
excluded_items.append(item_name)
|
|
if world.options.progressive_badges.value:
|
|
for item_name in exclude_due_to_settings.get("do_progressive_badges"):
|
|
excluded_items.append(item_name)
|
|
|
|
if world.options.starting_hammer.value == StartingHammer.option_Ultra:
|
|
excluded_items.append("Progressive Hammer")
|
|
if world.options.starting_hammer.value >= StartingHammer.option_Super:
|
|
excluded_items.append("Progressive Hammer")
|
|
if (world.options.starting_hammer.value >= StartingHammer.option_Normal and
|
|
world.options.gear_shuffle_mode.value != GearShuffleMode.option_Gear_Location_Shuffle):
|
|
excluded_items.append("Progressive Hammer")
|
|
if world.options.starting_boots.value == StartingBoots.option_Ultra:
|
|
excluded_items.append("Progressive Boots")
|
|
if world.options.starting_boots.value >= StartingBoots.option_Super:
|
|
excluded_items.append("Progressive Boots")
|
|
|
|
if world.options.partner_upgrades.value:
|
|
for item_name in exclude_due_to_settings.get("partner_upgrade_shuffle"):
|
|
excluded_items.append(item_name)
|
|
|
|
return excluded_items
|
|
|
|
|
|
def get_locations_to_exclude(world: "PaperMarioWorld", bc_removed_locations: list) -> list:
|
|
excluded_locations = []
|
|
|
|
# exclude locations which require more star spirits than are expected to be needed to beat the seed
|
|
if not world.options.power_star_hunt.value:
|
|
excluded_locations.extend(get_locations_beyond_spirit_requirements(world.options.star_way_spirits.value))
|
|
|
|
# below lines turned off to see if letting late game locations not be excluded is a problem or not
|
|
# exclude some amount of chapter 8 locations depending upon access requirements
|
|
# if world.options.seed_goal.value != SeedGoal.option_Open_Star_Way:
|
|
# late_game_locations = ch8_locations.copy()
|
|
# for bc_loc in bc_removed_locations:
|
|
# late_game_locations.remove(bc_loc)
|
|
#
|
|
# if not world.options.shuffle_star_beam.value:
|
|
# late_game_locations.remove("SSS Star Sanctuary Gift of the Stars")
|
|
#
|
|
# late_game_exclude_rate = get_star_haven_access_ratio(world.options) * 100
|
|
#
|
|
# for location in late_game_locations:
|
|
# if world.random.randint(1, 100) <= late_game_exclude_rate:
|
|
# excluded_locations.append(location)
|
|
|
|
# exclude merlow rewards
|
|
if not world.options.merlow_items.value:
|
|
excluded_locations.extend(location_groups["MerlowReward"])
|
|
|
|
# exclude rowf item locations
|
|
for location in location_groups["RowfShop"]:
|
|
set_number: int = int(location[34]) # Example string: "TT Plaza District Rowf's Shop Set 1 - 1"
|
|
if location not in excluded_locations and set_number > world.options.rowf_items.value:
|
|
excluded_locations.append(location)
|
|
|
|
# exclude rip cheato locations
|
|
for i in range(0, 11):
|
|
location_name = location_groups["RipCheato"][i]
|
|
if location_table[location_name][4] >= world.options.cheato_items.value:
|
|
excluded_locations.append(location_groups["RipCheato"][i])
|
|
|
|
# remove anything from the list that is already removed for LCL
|
|
for loc in excluded_locations:
|
|
if loc in world.ch_excluded_location_names:
|
|
excluded_locations.remove(loc)
|
|
|
|
return excluded_locations
|
|
|
|
|
|
def get_item_multiples_base_name(item_name: str) -> str:
|
|
if item_name in item_multiples_base_name.keys():
|
|
return item_multiples_base_name[item_name]
|
|
return item_name
|
|
|
|
|
|
def get_star_haven_access_ratio(options: PaperMarioOptions):
|
|
if options.seed_goal.value == SeedGoal.option_Open_Star_Way:
|
|
return 1
|
|
else:
|
|
if options.power_star_hunt.value:
|
|
return (options.star_way_power_stars.value / options.total_power_stars.value + options.star_way_spirits.value / 7) / 2
|
|
else:
|
|
return options.star_way_spirits.value / 7
|
|
|
|
|