From 0b26a99da0d6bbfa6f2e5d66ccb61023d9676ab9 Mon Sep 17 00:00:00 2001 From: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> Date: Sat, 20 Apr 2024 13:14:00 -0400 Subject: [PATCH] Jak 1: Add more scout fly ID's, refactor game/AP ID translation for easier reading and code reuse. --- worlds/jakanddaxter/GameID.py | 12 +- worlds/jakanddaxter/Items.py | 4 +- worlds/jakanddaxter/Locations.py | 104 +++------ worlds/jakanddaxter/Regions.py | 250 ++++++++++----------- worlds/jakanddaxter/Rules.py | 190 ++++++++-------- worlds/jakanddaxter/__init__.py | 27 +-- worlds/jakanddaxter/locs/CellLocations.py | 13 ++ worlds/jakanddaxter/locs/OrbLocations.py | 37 ++- worlds/jakanddaxter/locs/ScoutLocations.py | 88 +++++--- 9 files changed, 373 insertions(+), 352 deletions(-) diff --git a/worlds/jakanddaxter/GameID.py b/worlds/jakanddaxter/GameID.py index a025f2752a..d3e7582660 100644 --- a/worlds/jakanddaxter/GameID.py +++ b/worlds/jakanddaxter/GameID.py @@ -1,13 +1,5 @@ # All Jak And Daxter IDs must be offset by this number. -game_id = 746800000 +jak1_id = 741000000 # The name of the game. -game_name = "Jak and Daxter: The Precursor Legacy" - -# What follows are offsets for each Location/Item type, -# necessary for Archipelago to avoid collision between -# ID numbers shared across items. See respective -# Locations files for explanations. -cell_offset = 0 -fly_offset = 128 # 2^7 -orb_offset = 2097152 # 2^21 +jak1_name = "Jak and Daxter: The Precursor Legacy" diff --git a/worlds/jakanddaxter/Items.py b/worlds/jakanddaxter/Items.py index 4c9f16af0e..d051f15869 100644 --- a/worlds/jakanddaxter/Items.py +++ b/worlds/jakanddaxter/Items.py @@ -1,7 +1,7 @@ from BaseClasses import Item -from .GameID import game_name +from .GameID import jak1_name class JakAndDaxterItem(Item): - game: str = game_name + game: str = jak1_name diff --git a/worlds/jakanddaxter/Locations.py b/worlds/jakanddaxter/Locations.py index 84dd27f6ed..ef56137cf1 100644 --- a/worlds/jakanddaxter/Locations.py +++ b/worlds/jakanddaxter/Locations.py @@ -1,78 +1,46 @@ from BaseClasses import Location -from .GameID import game_id, game_name, cell_offset, fly_offset -from .locs import CellLocations, ScoutLocations +from .GameID import jak1_name +from .locs import CellLocations as Cells, ScoutLocations as Scouts class JakAndDaxterLocation(Location): - game: str = game_name + game: str = jak1_name # All Locations # Because all items in Jak And Daxter are unique and do not regenerate, we can use this same table as our item table. -# Each Item ID == its corresponding Location ID. And then we only have to do this ugly math once. +# Each Item ID == its corresponding Location ID. While we're here, do all the ID conversions needed. location_table = { - **{game_id + cell_offset + k: CellLocations.locGR_cellTable[k] - for k in CellLocations.locGR_cellTable}, - **{game_id + cell_offset + k: CellLocations.locSV_cellTable[k] - for k in CellLocations.locSV_cellTable}, - **{game_id + cell_offset + k: CellLocations.locFJ_cellTable[k] - for k in CellLocations.locFJ_cellTable}, - **{game_id + cell_offset + k: CellLocations.locSB_cellTable[k] - for k in CellLocations.locSB_cellTable}, - **{game_id + cell_offset + k: CellLocations.locMI_cellTable[k] - for k in CellLocations.locMI_cellTable}, - **{game_id + cell_offset + k: CellLocations.locFC_cellTable[k] - for k in CellLocations.locFC_cellTable}, - **{game_id + cell_offset + k: CellLocations.locRV_cellTable[k] - for k in CellLocations.locRV_cellTable}, - **{game_id + cell_offset + k: CellLocations.locPB_cellTable[k] - for k in CellLocations.locPB_cellTable}, - **{game_id + cell_offset + k: CellLocations.locLPC_cellTable[k] - for k in CellLocations.locLPC_cellTable}, - **{game_id + cell_offset + k: CellLocations.locBS_cellTable[k] - for k in CellLocations.locBS_cellTable}, - **{game_id + cell_offset + k: CellLocations.locMP_cellTable[k] - for k in CellLocations.locMP_cellTable}, - **{game_id + cell_offset + k: CellLocations.locVC_cellTable[k] - for k in CellLocations.locVC_cellTable}, - **{game_id + cell_offset + k: CellLocations.locSC_cellTable[k] - for k in CellLocations.locSC_cellTable}, - **{game_id + cell_offset + k: CellLocations.locSM_cellTable[k] - for k in CellLocations.locSM_cellTable}, - **{game_id + cell_offset + k: CellLocations.locLT_cellTable[k] - for k in CellLocations.locLT_cellTable}, - **{game_id + cell_offset + k: CellLocations.locGMC_cellTable[k] - for k in CellLocations.locGMC_cellTable}, - **{game_id + fly_offset + k: ScoutLocations.locGR_scoutTable[k] - for k in ScoutLocations.locGR_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locSV_scoutTable[k] - for k in ScoutLocations.locSV_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locFJ_scoutTable[k] - for k in ScoutLocations.locFJ_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locSB_scoutTable[k] - for k in ScoutLocations.locSB_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locMI_scoutTable[k] - for k in ScoutLocations.locMI_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locFC_scoutTable[k] - for k in ScoutLocations.locFC_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locRV_scoutTable[k] - for k in ScoutLocations.locRV_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locPB_scoutTable[k] - for k in ScoutLocations.locPB_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locLPC_scoutTable[k] - for k in ScoutLocations.locLPC_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locBS_scoutTable[k] - for k in ScoutLocations.locBS_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locMP_scoutTable[k] - for k in ScoutLocations.locMP_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locVC_scoutTable[k] - for k in ScoutLocations.locVC_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locSC_scoutTable[k] - for k in ScoutLocations.locSC_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locSM_scoutTable[k] - for k in ScoutLocations.locSM_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locLT_scoutTable[k] - for k in ScoutLocations.locLT_scoutTable}, - **{game_id + fly_offset + k: ScoutLocations.locGMC_scoutTable[k] - for k in ScoutLocations.locGMC_scoutTable} + **{Cells.to_ap_id(k): Cells.locGR_cellTable[k] for k in Cells.locGR_cellTable}, + **{Cells.to_ap_id(k): Cells.locSV_cellTable[k] for k in Cells.locSV_cellTable}, + **{Cells.to_ap_id(k): Cells.locFJ_cellTable[k] for k in Cells.locFJ_cellTable}, + **{Cells.to_ap_id(k): Cells.locSB_cellTable[k] for k in Cells.locSB_cellTable}, + **{Cells.to_ap_id(k): Cells.locMI_cellTable[k] for k in Cells.locMI_cellTable}, + **{Cells.to_ap_id(k): Cells.locFC_cellTable[k] for k in Cells.locFC_cellTable}, + **{Cells.to_ap_id(k): Cells.locRV_cellTable[k] for k in Cells.locRV_cellTable}, + **{Cells.to_ap_id(k): Cells.locPB_cellTable[k] for k in Cells.locPB_cellTable}, + **{Cells.to_ap_id(k): Cells.locLPC_cellTable[k] for k in Cells.locLPC_cellTable}, + **{Cells.to_ap_id(k): Cells.locBS_cellTable[k] for k in Cells.locBS_cellTable}, + **{Cells.to_ap_id(k): Cells.locMP_cellTable[k] for k in Cells.locMP_cellTable}, + **{Cells.to_ap_id(k): Cells.locVC_cellTable[k] for k in Cells.locVC_cellTable}, + **{Cells.to_ap_id(k): Cells.locSC_cellTable[k] for k in Cells.locSC_cellTable}, + **{Cells.to_ap_id(k): Cells.locSM_cellTable[k] for k in Cells.locSM_cellTable}, + **{Cells.to_ap_id(k): Cells.locLT_cellTable[k] for k in Cells.locLT_cellTable}, + **{Cells.to_ap_id(k): Cells.locGMC_cellTable[k] for k in Cells.locGMC_cellTable}, + **{Scouts.to_ap_id(k): Scouts.locGR_scoutTable[k] for k in Scouts.locGR_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locSV_scoutTable[k] for k in Scouts.locSV_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locFJ_scoutTable[k] for k in Scouts.locFJ_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locSB_scoutTable[k] for k in Scouts.locSB_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locMI_scoutTable[k] for k in Scouts.locMI_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locFC_scoutTable[k] for k in Scouts.locFC_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locRV_scoutTable[k] for k in Scouts.locRV_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locPB_scoutTable[k] for k in Scouts.locPB_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locLPC_scoutTable[k] for k in Scouts.locLPC_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locBS_scoutTable[k] for k in Scouts.locBS_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locMP_scoutTable[k] for k in Scouts.locMP_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locVC_scoutTable[k] for k in Scouts.locVC_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locSC_scoutTable[k] for k in Scouts.locSC_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locSM_scoutTable[k] for k in Scouts.locSM_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locLT_scoutTable[k] for k in Scouts.locLT_scoutTable}, + **{Scouts.to_ap_id(k): Scouts.locGMC_scoutTable[k] for k in Scouts.locGMC_scoutTable} } diff --git a/worlds/jakanddaxter/Regions.py b/worlds/jakanddaxter/Regions.py index 8fb62c9eda..8b4afe790c 100644 --- a/worlds/jakanddaxter/Regions.py +++ b/worlds/jakanddaxter/Regions.py @@ -1,13 +1,13 @@ import typing from enum import Enum, auto from BaseClasses import MultiWorld, Region -from .GameID import game_id, game_name, cell_offset, fly_offset +from .GameID import jak1_name from .Options import JakAndDaxterOptions from .Locations import JakAndDaxterLocation, location_table -from .locs import CellLocations, ScoutLocations +from .locs import CellLocations as Cells, ScoutLocations as Scouts -class JakAndDaxterLevel(int, Enum): +class Jak1Level(int, Enum): GEYSER_ROCK = auto() SANDOVER_VILLAGE = auto() FORBIDDEN_JUNGLE = auto() @@ -26,7 +26,7 @@ class JakAndDaxterLevel(int, Enum): GOL_AND_MAIAS_CITADEL = auto() -class JakAndDaxterSubLevel(int, Enum): +class Jak1SubLevel(int, Enum): MAIN_AREA = auto() FORBIDDEN_JUNGLE_SWITCH_ROOM = auto() FORBIDDEN_JUNGLE_PLANT_ROOM = auto() @@ -44,165 +44,161 @@ class JakAndDaxterSubLevel(int, Enum): GOL_AND_MAIAS_CITADEL_FINAL_BOSS = auto() -level_table: typing.Dict[JakAndDaxterLevel, str] = { - JakAndDaxterLevel.GEYSER_ROCK: "Geyser Rock", - JakAndDaxterLevel.SANDOVER_VILLAGE: "Sandover Village", - JakAndDaxterLevel.FORBIDDEN_JUNGLE: "Forbidden Jungle", - JakAndDaxterLevel.SENTINEL_BEACH: "Sentinel Beach", - JakAndDaxterLevel.MISTY_ISLAND: "Misty Island", - JakAndDaxterLevel.FIRE_CANYON: "Fire Canyon", - JakAndDaxterLevel.ROCK_VILLAGE: "Rock Village", - JakAndDaxterLevel.PRECURSOR_BASIN: "Precursor Basin", - JakAndDaxterLevel.LOST_PRECURSOR_CITY: "Lost Precursor City", - JakAndDaxterLevel.BOGGY_SWAMP: "Boggy Swamp", - JakAndDaxterLevel.MOUNTAIN_PASS: "Mountain Pass", - JakAndDaxterLevel.VOLCANIC_CRATER: "Volcanic Crater", - JakAndDaxterLevel.SPIDER_CAVE: "Spider Cave", - JakAndDaxterLevel.SNOWY_MOUNTAIN: "Snowy Mountain", - JakAndDaxterLevel.LAVA_TUBE: "Lava Tube", - JakAndDaxterLevel.GOL_AND_MAIAS_CITADEL: "Gol and Maia's Citadel" +level_table: typing.Dict[Jak1Level, str] = { + Jak1Level.GEYSER_ROCK: "Geyser Rock", + Jak1Level.SANDOVER_VILLAGE: "Sandover Village", + Jak1Level.FORBIDDEN_JUNGLE: "Forbidden Jungle", + Jak1Level.SENTINEL_BEACH: "Sentinel Beach", + Jak1Level.MISTY_ISLAND: "Misty Island", + Jak1Level.FIRE_CANYON: "Fire Canyon", + Jak1Level.ROCK_VILLAGE: "Rock Village", + Jak1Level.PRECURSOR_BASIN: "Precursor Basin", + Jak1Level.LOST_PRECURSOR_CITY: "Lost Precursor City", + Jak1Level.BOGGY_SWAMP: "Boggy Swamp", + Jak1Level.MOUNTAIN_PASS: "Mountain Pass", + Jak1Level.VOLCANIC_CRATER: "Volcanic Crater", + Jak1Level.SPIDER_CAVE: "Spider Cave", + Jak1Level.SNOWY_MOUNTAIN: "Snowy Mountain", + Jak1Level.LAVA_TUBE: "Lava Tube", + Jak1Level.GOL_AND_MAIAS_CITADEL: "Gol and Maia's Citadel" } -subLevel_table: typing.Dict[JakAndDaxterSubLevel, str] = { - JakAndDaxterSubLevel.MAIN_AREA: "Main Area", - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM: "Forbidden Jungle Switch Room", - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM: "Forbidden Jungle Plant Room", - JakAndDaxterSubLevel.SENTINEL_BEACH_CANNON_TOWER: "Sentinel Beach Cannon Tower", - JakAndDaxterSubLevel.PRECURSOR_BASIN_BLUE_RINGS: "Precursor Basin Blue Rings", - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM: "Lost Precursor City Sunken Room", - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM: "Lost Precursor City Helix Room", - JakAndDaxterSubLevel.BOGGY_SWAMP_FLUT_FLUT: "Boggy Swamp Flut Flut", - JakAndDaxterSubLevel.MOUNTAIN_PASS_RACE: "Mountain Pass Race", - JakAndDaxterSubLevel.MOUNTAIN_PASS_SHORTCUT: "Mountain Pass Shortcut", - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FLUT_FLUT: "Snowy Mountain Flut Flut", - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_LURKER_FORT: "Snowy Mountain Lurker Fort", - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FROZEN_BOX: "Snowy Mountain Frozen Box", - JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER: "Gol and Maia's Citadel Rotating Tower", - JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS: "Gol and Maia's Citadel Final Boss" +subLevel_table: typing.Dict[Jak1SubLevel, str] = { + Jak1SubLevel.MAIN_AREA: "Main Area", + Jak1SubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM: "Forbidden Jungle Switch Room", + Jak1SubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM: "Forbidden Jungle Plant Room", + Jak1SubLevel.SENTINEL_BEACH_CANNON_TOWER: "Sentinel Beach Cannon Tower", + Jak1SubLevel.PRECURSOR_BASIN_BLUE_RINGS: "Precursor Basin Blue Rings", + Jak1SubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM: "Lost Precursor City Sunken Room", + Jak1SubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM: "Lost Precursor City Helix Room", + Jak1SubLevel.BOGGY_SWAMP_FLUT_FLUT: "Boggy Swamp Flut Flut", + Jak1SubLevel.MOUNTAIN_PASS_RACE: "Mountain Pass Race", + Jak1SubLevel.MOUNTAIN_PASS_SHORTCUT: "Mountain Pass Shortcut", + Jak1SubLevel.SNOWY_MOUNTAIN_FLUT_FLUT: "Snowy Mountain Flut Flut", + Jak1SubLevel.SNOWY_MOUNTAIN_LURKER_FORT: "Snowy Mountain Lurker Fort", + Jak1SubLevel.SNOWY_MOUNTAIN_FROZEN_BOX: "Snowy Mountain Frozen Box", + Jak1SubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER: "Gol and Maia's Citadel Rotating Tower", + Jak1SubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS: "Gol and Maia's Citadel Final Boss" } class JakAndDaxterRegion(Region): - game: str = game_name + game: str = jak1_name -# Use the original ID's for each item to tell the Region which Locations are available in it. -# You do NOT need to add the item offsets, that will be handled by create_*_locations. +# Use the original game ID's for each item to tell the Region which Locations are available in it. +# You do NOT need to add the item offsets or game ID, that will be handled by create_*_locations. def create_regions(multiworld: MultiWorld, options: JakAndDaxterOptions, player: int): create_region(player, multiworld, "Menu") - region_gr = create_region(player, multiworld, level_table[JakAndDaxterLevel.GEYSER_ROCK]) - create_cell_locations(region_gr, CellLocations.locGR_cellTable) - create_fly_locations(region_gr, ScoutLocations.locGR_scoutTable) + region_gr = create_region(player, multiworld, level_table[Jak1Level.GEYSER_ROCK]) + create_cell_locations(region_gr, Cells.locGR_cellTable) + create_fly_locations(region_gr, Scouts.locGR_scoutTable) - region_sv = create_region(player, multiworld, level_table[JakAndDaxterLevel.SANDOVER_VILLAGE]) - create_cell_locations(region_sv, CellLocations.locSV_cellTable) - create_fly_locations(region_sv, ScoutLocations.locSV_scoutTable) + region_sv = create_region(player, multiworld, level_table[Jak1Level.SANDOVER_VILLAGE]) + create_cell_locations(region_sv, Cells.locSV_cellTable) + create_fly_locations(region_sv, Scouts.locSV_scoutTable) - region_fj = create_region(player, multiworld, level_table[JakAndDaxterLevel.FORBIDDEN_JUNGLE]) - create_cell_locations(region_fj, {k: CellLocations.locFJ_cellTable[k] for k in {3, 4, 5, 8, 9, 7}}) - create_fly_locations(region_fj, ScoutLocations.locFJ_scoutTable) + region_fj = create_region(player, multiworld, level_table[Jak1Level.FORBIDDEN_JUNGLE]) + create_cell_locations(region_fj, {k: Cells.locFJ_cellTable[k] for k in {3, 4, 5, 8, 9, 7}}) + create_fly_locations(region_fj, Scouts.locFJ_scoutTable) - sub_region_fjsr = create_subregion(region_fj, subLevel_table[JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM]) - create_cell_locations(sub_region_fjsr, {k: CellLocations.locFJ_cellTable[k] for k in {2}}) + sub_region_fjsr = create_subregion(region_fj, subLevel_table[Jak1SubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM]) + create_cell_locations(sub_region_fjsr, {k: Cells.locFJ_cellTable[k] for k in {2}}) - sub_region_fjpr = create_subregion(sub_region_fjsr, subLevel_table[JakAndDaxterSubLevel - .FORBIDDEN_JUNGLE_PLANT_ROOM]) - create_cell_locations(sub_region_fjpr, {k: CellLocations.locFJ_cellTable[k] for k in {6}}) + sub_region_fjpr = create_subregion(sub_region_fjsr, subLevel_table[Jak1SubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM]) + create_cell_locations(sub_region_fjpr, {k: Cells.locFJ_cellTable[k] for k in {6}}) - region_sb = create_region(player, multiworld, level_table[JakAndDaxterLevel.SENTINEL_BEACH]) - create_cell_locations(region_sb, {k: CellLocations.locSB_cellTable[k] for k in {15, 17, 16, 18, 21, 22, 20}}) - create_fly_locations(region_sb, ScoutLocations.locSB_scoutTable) + region_sb = create_region(player, multiworld, level_table[Jak1Level.SENTINEL_BEACH]) + create_cell_locations(region_sb, {k: Cells.locSB_cellTable[k] for k in {15, 17, 16, 18, 21, 22, 20}}) + create_fly_locations(region_sb, Scouts.locSB_scoutTable) - sub_region_sbct = create_subregion(region_sb, subLevel_table[JakAndDaxterSubLevel.SENTINEL_BEACH_CANNON_TOWER]) - create_cell_locations(sub_region_sbct, {k: CellLocations.locSB_cellTable[k] for k in {19}}) + sub_region_sbct = create_subregion(region_sb, subLevel_table[Jak1SubLevel.SENTINEL_BEACH_CANNON_TOWER]) + create_cell_locations(sub_region_sbct, {k: Cells.locSB_cellTable[k] for k in {19}}) - region_mi = create_region(player, multiworld, level_table[JakAndDaxterLevel.MISTY_ISLAND]) - create_cell_locations(region_mi, CellLocations.locMI_cellTable) - create_fly_locations(region_mi, ScoutLocations.locMI_scoutTable) + region_mi = create_region(player, multiworld, level_table[Jak1Level.MISTY_ISLAND]) + create_cell_locations(region_mi, Cells.locMI_cellTable) + create_fly_locations(region_mi, Scouts.locMI_scoutTable) - region_fc = create_region(player, multiworld, level_table[JakAndDaxterLevel.FIRE_CANYON]) - create_cell_locations(region_fc, CellLocations.locFC_cellTable) - create_fly_locations(region_fc, ScoutLocations.locFC_scoutTable) + region_fc = create_region(player, multiworld, level_table[Jak1Level.FIRE_CANYON]) + create_cell_locations(region_fc, Cells.locFC_cellTable) + create_fly_locations(region_fc, Scouts.locFC_scoutTable) - region_rv = create_region(player, multiworld, level_table[JakAndDaxterLevel.ROCK_VILLAGE]) - create_cell_locations(region_rv, CellLocations.locRV_cellTable) - create_fly_locations(region_rv, ScoutLocations.locRV_scoutTable) + region_rv = create_region(player, multiworld, level_table[Jak1Level.ROCK_VILLAGE]) + create_cell_locations(region_rv, Cells.locRV_cellTable) + create_fly_locations(region_rv, Scouts.locRV_scoutTable) - region_pb = create_region(player, multiworld, level_table[JakAndDaxterLevel.PRECURSOR_BASIN]) - create_cell_locations(region_pb, {k: CellLocations.locPB_cellTable[k] for k in {54, 53, 52, 56, 55, 58, 57}}) - create_fly_locations(region_pb, ScoutLocations.locPB_scoutTable) + region_pb = create_region(player, multiworld, level_table[Jak1Level.PRECURSOR_BASIN]) + create_cell_locations(region_pb, {k: Cells.locPB_cellTable[k] for k in {54, 53, 52, 56, 55, 58, 57}}) + create_fly_locations(region_pb, Scouts.locPB_scoutTable) - sub_region_pbbr = create_subregion(region_pb, subLevel_table[JakAndDaxterSubLevel.PRECURSOR_BASIN_BLUE_RINGS]) - create_cell_locations(sub_region_pbbr, {k: CellLocations.locPB_cellTable[k] for k in {59}}) + sub_region_pbbr = create_subregion(region_pb, subLevel_table[Jak1SubLevel.PRECURSOR_BASIN_BLUE_RINGS]) + create_cell_locations(sub_region_pbbr, {k: Cells.locPB_cellTable[k] for k in {59}}) - region_lpc = create_region(player, multiworld, level_table[JakAndDaxterLevel.LOST_PRECURSOR_CITY]) - create_cell_locations(region_lpc, {k: CellLocations.locLPC_cellTable[k] for k in {45, 48, 44, 51}}) - create_fly_locations(region_lpc, {k: ScoutLocations.locLPC_scoutTable[k] for k in {157, 158, 159, 160, 161, 162}}) + region_lpc = create_region(player, multiworld, level_table[Jak1Level.LOST_PRECURSOR_CITY]) + create_cell_locations(region_lpc, {k: Cells.locLPC_cellTable[k] for k in {45, 48, 44, 51}}) + create_fly_locations(region_lpc, {k: Scouts.locLPC_scoutTable[k] for k in {157, 158, 159, 160, 161, 162}}) - sub_region_lpcsr = create_subregion(region_lpc, subLevel_table[JakAndDaxterSubLevel - .LOST_PRECURSOR_CITY_SUNKEN_ROOM]) - create_cell_locations(sub_region_lpcsr, {k: CellLocations.locLPC_cellTable[k] for k in {47, 49}}) - create_fly_locations(region_lpc, {k: ScoutLocations.locLPC_scoutTable[k] for k in {163}}) + sub_region_lpcsr = create_subregion(region_lpc, subLevel_table[Jak1SubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM]) + create_cell_locations(sub_region_lpcsr, {k: Cells.locLPC_cellTable[k] for k in {47, 49}}) + create_fly_locations(region_lpc, {k: Scouts.locLPC_scoutTable[k] for k in {163}}) - sub_region_lpchr = create_subregion(region_lpc, subLevel_table[JakAndDaxterSubLevel - .LOST_PRECURSOR_CITY_HELIX_ROOM]) - create_cell_locations(sub_region_lpchr, {k: CellLocations.locLPC_cellTable[k] for k in {46, 50}}) + sub_region_lpchr = create_subregion(region_lpc, subLevel_table[Jak1SubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM]) + create_cell_locations(sub_region_lpchr, {k: Cells.locLPC_cellTable[k] for k in {46, 50}}) - region_bs = create_region(player, multiworld, level_table[JakAndDaxterLevel.BOGGY_SWAMP]) - create_cell_locations(region_bs, {k: CellLocations.locBS_cellTable[k] for k in {36, 38, 39, 40, 41, 42}}) - create_fly_locations(region_bs, {k: ScoutLocations.locBS_scoutTable[k] for k in {164, 165, 166, 167, 170}}) + region_bs = create_region(player, multiworld, level_table[Jak1Level.BOGGY_SWAMP]) + create_cell_locations(region_bs, {k: Cells.locBS_cellTable[k] for k in {36, 38, 39, 40, 41, 42}}) + create_fly_locations(region_bs, {k: Scouts.locBS_scoutTable[k] for k in {164, 165, 166, 167, 170}}) - sub_region_bsff = create_subregion(region_bs, subLevel_table[JakAndDaxterSubLevel.BOGGY_SWAMP_FLUT_FLUT]) - create_cell_locations(sub_region_bsff, {k: CellLocations.locBS_cellTable[k] for k in {43, 37}}) - create_fly_locations(sub_region_bsff, {k: ScoutLocations.locBS_scoutTable[k] for k in {168, 169}}) + sub_region_bsff = create_subregion(region_bs, subLevel_table[Jak1SubLevel.BOGGY_SWAMP_FLUT_FLUT]) + create_cell_locations(sub_region_bsff, {k: Cells.locBS_cellTable[k] for k in {43, 37}}) + create_fly_locations(sub_region_bsff, {k: Scouts.locBS_scoutTable[k] for k in {168, 169}}) - region_mp = create_region(player, multiworld, level_table[JakAndDaxterLevel.MOUNTAIN_PASS]) - create_cell_locations(region_mp, {k: CellLocations.locMP_cellTable[k] for k in {86}}) + region_mp = create_region(player, multiworld, level_table[Jak1Level.MOUNTAIN_PASS]) + create_cell_locations(region_mp, {k: Cells.locMP_cellTable[k] for k in {86}}) - sub_region_mpr = create_subregion(region_mp, subLevel_table[JakAndDaxterSubLevel.MOUNTAIN_PASS_RACE]) - create_cell_locations(sub_region_mpr, {k: CellLocations.locMP_cellTable[k] for k in {87, 88}}) - create_fly_locations(sub_region_mpr, ScoutLocations.locMP_scoutTable) + sub_region_mpr = create_subregion(region_mp, subLevel_table[Jak1SubLevel.MOUNTAIN_PASS_RACE]) + create_cell_locations(sub_region_mpr, {k: Cells.locMP_cellTable[k] for k in {87, 88}}) + create_fly_locations(sub_region_mpr, Scouts.locMP_scoutTable) - sub_region_mps = create_subregion(sub_region_mpr, subLevel_table[JakAndDaxterSubLevel.MOUNTAIN_PASS_SHORTCUT]) - create_cell_locations(sub_region_mps, {k: CellLocations.locMP_cellTable[k] for k in {110}}) + sub_region_mps = create_subregion(sub_region_mpr, subLevel_table[Jak1SubLevel.MOUNTAIN_PASS_SHORTCUT]) + create_cell_locations(sub_region_mps, {k: Cells.locMP_cellTable[k] for k in {110}}) - region_vc = create_region(player, multiworld, level_table[JakAndDaxterLevel.VOLCANIC_CRATER]) - create_cell_locations(region_vc, CellLocations.locVC_cellTable) - create_fly_locations(region_vc, ScoutLocations.locVC_scoutTable) + region_vc = create_region(player, multiworld, level_table[Jak1Level.VOLCANIC_CRATER]) + create_cell_locations(region_vc, Cells.locVC_cellTable) + create_fly_locations(region_vc, Scouts.locVC_scoutTable) - region_sc = create_region(player, multiworld, level_table[JakAndDaxterLevel.SPIDER_CAVE]) - create_cell_locations(region_sc, CellLocations.locSC_cellTable) - create_fly_locations(region_sc, ScoutLocations.locSC_scoutTable) + region_sc = create_region(player, multiworld, level_table[Jak1Level.SPIDER_CAVE]) + create_cell_locations(region_sc, Cells.locSC_cellTable) + create_fly_locations(region_sc, Scouts.locSC_scoutTable) - region_sm = create_region(player, multiworld, level_table[JakAndDaxterLevel.SNOWY_MOUNTAIN]) - create_cell_locations(region_sm, {k: CellLocations.locSM_cellTable[k] for k in {60, 61, 66, 64}}) - create_fly_locations(region_sm, {k: ScoutLocations.locSM_scoutTable[k] for k in {192, 193, 194, 195, 196}}) + region_sm = create_region(player, multiworld, level_table[Jak1Level.SNOWY_MOUNTAIN]) + create_cell_locations(region_sm, {k: Cells.locSM_cellTable[k] for k in {60, 61, 66, 64}}) + create_fly_locations(region_sm, {k: Scouts.locSM_scoutTable[k] for k in {192, 193, 194, 195, 196}}) - sub_region_smfb = create_subregion(region_sm, subLevel_table[JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FROZEN_BOX]) - create_cell_locations(sub_region_smfb, {k: CellLocations.locSM_cellTable[k] for k in {67}}) + sub_region_smfb = create_subregion(region_sm, subLevel_table[Jak1SubLevel.SNOWY_MOUNTAIN_FROZEN_BOX]) + create_cell_locations(sub_region_smfb, {k: Cells.locSM_cellTable[k] for k in {67}}) - sub_region_smff = create_subregion(region_sm, subLevel_table[JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FLUT_FLUT]) - create_cell_locations(sub_region_smff, {k: CellLocations.locSM_cellTable[k] for k in {63}}) + sub_region_smff = create_subregion(region_sm, subLevel_table[Jak1SubLevel.SNOWY_MOUNTAIN_FLUT_FLUT]) + create_cell_locations(sub_region_smff, {k: Cells.locSM_cellTable[k] for k in {63}}) - sub_region_smlf = create_subregion(region_sm, subLevel_table[JakAndDaxterSubLevel.SNOWY_MOUNTAIN_LURKER_FORT]) - create_cell_locations(sub_region_smlf, {k: CellLocations.locSM_cellTable[k] for k in {62, 65}}) - create_fly_locations(sub_region_smlf, {k: ScoutLocations.locSM_scoutTable[k] for k in {197, 198}}) + sub_region_smlf = create_subregion(region_sm, subLevel_table[Jak1SubLevel.SNOWY_MOUNTAIN_LURKER_FORT]) + create_cell_locations(sub_region_smlf, {k: Cells.locSM_cellTable[k] for k in {62, 65}}) + create_fly_locations(sub_region_smlf, {k: Scouts.locSM_scoutTable[k] for k in {197, 198}}) - region_lt = create_region(player, multiworld, level_table[JakAndDaxterLevel.LAVA_TUBE]) - create_cell_locations(region_lt, CellLocations.locLT_cellTable) - create_fly_locations(region_lt, ScoutLocations.locLT_scoutTable) + region_lt = create_region(player, multiworld, level_table[Jak1Level.LAVA_TUBE]) + create_cell_locations(region_lt, Cells.locLT_cellTable) + create_fly_locations(region_lt, Scouts.locLT_scoutTable) - region_gmc = create_region(player, multiworld, level_table[JakAndDaxterLevel.GOL_AND_MAIAS_CITADEL]) - create_cell_locations(region_gmc, {k: CellLocations.locGMC_cellTable[k] for k in {71, 72, 73}}) - create_fly_locations(region_gmc, {k: ScoutLocations.locGMC_scoutTable[k] for k in {206, 207, 208, 209, 210, 211}}) + region_gmc = create_region(player, multiworld, level_table[Jak1Level.GOL_AND_MAIAS_CITADEL]) + create_cell_locations(region_gmc, {k: Cells.locGMC_cellTable[k] for k in {71, 72, 73}}) + create_fly_locations(region_gmc, {k: Scouts.locGMC_scoutTable[k] for k in {206, 207, 208, 209, 210, 211}}) - sub_region_gmcrt = create_subregion(region_gmc, subLevel_table[JakAndDaxterSubLevel - .GOL_AND_MAIAS_CITADEL_ROTATING_TOWER]) - create_cell_locations(sub_region_gmcrt, {k: CellLocations.locGMC_cellTable[k] for k in {70, 91}}) - create_fly_locations(sub_region_gmcrt, {k: ScoutLocations.locGMC_scoutTable[k] for k in {212}}) + sub_region_gmcrt = create_subregion(region_gmc, subLevel_table[Jak1SubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER]) + create_cell_locations(sub_region_gmcrt, {k: Cells.locGMC_cellTable[k] for k in {70, 91}}) + create_fly_locations(sub_region_gmcrt, {k: Scouts.locGMC_scoutTable[k] for k in {212}}) - create_subregion(sub_region_gmcrt, subLevel_table[JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS]) + create_subregion(sub_region_gmcrt, subLevel_table[Jak1SubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS]) def create_region(player: int, multiworld: MultiWorld, name: str) -> JakAndDaxterRegion: @@ -219,13 +215,13 @@ def create_subregion(parent: Region, name: str) -> JakAndDaxterRegion: def create_cell_locations(region: Region, locations: typing.Dict[int, str]): region.locations += [JakAndDaxterLocation(region.player, - location_table[game_id + cell_offset + loc], - game_id + cell_offset + loc, + location_table[Cells.to_ap_id(loc)], + Cells.to_ap_id(loc), region) for loc in locations] def create_fly_locations(region: Region, locations: typing.Dict[int, str]): region.locations += [JakAndDaxterLocation(region.player, - location_table[game_id + fly_offset + loc], - game_id + fly_offset + loc, + location_table[Scouts.to_ap_id(loc)], + Scouts.to_ap_id(loc), region) for loc in locations] diff --git a/worlds/jakanddaxter/Rules.py b/worlds/jakanddaxter/Rules.py index 58585bba18..d0c778f9d9 100644 --- a/worlds/jakanddaxter/Rules.py +++ b/worlds/jakanddaxter/Rules.py @@ -1,225 +1,221 @@ from BaseClasses import MultiWorld -from .GameID import game_id, cell_offset, fly_offset from .Options import JakAndDaxterOptions -from .Regions import JakAndDaxterLevel, JakAndDaxterSubLevel, level_table, subLevel_table +from .Regions import Jak1Level, Jak1SubLevel, level_table, subLevel_table from .Locations import location_table as item_table -from .locs.CellLocations import locGR_cellTable +from .locs import CellLocations as Cells, ScoutLocations as Scouts def set_rules(multiworld: MultiWorld, options: JakAndDaxterOptions, player: int): # Setting up some useful variables here because the offset numbers can get confusing # for access rules. Feel free to add more variables here to keep the code more readable. - gr_cells = {game_id + cell_offset + k for k in locGR_cellTable} - fj_temple_top = game_id + cell_offset + 4 - fj_blue_switch = game_id + cell_offset + 2 - fj_plant_boss = game_id + cell_offset + 6 - fj_fisherman = game_id + cell_offset + 5 - sb_flut_flut = game_id + cell_offset + 17 - fc_end = game_id + cell_offset + 69 - pb_purple_rings = game_id + cell_offset + 58 - lpc_sunken = game_id + cell_offset + 47 - lpc_helix = game_id + cell_offset + 50 - mp_klaww = game_id + cell_offset + 86 - mp_end = game_id + cell_offset + 87 - sm_yellow_switch = game_id + cell_offset + 60 - sm_fort_gate = game_id + cell_offset + 63 - lt_end = game_id + cell_offset + 89 - gmc_blue_sage = game_id + cell_offset + 71 - gmc_red_sage = game_id + cell_offset + 72 - gmc_yellow_sage = game_id + cell_offset + 73 - gmc_green_sage = game_id + cell_offset + 70 + # You DO need to convert the game ID's to AP ID's here. + gr_cells = {Cells.to_ap_id(k) for k in Cells.locGR_cellTable} + fj_temple_top = Cells.to_ap_id(4) + fj_blue_switch = Cells.to_ap_id(2) + fj_plant_boss = Cells.to_ap_id(6) + fj_fisherman = Cells.to_ap_id(5) + sb_flut_flut = Cells.to_ap_id(17) + fc_end = Cells.to_ap_id(69) + pb_purple_rings = Cells.to_ap_id(58) + lpc_sunken = Cells.to_ap_id(47) + lpc_helix = Cells.to_ap_id(50) + mp_klaww = Cells.to_ap_id(86) + mp_end = Cells.to_ap_id(87) + sm_yellow_switch = Cells.to_ap_id(60) + sm_fort_gate = Cells.to_ap_id(63) + lt_end = Cells.to_ap_id(89) + gmc_blue_sage = Cells.to_ap_id(71) + gmc_red_sage = Cells.to_ap_id(72) + gmc_yellow_sage = Cells.to_ap_id(73) + gmc_green_sage = Cells.to_ap_id(70) # Start connecting regions and set their access rules. - connect_start(multiworld, player, JakAndDaxterLevel.GEYSER_ROCK) + connect_start(multiworld, player, Jak1Level.GEYSER_ROCK) connect_regions(multiworld, player, - JakAndDaxterLevel.GEYSER_ROCK, - JakAndDaxterLevel.SANDOVER_VILLAGE, + Jak1Level.GEYSER_ROCK, + Jak1Level.SANDOVER_VILLAGE, lambda state: state.has_all({item_table[k] for k in gr_cells}, player)) connect_regions(multiworld, player, - JakAndDaxterLevel.SANDOVER_VILLAGE, - JakAndDaxterLevel.FORBIDDEN_JUNGLE) + Jak1Level.SANDOVER_VILLAGE, + Jak1Level.FORBIDDEN_JUNGLE) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.FORBIDDEN_JUNGLE, - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM, + Jak1Level.FORBIDDEN_JUNGLE, + Jak1SubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM, lambda state: state.has(item_table[fj_temple_top], player)) connect_subregions(multiworld, player, - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM, - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM, + Jak1SubLevel.FORBIDDEN_JUNGLE_SWITCH_ROOM, + Jak1SubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM, lambda state: state.has(item_table[fj_blue_switch], player)) connect_sub_to_region(multiworld, player, - JakAndDaxterSubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM, - JakAndDaxterLevel.FORBIDDEN_JUNGLE, + Jak1SubLevel.FORBIDDEN_JUNGLE_PLANT_ROOM, + Jak1Level.FORBIDDEN_JUNGLE, lambda state: state.has(item_table[fj_plant_boss], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.SANDOVER_VILLAGE, - JakAndDaxterLevel.SENTINEL_BEACH) + Jak1Level.SANDOVER_VILLAGE, + Jak1Level.SENTINEL_BEACH) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.SENTINEL_BEACH, - JakAndDaxterSubLevel.SENTINEL_BEACH_CANNON_TOWER, + Jak1Level.SENTINEL_BEACH, + Jak1SubLevel.SENTINEL_BEACH_CANNON_TOWER, lambda state: state.has(item_table[fj_blue_switch], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.SANDOVER_VILLAGE, - JakAndDaxterLevel.MISTY_ISLAND, + Jak1Level.SANDOVER_VILLAGE, + Jak1Level.MISTY_ISLAND, lambda state: state.has(item_table[fj_fisherman], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.SANDOVER_VILLAGE, - JakAndDaxterLevel.FIRE_CANYON, + Jak1Level.SANDOVER_VILLAGE, + Jak1Level.FIRE_CANYON, lambda state: state.count_group("Power Cell", player) >= 20) connect_regions(multiworld, player, - JakAndDaxterLevel.FIRE_CANYON, - JakAndDaxterLevel.ROCK_VILLAGE, + Jak1Level.FIRE_CANYON, + Jak1Level.ROCK_VILLAGE, lambda state: state.has(item_table[fc_end], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.ROCK_VILLAGE, - JakAndDaxterLevel.PRECURSOR_BASIN) + Jak1Level.ROCK_VILLAGE, + Jak1Level.PRECURSOR_BASIN) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.PRECURSOR_BASIN, - JakAndDaxterSubLevel.PRECURSOR_BASIN_BLUE_RINGS, + Jak1Level.PRECURSOR_BASIN, + Jak1SubLevel.PRECURSOR_BASIN_BLUE_RINGS, lambda state: state.has(item_table[pb_purple_rings], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.ROCK_VILLAGE, - JakAndDaxterLevel.LOST_PRECURSOR_CITY) + Jak1Level.ROCK_VILLAGE, + Jak1Level.LOST_PRECURSOR_CITY) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.LOST_PRECURSOR_CITY, - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM) + Jak1Level.LOST_PRECURSOR_CITY, + Jak1SubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM) connect_subregions(multiworld, player, - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM, - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM) + Jak1SubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM, + Jak1SubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM) connect_sub_to_region(multiworld, player, - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM, - JakAndDaxterLevel.LOST_PRECURSOR_CITY, + Jak1SubLevel.LOST_PRECURSOR_CITY_HELIX_ROOM, + Jak1Level.LOST_PRECURSOR_CITY, lambda state: state.has(item_table[lpc_helix], player)) connect_sub_to_region(multiworld, player, - JakAndDaxterSubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM, - JakAndDaxterLevel.ROCK_VILLAGE, + Jak1SubLevel.LOST_PRECURSOR_CITY_SUNKEN_ROOM, + Jak1Level.ROCK_VILLAGE, lambda state: state.has(item_table[lpc_sunken], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.ROCK_VILLAGE, - JakAndDaxterLevel.BOGGY_SWAMP) + Jak1Level.ROCK_VILLAGE, + Jak1Level.BOGGY_SWAMP) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.BOGGY_SWAMP, - JakAndDaxterSubLevel.BOGGY_SWAMP_FLUT_FLUT, + Jak1Level.BOGGY_SWAMP, + Jak1SubLevel.BOGGY_SWAMP_FLUT_FLUT, lambda state: state.has(item_table[sb_flut_flut], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.ROCK_VILLAGE, - JakAndDaxterLevel.MOUNTAIN_PASS, + Jak1Level.ROCK_VILLAGE, + Jak1Level.MOUNTAIN_PASS, lambda state: state.count_group("Power Cell", player) >= 45) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.MOUNTAIN_PASS, - JakAndDaxterSubLevel.MOUNTAIN_PASS_RACE, + Jak1Level.MOUNTAIN_PASS, + Jak1SubLevel.MOUNTAIN_PASS_RACE, lambda state: state.has(item_table[mp_klaww], player)) connect_subregions(multiworld, player, - JakAndDaxterSubLevel.MOUNTAIN_PASS_RACE, - JakAndDaxterSubLevel.MOUNTAIN_PASS_SHORTCUT, + Jak1SubLevel.MOUNTAIN_PASS_RACE, + Jak1SubLevel.MOUNTAIN_PASS_SHORTCUT, lambda state: state.has(item_table[sm_yellow_switch], player)) connect_sub_to_region(multiworld, player, - JakAndDaxterSubLevel.MOUNTAIN_PASS_RACE, - JakAndDaxterLevel.VOLCANIC_CRATER, + Jak1SubLevel.MOUNTAIN_PASS_RACE, + Jak1Level.VOLCANIC_CRATER, lambda state: state.has(item_table[mp_end], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.VOLCANIC_CRATER, - JakAndDaxterLevel.SPIDER_CAVE) + Jak1Level.VOLCANIC_CRATER, + Jak1Level.SPIDER_CAVE) connect_regions(multiworld, player, - JakAndDaxterLevel.VOLCANIC_CRATER, - JakAndDaxterLevel.SNOWY_MOUNTAIN) + Jak1Level.VOLCANIC_CRATER, + Jak1Level.SNOWY_MOUNTAIN) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.SNOWY_MOUNTAIN, - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FROZEN_BOX, + Jak1Level.SNOWY_MOUNTAIN, + Jak1SubLevel.SNOWY_MOUNTAIN_FROZEN_BOX, lambda state: state.has(item_table[sm_yellow_switch], player)) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.SNOWY_MOUNTAIN, - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_FLUT_FLUT, + Jak1Level.SNOWY_MOUNTAIN, + Jak1SubLevel.SNOWY_MOUNTAIN_FLUT_FLUT, lambda state: state.has(item_table[sb_flut_flut], player)) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.SNOWY_MOUNTAIN, - JakAndDaxterSubLevel.SNOWY_MOUNTAIN_LURKER_FORT, + Jak1Level.SNOWY_MOUNTAIN, + Jak1SubLevel.SNOWY_MOUNTAIN_LURKER_FORT, lambda state: state.has(item_table[sm_fort_gate], player)) connect_regions(multiworld, player, - JakAndDaxterLevel.VOLCANIC_CRATER, - JakAndDaxterLevel.LAVA_TUBE, + Jak1Level.VOLCANIC_CRATER, + Jak1Level.LAVA_TUBE, lambda state: state.count_group("Power Cell", player) >= 72) connect_regions(multiworld, player, - JakAndDaxterLevel.LAVA_TUBE, - JakAndDaxterLevel.GOL_AND_MAIAS_CITADEL, + Jak1Level.LAVA_TUBE, + Jak1Level.GOL_AND_MAIAS_CITADEL, lambda state: state.has(item_table[lt_end], player)) connect_region_to_sub(multiworld, player, - JakAndDaxterLevel.GOL_AND_MAIAS_CITADEL, - JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER, + Jak1Level.GOL_AND_MAIAS_CITADEL, + Jak1SubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER, lambda state: state.has(item_table[gmc_blue_sage], player) and state.has(item_table[gmc_red_sage], player) and state.has(item_table[gmc_yellow_sage], player)) connect_subregions(multiworld, player, - JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER, - JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS, + Jak1SubLevel.GOL_AND_MAIAS_CITADEL_ROTATING_TOWER, + Jak1SubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS, lambda state: state.has(item_table[gmc_green_sage], player)) multiworld.completion_condition[player] = lambda state: state.can_reach( - multiworld.get_region(subLevel_table[JakAndDaxterSubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS], player), + multiworld.get_region(subLevel_table[Jak1SubLevel.GOL_AND_MAIAS_CITADEL_FINAL_BOSS], player), "Region", player) -def connect_start(multiworld: MultiWorld, player: int, target: JakAndDaxterLevel): +def connect_start(multiworld: MultiWorld, player: int, target: Jak1Level): menu_region = multiworld.get_region("Menu", player) start_region = multiworld.get_region(level_table[target], player) menu_region.connect(start_region) -def connect_regions(multiworld: MultiWorld, player: int, source: JakAndDaxterLevel, target: JakAndDaxterLevel, - rule=None): +def connect_regions(multiworld: MultiWorld, player: int, source: Jak1Level, target: Jak1Level, rule=None): source_region = multiworld.get_region(level_table[source], player) target_region = multiworld.get_region(level_table[target], player) source_region.connect(target_region, rule=rule) -def connect_region_to_sub(multiworld: MultiWorld, player: int, source: JakAndDaxterLevel, target: JakAndDaxterSubLevel, - rule=None): +def connect_region_to_sub(multiworld: MultiWorld, player: int, source: Jak1Level, target: Jak1SubLevel, rule=None): source_region = multiworld.get_region(level_table[source], player) target_region = multiworld.get_region(subLevel_table[target], player) source_region.connect(target_region, rule=rule) -def connect_sub_to_region(multiworld: MultiWorld, player: int, source: JakAndDaxterSubLevel, target: JakAndDaxterLevel, - rule=None): +def connect_sub_to_region(multiworld: MultiWorld, player: int, source: Jak1SubLevel, target: Jak1Level, rule=None): source_region = multiworld.get_region(subLevel_table[source], player) target_region = multiworld.get_region(level_table[target], player) source_region.connect(target_region, rule=rule) -def connect_subregions(multiworld: MultiWorld, player: int, source: JakAndDaxterSubLevel, target: JakAndDaxterSubLevel, - rule=None): +def connect_subregions(multiworld: MultiWorld, player: int, source: Jak1SubLevel, target: Jak1SubLevel, rule=None): source_region = multiworld.get_region(subLevel_table[source], player) target_region = multiworld.get_region(subLevel_table[target], player) source_region.connect(target_region, rule=rule) diff --git a/worlds/jakanddaxter/__init__.py b/worlds/jakanddaxter/__init__.py index 3a99126b0d..75c498e12f 100644 --- a/worlds/jakanddaxter/__init__.py +++ b/worlds/jakanddaxter/__init__.py @@ -1,16 +1,16 @@ from BaseClasses import Item, ItemClassification -from .Locations import JakAndDaxterLocation, location_table as item_table +from .GameID import jak1_id, jak1_name from .Options import JakAndDaxterOptions -from .Regions import JakAndDaxterLevel, JakAndDaxterSubLevel, JakAndDaxterRegion, level_table, subLevel_table, \ - create_regions -from .Rules import set_rules from .Items import JakAndDaxterItem -from .GameID import game_id, game_name, cell_offset, fly_offset, orb_offset +from .Locations import JakAndDaxterLocation, location_table as item_table +from .locs import CellLocations as Cells, ScoutLocations as Scouts, OrbLocations as Orbs +from .Regions import create_regions +from .Rules import set_rules from ..AutoWorld import World class JakAndDaxterWorld(World): - game: str = game_name + game: str = jak1_name data_version = 1 required_client_version = (0, 4, 5) @@ -18,13 +18,14 @@ class JakAndDaxterWorld(World): options: JakAndDaxterOptions # Stored as {ID: Name} pairs, these must now be swapped to {Name: ID} pairs. + # Remember, the game ID and various offsets for each item type have already been calculated. item_name_to_id = {item_table[k]: k for k in item_table} location_name_to_id = {item_table[k]: k for k in item_table} item_name_groups = { - "Power Cell": {item_table[k]: k - for k in item_table if k in range(game_id + cell_offset, game_id + fly_offset)}, - "Scout Fly": {item_table[k]: k - for k in item_table if k in range(game_id + fly_offset, game_id + orb_offset)}, + "Power Cell": {item_table[k]: k for k in item_table + if k in range(jak1_id, jak1_id + Scouts.fly_offset)}, + "Scout Fly": {item_table[k]: k for k in item_table + if k in range(jak1_id + Scouts.fly_offset, jak1_id + Orbs.orb_offset)}, "Precursor Orb": {} # TODO } @@ -39,13 +40,13 @@ class JakAndDaxterWorld(World): def create_item(self, name: str) -> Item: item_id = self.item_name_to_id[name] - if item_id in range(game_id + cell_offset, game_id + fly_offset): + if item_id in range(jak1_id, jak1_id + Scouts.fly_offset): # Power Cell classification = ItemClassification.progression_skip_balancing - elif item_id in range(game_id + fly_offset, game_id + orb_offset): + elif item_id in range(jak1_id + Scouts.fly_offset, jak1_id + Orbs.orb_offset): # Scout Fly classification = ItemClassification.progression_skip_balancing - elif item_id > game_id + orb_offset: + elif item_id > jak1_id + Orbs.orb_offset: # Precursor Orb classification = ItemClassification.filler # TODO else: diff --git a/worlds/jakanddaxter/locs/CellLocations.py b/worlds/jakanddaxter/locs/CellLocations.py index a16f40ce94..1bff477161 100644 --- a/worlds/jakanddaxter/locs/CellLocations.py +++ b/worlds/jakanddaxter/locs/CellLocations.py @@ -1,3 +1,5 @@ +from ..GameID import jak1_id + # Power Cells are given ID's between 0 and 116 by the game. # The game tracks all game-tasks as integers. @@ -5,6 +7,17 @@ # necessarily ordered, nor are they the first 101 in the task list. # The remaining ones are cutscenes and other events. + +# These helper functions do all the math required to get information about each +# power cell and translate its ID between AP and OpenGOAL. +def to_ap_id(game_id: int) -> int: + return jak1_id + game_id + + +def to_game_id(ap_id: int) -> int: + return ap_id - jak1_id + + # The ID's you see below correspond directly to that cell's game-task ID. # Geyser Rock diff --git a/worlds/jakanddaxter/locs/OrbLocations.py b/worlds/jakanddaxter/locs/OrbLocations.py index 2da5972e17..6b913dc11c 100644 --- a/worlds/jakanddaxter/locs/OrbLocations.py +++ b/worlds/jakanddaxter/locs/OrbLocations.py @@ -1,7 +1,38 @@ -# Precursor Orbs start at ??? and end at ??? +from ..GameID import jak1_id -# Given that Scout Flies are being offset by 2^20 to avoid collisions with power cells, -# I'm tentatively setting the Orb offset to 2^21, or 2,097,152. +# Precursor Orbs are not necessarily given ID's by the game. + +# Of the 2000 orbs (or "money") you can pick up, only 1233 are standalone ones you find in the overworld. +# We can identify them by Actor ID's, which run from 549 to 24433. Other actors reside in this range, +# so like Power Cells these are not ordered, nor contiguous, nor exclusively orbs. + +# In fact, other ID's in this range belong to actors that spawn orbs when they are activated or when they die, +# like steel crates, orb caches, Spider Cave gnawers, or jumping on the Plant Boss's head. + +# These orbs that spawn from parent actors DON'T have an Actor ID themselves - the parent object keeps +# track of how many of its orbs have been picked up. If you pick up only some of its orbs, it +# will respawn when you leave the area, and only drop the remaining number of orbs when activated/killed. +# Once all the orbs are picked up, the actor will permanently "retire" and never spawn again. +# The maximum number of orbs that any actor can spawn is 30 (the orb caches in citadel). Covering +# these ID-less orbs may need to be a future enhancement. TODO ^^ + +# Standalone orbs need 15 bits to identify themselves by Actor ID, +# so we can use 2^15 to offset them from scout flies, just like we offset +# scout flies from power cells by 2^10. +orb_offset = 32768 + + +# These helper functions do all the math required to get information about each +# precursor orb and translate its ID between AP and OpenGOAL. +def to_ap_id(game_id: int) -> int: + return jak1_id + orb_offset + game_id # Add the offsets and the orb Actor ID. + + +def to_game_id(ap_id: int) -> int: + return ap_id - jak1_id - orb_offset # Reverse process, subtract the offsets. + + +# The ID's you see below correspond directly to that orb's Actor ID in the game. # Geyser Rock locGR_orbTable = { diff --git a/worlds/jakanddaxter/locs/ScoutLocations.py b/worlds/jakanddaxter/locs/ScoutLocations.py index cf8fa08420..809df2d287 100644 --- a/worlds/jakanddaxter/locs/ScoutLocations.py +++ b/worlds/jakanddaxter/locs/ScoutLocations.py @@ -1,6 +1,8 @@ +from ..GameID import jak1_id + # Scout Flies are given ID's between 0 and 393311 by the game, explanation below. -# Each fly is given a unique 32-bit number broken into two 16-bit numbers. +# Each fly (or "buzzer") is given a unique 32-bit number broken into two 16-bit numbers. # The lower 16 bits are the game-task ID of the power cell the fly corresponds to. # The higher 16 bits are the index of the fly itself, from 000 (0) to 110 (6). @@ -10,10 +12,32 @@ # Because flies are indexed from 0, each 0th fly's full ID == the power cell's ID. # So we need to offset all of their ID's in order for Archipelago to separate them -# from their power cells. We use 128 (2^7) for this purpose, because scout flies -# never use the 8th lowest bit to describe themselves. +# from their power cells. We can use 1024 (2^10) for this purpose, because scout flies +# only ever need 10 bits to identify themselves (3 for the index, 7 for the cell ID). +fly_offset = 1024 -# TODO - The ID's you see below correspond directly to that fly's 32-bit ID in the game. + +# These helper functions do all the math required to get information about each +# scout fly and translate its ID between AP and OpenGOAL. +def to_ap_id(game_id: int) -> int: + cell_id = get_cell_id(game_id) # This is AP/OpenGOAL agnostic, works on either ID. + buzzer_index = (game_id - cell_id) >> 9 # Subtract the cell ID, bit shift the index down 9 places. + return jak1_id + fly_offset + buzzer_index + cell_id # Add the offsets, the bit-shifted index, and the cell ID. + + +def to_game_id(ap_id: int) -> int: + cell_id = get_cell_id(ap_id) # This is AP/OpenGOAL agnostic, works on either ID. + buzzer_index = ap_id - jak1_id - fly_offset - cell_id # Reverse process, subtract the offsets and the cell ID. + return (buzzer_index << 9) + cell_id # Bit shift the index up 9 places, re-add the cell ID. + + +def get_cell_id(buzzer_id: int) -> int: + return buzzer_id & 0b1111111 # Get the power cell ID from the lowest 7 bits. + + +# The ID's you see below correspond directly to that fly's 32-bit ID in the game. +# I used the decompiled entity JSON's and Jak's X/Y coordinates in Debug Mode +# to determine which box ID is which location. # Geyser Rock locGR_scoutTable = { @@ -61,46 +85,46 @@ locSB_scoutTable = { # Misty Island locMI_scoutTable = { - 129: "MI: Scout Fly Overlooking Entrance", - 130: "MI: Scout Fly On Ledge Path, First", - 131: "MI: Scout Fly On Ledge Path, Second", - 132: "MI: Scout Fly Overlooking Shipyard", - 133: "MI: Scout Fly On Ship", - 134: "MI: Scout Fly On Barrel Ramps", - 135: "MI: Scout Fly On Zoomer Ramps" + 327708: "MI: Scout Fly Overlooking Entrance", + 65564: "MI: Scout Fly On Ledge Near Arena Entrance", + 262172: "MI: Scout Fly Near Arena Door", + 28: "MI: Scout Fly On Ledge Near Arena Exit", + 131100: "MI: Scout Fly On Ship", + 196636: "MI: Scout Fly On Barrel Ramps", + 393244: "MI: Scout Fly On Zoomer Ramps" } # Fire Canyon locFC_scoutTable = { - 136: "FC: Scout Fly 1", - 137: "FC: Scout Fly 2", - 138: "FC: Scout Fly 3", - 139: "FC: Scout Fly 4", - 140: "FC: Scout Fly 5", - 141: "FC: Scout Fly 6", - 142: "FC: Scout Fly 7" + 393284: "FC: Scout Fly 1", + 68: "FC: Scout Fly 2", + 65604: "FC: Scout Fly 3", + 196676: "FC: Scout Fly 4", + 131140: "FC: Scout Fly 5", + 262212: "FC: Scout Fly 6", + 327748: "FC: Scout Fly 7" } # Rock Village locRV_scoutTable = { - 143: "RV: Scout Fly Behind Sage's Hut", - 144: "RV: Scout Fly On Path To Village", - 145: "RV: Scout Fly Behind Geologist", - 146: "RV: Scout Fly Behind Fiery Boulder", - 147: "RV: Scout Fly On Dock", - 148: "RV: Scout Fly At Pontoon Bridge", - 149: "RV: Scout Fly At Boggy Swamp Entrance" + 76: "RV: Scout Fly Behind Sage's Hut", + 131148: "RV: Scout Fly Near Waterfall", + 196684: "RV: Scout Fly Behind Geologist", + 262220: "RV: Scout Fly Behind Fiery Boulder", + 65612: "RV: Scout Fly On Dock", + 327756: "RV: Scout Fly At Pontoon Bridge", + 393292: "RV: Scout Fly At Boggy Swamp Entrance" } # Precursor Basin locPB_scoutTable = { - 150: "PB: Scout Fly Overlooking Entrance", - 151: "PB: Scout Fly Near Mole Hole", - 152: "PB: Scout Fly At Purple Ring Start", - 153: "PB: Scout Fly Overlooking Dark Eco Plant", - 154: "PB: Scout Fly At Green Ring Start", - 155: "PB: Scout Fly Before Big Jump", - 156: "PB: Scout Fly Near Dark Eco Plant" + 196665: "PB: Scout Fly Overlooking Entrance", + 393273: "PB: Scout Fly Near Mole Hole", + 131129: "PB: Scout Fly At Purple Ring Start", + 65593: "PB: Scout Fly Near Dark Eco Plant, Above", + 57: "PB: Scout Fly At Blue Ring Start", + 262201: "PB: Scout Fly Before Big Jump", + 327737: "PB: Scout Fly Near Dark Eco Plant, Below" } # Lost Precursor City