Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions worlds/crosscode/codegen/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,19 @@ def create_expression_region_connection(conn: RegionConnection):
]
)

if conn.metadata is not None:
# this will never be none, but it must claim that it can be to satisfy the typing gods
keys: list[ast.expr | None] = [ast.Constant(k) for k in conn.metadata.keys()]
values: list[ast.expr] = [ast.Constant(k) for k in conn.metadata.values()]

ast_region.keywords.append(ast.keyword(
arg="metadata",
value=ast.Dict(
keys=keys,
values=values,
)
))

ast.fix_missing_locations(ast_region)

return ast_region
Expand Down
11 changes: 11 additions & 0 deletions worlds/crosscode/codegen/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ def merge(original: T, addon: T, patch: bool = True) -> T:
return original

if isinstance(original, list):
if isinstance(addon, dict):
for idx, val in addon.items():
if idx == "*":
for child in original:
merge(child, val, patch)
continue
if not idx.isnumeric():
raise RuntimeError(f"Cannot merge list and dict with non-numeric keys")
merge(original[int(idx)], val, patch)
return original

if not isinstance(addon, list):
raise RuntimeError(f"Cannot merge type {type(original)} with {type(addon)}")
original.extend(addon)
Expand Down
22 changes: 12 additions & 10 deletions worlds/crosscode/codegen/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,12 @@ def parse_region_connection(self, raw: dict[str, typing.Any]) -> RegionConnectio
if "condition" in raw:
condition = self.parse_condition(raw["condition"])

return RegionConnection(region_from, region_to, condition)
return RegionConnection(
region_from=region_from,
region_to=region_to,
cond=condition,
metadata=raw.get("metadata", None)
)

def parse_regions_data(self, raw: dict[str, typing.Any]) -> RegionsData:
"""
Expand All @@ -377,14 +382,10 @@ def parse_regions_data(self, raw: dict[str, typing.Any]) -> RegionsData:
if not isinstance(raw_connections, list):
raise JsonParserError(raw, raw_connections, "regions data", "connection must be list")

regions_seen: set[str] = set()

connections = []

for raw_conn in raw_connections:
conn = self.parse_region_connection(raw_conn)
regions_seen.add(conn.region_to)
regions_seen.add(conn.region_from)

connections.append(conn)

Expand All @@ -398,11 +399,12 @@ def parse_regions_data(self, raw: dict[str, typing.Any]) -> RegionsData:
for goal_name, goal in raw["goals"].items():
goals[goal_name] = self.parse_goal(goal)

region_list = list(regions_seen)

region_list.sort()

return RegionsData(start, exclude, region_list, connections, goals)
return RegionsData(
starting_region=start,
excluded_regions=exclude,
region_connections=connections,
goals=goals
)

def parse_regions_data_list(self, raw: dict[str, dict[str, typing.Any]]) -> dict[str, RegionsData]:
"""
Expand Down
134 changes: 17 additions & 117 deletions worlds/crosscode/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,6 @@
"linear": RegionsData(
starting_region = "2",
excluded_regions = ['1'],
region_list = [
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'17.5',
'18',
'19',
'2',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'26.5',
'27',
'28',
'29',
'3',
'3.1',
'30',
'31',
'32',
'33',
'4',
'5',
'6',
'7',
'8',
'9',
],
region_connections = [
RegionConnection(region_from='2', region_to='3', cond=[ItemCondition(item_name='Green Leaf Shade', amount=1)]),
RegionConnection(region_from='3', region_to='4', cond=[ItemCondition(item_name='Mine Pass', amount=1), ItemCondition(item_name='Guild Pass', amount=1)]),
Expand Down Expand Up @@ -99,69 +62,6 @@
"open": RegionsData(
starting_region = "open2",
excluded_regions = ['open1'],
region_list = [
'open10',
'open10.Grove',
'open10.Infested',
'open10.Left',
'open10.Mid',
'open10.Right',
'open11',
'open13.1',
'open13.2',
'open14.1',
'open14.2',
'open14.3',
'open14.4',
'open14.5',
'open15.1',
'open15.2',
'open15.3',
'open16',
'open16.1',
'open17',
'open18',
'open2',
'open20',
'open3',
'open3.1',
'open4.1',
'open4.2',
'open4.3',
'open4.4',
'open4.5',
'open4.6',
'open4.7',
'open4.8',
'open5',
'open6',
'open7.1',
'open7.2',
'open7.3',
'open7.4',
'open7.5',
'open7.6',
'open7.7',
'open7.8',
'open8',
'open9',
'openDLC1',
'openDLC_Beach',
'openDLC_DungeonB2_L',
'openDLC_DungeonB2_L.1',
'openDLC_DungeonB2_R',
'openDLC_DungeonB2_R.1',
'openDLC_DungeonB3_L',
'openDLC_DungeonB3_R',
'openDLC_DungeonBoss',
'openDLC_DungeonEntry',
'openDLC_DungeonEntry.1F',
'openDLC_DungeonEntry.1L',
'openDLC_DungeonEntry.1R',
'openDLC_DungeonGF_L',
'openDLC_DungeonGF_R',
'openDLC_DungeonMain',
],
region_connections = [
RegionConnection(region_from='open2', region_to='open3', cond=[ItemCondition(item_name='Green Leaf Shade', amount=1)]),
RegionConnection(region_from='open3', region_to='open4.1', cond=[ItemCondition(item_name='Mine Pass', amount=1)]),
Expand Down Expand Up @@ -210,23 +110,23 @@
RegionConnection(region_from='open16', region_to='open17', cond=[ItemCondition(item_name='Old Dojo Key', amount=1)]),
RegionConnection(region_from='open16', region_to='open16.1', cond=[ItemCondition(item_name='Meteor Shade', amount=1)]),
RegionConnection(region_from='open16', region_to='open18', cond=[VariableCondition(name='vwPassage')]),
RegionConnection(region_from='open3', region_to='openDLC1', cond=[ItemCondition(item_name='Guild Pass', amount=1)]),
RegionConnection(region_from='open20', region_to='openDLC_Beach', cond=[ItemCondition(item_name='Azure Archipelago Pass', amount=1)]),
RegionConnection(region_from='open16.1', region_to='openDLC_DungeonEntry', cond=[ItemCondition(item_name='Ancient Shade', amount=1)]),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1F', cond=[ItemCondition(item_name="Ku'lero Key", amount=3), ItemCondition(item_name='Wave', amount=1)]),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1L', cond=[ItemCondition(item_name="Ku'lero Key", amount=3)]),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1R', cond=[OrCondition(subconditions=[AndCondition(subconditions=[ItemCondition(item_name="Ku'lero Key", amount=2), ItemCondition(item_name='Wave', amount=1)]), ItemCondition(item_name="Ku'lero Key", amount=3)])]),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonGF_R', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)]),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonGF_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Wave', amount=1)]),
RegionConnection(region_from='openDLC_DungeonGF_R', region_to='openDLC_DungeonMain', cond=[]),
RegionConnection(region_from='openDLC_DungeonGF_L', region_to='openDLC_DungeonMain', cond=[]),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB2_R', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Cold', amount=1)]),
RegionConnection(region_from='openDLC_DungeonB2_R', region_to='openDLC_DungeonB2_R.1', cond=[ItemCondition(item_name='Wave', amount=1)]),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB2_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Wave', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)]),
RegionConnection(region_from='openDLC_DungeonB2_L', region_to='openDLC_DungeonB2_L.1', cond=[ItemCondition(item_name="Ku'lero Key", amount=3)]),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB3_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)]),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB3_R', cond=[ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Wave', amount=1)]),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonBoss', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Wave', amount=1), ItemCondition(item_name="Ku'lero Master Key", amount=1)]),
RegionConnection(region_from='open3', region_to='openDLC1', cond=[ItemCondition(item_name='Guild Pass', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='open20', region_to='openDLC_Beach', cond=[ItemCondition(item_name='Azure Archipelago Pass', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='open16.1', region_to='openDLC_DungeonEntry', cond=[ItemCondition(item_name='Ancient Shade', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1F', cond=[ItemCondition(item_name="Ku'lero Key", amount=3), ItemCondition(item_name='Wave', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1L', cond=[ItemCondition(item_name="Ku'lero Key", amount=3)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonEntry.1R', cond=[OrCondition(subconditions=[AndCondition(subconditions=[ItemCondition(item_name="Ku'lero Key", amount=2), ItemCondition(item_name='Wave', amount=1)]), ItemCondition(item_name="Ku'lero Key", amount=3)])], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonGF_R', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonEntry', region_to='openDLC_DungeonGF_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Wave', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonGF_R', region_to='openDLC_DungeonMain', cond=[], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonGF_L', region_to='openDLC_DungeonMain', cond=[], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB2_R', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Cold', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonB2_R', region_to='openDLC_DungeonB2_R.1', cond=[ItemCondition(item_name='Wave', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB2_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Wave', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonB2_L', region_to='openDLC_DungeonB2_L.1', cond=[ItemCondition(item_name="Ku'lero Key", amount=3)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB3_L', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonB3_R', cond=[ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Wave', amount=1)], metadata={'dlc': True}),
RegionConnection(region_from='openDLC_DungeonMain', region_to='openDLC_DungeonBoss', cond=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Wave', amount=1), ItemCondition(item_name="Ku'lero Master Key", amount=1)], metadata={'dlc': True}),
],
goals = {
'creator': Goal(region='open18', condition=[ItemCondition(item_name='Heat', amount=1), ItemCondition(item_name='Cold', amount=1), ItemCondition(item_name='Shock', amount=1), ItemCondition(item_name='Wave', amount=1), VariableCondition(name='vtShadeLock')]),
Expand Down
1 change: 0 additions & 1 deletion worlds/crosscode/templates/regions.template.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"{{mode}}": RegionsData(
starting_region = "{{r.starting_region}}",
excluded_regions = {{r.excluded_regions}},
region_list = {{r.region_list | emit_list("constant") | indent(8)}},
region_connections = {{r.region_connections | emit_list("region_connection") | indent(8)}},
goals = {{r.goals.items() | emit_dict("constant", "goal") | indent(8) }}
),
Expand Down
10 changes: 9 additions & 1 deletion worlds/crosscode/types/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ def satisfied(self, state: CollectionState, player: int, location: int | None, a

mode: str = args["mode"]
if self.target_mode is None or mode == self.target_mode:
# BAD BAD BAD
# This will check *every time* this condition is called whether the region exists.
# I'm only doing this because I know I'm going to optimize it later.
# If we still have to check region existence in the refactor we have to calculate it early.
if self.region_name not in state.multiworld.regions.region_cache[player]:
return False
return state.can_reach_region(self.region_name, player)

return True
Expand Down Expand Up @@ -175,7 +181,9 @@ def satisfied(self, state: CollectionState, player: int, location: int | None, a
collected = sum([
amount
for region, amount in args["region_botanics_amounts"].items()
if state.can_reach_region(region, player)
# See the other comment beginning with "BAD BAD BAD" -- this is bad for the same reason
if region in state.multiworld.regions.region_cache[player]
and state.can_reach_region(region, player)
])

return collected / args["botanics_completion_amount"] >= self.amount
Expand Down
18 changes: 9 additions & 9 deletions worlds/crosscode/types/pools.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,17 @@ def __init__(self, world_data: WorldData, opts: IncludeOptions):
weights = {}

for loc in world_data.pool_locations:
if self.__should_include(loc.metadata):
if self.should_include(loc.metadata):
self.location_pool.append(loc)

for ev in world_data.events_dict.values():
if self.__should_include(ev.metadata):
if self.should_include(ev.metadata):
self.event_pool.append(ev)

for name, pool in world_data.item_pools_template.items():
counter = defaultdict(lambda: 0)
for entry in pool:
if self.__should_include(entry.metadata):
if self.should_include(entry.metadata):
counter[entry.item] += entry.quantity

self.item_pools[name] = counter
Expand All @@ -92,27 +92,27 @@ def __init__(self, world_data: WorldData, opts: IncludeOptions):
for shop_name, locations in world_data.per_shop_locations.items():
shop_locations = []
for loc in locations.values():
if self.__should_include(loc.metadata):
if self.should_include(loc.metadata):
shop_locations.append(loc)
self.per_shop_location_pool[shop_name] = shop_locations

for loc in world_data.global_shop_locations.values():
if self.__should_include(loc.metadata):
if self.should_include(loc.metadata):
self.global_shop_location_pool.append(loc)

for shop_pool, pool_name in (
(world_data.shop_unlock_by_id, "shop_unlock_by_id"),
(world_data.shop_unlock_by_shop, "shop_unlock_by_shop"),
(world_data.shop_unlock_by_shop_and_id, "shop_unlock_by_shop_and_id")
):
self.item_pools[pool_name] = { entry.item: 1 for entry in shop_pool.values() if self.__should_include(entry.metadata) }
self.item_pools[pool_name] = { entry.item: 1 for entry in shop_pool.values() if self.should_include(entry.metadata) }

for chain_name, chain in world_data.progressive_chains.items():
item_list: list[ItemData] = []
self.progressive_chains[chain_name] = item_list
items = self.locate_chain(chain)
for idx, entry in enumerate(items):
if self.__should_include(entry.metadata):
if self.should_include(entry.metadata):
item_list.append(entry.item)
prog_item = world_data.progressive_items[chain_name].name
self.item_progressive_replacements[entry.item.name].append((prog_item, idx + 1))
Expand All @@ -129,12 +129,12 @@ def locate_chain(self, chain: ProgressiveItemChain) -> list[ProgressiveChainEntr
for subchain in chain.subchains:
if subchain.metadata is None:
return subchain.chain
if self.__should_include(subchain.metadata):
if self.should_include(subchain.metadata):
return subchain.chain

return []

def __should_include(self, metadata: IncludeOptions | None) -> bool:
def should_include(self, metadata: IncludeOptions | None) -> bool:
# Technically the class allows metadata to be None.
# So we'll assign a local variable and use that instead.
if metadata is None:
Expand Down
3 changes: 2 additions & 1 deletion worlds/crosscode/types/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
import typing

from .condition import Condition
from .metadata import IncludeOptions

@dataclass
class RegionConnection:
region_from: str
region_to: str
cond: typing.Optional[list[Condition]]
metadata: typing.Optional[IncludeOptions] = None

@dataclass
class Goal:
Expand All @@ -18,6 +20,5 @@ class Goal:
class RegionsData:
starting_region: str
excluded_regions: typing.List[str]
region_list: typing.List[str]
region_connections: typing.List[RegionConnection]
goals: typing.Dict[str, Goal]
Loading
Loading