Files
Grinch-AP/worlds/celeste_open_world/Items.py

265 lines
19 KiB
Python
Raw Permalink Normal View History

Celeste (Open World): Implement New Game (#4937) * APWorld Skeleton * Hair Color Rando and first items * All interactable items * Checkpoint Items and Locations * First pass sample intermediate data * Bulk of Region/location code * JSON Data Parser * New items and Level Item mapping * Data Parsing fixes and most of 1a data * 1a complete data and region/location/item creation fixes * Add Key Location type and ID output * Add options to slot data * 1B Level Data * Added Location logging * Add Goal Area Options * 1c Level Data * Old Site A B C level data * Key/Binosanity and Hair Length options * Key Item/Location and Clutter Event handling * Remove generic 'keys' item * 3a level data * 3b and 3c level data * Chapter 4 level data * Chapter 5 Logic Data * Chapter 5 level data * Trap Support * Add TrapLink Support * Chapter 6 A/B/C Level Data * Add active_levels to slot_data * Item and Location Name Groups + style cleanups * Chapter 7 Level Data and Items, Gemsanity option * Goal Area and victory handling * Fix slot_data * Add Core Level Data * Carsanity * Farewell Level Data and ID Range Update * Farewell level data and handling * Music Shuffle * Require Cassettes * Change default trap expiration action to Deaths * Handle Poetry * Mod versioning * Rename folder, general cleanup * Additional Cleanup * Handle Farewell Golden Goal when Include Goldens is off * Better handling of Farewell Golden * Update Docs * Beta test bug fixes * Bump to v1.0.0 * Update Changelog * Several Logic tweaks * Update APWorld Version * Add Celeste (Open World) to README * Peer review changes * Logic Fixes: * Adjust Mirror Temple B Key logic * Increment APWorld version * Fix several logic bugs * Add missing link * Add Item Name Groups for common alternative item names * Account for Madeline's post-Celeste hair-dying activities * Account for ignored member variable and hardcoded color in Celeste codebase * Add Blue Clouds to the logic of reaching Farewell - intro-02-launch * Type checking workaround * Bump version number * Adjust Setup Guide * Minor typing fixes * Logic and PR fixes * Increment APWorld Version * Use more world helpers * Core review * CODEOWNERS
2025-08-31 17:31:09 -04:00
from typing import NamedTuple, Optional
from BaseClasses import Item, ItemClassification
from .Names import ItemName
level_item_lists: dict[str, set[str]] = {
"0a": set(),
"1a": {ItemName.springs, ItemName.traffic_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"1b": {ItemName.springs, ItemName.traffic_blocks, ItemName.dash_refills, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"1c": {ItemName.traffic_blocks, ItemName.dash_refills, ItemName.coins},
"2a": {ItemName.springs, ItemName.dream_blocks, ItemName.traffic_blocks, ItemName.strawberry_seeds, ItemName.dash_refills, ItemName.coins},
"2b": {ItemName.springs, ItemName.dream_blocks, ItemName.dash_refills, ItemName.coins, ItemName.blue_cassette_blocks},
"2c": {ItemName.springs, ItemName.dream_blocks, ItemName.dash_refills, ItemName.coins},
"3a": {ItemName.springs, ItemName.moving_platforms, ItemName.sinking_platforms, ItemName.dash_refills, ItemName.coins, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"3b": {ItemName.springs, ItemName.dash_refills, ItemName.sinking_platforms, ItemName.coins, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"3c": {ItemName.dash_refills, ItemName.sinking_platforms, ItemName.coins},
"4a": {ItemName.blue_clouds, ItemName.blue_boosters, ItemName.moving_platforms, ItemName.coins, ItemName.strawberry_seeds, ItemName.springs, ItemName.move_blocks, ItemName.pink_clouds, ItemName.white_block, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"4b": {ItemName.blue_boosters, ItemName.moving_platforms, ItemName.move_blocks, ItemName.springs, ItemName.coins, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.dash_refills, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"4c": {ItemName.blue_boosters, ItemName.move_blocks, ItemName.dash_refills, ItemName.pink_clouds},
"5a": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills, ItemName.coins, ItemName.springs, ItemName.torches, ItemName.seekers, ItemName.theo_crystal, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"5b": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills, ItemName.coins, ItemName.springs, ItemName.torches, ItemName.seekers, ItemName.theo_crystal, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"5c": {ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.dash_refills},
"6a": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers, ItemName.springs, ItemName.coins, ItemName.badeline_boosters, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"6b": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers, ItemName.coins, ItemName.springs, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"6c": {ItemName.feathers, ItemName.kevin_blocks, ItemName.dash_refills, ItemName.bumpers},
"7a": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.traffic_blocks, ItemName.coins, ItemName.dream_blocks, ItemName.sinking_platforms, ItemName.blue_boosters, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.move_blocks, ItemName.moving_platforms, ItemName.swap_blocks, ItemName.red_boosters, ItemName.dash_switches, ItemName.feathers, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"7b": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.traffic_blocks, ItemName.coins, ItemName.dream_blocks, ItemName.moving_platforms, ItemName.blue_boosters, ItemName.blue_clouds, ItemName.pink_clouds, ItemName.move_blocks, ItemName.swap_blocks, ItemName.red_boosters, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"7c": {ItemName.springs, ItemName.dash_refills, ItemName.badeline_boosters, ItemName.coins, ItemName.pink_clouds},
# Epilogue
"8a": set(),
# Core
"9a": {ItemName.springs, ItemName.dash_refills, ItemName.fire_ice_balls, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.coins, ItemName.badeline_boosters, ItemName.feathers, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"9b": {ItemName.springs, ItemName.dash_refills, ItemName.fire_ice_balls, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.coins, ItemName.badeline_boosters, ItemName.dream_blocks, ItemName.moving_platforms, ItemName.blue_clouds, ItemName.swap_blocks, ItemName.kevin_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks},
"9c": {ItemName.dash_refills, ItemName.bumpers, ItemName.core_toggles, ItemName.core_blocks, ItemName.traffic_blocks, ItemName.dream_blocks, ItemName.pink_clouds, ItemName.swap_blocks, ItemName.kevin_blocks},
# Farewell Pre/Post Empty Space
"10a": {ItemName.blue_clouds, ItemName.badeline_boosters, ItemName.dash_refills, ItemName.double_dash_refills, ItemName.swap_blocks, ItemName.springs, ItemName.pufferfish, ItemName.coins, ItemName.dream_blocks, ItemName.jellyfish, ItemName.red_boosters, ItemName.dash_switches, ItemName.move_blocks, ItemName.breaker_boxes, ItemName.traffic_blocks},
"10b": {ItemName.dream_blocks, ItemName.badeline_boosters, ItemName.bird, ItemName.dash_refills, ItemName.double_dash_refills, ItemName.kevin_blocks, ItemName.coins, ItemName.traffic_blocks, ItemName.move_blocks, ItemName.blue_boosters, ItemName.springs, ItemName.feathers, ItemName.swap_blocks, ItemName.red_boosters, ItemName.core_blocks, ItemName.fire_ice_balls, ItemName.kevin_blocks, ItemName.pink_cassette_blocks, ItemName.blue_cassette_blocks, ItemName.yellow_cassette_blocks, ItemName.green_cassette_blocks, ItemName.breaker_boxes, ItemName.pufferfish, ItemName.jellyfish},
"10c": {ItemName.badeline_boosters, ItemName.double_dash_refills, ItemName.springs, ItemName.pufferfish, ItemName.jellyfish},
}
level_cassette_items: dict[str, str] = {
"0a": ItemName.prologue_cassette,
"1a": ItemName.fc_a_cassette,
"1b": ItemName.fc_b_cassette,
"1c": ItemName.fc_c_cassette,
"2a": ItemName.os_a_cassette,
"2b": ItemName.os_b_cassette,
"2c": ItemName.os_c_cassette,
"3a": ItemName.cr_a_cassette,
"3b": ItemName.cr_b_cassette,
"3c": ItemName.cr_c_cassette,
"4a": ItemName.gr_a_cassette,
"4b": ItemName.gr_b_cassette,
"4c": ItemName.gr_c_cassette,
"5a": ItemName.mt_a_cassette,
"5b": ItemName.mt_b_cassette,
"5c": ItemName.mt_c_cassette,
"6a": ItemName.ref_a_cassette,
"6b": ItemName.ref_b_cassette,
"6c": ItemName.ref_c_cassette,
"7a": ItemName.sum_a_cassette,
"7b": ItemName.sum_b_cassette,
"7c": ItemName.sum_c_cassette,
"8a": ItemName.epilogue_cassette,
"9a": ItemName.core_a_cassette,
"9b": ItemName.core_b_cassette,
"9c": ItemName.core_c_cassette,
"10a":ItemName.farewell_cassette,
}
celeste_base_id: int = 0xCA10000
class CelesteItem(Item):
game = "Celeste"
class CelesteItemData(NamedTuple):
code: Optional[int] = None
type: ItemClassification = ItemClassification.filler
collectable_item_data_table: dict[str, CelesteItemData] = {
ItemName.strawberry: CelesteItemData(celeste_base_id + 0x0, ItemClassification.progression_skip_balancing),
ItemName.raspberry: CelesteItemData(celeste_base_id + 0x1, ItemClassification.filler),
}
goal_item_data_table: dict[str, CelesteItemData] = {
ItemName.house_keys: CelesteItemData(celeste_base_id + 0x10, ItemClassification.progression_skip_balancing),
}
trap_item_data_table: dict[str, CelesteItemData] = {
ItemName.bald_trap: CelesteItemData(celeste_base_id + 0x20, ItemClassification.trap),
ItemName.literature_trap: CelesteItemData(celeste_base_id + 0x21, ItemClassification.trap),
ItemName.stun_trap: CelesteItemData(celeste_base_id + 0x22, ItemClassification.trap),
ItemName.invisible_trap: CelesteItemData(celeste_base_id + 0x23, ItemClassification.trap),
ItemName.fast_trap: CelesteItemData(celeste_base_id + 0x24, ItemClassification.trap),
ItemName.slow_trap: CelesteItemData(celeste_base_id + 0x25, ItemClassification.trap),
ItemName.ice_trap: CelesteItemData(celeste_base_id + 0x26, ItemClassification.trap),
ItemName.reverse_trap: CelesteItemData(celeste_base_id + 0x28, ItemClassification.trap),
ItemName.screen_flip_trap: CelesteItemData(celeste_base_id + 0x29, ItemClassification.trap),
ItemName.laughter_trap: CelesteItemData(celeste_base_id + 0x2A, ItemClassification.trap),
ItemName.hiccup_trap: CelesteItemData(celeste_base_id + 0x2B, ItemClassification.trap),
ItemName.zoom_trap: CelesteItemData(celeste_base_id + 0x2C, ItemClassification.trap),
}
checkpoint_item_data_table: dict[str, CelesteItemData] = {}
key_item_data_table: dict[str, CelesteItemData] = {}
gem_item_data_table: dict[str, CelesteItemData] = {}
interactable_item_data_table: dict[str, CelesteItemData] = {
ItemName.springs: CelesteItemData(celeste_base_id + 0x2000 + 0x00, ItemClassification.progression),
ItemName.traffic_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x01, ItemClassification.progression),
ItemName.pink_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x02, ItemClassification.progression),
ItemName.blue_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x03, ItemClassification.progression),
ItemName.dream_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x04, ItemClassification.progression),
ItemName.coins: CelesteItemData(celeste_base_id + 0x2000 + 0x05, ItemClassification.progression),
ItemName.strawberry_seeds: CelesteItemData(celeste_base_id + 0x2000 + 0x1F, ItemClassification.progression),
ItemName.sinking_platforms: CelesteItemData(celeste_base_id + 0x2000 + 0x20, ItemClassification.progression),
ItemName.moving_platforms: CelesteItemData(celeste_base_id + 0x2000 + 0x06, ItemClassification.progression),
ItemName.blue_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x07, ItemClassification.progression),
ItemName.blue_clouds: CelesteItemData(celeste_base_id + 0x2000 + 0x08, ItemClassification.progression),
ItemName.move_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x09, ItemClassification.progression),
ItemName.white_block: CelesteItemData(celeste_base_id + 0x2000 + 0x21, ItemClassification.progression),
ItemName.swap_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x0A, ItemClassification.progression),
ItemName.red_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x0B, ItemClassification.progression),
ItemName.torches: CelesteItemData(celeste_base_id + 0x2000 + 0x22, ItemClassification.useful),
ItemName.theo_crystal: CelesteItemData(celeste_base_id + 0x2000 + 0x0C, ItemClassification.progression),
ItemName.feathers: CelesteItemData(celeste_base_id + 0x2000 + 0x0D, ItemClassification.progression),
ItemName.bumpers: CelesteItemData(celeste_base_id + 0x2000 + 0x0E, ItemClassification.progression),
ItemName.kevin_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x0F, ItemClassification.progression),
ItemName.pink_clouds: CelesteItemData(celeste_base_id + 0x2000 + 0x10, ItemClassification.progression),
ItemName.badeline_boosters: CelesteItemData(celeste_base_id + 0x2000 + 0x11, ItemClassification.progression),
ItemName.fire_ice_balls: CelesteItemData(celeste_base_id + 0x2000 + 0x12, ItemClassification.progression),
ItemName.core_toggles: CelesteItemData(celeste_base_id + 0x2000 + 0x13, ItemClassification.progression),
ItemName.core_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x14, ItemClassification.progression),
ItemName.pufferfish: CelesteItemData(celeste_base_id + 0x2000 + 0x15, ItemClassification.progression),
ItemName.jellyfish: CelesteItemData(celeste_base_id + 0x2000 + 0x16, ItemClassification.progression),
ItemName.breaker_boxes: CelesteItemData(celeste_base_id + 0x2000 + 0x17, ItemClassification.progression),
ItemName.dash_refills: CelesteItemData(celeste_base_id + 0x2000 + 0x18, ItemClassification.progression),
ItemName.double_dash_refills: CelesteItemData(celeste_base_id + 0x2000 + 0x19, ItemClassification.progression),
ItemName.yellow_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x1A, ItemClassification.progression),
ItemName.green_cassette_blocks: CelesteItemData(celeste_base_id + 0x2000 + 0x1B, ItemClassification.progression),
ItemName.bird: CelesteItemData(celeste_base_id + 0x2000 + 0x23, ItemClassification.progression),
ItemName.dash_switches: CelesteItemData(celeste_base_id + 0x2000 + 0x1C, ItemClassification.progression),
ItemName.seekers: CelesteItemData(celeste_base_id + 0x2000 + 0x1D, ItemClassification.progression),
}
cassette_item_data_table: dict[str, CelesteItemData] = {
ItemName.prologue_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x00, ItemClassification.filler),
ItemName.fc_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x01, ItemClassification.filler),
ItemName.fc_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x02, ItemClassification.filler),
ItemName.fc_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x03, ItemClassification.filler),
ItemName.os_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x04, ItemClassification.filler),
ItemName.os_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x05, ItemClassification.filler),
ItemName.os_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x06, ItemClassification.filler),
ItemName.cr_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x07, ItemClassification.filler),
ItemName.cr_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x08, ItemClassification.filler),
ItemName.cr_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x09, ItemClassification.filler),
ItemName.gr_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0A, ItemClassification.filler),
ItemName.gr_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0B, ItemClassification.filler),
ItemName.gr_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0C, ItemClassification.filler),
ItemName.mt_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0D, ItemClassification.filler),
ItemName.mt_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0E, ItemClassification.filler),
ItemName.mt_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x0F, ItemClassification.filler),
ItemName.ref_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x10, ItemClassification.filler),
ItemName.ref_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x11, ItemClassification.filler),
ItemName.ref_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x12, ItemClassification.filler),
ItemName.sum_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x13, ItemClassification.filler),
ItemName.sum_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x14, ItemClassification.filler),
ItemName.sum_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x15, ItemClassification.filler),
ItemName.epilogue_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x16, ItemClassification.filler),
ItemName.core_a_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x17, ItemClassification.filler),
ItemName.core_b_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x18, ItemClassification.filler),
ItemName.core_c_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x19, ItemClassification.filler),
ItemName.farewell_cassette: CelesteItemData(celeste_base_id + 0x1000 + 0x1A, ItemClassification.filler),
}
crystal_heart_item_data_table: dict[str, CelesteItemData] = {
ItemName.crystal_heart_1: CelesteItemData(celeste_base_id + 0x3000 + 0x00, ItemClassification.filler),
ItemName.crystal_heart_2: CelesteItemData(celeste_base_id + 0x3000 + 0x01, ItemClassification.filler),
ItemName.crystal_heart_3: CelesteItemData(celeste_base_id + 0x3000 + 0x02, ItemClassification.filler),
ItemName.crystal_heart_4: CelesteItemData(celeste_base_id + 0x3000 + 0x03, ItemClassification.filler),
ItemName.crystal_heart_5: CelesteItemData(celeste_base_id + 0x3000 + 0x04, ItemClassification.filler),
ItemName.crystal_heart_6: CelesteItemData(celeste_base_id + 0x3000 + 0x05, ItemClassification.filler),
ItemName.crystal_heart_7: CelesteItemData(celeste_base_id + 0x3000 + 0x06, ItemClassification.filler),
ItemName.crystal_heart_8: CelesteItemData(celeste_base_id + 0x3000 + 0x07, ItemClassification.filler),
ItemName.crystal_heart_9: CelesteItemData(celeste_base_id + 0x3000 + 0x08, ItemClassification.filler),
ItemName.crystal_heart_10: CelesteItemData(celeste_base_id + 0x3000 + 0x09, ItemClassification.filler),
ItemName.crystal_heart_11: CelesteItemData(celeste_base_id + 0x3000 + 0x0A, ItemClassification.filler),
ItemName.crystal_heart_12: CelesteItemData(celeste_base_id + 0x3000 + 0x0B, ItemClassification.filler),
ItemName.crystal_heart_13: CelesteItemData(celeste_base_id + 0x3000 + 0x0C, ItemClassification.filler),
ItemName.crystal_heart_14: CelesteItemData(celeste_base_id + 0x3000 + 0x0D, ItemClassification.filler),
ItemName.crystal_heart_15: CelesteItemData(celeste_base_id + 0x3000 + 0x0E, ItemClassification.filler),
ItemName.crystal_heart_16: CelesteItemData(celeste_base_id + 0x3000 + 0x0F, ItemClassification.filler),
}
def add_checkpoint_to_table(id: int, name: str):
checkpoint_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
def add_key_to_table(id: int, name: str):
key_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
def add_gem_to_table(id: int, name: str):
gem_item_data_table[name] = CelesteItemData(id, ItemClassification.progression)
def generate_item_data_table() -> dict[str, CelesteItemData]:
return {**collectable_item_data_table,
**goal_item_data_table,
**trap_item_data_table,
**checkpoint_item_data_table,
**key_item_data_table,
**gem_item_data_table,
**cassette_item_data_table,
**crystal_heart_item_data_table,
**interactable_item_data_table}
def generate_item_table() -> dict[str, int]:
return {name: data.code for name, data in generate_item_data_table().items() if data.code is not None}
def generate_item_groups() -> dict[str, list[str]]:
item_groups: dict[str, list[str]] = {
"Collectables": list(collectable_item_data_table.keys()),
"Traps": list(trap_item_data_table.keys()),
"Checkpoints": list(checkpoint_item_data_table.keys()),
"Keys": list(key_item_data_table.keys()),
"Gems": list(gem_item_data_table.keys()),
"Cassettes": list(cassette_item_data_table.keys()),
"Crystal Hearts": list(crystal_heart_item_data_table.keys()),
"Interactables": list(interactable_item_data_table.keys()),
# Commonly mistaken names
"Green Boosters": [ItemName.blue_boosters],
"Green Bubbles": [ItemName.blue_boosters],
"Blue Bubbles": [ItemName.blue_boosters],
"Red Bubbles": [ItemName.red_boosters],
"Touch Switches": [ItemName.coins],
}
return item_groups