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:
		
							parent
							
								
									8d559daa35
								
							
						
					
					
						commit
						c7284f90d9
					
				| 
						 | 
				
			
			@ -113,7 +113,6 @@ class MultiWorld():
 | 
			
		|||
        self.dark_world_light_cone = False
 | 
			
		||||
        self.rupoor_cost = 10
 | 
			
		||||
        self.aga_randomness = True
 | 
			
		||||
        self.lock_aga_door_in_escape = False
 | 
			
		||||
        self.save_and_quit_from_boss = True
 | 
			
		||||
        self.custom = False
 | 
			
		||||
        self.customitemarray = []
 | 
			
		||||
| 
						 | 
				
			
			@ -122,6 +121,7 @@ class MultiWorld():
 | 
			
		|||
        self.early_items = {player: {} for player in self.player_ids}
 | 
			
		||||
        self.local_early_items = {player: {} for player in self.player_ids}
 | 
			
		||||
        self.indirect_connections = {}
 | 
			
		||||
        self.start_inventory_from_pool = {player: Options.StartInventoryPool({}) for player in range(1, players + 1)}
 | 
			
		||||
        self.fix_trock_doors = self.AttributeProxy(
 | 
			
		||||
            lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted')
 | 
			
		||||
        self.fix_skullwoods_exit = self.AttributeProxy(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										29
									
								
								Main.py
								
								
								
								
							
							
						
						
									
										29
									
								
								Main.py
								
								
								
								
							| 
						 | 
				
			
			@ -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 _ in range(count):
 | 
			
		||||
                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.')
 | 
			
		||||
    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")
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
    for group_id, group in world.groups.items():
 | 
			
		||||
        def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -897,6 +897,13 @@ class StartInventory(ItemDict):
 | 
			
		|||
    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):
 | 
			
		||||
    """Start with these item's locations prefilled into the !hint command."""
 | 
			
		||||
    display_name = "Start Hints"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import typing
 | 
			
		||||
 | 
			
		||||
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):
 | 
			
		||||
| 
						 | 
				
			
			@ -466,5 +466,6 @@ alttp_options: typing.Dict[str, type(Option)] = {
 | 
			
		|||
    "beemizer_total_chance": BeemizerTotalChance,
 | 
			
		||||
    "beemizer_trap_chance": BeemizerTrapChance,
 | 
			
		||||
    "death_link": DeathLink,
 | 
			
		||||
    "allow_collect": AllowCollect
 | 
			
		||||
    "allow_collect": AllowCollect,
 | 
			
		||||
    "start_inventory_from_pool": StartInventoryPool,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1247,8 +1247,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
 | 
			
		|||
    # assorted fixes
 | 
			
		||||
    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
 | 
			
		||||
    rom.write_byte(0x180169,
 | 
			
		||||
                   0x01 if world.lock_aga_door_in_escape else 0x00)  # Lock or unlock aga tower door during escape sequence.
 | 
			
		||||
    # Lock or unlock aga tower door during escape sequence.
 | 
			
		||||
    rom.write_byte(0x180169, 0x00)
 | 
			
		||||
    if world.mode[player] == 'inverted':
 | 
			
		||||
        rom.write_byte(0x180169, 0x02)  # lock aga/ganon tower door with crystals in inverted
 | 
			
		||||
    rom.write_byte(0x180171,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,8 @@ from __future__ import annotations
 | 
			
		|||
import typing
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# schema helpers
 | 
			
		||||
| 
						 | 
				
			
			@ -454,6 +455,7 @@ factorio_options: typing.Dict[str, type(Option)] = {
 | 
			
		|||
    "evolution_trap_increase": EvolutionTrapIncrease,
 | 
			
		||||
    "death_link": DeathLink,
 | 
			
		||||
    "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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import typing
 | 
			
		||||
 | 
			
		||||
from Options import Choice, Range, DeathLink, DefaultOnToggle
 | 
			
		||||
from Options import Choice, Range, DeathLink, DefaultOnToggle, StartInventoryPool
 | 
			
		||||
from .Creatures import all_creatures, Definitions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,4 +104,5 @@ options = {
 | 
			
		|||
    "creature_scans": CreatureScans,
 | 
			
		||||
    "creature_scan_logic": AggressiveScanLogic,
 | 
			
		||||
    "death_link": SubnauticaDeathLink,
 | 
			
		||||
    "start_inventory_from_pool": StartInventoryPool,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue