Merge remote-tracking branch 'Espeon/minecraft' into Archipelago_Main
This commit is contained in:
commit
8030db03ad
|
@ -894,13 +894,15 @@ class CollectionState(object):
|
|||
return self.fortress_loot(player) and normal_kill
|
||||
|
||||
def can_kill_ender_dragon(self, player: int):
|
||||
# Since it is possible to kill the dragon without getting any of the advancements related to it, we need to require that it can be respawned.
|
||||
respawn_dragon = self.can_reach('The Nether', 'Region', player) and self.has('Ingot Crafting', player)
|
||||
if self.combat_difficulty(player) == 'easy':
|
||||
return self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and self.has('Archery', player) and \
|
||||
self.can_brew_potions(player) and self.can_enchant(player)
|
||||
return respawn_dragon and self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and \
|
||||
self.has('Archery', player) and self.can_brew_potions(player) and self.can_enchant(player)
|
||||
if self.combat_difficulty(player) == 'hard':
|
||||
return (self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \
|
||||
(self.has('Progressive Weapons', player, 1) and self.has('Bed', player))
|
||||
return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player)
|
||||
return respawn_dragon and ((self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \
|
||||
(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:
|
||||
|
|
12
Main.py
12
Main.py
|
@ -21,8 +21,6 @@ from Fill import distribute_items_restrictive, flood_items, balance_multiworld_p
|
|||
from worlds.alttp.Shops import create_shops, ShopSlotFill, SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
|
||||
from worlds.alttp.ItemPool import generate_itempool, difficulties, fill_prizes
|
||||
from Utils import output_path, parse_player_names, get_options, __version__, version_tuple
|
||||
from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data
|
||||
from worlds.minecraft.Regions import minecraft_create_regions
|
||||
from worlds.generic.Rules import locality_rules
|
||||
from worlds import Games, lookup_any_item_name_to_id, AutoWorld
|
||||
import Patch
|
||||
|
@ -196,9 +194,6 @@ def main(args, seed=None):
|
|||
|
||||
AutoWorld.call_all(world, "create_regions")
|
||||
|
||||
for player in world.minecraft_player_ids:
|
||||
minecraft_create_regions(world, player)
|
||||
|
||||
for player in world.alttp_player_ids:
|
||||
if world.open_pyramid[player] == 'goal':
|
||||
world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt',
|
||||
|
@ -260,9 +255,6 @@ def main(args, seed=None):
|
|||
|
||||
AutoWorld.call_all(world, "generate_basic")
|
||||
|
||||
for player in world.minecraft_player_ids:
|
||||
gen_minecraft(world, player)
|
||||
|
||||
logger.info("Running Item Plando")
|
||||
|
||||
for item in world.itempool:
|
||||
|
@ -511,7 +503,7 @@ def main(args, seed=None):
|
|||
for slot in world.hk_player_ids:
|
||||
slot_data[slot] = AutoWorld.call_single(world, "fill_slot_data", slot)
|
||||
for slot in world.minecraft_player_ids:
|
||||
slot_data[slot] = fill_minecraft_slot_data(world, slot)
|
||||
slot_data[slot] = AutoWorld.call_single(world, "fill_slot_data", slot)
|
||||
|
||||
locations_data: Dict[int, Dict[int, Tuple[int, int]]] = {player: {} for player in world.player_ids}
|
||||
for location in world.get_filled_locations():
|
||||
|
@ -563,8 +555,6 @@ def main(args, seed=None):
|
|||
if multidata_task:
|
||||
multidata_task.result() # retrieve exception if one exists
|
||||
pool.shutdown() # wait for all queued tasks to complete
|
||||
for player in world.minecraft_player_ids: # Doing this after shutdown prevents the .apmc from being generated if there's an error
|
||||
generate_mc_data(world, player)
|
||||
if not args.skip_playthrough:
|
||||
logger.info('Calculating playthrough.')
|
||||
create_playthrough(world)
|
||||
|
|
|
@ -175,6 +175,9 @@ class Range(Option, int):
|
|||
return cls(data)
|
||||
return cls.from_text(str(data))
|
||||
|
||||
def get_option_name(self):
|
||||
return str(self.value)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
|
|
|
@ -102,10 +102,7 @@ Factorio:
|
|||
burner-mining-drill: 19
|
||||
stone-furnace: 19
|
||||
Minecraft:
|
||||
advancement_goal: # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game.
|
||||
few: 0 # 30 advancements
|
||||
normal: 1 # 50
|
||||
many: 0 # 70
|
||||
advancement_goal: 50 # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game.
|
||||
combat_difficulty: # Modifies the level of items logically required for exploring dangerous areas and fighting bosses.
|
||||
easy: 0
|
||||
normal: 1
|
||||
|
@ -119,7 +116,7 @@ Minecraft:
|
|||
include_postgame_advancements: # Some advancements require defeating the Ender Dragon; this will junk-fill them so you won't have to finish to send some items.
|
||||
on: 0
|
||||
off: 1
|
||||
shuffle_structures: # CURRENTLY DISABLED; enables shuffling of villages, outposts, fortresses, bastions, and end cities.
|
||||
shuffle_structures: # Enables shuffling of villages, outposts, fortresses, bastions, and end cities.
|
||||
on: 0
|
||||
off: 1
|
||||
A Link to the Past:
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import worlds.minecraft.Options
|
||||
from test.TestBase import TestBase
|
||||
from BaseClasses import MultiWorld
|
||||
from worlds.minecraft import minecraft_gen_item_pool
|
||||
from worlds.minecraft.Regions import minecraft_create_regions, link_minecraft_structures
|
||||
from worlds.minecraft.Rules import set_rules
|
||||
from worlds import AutoWorld
|
||||
from worlds.minecraft import MinecraftWorld
|
||||
from worlds.minecraft.Items import MinecraftItem, item_table
|
||||
import Options
|
||||
from worlds.minecraft.Options import AdvancementGoal, CombatDifficulty
|
||||
|
||||
# Converts the name of an item into an item object
|
||||
def MCItemFactory(items, player: int):
|
||||
|
@ -29,16 +28,16 @@ class TestMinecraft(TestBase):
|
|||
def setUp(self):
|
||||
self.world = MultiWorld(1)
|
||||
self.world.game[1] = "Minecraft"
|
||||
self.world.worlds[1] = MinecraftWorld(self.world, 1)
|
||||
exclusion_pools = ['hard', 'insane', 'postgame']
|
||||
for pool in exclusion_pools:
|
||||
setattr(self.world, f"include_{pool}_advancements", [False, False])
|
||||
setattr(self.world, "advancement_goal", [0, worlds.minecraft.Options.AdvancementGoal(value=0)])
|
||||
setattr(self.world, "shuffle_structures", [False, False])
|
||||
setattr(self.world, "combat_difficulty", [0, worlds.minecraft.Options.CombatDifficulty(value=1)])
|
||||
minecraft_create_regions(self.world, 1)
|
||||
link_minecraft_structures(self.world, 1)
|
||||
minecraft_gen_item_pool(self.world, 1)
|
||||
set_rules(self.world, 1)
|
||||
setattr(self.world, "advancement_goal", {1: AdvancementGoal(30)})
|
||||
setattr(self.world, "shuffle_structures", {1: False})
|
||||
setattr(self.world, "combat_difficulty", {1: CombatDifficulty(1)}) # normal
|
||||
AutoWorld.call_single(self.world, "create_regions", 1)
|
||||
AutoWorld.call_single(self.world, "generate_basic", 1)
|
||||
AutoWorld.call_single(self.world, "set_rules", 1)
|
||||
|
||||
def _get_items(self, item_pool, all_except):
|
||||
if all_except and len(all_except) > 0:
|
||||
|
|
|
@ -32,7 +32,7 @@ assert len(lookup_any_location_name_to_id) == len(lookup_any_location_id_to_name
|
|||
|
||||
network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name,
|
||||
"lookup_any_item_id_to_name": lookup_any_item_id_to_name,
|
||||
"version": 6}
|
||||
"version": 7}
|
||||
|
||||
|
||||
@enum.unique
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
||||
from BaseClasses import Item
|
||||
import typing
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
|
@ -46,6 +46,7 @@ item_table = {
|
|||
"8 Gold Ore": ItemData(45032, False),
|
||||
"Rotten Flesh": ItemData(45033, False),
|
||||
"Single Arrow": ItemData(45034, False),
|
||||
"Bee Trap (Minecraft)": ItemData(45100, False),
|
||||
|
||||
"Victory": ItemData(0, True)
|
||||
}
|
||||
|
@ -67,8 +68,9 @@ item_frequencies = {
|
|||
"4 Lapis Lazuli": 2,
|
||||
"16 Porkchops": 8,
|
||||
"8 Gold Ore": 4,
|
||||
"Rotten Flesh": 4,
|
||||
"Single Arrow": 0
|
||||
"Rotten Flesh": 2,
|
||||
"Single Arrow": 0,
|
||||
"Bee Trap (Minecraft)": 2
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
||||
from BaseClasses import Location
|
||||
import typing
|
||||
|
||||
class AdvData(typing.NamedTuple):
|
||||
|
@ -114,6 +114,7 @@ exclusion_table = {
|
|||
"Two by Two": "100 XP",
|
||||
"Two Birds, One Arrow": "50 XP",
|
||||
"Arbalistic": "100 XP",
|
||||
"Monsters Hunted": "100 XP",
|
||||
"Beaconator": "50 XP",
|
||||
"A Balanced Diet": "100 XP",
|
||||
"Uneasy Alliance": "100 XP",
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import typing
|
||||
|
||||
from Options import Choice, Option, Toggle
|
||||
from Options import Choice, Option, Toggle, Range
|
||||
|
||||
|
||||
class AdvancementGoal(Choice):
|
||||
option_few = 0
|
||||
option_normal = 1
|
||||
option_many = 2
|
||||
default = 1
|
||||
class AdvancementGoal(Range):
|
||||
range_start = 0
|
||||
range_end = 87
|
||||
default = 50
|
||||
|
||||
|
||||
class CombatDifficulty(Choice):
|
||||
|
|
|
@ -1,73 +1,44 @@
|
|||
from .Locations import MinecraftAdvancement, advancement_table
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
||||
|
||||
def minecraft_create_regions(world: MultiWorld, player: int):
|
||||
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, None, region_name, player)
|
||||
ret.world = world
|
||||
ret.locations = [ MinecraftAdvancement(player, loc_name, loc_data.id, ret)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name ]
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
return ret
|
||||
|
||||
world.regions += [MCRegion(*r) for r in mc_regions]
|
||||
def link_minecraft_structures(world, player):
|
||||
|
||||
# Link mandatory connections first
|
||||
for (exit, region) in mandatory_connections:
|
||||
world.get_entrance(exit, player).connect(world.get_region(region, player))
|
||||
|
||||
def link_minecraft_structures(world: MultiWorld, player: int):
|
||||
|
||||
# Get all unpaired exits and all regions without entrances (except the Menu)
|
||||
# This function is destructive on these lists.
|
||||
exits = [exit.name for r in world.regions if r.player == player for exit in r.exits if exit.connected_region == None]
|
||||
structs = [r.name for r in world.regions if r.player == player and r.entrances == [] and r.name != 'Menu']
|
||||
exits_spoiler = exits[:] # copy the original order for the spoiler log
|
||||
try:
|
||||
assert len(exits) == len(structs)
|
||||
except AssertionError as e: # this should never happen
|
||||
raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player}") from e
|
||||
raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player} ({world.player_names[player]})")
|
||||
num_regions = len(exits)
|
||||
pairs = {}
|
||||
|
||||
def check_valid_connection(exit, struct):
|
||||
if (exit in exits) and (struct in structs) and (exit not in pairs):
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_pair(exit, struct):
|
||||
try:
|
||||
assert exit in exits
|
||||
assert struct in structs
|
||||
except AssertionError as e:
|
||||
raise Exception(f"Invalid connection: {exit} => {struct} for player {player}")
|
||||
pairs[exit] = struct
|
||||
exits.remove(exit)
|
||||
structs.remove(struct)
|
||||
if (exit in exits) and (struct in structs) and (exit not in illegal_connections.get(struct, [])):
|
||||
pairs[exit] = struct
|
||||
exits.remove(exit)
|
||||
structs.remove(struct)
|
||||
else:
|
||||
raise Exception(f"Invalid connection: {exit} => {struct} for player {player} ({world.player_names[player]})")
|
||||
|
||||
# Plando stuff. Remove any utilized exits/structs from the lists.
|
||||
# Raise error if trying to put Nether Fortress in the End.
|
||||
# Connect plando structures first
|
||||
if world.plando_connections[player]:
|
||||
for connection in world.plando_connections[player]:
|
||||
try:
|
||||
if connection.entrance == 'The End Structure' and connection.exit == 'Nether Fortress':
|
||||
raise Exception(f"Cannot place Nether Fortress in the End for player {player}")
|
||||
set_pair(connection.entrance, connection.exit)
|
||||
except Exception as e:
|
||||
raise Exception(f"Could not connect using {connection}") from e
|
||||
for conn in world.plando_connections[player]:
|
||||
set_pair(conn.entrance, conn.exit)
|
||||
|
||||
# The algorithm tries to place the most restrictive structures first. This algorithm always works on the
|
||||
# relatively small set of restrictions here, but does not work on all possible inputs with valid configurations.
|
||||
if world.shuffle_structures[player]:
|
||||
# Can't put Nether Fortress in the End
|
||||
if 'The End Structure' in exits and 'Nether Fortress' in structs:
|
||||
structs.sort(reverse=True, key=lambda s: len(illegal_connections.get(s, [])))
|
||||
for struct in structs[:]:
|
||||
try:
|
||||
end_struct = world.random.choice([s for s in structs if s != 'Nether Fortress'])
|
||||
set_pair('The End Structure', end_struct)
|
||||
except IndexError as e: # should only happen if structs is emptied by plando
|
||||
raise Exception(f"Plando forced Nether Fortress in the End for player {player}") from e
|
||||
world.random.shuffle(structs)
|
||||
for exit, struct in zip(exits[:], structs[:]):
|
||||
exit = world.random.choice([e for e in exits if e not in illegal_connections.get(struct, [])])
|
||||
except IndexError:
|
||||
raise Exception(f"No valid structure placements remaining for player {player} ({world.player_names[player]})")
|
||||
set_pair(exit, struct)
|
||||
else: # write remaining default connections
|
||||
for (exit, struct) in default_connections:
|
||||
|
@ -77,13 +48,15 @@ def link_minecraft_structures(world: MultiWorld, player: int):
|
|||
# Make sure we actually paired everything; might fail if plando
|
||||
try:
|
||||
assert len(exits) == len(structs) == 0
|
||||
except AssertionError as e:
|
||||
raise Exception(f"Failed to connect all Minecraft structures for player {player}; check plando settings in yaml") from e
|
||||
except AssertionError:
|
||||
raise Exception(f"Failed to connect all Minecraft structures for player {player} ({world.player_names[player]})")
|
||||
|
||||
for exit in exits_spoiler:
|
||||
world.get_entrance(exit, player).connect(world.get_region(pairs[exit], player))
|
||||
if world.shuffle_structures[player] or world.plando_connections[player]:
|
||||
world.spoiler.set_entrance(exit, pairs[exit], 'entrance', player)
|
||||
|
||||
|
||||
for exit, struct in pairs.items():
|
||||
world.get_entrance(exit, player).connect(world.get_region(struct, player))
|
||||
if world.shuffle_structures[player]:
|
||||
world.spoiler.set_entrance(exit, struct, 'entrance', player)
|
||||
|
||||
# (Region name, list of exits)
|
||||
mc_regions = [
|
||||
|
@ -112,3 +85,9 @@ default_connections = {
|
|||
('Nether Structure 2', 'Bastion Remnant'),
|
||||
('The End Structure', 'End City')
|
||||
}
|
||||
|
||||
# Structure: illegal locations
|
||||
illegal_connections = {
|
||||
'Nether Fortress': ['The End Structure']
|
||||
}
|
||||
|
||||
|
|
|
@ -14,26 +14,19 @@ def set_rules(world: MultiWorld, player: int):
|
|||
(location.name not in postgame_advancements) and
|
||||
location.can_reach(state)]
|
||||
|
||||
# 92 total advancements, 16 are typically excluded, 1 is Free the End. Goal is to complete X advancements and then Free the End.
|
||||
goal_map = {
|
||||
'few': 30,
|
||||
'normal': 50,
|
||||
'many': 70
|
||||
}
|
||||
goal = goal_map[getattr(world, 'advancement_goal')[player].get_option_name()]
|
||||
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region',
|
||||
player) and state.can_kill_ender_dragon(
|
||||
player)
|
||||
# 92 total advancements. Goal is to complete X advancements and then Free the End.
|
||||
# There are 5 advancements which cannot be included for dragon spawning (4 postgame, Free the End)
|
||||
# Hence the true maximum is (92 - 5) = 87
|
||||
goal = int(world.advancement_goal[player].value)
|
||||
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', player) and state.can_kill_ender_dragon(player)
|
||||
|
||||
if world.logic[player] != 'nologic':
|
||||
world.completion_condition[player] = lambda state: state.has('Victory', player)
|
||||
|
||||
set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and
|
||||
(state.has('Bucket', player) or state.has(
|
||||
'Progressive Tools', player, 3)) and
|
||||
state.has_iron_ingots(player))
|
||||
set_rule(world.get_entrance("End Portal", player),
|
||||
lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4))
|
||||
set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and
|
||||
(state.has('Bucket', player) or state.has('Progressive Tools', player, 3)) and
|
||||
state.has_iron_ingots(player))
|
||||
set_rule(world.get_entrance("End Portal", player), lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4))
|
||||
set_rule(world.get_entrance("Overworld Structure 1", player), lambda state: state.can_adventure(player))
|
||||
set_rule(world.get_entrance("Overworld Structure 2", player), lambda state: state.can_adventure(player))
|
||||
set_rule(world.get_entrance("Nether Structure 1", player), lambda state: state.can_adventure(player))
|
||||
|
@ -44,158 +37,98 @@ def set_rules(world: MultiWorld, player: int):
|
|||
|
||||
set_rule(world.get_location("Who is Cutting Onions?", player), lambda state: state.can_piglin_trade(player))
|
||||
set_rule(world.get_location("Oh Shiny", player), lambda state: state.can_piglin_trade(player))
|
||||
set_rule(world.get_location("Suit Up", player),
|
||||
lambda state: state.has("Progressive Armor", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Very Very Frightening", player),
|
||||
lambda state: state.has("Channeling Book", player) and state.can_use_anvil(player) and state.can_enchant(
|
||||
player) and \
|
||||
((world.get_region('Village', player).entrances[
|
||||
0].parent_region.name != 'The End' and state.can_reach('Village', 'Region',
|
||||
player)) or state.can_reach(
|
||||
'Zombie Doctor', 'Location',
|
||||
player))) # need villager into the overworld for lightning strike
|
||||
set_rule(world.get_location("Hot Stuff", player),
|
||||
lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Free the End", player),
|
||||
lambda state: can_complete(state) and state.has('Ingot Crafting', player) and state.can_reach('The Nether',
|
||||
'Region',
|
||||
player))
|
||||
set_rule(world.get_location("A Furious Cocktail", player), lambda state: state.can_brew_potions(player) and
|
||||
state.has("Fishing Rod",
|
||||
player) and # Water Breathing
|
||||
state.can_reach('The Nether', 'Region',
|
||||
player) and # Regeneration, Fire Resistance, gold nuggets
|
||||
state.can_reach('Village', 'Region',
|
||||
player) and # Night Vision, Invisibility
|
||||
state.can_reach('Bring Home the Beacon',
|
||||
'Location',
|
||||
player)) # Resistance
|
||||
set_rule(world.get_location("Suit Up", player), lambda state: state.has("Progressive Armor", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Very Very Frightening", player), lambda state: state.has("Channeling Book", player) and state.can_use_anvil(player) and state.can_enchant(player) and \
|
||||
((world.get_region('Village', player).entrances[0].parent_region.name != 'The End' and state.can_reach('Village', 'Region', player)) or state.can_reach('Zombie Doctor', 'Location', player))) # need villager into the overworld for lightning strike
|
||||
set_rule(world.get_location("Hot Stuff", player), lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Free the End", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("A Furious Cocktail", player), lambda state: state.can_brew_potions(player) and
|
||||
state.has("Fishing Rod", player) and # Water Breathing
|
||||
state.can_reach('The Nether', 'Region', player) and # Regeneration, Fire Resistance, gold nuggets
|
||||
state.can_reach('Village', 'Region', player) and # Night Vision, Invisibility
|
||||
state.can_reach('Bring Home the Beacon', 'Location', player)) # Resistance
|
||||
set_rule(world.get_location("Best Friends Forever", player), lambda state: True)
|
||||
set_rule(world.get_location("Bring Home the Beacon", player),
|
||||
lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
|
||||
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
|
||||
set_rule(world.get_location("Not Today, Thank You", player),
|
||||
lambda state: state.has("Shield", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Isn't It Iron Pick", player),
|
||||
lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Bring Home the Beacon", player), lambda state: state.can_kill_wither(player) and
|
||||
state.has_diamond_pickaxe(player) and state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
|
||||
set_rule(world.get_location("Not Today, Thank You", player), lambda state: state.has("Shield", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Isn't It Iron Pick", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Local Brewery", player), lambda state: state.can_brew_potions(player))
|
||||
set_rule(world.get_location("The Next Generation", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("Fishy Business", player), lambda state: state.has("Fishing Rod", player))
|
||||
set_rule(world.get_location("Hot Tourist Destinations", player), lambda state: True)
|
||||
set_rule(world.get_location("This Boat Has Legs", player),
|
||||
lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod",
|
||||
player))
|
||||
set_rule(world.get_location("This Boat Has Legs", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", player))
|
||||
set_rule(world.get_location("Sniper Duel", player), lambda state: state.has("Archery", player))
|
||||
set_rule(world.get_location("Nether", player), lambda state: True)
|
||||
set_rule(world.get_location("Great View From Up Here", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("How Did We Get Here?", player),
|
||||
lambda state: state.can_brew_potions(player) and state.has_gold_ingots(
|
||||
player) and # most effects; Absorption
|
||||
state.can_reach('End City', 'Region', player) and state.can_reach('The Nether', 'Region',
|
||||
player) and # Levitation; potion ingredients
|
||||
state.has("Fishing Rod", player) and state.has("Archery",
|
||||
player) and # Pufferfish, Nautilus Shells; spectral arrows
|
||||
set_rule(world.get_location("How Did We Get Here?", player), lambda state: state.can_brew_potions(player) and
|
||||
state.has_gold_ingots(player) and # Absorption
|
||||
state.can_reach('End City', 'Region', player) and # Levitation
|
||||
state.can_reach('The Nether', 'Region', player) and # potion ingredients
|
||||
state.has("Fishing Rod", player) and state.has("Archery",player) and # Pufferfish, Nautilus Shells; spectral arrows
|
||||
state.can_reach("Bring Home the Beacon", "Location", player) and # Haste
|
||||
state.can_reach("Hero of the Village", "Location", player)) # Bad Omen, Hero of the Village
|
||||
set_rule(world.get_location("Bullseye", player),
|
||||
lambda state: state.has("Archery", player) and state.has("Progressive Tools", player,
|
||||
2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Bullseye", player), lambda state: state.has("Archery", player) and state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Spooky Scary Skeleton", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("Two by Two", player),
|
||||
lambda state: state.has_iron_ingots(player) and state.can_adventure(
|
||||
player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots
|
||||
set_rule(world.get_location("Two by Two", player), lambda state: state.has_iron_ingots(player) and state.can_adventure(player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots
|
||||
set_rule(world.get_location("Stone Age", player), lambda state: True)
|
||||
set_rule(world.get_location("Two Birds, One Arrow", player),
|
||||
lambda state: state.craft_crossbow(player) and state.can_enchant(player))
|
||||
set_rule(world.get_location("Two Birds, One Arrow", player), lambda state: state.craft_crossbow(player) and state.can_enchant(player))
|
||||
set_rule(world.get_location("We Need to Go Deeper", player), lambda state: True)
|
||||
set_rule(world.get_location("Who's the Pillager Now?", player), lambda state: state.craft_crossbow(player))
|
||||
set_rule(world.get_location("Getting an Upgrade", player), lambda state: state.has("Progressive Tools", player))
|
||||
set_rule(world.get_location("Tactical Fishing", player),
|
||||
lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Zombie Doctor", player),
|
||||
lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player))
|
||||
set_rule(world.get_location("Tactical Fishing", player), lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Zombie Doctor", player), lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player))
|
||||
set_rule(world.get_location("The City at the End of the Game", player), lambda state: True)
|
||||
set_rule(world.get_location("Ice Bucket Challenge", player), lambda state: state.has_diamond_pickaxe(player))
|
||||
set_rule(world.get_location("Remote Getaway", player), lambda state: True)
|
||||
set_rule(world.get_location("Into Fire", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("War Pigs", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("Take Aim", player), lambda state: state.has("Archery", player))
|
||||
set_rule(world.get_location("Total Beelocation", player),
|
||||
lambda state: state.has("Silk Touch Book", player) and state.can_use_anvil(player) and state.can_enchant(
|
||||
player))
|
||||
set_rule(world.get_location("Arbalistic", player),
|
||||
lambda state: state.craft_crossbow(player) and state.has("Piercing IV Book", player) and
|
||||
state.can_use_anvil(player) and state.can_enchant(player))
|
||||
set_rule(world.get_location("The End... Again...", player),
|
||||
lambda state: can_complete(state) and state.has("Ingot Crafting", player) and state.can_reach('The Nether',
|
||||
'Region',
|
||||
player)) # furnace for glass, nether for ghast tears
|
||||
set_rule(world.get_location("Total Beelocation", player), lambda state: state.has("Silk Touch Book", player) and state.can_use_anvil(player) and state.can_enchant(player))
|
||||
set_rule(world.get_location("Arbalistic", player), lambda state: state.craft_crossbow(player) and state.has("Piercing IV Book", player) and
|
||||
state.can_use_anvil(player) and state.can_enchant(player))
|
||||
set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state))
|
||||
set_rule(world.get_location("Acquire Hardware", player), lambda state: state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Not Quite \"Nine\" Lives", player),
|
||||
lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player))
|
||||
set_rule(world.get_location("Cover Me With Diamonds", player),
|
||||
lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location",
|
||||
player))
|
||||
set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player))
|
||||
set_rule(world.get_location("Cover Me With Diamonds", player), lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", player))
|
||||
set_rule(world.get_location("Sky's the Limit", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("Hired Help", player),
|
||||
lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Hired Help", player), lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("Return to Sender", player), lambda state: True)
|
||||
set_rule(world.get_location("Sweet Dreams", player),
|
||||
lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player))
|
||||
set_rule(world.get_location("You Need a Mint", player),
|
||||
lambda state: can_complete(state) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("Sweet Dreams", player), lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player))
|
||||
set_rule(world.get_location("You Need a Mint", player), lambda state: can_complete(state) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("Adventure", player), lambda state: True)
|
||||
set_rule(world.get_location("Monsters Hunted", player),
|
||||
lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod",
|
||||
player)) # pufferfish for Water Breathing
|
||||
set_rule(world.get_location("Monsters Hunted", player), lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing
|
||||
set_rule(world.get_location("Enchanter", player), lambda state: state.can_enchant(player))
|
||||
set_rule(world.get_location("Voluntary Exile", player), lambda state: state.basic_combat(player))
|
||||
set_rule(world.get_location("Eye Spy", player), lambda state: state.enter_stronghold(player))
|
||||
set_rule(world.get_location("The End", player), lambda state: True)
|
||||
set_rule(world.get_location("Serious Dedication", player),
|
||||
lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(
|
||||
player))
|
||||
set_rule(world.get_location("Serious Dedication", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player))
|
||||
set_rule(world.get_location("Postmortal", player), lambda state: state.complete_raid(player))
|
||||
set_rule(world.get_location("Monster Hunter", player), lambda state: True)
|
||||
set_rule(world.get_location("Adventuring Time", player), lambda state: state.can_adventure(player))
|
||||
set_rule(world.get_location("A Seedy Place", player), lambda state: True)
|
||||
set_rule(world.get_location("Those Were the Days", player), lambda state: True)
|
||||
set_rule(world.get_location("Hero of the Village", player), lambda state: state.complete_raid(player))
|
||||
set_rule(world.get_location("Hidden in the Depths", player),
|
||||
lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe(
|
||||
player)) # bed mining :)
|
||||
set_rule(world.get_location("Beaconator", player),
|
||||
lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
|
||||
set_rule(world.get_location("Hidden in the Depths", player), lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe(player)) # bed mining :)
|
||||
set_rule(world.get_location("Beaconator", player), lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
|
||||
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
|
||||
set_rule(world.get_location("Withering Heights", player), lambda state: state.can_kill_wither(player))
|
||||
set_rule(world.get_location("A Balanced Diet", player),
|
||||
lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple
|
||||
state.has("Resource Blocks", player) and state.can_reach('The End', 'Region',
|
||||
player)) # notch apple, chorus fruit
|
||||
set_rule(world.get_location("A Balanced Diet", player), lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple
|
||||
state.has("Resource Blocks", player) and state.can_reach('The End', 'Region', player)) # notch apple, chorus fruit
|
||||
set_rule(world.get_location("Subspace Bubble", player), lambda state: state.has_diamond_pickaxe(player))
|
||||
set_rule(world.get_location("Husbandry", player), lambda state: True)
|
||||
set_rule(world.get_location("Country Lode, Take Me Home", player),
|
||||
lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(
|
||||
player))
|
||||
set_rule(world.get_location("Bee Our Guest", player),
|
||||
lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("Country Lode, Take Me Home", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player))
|
||||
set_rule(world.get_location("Bee Our Guest", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("What a Deal!", player), lambda state: True)
|
||||
set_rule(world.get_location("Uneasy Alliance", player),
|
||||
lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player))
|
||||
set_rule(world.get_location("Diamonds!", player),
|
||||
lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("A Terrible Fortress", player),
|
||||
lambda state: True) # since you don't have to fight anything
|
||||
set_rule(world.get_location("Uneasy Alliance", player), lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player))
|
||||
set_rule(world.get_location("Diamonds!", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
|
||||
set_rule(world.get_location("A Terrible Fortress", player), lambda state: True) # since you don't have to fight anything
|
||||
set_rule(world.get_location("A Throwaway Joke", player), lambda state: True) # kill drowned
|
||||
set_rule(world.get_location("Minecraft", player), lambda state: True)
|
||||
set_rule(world.get_location("Sticky Situation", player),
|
||||
lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("Sticky Situation", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
|
||||
set_rule(world.get_location("Ol' Betsy", player), lambda state: state.craft_crossbow(player))
|
||||
set_rule(world.get_location("Cover Me in Debris", player),
|
||||
lambda state: state.has("Progressive Armor", player, 2) and
|
||||
set_rule(world.get_location("Cover Me in Debris", player), lambda state: state.has("Progressive Armor", player, 2) and
|
||||
state.has("8 Netherite Scrap", player, 2) and state.has("Ingot Crafting", player) and
|
||||
state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths",
|
||||
"Location", player))
|
||||
state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths", "Location", player))
|
||||
set_rule(world.get_location("The End?", player), lambda state: True)
|
||||
set_rule(world.get_location("The Parrots and the Bats", player), lambda state: True)
|
||||
set_rule(world.get_location("A Complete Catalogue", player), lambda state: True) # kill fish for raw
|
||||
|
@ -203,19 +136,13 @@ def set_rules(world: MultiWorld, player: int):
|
|||
set_rule(world.get_location("Time to Mine!", player), lambda state: True)
|
||||
set_rule(world.get_location("Hot Topic", player), lambda state: state.has("Ingot Crafting", player))
|
||||
set_rule(world.get_location("Bake Bread", player), lambda state: True)
|
||||
set_rule(world.get_location("The Lie", player),
|
||||
lambda state: state.has_iron_ingots(player) and state.has("Bucket", player))
|
||||
set_rule(world.get_location("On a Rail", player),
|
||||
lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails
|
||||
set_rule(world.get_location("The Lie", player), lambda state: state.has_iron_ingots(player) and state.has("Bucket", player))
|
||||
set_rule(world.get_location("On a Rail", player), lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails
|
||||
set_rule(world.get_location("Time to Strike!", player), lambda state: True)
|
||||
set_rule(world.get_location("Cow Tipper", player), lambda state: True)
|
||||
set_rule(world.get_location("When Pigs Fly", player),
|
||||
lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod",
|
||||
player) and state.can_adventure(
|
||||
player))
|
||||
set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and (
|
||||
state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region',
|
||||
player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit
|
||||
set_rule(world.get_location("When Pigs Fly", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and
|
||||
state.has("Fishing Rod", player) and state.can_adventure(player))
|
||||
set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and
|
||||
(state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region', player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit
|
||||
set_rule(world.get_location("Librarian", player), lambda state: state.has("Enchanting", player))
|
||||
set_rule(world.get_location("Overpowered", player),
|
||||
lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player))
|
||||
set_rule(world.get_location("Overpowered", player), lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player))
|
||||
|
|
|
@ -1,81 +1,92 @@
|
|||
from .Items import MinecraftItem, item_table, item_frequencies
|
||||
from .Locations import exclusion_table, events_table
|
||||
from .Regions import link_minecraft_structures
|
||||
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 MultiWorld
|
||||
from BaseClasses import Region, Entrance
|
||||
from .Options import minecraft_options
|
||||
from ..AutoWorld import World
|
||||
|
||||
client_version = (0, 4)
|
||||
|
||||
class MinecraftWorld(World):
|
||||
game: str = "Minecraft"
|
||||
options = minecraft_options
|
||||
|
||||
|
||||
client_version = (0, 3)
|
||||
def _get_mc_data(self):
|
||||
exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2",
|
||||
"The End Structure"]
|
||||
return {
|
||||
'world_seed': self.world.slot_seeds[self.player].getrandbits(32),
|
||||
# consistent and doesn't interfere with other generation
|
||||
'seed_name': self.world.seed_name,
|
||||
'player_name': self.world.get_player_names(self.player),
|
||||
'player_id': self.player,
|
||||
'client_version': client_version,
|
||||
'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits}
|
||||
}
|
||||
|
||||
|
||||
def get_mc_data(world: MultiWorld, player: int):
|
||||
exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2",
|
||||
"The End Structure"]
|
||||
return {
|
||||
'world_seed': world.slot_seeds[player].getrandbits(32),
|
||||
# consistent and doesn't interfere with other generation
|
||||
'seed_name': world.seed_name,
|
||||
'player_name': world.get_player_names(player),
|
||||
'player_id': player,
|
||||
'client_version': client_version,
|
||||
'structures': {exit: world.get_entrance(exit, player).connected_region.name for exit in exits}
|
||||
}
|
||||
def generate_basic(self):
|
||||
link_minecraft_structures(self.world, self.player)
|
||||
|
||||
pool = []
|
||||
for item_name, item_data in item_table.items():
|
||||
for count in range(item_frequencies.get(item_name, 1)):
|
||||
pool.append(MinecraftItem(item_name, item_data.progression, item_data.code, self.player))
|
||||
|
||||
prefill_pool = {}
|
||||
prefill_pool.update(events_table)
|
||||
exclusion_pools = ['hard', 'insane', 'postgame']
|
||||
for key in exclusion_pools:
|
||||
if not getattr(self.world, f"include_{key}_advancements")[self.player]:
|
||||
prefill_pool.update(exclusion_table[key])
|
||||
|
||||
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)
|
||||
self.world.push_item(location, item, collect=False)
|
||||
pool.remove(item)
|
||||
location.event = item_data.progression
|
||||
location.locked = True
|
||||
|
||||
self.world.itempool += pool
|
||||
|
||||
|
||||
def generate_mc_data(world: MultiWorld, player: int):
|
||||
import base64, json
|
||||
from Utils import output_path
|
||||
|
||||
data = get_mc_data(world, player)
|
||||
filename = f"AP_{world.seed_name}_P{player}_{world.get_player_names(player)}.apmc"
|
||||
with open(output_path(filename), 'wb') as f:
|
||||
f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||
def set_rules(self):
|
||||
set_rules(self.world, self.player)
|
||||
|
||||
|
||||
def fill_minecraft_slot_data(world: MultiWorld, player: int):
|
||||
slot_data = get_mc_data(world, player)
|
||||
for option_name in minecraft_options:
|
||||
option = getattr(world, option_name)[player]
|
||||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
def create_regions(self):
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, None, region_name, self.player)
|
||||
ret.world = self.world
|
||||
ret.locations = [ MinecraftAdvancement(self.player, loc_name, loc_data.id, ret)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name ]
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(self.player, exit, ret))
|
||||
return ret
|
||||
|
||||
self.world.regions += [MCRegion(*r) for r in mc_regions]
|
||||
|
||||
|
||||
# Generates the item pool given the table and frequencies in Items.py.
|
||||
def minecraft_gen_item_pool(world: MultiWorld, player: int):
|
||||
pool = []
|
||||
for item_name, item_data in item_table.items():
|
||||
for count in range(item_frequencies.get(item_name, 1)):
|
||||
pool.append(MinecraftItem(item_name, item_data.progression, item_data.code, player))
|
||||
def generate_output(self):
|
||||
import json
|
||||
from base64 import b64encode
|
||||
from Utils import output_path
|
||||
|
||||
prefill_pool = {}
|
||||
prefill_pool.update(events_table)
|
||||
exclusion_pools = ['hard', 'insane', 'postgame']
|
||||
for key in exclusion_pools:
|
||||
if not getattr(world, f"include_{key}_advancements")[player]:
|
||||
prefill_pool.update(exclusion_table[key])
|
||||
|
||||
for loc_name, item_name in prefill_pool.items():
|
||||
item_data = item_table[item_name]
|
||||
location = world.get_location(loc_name, player)
|
||||
item = MinecraftItem(item_name, item_data.progression, item_data.code, player)
|
||||
world.push_item(location, item, collect=False)
|
||||
pool.remove(item)
|
||||
location.event = item_data.progression
|
||||
location.locked = True
|
||||
|
||||
world.itempool += pool
|
||||
data = self._get_mc_data()
|
||||
filename = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_names(self.player)}.apmc"
|
||||
with open(output_path(filename), 'wb') as f:
|
||||
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||
|
||||
|
||||
# Generate Minecraft world.
|
||||
def gen_minecraft(world: MultiWorld, player: int):
|
||||
link_minecraft_structures(world, player)
|
||||
minecraft_gen_item_pool(world, player)
|
||||
set_rules(world, player)
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue