mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-25 08:03:20 -07:00
Violet code review updates.
This commit is contained in:
@@ -10,13 +10,6 @@ from .locs import (OrbLocations as Orbs,
|
||||
class JakAndDaxterLocation(Location):
|
||||
game: str = jak1_name
|
||||
|
||||
# In AP 0.5.0, the base Location.can_reach function had its two boolean conditions swapped for a faster
|
||||
# short-circuit for better performance. However, Jak seeds actually generate faster using the older method,
|
||||
# which has been re-implemented below.
|
||||
def can_reach(self, state: CollectionState) -> bool:
|
||||
assert self.parent_region, "Can't reach location without region"
|
||||
return self.parent_region.can_reach(state) and self.access_rule(state)
|
||||
|
||||
|
||||
# Different tables for location groups.
|
||||
# Each Item ID == its corresponding Location ID. While we're here, do all the ID conversions needed.
|
||||
|
||||
@@ -55,10 +55,11 @@ def create_regions(world: JakAndDaxterWorld):
|
||||
for bundle_index in range(bundle_count):
|
||||
|
||||
# Unlike Per-Level Orbsanity, Global Orbsanity Locations always have a level_index of 16.
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(16,
|
||||
bundle_index,
|
||||
access_rule=lambda state, bundle=bundle_index:
|
||||
can_reach_orbs_global(state, player, world, bundle))
|
||||
access_rule=lambda state, orb_amount=amount:
|
||||
can_reach_orbs_global(state, player, world, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
menu.connect(orbs)
|
||||
|
||||
|
||||
@@ -29,27 +29,26 @@ def set_orb_trade_rule(world: JakAndDaxterWorld):
|
||||
|
||||
def recalculate_reachable_orbs(state: CollectionState, player: int, world: JakAndDaxterWorld) -> None:
|
||||
|
||||
if not state.prog_items[player]["Reachable Orbs Fresh"]:
|
||||
# Recalculate every level, every time the cache is stale, because you don't know
|
||||
# when a specific bundle of orbs in one level may unlock access to another.
|
||||
accessible_total_orbs = 0
|
||||
for level in level_table:
|
||||
accessible_level_orbs = count_reachable_orbs_level(state, world, level)
|
||||
accessible_total_orbs += accessible_level_orbs
|
||||
state.prog_items[player][f"{level} Reachable Orbs".lstrip()] = accessible_level_orbs
|
||||
|
||||
# Recalculate every level, every time the cache is stale, because you don't know
|
||||
# when a specific bundle of orbs in one level may unlock access to another.
|
||||
for level in level_table:
|
||||
state.prog_items[player][f"{level} Reachable Orbs".strip()] = (
|
||||
count_reachable_orbs_level(state, world, level))
|
||||
|
||||
# Also recalculate the global count, still used even when Orbsanity is Off.
|
||||
state.prog_items[player]["Reachable Orbs"] = count_reachable_orbs_global(state, world)
|
||||
state.prog_items[player]["Reachable Orbs Fresh"] = True
|
||||
# Also recalculate the global count, still used even when Orbsanity is Off.
|
||||
state.prog_items[player]["Reachable Orbs"] = accessible_total_orbs
|
||||
state.prog_items[player]["Reachable Orbs Fresh"] = True
|
||||
|
||||
|
||||
def count_reachable_orbs_global(state: CollectionState,
|
||||
world: JakAndDaxterWorld) -> int:
|
||||
|
||||
accessible_orbs = 0
|
||||
for level_regions in world.level_to_regions.values():
|
||||
for level_regions in world.level_to_orb_regions.values():
|
||||
for region in level_regions:
|
||||
# Rely on short-circuiting to skip region.can_reach whenever possible.
|
||||
if region.orb_count > 0 and region.can_reach(state):
|
||||
if region.can_reach(state):
|
||||
accessible_orbs += region.orb_count
|
||||
return accessible_orbs
|
||||
|
||||
@@ -59,9 +58,8 @@ def count_reachable_orbs_level(state: CollectionState,
|
||||
level_name: str = "") -> int:
|
||||
|
||||
accessible_orbs = 0
|
||||
for region in world.level_to_regions[level_name]:
|
||||
# Rely on short-circuiting to skip region.can_reach whenever possible.
|
||||
if region.orb_count > 0 and region.can_reach(state):
|
||||
for region in world.level_to_orb_regions[level_name]:
|
||||
if region.can_reach(state):
|
||||
accessible_orbs += region.orb_count
|
||||
return accessible_orbs
|
||||
|
||||
@@ -69,20 +67,24 @@ def count_reachable_orbs_level(state: CollectionState,
|
||||
def can_reach_orbs_global(state: CollectionState,
|
||||
player: int,
|
||||
world: JakAndDaxterWorld,
|
||||
bundle: int) -> bool:
|
||||
orb_amount: int) -> bool:
|
||||
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
return state.has("Reachable Orbs", player, world.orb_bundle_size * (bundle + 1))
|
||||
if not state.prog_items[player]["Reachable Orbs Fresh"]:
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
|
||||
return state.has("Reachable Orbs", player, orb_amount)
|
||||
|
||||
|
||||
def can_reach_orbs_level(state: CollectionState,
|
||||
player: int,
|
||||
world: JakAndDaxterWorld,
|
||||
level_name: str,
|
||||
bundle: int) -> bool:
|
||||
orb_amount: int) -> bool:
|
||||
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
return state.has(f"{level_name} Reachable Orbs", player, world.orb_bundle_size * (bundle + 1))
|
||||
if not state.prog_items[player]["Reachable Orbs Fresh"]:
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
|
||||
return state.has(f"{level_name} Reachable Orbs", player, orb_amount)
|
||||
|
||||
|
||||
def can_trade_vanilla(state: CollectionState,
|
||||
@@ -92,7 +94,9 @@ def can_trade_vanilla(state: CollectionState,
|
||||
required_previous_trade: typing.Optional[int] = None) -> bool:
|
||||
|
||||
# With Orbsanity Off, Reachable Orbs are in fact Tradeable Orbs.
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
if not state.prog_items[player]["Reachable Orbs Fresh"]:
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
|
||||
if required_previous_trade:
|
||||
name_of_previous_trade = location_table[Cells.to_ap_id(required_previous_trade)]
|
||||
return (state.has("Reachable Orbs", player, required_orbs)
|
||||
@@ -107,7 +111,9 @@ def can_trade_orbsanity(state: CollectionState,
|
||||
required_previous_trade: typing.Optional[int] = None) -> bool:
|
||||
|
||||
# Yes, even Orbsanity trades may unlock access to new Reachable Orbs.
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
if not state.prog_items[player]["Reachable Orbs Fresh"]:
|
||||
recalculate_reachable_orbs(state, player, world)
|
||||
|
||||
if required_previous_trade:
|
||||
name_of_previous_trade = location_table[Cells.to_ap_id(required_previous_trade)]
|
||||
return (state.has("Tradeable Orbs", player, required_orbs)
|
||||
|
||||
@@ -226,14 +226,16 @@ class JakAndDaxterWorld(World):
|
||||
power_cell_thresholds: list[int]
|
||||
trap_weights: tuple[list[str], list[int]]
|
||||
|
||||
# Store a dictionary of levels to regions for faster access.
|
||||
level_to_regions: dict[str, list[JakAndDaxterRegion]]
|
||||
# Store these dictionaries for speed improvements.
|
||||
level_to_regions: dict[str, list[JakAndDaxterRegion]] # Contains all levels and regions.
|
||||
level_to_orb_regions: dict[str, list[JakAndDaxterRegion]] # Contains only regions which contain orbs.
|
||||
|
||||
# Handles various options validation, rules enforcement, and caching of important information.
|
||||
def generate_early(self) -> None:
|
||||
|
||||
# Initialize the level-region dictionary.
|
||||
self.level_to_regions = defaultdict(list)
|
||||
self.level_to_orb_regions = defaultdict(list)
|
||||
|
||||
# Cache the power cell threshold values for quicker reference.
|
||||
self.power_cell_thresholds = [
|
||||
@@ -315,16 +317,22 @@ class JakAndDaxterWorld(World):
|
||||
create_regions(self)
|
||||
|
||||
# Don't forget to add the created regions to the multiworld!
|
||||
for level_regions in self.level_to_regions.values():
|
||||
self.multiworld.regions.extend(level_regions)
|
||||
for level in self.level_to_regions:
|
||||
self.multiworld.regions.extend(self.level_to_regions[level])
|
||||
|
||||
# As a lazy measure, let's also fill level_to_orb_regions here.
|
||||
# This should help speed up orbsanity calculations.
|
||||
self.level_to_orb_regions[level] = [reg for reg in self.level_to_regions[level] if reg.orb_count > 0]
|
||||
|
||||
# from Utils import visualize_regions
|
||||
# visualize_regions(self.multiworld.get_region("Menu", self.player), "jakanddaxter.puml")
|
||||
|
||||
# Helper function to reuse some nasty if/else trees. This outputs a list of pairs of item count and classification.
|
||||
# For instance, not all 101 power cells need to be marked progression if you only need 72 to beat the game. So we
|
||||
# will have 72 Progression Power Cells, and 29 Filler Power Cells.
|
||||
def item_type_helper(self, item: int) -> list[tuple[int, ItemClass]]:
|
||||
"""
|
||||
Helper function to reuse some nasty if/else trees. This outputs a list of pairs of item count and classification.
|
||||
For instance, not all 101 power cells need to be marked progression if you only need 72 to beat the game. So we
|
||||
will have 72 Progression Power Cells, and 29 Filler Power Cells.
|
||||
"""
|
||||
counts_and_classes: list[tuple[int, ItemClass]] = []
|
||||
|
||||
# Make N Power Cells. We only want AP's Progression Fill routine to handle the amount of cells we need
|
||||
|
||||
@@ -160,10 +160,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(8,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(5,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -90,10 +90,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt
|
||||
|
||||
bundle_count = 150 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(3,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -35,10 +35,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(0,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -115,10 +115,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(15,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(14,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -142,10 +142,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(7,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -118,10 +118,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 150 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(4,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -53,10 +53,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(10,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -25,10 +25,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(9,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -61,10 +61,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> tuple[JakAndDaxt
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(6,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -70,10 +70,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(1,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -95,10 +95,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 150 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(2,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -190,10 +190,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(12,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -114,10 +114,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 200 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(13,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
@@ -39,10 +39,11 @@ def build_regions(level_name: str, world: JakAndDaxterWorld) -> JakAndDaxterRegi
|
||||
|
||||
bundle_count = 50 // world.orb_bundle_size
|
||||
for bundle_index in range(bundle_count):
|
||||
amount = world.orb_bundle_size * (bundle_index + 1)
|
||||
orbs.add_orb_locations(11,
|
||||
bundle_index,
|
||||
access_rule=lambda state, level=level_name, bundle=bundle_index:
|
||||
can_reach_orbs_level(state, player, world, level, bundle))
|
||||
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||||
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||||
multiworld.regions.append(orbs)
|
||||
main_area.connect(orbs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user