Files
Grinch-AP/worlds/shapez/items.py

280 lines
12 KiB
Python
Raw Normal View History

from typing import Callable, Any
from BaseClasses import Item, ItemClassification as IClass
from .options import ShapezOptions
from .data.strings import GOALS, ITEMS, OTHER
def is_mam_achievement_included(options: ShapezOptions) -> IClass:
return IClass.progression if options.include_achievements and (not options.goal == GOALS.vanilla) else IClass.useful
def is_achievements_included(options: ShapezOptions) -> IClass:
return IClass.progression if options.include_achievements else IClass.useful
def is_goal_efficiency_iii(options: ShapezOptions) -> IClass:
return IClass.progression if options.goal == GOALS.efficiency_iii else IClass.useful
def always_progression(options: ShapezOptions) -> IClass:
return IClass.progression
def always_useful(options: ShapezOptions) -> IClass:
return IClass.useful
def always_filler(options: ShapezOptions) -> IClass:
return IClass.filler
def always_trap(options: ShapezOptions) -> IClass:
return IClass.trap
# Routing buildings are not needed to complete the game, but building factories without balancers and tunnels
# would be unreasonably complicated and time-consuming.
# Some buildings are not needed to complete the game, but are "logically needed" for the "MAM" achievement.
buildings_processing: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.cutter: always_progression,
ITEMS.cutter_quad: always_progression,
ITEMS.rotator: always_progression,
ITEMS.rotator_ccw: always_progression,
ITEMS.rotator_180: always_progression,
ITEMS.stacker: always_progression,
ITEMS.painter: always_progression,
ITEMS.painter_double: always_progression,
ITEMS.painter_quad: always_progression,
ITEMS.color_mixer: always_progression,
}
buildings_routing: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.balancer: always_progression,
ITEMS.comp_merger: always_progression,
ITEMS.comp_splitter: always_progression,
ITEMS.tunnel: always_progression,
ITEMS.tunnel_tier_ii: is_mam_achievement_included,
}
buildings_other: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.trash: always_progression,
ITEMS.extractor_chain: always_useful
}
buildings_top_row: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.belt_reader: is_mam_achievement_included,
ITEMS.storage: is_achievements_included,
ITEMS.switch: always_progression,
ITEMS.item_filter: is_mam_achievement_included,
ITEMS.display: always_useful
}
buildings_wires: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.wires: always_progression,
ITEMS.const_signal: always_progression,
ITEMS.logic_gates: is_mam_achievement_included,
ITEMS.virtual_proc: is_mam_achievement_included
}
gameplay_unlocks: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.blueprints: is_achievements_included
}
upgrades: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.upgrade_big_belt: always_progression,
ITEMS.upgrade_big_miner: always_useful,
ITEMS.upgrade_big_proc: always_useful,
ITEMS.upgrade_big_paint: always_useful,
ITEMS.upgrade_small_belt: always_filler,
ITEMS.upgrade_small_miner: always_filler,
ITEMS.upgrade_small_proc: always_filler,
ITEMS.upgrade_small_paint: always_filler
}
whacky_upgrades: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.upgrade_gigantic_belt: always_progression,
ITEMS.upgrade_gigantic_miner: always_useful,
ITEMS.upgrade_gigantic_proc: always_useful,
ITEMS.upgrade_gigantic_paint: always_useful,
ITEMS.upgrade_rising_belt: always_progression,
ITEMS.upgrade_rising_miner: always_useful,
ITEMS.upgrade_rising_proc: always_useful,
ITEMS.upgrade_rising_paint: always_useful,
ITEMS.upgrade_big_random: always_useful,
ITEMS.upgrade_small_random: always_filler,
}
whacky_upgrade_traps: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.trap_upgrade_belt: always_trap,
ITEMS.trap_upgrade_miner: always_trap,
ITEMS.trap_upgrade_proc: always_trap,
ITEMS.trap_upgrade_paint: always_trap,
ITEMS.trap_upgrade_demonic_belt: always_trap,
ITEMS.trap_upgrade_demonic_miner: always_trap,
ITEMS.trap_upgrade_demonic_proc: always_trap,
ITEMS.trap_upgrade_demonic_paint: always_trap,
}
bundles: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.bundle_blueprint: always_filler,
ITEMS.bundle_level: always_filler,
ITEMS.bundle_upgrade: always_filler
}
standard_traps: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.trap_locked: always_trap,
ITEMS.trap_throttled: always_trap,
ITEMS.trap_malfunction: always_trap,
ITEMS.trap_inflation: always_trap,
ITEMS.trap_clear_belts: always_trap,
}
random_draining_trap: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.trap_draining_inv: always_trap
}
split_draining_traps: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.trap_draining_blueprint: always_trap,
ITEMS.trap_draining_level: always_trap,
ITEMS.trap_draining_upgrade: always_trap
}
belt_and_extractor: dict[str, Callable[[ShapezOptions], IClass]] = {
ITEMS.belt: always_progression,
ITEMS.extractor: always_progression
}
item_table: dict[str, Callable[[ShapezOptions], IClass]] = {
**buildings_processing,
**buildings_routing,
**buildings_other,
**buildings_top_row,
**buildings_wires,
**gameplay_unlocks,
**upgrades,
**whacky_upgrades,
**whacky_upgrade_traps,
**bundles,
**standard_traps,
**random_draining_trap,
**split_draining_traps,
**belt_and_extractor
}
big_upgrades = [
ITEMS.upgrade_big_belt,
ITEMS.upgrade_big_miner,
ITEMS.upgrade_big_proc,
ITEMS.upgrade_big_paint
]
small_upgrades = [
ITEMS.upgrade_small_belt,
ITEMS.upgrade_small_miner,
ITEMS.upgrade_small_proc,
ITEMS.upgrade_small_paint
]
def filler(random: float, whacky_allowed: bool) -> str:
"""Returns a random filler item."""
bundles_list = [*bundles]
return random_choice_nested(random, [
small_upgrades,
[
bundles_list,
bundles_list,
[
big_upgrades,
[*whacky_upgrades] if whacky_allowed else big_upgrades,
],
],
])
def trap(random: float, split_draining: bool, whacky_allowed: bool) -> str:
"""Returns a random trap item."""
pool = [
*standard_traps,
ITEMS.trap_draining_inv if not split_draining else [*split_draining_traps],
]
if whacky_allowed:
pool.append([*whacky_upgrade_traps])
return random_choice_nested(random, pool)
def random_choice_nested(random: float, nested: list[Any]) -> Any:
"""Helper function for getting a random element from a nested list."""
current: Any = nested
while isinstance(current, list):
index_float = random*len(current)
current = current[int(index_float)]
random = index_float-int(index_float)
return current
item_descriptions = { # TODO replace keys with global strings and update with whacky upgrades
"Balancer": "A routing building, that can merge two belts into one, split a belt in two, " +
"or balance the items of two belts",
"Tunnel": "A routing building consisting of two parts, that allows for gaps in belts",
"Compact Merger": "A small routing building, that merges two belts into one",
"Tunnel Tier II": "A routing building consisting of two parts, that allows for even longer gaps in belts",
"Compact Splitter": "A small routing building, that splits a belt in two",
"Cutter": "A processing building, that cuts shapes vertically in two halves",
"Rotator": "A processing building, that rotates shapes 90 degrees clockwise",
"Painter": "A processing building, that paints shapes in a given color",
"Rotator (CCW)": "A processing building, that rotates shapes 90 degrees counter-clockwise",
"Color Mixer": "A processing building, that mixes two colors together to create a new one",
"Stacker": "A processing building, that combines two shapes with missing parts or puts one on top of the other",
"Quad Cutter": "A processing building, that cuts shapes in four quarter parts",
"Double Painter": "A processing building, that paints two shapes in a given color",
"Rotator (180°)": "A processing building, that rotates shapes 180 degrees",
"Quad Painter": "A processing building, that paint each quarter of a shape in another given color and requires " +
"wire inputs for each color to work",
"Trash": "A building, that destroys unused shapes",
"Chaining Extractor": "An upgrade to extractors, that can increase the output without balancers or mergers",
"Belt Reader": "A wired building, that shows the average amount of items passing through per second",
"Storage": "A building, that stores up to 5000 of a certain shape",
"Switch": "A building, that sends a constant boolean signal",
"Item Filter": "A wired building, that filters items based on wire input",
"Display": "A wired building, that displays a shape or color based on wire input",
"Wires": "The main building of the wires layer, that carries signals between other buildings",
"Constant Signal": "A building on the wires layer, that sends a constant shape, color, or boolean signal",
"Logic Gates": "Multiple buildings on the wires layer, that perform logical operations on wire signals",
"Virtual Processing": "Multiple buildings on the wires layer, that process wire signals like processor buildings",
"Blueprints": "A game mechanic, that allows copy-pasting multiple buildings at once",
"Big Belt Upgrade": "An upgrade, that adds 1 to the speed multiplier of belts, distributors, and tunnels",
"Big Miner Upgrade": "An upgrade, that adds 1 to the speed multiplier of extractors",
"Big Processors Upgrade": "An upgrade, that adds 1 to the speed multiplier of cutters, rotators, and stackers",
"Big Painting Upgrade": "An upgrade, that adds 1 to the speed multiplier of painters and color mixers",
"Small Belt Upgrade": "An upgrade, that adds 0.1 to the speed multiplier of belts, distributors, and tunnels",
"Small Miner Upgrade": "An upgrade, that adds 0.1 to the speed multiplier of extractors",
"Small Processors Upgrade": "An upgrade, that adds 0.1 to the speed multiplier of cutters, rotators, and stackers",
"Small Painting Upgrade": "An upgrade, that adds 0.1 to the speed multiplier of painters and color mixers",
"Blueprint Shapes Bundle": "A bundle with 1000 blueprint shapes, instantly delivered to the hub",
"Level Shapes Bundle": "A bundle with some shapes needed for the current level, " +
"instantly delivered to the hub",
"Upgrade Shapes Bundle": "A bundle with some shapes needed for a random upgrade, " +
"instantly delivered to the hub",
"Inventory Draining Trap": "Randomly drains either blueprint shapes, current level requirement shapes, " +
"or random upgrade requirement shapes, by half",
"Blueprint Shapes Draining Trap": "Drains the stored blueprint shapes by half",
"Level Shapes Draining Trap": "Drains the current level requirement shapes by half",
"Upgrade Shapes Draining Trap": "Drains a random upgrade requirement shape by half",
"Locked Building Trap": "Locks a random building from being placed for 15-60 seconds",
"Throttled Building Trap": "Halves the speed of a random building for 15-60 seconds",
"Malfunctioning Trap": "Makes a random building process items incorrectly for 15-60 seconds",
"Inflation Trap": "Permanently increases the required shapes multiplier by 1. "
"In other words: Permanently increases required shapes by 10% of the standard amount.",
"Belt": "One of the most important buildings in the game, that transports your shapes and colors from one " +
"place to another",
"Extractor": "One of the most important buildings in the game, that extracts shapes from those randomly " +
"generated patches"
}
class ShapezItem(Item):
game = OTHER.game_name