Files
dockipelago/worlds/dk64/randomizer/ShuffleCrates.py
Jonathan Tinney 7971961166
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
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

201 lines
9.7 KiB
Python

"""Shuffle Melon Crate Locations."""
from randomizer.Enums.Plandomizer import PlandoItems
from randomizer.Lists import Exceptions
import randomizer.LogicFiles.AngryAztec
import randomizer.LogicFiles.CreepyCastle
import randomizer.LogicFiles.CrystalCaves
import randomizer.LogicFiles.DKIsles
import randomizer.LogicFiles.FranticFactory
import randomizer.LogicFiles.FungiForest
import randomizer.LogicFiles.GloomyGalleon
import randomizer.LogicFiles.HideoutHelm
import randomizer.LogicFiles.JungleJapes
from randomizer.Enums.Levels import Levels
from randomizer.Enums.Locations import Locations
from randomizer.Lists.CustomLocations import CustomLocation, CustomLocations, LocationTypes
from randomizer.LogicClasses import LocationLogic
def addCrate(spoiler, MelonCrate: CustomLocation, enum_val: int, name: str, level: Levels):
"""Add crate to relevant Logic Region."""
level_to_name = {
Levels.DKIsles: "Isles",
Levels.JungleJapes: "Japes",
Levels.AngryAztec: "Aztec",
Levels.FranticFactory: "Factory",
Levels.GloomyGalleon: "Galleon",
Levels.FungiForest: "Forest",
Levels.CrystalCaves: "Caves",
Levels.CreepyCastle: "Castle",
Levels.HideoutHelm: "Helm",
}
spoiler.RegionList[MelonCrate.logic_region].locations.append(LocationLogic(enum_val, MelonCrate.logic))
spoiler.LocationList[enum_val].name = f"{level_to_name[level]} Melon Crate: {name}"
spoiler.LocationList[enum_val].default_mapid_data[0].map = MelonCrate.map
spoiler.LocationList[enum_val].level = level
def removeMelonCrate(spoiler):
"""Remove all crates from Logic regions."""
level_logic_regions = [
randomizer.LogicFiles.DKIsles.LogicRegions,
randomizer.LogicFiles.JungleJapes.LogicRegions,
randomizer.LogicFiles.AngryAztec.LogicRegions,
randomizer.LogicFiles.FranticFactory.LogicRegions,
randomizer.LogicFiles.GloomyGalleon.LogicRegions,
randomizer.LogicFiles.FungiForest.LogicRegions,
randomizer.LogicFiles.CrystalCaves.LogicRegions,
randomizer.LogicFiles.CreepyCastle.LogicRegions,
randomizer.LogicFiles.HideoutHelm.LogicRegions,
]
for level in level_logic_regions:
for region in level:
region_data = spoiler.RegionList[region]
region_data.locations = [x for x in region_data.locations if x.id < Locations.MelonCrate_Location00 or x.id > Locations.MelonCrate_Location12]
def fillPlandoDict(plando_dict: dict, plando_input):
"""Fill the plando_dict variable, using input from the plandomizer_dict."""
for crate in plando_input:
if crate["level"] != -1:
plando_dict[crate["level"]].append(crate["location"])
def getPlandoCrateDistribution(random, plando_dict: dict):
"""Adapt the melon crate balance to the user's plandomizer input."""
distribution = []
for level in plando_dict.keys():
distribution.append(len(plando_dict[level]))
running_total = sum(distribution)
if running_total < 13:
# Make sure as many levels as possible have 1+ melon crate
for level in range(len(distribution)):
if distribution[level] < 1:
distribution[level] += 1
running_total += 1
if running_total >= 13:
break
if running_total < 13:
# Make sure as many levels as possible have 2 melon crates
level_priority = list(range(0, 9))
random.shuffle(level_priority)
amount_of_levels = 4
for level in range(len(distribution)):
if distribution[level_priority[level]] < 2:
distribution[level_priority[level]] += 1
running_total += 1
amount_of_levels -= 1
if running_total >= 13 or amount_of_levels <= 0:
break
return distribution
def ShuffleMelonCrates(spoiler, human_spoiler):
"""Shuffle Melon Crate Locations."""
removeMelonCrate(spoiler)
spoiler.meloncrate_placement = []
total_MelonCrate_list = {
Levels.DKIsles: [],
Levels.JungleJapes: [],
Levels.AngryAztec: [],
Levels.FranticFactory: [],
Levels.GloomyGalleon: [],
Levels.FungiForest: [],
Levels.CrystalCaves: [],
Levels.CreepyCastle: [],
Levels.HideoutHelm: [],
}
for key in total_MelonCrate_list:
human_spoiler[key.name] = [] # Ensure order
plando_dict = {
Levels.DKIsles: [],
Levels.JungleJapes: [],
Levels.AngryAztec: [],
Levels.FranticFactory: [],
Levels.GloomyGalleon: [],
Levels.FungiForest: [],
Levels.CrystalCaves: [],
Levels.CreepyCastle: [],
Levels.HideoutHelm: [],
}
if spoiler.settings.enable_plandomizer and spoiler.settings.plandomizer_dict["plando_melon_crates"] != []:
fillPlandoDict(plando_dict, spoiler.settings.plandomizer_dict["plando_melon_crates"])
for key in total_MelonCrate_list.keys():
for SingleMelonCrateLocation in CustomLocations[key]:
if SingleMelonCrateLocation.is_galleon_floating_crate or SingleMelonCrateLocation.isValidLocation(LocationTypes.MelonCrate):
SingleMelonCrateLocation.setCustomLocation(False)
if not spoiler.settings.enable_plandomizer or (SingleMelonCrateLocation.name not in spoiler.settings.plandomizer_dict["reserved_custom_locations"][key]):
total_MelonCrate_list[key].append(SingleMelonCrateLocation)
# Make sure levels with multiple Melon Crates plandomized are handled first, before the shuffler runs out of dual levels
if spoiler.settings.enable_plandomizer and spoiler.settings.plandomizer_dict["plando_melon_crates"] != []:
distribution = getPlandoCrateDistribution(spoiler.settings.random, plando_dict)
count = 0
for level in plando_dict:
area_meloncrate = total_MelonCrate_list[level]
select_random_meloncrate_from_area(area_meloncrate, distribution[count], level, spoiler, human_spoiler, plando_dict)
del total_MelonCrate_list[level]
count += 1
else:
for SingleMelonCrateLocation in range(4):
area_key = spoiler.settings.random.choice(list(total_MelonCrate_list.keys()))
area_meloncrate = total_MelonCrate_list[area_key]
select_random_meloncrate_from_area(area_meloncrate, 2, area_key, spoiler, human_spoiler, plando_dict)
del total_MelonCrate_list[area_key]
for area_key in total_MelonCrate_list.keys():
area_meloncrate = total_MelonCrate_list[area_key]
select_random_meloncrate_from_area(area_meloncrate, 1, area_key, spoiler, human_spoiler, plando_dict)
# Create the locations for the melon crates
sorted_MelonCrates = spoiler.meloncrate_placement.copy()
sorted_MelonCrates = sorted(sorted_MelonCrates, key=lambda d: d["score"])
for MelonCrate_index, MelonCrate in enumerate(sorted_MelonCrates):
MelonCrate["enum"] = Locations.MelonCrate_Location00 + MelonCrate_index
addCrate(spoiler, MelonCrate["MelonCrate"], MelonCrate["enum"], MelonCrate["name"], MelonCrate["level"])
MelonCrate["MelonCrate"] = None
# Resolve location-item combinations for plando
if spoiler.settings.enable_plandomizer and spoiler.settings.plandomizer_dict["plando_melon_crates"] != []:
for item_placement in spoiler.settings.plandomizer_dict["plando_melon_crates"]:
for MelonCrate_index, MelonCrate in enumerate(sorted_MelonCrates):
if item_placement["location"] == MelonCrate["name"] and item_placement["level"] == MelonCrate["level"] and item_placement["reward"] != -1:
spoiler.settings.plandomizer_dict["locations"][MelonCrate["enum"]] = item_placement["reward"]
return human_spoiler.copy()
def select_random_meloncrate_from_area(area_meloncrate, amount, level, spoiler, human_spoiler, plando_input):
"""Select <amount> random melon crates from <area_meloncrate>, which is a list of melon crates. Makes sure max 1 melon crate per group is selected."""
human_spoiler[level.name] = []
for iterations in range(amount):
allow_same_group_crate = False
selected_crate = spoiler.settings.random.choice(area_meloncrate) # selects a random crate from the list
selected_crate_name = selected_crate.name
# Give plandomizer an opportunity to get the final say
if spoiler.settings.enable_plandomizer and spoiler.settings.plandomizer_dict["plando_melon_crates"] != []:
if len(plando_input[level]) > 1:
allow_same_group_crate = True
if len(plando_input[level]) > iterations:
selected_crate_name = plando_input[level][iterations]
for meloncrate in CustomLocations[level]: # enables the selected crate
if meloncrate.name == selected_crate_name:
meloncrate.setCustomLocation(True)
human_spoiler[level.name].append(meloncrate.name)
local_map_index = len([x for x in spoiler.meloncrate_placement if x["map"] == meloncrate.map])
spoiler.meloncrate_placement.append(
{
"name": meloncrate.name,
"map": meloncrate.map,
"MelonCrate": meloncrate,
"level": level,
"score": (meloncrate.map * 100) + local_map_index,
},
)
area_meloncrate.remove(selected_crate)
break
if amount > 1 and not allow_same_group_crate: # if multiple crates are picked, remove crates from the same group, prevent them from being picked
area_meloncrate = [crate for crate in area_meloncrate if crate.group != selected_crate.group]