mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 05:23:23 -07:00
Implemented abstract base classes + some fixes
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from typing import Tuple, Optional, Dict, Set, List
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
|
||||
@@ -11,7 +11,7 @@ class PowerInfrastructureLevel(IntEnum):
|
||||
def to_name(self):
|
||||
return "Power level: " + self.name
|
||||
|
||||
liquids: Set[str] = {
|
||||
liquids: set[str] = {
|
||||
"Water",
|
||||
"Liquid Biofuel",
|
||||
"Crude Oil",
|
||||
@@ -29,7 +29,7 @@ liquids: Set[str] = {
|
||||
"Dark Matter Residue"
|
||||
}
|
||||
|
||||
radio_actives: Set[str] = {
|
||||
radio_actives: set[str] = {
|
||||
"Uranium",
|
||||
"Encased Uranium Cell",
|
||||
"Uranium Fuel Rod"
|
||||
@@ -50,20 +50,20 @@ class Recipe():
|
||||
"""
|
||||
name: str
|
||||
building: str
|
||||
inputs: Tuple[str, ...]
|
||||
inputs: tuple[str, ...]
|
||||
minimal_belt_speed: int
|
||||
handcraftable: bool
|
||||
implicitly_unlocked: bool
|
||||
"""No explicit location/item is needed to unlock this recipe, you have access as soon as dependencies are met (ex. Water, Leaves, tutorial starting items)"""
|
||||
additional_outputs: Tuple[str, ...]
|
||||
additional_outputs: tuple[str, ...]
|
||||
minimal_tier: int
|
||||
|
||||
needs_pipes: bool
|
||||
is_radio_active: bool
|
||||
|
||||
def __init__(self, name: str, building: Optional[str] = None, inputs: Optional[Tuple[str, ...]] = None,
|
||||
def __init__(self, name: str, building: Optional[str] = None, inputs: Optional[tuple[str, ...]] = None,
|
||||
minimal_belt_speed: int = 1, handcraftable: bool = False, implicitly_unlocked: bool = False,
|
||||
additional_outputs: Optional[Tuple[str, ...]] = None, minimal_tier: Optional[int] = 1):
|
||||
additional_outputs: Optional[tuple[str, ...]] = None, minimal_tier: Optional[int] = 1):
|
||||
self.name = "Recipe: " + name
|
||||
self.building = building
|
||||
self.inputs = inputs
|
||||
@@ -73,7 +73,7 @@ class Recipe():
|
||||
self.additional_outputs = additional_outputs
|
||||
self.minimal_tier = minimal_tier
|
||||
|
||||
all_parts: List[str] = [name]
|
||||
all_parts: list[str] = [name]
|
||||
if inputs:
|
||||
all_parts += inputs
|
||||
if additional_outputs:
|
||||
@@ -86,7 +86,7 @@ class Building(Recipe):
|
||||
power_requirement: Optional[PowerInfrastructureLevel]
|
||||
can_produce: bool
|
||||
|
||||
def __init__(self, name: str, inputs: Optional[Tuple[str, ...]] = None,
|
||||
def __init__(self, name: str, inputs: Optional[tuple[str, ...]] = None,
|
||||
power_requirement: Optional[PowerInfrastructureLevel] = None, can_produce: bool = True,
|
||||
implicitly_unlocked: bool = False):
|
||||
super().__init__(name, None, inputs, handcraftable=True, implicitly_unlocked=implicitly_unlocked)
|
||||
@@ -98,13 +98,13 @@ class Building(Recipe):
|
||||
|
||||
class MamNode():
|
||||
name: str
|
||||
unlock_cost: Dict[str, int]
|
||||
unlock_cost: dict[str, int]
|
||||
"""All game items must be submitted to purchase this MamNode"""
|
||||
depends_on: Tuple[str, ...]
|
||||
depends_on: tuple[str, ...]
|
||||
"""At least one of these prerequisite MamNodes must be unlocked to purchase this MamNode"""
|
||||
minimal_tier: Optional[int]
|
||||
|
||||
def __init__(self, name: str, unlock_cost: Dict[str, int], depends_on: Tuple[str, ...],
|
||||
def __init__(self, name: str, unlock_cost: dict[str, int], depends_on: tuple[str, ...],
|
||||
minimal_tier: Optional[int] = 1):
|
||||
self.name = name
|
||||
self.unlock_cost = unlock_cost
|
||||
@@ -113,11 +113,11 @@ class MamNode():
|
||||
|
||||
|
||||
class MamTree():
|
||||
access_items: Tuple[str, ...]
|
||||
access_items: tuple[str, ...]
|
||||
"""At least one of these game items must enter the player inventory for this MamTree to be available"""
|
||||
nodes: Tuple[MamNode, ...]
|
||||
nodes: tuple[MamNode, ...]
|
||||
|
||||
def __init__(self, access_items: Tuple[str, ...], nodes: Tuple[MamNode, ...]):
|
||||
def __init__(self, access_items: tuple[str, ...], nodes: tuple[MamNode, ...]):
|
||||
self.access_items = access_items
|
||||
self.nodes = nodes
|
||||
|
||||
@@ -134,7 +134,7 @@ class DropPodData:
|
||||
|
||||
|
||||
class GameLogic:
|
||||
recipes: Dict[str, Tuple[Recipe, ...]] = {
|
||||
recipes: dict[str, tuple[Recipe, ...]] = {
|
||||
# This Dict should only contain items that are used somewhere in a logic chain
|
||||
|
||||
# Exploration Items
|
||||
@@ -499,7 +499,7 @@ class GameLogic:
|
||||
"Alien DNA Capsule": (
|
||||
Recipe("Alien DNA Capsule", "Constructor", ("Alien Protein", ), handcraftable=True), ),
|
||||
"Black Powder": (
|
||||
Recipe("Black Powder", "Assembler", ("Coal", "Sulfur"), handcraftable=True),
|
||||
Recipe("Black Powder", "Equipment Workshop", ("Coal", "Sulfur"), handcraftable=True),
|
||||
Recipe("Fine Black Powder", "Assembler", ("Sulfur", "Compacted Coal"))),
|
||||
"Smokeless Powder": (
|
||||
Recipe("Smokeless Powder", "Refinery", ("Black Powder", "Heavy Oil Residue")), ),
|
||||
@@ -579,7 +579,7 @@ class GameLogic:
|
||||
#1.0
|
||||
}
|
||||
|
||||
buildings: Dict[str, Building] = {
|
||||
buildings: dict[str, Building] = {
|
||||
"Constructor": Building("Constructor", ("Reinforced Iron Plate", "Cable"), PowerInfrastructureLevel.Basic, implicitly_unlocked=True),
|
||||
"Assembler": Building("Assembler", ("Reinforced Iron Plate", "Rotor", "Cable"), PowerInfrastructureLevel.Basic),
|
||||
"Manufacturer": Building("Manufacturer", ("Motor", "Heavy Modular Frame", "Cable", "Plastic"), PowerInfrastructureLevel.Advanced),
|
||||
@@ -632,13 +632,13 @@ class GameLogic:
|
||||
#1.0
|
||||
}
|
||||
|
||||
handcraftable_recipes: Dict[str, List[Recipe]] = {}
|
||||
handcraftable_recipes: dict[str, list[Recipe]] = {}
|
||||
for part, recipes_per_part in recipes.items():
|
||||
for recipe in recipes_per_part:
|
||||
if recipe.handcraftable:
|
||||
handcraftable_recipes.setdefault(part, list()).append(recipe)
|
||||
|
||||
implicitly_unlocked_recipes: Dict[str, Recipe] = {
|
||||
implicitly_unlocked_recipes: dict[str, Recipe] = {
|
||||
recipe.name: recipe
|
||||
for recipes_per_part in recipes.values()
|
||||
for recipe in recipes_per_part if recipe.implicitly_unlocked
|
||||
@@ -648,7 +648,7 @@ class GameLogic:
|
||||
for building in buildings.values() if building.implicitly_unlocked
|
||||
})
|
||||
|
||||
requirement_per_powerlevel: Dict[PowerInfrastructureLevel, Tuple[Recipe, ...]] = {
|
||||
requirement_per_powerlevel: dict[PowerInfrastructureLevel, tuple[Recipe, ...]] = {
|
||||
# no need to polute the logic by including higher level recipes based on previus recipes
|
||||
PowerInfrastructureLevel.Basic: (
|
||||
Recipe("Biomass Power (Biomass)", "Biomass Burner", ("Biomass", ), implicitly_unlocked=True),
|
||||
@@ -678,7 +678,7 @@ class GameLogic:
|
||||
|
||||
slots_per_milestone: int = 8
|
||||
|
||||
hub_layout: Tuple[Tuple[Dict[str, int], ...], ...] = (
|
||||
hub_layout: tuple[tuple[dict[str, int], ...], ...] = (
|
||||
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildHubData.CC_BuildHubData'
|
||||
( # Tier 1
|
||||
{"Concrete":200, "Iron Plate":100, "Iron Rod":100, }, # Schematic: Base Building (Schematic_1-1_C)
|
||||
@@ -744,7 +744,7 @@ class GameLogic:
|
||||
)
|
||||
|
||||
# Values from /Game/FactoryGame/Schematics/Progression/BP_GamePhaseManager.BP_GamePhaseManager
|
||||
space_elevator_tiers: Tuple[Dict[str, int], ...] = (
|
||||
space_elevator_tiers: tuple[dict[str, int], ...] = (
|
||||
{ "Smart Plating": 50 },
|
||||
{ "Smart Plating": 500, "Versatile Framework": 500, "Automated Wiring": 100 },
|
||||
{ "Versatile Framework": 2500, "Modular Engine": 500, "Adaptive Control Unit": 100 },
|
||||
@@ -754,7 +754,7 @@ class GameLogic:
|
||||
|
||||
# Do not regenerate as format got changed
|
||||
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildMamData.CC_BuildMamData'
|
||||
man_trees: Dict[str, MamTree] = {
|
||||
man_trees: dict[str, MamTree] = {
|
||||
"Alien Organisms": MamTree(("Hog Remains", "Plasma Spitter Remains", "Stinger Remains", "Hatcher Remains"), ( # Alien Organisms (BPD_ResearchTree_AlienOrganisms_C)
|
||||
MamNode("Inflated Pocket Dimension", {"Alien Protein":3,"Cable":1000,}, depends_on=("Bio-Organic Properties", )), #(Research_AOrgans_3_C)
|
||||
MamNode("Hostile Organism Detection", {"Alien DNA Capsule":10,"Crystal Oscillator":5,"High-Speed Connector":5,}, depends_on=("Bio-Organic Properties", )), #(Research_AOrganisms_2_C)
|
||||
@@ -869,7 +869,7 @@ class GameLogic:
|
||||
))
|
||||
}
|
||||
|
||||
drop_pods: List[DropPodData] = [
|
||||
drop_pods: list[DropPodData] = [
|
||||
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildDropPodLocations.CC_BuildDropPodLocations'
|
||||
DropPodData(-29068, -22640, 17384, "Encased Industrial Beam", 0), # Unlocks with: 4 x Desc_SteelPlateReinforced_C
|
||||
DropPodData(-33340, 5176, 23519, "Crystal Oscillator", 0), # Unlocks with: 5 x Desc_CrystalOscillator_C
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from enum import IntFlag
|
||||
from typing import NamedTuple, Set
|
||||
from typing import NamedTuple
|
||||
from BaseClasses import ItemClassification
|
||||
|
||||
class ItemGroups(IntFlag):
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
from random import Random
|
||||
from typing import ClassVar, Dict, Set, List, Tuple, Optional
|
||||
from typing import ClassVar, Optional
|
||||
from BaseClasses import Item, ItemClassification as C, MultiWorld
|
||||
from .GameLogic import GameLogic
|
||||
from .Options import SatisfactoryOptions
|
||||
from .ItemData import ItemData, ItemGroups as G
|
||||
from .Options import SatisfactoryOptions
|
||||
from .CriticalPathCalculator import CriticalPathCalculator
|
||||
import logging
|
||||
|
||||
|
||||
class Items:
|
||||
item_data: ClassVar[Dict[str, ItemData]] = {
|
||||
item_data: ClassVar[dict[str, ItemData]] = {
|
||||
# Resource Bundles
|
||||
"Bundle: Adaptive Control Unit": ItemData(G.Parts, 1338000),
|
||||
"Bundle: AI Limiter": ItemData(G.Parts, 1338001),
|
||||
@@ -167,7 +165,7 @@ class Items:
|
||||
"Bundle: Gas Nobelisk": ItemData(G.Ammo, 1338163),
|
||||
"Bundle: Hazmat Suit": ItemData(G.Equipment, 1338164),
|
||||
"Bundle: Homing Rifle Ammo": ItemData(G.Ammo, 1338165),
|
||||
"Bundle: Hover Pack": ItemData(G.Equipment, 1338166),
|
||||
"Bundle: Hoverpack": ItemData(G.Equipment, 1338166),
|
||||
"Bundle: Iron Rebar": ItemData(G.Ammo, 1338167),
|
||||
"Bundle: Jetpack": ItemData(G.Equipment, 1338168),
|
||||
"Bundle: Medicinal Inhaler": ItemData(G.Ammo, 1338169),
|
||||
@@ -195,7 +193,14 @@ class Items:
|
||||
"Expanded Toolbelt": ItemData(G.Upgrades, 1338190, C.useful, 5),
|
||||
"Dimensional Depot upload from inventory": ItemData(G.Upgrades, 1338191, C.useful),
|
||||
|
||||
#1338191 - 1338199 Reserved for future equipment/ammo
|
||||
# added in 1.1
|
||||
"Recipe: Hoverpack": ItemData(G.Recipe, 1338192, C.useful),
|
||||
"Bundle: Iodine-Infused Filter": ItemData(G.Ammo, 1338193),
|
||||
"Recipe: Jetpack": ItemData(G.Recipe, 1338194, C.useful),
|
||||
"Recipe: Nobelisk Detonator": ItemData(G.Recipe, 1338195, C.progression),
|
||||
"Recipe: Portable Miner": ItemData(G.Equipment, 1338196, C.progression),
|
||||
|
||||
#1338197 - 1338199 Reserved for future equipment/ammo
|
||||
|
||||
#1338200+ Recipes / buildings / schematics
|
||||
"Recipe: Reinforced Iron Plate": ItemData(G.Recipe, 1338200, C.progression),
|
||||
@@ -354,7 +359,7 @@ class Items:
|
||||
"Recipe: Plutonium Fuel Rod": ItemData(G.Recipe, 1338353),
|
||||
"Recipe: Plutonium Fuel Unit": ItemData(G.Recipe, 1338354),
|
||||
"Recipe: Gas Filter": ItemData(G.Recipe, 1338355, C.progression),
|
||||
"Recipe: Iodine Infused Filter": ItemData(G.Recipe, 1338356, C.progression),
|
||||
"Recipe: Iodine-Infused Filter": ItemData(G.Recipe, 1338356, C.progression),
|
||||
"Recipe: Assembly Director System": ItemData(G.Recipe, 1338357, C.progression),
|
||||
"Recipe: Magnetic Field Generator": ItemData(G.Recipe, 1338358, C.progression),
|
||||
"Recipe: Copper Powder": ItemData(G.Recipe, 1338359, C.progression),
|
||||
@@ -374,7 +379,7 @@ class Items:
|
||||
"Recipe: Biomass (Wood)": ItemData(G.Recipe, 1338373, C.progression),
|
||||
"Recipe: Biomass (Mycelia)": ItemData(G.Recipe, 1338374, C.progression),
|
||||
"Recipe: Biomass (Alien Protein)": ItemData(G.Recipe, 1338375, C.progression),
|
||||
"Recipe: Turbo Rifle Ammo (Packaged)": ItemData(G.Recipe, 1338376),
|
||||
"Recipe: Turbo Rifle Ammo (Packaged)": ItemData(G.Recipe, 1338376, C.useful),
|
||||
"Recipe: Fabric": ItemData(G.Recipe, 1338377, C.progression),
|
||||
"Recipe: Polyester Fabric": ItemData(G.Recipe, 1338378, C.progression),
|
||||
"Recipe: Solid Biofuel": ItemData(G.Recipe, 1338379, C.progression),
|
||||
@@ -402,15 +407,15 @@ class Items:
|
||||
"Recipe: Black Powder": ItemData(G.Recipe, 1338401, C.progression),
|
||||
"Recipe: Blade Runners": ItemData(G.Recipe, 1338402, C.useful),
|
||||
"Recipe: Chainsaw": ItemData(G.Recipe, 1338403, C.useful),
|
||||
"Recipe: Cluster Nobelisk": ItemData(G.Recipe, 1338404),
|
||||
"Recipe: Explosive Rebar": ItemData(G.Recipe, 1338405),
|
||||
"Recipe: Cluster Nobelisk": ItemData(G.Recipe, 1338404, C.useful),
|
||||
"Recipe: Explosive Rebar": ItemData(G.Recipe, 1338405, C.useful),
|
||||
"Recipe: Factory Cart": ItemData(G.Recipe, 1338406, C.useful),
|
||||
"Recipe: Gas Nobelisk": ItemData(G.Recipe, 1338407),
|
||||
"Recipe: Gas Nobelisk": ItemData(G.Recipe, 1338407, C.useful),
|
||||
"Recipe: Golden Factory Cart": ItemData(G.Recipe, 1338408),
|
||||
"Recipe: Homing Rifle Ammo": ItemData(G.Recipe, 1338409),
|
||||
"Recipe: Homing Rifle Ammo": ItemData(G.Recipe, 1338409, C.useful),
|
||||
"Recipe: Iron Rebar": ItemData(G.Recipe, 1338410, C.progression),
|
||||
"Recipe: Nobelisk": ItemData(G.Recipe, 1338411, C.progression),
|
||||
"Recipe: Nuke Nobelisk": ItemData(G.Recipe, 1338412),
|
||||
"Recipe: Nuke Nobelisk": ItemData(G.Recipe, 1338412, C.useful),
|
||||
"Recipe: Nutritional Inhaler": ItemData(G.Recipe, 1338413, C.useful),
|
||||
"Recipe: Object Scanner": ItemData(G.Recipe, 1338414, C.progression),
|
||||
"Recipe: Parachute": ItemData(G.Recipe, 1338415, C.useful),
|
||||
@@ -418,10 +423,10 @@ class Items:
|
||||
"Recipe: Rebar Gun": ItemData(G.Recipe, 1338417, C.useful),
|
||||
"Recipe: Rifle": ItemData(G.Recipe, 1338418, C.useful),
|
||||
"Recipe: Rifle Ammo": ItemData(G.Recipe, 1338419, C.progression),
|
||||
"Recipe: Shatter Rebar": ItemData(G.Recipe, 1338420),
|
||||
"Recipe: Stun Rebar": ItemData(G.Recipe, 1338421),
|
||||
"Recipe: Shatter Rebar": ItemData(G.Recipe, 1338420, C.useful),
|
||||
"Recipe: Stun Rebar": ItemData(G.Recipe, 1338421, C.useful),
|
||||
"Recipe: Therapeutic Inhaler": ItemData(G.Recipe, 1338422, C.useful),
|
||||
"Recipe: Turbo Rifle Ammo": ItemData(G.Recipe, 1338423),
|
||||
"Recipe: Turbo Rifle Ammo": ItemData(G.Recipe, 1338423, C.useful),
|
||||
"Recipe: Vitamin Inhaler": ItemData(G.Recipe, 1338424, C.useful),
|
||||
"Recipe: Xeno-Basher": ItemData(G.Recipe, 1338425, C.useful),
|
||||
"Recipe: Xeno-Zapper": ItemData(G.Recipe, 1338426, C.useful),
|
||||
@@ -442,7 +447,7 @@ class Items:
|
||||
"Recipe: Turbo Diamonds": ItemData(G.Recipe, 1338439, C.progression),
|
||||
"Recipe: Time Crystal": ItemData(G.Recipe, 1338440, C.progression),
|
||||
"Recipe: Superposition Oscillator": ItemData(G.Recipe, 1338441, C.progression),
|
||||
#"Recipe: Excited Photonic Matter": ItemData(G.Recipe, 1338442, C.progression), should probably be unlocked with converter
|
||||
#"Recipe: Excited Photonic Matter": ItemData(G.Recipe, 1338442, C.progression), unlocked with converter
|
||||
"Recipe: Rocket Fuel": ItemData(G.Recipe, 1338443, C.progression),
|
||||
"Recipe: Nitro Rocket Fuel": ItemData(G.Recipe, 1338444, C.progression),
|
||||
"Recipe: Ionized Fuel": ItemData(G.Recipe, 1338445, C.useful),
|
||||
@@ -552,17 +557,11 @@ class Items:
|
||||
"Building: Label Sign Bundle": ItemData(G.Building | G.Signs, 1338678, C.filler, 0),
|
||||
"Building: Display Sign Bundle": ItemData(G.Building | G.Signs, 1338679, C.filler, 0),
|
||||
"Building: Billboard Set": ItemData(G.Building | G.Signs, 1338680, C.filler, 0),
|
||||
"Building: Walls Metal": ItemData(G.Building | G.Walls, 1338681, C.filler, 0),
|
||||
#1338681 Moved to cosmetics
|
||||
"Building: Metal Pillar": ItemData(G.Pilars, 1338682, C.filler, 0),
|
||||
"Building: Concrete Pillar": ItemData(G.Pilars, 1338683, C.filler, 0),
|
||||
"Building: Frame Pillar": ItemData(G.Pilars, 1338684, C.filler, 0),
|
||||
"Building: Walls Concrete": ItemData(G.Building | G.Walls, 1338685, C.filler, 0),
|
||||
#"Building: Big Metal Pillar": ItemData(G.Pilars, 1338686, C.filler, 0),
|
||||
#"Building: Big Concrete Pillar": ItemData(G.Pilars, 1338687, C.filler, 0),
|
||||
#"Building: Big Frame Pillar": ItemData(G.Pilars, 1338688, C.filler, 0),
|
||||
#"Building: Beam Support": ItemData(G.Beams, 1338689, C.filler, 0),
|
||||
#"Building: Beam Connector": ItemData(G.Beams, 1338690, C.filler, 0),
|
||||
#"Building: Beam Connector Double": ItemData(G.Beams, 1338691, C.filler, 0),
|
||||
#1338685 - 1338691 Moved to cosmetics
|
||||
"Building: Foundation": ItemData(G.Building | G.Foundations | G.AlwaysUseful, 1338692, C.progression),
|
||||
"Building: Half Foundation": ItemData(G.Foundations, 1338693, C.filler, 0),
|
||||
"Building: Corner Ramp Pack": ItemData(G.Foundations, 1338694, C.filler, 0),
|
||||
@@ -570,7 +569,7 @@ class Items:
|
||||
"Building: Inverted Corner Ramp Pack": ItemData(G.Foundations, 1338696, C.filler, 0),
|
||||
"Building: Quarter Pipes Pack": ItemData(G.Foundations, 1338697, C.filler, 0),
|
||||
"Building: Quarter Pipe Extensions Pack": ItemData(G.Foundations, 1338698, C.filler, 0),
|
||||
"Building: Frame foundation": ItemData(G.Foundations, 1338699, C.filler, 0),
|
||||
"Building: Frame Foundation": ItemData(G.Foundations, 1338699, C.filler, 0),
|
||||
"Building: Wall Outlet Mk.1": ItemData(G.Building, 1338700, C.useful),
|
||||
"Building: Wall Outlet Mk.2": ItemData(G.Building, 1338701, C.useful),
|
||||
"Building: Wall Outlet Mk.3": ItemData(G.Building, 1338702, C.useful),
|
||||
@@ -634,7 +633,7 @@ class Items:
|
||||
"Customizer: Caterium Paint Finish": ItemData(G.Customizer, 1338773, C.filler, 0),
|
||||
# 1.0
|
||||
|
||||
#1338773 - 1338799 Reserved for buildings
|
||||
#1338774 - 1338799 Reserved for buildings
|
||||
|
||||
# Transports 1338800 - 1338898
|
||||
# Drones (including Drone)
|
||||
@@ -689,14 +688,14 @@ class Items:
|
||||
|
||||
non_unique_item_categories: ClassVar[G] = G.Parts | G.Equipment | G.Ammo | G.Trap | G.Upgrades
|
||||
pool_item_categories: ClassVar[G] = G.Recipe | G.Building | G.Equipment | G.Transport | G.Upgrades
|
||||
item_names_and_ids: ClassVar[Dict[str, int]] = {name: item_data.code for name, item_data in item_data.items()}
|
||||
filler_items: ClassVar[Tuple[str, ...]] = tuple(item for item, details in item_data.items()
|
||||
item_names_and_ids: ClassVar[dict[str, int]] = {name: item_data.code for name, item_data in item_data.items()}
|
||||
filler_items: ClassVar[tuple[str, ...]] = tuple(item for item, details in item_data.items()
|
||||
if details.category & (G.Parts | G.Ammo))
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_item_names_per_category(cls) -> Dict[str, Set[str]]:
|
||||
categories: Dict[str, Set[str]] = {}
|
||||
def get_item_names_per_category(cls) -> dict[str, set[str]]:
|
||||
categories: dict[str, set[str]] = {}
|
||||
|
||||
for name, data in cls.item_data.items():
|
||||
for category in data.category:
|
||||
@@ -731,18 +730,18 @@ class Items:
|
||||
return Item(name, type, data.code, player)
|
||||
|
||||
|
||||
def get_filler_item_name(self, filler_items: Tuple[str, ...], random: Random, options: SatisfactoryOptions) -> str:
|
||||
def get_filler_item_name(self, filler_items: tuple[str, ...], random: Random, options: SatisfactoryOptions) -> str:
|
||||
trap_chance: int = options.trap_chance.value
|
||||
enabled_traps: List[str] = options.trap_selection_override.value
|
||||
enabled_traps: list[str] = options.trap_selection_override.value
|
||||
|
||||
if enabled_traps and random.random() < (trap_chance / 100):
|
||||
return random.choice(enabled_traps)
|
||||
else:
|
||||
return random.choice(self.filler_items)
|
||||
return random.choice(filler_items)
|
||||
|
||||
|
||||
def get_excluded_items(self, multiworld: MultiWorld, options: SatisfactoryOptions) -> Set[str]:
|
||||
excluded_items: Set[str] = set()
|
||||
def get_excluded_items(self, multiworld: MultiWorld, options: SatisfactoryOptions) -> set[str]:
|
||||
excluded_items: set[str] = set()
|
||||
excluded_items.update("Bundle: "+ part for part in self.critical_path.parts_to_exclude)
|
||||
excluded_items.update(recipe for recipe in self.critical_path.recipes_to_exclude)
|
||||
excluded_items.update("Building: "+ building for building in self.critical_path.buildings_to_exclude)
|
||||
@@ -758,10 +757,10 @@ class Items:
|
||||
|
||||
|
||||
def build_item_pool(self, random: Random, multiworld: MultiWorld,
|
||||
options: SatisfactoryOptions, number_of_locations: int) -> List[Item]:
|
||||
excluded_from_pool: Set[str] = self.get_excluded_items(multiworld, options) \
|
||||
options: SatisfactoryOptions, number_of_locations: int) -> list[Item]:
|
||||
excluded_from_pool: set[str] = self.get_excluded_items(multiworld, options) \
|
||||
.union(self.logic.implicitly_unlocked_recipes.keys())
|
||||
pool: List[Item] = []
|
||||
pool: list[Item] = []
|
||||
|
||||
for name, data in self.item_data.items():
|
||||
if data.count > 0 \
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from typing import List, Optional, Callable, Tuple, Dict, Iterable, ClassVar
|
||||
from typing import ClassVar, Optional
|
||||
from collections.abc import Iterable, Callable
|
||||
from math import ceil, floor
|
||||
from BaseClasses import CollectionState
|
||||
from .GameLogic import GameLogic, Recipe, Building, PowerInfrastructureLevel, DropPodData
|
||||
from .StateLogic import StateLogic, EventId, part_event_prefix, building_event_prefix
|
||||
from .Items import Items
|
||||
from .Options import SatisfactoryOptions
|
||||
from .CriticalPathCalculator import CriticalPathCalculator
|
||||
from math import ceil, floor
|
||||
|
||||
|
||||
class LocationData():
|
||||
__slots__ = ("region", "name", "event_name", "code", "non_progression", "rule")
|
||||
@@ -29,9 +29,9 @@ class LocationData():
|
||||
|
||||
class Part(LocationData):
|
||||
@staticmethod
|
||||
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str,
|
||||
final_elevator_tier: int) -> List[LocationData]:
|
||||
recipes_per_region: Dict[str, List[Recipe]] = {}
|
||||
def get_parts(state_logic: StateLogic, recipes: tuple[Recipe, ...], name: str,
|
||||
final_elevator_tier: int) -> list[LocationData]:
|
||||
recipes_per_region: dict[str, list[Recipe]] = {}
|
||||
|
||||
for recipe in recipes:
|
||||
if recipe.minimal_tier > final_elevator_tier:
|
||||
@@ -174,7 +174,7 @@ class Locations():
|
||||
self.critical_path = critical_path
|
||||
|
||||
|
||||
def get_base_location_table(self, max_tier: int) -> List[LocationData]:
|
||||
def get_base_location_table(self, max_tier: int) -> list[LocationData]:
|
||||
all_locations = [
|
||||
MamSlot("Alien Organisms", "Inflated Pocket Dimension", 1338500),
|
||||
MamSlot("Alien Organisms", "Hostile Organism Detection", 1338501),
|
||||
@@ -304,7 +304,7 @@ class Locations():
|
||||
|
||||
return all_locations
|
||||
|
||||
def get_locations_for_data_package(self) -> Dict[str, int]:
|
||||
def get_locations_for_data_package(self) -> dict[str, int]:
|
||||
"Must include all possible location names and their id's"
|
||||
|
||||
# 1338000 - 1338499 - Milestones
|
||||
@@ -320,7 +320,7 @@ class Locations():
|
||||
|
||||
return {location.name: location.code for location in location_table}
|
||||
|
||||
def get_locations(self) -> List[LocationData]:
|
||||
def get_locations(self) -> list[LocationData]:
|
||||
"Only return location used in this game based on settings"
|
||||
|
||||
if not self.game_logic or not self.options or not self.state_logic or not self.items:
|
||||
@@ -335,8 +335,8 @@ class Locations():
|
||||
|
||||
return location_table
|
||||
|
||||
def get_hub_locations(self, for_data_package: bool, max_tier: int) -> List[LocationData]:
|
||||
location_table: List[LocationData] = []
|
||||
def get_hub_locations(self, for_data_package: bool, max_tier: int) -> list[LocationData]:
|
||||
location_table: list[LocationData] = []
|
||||
|
||||
number_of_slots_per_milestone_for_game: int
|
||||
if (for_data_package):
|
||||
@@ -364,8 +364,8 @@ class Locations():
|
||||
|
||||
return location_table
|
||||
|
||||
def get_logical_event_locations(self, final_elevator_tier: int) -> List[LocationData]:
|
||||
location_table: List[LocationData] = []
|
||||
def get_logical_event_locations(self, final_elevator_tier: int) -> list[LocationData]:
|
||||
location_table: list[LocationData] = []
|
||||
|
||||
# for performance plan is to upfront calculated everything we need
|
||||
# and than create one massive state.has_all for each logical gate (hub tiers, elevator tiers)
|
||||
@@ -391,17 +391,17 @@ class Locations():
|
||||
return location_table
|
||||
|
||||
def get_hard_drive_locations(self, for_data_package: bool, max_tier: int, available_parts: set[str]) \
|
||||
-> List[LocationData]:
|
||||
hard_drive_locations: List[HardDrive] = []
|
||||
-> list[LocationData]:
|
||||
hard_drive_locations: list[HardDrive] = []
|
||||
|
||||
bucket_size: int
|
||||
drop_pod_data: List[DropPodData]
|
||||
drop_pod_data: list[DropPodData]
|
||||
if for_data_package:
|
||||
bucket_size = 0
|
||||
drop_pod_data = []
|
||||
else:
|
||||
bucket_size = floor((self.drop_pod_location_id_end - self.drop_pod_location_id_start) / max_tier)
|
||||
drop_pod_data = self.game_logic.drop_pods
|
||||
drop_pod_data: list[DropPodData] = self.game_logic.drop_pods
|
||||
# sort, easily obtainable first, should be deterministic
|
||||
drop_pod_data.sort(key = lambda data: ("!" if data.item == None else data.item) + str(data.x - data.z))
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List, Any, Tuple, ClassVar, cast
|
||||
from typing import ClassVar, Any, cast
|
||||
from enum import IntEnum
|
||||
from Options import PerGameCommonOptions, DeathLink, AssembleOptions, Visibility
|
||||
from Options import Range, Toggle, OptionSet, StartInventoryPool, NamedRange, Choice
|
||||
from schema import Schema, And, Use
|
||||
from schema import Schema, And
|
||||
|
||||
class Placement(IntEnum):
|
||||
starting_inventory = 0
|
||||
@@ -11,7 +11,7 @@ class Placement(IntEnum):
|
||||
somewhere = 2
|
||||
|
||||
class PlacementLogicMeta(AssembleOptions):
|
||||
def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "PlacementLogicMeta":
|
||||
def __new__(mcs, name: str, bases: tuple[type], attrs: dict[Any, Any]) -> "PlacementLogicMeta":
|
||||
if "default" in attrs and isinstance(attrs["default"], Placement):
|
||||
attrs["default"] = int(attrs["default"])
|
||||
|
||||
@@ -24,7 +24,7 @@ class PlacementLogic(Choice, metaclass=PlacementLogicMeta):
|
||||
option_somewhere = Placement.somewhere
|
||||
|
||||
class ChoiceMapMeta(AssembleOptions):
|
||||
def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "ChoiceMapMeta":
|
||||
def __new__(mcs, name: str, bases: tuple[type], attrs: dict[Any, Any]) -> "ChoiceMapMeta":
|
||||
if "choices" in attrs:
|
||||
for index, choice in enumerate(attrs["choices"].keys()):
|
||||
option_name = "option_" + choice.replace(' ', '_')
|
||||
@@ -35,9 +35,9 @@ class ChoiceMapMeta(AssembleOptions):
|
||||
|
||||
class ChoiceMap(Choice, metaclass=ChoiceMapMeta):
|
||||
# TODO `default` doesn't do anything, default is always the first `choices` value. if uncommented it messes up the template file generation (caps mismatch)
|
||||
choices: ClassVar[Dict[str, List[str]]]
|
||||
choices: ClassVar[dict[str, list[str]]]
|
||||
|
||||
def get_selected_list(self) -> List[str]:
|
||||
def get_selected_list(self) -> list[str]:
|
||||
for index, choice in enumerate(self.choices.keys()):
|
||||
if index == self.value:
|
||||
return self.choices[choice]
|
||||
@@ -55,8 +55,8 @@ class ElevatorTier(NamedRange):
|
||||
"one package (tiers 1-2)": 1,
|
||||
"two packages (tiers 1-4)": 2,
|
||||
"three packages (tiers 1-6)": 3,
|
||||
"four packages (tiers 7-8)": 4,
|
||||
"five packages (tier 9)": 5,
|
||||
"four packages (tiers 1-8)": 4,
|
||||
"five packages (tiers 1-9)": 5,
|
||||
}
|
||||
|
||||
class ResourceSinkPoints(NamedRange):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import List, Set, Dict, Tuple, Optional, Callable
|
||||
from typing import Optional
|
||||
from collections.abc import Callable
|
||||
from BaseClasses import MultiWorld, Region, Location, Item, CollectionState
|
||||
from .Locations import LocationData
|
||||
from .GameLogic import GameLogic, PowerInfrastructureLevel
|
||||
@@ -32,9 +33,9 @@ class SatisfactoryLocation(Location):
|
||||
|
||||
def create_regions_and_return_locations(world: MultiWorld, options: SatisfactoryOptions, player: int,
|
||||
game_logic: GameLogic, state_logic: StateLogic, critical_path: CriticalPathCalculator,
|
||||
locations: List[LocationData]):
|
||||
locations: list[LocationData]):
|
||||
|
||||
region_names: List[str] = [
|
||||
region_names: list[str] = [
|
||||
"Overworld",
|
||||
"Mam",
|
||||
"AWESOME Shop"
|
||||
@@ -60,20 +61,20 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
|
||||
if node.minimal_tier <= options.final_elevator_package:
|
||||
region_names.append(f"{tree_name}: {node.name}")
|
||||
|
||||
locations_per_region: Dict[str, LocationData] = get_locations_per_region(locations)
|
||||
regions: Dict[str, Region] = create_regions(world, player, locations_per_region, region_names)
|
||||
locations_per_region: dict[str, LocationData] = get_locations_per_region(locations)
|
||||
regions: dict[str, Region] = create_regions(world, player, locations_per_region, region_names)
|
||||
|
||||
if __debug__:
|
||||
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
|
||||
|
||||
world.regions += regions.values()
|
||||
|
||||
super_early_game_buildings: List[str] = [
|
||||
super_early_game_buildings: list[str] = [
|
||||
"Foundation",
|
||||
"Walls Orange"
|
||||
]
|
||||
|
||||
early_game_buildings: List[str] = [
|
||||
early_game_buildings: list[str] = [
|
||||
PowerInfrastructureLevel.Automated.to_name()
|
||||
]
|
||||
|
||||
@@ -112,7 +113,7 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
|
||||
connect(regions, "Overworld", "AWESOME Shop", lambda state:
|
||||
state_logic.can_build_all(state, ("AWESOME Shop", "AWESOME Sink")))
|
||||
|
||||
def can_produce_all_allowing_handcrafting(parts: Tuple[str, ...]) -> Callable[[CollectionState], bool]:
|
||||
def can_produce_all_allowing_handcrafting(parts: tuple[str, ...]) -> Callable[[CollectionState], bool]:
|
||||
def logic_rule(state: CollectionState):
|
||||
return state_logic.can_produce_all_allowing_handcrafting(state, game_logic, parts)
|
||||
|
||||
@@ -148,7 +149,7 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
|
||||
lambda state, parts=node.unlock_cost.keys(): state_logic.can_produce_all(state, parts))
|
||||
|
||||
|
||||
def throwIfAnyLocationIsNotAssignedToARegion(regions: Dict[str, Region], regionNames: Set[str]):
|
||||
def throwIfAnyLocationIsNotAssignedToARegion(regions: dict[str, Region], regionNames: set[str]):
|
||||
existingRegions = set()
|
||||
|
||||
for region in regions.keys():
|
||||
@@ -159,7 +160,7 @@ def throwIfAnyLocationIsNotAssignedToARegion(regions: Dict[str, Region], regionN
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int,
|
||||
locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
|
||||
locations_per_region: dict[str, list[LocationData]], name: str) -> Region:
|
||||
|
||||
region = Region(name, player, world)
|
||||
|
||||
@@ -171,10 +172,10 @@ def create_region(world: MultiWorld, player: int,
|
||||
return region
|
||||
|
||||
|
||||
def create_regions(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
|
||||
region_names: List[str]) -> Dict[str, Region]:
|
||||
def create_regions(world: MultiWorld, player: int, locations_per_region: dict[str, list[LocationData]],
|
||||
region_names: list[str]) -> dict[str, Region]:
|
||||
|
||||
regions: Dict[str, Region] = {}
|
||||
regions: dict[str, Region] = {}
|
||||
|
||||
for name in region_names:
|
||||
regions[name] = create_region(world, player, locations_per_region, name)
|
||||
@@ -182,7 +183,7 @@ def create_regions(world: MultiWorld, player: int, locations_per_region: Dict[st
|
||||
return regions
|
||||
|
||||
|
||||
def connect(regions: Dict[str, Region], source: str, target: str,
|
||||
def connect(regions: dict[str, Region], source: str, target: str,
|
||||
rule: Optional[Callable[[CollectionState], bool]] = None):
|
||||
|
||||
sourceRegion = regions[source]
|
||||
@@ -191,8 +192,8 @@ def connect(regions: Dict[str, Region], source: str, target: str,
|
||||
sourceRegion.connect(targetRegion, rule=rule)
|
||||
|
||||
|
||||
def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
|
||||
per_region: Dict[str, List[LocationData]] = {}
|
||||
def get_locations_per_region(locations: list[LocationData]) -> dict[str, list[LocationData]]:
|
||||
per_region: dict[str, list[LocationData]] = {}
|
||||
|
||||
for location in locations:
|
||||
per_region.setdefault(location.region, []).append(location)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Tuple, List, Optional, Set, Iterable
|
||||
from typing import Optional
|
||||
from collections.abc import Iterable
|
||||
from BaseClasses import CollectionState
|
||||
from .GameLogic import GameLogic, Recipe, PowerInfrastructureLevel
|
||||
from .Options import SatisfactoryOptions
|
||||
@@ -11,7 +12,7 @@ building_event_prefix = "Can Build: "
|
||||
class StateLogic:
|
||||
player: int
|
||||
options: SatisfactoryOptions
|
||||
initial_unlocked_items: Set[str]
|
||||
initial_unlocked_items: set[str]
|
||||
|
||||
def __init__(self, player: int, options: SatisfactoryOptions):
|
||||
self.player = player
|
||||
@@ -42,7 +43,7 @@ class StateLogic:
|
||||
state.has_all(map(self.to_part_event, parts), self.player)
|
||||
|
||||
def can_produce_all_allowing_handcrafting(self, state: CollectionState, logic: GameLogic,
|
||||
parts: Optional[Tuple[str, ...]]) -> bool:
|
||||
parts: Optional[tuple[str, ...]]) -> bool:
|
||||
|
||||
def can_handcraft_part(part: str) -> bool:
|
||||
if self.can_produce(state, part):
|
||||
@@ -50,7 +51,7 @@ class StateLogic:
|
||||
elif part not in logic.handcraftable_recipes:
|
||||
return False
|
||||
|
||||
recipes: List[Recipe] = logic.handcraftable_recipes[part]
|
||||
recipes: list[Recipe] = logic.handcraftable_recipes[part]
|
||||
|
||||
return any(
|
||||
self.has_recipe(state, recipe)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from BaseClasses import Tutorial
|
||||
from ..AutoWorld import WebWorld
|
||||
|
||||
|
||||
class SatisfactoryWebWorld(WebWorld):
|
||||
theme = "dirt"
|
||||
setup = Tutorial(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Dict, List, Set, TextIO, ClassVar
|
||||
from typing import TextIO, ClassVar
|
||||
from BaseClasses import Item, ItemClassification, CollectionState
|
||||
from .GameLogic import GameLogic
|
||||
from .Items import Items
|
||||
@@ -52,13 +52,13 @@ class SatisfactoryWorld(World):
|
||||
if not self.options.trap_selection_override.value:
|
||||
self.options.trap_selection_override.value = self.options.trap_selection_preset.get_selected_list()
|
||||
|
||||
starting_inventory: List[str] = self.options.starting_inventory_preset.get_selected_list()
|
||||
starting_inventory: list[str] = self.options.starting_inventory_preset.get_selected_list()
|
||||
for item_name in starting_inventory:
|
||||
self.push_precollected(item_name)
|
||||
|
||||
|
||||
def create_regions(self) -> None:
|
||||
locations: List[LocationData] = \
|
||||
locations: list[LocationData] = \
|
||||
Locations(self.game_logic, self.options, self.state_logic, self.items, self.critical_path).get_locations()
|
||||
create_regions_and_return_locations(
|
||||
self.multiworld, self.options, self.player, self.game_logic, self.state_logic, self.critical_path,
|
||||
@@ -76,8 +76,7 @@ class SatisfactoryWorld(World):
|
||||
def set_rules(self) -> None:
|
||||
resource_sink_goal: bool = "AWESOME Sink Points" in self.options.goal_selection
|
||||
|
||||
required_parts: Set[str] = \
|
||||
set(self.game_logic.space_elevator_tiers[self.options.final_elevator_package.value - 1].keys())
|
||||
required_parts = set(self.game_logic.space_elevator_tiers[self.options.final_elevator_package.value - 1].keys())
|
||||
|
||||
if resource_sink_goal:
|
||||
required_parts.union(self.game_logic.buildings["AWESOME Sink"].inputs)
|
||||
@@ -100,8 +99,8 @@ class SatisfactoryWorld(World):
|
||||
return change
|
||||
|
||||
|
||||
def fill_slot_data(self) -> Dict[str, object]:
|
||||
slot_hub_layout: List[List[Dict[str, int]]] = []
|
||||
def fill_slot_data(self) -> dict[str, object]:
|
||||
slot_hub_layout: list[list[dict[str, int]]] = []
|
||||
|
||||
for tier, milestones in enumerate(self.game_logic.hub_layout, 1):
|
||||
slot_hub_layout.append([])
|
||||
|
||||
Reference in New Issue
Block a user