AutoWorld: basic Item handling
This commit is contained in:
parent
babd809fa6
commit
31c550d410
|
@ -272,6 +272,8 @@ class MultiWorld():
|
|||
return next(location for location in self.get_locations() if
|
||||
location.item and location.item.name == item and location.item.player == player)
|
||||
|
||||
def create_item(self, item_name: str, player: int) -> Item:
|
||||
return self.worlds[player].create_item(item_name)
|
||||
|
||||
def push_precollected(self, item: Item):
|
||||
item.world = self
|
||||
|
@ -859,7 +861,6 @@ class CollectionState(object):
|
|||
(self.has('Progressive Weapons', player, 1) and self.has('Bed', player)))
|
||||
return respawn_dragon and self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player)
|
||||
|
||||
|
||||
def collect(self, item: Item, event: bool = False, location: Location = None) -> bool:
|
||||
if location:
|
||||
self.locations_checked.add(location)
|
||||
|
|
6
Main.py
6
Main.py
|
@ -9,7 +9,7 @@ import pickle
|
|||
from typing import Dict, Tuple
|
||||
|
||||
from BaseClasses import MultiWorld, CollectionState, Region, Item
|
||||
from worlds.alttp.Items import ItemFactory, item_name_groups
|
||||
from worlds.alttp.Items import item_name_groups
|
||||
from worlds.alttp.Regions import create_regions, mark_light_world_regions, \
|
||||
lookup_vanilla_location_to_entrance
|
||||
from worlds.alttp.InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||
|
@ -453,8 +453,8 @@ def main(args, seed=None):
|
|||
for index, take_any in enumerate(takeanyregions):
|
||||
for region in [world.get_region(take_any, player) for player in range(1, world.players + 1) if
|
||||
world.retro[player]]:
|
||||
item = ItemFactory(region.shop.inventory[(0 if take_any == "Old Man Sword Cave" else 1)]['item'],
|
||||
region.player)
|
||||
item = world.create_item(region.shop.inventory[(0 if take_any == "Old Man Sword Cave" else 1)]['item'],
|
||||
region.player)
|
||||
player = region.player
|
||||
location_id = SHOP_ID_START + total_shop_slots + index
|
||||
|
||||
|
|
|
@ -516,10 +516,11 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
|||
ret.game = get_choice("game", weights)
|
||||
if ret.game not in weights:
|
||||
raise Exception(f"No game options for selected game \"{ret.game}\" found.")
|
||||
world_type = AutoWorldRegister.world_types[ret.game]
|
||||
game_weights = weights[ret.game]
|
||||
ret.local_items = set()
|
||||
for item_name in game_weights.get('local_items', []):
|
||||
items = item_name_groups.get(item_name, {item_name})
|
||||
items = world_type.item_name_groups.get(item_name, {item_name})
|
||||
for item in items:
|
||||
if item in lookup_any_item_name_to_id:
|
||||
ret.local_items.add(item)
|
||||
|
@ -528,7 +529,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
|||
|
||||
ret.non_local_items = set()
|
||||
for item_name in game_weights.get('non_local_items', []):
|
||||
items = item_name_groups.get(item_name, {item_name})
|
||||
items = world_type.item_name_groups.get(item_name, {item_name})
|
||||
for item in items:
|
||||
if item in lookup_any_item_name_to_id:
|
||||
ret.non_local_items.add(item)
|
||||
|
@ -556,7 +557,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
|||
else:
|
||||
setattr(ret, option_name, option.from_any(get_choice(option_name, game_weights)))
|
||||
except Exception as e:
|
||||
raise Exception(f"Error generating option {option_name} in {ret.game}")
|
||||
raise Exception(f"Error generating option {option_name} in {ret.game}") from e
|
||||
else:
|
||||
setattr(ret, option_name, option(option.default))
|
||||
if ret.game == "Minecraft":
|
||||
|
@ -778,7 +779,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
|||
get_choice("direction", placement, "both")
|
||||
))
|
||||
|
||||
|
||||
ret.sprite_pool = weights.get('sprite_pool', [])
|
||||
ret.sprite = get_choice('sprite', weights, "Link")
|
||||
if 'random_sprite_on_event' in weights:
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from BaseClasses import MultiWorld
|
||||
from typing import Dict, Set, Tuple
|
||||
|
||||
from BaseClasses import MultiWorld, Item, CollectionState
|
||||
|
||||
|
||||
class AutoWorldRegister(type):
|
||||
|
@ -29,6 +31,9 @@ class World(metaclass=AutoWorldRegister):
|
|||
player: int
|
||||
options: dict = {}
|
||||
topology_present: bool = False # indicate if world type has any meaningful layout/pathing
|
||||
item_names: Set[str] = frozenset()
|
||||
# maps item group names to sets of items. Example: "Weapons" -> {"Sword", "Bow"}
|
||||
item_name_groups: Dict[str, Set[str]] = {}
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
self.world = world
|
||||
|
@ -49,15 +54,19 @@ class World(metaclass=AutoWorldRegister):
|
|||
If you need any last-second randomization, use MultiWorld.slot_seeds[slot] instead."""
|
||||
pass
|
||||
|
||||
def get_required_client_version(self) -> tuple:
|
||||
def get_required_client_version(self) -> Tuple[int, int, int]:
|
||||
return 0, 0, 3
|
||||
|
||||
# end of Main.py calls
|
||||
|
||||
def collect(self, state, item) -> bool:
|
||||
def collect(self, state: CollectionState, item: Item) -> bool:
|
||||
"""Collect an item into state. For speed reasons items that aren't logically useful get skipped."""
|
||||
if item.advancement:
|
||||
state.prog_items[item.name, item.player] += 1
|
||||
return True # indicate that a logical state change has occured
|
||||
return False
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
"""Create an item for this world type and player.
|
||||
Warning: this may be called with self.world = None, for example by MultiServer"""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -6,17 +6,19 @@ def GetBeemizerItem(world, player, item):
|
|||
if world.beemizer[player] and item_name in trap_replaceable:
|
||||
if world.random.random() < world.beemizer[player] * 0.25:
|
||||
if world.random.random() < (0.5 + world.beemizer[player] * 0.1):
|
||||
return "Bee Trap" if isinstance(item, str) else ItemFactory("Bee Trap", player)
|
||||
return "Bee Trap" if isinstance(item, str) else world.create_item("Bee Trap", player)
|
||||
else:
|
||||
return "Bee" if isinstance(item, str) else ItemFactory("Bee", player)
|
||||
return "Bee" if isinstance(item, str) else world.create_item("Bee", player)
|
||||
else:
|
||||
return item
|
||||
else:
|
||||
return item
|
||||
|
||||
|
||||
def ItemFactory(items, player):
|
||||
from worlds.alttp import ALttPItem
|
||||
# should be replaced with direct world.create_item(item) call in the future
|
||||
def ItemFactory(items, player: int):
|
||||
from worlds.alttp import ALTTPWorld
|
||||
world = ALTTPWorld(None, player)
|
||||
ret = []
|
||||
singleton = False
|
||||
if isinstance(items, str):
|
||||
|
@ -24,7 +26,7 @@ def ItemFactory(items, player):
|
|||
singleton = True
|
||||
for item in items:
|
||||
if item in item_table:
|
||||
ret.append(ALttPItem(item, *item_table[item], player))
|
||||
ret.append(world.create_item(item))
|
||||
else:
|
||||
raise Exception(f"Unknown item {item}")
|
||||
|
||||
|
@ -211,6 +213,8 @@ item_table = {'Bow': ItemData(True, None, 0x0B, 'You have\nchosen the\narcher cl
|
|||
'Open Floodgate': ItemData(True, 'Event', None, None, None, None, None, None, None, None),
|
||||
}
|
||||
|
||||
as_dict_item_table = {name: data._asdict() for name, data in item_table.items()}
|
||||
|
||||
progression_mapping = {
|
||||
"Golden Sword": ("Progressive Sword", 4),
|
||||
"Tempered Sword": ("Progressive Sword", 3),
|
||||
|
@ -268,8 +272,8 @@ for basename, substring in _simple_groups:
|
|||
|
||||
del (_simple_groups)
|
||||
|
||||
progression_items = {name for name, data in item_table.items() if type(data[2]) == int and data[0]}
|
||||
item_name_groups['Everything'] = {name for name, data in item_table.items() if type(data[2]) == int}
|
||||
progression_items = {name for name, data in item_table.items() if type(data.item_code) == int and data.advancement}
|
||||
item_name_groups['Everything'] = {name for name, data in item_table.items() if type(data.item_code) == int}
|
||||
item_name_groups['Progression Items'] = progression_items
|
||||
item_name_groups['Non Progression Items'] = item_name_groups['Everything'] - progression_items
|
||||
|
||||
|
|
|
@ -3,11 +3,15 @@ from typing import Optional
|
|||
from BaseClasses import Location, Item, CollectionState
|
||||
from ..AutoWorld import World
|
||||
from .Options import alttp_options
|
||||
from .Items import as_dict_item_table, item_name_groups, item_table
|
||||
|
||||
|
||||
class ALTTPWorld(World):
|
||||
game: str = "A Link to the Past"
|
||||
options = alttp_options
|
||||
topology_present = True
|
||||
item_name_groups = item_name_groups
|
||||
item_names = frozenset(item_table)
|
||||
|
||||
def collect(self, state: CollectionState, item: Item) -> bool:
|
||||
if item.name.startswith('Progressive '):
|
||||
|
@ -66,6 +70,9 @@ class ALTTPWorld(World):
|
|||
def get_required_client_version(self) -> tuple:
|
||||
return max((0, 1, 4), super(ALTTPWorld, self).get_required_client_version())
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return ALttPItem(name, self.player, **as_dict_item_table[name])
|
||||
|
||||
|
||||
class ALttPLocation(Location):
|
||||
game: str = "A Link to the Past"
|
||||
|
@ -80,16 +87,16 @@ class ALttPLocation(Location):
|
|||
|
||||
|
||||
class ALttPItem(Item):
|
||||
|
||||
game: str = "A Link to the Past"
|
||||
|
||||
def __init__(self, name='', advancement=False, type=None, code=None, pedestal_hint=None, pedestal_credit=None, sickkid_credit=None, zora_credit=None, witch_credit=None, fluteboy_credit=None, hint_text=None, player=None):
|
||||
super(ALttPItem, self).__init__(name, advancement, code, player)
|
||||
def __init__(self, name, player, advancement=False, type=None, item_code=None, pedestal_hint=None, pedestal_credit=None,
|
||||
sick_kid_credit=None, zora_credit=None, witch_credit=None, flute_boy_credit=None, hint_text=None):
|
||||
super(ALttPItem, self).__init__(name, advancement, item_code, player)
|
||||
self.type = type
|
||||
self._pedestal_hint_text = pedestal_hint
|
||||
self.pedestal_credit_text = pedestal_credit
|
||||
self.sickkid_credit_text = sickkid_credit
|
||||
self.sickkid_credit_text = sick_kid_credit
|
||||
self.zora_credit_text = zora_credit
|
||||
self.magicshop_credit_text = witch_credit
|
||||
self.fluteboy_credit_text = fluteboy_credit
|
||||
self.fluteboy_credit_text = flute_boy_credit
|
||||
self._hint_text = hint_text
|
|
@ -4,29 +4,30 @@ from BaseClasses import Region, Entrance, Location, Item
|
|||
from .Technologies import base_tech_table, recipe_sources, base_technology_table, advancement_technologies, \
|
||||
all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes, \
|
||||
progressive_technology_table, common_tech_table, tech_to_progressive_lookup, progressive_tech_table, \
|
||||
get_science_pack_pools, Recipe, recipes, technology_table
|
||||
get_science_pack_pools, Recipe, recipes, technology_table, tech_table
|
||||
from .Shapes import get_shapes
|
||||
from .Mod import generate_mod
|
||||
from .Options import factorio_options
|
||||
|
||||
|
||||
class FactorioItem(Item):
|
||||
game = "Factorio"
|
||||
|
||||
|
||||
class Factorio(World):
|
||||
game: str = "Factorio"
|
||||
static_nodes = {"automation", "logistics", "rocket-silo"}
|
||||
custom_recipes = {}
|
||||
additional_advancement_technologies = set()
|
||||
item_names = frozenset(tech_table)
|
||||
|
||||
def generate_basic(self):
|
||||
for tech_name, tech_id in base_tech_table.items():
|
||||
if self.world.progressive and tech_name in tech_to_progressive_lookup:
|
||||
item_name = tech_to_progressive_lookup[tech_name]
|
||||
tech_id = progressive_tech_table[item_name]
|
||||
for tech_name in base_tech_table:
|
||||
if self.world.progressive:
|
||||
item_name = tech_to_progressive_lookup.get(tech_name, tech_name)
|
||||
else:
|
||||
item_name = tech_name
|
||||
|
||||
tech_item = Item(item_name, item_name in advancement_technologies or
|
||||
item_name in self.additional_advancement_technologies,
|
||||
tech_id, self.player)
|
||||
tech_item.game = "Factorio"
|
||||
item_name = item_name
|
||||
tech_item = self.create_item(item_name)
|
||||
if tech_name in self.static_nodes:
|
||||
self.world.get_location(tech_name, self.player).place_locked_item(tech_item)
|
||||
else:
|
||||
|
@ -157,3 +158,9 @@ class Factorio(World):
|
|||
if tech in tech_to_progressive_lookup:
|
||||
prog_add.add(tech_to_progressive_lookup[tech])
|
||||
self.additional_advancement_technologies |= prog_add
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
assert name in tech_table
|
||||
return FactorioItem(name, name in advancement_technologies or
|
||||
name in self.additional_advancement_technologies,
|
||||
tech_table[name], self.player)
|
|
@ -1,6 +1,7 @@
|
|||
from typing import NamedTuple, Union
|
||||
import logging
|
||||
|
||||
|
||||
class PlandoItem(NamedTuple):
|
||||
item: str
|
||||
location: str
|
||||
|
@ -25,7 +26,3 @@ class PlandoConnection(NamedTuple):
|
|||
entrance: str
|
||||
exit: str
|
||||
direction: str # entrance, exit or both
|
||||
|
||||
|
||||
class World():
|
||||
pass
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import logging
|
||||
from typing import Set
|
||||
|
||||
logger = logging.getLogger("Hollow Knight")
|
||||
|
||||
|
@ -14,6 +15,7 @@ from ..AutoWorld import World
|
|||
class HKWorld(World):
|
||||
game: str = "Hollow Knight"
|
||||
options = hollow_knight_options
|
||||
item_names: Set[str] = frozenset(item_table)
|
||||
|
||||
def generate_basic(self):
|
||||
# Link regions
|
||||
|
@ -22,8 +24,7 @@ class HKWorld(World):
|
|||
# Generate item pool
|
||||
pool = []
|
||||
for item_name, item_data in item_table.items():
|
||||
|
||||
item = HKItem(item_name, item_data.advancement, item_data.id, item_data.type, player=self.player)
|
||||
item = self.create_item(item_name)
|
||||
|
||||
if item_data.type == "Event":
|
||||
event_location = self.world.get_location(item_name, self.player)
|
||||
|
@ -83,6 +84,9 @@ class HKWorld(World):
|
|||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_data = item_table[name]
|
||||
return HKItem(name, item_data.advancement, item_data.id, item_data.type, self.player)
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, None, name, player)
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from BaseClasses import Item
|
||||
import typing
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
code: int
|
||||
progression: bool
|
||||
|
||||
|
||||
class MinecraftItem(Item):
|
||||
game: str = "Minecraft"
|
||||
def __init__(self, name: str, progression: bool, code: int, player: int):
|
||||
super().__init__(name, progression, code if code else None, player)
|
||||
|
||||
|
||||
item_table = {
|
||||
"Archery": ItemData(45000, True),
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
from typing import Dict, Set
|
||||
|
||||
|
||||
from .Items import MinecraftItem, item_table, item_frequencies
|
||||
from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, events_table
|
||||
from .Regions import mc_regions, link_minecraft_structures
|
||||
from .Rules import set_rules
|
||||
|
||||
from BaseClasses import Region, Entrance
|
||||
from BaseClasses import Region, Entrance, Item
|
||||
from .Options import minecraft_options
|
||||
from ..AutoWorld import World
|
||||
|
||||
|
@ -13,6 +16,7 @@ class MinecraftWorld(World):
|
|||
game: str = "Minecraft"
|
||||
options = minecraft_options
|
||||
topology_present = True
|
||||
item_names = frozenset(item_table)
|
||||
|
||||
def _get_mc_data(self):
|
||||
exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2",
|
||||
|
@ -27,7 +31,6 @@ class MinecraftWorld(World):
|
|||
'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits}
|
||||
}
|
||||
|
||||
|
||||
def generate_basic(self):
|
||||
link_minecraft_structures(self.world, self.player)
|
||||
|
||||
|
@ -35,9 +38,9 @@ class MinecraftWorld(World):
|
|||
pool_counts = item_frequencies.copy()
|
||||
if getattr(self.world, "bee_traps")[self.player]:
|
||||
pool_counts.update({"Rotten Flesh": 0, "Bee Trap (Minecraft)": 4})
|
||||
for item_name, item_data in item_table.items():
|
||||
for item_name in item_table:
|
||||
for count in range(pool_counts.get(item_name, 1)):
|
||||
pool.append(MinecraftItem(item_name, item_data.progression, item_data.code, self.player))
|
||||
pool.append(self.create_item(item_name))
|
||||
|
||||
prefill_pool = {}
|
||||
prefill_pool.update(events_table)
|
||||
|
@ -49,7 +52,7 @@ class MinecraftWorld(World):
|
|||
for loc_name, item_name in prefill_pool.items():
|
||||
item_data = item_table[item_name]
|
||||
location = self.world.get_location(loc_name, self.player)
|
||||
item = MinecraftItem(item_name, item_data.progression, item_data.code, self.player)
|
||||
item = self.create_item(item_name)
|
||||
self.world.push_item(location, item, collect=False)
|
||||
pool.remove(item)
|
||||
location.event = item_data.progression
|
||||
|
@ -57,11 +60,9 @@ class MinecraftWorld(World):
|
|||
|
||||
self.world.itempool += pool
|
||||
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
|
||||
|
||||
def create_regions(self):
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, None, region_name, self.player)
|
||||
|
@ -75,7 +76,6 @@ class MinecraftWorld(World):
|
|||
|
||||
self.world.regions += [MCRegion(*r) for r in mc_regions]
|
||||
|
||||
|
||||
def generate_output(self):
|
||||
import json
|
||||
from base64 import b64encode
|
||||
|
@ -86,10 +86,13 @@ class MinecraftWorld(World):
|
|||
with open(output_path(filename), 'wb') as f:
|
||||
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = self._get_mc_data()
|
||||
for option_name in minecraft_options:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_data = item_table[name]
|
||||
return MinecraftItem(name, item_data.progression, item_data.code, self.player)
|
Loading…
Reference in New Issue