Merge branch 'main' into add_dark_souls_III

This commit is contained in:
Marechal-l
2022-07-16 20:03:44 +02:00
13 changed files with 215 additions and 71 deletions
+3 -1
View File
@@ -154,8 +154,10 @@ def autogen(config: dict):
while 1:
time.sleep(0.1)
with db_session:
# for update locks the database row(s) during transaction, preventing writes from elsewhere
to_start = select(
generation for generation in Generation if generation.state == STATE_QUEUED)
generation for generation in Generation
if generation.state == STATE_QUEUED).for_update()
for generation in to_start:
launch_generator(generator_pool, generation)
except AlreadyRunningException:
+18 -20
View File
@@ -4,7 +4,7 @@ import random
import json
import zipfile
from collections import Counter
from typing import Dict, Optional as TypeOptional
from typing import Dict, Optional, Any
from Utils import __version__
from flask import request, flash, redirect, url_for, session, render_template
@@ -15,7 +15,7 @@ from BaseClasses import seeddigits, get_seed
from Generate import handle_name, PlandoSettings
import pickle
from .models import *
from .models import Generation, STATE_ERROR, STATE_QUEUED, commit, db_session, Seed, UUID
from WebHostLib import app
from .check import get_yaml_data, roll_options
from .upload import upload_zip_to_db
@@ -30,16 +30,15 @@ def get_meta(options_source: dict) -> dict:
}
plando_options -= {""}
meta = {
server_options = {
"hint_cost": int(options_source.get("hint_cost", 10)),
"forfeit_mode": options_source.get("forfeit_mode", "goal"),
"remaining_mode": options_source.get("remaining_mode", "disabled"),
"collect_mode": options_source.get("collect_mode", "disabled"),
"item_cheat": bool(int(options_source.get("item_cheat", 1))),
"server_password": options_source.get("server_password", None),
"plando_options": list(plando_options)
}
return meta
return {"server_options": server_options, "plando_options": list(plando_options)}
@app.route('/generate', methods=['GET', 'POST'])
@@ -60,13 +59,13 @@ def generate(race=False):
results, gen_options = roll_options(options, meta["plando_options"])
if race:
meta["item_cheat"] = False
meta["remaining_mode"] = "disabled"
meta["server_options"]["item_cheat"] = False
meta["server_options"]["remaining_mode"] = "disabled"
if any(type(result) == str for result in results.values()):
return render_template("checkResult.html", results=results)
elif len(gen_options) > app.config["MAX_ROLL"]:
flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players for now. "
flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players. "
f"If you have a larger group, please generate it yourself and upload it.")
elif len(gen_options) >= app.config["JOB_THRESHOLD"]:
gen = Generation(
@@ -92,23 +91,22 @@ def generate(race=False):
return render_template("generate.html", race=race, version=__version__)
def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=None, sid=None):
def gen_game(gen_options, meta: Optional[Dict[str, Any]] = None, owner=None, sid=None):
if not meta:
meta: Dict[str, object] = {}
meta: Dict[str, Any] = {}
meta.setdefault("server_options", {}).setdefault("hint_cost", 10)
race = meta.setdefault("race", False)
meta.setdefault("hint_cost", 10)
race = meta.get("race", False)
del (meta["race"])
plando_options = meta.get("plando", {"bosses", "items", "connections", "texts"})
del (meta["plando_options"])
try:
target = tempfile.TemporaryDirectory()
playercount = len(gen_options)
seed = get_seed()
random.seed(seed)
if race:
random.seed() # reset to time-based random source
random.seed() # use time-based random source
else:
random.seed(seed)
seedname = "W" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))
@@ -120,7 +118,8 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
erargs.outputname = seedname
erargs.outputpath = target.name
erargs.teams = 1
erargs.plando_options = PlandoSettings.from_set(plando_options)
erargs.plando_options = PlandoSettings.from_set(meta.setdefault("plando_options",
{"bosses", "items", "connections", "texts"}))
name_counter = Counter()
for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
@@ -136,7 +135,7 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
erargs.name[player] = handle_name(erargs.name[player], player, name_counter)
if len(set(erargs.name.values())) != len(erargs.name):
raise Exception(f"Names have to be unique. Names: {Counter(erargs.name.values())}")
ERmain(erargs, seed, baked_server_options=meta)
ERmain(erargs, seed, baked_server_options=meta["server_options"])
return upload_to_db(target.name, sid, owner, race)
except BaseException as e:
@@ -148,7 +147,6 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
meta = json.loads(gen.meta)
meta["error"] = (e.__class__.__name__ + ": " + str(e))
gen.meta = json.dumps(meta)
commit()
raise
+1
View File
@@ -129,6 +129,7 @@ Type: dirifempty; Name: "{app}"
[InstallDelete]
Type: files; Name: "{app}\ArchipelagoLttPClient.exe"
Type: filesandordirs; Name: "{app}\lib\worlds\rogue-legacy*"
[Registry]
+7 -3
View File
@@ -175,12 +175,15 @@ A Link to the Past:
retro_caves:
on: 0 # Zelda-1 like mode. There are randomly placed take-any caves that contain one Sword and choices of Heart Container/Blue Potion.
off: 50
hints: # Vendors: King Zora and Bottle Merchant say what they're selling.
# On/Full: Put item and entrance placement hints on telepathic tiles and some NPCs, Full removes joke hints.
hints: # On/Full: Put item and entrance placement hints on telepathic tiles and some NPCs, Full removes joke hints.
'on': 50
vendors: 0
'off': 0
full: 0
scams: # If on, these Merchants will no longer tell you what they're selling.
'off': 50
'king_zora': 0
'bottle_merchant': 0
'all': 0
swordless:
on: 0 # Your swords are replaced by rupees. Gameplay changes have been made to accommodate this change
off: 1
@@ -273,6 +276,7 @@ A Link to the Past:
p: 0 # Randomize the prices of the items in shop inventories
u: 0 # Shuffle capacity upgrades into the item pool (and allow them to traverse the multiworld)
w: 0 # Consider witch's hut like any other shop and shuffle/randomize it too
P: 0 # Prices of the items in shop inventories cost hearts, arrow, or bombs instead of rupees
ip: 0 # Shuffle inventories and randomize prices
fpu: 0 # Generate new inventories, randomize prices and shuffle capacity upgrades into item pool
uip: 0 # Shuffle inventories, randomize prices and shuffle capacity upgrades into the item pool
+4 -4
View File
@@ -1088,10 +1088,10 @@ def get_pool_core(world):
placed_items['Hideout Jail Guard (4 Torches)'] = 'Recovery Heart'
skip_in_spoiler_locations.extend(['Hideout Jail Guard (2 Torches)', 'Hideout Jail Guard (3 Torches)', 'Hideout Jail Guard (4 Torches)'])
else:
placed_items['Hideout Jail Guard (1 Torch)'] = 'Small Key (Gerudo Fortress)'
placed_items['Hideout Jail Guard (2 Torches)'] = 'Small Key (Gerudo Fortress)'
placed_items['Hideout Jail Guard (3 Torches)'] = 'Small Key (Gerudo Fortress)'
placed_items['Hideout Jail Guard (4 Torches)'] = 'Small Key (Gerudo Fortress)'
placed_items['Hideout Jail Guard (1 Torch)'] = 'Small Key (Thieves Hideout)'
placed_items['Hideout Jail Guard (2 Torches)'] = 'Small Key (Thieves Hideout)'
placed_items['Hideout Jail Guard (3 Torches)'] = 'Small Key (Thieves Hideout)'
placed_items['Hideout Jail Guard (4 Torches)'] = 'Small Key (Thieves Hideout)'
if world.shuffle_gerudo_card and world.gerudo_fortress != 'open':
pool.append('Gerudo Membership Card')
+1
View File
@@ -619,6 +619,7 @@ class Patch:
if (self.myWorld.Config.Keysanity):
self.patches.append((Snes(0x40003B), [ 1 ])) #// MapMode #$00 = Always On (default) - #$01 = Require Map Item
self.patches.append((Snes(0x400045), [ 0x0f ])) #// display ----dcba a: Small Keys, b: Big Key, c: Map, d: Compass
self.patches.append((Snes(0x40016A), [ 0x01 ])) #// enable local item dialog boxes for dungeon and keycard items
def WriteSMKeyCardDoors(self):
if (not self.myWorld.Config.Keysanity):
+17 -7
View File
@@ -27,14 +27,18 @@ class SMZ3CollectionState(metaclass=AutoLogicRegister):
# for unit tests where MultiWorld is instantiated before worlds
if hasattr(parent, "state"):
self.smz3state = {player: TotalSMZ3Item.Progression([]) for player in parent.get_game_players("SMZ3")}
for player, group in parent.groups.items():
if (group["game"] == "SMZ3"):
self.smz3state[player] = TotalSMZ3Item.Progression([])
if player not in parent.state.smz3state:
parent.state.smz3state[player] = TotalSMZ3Item.Progression([])
else:
self.smz3state = {}
def copy_mixin(self, ret) -> CollectionState:
ret.smz3state = {player: copy.deepcopy(self.smz3state[player]) for player in self.world.get_game_players("SMZ3")}
ret.smz3state = {player: copy.deepcopy(self.smz3state[player]) for player in self.smz3state}
return ret
class SMZ3Web(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Guide",
@@ -106,6 +110,7 @@ class SMZ3World(World):
niceItems = TotalSMZ3Item.Item.CreateNicePool(self.smz3World)
junkItems = TotalSMZ3Item.Item.CreateJunkPool(self.smz3World)
allJunkItems = niceItems + junkItems
self.junkItemsNames = [item.Type.name for item in junkItems]
if (self.smz3World.Config.Keysanity):
progressionItems = self.progression + self.dungeon + self.keyCardsItems
@@ -256,11 +261,11 @@ class SMZ3World(World):
base_combined_rom = basepatch.apply(base_combined_rom)
patcher = TotalSMZ3Patch(self.smz3World,
[world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World)],
[world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")],
self.world.seed_name,
self.world.seed,
self.local_random,
self.world.world_name_lookup,
{v: k for k, v in self.world.player_name.items()},
next(iter(loc.player for loc in self.world.get_locations() if (loc.item.name == "SilverArrows" and loc.item.player == self.player))))
patches = patcher.Create(self.smz3World.Config)
patches.update(self.apply_sm_custom_sprite())
@@ -312,7 +317,7 @@ class SMZ3World(World):
return slot_data
def collect(self, state: CollectionState, item: Item) -> bool:
state.smz3state[item.player].Add([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)])
state.smz3state[self.player].Add([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World if hasattr(self, "smz3World") else None)])
if item.advancement:
state.prog_items[item.name, item.player] += 1
return True # indicate that a logical state change has occured
@@ -321,7 +326,7 @@ class SMZ3World(World):
def remove(self, state: CollectionState, item: Item) -> bool:
name = self.collect_item(state, item, True)
if name:
state.smz3state[item.player].Remove([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)])
state.smz3state[item.player].Remove([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World if hasattr(self, "smz3World") else None)])
state.prog_items[name, item.player] -= 1
if state.prog_items[name, item.player] < 1:
del (state.prog_items[name, item.player])
@@ -330,7 +335,9 @@ class SMZ3World(World):
def create_item(self, name: str) -> Item:
return SMZ3Item(name, ItemClassification.progression,
TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], player = self.player)
TotalSMZ3Item.ItemType[name], self.item_name_to_id[name],
self.player,
TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[name], self))
def pre_fill(self):
from Fill import fill_restrictive
@@ -364,6 +371,9 @@ class SMZ3World(World):
else:
return []
def get_filler_item_name(self) -> str:
return self.world.random.choice(self.junkItemsNames)
def write_spoiler(self, spoiler_handle: TextIO):
self.world.spoiler.unreachables.update(self.unreachable)
Binary file not shown.
+82
View File
@@ -0,0 +1,82 @@
from typing import Dict, Set, List
# EN Locale Creature Name to rough depth in meters found at
all_creatures: Dict[str, int] = {
"Gasopod": 0,
"Bladderfish": 0,
"Ancient Floater": 0,
"Skyray": 0,
"Garryfish": 0,
"Peeper": 0,
"Shuttlebug": 0,
"Rabbit Ray": 0,
"Stalker": 0,
"Floater": 0,
"Holefish": 0,
"Cave Crawler": 0,
"Hoopfish": 0,
"Crashfish": 0,
"Hoverfish": 0,
"Spadefish": 0,
"Reefback Leviathan": 0,
"Reaper Leviathan": 0,
"Warper": 0,
"Boomerang": 0,
"Biter": 200,
"Sand Shark": 200,
"Bleeder": 200,
"Crabsnake": 300,
"Jellyray": 300,
"Oculus": 300,
"Mesmer": 300,
"Eyeye": 300,
"Reginald": 400,
"Sea Treader Leviathan": 400,
"Crabsquid": 400,
"Ampeel": 400,
"Boneshark": 400,
"Rockgrub": 400,
"Ghost Leviathan": 500,
"Ghost Leviathan Juvenile": 500,
"Spinefish": 600,
"Blighter": 600,
"Blood Crawler": 600,
"Ghostray": 1000,
"Amoeboid": 1000,
"River Prowler": 1000,
"Red Eyeye": 1300,
"Magmarang": 1300,
"Crimson Ray": 1300,
"Lava Larva": 1300,
"Lava Lizard": 1300,
"Sea Dragon Leviathan": 1300,
"Sea Emperor Leviathan": 1700,
"Sea Emperor Juvenile": 1700,
# "Cuddlefish": 300, # maybe at some point, needs hatching in containment chamber (20 real-life minutes)
}
# be nice and make these require Stasis Rifle
aggressive: Set[str] = {
"Cave Crawler", # is very easy without Stasis Rifle, but included for consistency
"Crashfish",
"Bleeder",
"Mesmer",
"Reaper Leviathan",
"Crabsquid",
"Warper",
"Crabsnake",
"Ampeel",
"Boneshark",
"Lava Lizard",
"Sea Dragon Leviathan",
"River Prowler",
}
suffix: str = " Scan"
creature_locations: Dict[str, int] = {
creature+suffix: creature_id for creature_id, creature in enumerate(all_creatures, start=34000)
}
all_creatures_presorted: List[str] = sorted(all_creatures)
+1 -1
View File
@@ -166,7 +166,7 @@ item_table: Dict[int, ItemDict] = {
'count': 5,
'name': 'Seamoth Fragment',
'tech_type': 'SeamothFragment'},
35039: {'classification': ItemClassification.useful,
35039: {'classification': ItemClassification.progression,
'count': 2,
'name': 'Stasis Rifle Fragment',
'tech_type': 'StasisRifleFragment'},
+10 -1
View File
@@ -1,4 +1,5 @@
from Options import Choice
from Options import Choice, Range
from .Creatures import all_creatures
class ItemPool(Choice):
@@ -31,7 +32,15 @@ class Goal(Choice):
}[self.value]
class CreatureScans(Range):
"""Place items on specific creature scans.
Warning: Includes aggressive Leviathans."""
display_name = "Creature Scans"
range_end = len(all_creatures)
options = {
"item_pool": ItemPool,
"goal": Goal,
"creature_scans": CreatureScans
}
+56 -30
View File
@@ -1,112 +1,122 @@
from typing import TYPE_CHECKING
from worlds.generic.Rules import set_rule
from .Locations import location_table, LocationDict
from .Creatures import all_creatures, aggressive, suffix
import math
if TYPE_CHECKING:
from . import SubnauticaWorld
def has_seaglide(state, player):
def has_seaglide(state, player: int):
return state.has("Seaglide Fragment", player, 2)
def has_modification_station(state, player):
def has_modification_station(state, player: int):
return state.has("Modification Station Fragment", player, 3)
def has_mobile_vehicle_bay(state, player):
def has_mobile_vehicle_bay(state, player: int):
return state.has("Mobile Vehicle Bay Fragment", player, 3)
def has_moonpool(state, player):
def has_moonpool(state, player: int):
return state.has("Moonpool Fragment", player, 2)
def has_vehicle_upgrade_console(state, player):
def has_vehicle_upgrade_console(state, player: int):
return state.has("Vehicle Upgrade Console", player) and \
has_moonpool(state, player)
def has_seamoth(state, player):
def has_seamoth(state, player: int):
return state.has("Seamoth Fragment", player, 3) and \
has_mobile_vehicle_bay(state, player)
def has_seamoth_depth_module_mk1(state, player):
def has_seamoth_depth_module_mk1(state, player: int):
return has_vehicle_upgrade_console(state, player)
def has_seamoth_depth_module_mk2(state, player):
def has_seamoth_depth_module_mk2(state, player: int):
return has_seamoth_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_seamoth_depth_module_mk3(state, player):
def has_seamoth_depth_module_mk3(state, player: int):
return has_seamoth_depth_module_mk2(state, player) and \
has_modification_station(state, player)
def has_cyclops_bridge(state, player):
def has_cyclops_bridge(state, player: int):
return state.has("Cyclops Bridge Fragment", player, 3)
def has_cyclops_engine(state, player):
def has_cyclops_engine(state, player: int):
return state.has("Cyclops Engine Fragment", player, 3)
def has_cyclops_hull(state, player):
def has_cyclops_hull(state, player: int):
return state.has("Cyclops Hull Fragment", player, 3)
def has_cyclops(state, player):
def has_cyclops(state, player: int):
return has_cyclops_bridge(state, player) and \
has_cyclops_engine(state, player) and \
has_cyclops_hull(state, player) and \
has_mobile_vehicle_bay(state, player)
def has_cyclops_depth_module_mk1(state, player):
def has_cyclops_depth_module_mk1(state, player: int):
return state.has("Cyclops Depth Module MK1", player) and \
has_modification_station(state, player)
def has_cyclops_depth_module_mk2(state, player):
def has_cyclops_depth_module_mk2(state, player: int):
return has_cyclops_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_cyclops_depth_module_mk3(state, player):
def has_cyclops_depth_module_mk3(state, player: int):
return has_cyclops_depth_module_mk2(state, player) and \
has_modification_station(state, player)
def has_prawn(state, player):
def has_prawn(state, player: int):
return state.has("Prawn Suit Fragment", player, 4) and \
has_mobile_vehicle_bay(state, player)
def has_praw_propulsion_arm(state, player):
def has_praw_propulsion_arm(state, player: int):
return state.has("Prawn Suit Propulsion Cannon Fragment", player, 2) and \
has_vehicle_upgrade_console(state, player)
def has_prawn_depth_module_mk1(state, player):
def has_prawn_depth_module_mk1(state, player: int):
return has_vehicle_upgrade_console(state, player)
def has_prawn_depth_module_mk2(state, player):
def has_prawn_depth_module_mk2(state, player: int):
return has_prawn_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_laser_cutter(state, player):
def has_laser_cutter(state, player: int):
return state.has("Laser Cutter Fragment", player, 3)
def has_stasis_rile(state, player: int):
return state.has("Stasis Rifle Fragment", player, 2)
# Either we have propulsion cannon, or prawn + propulsion cannon arm
def has_propulsion_cannon(state, player):
def has_propulsion_cannon(state, player: int):
return state.has("Propulsion Cannon Fragment", player, 2) or \
(has_prawn(state, player) and has_praw_propulsion_arm(state, player))
def has_cyclops_shield(state, player):
def has_cyclops_shield(state, player: int):
return has_cyclops(state, player) and \
state.has("Cyclops Shield Generator", player)
@@ -119,7 +129,7 @@ def has_cyclops_shield(state, player):
# negligeable with from high capacity tank. 430m -> 460m
# Fins are not used when using seaglide
#
def get_max_swim_depth(state, player):
def get_max_swim_depth(state, player: int):
# TODO, Make this a difficulty setting.
# Only go up to 200m without any submarines for now.
return 200
@@ -130,7 +140,7 @@ def get_max_swim_depth(state, player):
# has_ultra_glide_fins = state.has("Ultra Glide Fins", player)
# max_depth = 400 # More like 430m. Give some room
# if has_seaglide(state, player):
# if has_seaglide(state, player: int):
# if has_ultra_high_capacity_tank:
# max_depth = 750 # It's about 50m more. Give some room
# else:
@@ -146,7 +156,7 @@ def get_max_swim_depth(state, player):
# return max_depth
def get_seamoth_max_depth(state, player):
def get_seamoth_max_depth(state, player: int):
if has_seamoth(state, player):
if has_seamoth_depth_module_mk3(state, player):
return 900
@@ -186,7 +196,7 @@ def get_prawn_max_depth(state, player):
return 0
def get_max_depth(state, player):
def get_max_depth(state, player: int):
# TODO, Difficulty option, we can add vehicle depth + swim depth
# But at this point, we have to consider traver distance in caves, not
# just depth
@@ -196,7 +206,7 @@ def get_max_depth(state, player):
get_prawn_max_depth(state, player))
def can_access_location(state, player: int, loc: LocationDict):
def can_access_location(state, player: int, loc: LocationDict) -> bool:
need_laser_cutter = loc.get("need_laser_cutter", False)
if need_laser_cutter and not has_laser_cutter(state, player):
return False
@@ -225,17 +235,33 @@ def can_access_location(state, player: int, loc: LocationDict):
return get_max_depth(state, player) >= depth
def set_location_rule(world, player, loc):
def set_location_rule(world, player: int, loc: LocationDict):
set_rule(world.get_location(loc["name"], player), lambda state: can_access_location(state, player, loc))
def set_rules(subnautica_world):
def can_scan_creature(state, player: int, creature: str) -> bool:
if not has_seaglide(state, player):
return False
if creature in aggressive and not has_stasis_rile(state, player):
return False
return get_max_depth(state, player) >= all_creatures[creature]
def set_creature_rule(world, player, creature_name: str):
set_rule(world.get_location(creature_name + suffix, player),
lambda state: can_scan_creature(state, player, creature_name))
def set_rules(subnautica_world: "SubnauticaWorld"):
player = subnautica_world.player
world = subnautica_world.world
for loc in location_table.values():
set_location_rule(world, player, loc)
for creature_name in subnautica_world.creatures_to_scan:
set_creature_rule(world, player, creature_name)
# Victory locations
set_rule(world.get_location("Neptune Launch", player), lambda state:
get_max_depth(state, player) >= 1444 and
+15 -4
View File
@@ -5,6 +5,7 @@ from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassifi
from worlds.AutoWorld import World, WebWorld
from . import Items
from . import Locations
from . import Creatures
from . import Options
from .Items import item_table
from .Rules import set_rules
@@ -23,6 +24,10 @@ class SubnaticaWeb(WebWorld):
)]
all_locations = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()}
all_locations.update(Creatures.creature_locations)
class SubnauticaWorld(World):
"""
Subnautica is an undersea exploration game. Stranded on an alien world, you become infected by
@@ -33,25 +38,30 @@ class SubnauticaWorld(World):
web = SubnaticaWeb()
item_name_to_id = {data["name"]: item_id for item_id, data in Items.item_table.items()}
location_name_to_id = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()}
location_name_to_id = all_locations
options = Options.options
data_version = 2
data_version = 3
required_client_version = (0, 3, 3)
prefill_items: List[Item]
creatures_to_scan: List[str]
def generate_early(self) -> None:
self.prefill_items = [
self.create_item("Seaglide Fragment"),
self.create_item("Seaglide Fragment")
]
self.creatures_to_scan = self.world.random.sample(Creatures.all_creatures_presorted,
self.world.creature_scans[self.player].value)
def create_regions(self):
self.world.regions += [
self.create_region("Menu", None, ["Lifepod 5"]),
self.create_region("Planet 4546B",
Locations.events + [location["name"] for location in Locations.location_table.values()])
Locations.events +
[location["name"] for location in Locations.location_table.values()] +
[creature+Creatures.suffix for creature in self.creatures_to_scan])
]
# refer to Rules.py
@@ -64,7 +74,7 @@ class SubnauticaWorld(World):
# Generate item pool
pool = []
neptune_launch_platform = None
extras = 0
extras = self.world.creature_scans[self.player].value
valuable = self.world.item_pool[self.player] == Options.ItemPool.option_valuable
for item in item_table.values():
for i in range(item["count"]):
@@ -105,6 +115,7 @@ class SubnauticaWorld(World):
slot_data: Dict[str, Any] = {
"goal": goal.current_key,
"vanilla_tech": vanilla_tech,
"creatures_to_scan": self.creatures_to_scan
}
return slot_data