Core: implement start_inventory_from_pool (#1170)

* Core: implement start_inventory_from_pool

* Factorio/LttP/Subnautica: add start_inventory_from_pool Option
This commit is contained in:
Fabian Dill 2023-04-10 21:13:33 +02:00 committed by GitHub
parent 8d559daa35
commit c7284f90d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 47 additions and 7 deletions

View File

@ -113,7 +113,6 @@ class MultiWorld():
self.dark_world_light_cone = False self.dark_world_light_cone = False
self.rupoor_cost = 10 self.rupoor_cost = 10
self.aga_randomness = True self.aga_randomness = True
self.lock_aga_door_in_escape = False
self.save_and_quit_from_boss = True self.save_and_quit_from_boss = True
self.custom = False self.custom = False
self.customitemarray = [] self.customitemarray = []
@ -122,6 +121,7 @@ class MultiWorld():
self.early_items = {player: {} for player in self.player_ids} self.early_items = {player: {} for player in self.player_ids}
self.local_early_items = {player: {} for player in self.player_ids} self.local_early_items = {player: {} for player in self.player_ids}
self.indirect_connections = {} self.indirect_connections = {}
self.start_inventory_from_pool = {player: Options.StartInventoryPool({}) for player in range(1, players + 1)}
self.fix_trock_doors = self.AttributeProxy( self.fix_trock_doors = self.AttributeProxy(
lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted') lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted')
self.fix_skullwoods_exit = self.AttributeProxy( self.fix_skullwoods_exit = self.AttributeProxy(

29
Main.py
View File

@ -115,6 +115,9 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
for item_name, count in world.start_inventory[player].value.items(): for item_name, count in world.start_inventory[player].value.items():
for _ in range(count): for _ in range(count):
world.push_precollected(world.create_item(item_name, player)) world.push_precollected(world.create_item(item_name, player))
for item_name, count in world.start_inventory_from_pool[player].value.items():
for _ in range(count):
world.push_precollected(world.create_item(item_name, player))
logger.info('Creating World.') logger.info('Creating World.')
AutoWorld.call_all(world, "create_regions") AutoWorld.call_all(world, "create_regions")
@ -149,6 +152,32 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
AutoWorld.call_all(world, "generate_basic") AutoWorld.call_all(world, "generate_basic")
# remove starting inventory from pool items.
# Because some worlds don't actually create items during create_items this has to be as late as possible.
if any(world.start_inventory_from_pool[player].value for player in world.player_ids):
new_items: List[Item] = []
depletion_pool: Dict[int, Dict[str, int]] = {
player: world.start_inventory_from_pool[player].value.copy() for player in world.player_ids}
for player, items in depletion_pool.items():
player_world: AutoWorld.World = world.worlds[player]
for count in items.values():
new_items.append(player_world.create_filler())
target: int = sum(sum(items.values()) for items in depletion_pool.values())
for item in world.itempool:
if depletion_pool[item.player].get(item.name, 0):
target -= 1
depletion_pool[item.player][item.name] -= 1
# quick abort if we have found all items
if not target:
break
else:
new_items.append(item)
for player, remaining_items in depletion_pool.items():
if remaining_items:
raise Exception(f"{world.get_player_name(player)}"
f" is trying to remove items from their pool that don't exist: {remaining_items}")
world.itempool[:] = new_items
# temporary home for item links, should be moved out of Main # temporary home for item links, should be moved out of Main
for group_id, group in world.groups.items(): for group_id, group in world.groups.items():
def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[

View File

@ -897,6 +897,13 @@ class StartInventory(ItemDict):
display_name = "Start Inventory" display_name = "Start Inventory"
class StartInventoryPool(StartInventory):
"""Start with these items and don't place them in the world.
The game decides what the replacement items will be."""
verify_item_name = True
display_name = "Start Inventory from Pool"
class StartHints(ItemSet): class StartHints(ItemSet):
"""Start with these item's locations prefilled into the !hint command.""" """Start with these item's locations prefilled into the !hint command."""
display_name = "Start Hints" display_name = "Start Hints"

View File

@ -1,7 +1,7 @@
import typing import typing
from BaseClasses import MultiWorld from BaseClasses import MultiWorld
from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, TextChoice, PlandoBosses from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool, PlandoBosses
class Logic(Choice): class Logic(Choice):
@ -466,5 +466,6 @@ alttp_options: typing.Dict[str, type(Option)] = {
"beemizer_total_chance": BeemizerTotalChance, "beemizer_total_chance": BeemizerTotalChance,
"beemizer_trap_chance": BeemizerTrapChance, "beemizer_trap_chance": BeemizerTrapChance,
"death_link": DeathLink, "death_link": DeathLink,
"allow_collect": AllowCollect "allow_collect": AllowCollect,
"start_inventory_from_pool": StartInventoryPool,
} }

View File

@ -1247,8 +1247,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# assorted fixes # assorted fixes
rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world[ rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world[
player] else 0x00) # Toggle whether to be in real/fake dark world when dying in a DW dungeon before killing aga1 player] else 0x00) # Toggle whether to be in real/fake dark world when dying in a DW dungeon before killing aga1
rom.write_byte(0x180169, # Lock or unlock aga tower door during escape sequence.
0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. rom.write_byte(0x180169, 0x00)
if world.mode[player] == 'inverted': if world.mode[player] == 'inverted':
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
rom.write_byte(0x180171, rom.write_byte(0x180171,

View File

@ -2,7 +2,8 @@ from __future__ import annotations
import typing import typing
import datetime import datetime
from Options import Choice, OptionDict, OptionSet, ItemDict, Option, DefaultOnToggle, Range, DeathLink, Toggle from Options import Choice, OptionDict, OptionSet, ItemDict, Option, DefaultOnToggle, Range, DeathLink, Toggle, \
StartInventoryPool
from schema import Schema, Optional, And, Or from schema import Schema, Optional, And, Or
# schema helpers # schema helpers
@ -454,6 +455,7 @@ factorio_options: typing.Dict[str, type(Option)] = {
"evolution_trap_increase": EvolutionTrapIncrease, "evolution_trap_increase": EvolutionTrapIncrease,
"death_link": DeathLink, "death_link": DeathLink,
"energy_link": EnergyLink, "energy_link": EnergyLink,
"start_inventory_from_pool": StartInventoryPool,
} }
# spoilers below. If you spoil it for yourself, please at least don't spoil it for anyone else. # spoilers below. If you spoil it for yourself, please at least don't spoil it for anyone else.

View File

@ -1,6 +1,6 @@
import typing import typing
from Options import Choice, Range, DeathLink, DefaultOnToggle from Options import Choice, Range, DeathLink, DefaultOnToggle, StartInventoryPool
from .Creatures import all_creatures, Definitions from .Creatures import all_creatures, Definitions
@ -104,4 +104,5 @@ options = {
"creature_scans": CreatureScans, "creature_scans": CreatureScans,
"creature_scan_logic": AggressiveScanLogic, "creature_scan_logic": AggressiveScanLogic,
"death_link": SubnauticaDeathLink, "death_link": SubnauticaDeathLink,
"start_inventory_from_pool": StartInventoryPool,
} }