mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-25 06:23:22 -07:00
Compare commits
10 Commits
0.6.0
...
NewSoupVi-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77e6fa0010 | ||
|
|
d26db6f213 | ||
|
|
bb6c753583 | ||
|
|
ca08e4b950 | ||
|
|
5a6b02dbd3 | ||
|
|
14416b1050 | ||
|
|
da4e6fc532 | ||
|
|
57d8b69a6d | ||
|
|
c9d8a8661c | ||
|
|
4a3d23e0e6 |
6
kvui.py
6
kvui.py
@@ -817,6 +817,12 @@ class HintLayout(BoxLayout):
|
|||||||
boxlayout.add_widget(AutocompleteHintInput())
|
boxlayout.add_widget(AutocompleteHintInput())
|
||||||
self.add_widget(boxlayout)
|
self.add_widget(boxlayout)
|
||||||
|
|
||||||
|
def fix_heights(self):
|
||||||
|
for child in self.children:
|
||||||
|
fix_func = getattr(child, "fix_heights", None)
|
||||||
|
if fix_func:
|
||||||
|
fix_func()
|
||||||
|
|
||||||
|
|
||||||
status_names: typing.Dict[HintStatus, str] = {
|
status_names: typing.Dict[HintStatus, str] = {
|
||||||
HintStatus.HINT_FOUND: "Found",
|
HintStatus.HINT_FOUND: "Found",
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -19,7 +19,7 @@ from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
|
|||||||
|
|
||||||
|
|
||||||
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
||||||
requirement = 'cx-Freeze==7.2.0'
|
requirement = 'cx-Freeze==8.0.0'
|
||||||
try:
|
try:
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
try:
|
try:
|
||||||
|
|||||||
14
test/general/test_packages.py
Normal file
14
test/general/test_packages.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class TestPackages(unittest.TestCase):
|
||||||
|
def test_packages_have_init(self):
|
||||||
|
"""Test that all world folders containing .py files also have a __init__.py file,
|
||||||
|
to indicate full package rather than namespace package."""
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
worlds_path = Utils.local_path("worlds")
|
||||||
|
for dirpath, dirnames, filenames in os.walk(worlds_path):
|
||||||
|
with self.subTest(directory=dirpath):
|
||||||
|
self.assertEqual("__init__.py" in filenames, any(file.endswith(".py") for file in filenames))
|
||||||
@@ -48,6 +48,10 @@ class CivVIContainer(APContainer, metaclass=AutoPatchRegister):
|
|||||||
opened_zipfile.writestr(filename, yml)
|
opened_zipfile.writestr(filename, yml)
|
||||||
super().write_contents(opened_zipfile)
|
super().write_contents(opened_zipfile)
|
||||||
|
|
||||||
|
def sanitize_value(value: str) -> str:
|
||||||
|
"""Removes values that can cause issues in XML"""
|
||||||
|
return value.replace('"', "'").replace('&', 'and')
|
||||||
|
|
||||||
|
|
||||||
def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int:
|
def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int:
|
||||||
"""
|
"""
|
||||||
@@ -63,7 +67,7 @@ def get_formatted_player_name(world: 'CivVIWorld', player: int) -> str:
|
|||||||
Returns the name of the player in the world
|
Returns the name of the player in the world
|
||||||
"""
|
"""
|
||||||
if player != world.player:
|
if player != world.player:
|
||||||
return f"{world.multiworld.player_name[player]}{apo}s"
|
return sanitize_value(f"{world.multiworld.player_name[player]}{apo}s")
|
||||||
return "Your"
|
return "Your"
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +110,7 @@ def generate_new_items(world: 'CivVIWorld') -> str:
|
|||||||
<Row TechnologyType="TECH_BLOCKER" Name="TECH_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Tech created to prevent players from researching their own tech. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
|
<Row TechnologyType="TECH_BLOCKER" Name="TECH_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Tech created to prevent players from researching their own tech. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
|
||||||
{"".join([f'{tab}<Row TechnologyType="{location.name}" '
|
{"".join([f'{tab}<Row TechnologyType="{location.name}" '
|
||||||
f'Name="{get_formatted_player_name(world, location.item.player)} '
|
f'Name="{get_formatted_player_name(world, location.item.player)} '
|
||||||
f'{location.item.name}" '
|
f'{sanitize_value(location.item.name)}" '
|
||||||
f'EraType="{world.location_table[location.name].era_type}" '
|
f'EraType="{world.location_table[location.name].era_type}" '
|
||||||
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
|
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
|
||||||
f'Cost="{get_cost(world, world.location_table[location.name])}" '
|
f'Cost="{get_cost(world, world.location_table[location.name])}" '
|
||||||
@@ -122,7 +126,7 @@ def generate_new_items(world: 'CivVIWorld') -> str:
|
|||||||
<Row CivicType="CIVIC_BLOCKER" Name="CIVIC_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Civic created to prevent players from researching their own civics. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
|
<Row CivicType="CIVIC_BLOCKER" Name="CIVIC_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Civic created to prevent players from researching their own civics. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
|
||||||
{"".join([f'{tab}<Row CivicType="{location.name}" '
|
{"".join([f'{tab}<Row CivicType="{location.name}" '
|
||||||
f'Name="{get_formatted_player_name(world, location.item.player)} '
|
f'Name="{get_formatted_player_name(world, location.item.player)} '
|
||||||
f'{location.item.name}" '
|
f'{sanitize_value(location.item.name)}" '
|
||||||
f'EraType="{world.location_table[location.name].era_type}" '
|
f'EraType="{world.location_table[location.name].era_type}" '
|
||||||
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
|
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
|
||||||
f'Cost="{get_cost(world, world.location_table[location.name])}" '
|
f'Cost="{get_cost(world, world.location_table[location.name])}" '
|
||||||
|
|||||||
0
worlds/civ_6/data/__init__.py
Normal file
0
worlds/civ_6/data/__init__.py
Normal file
0
worlds/cv64/data/__init__.py
Normal file
0
worlds/cv64/data/__init__.py
Normal file
@@ -644,6 +644,9 @@ class CV64PatchExtensions(APPatchExtension):
|
|||||||
# Replace the PowerUp in the Forest Special1 Bridge 3HB rock with an L jewel if 3HBs aren't randomized
|
# Replace the PowerUp in the Forest Special1 Bridge 3HB rock with an L jewel if 3HBs aren't randomized
|
||||||
if not options["multi_hit_breakables"]:
|
if not options["multi_hit_breakables"]:
|
||||||
rom_data.write_byte(0x10C7A1, 0x03)
|
rom_data.write_byte(0x10C7A1, 0x03)
|
||||||
|
# Replace the PowerUp in one of the lizard lockers if the lizard locker items aren't randomized.
|
||||||
|
if not options["lizard_locker_items"]:
|
||||||
|
rom_data.write_byte(0xBFCA07, 0x03)
|
||||||
# Change the appearance of the Pot-Pourri to that of a larger PowerUp regardless of the above setting, so other
|
# Change the appearance of the Pot-Pourri to that of a larger PowerUp regardless of the above setting, so other
|
||||||
# game PermaUps are distinguishable.
|
# game PermaUps are distinguishable.
|
||||||
rom_data.write_int32s(0xEE558, [0x06005F08, 0x3FB00000, 0xFFFFFF00])
|
rom_data.write_int32s(0xEE558, [0x06005F08, 0x3FB00000, 0xFFFFFF00])
|
||||||
@@ -714,7 +717,11 @@ class CV64PatchExtensions(APPatchExtension):
|
|||||||
rom_data.write_int32(0x10CF38, 0x8000FF4D) # CT final room door slab
|
rom_data.write_int32(0x10CF38, 0x8000FF4D) # CT final room door slab
|
||||||
rom_data.write_int32(0x10CF44, 0x1000FF4D) # CT Renon slab
|
rom_data.write_int32(0x10CF44, 0x1000FF4D) # CT Renon slab
|
||||||
rom_data.write_int32(0x10C908, 0x0008FF4D) # Villa foyer chandelier
|
rom_data.write_int32(0x10C908, 0x0008FF4D) # Villa foyer chandelier
|
||||||
rom_data.write_byte(0x10CF37, 0x04) # pointer for CT final room door slab item data
|
|
||||||
|
# Change the pointer to the Clock Tower final room 3HB door slab drops to not share its values with those of the
|
||||||
|
# 3HB slab near Renon at the top of the room.
|
||||||
|
if options["multi_hit_breakables"]:
|
||||||
|
rom_data.write_byte(0x10CF37, 0x04)
|
||||||
|
|
||||||
# Once-per-frame gameplay checks
|
# Once-per-frame gameplay checks
|
||||||
rom_data.write_int32(0x6C848, 0x080FF40D) # J 0x803FD034
|
rom_data.write_int32(0x6C848, 0x080FF40D) # J 0x803FD034
|
||||||
@@ -1000,6 +1007,7 @@ def write_patch(world: "CV64World", patch: CV64ProcedurePatch, offset_data: Dict
|
|||||||
"multi_hit_breakables": world.options.multi_hit_breakables.value,
|
"multi_hit_breakables": world.options.multi_hit_breakables.value,
|
||||||
"drop_previous_sub_weapon": world.options.drop_previous_sub_weapon.value,
|
"drop_previous_sub_weapon": world.options.drop_previous_sub_weapon.value,
|
||||||
"countdown": world.options.countdown.value,
|
"countdown": world.options.countdown.value,
|
||||||
|
"lizard_locker_items": world.options.lizard_locker_items.value,
|
||||||
"shopsanity": world.options.shopsanity.value,
|
"shopsanity": world.options.shopsanity.value,
|
||||||
"panther_dash": world.options.panther_dash.value,
|
"panther_dash": world.options.panther_dash.value,
|
||||||
"big_toss": world.options.big_toss.value,
|
"big_toss": world.options.big_toss.value,
|
||||||
|
|||||||
0
worlds/cvcotm/data/__init__.py
Normal file
0
worlds/cvcotm/data/__init__.py
Normal file
@@ -3,7 +3,6 @@ import settings
|
|||||||
import base64
|
import base64
|
||||||
import threading
|
import threading
|
||||||
import requests
|
import requests
|
||||||
import yaml
|
|
||||||
from worlds.AutoWorld import World, WebWorld
|
from worlds.AutoWorld import World, WebWorld
|
||||||
from BaseClasses import Tutorial
|
from BaseClasses import Tutorial
|
||||||
from .Regions import create_regions, location_table, set_rules, stage_set_rules, rooms, non_dead_end_crest_rooms,\
|
from .Regions import create_regions, location_table, set_rules, stage_set_rules, rooms, non_dead_end_crest_rooms,\
|
||||||
@@ -134,7 +133,7 @@ class FFMQWorld(World):
|
|||||||
errors.append([api_url, err])
|
errors.append([api_url, err])
|
||||||
else:
|
else:
|
||||||
if response.ok:
|
if response.ok:
|
||||||
world.rooms = rooms_data[query] = yaml.load(response.text, yaml.Loader)
|
world.rooms = rooms_data[query] = Utils.parse_yaml(response.text)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
api_urls.remove(api_url)
|
api_urls.remove(api_url)
|
||||||
|
|||||||
0
worlds/ffmq/data/__init__.py
Normal file
0
worlds/ffmq/data/__init__.py
Normal file
0
worlds/kh2/Names/__init__.py
Normal file
0
worlds/kh2/Names/__init__.py
Normal file
0
worlds/ladx/LADXR/__init__.py
Normal file
0
worlds/ladx/LADXR/__init__.py
Normal file
0
worlds/ladx/LADXR/locations/__init__.py
Normal file
0
worlds/ladx/LADXR/locations/__init__.py
Normal file
0
worlds/ladx/LADXR/mapgen/locations/__init__.py
Normal file
0
worlds/ladx/LADXR/mapgen/locations/__init__.py
Normal file
0
worlds/ladx/LADXR/mapgen/roomtype/__init__.py
Normal file
0
worlds/ladx/LADXR/mapgen/roomtype/__init__.py
Normal file
0
worlds/ladx/LADXR/patches/__init__.py
Normal file
0
worlds/ladx/LADXR/patches/__init__.py
Normal file
0
worlds/landstalker/data/__init__.py
Normal file
0
worlds/landstalker/data/__init__.py
Normal file
0
worlds/mlss/Names/__init__.py
Normal file
0
worlds/mlss/Names/__init__.py
Normal file
Binary file not shown.
0
worlds/mmbn3/Names/__init__.py
Normal file
0
worlds/mmbn3/Names/__init__.py
Normal file
@@ -52,11 +52,13 @@ class MuseDashCollections:
|
|||||||
"Nyaa SFX Trap": STARTING_CODE + 8,
|
"Nyaa SFX Trap": STARTING_CODE + 8,
|
||||||
"Error SFX Trap": STARTING_CODE + 9,
|
"Error SFX Trap": STARTING_CODE + 9,
|
||||||
"Focus Line Trap": STARTING_CODE + 10,
|
"Focus Line Trap": STARTING_CODE + 10,
|
||||||
|
"Beefcake SFX Trap": STARTING_CODE + 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
sfx_trap_items: List[str] = [
|
sfx_trap_items: List[str] = [
|
||||||
"Nyaa SFX Trap",
|
"Nyaa SFX Trap",
|
||||||
"Error SFX Trap",
|
"Error SFX Trap",
|
||||||
|
"Beefcake SFX Trap",
|
||||||
]
|
]
|
||||||
|
|
||||||
filler_items: Dict[str, int] = {
|
filler_items: Dict[str, int] = {
|
||||||
|
|||||||
@@ -627,4 +627,10 @@ SONG_DATA: Dict[str, SongData] = {
|
|||||||
"Sharp Bubbles": SongData(2900751, "83-3", "Cosmic Radio 2024", True, 7, 9, 11),
|
"Sharp Bubbles": SongData(2900751, "83-3", "Cosmic Radio 2024", True, 7, 9, 11),
|
||||||
"Replay": SongData(2900752, "83-4", "Cosmic Radio 2024", True, 5, 7, 9),
|
"Replay": SongData(2900752, "83-4", "Cosmic Radio 2024", True, 5, 7, 9),
|
||||||
"Cosmic Dusty Girl": SongData(2900753, "83-5", "Cosmic Radio 2024", True, 5, 7, 9),
|
"Cosmic Dusty Girl": SongData(2900753, "83-5", "Cosmic Radio 2024", True, 5, 7, 9),
|
||||||
|
"Meow Rock feat. Chun Ge, Yuan Shen": SongData(2900754, "84-0", "Muse Dash Legend", True, None, None, None),
|
||||||
|
"Even if you make an old radio song with AI": SongData(2900755, "84-1", "Muse Dash Legend", False, 3, 6, 8),
|
||||||
|
"Unusual Sketchbook": SongData(2900756, "84-2", "Muse Dash Legend", True, 6, 8, 11),
|
||||||
|
"TransientTears": SongData(2900757, "84-3", "Muse Dash Legend", True, 6, 8, 11),
|
||||||
|
"SHOOTING*STAR": SongData(2900758, "84-4", "Muse Dash Legend", False, 5, 7, 9),
|
||||||
|
"But the Blue Bird is Already Dead": SongData(2900759, "84-5", "Muse Dash Legend", False, 6, 8, 10),
|
||||||
}
|
}
|
||||||
|
|||||||
0
worlds/osrs/LogicCSV/__init__.py
Normal file
0
worlds/osrs/LogicCSV/__init__.py
Normal file
@@ -14,6 +14,9 @@ _not_ used for logical access (the seed will never require you to catch somethin
|
|||||||
|
|
||||||
- Now excludes the location "Navel Rock Top - Hidden Item Sacred Ash" if your goal is Champion and you didn't randomize
|
- Now excludes the location "Navel Rock Top - Hidden Item Sacred Ash" if your goal is Champion and you didn't randomize
|
||||||
event tickets.
|
event tickets.
|
||||||
|
- Fixed handling of shuffle option for badges/HMs in the case that the player sets those items to nonlocal or uses
|
||||||
|
plando to put an item in one of those locations, or in the case that fill gets itself stuck on these items and has to
|
||||||
|
retry.
|
||||||
|
|
||||||
# 2.3.0
|
# 2.3.0
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import os
|
|||||||
import pkgutil
|
import pkgutil
|
||||||
from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar, TextIO, Union
|
from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar, TextIO, Union
|
||||||
|
|
||||||
from BaseClasses import ItemClassification, MultiWorld, Tutorial, LocationProgressType
|
from BaseClasses import CollectionState, ItemClassification, MultiWorld, Tutorial, LocationProgressType
|
||||||
from Fill import FillError, fill_restrictive
|
from Fill import FillError, fill_restrictive
|
||||||
from Options import OptionError, Toggle
|
from Options import OptionError, Toggle
|
||||||
import settings
|
import settings
|
||||||
@@ -100,6 +100,7 @@ class PokemonEmeraldWorld(World):
|
|||||||
|
|
||||||
required_client_version = (0, 4, 6)
|
required_client_version = (0, 4, 6)
|
||||||
|
|
||||||
|
item_pool: List[PokemonEmeraldItem]
|
||||||
badge_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
|
badge_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
|
||||||
hm_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
|
hm_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
|
||||||
free_fly_location_id: int
|
free_fly_location_id: int
|
||||||
@@ -185,7 +186,7 @@ class PokemonEmeraldWorld(World):
|
|||||||
|
|
||||||
# In race mode we don't patch any item location information into the ROM
|
# In race mode we don't patch any item location information into the ROM
|
||||||
if self.multiworld.is_race and not self.options.remote_items:
|
if self.multiworld.is_race and not self.options.remote_items:
|
||||||
logging.warning("Pokemon Emerald: Forcing Player %s (%s) to use remote items due to race mode.",
|
logging.warning("Pokemon Emerald: Forcing player %s (%s) to use remote items due to race mode.",
|
||||||
self.player, self.player_name)
|
self.player, self.player_name)
|
||||||
self.options.remote_items.value = Toggle.option_true
|
self.options.remote_items.value = Toggle.option_true
|
||||||
|
|
||||||
@@ -197,7 +198,7 @@ class PokemonEmeraldWorld(World):
|
|||||||
|
|
||||||
# Prevent setting the number of required legendaries higher than the number of enabled legendaries
|
# Prevent setting the number of required legendaries higher than the number of enabled legendaries
|
||||||
if self.options.legendary_hunt_count.value > len(self.options.allowed_legendary_hunt_encounters.value):
|
if self.options.legendary_hunt_count.value > len(self.options.allowed_legendary_hunt_encounters.value):
|
||||||
logging.warning("Pokemon Emerald: Legendary hunt count for Player %s (%s) higher than number of allowed "
|
logging.warning("Pokemon Emerald: Legendary hunt count for player %s (%s) higher than number of allowed "
|
||||||
"legendary encounters. Reducing to number of allowed encounters.", self.player,
|
"legendary encounters. Reducing to number of allowed encounters.", self.player,
|
||||||
self.player_name)
|
self.player_name)
|
||||||
self.options.legendary_hunt_count.value = len(self.options.allowed_legendary_hunt_encounters.value)
|
self.options.legendary_hunt_count.value = len(self.options.allowed_legendary_hunt_encounters.value)
|
||||||
@@ -234,10 +235,17 @@ class PokemonEmeraldWorld(World):
|
|||||||
max_norman_count = 4
|
max_norman_count = 4
|
||||||
|
|
||||||
if self.options.norman_count.value > max_norman_count:
|
if self.options.norman_count.value > max_norman_count:
|
||||||
logging.warning("Pokemon Emerald: Norman requirements for Player %s (%s) are unsafe in combination with "
|
logging.warning("Pokemon Emerald: Norman requirements for player %s (%s) are unsafe in combination with "
|
||||||
"other settings. Reducing to 4.", self.player, self.player_name)
|
"other settings. Reducing to 4.", self.player, self.player_name)
|
||||||
self.options.norman_count.value = max_norman_count
|
self.options.norman_count.value = max_norman_count
|
||||||
|
|
||||||
|
# Shuffled badges/hms will always be placed locally, so add them to local_items
|
||||||
|
if self.options.badges == RandomizeBadges.option_shuffle:
|
||||||
|
self.options.local_items.value.update(self.item_name_groups["Badge"])
|
||||||
|
|
||||||
|
if self.options.hms == RandomizeHms.option_shuffle:
|
||||||
|
self.options.local_items.value.update(self.item_name_groups["HM"])
|
||||||
|
|
||||||
def create_regions(self) -> None:
|
def create_regions(self) -> None:
|
||||||
from .regions import create_regions
|
from .regions import create_regions
|
||||||
all_regions = create_regions(self)
|
all_regions = create_regions(self)
|
||||||
@@ -377,12 +385,11 @@ class PokemonEmeraldWorld(World):
|
|||||||
item_locations = [location for location in item_locations if emerald_data.locations[location.key].category not in filter_categories]
|
item_locations = [location for location in item_locations if emerald_data.locations[location.key].category not in filter_categories]
|
||||||
default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations]
|
default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations]
|
||||||
|
|
||||||
# Take the itempool as-is
|
|
||||||
if self.options.item_pool_type == ItemPoolType.option_shuffled:
|
if self.options.item_pool_type == ItemPoolType.option_shuffled:
|
||||||
self.multiworld.itempool += default_itempool
|
# Take the itempool as-is
|
||||||
|
self.item_pool = default_itempool
|
||||||
# Recreate the itempool from random items
|
|
||||||
elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced):
|
elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced):
|
||||||
|
# Recreate the itempool from random items
|
||||||
item_categories = ["Ball", "Healing", "Rare Candy", "Vitamin", "Evolution Stone",
|
item_categories = ["Ball", "Healing", "Rare Candy", "Vitamin", "Evolution Stone",
|
||||||
"Money", "TM", "Held", "Misc", "Berry"]
|
"Money", "TM", "Held", "Misc", "Berry"]
|
||||||
|
|
||||||
@@ -392,6 +399,7 @@ class PokemonEmeraldWorld(World):
|
|||||||
if not item.advancement:
|
if not item.advancement:
|
||||||
item_category_counter.update([tag for tag in item.tags if tag in item_categories])
|
item_category_counter.update([tag for tag in item.tags if tag in item_categories])
|
||||||
|
|
||||||
|
self.item_pool = []
|
||||||
item_category_weights = [item_category_counter.get(category) for category in item_categories]
|
item_category_weights = [item_category_counter.get(category) for category in item_categories]
|
||||||
item_category_weights = [weight if weight is not None else 0 for weight in item_category_weights]
|
item_category_weights = [weight if weight is not None else 0 for weight in item_category_weights]
|
||||||
|
|
||||||
@@ -436,19 +444,10 @@ class PokemonEmeraldWorld(World):
|
|||||||
item_code = self.random.choice(fill_item_candidates_by_category[category])
|
item_code = self.random.choice(fill_item_candidates_by_category[category])
|
||||||
item = self.create_item_by_code(item_code)
|
item = self.create_item_by_code(item_code)
|
||||||
|
|
||||||
self.multiworld.itempool.append(item)
|
self.item_pool.append(item)
|
||||||
|
|
||||||
def set_rules(self) -> None:
|
self.multiworld.itempool += self.item_pool
|
||||||
from .rules import set_rules
|
|
||||||
set_rules(self)
|
|
||||||
|
|
||||||
def generate_basic(self) -> None:
|
|
||||||
# Create auth
|
|
||||||
# self.auth = self.random.randbytes(16) # Requires >=3.9
|
|
||||||
self.auth = self.random.getrandbits(16 * 8).to_bytes(16, "little")
|
|
||||||
|
|
||||||
randomize_types(self)
|
|
||||||
randomize_wild_encounters(self)
|
|
||||||
set_free_fly(self)
|
set_free_fly(self)
|
||||||
set_legendary_cave_entrances(self)
|
set_legendary_cave_entrances(self)
|
||||||
|
|
||||||
@@ -475,9 +474,20 @@ class PokemonEmeraldWorld(World):
|
|||||||
if not self.options.key_items:
|
if not self.options.key_items:
|
||||||
convert_unrandomized_items_to_events(LocationCategory.KEY)
|
convert_unrandomized_items_to_events(LocationCategory.KEY)
|
||||||
|
|
||||||
def pre_fill(self) -> None:
|
def set_rules(self):
|
||||||
# Badges and HMs that are set to shuffle need to be placed at
|
from .rules import set_rules
|
||||||
# their own subset of locations
|
set_rules(self)
|
||||||
|
|
||||||
|
def connect_entrances(self):
|
||||||
|
randomize_wild_encounters(self)
|
||||||
|
self.shuffle_badges_hms()
|
||||||
|
# For entrance randomization, disconnect entrances here, randomize map, then
|
||||||
|
# undo badge/HM placement and re-shuffle them in the new map.
|
||||||
|
|
||||||
|
def shuffle_badges_hms(self) -> None:
|
||||||
|
my_progression_items = [item for item in self.item_pool if item.advancement]
|
||||||
|
my_locations = list(self.get_locations())
|
||||||
|
|
||||||
if self.options.badges == RandomizeBadges.option_shuffle:
|
if self.options.badges == RandomizeBadges.option_shuffle:
|
||||||
badge_locations: List[PokemonEmeraldLocation]
|
badge_locations: List[PokemonEmeraldLocation]
|
||||||
badge_items: List[PokemonEmeraldItem]
|
badge_items: List[PokemonEmeraldItem]
|
||||||
@@ -502,41 +512,20 @@ class PokemonEmeraldWorld(World):
|
|||||||
badge_priority["Knuckle Badge"] = 0
|
badge_priority["Knuckle Badge"] = 0
|
||||||
badge_items.sort(key=lambda item: badge_priority.get(item.name, 0))
|
badge_items.sort(key=lambda item: badge_priority.get(item.name, 0))
|
||||||
|
|
||||||
# Un-exclude badge locations, since we need to put progression items on them
|
# Build state
|
||||||
for location in badge_locations:
|
state = CollectionState(self.multiworld)
|
||||||
location.progress_type = LocationProgressType.DEFAULT \
|
for item in my_progression_items:
|
||||||
if location.progress_type == LocationProgressType.EXCLUDED \
|
state.collect(item, True)
|
||||||
else location.progress_type
|
# If HM shuffle is on, HMs are neither placed in locations nor in
|
||||||
|
# the item pool, so we also need to collect them.
|
||||||
collection_state = self.multiworld.get_all_state(False)
|
|
||||||
|
|
||||||
# If HM shuffle is on, HMs are not placed and not in the pool, so
|
|
||||||
# `get_all_state` did not contain them. Collect them manually for
|
|
||||||
# this fill. We know that they will be included in all state after
|
|
||||||
# this stage.
|
|
||||||
if self.hm_shuffle_info is not None:
|
if self.hm_shuffle_info is not None:
|
||||||
for _, item in self.hm_shuffle_info:
|
for _, item in self.hm_shuffle_info:
|
||||||
collection_state.collect(item)
|
state.collect(item, True)
|
||||||
|
state.sweep_for_advancements(my_locations)
|
||||||
|
|
||||||
# In specific very constrained conditions, fill_restrictive may run
|
# Shuffle badges
|
||||||
# out of swaps before it finds a valid solution if it gets unlucky.
|
self.fill_subset_with_retries(badge_items, badge_locations, state)
|
||||||
# This is a band-aid until fill/swap can reliably find those solutions.
|
|
||||||
attempts_remaining = 2
|
|
||||||
while attempts_remaining > 0:
|
|
||||||
attempts_remaining -= 1
|
|
||||||
self.random.shuffle(badge_locations)
|
|
||||||
try:
|
|
||||||
fill_restrictive(self.multiworld, collection_state, badge_locations, badge_items,
|
|
||||||
single_player_placement=True, lock=True, allow_excluded=True)
|
|
||||||
break
|
|
||||||
except FillError as exc:
|
|
||||||
if attempts_remaining == 0:
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
logging.debug(f"Failed to shuffle badges for player {self.player}. Retrying.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Badges are guaranteed to be either placed or in the multiworld's itempool now
|
|
||||||
if self.options.hms == RandomizeHms.option_shuffle:
|
if self.options.hms == RandomizeHms.option_shuffle:
|
||||||
hm_locations: List[PokemonEmeraldLocation]
|
hm_locations: List[PokemonEmeraldLocation]
|
||||||
hm_items: List[PokemonEmeraldItem]
|
hm_items: List[PokemonEmeraldItem]
|
||||||
@@ -559,33 +548,56 @@ class PokemonEmeraldWorld(World):
|
|||||||
if self.options.badges == RandomizeBadges.option_vanilla and \
|
if self.options.badges == RandomizeBadges.option_vanilla and \
|
||||||
self.options.require_flash in (DarkCavesRequireFlash.option_both, DarkCavesRequireFlash.option_only_granite_cave):
|
self.options.require_flash in (DarkCavesRequireFlash.option_both, DarkCavesRequireFlash.option_only_granite_cave):
|
||||||
hm_priority["HM05 Flash"] = 0
|
hm_priority["HM05 Flash"] = 0
|
||||||
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0))
|
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0), reverse=True)
|
||||||
|
|
||||||
# Un-exclude HM locations, since we need to put progression items on them
|
# Build state
|
||||||
for location in hm_locations:
|
# Badges are either in the item pool, or already placed and collected during sweep
|
||||||
location.progress_type = LocationProgressType.DEFAULT \
|
state = CollectionState(self.multiworld)
|
||||||
if location.progress_type == LocationProgressType.EXCLUDED \
|
for item in my_progression_items:
|
||||||
else location.progress_type
|
state.collect(item, True)
|
||||||
|
state.sweep_for_advancements(my_locations)
|
||||||
|
|
||||||
collection_state = self.multiworld.get_all_state(False)
|
# Shuffle HMs
|
||||||
|
self.fill_subset_with_retries(hm_items, hm_locations, state)
|
||||||
|
|
||||||
# In specific very constrained conditions, fill_restrictive may run
|
def fill_subset_with_retries(self, items: list[PokemonEmeraldItem], locations: list[PokemonEmeraldLocation], state: CollectionState):
|
||||||
# out of swaps before it finds a valid solution if it gets unlucky.
|
# Un-exclude locations, since we need to put progression items on them
|
||||||
# This is a band-aid until fill/swap can reliably find those solutions.
|
for location in locations:
|
||||||
attempts_remaining = 2
|
location.progress_type = LocationProgressType.DEFAULT \
|
||||||
while attempts_remaining > 0:
|
if location.progress_type == LocationProgressType.EXCLUDED \
|
||||||
attempts_remaining -= 1
|
else location.progress_type
|
||||||
self.random.shuffle(hm_locations)
|
|
||||||
try:
|
|
||||||
fill_restrictive(self.multiworld, collection_state, hm_locations, hm_items,
|
|
||||||
single_player_placement=True, lock=True, allow_excluded=True)
|
|
||||||
break
|
|
||||||
except FillError as exc:
|
|
||||||
if attempts_remaining == 0:
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
logging.debug(f"Failed to shuffle HMs for player {self.player}. Retrying.")
|
# In specific very constrained conditions, `fill_restrictive` may run
|
||||||
continue
|
# out of swaps before it finds a valid solution if it gets unlucky.
|
||||||
|
attempts_remaining = 2
|
||||||
|
while attempts_remaining > 0:
|
||||||
|
attempts_remaining -= 1
|
||||||
|
locations_copy = locations.copy()
|
||||||
|
items_copy = items.copy()
|
||||||
|
self.random.shuffle(locations_copy)
|
||||||
|
try:
|
||||||
|
fill_restrictive(self.multiworld, state, locations_copy, items_copy, single_player_placement=True,
|
||||||
|
lock=True)
|
||||||
|
break
|
||||||
|
except FillError as exc:
|
||||||
|
if attempts_remaining <= 0:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
# Undo partial item placement
|
||||||
|
for location in locations:
|
||||||
|
location.locked = False
|
||||||
|
if location.item is not None:
|
||||||
|
location.item.location = None
|
||||||
|
location.item = None
|
||||||
|
|
||||||
|
logging.debug(f"Failed to shuffle items for player {self.player} ({self.player_name}). Retrying.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
def generate_basic(self) -> None:
|
||||||
|
# Create auth
|
||||||
|
self.auth = self.random.randbytes(16)
|
||||||
|
|
||||||
|
randomize_types(self)
|
||||||
|
|
||||||
def generate_output(self, output_directory: str) -> None:
|
def generate_output(self, output_directory: str) -> None:
|
||||||
self.modified_trainers = copy.deepcopy(emerald_data.trainers)
|
self.modified_trainers = copy.deepcopy(emerald_data.trainers)
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class SMWorld(World):
|
|||||||
Logic.factory('vanilla')
|
Logic.factory('vanilla')
|
||||||
|
|
||||||
dummy_rom_file = Utils.user_path(SMSettings.RomFile.copy_to) # actual rom set in generate_output
|
dummy_rom_file = Utils.user_path(SMSettings.RomFile.copy_to) # actual rom set in generate_output
|
||||||
self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player)
|
self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player, self.multiworld.seed, self.random)
|
||||||
self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
|
self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
|
||||||
|
|
||||||
# keeps Nothing items local so no player will ever pickup Nothing
|
# keeps Nothing items local so no player will ever pickup Nothing
|
||||||
@@ -314,11 +314,11 @@ class SMWorld(World):
|
|||||||
raise KeyError(f"Item {name} for {self.player_name} is invalid.")
|
raise KeyError(f"Item {name} for {self.player_name} is invalid.")
|
||||||
|
|
||||||
def get_filler_item_name(self) -> str:
|
def get_filler_item_name(self) -> str:
|
||||||
if self.multiworld.random.randint(0, 100) < self.options.minor_qty.value:
|
if self.random.randint(0, 100) < self.options.minor_qty.value:
|
||||||
power_bombs = self.options.power_bomb_qty.value
|
power_bombs = self.options.power_bomb_qty.value
|
||||||
missiles = self.options.missile_qty.value
|
missiles = self.options.missile_qty.value
|
||||||
super_missiles = self.options.super_qty.value
|
super_missiles = self.options.super_qty.value
|
||||||
roll = self.multiworld.random.randint(1, power_bombs + missiles + super_missiles)
|
roll = self.random.randint(1, power_bombs + missiles + super_missiles)
|
||||||
if roll <= power_bombs:
|
if roll <= power_bombs:
|
||||||
return "Power Bomb"
|
return "Power Bomb"
|
||||||
elif roll <= power_bombs + missiles:
|
elif roll <= power_bombs + missiles:
|
||||||
@@ -340,8 +340,8 @@ class SMWorld(World):
|
|||||||
else:
|
else:
|
||||||
nonChozoLoc.append(loc)
|
nonChozoLoc.append(loc)
|
||||||
|
|
||||||
self.multiworld.random.shuffle(nonChozoLoc)
|
self.random.shuffle(nonChozoLoc)
|
||||||
self.multiworld.random.shuffle(chozoLoc)
|
self.random.shuffle(chozoLoc)
|
||||||
missingCount = len(self.NothingPool) - len(nonChozoLoc)
|
missingCount = len(self.NothingPool) - len(nonChozoLoc)
|
||||||
locations = nonChozoLoc
|
locations = nonChozoLoc
|
||||||
if (missingCount > 0):
|
if (missingCount > 0):
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import copy
|
import copy
|
||||||
import random
|
|
||||||
from ..logic.logic import Logic
|
from ..logic.logic import Logic
|
||||||
from ..utils.parameters import Knows
|
from ..utils.parameters import Knows
|
||||||
from ..graph.location import locationsDict
|
from ..graph.location import locationsDict
|
||||||
@@ -136,7 +135,8 @@ class GraphUtils:
|
|||||||
refused[apName] = cause
|
refused[apName] = cause
|
||||||
return ret, refused
|
return ret, refused
|
||||||
|
|
||||||
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs):
|
@staticmethod
|
||||||
|
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs, random):
|
||||||
locs = locationsDict
|
locs = locationsDict
|
||||||
preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)]
|
preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)]
|
||||||
possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs]
|
possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs]
|
||||||
@@ -160,7 +160,8 @@ class GraphUtils:
|
|||||||
ap = getAccessPoint(startApName)
|
ap = getAccessPoint(startApName)
|
||||||
return ap.Start['patches'] if 'patches' in ap.Start else []
|
return ap.Start['patches'] if 'patches' in ap.Start else []
|
||||||
|
|
||||||
def createBossesTransitions():
|
@staticmethod
|
||||||
|
def createBossesTransitions(random):
|
||||||
transitions = vanillaBossesTransitions
|
transitions = vanillaBossesTransitions
|
||||||
def isVanilla():
|
def isVanilla():
|
||||||
for t in vanillaBossesTransitions:
|
for t in vanillaBossesTransitions:
|
||||||
@@ -180,13 +181,15 @@ class GraphUtils:
|
|||||||
transitions.append((src,dst))
|
transitions.append((src,dst))
|
||||||
return transitions
|
return transitions
|
||||||
|
|
||||||
def createAreaTransitions(lightAreaRando=False):
|
@staticmethod
|
||||||
|
def createAreaTransitions(lightAreaRando=False, *, random):
|
||||||
if lightAreaRando:
|
if lightAreaRando:
|
||||||
return GraphUtils.createLightAreaTransitions()
|
return GraphUtils.createLightAreaTransitions(random=random)
|
||||||
else:
|
else:
|
||||||
return GraphUtils.createRegularAreaTransitions()
|
return GraphUtils.createRegularAreaTransitions(random=random)
|
||||||
|
|
||||||
def createRegularAreaTransitions(apList=None, apPred=None):
|
@staticmethod
|
||||||
|
def createRegularAreaTransitions(apList=None, apPred=None, *, random):
|
||||||
if apList is None:
|
if apList is None:
|
||||||
apList = Logic.accessPoints
|
apList = Logic.accessPoints
|
||||||
if apPred is None:
|
if apPred is None:
|
||||||
@@ -239,7 +242,8 @@ class GraphUtils:
|
|||||||
transitions.append((ap.Name, ap.Name))
|
transitions.append((ap.Name, ap.Name))
|
||||||
|
|
||||||
# crateria can be forced in corner cases
|
# crateria can be forced in corner cases
|
||||||
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None):
|
@staticmethod
|
||||||
|
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None, *, random):
|
||||||
if forcedAreas is None:
|
if forcedAreas is None:
|
||||||
forcedAreas = []
|
forcedAreas = []
|
||||||
if startApName == 'Ceres':
|
if startApName == 'Ceres':
|
||||||
@@ -316,7 +320,8 @@ class GraphUtils:
|
|||||||
GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas))
|
GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas))
|
||||||
return transitions
|
return transitions
|
||||||
|
|
||||||
def createLightAreaTransitions():
|
@staticmethod
|
||||||
|
def createLightAreaTransitions(random):
|
||||||
# group APs by area
|
# group APs by area
|
||||||
aps = {}
|
aps = {}
|
||||||
totalCount = 0
|
totalCount = 0
|
||||||
@@ -407,7 +412,8 @@ class GraphUtils:
|
|||||||
|
|
||||||
return rooms
|
return rooms
|
||||||
|
|
||||||
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape):
|
@staticmethod
|
||||||
|
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape, random):
|
||||||
n = len(possibleTargets)
|
n = len(possibleTargets)
|
||||||
assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets)
|
assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets)
|
||||||
GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape))
|
GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape))
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import random
|
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..utils.utils import getRangeDict, chooseFromRange
|
from ..utils.utils import getRangeDict, chooseFromRange
|
||||||
from ..rando.ItemLocContainer import ItemLocation
|
from ..rando.ItemLocContainer import ItemLocation
|
||||||
@@ -23,8 +22,9 @@ class Choice(object):
|
|||||||
|
|
||||||
# simple random choice, that chooses an item first, then a locatio to put it in
|
# simple random choice, that chooses an item first, then a locatio to put it in
|
||||||
class ItemThenLocChoice(Choice):
|
class ItemThenLocChoice(Choice):
|
||||||
def __init__(self, restrictions):
|
def __init__(self, restrictions, random):
|
||||||
super(ItemThenLocChoice, self).__init__(restrictions)
|
super(ItemThenLocChoice, self).__init__(restrictions)
|
||||||
|
self.random = random
|
||||||
|
|
||||||
def chooseItemLoc(self, itemLocDict, isProg):
|
def chooseItemLoc(self, itemLocDict, isProg):
|
||||||
itemList = self.getItemList(itemLocDict)
|
itemList = self.getItemList(itemLocDict)
|
||||||
@@ -49,7 +49,7 @@ class ItemThenLocChoice(Choice):
|
|||||||
return self.chooseItemRandom(itemList)
|
return self.chooseItemRandom(itemList)
|
||||||
|
|
||||||
def chooseItemRandom(self, itemList):
|
def chooseItemRandom(self, itemList):
|
||||||
return random.choice(itemList)
|
return self.random.choice(itemList)
|
||||||
|
|
||||||
def chooseLocation(self, locList, item, isProg):
|
def chooseLocation(self, locList, item, isProg):
|
||||||
if len(locList) == 0:
|
if len(locList) == 0:
|
||||||
@@ -63,12 +63,12 @@ class ItemThenLocChoice(Choice):
|
|||||||
return self.chooseLocationRandom(locList)
|
return self.chooseLocationRandom(locList)
|
||||||
|
|
||||||
def chooseLocationRandom(self, locList):
|
def chooseLocationRandom(self, locList):
|
||||||
return random.choice(locList)
|
return self.random.choice(locList)
|
||||||
|
|
||||||
# Choice specialization for prog speed based filler
|
# Choice specialization for prog speed based filler
|
||||||
class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
||||||
def __init__(self, restrictions, progSpeedParams, distanceProp, services):
|
def __init__(self, restrictions, progSpeedParams, distanceProp, services, random):
|
||||||
super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions)
|
super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions, random)
|
||||||
self.progSpeedParams = progSpeedParams
|
self.progSpeedParams = progSpeedParams
|
||||||
self.distanceProp = distanceProp
|
self.distanceProp = distanceProp
|
||||||
self.services = services
|
self.services = services
|
||||||
@@ -104,7 +104,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
|||||||
if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1:
|
if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1:
|
||||||
item, locList = list(itemLocDict.items())[0]
|
item, locList = list(itemLocDict.items())[0]
|
||||||
if item.Type == 'Morph':
|
if item.Type == 'Morph':
|
||||||
morphLocs = self.restrictions.lateMorphCheck(container, locList)
|
morphLocs = self.restrictions.lateMorphCheck(container, locList, self.random)
|
||||||
if morphLocs is not None:
|
if morphLocs is not None:
|
||||||
itemLocDict[item] = morphLocs
|
itemLocDict[item] = morphLocs
|
||||||
else:
|
else:
|
||||||
@@ -115,7 +115,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
|||||||
assert len(locs) == 1 and locs[0].Name == item.Name
|
assert len(locs) == 1 and locs[0].Name == item.Name
|
||||||
return ItemLocation(item, locs[0])
|
return ItemLocation(item, locs[0])
|
||||||
# late doors check for random door colors
|
# late doors check for random door colors
|
||||||
if self.restrictions.isLateDoors() and random.random() < self.lateDoorsProb:
|
if self.restrictions.isLateDoors() and self.random.random() < self.lateDoorsProb:
|
||||||
self.processLateDoors(itemLocDict, ap, container)
|
self.processLateDoors(itemLocDict, ap, container)
|
||||||
self.progressionItemLocs = progressionItemLocs
|
self.progressionItemLocs = progressionItemLocs
|
||||||
self.ap = ap
|
self.ap = ap
|
||||||
@@ -145,14 +145,14 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
|||||||
|
|
||||||
def chooseLocationProg(self, locs, item):
|
def chooseLocationProg(self, locs, item):
|
||||||
locs = self.getLocsSpreadProgression(locs)
|
locs = self.getLocsSpreadProgression(locs)
|
||||||
random.shuffle(locs)
|
self.random.shuffle(locs)
|
||||||
ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs)
|
ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs)
|
||||||
self.log.debug('chooseLocationProg. ret='+ret.Name)
|
self.log.debug('chooseLocationProg. ret='+ret.Name)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# get choose function from a weighted dict
|
# get choose function from a weighted dict
|
||||||
def getChooseFunc(self, rangeDict, funcDict):
|
def getChooseFunc(self, rangeDict, funcDict):
|
||||||
v = chooseFromRange(rangeDict)
|
v = chooseFromRange(rangeDict, self.random)
|
||||||
|
|
||||||
return funcDict[v]
|
return funcDict[v]
|
||||||
|
|
||||||
@@ -209,6 +209,6 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
|
|||||||
for i in range(len(availableLocations)):
|
for i in range(len(availableLocations)):
|
||||||
loc = availableLocations[i]
|
loc = availableLocations[i]
|
||||||
d = distances[i]
|
d = distances[i]
|
||||||
if d == maxDist or random.random() >= self.spreadProb:
|
if d == maxDist or self.random.random() >= self.spreadProb:
|
||||||
locs.append(loc)
|
locs.append(loc)
|
||||||
return locs
|
return locs
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import copy, time, random
|
import copy, time
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..logic.cache import RequestCache
|
from ..logic.cache import RequestCache
|
||||||
from ..rando.RandoServices import RandoServices
|
from ..rando.RandoServices import RandoServices
|
||||||
@@ -15,11 +15,11 @@ from ..graph.graph_utils import GraphUtils
|
|||||||
# item pool is not empty).
|
# item pool is not empty).
|
||||||
# entry point is generateItems
|
# entry point is generateItems
|
||||||
class Filler(object):
|
class Filler(object):
|
||||||
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
|
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
|
||||||
self.startAP = startAP
|
self.startAP = startAP
|
||||||
self.cache = RequestCache()
|
self.cache = RequestCache()
|
||||||
self.graph = graph
|
self.graph = graph
|
||||||
self.services = RandoServices(graph, restrictions, self.cache)
|
self.services = RandoServices(graph, restrictions, self.cache, random=random)
|
||||||
self.restrictions = restrictions
|
self.restrictions = restrictions
|
||||||
self.settings = restrictions.settings
|
self.settings = restrictions.settings
|
||||||
self.endDate = endDate
|
self.endDate = endDate
|
||||||
@@ -108,9 +108,9 @@ class Filler(object):
|
|||||||
|
|
||||||
# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm)
|
# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm)
|
||||||
class FrontFiller(Filler):
|
class FrontFiller(Filler):
|
||||||
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
|
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
|
||||||
super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate)
|
super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate, random=random)
|
||||||
self.choice = ItemThenLocChoice(restrictions)
|
self.choice = ItemThenLocChoice(restrictions, random)
|
||||||
self.stdStart = GraphUtils.isStandardStart(self.startAP)
|
self.stdStart = GraphUtils.isStandardStart(self.startAP)
|
||||||
|
|
||||||
def isEarlyGame(self):
|
def isEarlyGame(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import random, copy
|
import copy
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint
|
from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint
|
||||||
from ..logic.logic import Logic
|
from ..logic.logic import Logic
|
||||||
@@ -11,13 +11,14 @@ from collections import defaultdict
|
|||||||
|
|
||||||
# creates graph and handles randomized escape
|
# creates graph and handles randomized escape
|
||||||
class GraphBuilder(object):
|
class GraphBuilder(object):
|
||||||
def __init__(self, graphSettings):
|
def __init__(self, graphSettings, random):
|
||||||
self.graphSettings = graphSettings
|
self.graphSettings = graphSettings
|
||||||
self.areaRando = graphSettings.areaRando
|
self.areaRando = graphSettings.areaRando
|
||||||
self.bossRando = graphSettings.bossRando
|
self.bossRando = graphSettings.bossRando
|
||||||
self.escapeRando = graphSettings.escapeRando
|
self.escapeRando = graphSettings.escapeRando
|
||||||
self.minimizerN = graphSettings.minimizerN
|
self.minimizerN = graphSettings.minimizerN
|
||||||
self.log = log.get('GraphBuilder')
|
self.log = log.get('GraphBuilder')
|
||||||
|
self.random = random
|
||||||
|
|
||||||
# builds everything but escape transitions
|
# builds everything but escape transitions
|
||||||
def createGraph(self, maxDiff):
|
def createGraph(self, maxDiff):
|
||||||
@@ -48,18 +49,18 @@ class GraphBuilder(object):
|
|||||||
objForced = forcedAreas.intersection(escAreas)
|
objForced = forcedAreas.intersection(escAreas)
|
||||||
escAreasList = sorted(list(escAreas))
|
escAreasList = sorted(list(escAreas))
|
||||||
while len(objForced) < n and len(escAreasList) > 0:
|
while len(objForced) < n and len(escAreasList) > 0:
|
||||||
objForced.add(escAreasList.pop(random.randint(0, len(escAreasList)-1)))
|
objForced.add(escAreasList.pop(self.random.randint(0, len(escAreasList)-1)))
|
||||||
forcedAreas = forcedAreas.union(objForced)
|
forcedAreas = forcedAreas.union(objForced)
|
||||||
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas)))
|
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas)), random=self.random)
|
||||||
else:
|
else:
|
||||||
if not self.bossRando:
|
if not self.bossRando:
|
||||||
transitions += vanillaBossesTransitions
|
transitions += vanillaBossesTransitions
|
||||||
else:
|
else:
|
||||||
transitions += GraphUtils.createBossesTransitions()
|
transitions += GraphUtils.createBossesTransitions(self.random)
|
||||||
if not self.areaRando:
|
if not self.areaRando:
|
||||||
transitions += vanillaTransitions
|
transitions += vanillaTransitions
|
||||||
else:
|
else:
|
||||||
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando)
|
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando, random=self.random)
|
||||||
ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
|
ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
|
||||||
Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff)
|
Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff)
|
||||||
return ret
|
return ret
|
||||||
@@ -100,7 +101,7 @@ class GraphBuilder(object):
|
|||||||
self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None)
|
self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None)
|
||||||
self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer']))
|
self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer']))
|
||||||
# animals
|
# animals
|
||||||
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst)
|
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst, self.random)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _getTargets(self, sm, graph, maxDiff):
|
def _getTargets(self, sm, graph, maxDiff):
|
||||||
@@ -110,7 +111,7 @@ class GraphBuilder(object):
|
|||||||
if len(possibleTargets) == 0:
|
if len(possibleTargets) == 0:
|
||||||
self.log.debug("Can't randomize escape, fallback to vanilla")
|
self.log.debug("Can't randomize escape, fallback to vanilla")
|
||||||
possibleTargets.append('Climb Bottom Left')
|
possibleTargets.append('Climb Bottom Left')
|
||||||
random.shuffle(possibleTargets)
|
self.random.shuffle(possibleTargets)
|
||||||
return possibleTargets
|
return possibleTargets
|
||||||
|
|
||||||
def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff):
|
def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange
|
from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
import logging, copy, random
|
import logging, copy
|
||||||
|
|
||||||
class Item:
|
class Item:
|
||||||
__slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' )
|
__slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' )
|
||||||
@@ -335,7 +335,7 @@ class ItemManager:
|
|||||||
itemCode = item.Code + modifier
|
itemCode = item.Code + modifier
|
||||||
return itemCode
|
return itemCode
|
||||||
|
|
||||||
def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff):
|
def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff, random):
|
||||||
self.qty = qty
|
self.qty = qty
|
||||||
self.sm = sm
|
self.sm = sm
|
||||||
self.majorsSplit = majorsSplit
|
self.majorsSplit = majorsSplit
|
||||||
@@ -344,6 +344,7 @@ class ItemManager:
|
|||||||
self.maxDiff = maxDiff
|
self.maxDiff = maxDiff
|
||||||
self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major'
|
self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major'
|
||||||
self.itemPool = []
|
self.itemPool = []
|
||||||
|
self.random = random
|
||||||
|
|
||||||
def newItemPool(self, addBosses=True):
|
def newItemPool(self, addBosses=True):
|
||||||
self.itemPool = []
|
self.itemPool = []
|
||||||
@@ -386,7 +387,7 @@ class ItemManager:
|
|||||||
return ItemManager.Items[itemType].withClass(itemClass)
|
return ItemManager.Items[itemType].withClass(itemClass)
|
||||||
|
|
||||||
def createItemPool(self, exclude=None):
|
def createItemPool(self, exclude=None):
|
||||||
itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff)
|
itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff, self.random)
|
||||||
self.itemPool = itemPoolGenerator.getItemPool()
|
self.itemPool = itemPoolGenerator.getItemPool()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -402,20 +403,20 @@ class ItemPoolGenerator(object):
|
|||||||
nbBosses = 9
|
nbBosses = 9
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff):
|
def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
|
||||||
if majorsSplit == 'Chozo':
|
if majorsSplit == 'Chozo':
|
||||||
return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff)
|
return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff, random)
|
||||||
elif majorsSplit == 'Plando':
|
elif majorsSplit == 'Plando':
|
||||||
return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff)
|
return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff, random)
|
||||||
elif nLocs == ItemPoolGenerator.maxLocs:
|
elif nLocs == ItemPoolGenerator.maxLocs:
|
||||||
if majorsSplit == "Scavenger":
|
if majorsSplit == "Scavenger":
|
||||||
return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff)
|
return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff, random)
|
||||||
else:
|
else:
|
||||||
return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff)
|
return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff, random)
|
||||||
else:
|
else:
|
||||||
return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff)
|
return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff, random)
|
||||||
|
|
||||||
def __init__(self, itemManager, qty, sm, maxDiff):
|
def __init__(self, itemManager, qty, sm, maxDiff, random):
|
||||||
self.itemManager = itemManager
|
self.itemManager = itemManager
|
||||||
self.qty = qty
|
self.qty = qty
|
||||||
self.sm = sm
|
self.sm = sm
|
||||||
@@ -423,12 +424,13 @@ class ItemPoolGenerator(object):
|
|||||||
self.maxEnergy = 18 # 14E, 4R
|
self.maxEnergy = 18 # 14E, 4R
|
||||||
self.maxDiff = maxDiff
|
self.maxDiff = maxDiff
|
||||||
self.log = log.get('ItemPool')
|
self.log = log.get('ItemPool')
|
||||||
|
self.random = random
|
||||||
|
|
||||||
def isUltraSparseNoTanks(self):
|
def isUltraSparseNoTanks(self):
|
||||||
# if low stuff botwoon is not known there is a hard energy req of one tank, even
|
# if low stuff botwoon is not known there is a hard energy req of one tank, even
|
||||||
# with both suits
|
# with both suits
|
||||||
lowStuffBotwoon = self.sm.knowsLowStuffBotwoon()
|
lowStuffBotwoon = self.sm.knowsLowStuffBotwoon()
|
||||||
return random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
|
return self.random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
|
||||||
|
|
||||||
def calcMaxMinors(self):
|
def calcMaxMinors(self):
|
||||||
pool = self.itemManager.getItemPool()
|
pool = self.itemManager.getItemPool()
|
||||||
@@ -464,7 +466,7 @@ class ItemPoolGenerator(object):
|
|||||||
rangeDict = getRangeDict(ammoQty)
|
rangeDict = getRangeDict(ammoQty)
|
||||||
self.log.debug("rangeDict: {}".format(rangeDict))
|
self.log.debug("rangeDict: {}".format(rangeDict))
|
||||||
while len(self.itemManager.getItemPool()) < maxItems:
|
while len(self.itemManager.getItemPool()) < maxItems:
|
||||||
item = chooseFromRange(rangeDict)
|
item = chooseFromRange(rangeDict, self.random)
|
||||||
self.itemManager.addMinor(item)
|
self.itemManager.addMinor(item)
|
||||||
else:
|
else:
|
||||||
minorsTypes = ['Missile', 'Super', 'PowerBomb']
|
minorsTypes = ['Missile', 'Super', 'PowerBomb']
|
||||||
@@ -522,7 +524,7 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
|
|||||||
# no etank nor reserve
|
# no etank nor reserve
|
||||||
self.itemManager.removeItem('ETank')
|
self.itemManager.removeItem('ETank')
|
||||||
self.itemManager.addItem('NoEnergy', 'Chozo')
|
self.itemManager.addItem('NoEnergy', 'Chozo')
|
||||||
elif random.random() < 0.5:
|
elif self.random.random() < 0.5:
|
||||||
# replace only etank with reserve
|
# replace only etank with reserve
|
||||||
self.itemManager.removeItem('ETank')
|
self.itemManager.removeItem('ETank')
|
||||||
self.itemManager.addItem('Reserve', 'Chozo')
|
self.itemManager.addItem('Reserve', 'Chozo')
|
||||||
@@ -535,9 +537,9 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
|
|||||||
# 4-6
|
# 4-6
|
||||||
# already 3E and 1R
|
# already 3E and 1R
|
||||||
alreadyInPool = 4
|
alreadyInPool = 4
|
||||||
rest = randGaussBounds(2, 5)
|
rest = randGaussBounds(self.random, 2, 5)
|
||||||
if rest >= 1:
|
if rest >= 1:
|
||||||
if random.random() < 0.5:
|
if self.random.random() < 0.5:
|
||||||
self.itemManager.addItem('Reserve', 'Minor')
|
self.itemManager.addItem('Reserve', 'Minor')
|
||||||
else:
|
else:
|
||||||
self.itemManager.addItem('ETank', 'Minor')
|
self.itemManager.addItem('ETank', 'Minor')
|
||||||
@@ -550,13 +552,13 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
|
|||||||
# 8-12
|
# 8-12
|
||||||
# add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
|
# add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
if random.random() < 0.5:
|
if self.random.random() < 0.5:
|
||||||
self.itemManager.addItem('Reserve', 'Minor')
|
self.itemManager.addItem('Reserve', 'Minor')
|
||||||
else:
|
else:
|
||||||
self.itemManager.addItem('ETank', 'Minor')
|
self.itemManager.addItem('ETank', 'Minor')
|
||||||
# 7 already in the pool (3 E, 1 R, + the previous 3)
|
# 7 already in the pool (3 E, 1 R, + the previous 3)
|
||||||
alreadyInPool = 7
|
alreadyInPool = 7
|
||||||
rest = 1 + randGaussBounds(4, 3.7)
|
rest = 1 + randGaussBounds(self.random, 4, 3.7)
|
||||||
for i in range(rest):
|
for i in range(rest):
|
||||||
self.itemManager.addItem('ETank', 'Minor')
|
self.itemManager.addItem('ETank', 'Minor')
|
||||||
# fill the rest with NoEnergy
|
# fill the rest with NoEnergy
|
||||||
@@ -581,10 +583,10 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
|
|||||||
return self.itemManager.getItemPool()
|
return self.itemManager.getItemPool()
|
||||||
|
|
||||||
class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
||||||
def __init__(self, itemManager, qty, sm, maxDiff):
|
def __init__(self, itemManager, qty, sm, maxDiff, random):
|
||||||
super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff)
|
super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff, random)
|
||||||
self.sparseRest = 1 + randGaussBounds(2, 5)
|
self.sparseRest = 1 + randGaussBounds(self.random,2, 5)
|
||||||
self.mediumRest = 3 + randGaussBounds(4, 3.7)
|
self.mediumRest = 3 + randGaussBounds(self.random, 4, 3.7)
|
||||||
self.ultraSparseNoTanks = self.isUltraSparseNoTanks()
|
self.ultraSparseNoTanks = self.isUltraSparseNoTanks()
|
||||||
|
|
||||||
def addNoEnergy(self):
|
def addNoEnergy(self):
|
||||||
@@ -609,7 +611,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
|||||||
# no energy at all
|
# no energy at all
|
||||||
self.addNoEnergy()
|
self.addNoEnergy()
|
||||||
else:
|
else:
|
||||||
if random.random() < 0.5:
|
if self.random.random() < 0.5:
|
||||||
self.itemManager.addItem('ETank')
|
self.itemManager.addItem('ETank')
|
||||||
else:
|
else:
|
||||||
self.itemManager.addItem('Reserve')
|
self.itemManager.addItem('Reserve')
|
||||||
@@ -620,7 +622,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
|||||||
|
|
||||||
elif energyQty == 'sparse':
|
elif energyQty == 'sparse':
|
||||||
# 4-6
|
# 4-6
|
||||||
if random.random() < 0.5:
|
if self.random.random() < 0.5:
|
||||||
self.itemManager.addItem('Reserve')
|
self.itemManager.addItem('Reserve')
|
||||||
else:
|
else:
|
||||||
self.itemManager.addItem('ETank')
|
self.itemManager.addItem('ETank')
|
||||||
@@ -639,7 +641,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
|||||||
alreadyInPool = 2
|
alreadyInPool = 2
|
||||||
n = getE(3)
|
n = getE(3)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
if random.random() < 0.5:
|
if self.random.random() < 0.5:
|
||||||
self.itemManager.addItem('Reserve')
|
self.itemManager.addItem('Reserve')
|
||||||
else:
|
else:
|
||||||
self.itemManager.addItem('ETank')
|
self.itemManager.addItem('ETank')
|
||||||
@@ -676,15 +678,15 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
|
|||||||
return self.itemManager.getItemPool()
|
return self.itemManager.getItemPool()
|
||||||
|
|
||||||
class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors):
|
class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors):
|
||||||
def __init__(self, itemManager, qty, sm, maxDiff):
|
def __init__(self, itemManager, qty, sm, maxDiff, random):
|
||||||
super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff)
|
super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff, random)
|
||||||
|
|
||||||
def addNoEnergy(self):
|
def addNoEnergy(self):
|
||||||
self.itemManager.addItem('Nothing')
|
self.itemManager.addItem('Nothing')
|
||||||
|
|
||||||
class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
|
class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
|
||||||
def __init__(self, itemManager, qty, sm, nLocs, maxDiff):
|
def __init__(self, itemManager, qty, sm, nLocs, maxDiff, random):
|
||||||
super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff)
|
super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff, random)
|
||||||
self.maxItems = nLocs
|
self.maxItems = nLocs
|
||||||
self.calcMaxAmmo()
|
self.calcMaxAmmo()
|
||||||
nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy'])
|
nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy'])
|
||||||
@@ -716,8 +718,8 @@ class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
|
|||||||
self.log.debug("maxEnergy: "+str(self.maxEnergy))
|
self.log.debug("maxEnergy: "+str(self.maxEnergy))
|
||||||
|
|
||||||
class ItemPoolGeneratorPlando(ItemPoolGenerator):
|
class ItemPoolGeneratorPlando(ItemPoolGenerator):
|
||||||
def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff):
|
def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
|
||||||
super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff)
|
super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff, random)
|
||||||
# in exclude dict:
|
# in exclude dict:
|
||||||
# in alreadyPlacedItems:
|
# in alreadyPlacedItems:
|
||||||
# dict of 'itemType: count' of items already added in the plando.
|
# dict of 'itemType: count' of items already added in the plando.
|
||||||
@@ -805,7 +807,7 @@ class ItemPoolGeneratorPlando(ItemPoolGenerator):
|
|||||||
if ammoQty:
|
if ammoQty:
|
||||||
rangeDict = getRangeDict(ammoQty)
|
rangeDict = getRangeDict(ammoQty)
|
||||||
while len(self.itemManager.getItemPool()) < maxItems and remain > 0:
|
while len(self.itemManager.getItemPool()) < maxItems and remain > 0:
|
||||||
item = chooseFromRange(rangeDict)
|
item = chooseFromRange(rangeDict, self.random)
|
||||||
self.itemManager.addMinor(item)
|
self.itemManager.addMinor(item)
|
||||||
remain -= 1
|
remain -= 1
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import sys, random, time
|
import sys, time
|
||||||
|
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..logic.logic import Logic
|
from ..logic.logic import Logic
|
||||||
@@ -14,7 +14,7 @@ from ..utils.doorsmanager import DoorsManager
|
|||||||
|
|
||||||
# entry point for rando execution ("randomize" method)
|
# entry point for rando execution ("randomize" method)
|
||||||
class RandoExec(object):
|
class RandoExec(object):
|
||||||
def __init__(self, seedName, vcr, randoSettings, graphSettings, player):
|
def __init__(self, seedName, vcr, randoSettings, graphSettings, player, random):
|
||||||
self.errorMsg = ""
|
self.errorMsg = ""
|
||||||
self.seedName = seedName
|
self.seedName = seedName
|
||||||
self.vcr = vcr
|
self.vcr = vcr
|
||||||
@@ -22,6 +22,7 @@ class RandoExec(object):
|
|||||||
self.graphSettings = graphSettings
|
self.graphSettings = graphSettings
|
||||||
self.log = log.get('RandoExec')
|
self.log = log.get('RandoExec')
|
||||||
self.player = player
|
self.player = player
|
||||||
|
self.random = random
|
||||||
|
|
||||||
# processes settings to :
|
# processes settings to :
|
||||||
# - create Restrictions and GraphBuilder objects
|
# - create Restrictions and GraphBuilder objects
|
||||||
@@ -31,7 +32,7 @@ class RandoExec(object):
|
|||||||
vcr = VCR(self.seedName, 'rando') if self.vcr == True else None
|
vcr = VCR(self.seedName, 'rando') if self.vcr == True else None
|
||||||
self.errorMsg = ""
|
self.errorMsg = ""
|
||||||
split = self.randoSettings.restrictions['MajorMinor']
|
split = self.randoSettings.restrictions['MajorMinor']
|
||||||
self.graphBuilder = GraphBuilder(self.graphSettings)
|
self.graphBuilder = GraphBuilder(self.graphSettings, self.random)
|
||||||
container = None
|
container = None
|
||||||
i = 0
|
i = 0
|
||||||
attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1
|
attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1
|
||||||
@@ -43,10 +44,10 @@ class RandoExec(object):
|
|||||||
while container is None and i < attempts and now <= endDate:
|
while container is None and i < attempts and now <= endDate:
|
||||||
self.restrictions = Restrictions(self.randoSettings)
|
self.restrictions = Restrictions(self.randoSettings)
|
||||||
if self.graphSettings.doorsColorsRando == True:
|
if self.graphSettings.doorsColorsRando == True:
|
||||||
DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player)
|
DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player, self.random)
|
||||||
self.areaGraph = self.graphBuilder.createGraph(self.randoSettings.maxDiff)
|
self.areaGraph = self.graphBuilder.createGraph(self.randoSettings.maxDiff)
|
||||||
services = RandoServices(self.areaGraph, self.restrictions)
|
services = RandoServices(self.areaGraph, self.restrictions, random=self.random)
|
||||||
setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player)
|
setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player, self.random)
|
||||||
self.setup = setup
|
self.setup = setup
|
||||||
container = setup.createItemLocContainer(endDate, vcr)
|
container = setup.createItemLocContainer(endDate, vcr)
|
||||||
if container is None:
|
if container is None:
|
||||||
@@ -78,7 +79,7 @@ class RandoExec(object):
|
|||||||
n = nMaj
|
n = nMaj
|
||||||
elif split == 'Chozo':
|
elif split == 'Chozo':
|
||||||
n = nChozo
|
n = nChozo
|
||||||
GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n)
|
GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n, self.random)
|
||||||
|
|
||||||
def postProcessItemLocs(self, itemLocs, hide):
|
def postProcessItemLocs(self, itemLocs, hide):
|
||||||
# hide some items like in dessy's
|
# hide some items like in dessy's
|
||||||
@@ -89,7 +90,7 @@ class RandoExec(object):
|
|||||||
if (item.Category != "Nothing"
|
if (item.Category != "Nothing"
|
||||||
and loc.CanHidden == True
|
and loc.CanHidden == True
|
||||||
and loc.Visibility == 'Visible'):
|
and loc.Visibility == 'Visible'):
|
||||||
if bool(random.getrandbits(1)) == True:
|
if bool(self.random.getrandbits(1)) == True:
|
||||||
loc.Visibility = 'Hidden'
|
loc.Visibility = 'Hidden'
|
||||||
# put nothing in unfilled locations
|
# put nothing in unfilled locations
|
||||||
filledLocNames = [il.Location.Name for il in itemLocs]
|
filledLocNames = [il.Location.Name for il in itemLocs]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
import copy, sys, logging, os
|
||||||
import copy, random, sys, logging, os
|
|
||||||
from enum import Enum, unique
|
from enum import Enum, unique
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..utils.parameters import infinity
|
from ..utils.parameters import infinity
|
||||||
@@ -19,12 +18,13 @@ class ComebackCheckType(Enum):
|
|||||||
|
|
||||||
# collection of stateless services to be used mainly by fillers
|
# collection of stateless services to be used mainly by fillers
|
||||||
class RandoServices(object):
|
class RandoServices(object):
|
||||||
def __init__(self, graph, restrictions, cache=None):
|
def __init__(self, graph, restrictions, cache=None, *, random):
|
||||||
self.restrictions = restrictions
|
self.restrictions = restrictions
|
||||||
self.settings = restrictions.settings
|
self.settings = restrictions.settings
|
||||||
self.areaGraph = graph
|
self.areaGraph = graph
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
self.log = log.get('RandoServices')
|
self.log = log.get('RandoServices')
|
||||||
|
self.random = random
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def printProgress(s):
|
def printProgress(s):
|
||||||
@@ -217,7 +217,7 @@ class RandoServices(object):
|
|||||||
# choose a morph item location in that context
|
# choose a morph item location in that context
|
||||||
morphItemLoc = ItemLocation(
|
morphItemLoc = ItemLocation(
|
||||||
morph,
|
morph,
|
||||||
random.choice(morphLocs)
|
self.random.choice(morphLocs)
|
||||||
)
|
)
|
||||||
# acquire morph in new context and see if we can still open new locs
|
# acquire morph in new context and see if we can still open new locs
|
||||||
newAP = self.collect(ap, containerCpy, morphItemLoc)
|
newAP = self.collect(ap, containerCpy, morphItemLoc)
|
||||||
@@ -232,7 +232,7 @@ class RandoServices(object):
|
|||||||
if morphLocItem is None or len(itemLocDict) == 1:
|
if morphLocItem is None or len(itemLocDict) == 1:
|
||||||
# no morph, or it is the only possibility: nothing to do
|
# no morph, or it is the only possibility: nothing to do
|
||||||
return
|
return
|
||||||
morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem])
|
morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem], self.random)
|
||||||
if morphLocs is not None:
|
if morphLocs is not None:
|
||||||
itemLocDict[morphLocItem] = morphLocs
|
itemLocDict[morphLocItem] = morphLocs
|
||||||
else:
|
else:
|
||||||
@@ -380,10 +380,10 @@ class RandoServices(object):
|
|||||||
(itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck)
|
(itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck)
|
||||||
assert not isProg
|
assert not isProg
|
||||||
items = list(itemLocDict.keys())
|
items = list(itemLocDict.keys())
|
||||||
random.shuffle(items)
|
self.random.shuffle(items)
|
||||||
for item in items:
|
for item in items:
|
||||||
cont = copy.copy(container)
|
cont = copy.copy(container)
|
||||||
loc = random.choice(itemLocDict[item])
|
loc = self.random.choice(itemLocDict[item])
|
||||||
itemLoc1 = ItemLocation(item, loc)
|
itemLoc1 = ItemLocation(item, loc)
|
||||||
self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1))
|
self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1))
|
||||||
newAP = self.collect(ap, cont, itemLoc1)
|
newAP = self.collect(ap, cont, itemLoc1)
|
||||||
@@ -391,8 +391,8 @@ class RandoServices(object):
|
|||||||
self.cache.reset()
|
self.cache.reset()
|
||||||
(ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck)
|
(ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck)
|
||||||
if isProg:
|
if isProg:
|
||||||
item2 = random.choice(list(ild.keys()))
|
item2 = self.random.choice(list(ild.keys()))
|
||||||
itemLoc2 = ItemLocation(item2, random.choice(ild[item2]))
|
itemLoc2 = ItemLocation(item2, self.random.choice(ild[item2]))
|
||||||
self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2))
|
self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2))
|
||||||
return (itemLoc1, itemLoc2)
|
return (itemLoc1, itemLoc2)
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
import sys
|
||||||
import sys, random
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from ..rando.Items import ItemManager
|
from ..rando.Items import ItemManager
|
||||||
from ..utils.utils import getRangeDict, chooseFromRange
|
from ..utils.utils import getRangeDict, chooseFromRange
|
||||||
@@ -32,11 +31,11 @@ class RandoSettings(object):
|
|||||||
def isPlandoRando(self):
|
def isPlandoRando(self):
|
||||||
return self.PlandoOptions is not None
|
return self.PlandoOptions is not None
|
||||||
|
|
||||||
def getItemManager(self, smbm, nLocs, bossesItems):
|
def getItemManager(self, smbm, nLocs, bossesItems, random):
|
||||||
if not self.isPlandoRando():
|
if not self.isPlandoRando():
|
||||||
return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff)
|
return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
|
||||||
else:
|
else:
|
||||||
return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff)
|
return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
|
||||||
|
|
||||||
def getExcludeItems(self, locations):
|
def getExcludeItems(self, locations):
|
||||||
if not self.isPlandoRando():
|
if not self.isPlandoRando():
|
||||||
@@ -94,7 +93,7 @@ class ProgSpeedParameters(object):
|
|||||||
self.restrictions = restrictions
|
self.restrictions = restrictions
|
||||||
self.nLocs = nLocs
|
self.nLocs = nLocs
|
||||||
|
|
||||||
def getVariableSpeed(self):
|
def getVariableSpeed(self, random):
|
||||||
ranges = getRangeDict({
|
ranges = getRangeDict({
|
||||||
'slowest':7,
|
'slowest':7,
|
||||||
'slow':20,
|
'slow':20,
|
||||||
@@ -102,7 +101,7 @@ class ProgSpeedParameters(object):
|
|||||||
'fast':27,
|
'fast':27,
|
||||||
'fastest':11
|
'fastest':11
|
||||||
})
|
})
|
||||||
return chooseFromRange(ranges)
|
return chooseFromRange(ranges, random)
|
||||||
|
|
||||||
def getMinorHelpProb(self, progSpeed):
|
def getMinorHelpProb(self, progSpeed):
|
||||||
if self.restrictions.split != 'Major':
|
if self.restrictions.split != 'Major':
|
||||||
@@ -134,7 +133,7 @@ class ProgSpeedParameters(object):
|
|||||||
def isSlow(self, progSpeed):
|
def isSlow(self, progSpeed):
|
||||||
return progSpeed == "slow" or (progSpeed == "slowest" and self.restrictions.split == "Chozo")
|
return progSpeed == "slow" or (progSpeed == "slowest" and self.restrictions.split == "Chozo")
|
||||||
|
|
||||||
def getItemLimit(self, progSpeed):
|
def getItemLimit(self, progSpeed, random):
|
||||||
itemLimit = self.nLocs
|
itemLimit = self.nLocs
|
||||||
if self.isSlow(progSpeed):
|
if self.isSlow(progSpeed):
|
||||||
itemLimit = int(self.nLocs*0.209) # 21 for 105
|
itemLimit = int(self.nLocs*0.209) # 21 for 105
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import copy, random
|
import copy
|
||||||
|
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..utils.utils import randGaussBounds
|
from ..utils.utils import randGaussBounds
|
||||||
@@ -16,8 +16,9 @@ from ..rom.rom_patches import RomPatches
|
|||||||
# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions
|
# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions
|
||||||
# the entry point is createItemLocContainer
|
# the entry point is createItemLocContainer
|
||||||
class RandoSetup(object):
|
class RandoSetup(object):
|
||||||
def __init__(self, graphSettings, locations, services, player):
|
def __init__(self, graphSettings, locations, services, player, random):
|
||||||
self.sm = SMBoolManager(player, services.settings.maxDiff)
|
self.sm = SMBoolManager(player, services.settings.maxDiff)
|
||||||
|
self.random = random
|
||||||
self.settings = services.settings
|
self.settings = services.settings
|
||||||
self.graphSettings = graphSettings
|
self.graphSettings = graphSettings
|
||||||
self.startAP = graphSettings.startAP
|
self.startAP = graphSettings.startAP
|
||||||
@@ -31,7 +32,7 @@ class RandoSetup(object):
|
|||||||
# print("nLocs Setup: "+str(len(self.locations)))
|
# print("nLocs Setup: "+str(len(self.locations)))
|
||||||
# in minimizer we can have some missing boss locs
|
# in minimizer we can have some missing boss locs
|
||||||
bossesItems = [loc.BossItemType for loc in self.locations if loc.isBoss()]
|
bossesItems = [loc.BossItemType for loc in self.locations if loc.isBoss()]
|
||||||
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems)
|
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems, random)
|
||||||
self.forbiddenItems = []
|
self.forbiddenItems = []
|
||||||
self.restrictedLocs = []
|
self.restrictedLocs = []
|
||||||
self.lastRestricted = []
|
self.lastRestricted = []
|
||||||
@@ -165,7 +166,7 @@ class RandoSetup(object):
|
|||||||
return True
|
return True
|
||||||
self.log.debug("********* PRE RANDO START")
|
self.log.debug("********* PRE RANDO START")
|
||||||
container = copy.copy(self.container)
|
container = copy.copy(self.container)
|
||||||
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container)
|
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container, random=self.random)
|
||||||
condition = filler.createStepCountCondition(4)
|
condition = filler.createStepCountCondition(4)
|
||||||
(isStuck, itemLocations, progItems) = filler.generateItems(condition)
|
(isStuck, itemLocations, progItems) = filler.generateItems(condition)
|
||||||
self.log.debug("********* PRE RANDO END")
|
self.log.debug("********* PRE RANDO END")
|
||||||
@@ -345,9 +346,9 @@ class RandoSetup(object):
|
|||||||
def getForbiddenItemsFromList(self, itemList):
|
def getForbiddenItemsFromList(self, itemList):
|
||||||
self.log.debug('getForbiddenItemsFromList: ' + str(itemList))
|
self.log.debug('getForbiddenItemsFromList: ' + str(itemList))
|
||||||
remove = []
|
remove = []
|
||||||
n = randGaussBounds(len(itemList))
|
n = randGaussBounds(self.random, len(itemList))
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
idx = random.randint(0, len(itemList) - 1)
|
idx = self.random.randint(0, len(itemList) - 1)
|
||||||
item = itemList.pop(idx)
|
item = itemList.pop(idx)
|
||||||
if item is not None:
|
if item is not None:
|
||||||
remove.append(item)
|
remove.append(item)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import copy, random
|
import copy
|
||||||
from ..utils import log
|
from ..utils import log
|
||||||
from ..graph.graph_utils import getAccessPoint
|
from ..graph.graph_utils import getAccessPoint
|
||||||
from ..rando.ItemLocContainer import getLocListStr
|
from ..rando.ItemLocContainer import getLocListStr
|
||||||
@@ -112,7 +112,7 @@ class Restrictions(object):
|
|||||||
return item.Class == "Minor"
|
return item.Class == "Minor"
|
||||||
|
|
||||||
# return True if we can keep morph as a possibility
|
# return True if we can keep morph as a possibility
|
||||||
def lateMorphCheck(self, container, possibleLocs):
|
def lateMorphCheck(self, container, possibleLocs, random):
|
||||||
# the closer we get to the limit the higher the chances of allowing morph
|
# the closer we get to the limit the higher the chances of allowing morph
|
||||||
proba = random.randint(0, self.lateMorphLimit)
|
proba = random.randint(0, self.lateMorphLimit)
|
||||||
if self.split == 'Full':
|
if self.split == 'Full':
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from Utils import output_path
|
from Utils import output_path
|
||||||
import argparse, os.path, json, sys, shutil, random, copy, requests
|
import argparse, os.path, json, sys, shutil, copy, requests
|
||||||
|
|
||||||
from .rando.RandoSettings import RandoSettings, GraphSettings
|
from .rando.RandoSettings import RandoSettings, GraphSettings
|
||||||
from .rando.RandoExec import RandoExec
|
from .rando.RandoExec import RandoExec
|
||||||
@@ -39,7 +39,7 @@ objectives = defaultMultiValues['objective']
|
|||||||
tourians = defaultMultiValues['tourian']
|
tourians = defaultMultiValues['tourian']
|
||||||
areaRandomizations = defaultMultiValues['areaRandomization']
|
areaRandomizations = defaultMultiValues['areaRandomization']
|
||||||
|
|
||||||
def randomMulti(args, param, defaultMultiValues):
|
def randomMulti(args, param, defaultMultiValues, random):
|
||||||
value = args[param]
|
value = args[param]
|
||||||
|
|
||||||
isRandom = False
|
isRandom = False
|
||||||
@@ -250,10 +250,11 @@ class VariaRandomizer:
|
|||||||
parser.add_argument('--tourianList', help="list to choose from when random",
|
parser.add_argument('--tourianList', help="list to choose from when random",
|
||||||
dest='tourianList', nargs='?', default=None)
|
dest='tourianList', nargs='?', default=None)
|
||||||
|
|
||||||
def __init__(self, options, rom, player):
|
def __init__(self, options, rom, player, seed, random):
|
||||||
# parse args
|
# parse args
|
||||||
self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values
|
self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values
|
||||||
self.player = player
|
self.player = player
|
||||||
|
self.random = random
|
||||||
args = self.args
|
args = self.args
|
||||||
args.rom = rom
|
args.rom = rom
|
||||||
# args.startLocation = to_pascal_case_with_space(options.startLocation.current_key)
|
# args.startLocation = to_pascal_case_with_space(options.startLocation.current_key)
|
||||||
@@ -323,11 +324,13 @@ class VariaRandomizer:
|
|||||||
|
|
||||||
logger.debug("preset: {}".format(preset))
|
logger.debug("preset: {}".format(preset))
|
||||||
|
|
||||||
# if no seed given, choose one
|
# Archipelago provides a seed for the multiworld.
|
||||||
if args.seed == 0:
|
self.seed = seed
|
||||||
self.seed = random.randrange(sys.maxsize)
|
# # if no seed given, choose one
|
||||||
else:
|
# if args.seed == 0:
|
||||||
self.seed = args.seed
|
# self.seed = random.randrange(sys.maxsize)
|
||||||
|
# else:
|
||||||
|
# self.seed = args.seed
|
||||||
logger.debug("seed: {}".format(self.seed))
|
logger.debug("seed: {}".format(self.seed))
|
||||||
|
|
||||||
if args.raceMagic is not None:
|
if args.raceMagic is not None:
|
||||||
@@ -360,12 +363,12 @@ class VariaRandomizer:
|
|||||||
logger.debug("maxDifficulty: {}".format(self.maxDifficulty))
|
logger.debug("maxDifficulty: {}".format(self.maxDifficulty))
|
||||||
|
|
||||||
# handle random parameters with dynamic pool of values
|
# handle random parameters with dynamic pool of values
|
||||||
(_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds)
|
(_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds, random)
|
||||||
(_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs)
|
(_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs, random)
|
||||||
(majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits)
|
(majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits, random)
|
||||||
(_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours)
|
(_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours, random)
|
||||||
(_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians)
|
(_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians, random)
|
||||||
(areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations)
|
(areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations, random)
|
||||||
areaRandomization = args.area in ['light', 'full']
|
areaRandomization = args.area in ['light', 'full']
|
||||||
lightArea = args.area == 'light'
|
lightArea = args.area == 'light'
|
||||||
|
|
||||||
@@ -626,7 +629,7 @@ class VariaRandomizer:
|
|||||||
if args.objective:
|
if args.objective:
|
||||||
if (args.objectiveRandom):
|
if (args.objectiveRandom):
|
||||||
availableObjectives = [goal for goal in objectives if goal != "collect 100% items"] if "random" in args.objectiveList else args.objectiveList
|
availableObjectives = [goal for goal in objectives if goal != "collect 100% items"] if "random" in args.objectiveList else args.objectiveList
|
||||||
self.objectivesManager.setRandom(args.nbObjective, availableObjectives)
|
self.objectivesManager.setRandom(args.nbObjective, availableObjectives, self.random)
|
||||||
else:
|
else:
|
||||||
maxActiveGoals = Objectives.maxActiveGoals - addedObjectives
|
maxActiveGoals = Objectives.maxActiveGoals - addedObjectives
|
||||||
if len(args.objective) > maxActiveGoals:
|
if len(args.objective) > maxActiveGoals:
|
||||||
@@ -660,7 +663,7 @@ class VariaRandomizer:
|
|||||||
# print("energyQty:{}".format(energyQty))
|
# print("energyQty:{}".format(energyQty))
|
||||||
|
|
||||||
#try:
|
#try:
|
||||||
self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player)
|
self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player, self.random)
|
||||||
self.container = self.randoExec.randomize()
|
self.container = self.randoExec.randomize()
|
||||||
# if we couldn't find an area layout then the escape graph is not created either
|
# if we couldn't find an area layout then the escape graph is not created either
|
||||||
# and getDoorConnections will crash if random escape is activated.
|
# and getDoorConnections will crash if random escape is activated.
|
||||||
@@ -690,7 +693,7 @@ class VariaRandomizer:
|
|||||||
'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips',
|
'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips',
|
||||||
'phantoonimals.ips', 'ridleyimals.ips']
|
'phantoonimals.ips', 'ridleyimals.ips']
|
||||||
if args.escapeRando == False:
|
if args.escapeRando == False:
|
||||||
args.patches.append(random.choice(animalsPatches))
|
args.patches.append(self.random.choice(animalsPatches))
|
||||||
args.patches.append("Escape_Animals_Change_Event")
|
args.patches.append("Escape_Animals_Change_Event")
|
||||||
else:
|
else:
|
||||||
optErrMsgs.append("Ignored animals surprise because of escape randomization")
|
optErrMsgs.append("Ignored animals surprise because of escape randomization")
|
||||||
@@ -760,9 +763,9 @@ class VariaRandomizer:
|
|||||||
# patch local rom
|
# patch local rom
|
||||||
# romFileName = args.rom
|
# romFileName = args.rom
|
||||||
# shutil.copyfile(romFileName, outputFilename)
|
# shutil.copyfile(romFileName, outputFilename)
|
||||||
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player)
|
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player, random=self.random)
|
||||||
else:
|
else:
|
||||||
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic)
|
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, random=self.random)
|
||||||
|
|
||||||
if customPrePatchApply != None:
|
if customPrePatchApply != None:
|
||||||
customPrePatchApply(romPatcher)
|
customPrePatchApply(romPatcher)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class RomPatcher:
|
|||||||
'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips']
|
'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips']
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, settings=None, romFileName=None, magic=None, player=0):
|
def __init__(self, settings=None, romFileName=None, magic=None, player=0, *, random):
|
||||||
self.log = log.get('RomPatcher')
|
self.log = log.get('RomPatcher')
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
#self.romFileName = romFileName
|
#self.romFileName = romFileName
|
||||||
@@ -76,6 +76,7 @@ class RomPatcher:
|
|||||||
0x93ea: self.forceRoomCRE
|
0x93ea: self.forceRoomCRE
|
||||||
}
|
}
|
||||||
self.player = player
|
self.player = player
|
||||||
|
self.random = random
|
||||||
|
|
||||||
def patchRom(self):
|
def patchRom(self):
|
||||||
self.applyIPSPatches()
|
self.applyIPSPatches()
|
||||||
@@ -496,9 +497,9 @@ class RomPatcher:
|
|||||||
self.ipsPatches = []
|
self.ipsPatches = []
|
||||||
|
|
||||||
def writeSeed(self, seed):
|
def writeSeed(self, seed):
|
||||||
random.seed(seed)
|
r = random.Random(seed)
|
||||||
seedInfo = random.randint(0, 0xFFFF)
|
seedInfo = r.randint(0, 0xFFFF)
|
||||||
seedInfo2 = random.randint(0, 0xFFFF)
|
seedInfo2 = r.randint(0, 0xFFFF)
|
||||||
self.romFile.writeWord(seedInfo, snes_to_pc(0xdfff00))
|
self.romFile.writeWord(seedInfo, snes_to_pc(0xdfff00))
|
||||||
self.romFile.writeWord(seedInfo2)
|
self.romFile.writeWord(seedInfo2)
|
||||||
|
|
||||||
@@ -1066,7 +1067,7 @@ class RomPatcher:
|
|||||||
|
|
||||||
def writeObjectives(self, itemLocs, tourian):
|
def writeObjectives(self, itemLocs, tourian):
|
||||||
objectives = Objectives.objDict[self.player]
|
objectives = Objectives.objDict[self.player]
|
||||||
objectives.writeGoals(self.romFile)
|
objectives.writeGoals(self.romFile, self.random)
|
||||||
objectives.writeIntroObjectives(self.romFile, tourian)
|
objectives.writeIntroObjectives(self.romFile, tourian)
|
||||||
self.writeItemsMasks(itemLocs)
|
self.writeItemsMasks(itemLocs)
|
||||||
# hack bomb_torizo.ips to wake BT in all cases if necessary, ie chozo bots objective is on, and nothing at bombs
|
# hack bomb_torizo.ips to wake BT in all cases if necessary, ie chozo bots objective is on, and nothing at bombs
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import random
|
|
||||||
from enum import IntEnum,IntFlag
|
from enum import IntEnum,IntFlag
|
||||||
import copy
|
import copy
|
||||||
from ..logic.smbool import SMBool
|
from ..logic.smbool import SMBool
|
||||||
@@ -123,7 +122,7 @@ class Door(object):
|
|||||||
else:
|
else:
|
||||||
return [color for color in colorsList if color not in self.forbiddenColors]
|
return [color for color in colorsList if color not in self.forbiddenColors]
|
||||||
|
|
||||||
def randomize(self, allowGreyDoors):
|
def randomize(self, allowGreyDoors, random):
|
||||||
if self.canRandomize():
|
if self.canRandomize():
|
||||||
if self.canGrey and allowGreyDoors:
|
if self.canGrey and allowGreyDoors:
|
||||||
self.setColor(random.choice(self.filterColorList(colorsListGrey)))
|
self.setColor(random.choice(self.filterColorList(colorsListGrey)))
|
||||||
@@ -347,9 +346,9 @@ class DoorsManager():
|
|||||||
currentDoors['CrabShaftRight'].forceBlue()
|
currentDoors['CrabShaftRight'].forceBlue()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def randomize(allowGreyDoors, player):
|
def randomize(allowGreyDoors, player, random):
|
||||||
for door in DoorsManager.doorsDict[player].values():
|
for door in DoorsManager.doorsDict[player].values():
|
||||||
door.randomize(allowGreyDoors)
|
door.randomize(allowGreyDoors, random)
|
||||||
# set both ends of toilet to the same color to avoid soft locking in area rando
|
# set both ends of toilet to the same color to avoid soft locking in area rando
|
||||||
toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom']
|
toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom']
|
||||||
toiletBottom = DoorsManager.doorsDict[player]['OasisTop']
|
toiletBottom = DoorsManager.doorsDict[player]['OasisTop']
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import copy
|
import copy
|
||||||
import random
|
|
||||||
from ..rom.addresses import Addresses
|
from ..rom.addresses import Addresses
|
||||||
from ..rom.rom import pc_to_snes
|
from ..rom.rom import pc_to_snes
|
||||||
from ..logic.helpers import Bosses
|
from ..logic.helpers import Bosses
|
||||||
@@ -28,7 +27,7 @@ class Synonyms(object):
|
|||||||
]
|
]
|
||||||
alreadyUsed = []
|
alreadyUsed = []
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getVerb():
|
def getVerb(random):
|
||||||
verb = random.choice(Synonyms.killSynonyms)
|
verb = random.choice(Synonyms.killSynonyms)
|
||||||
while verb in Synonyms.alreadyUsed:
|
while verb in Synonyms.alreadyUsed:
|
||||||
verb = random.choice(Synonyms.killSynonyms)
|
verb = random.choice(Synonyms.killSynonyms)
|
||||||
@@ -88,10 +87,10 @@ class Goal(object):
|
|||||||
# not all objectives require an ap (like limit objectives)
|
# not all objectives require an ap (like limit objectives)
|
||||||
return self.clearFunc(smbm, ap)
|
return self.clearFunc(smbm, ap)
|
||||||
|
|
||||||
def getText(self):
|
def getText(self, random):
|
||||||
out = "{}. ".format(self.rank)
|
out = "{}. ".format(self.rank)
|
||||||
if self.useSynonym:
|
if self.useSynonym:
|
||||||
out += self.text.format(Synonyms.getVerb())
|
out += self.text.format(Synonyms.getVerb(random))
|
||||||
else:
|
else:
|
||||||
out += self.text
|
out += self.text
|
||||||
assert len(out) <= 28, "Goal text '{}' is too long: {}, max 28".format(out, len(out))
|
assert len(out) <= 28, "Goal text '{}' is too long: {}, max 28".format(out, len(out))
|
||||||
@@ -676,7 +675,7 @@ class Objectives(object):
|
|||||||
return [goal.name for goal in _goals.values() if goal.available and (not removeNothing or goal.name != "nothing")]
|
return [goal.name for goal in _goals.values() if goal.available and (not removeNothing or goal.name != "nothing")]
|
||||||
|
|
||||||
# call from rando
|
# call from rando
|
||||||
def setRandom(self, nbGoals, availableGoals):
|
def setRandom(self, nbGoals, availableGoals, random):
|
||||||
while self.nbActiveGoals < nbGoals and availableGoals:
|
while self.nbActiveGoals < nbGoals and availableGoals:
|
||||||
goalName = random.choice(availableGoals)
|
goalName = random.choice(availableGoals)
|
||||||
self.addGoal(goalName)
|
self.addGoal(goalName)
|
||||||
@@ -702,7 +701,7 @@ class Objectives(object):
|
|||||||
LOG.debug("tourianRequired: {}".format(self.tourianRequired))
|
LOG.debug("tourianRequired: {}".format(self.tourianRequired))
|
||||||
|
|
||||||
# call from rando
|
# call from rando
|
||||||
def writeGoals(self, romFile):
|
def writeGoals(self, romFile, random):
|
||||||
# write check functions
|
# write check functions
|
||||||
romFile.seek(Addresses.getOne('objectivesList'))
|
romFile.seek(Addresses.getOne('objectivesList'))
|
||||||
for goal in self.activeGoals:
|
for goal in self.activeGoals:
|
||||||
@@ -736,7 +735,7 @@ class Objectives(object):
|
|||||||
space = 3 if self.nbActiveGoals == 5 else 4
|
space = 3 if self.nbActiveGoals == 5 else 4
|
||||||
for i, goal in enumerate(self.activeGoals):
|
for i, goal in enumerate(self.activeGoals):
|
||||||
addr = baseAddr + i * lineLength * space
|
addr = baseAddr + i * lineLength * space
|
||||||
text = goal.getText()
|
text = goal.getText(random)
|
||||||
romFile.seek(addr)
|
romFile.seek(addr)
|
||||||
for c in text:
|
for c in text:
|
||||||
if c not in char2tile:
|
if c not in char2tile:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import io
|
import io
|
||||||
import os, json, re, random
|
import os, json, re
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -88,7 +88,7 @@ def normalizeRounding(n):
|
|||||||
|
|
||||||
# gauss random in [0, r] range
|
# gauss random in [0, r] range
|
||||||
# the higher the slope, the less probable extreme values are.
|
# the higher the slope, the less probable extreme values are.
|
||||||
def randGaussBounds(r, slope=5):
|
def randGaussBounds(random, r, slope=5):
|
||||||
r = float(r)
|
r = float(r)
|
||||||
n = normalizeRounding(random.gauss(r/2, r/slope))
|
n = normalizeRounding(random.gauss(r/2, r/slope))
|
||||||
if n < 0:
|
if n < 0:
|
||||||
@@ -111,7 +111,7 @@ def getRangeDict(weightDict):
|
|||||||
|
|
||||||
return rangeDict
|
return rangeDict
|
||||||
|
|
||||||
def chooseFromRange(rangeDict):
|
def chooseFromRange(rangeDict, random):
|
||||||
r = random.random()
|
r = random.random()
|
||||||
val = None
|
val = None
|
||||||
for v in sorted(rangeDict, key=rangeDict.get):
|
for v in sorted(rangeDict, key=rangeDict.get):
|
||||||
|
|||||||
0
worlds/smw/Names/__init__.py
Normal file
0
worlds/smw/Names/__init__.py
Normal file
0
worlds/tww/randomizers/__init__.py
Normal file
0
worlds/tww/randomizers/__init__.py
Normal file
0
worlds/witness/data/settings/__init__.py
Normal file
0
worlds/witness/data/settings/__init__.py
Normal file
@@ -617,7 +617,7 @@ if is_easter_time():
|
|||||||
easter_special_option_group = OptionGroup("EASTER SPECIAL", [
|
easter_special_option_group = OptionGroup("EASTER SPECIAL", [
|
||||||
EasterEggHunt,
|
EasterEggHunt,
|
||||||
])
|
])
|
||||||
witness_option_groups = [easter_special_option_group, *witness_option_groups]
|
witness_option_groups.insert(2, easter_special_option_group)
|
||||||
else:
|
else:
|
||||||
silly_options_group = next(group for group in witness_option_groups if group.name == "Silly Options")
|
silly_options_group = next(group for group in witness_option_groups if group.name == "Silly Options")
|
||||||
silly_options_group.options.append(EasterEggHunt)
|
silly_options_group.options.append(EasterEggHunt)
|
||||||
|
|||||||
Reference in New Issue
Block a user