Files
Grinch-AP/worlds/celeste_open_world/Levels.py
PoryGone c753fbff2d 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 23:31:09 +02:00

209 lines
5.6 KiB
Python

from __future__ import annotations
from enum import IntEnum
from BaseClasses import CollectionState
goal_area_option_to_name: dict[int, str] = {
0: "7a",
1: "7b",
2: "7c",
3: "9a",
4: "9b",
5: "9c",
6: "10a",
7: "10b",
8: "10c",
}
goal_area_option_to_display_name: dict[int, str] = {
0: "The Summit A",
1: "The Summit B",
2: "The Summit C",
3: "Core A",
4: "Core B",
5: "Core C",
6: "Farewell",
7: "Farewell",
8: "Farewell",
}
goal_area_to_location_name: dict[str, str] = {
"7a": "The Summit A - Level Clear",
"7b": "The Summit B - Level Clear",
"7c": "The Summit C - Level Clear",
"9a": "Core A - Level Clear",
"9b": "Core B - Level Clear",
"9c": "Core C - Level Clear",
"10a": "Farewell - Crystal Heart?",
"10b": "Farewell - Level Clear",
"10c": "Farewell - Golden Strawberry",
}
class LocationType(IntEnum):
strawberry = 0
golden_strawberry = 1
cassette = 2
crystal_heart = 3
checkpoint = 4
level_clear = 5
key = 6
binoculars = 7
room_enter = 8
clutter = 9
gem = 10
car = 11
class DoorDirection(IntEnum):
up = 0
right = 1
down = 2
left = 3
special = 4
class Door:
name: str
room_name: str
room: Room
dir: DoorDirection
blocked: bool
closes_behind: bool
region: PreRegion
def __init__(self, name: str, room_name: str, dir: DoorDirection, blocked: bool, closes_behind: bool):
self.name = name
self.room_name = room_name
self.dir = dir
self.blocked = blocked
self.closes_behind = closes_behind
# Find PreRegion later using our name once we know it exists
class PreRegion:
name: str
room_name: str
room: Room
connections: list[RegionConnection]
locations: list[LevelLocation]
def __init__(self, name: str, room_name: str, connections: list[RegionConnection], locations: list[LevelLocation]):
self.name = name
self.room_name = room_name
self.connections = connections.copy()
self.locations = locations.copy()
for loc in self.locations:
loc.region = self
class RegionConnection:
source_name: str
source: PreRegion
destination_name: str
destination: PreRegion
possible_access: list[list[str]]
def __init__(self, source_name: str, destination_name: str, possible_access: list[list[str]] = []):
self.source_name = source_name
self.destination_name = destination_name
self.possible_access = possible_access.copy()
class LevelLocation:
name: str
display_name: str
region_name: str
region: PreRegion
loc_type: LocationType
possible_access: list[list[str]]
def __init__(self, name: str, display_name: str, region_name: str, loc_type: LocationType, possible_access: list[list[str]] = []):
self.name = name
self.display_name = display_name
self.region_name = region_name
self.loc_type = loc_type
self.possible_access = possible_access.copy()
class Room:
level_name: str
name: str
display_name: str
regions: list[PreRegion]
doors: list[Door]
checkpoint: str
checkpoint_region: str
def __init__(self, level_name: str, name: str, display_name: str, regions: list[PreRegion], doors: list[Door], checkpoint: str = None, checkpoint_region: str = None):
self.level_name = level_name
self.name = name
self.display_name = display_name
self.regions = regions.copy()
self.doors = doors.copy()
self.checkpoint = checkpoint
self.checkpoint_region = checkpoint_region
from .data.CelesteLevelData import all_regions
for reg in self.regions:
reg.room = self
for reg_con in reg.connections:
reg_con.source = reg
reg_con.destination = all_regions[reg_con.destination_name]
for door in self.doors:
door.room = self
class RoomConnection:
level_name: str
source: Door
dest: Door
two_way: bool
def __init__(self, level_name: str, source: Door, dest: Door):
self.level_name = level_name
self.source = source
self.dest = dest
self.two_way = not self.dest.closes_behind
if (self.source.dir == DoorDirection.left and self.dest.dir != DoorDirection.right or
self.source.dir == DoorDirection.right and self.dest.dir != DoorDirection.left or
self.source.dir == DoorDirection.up and self.dest.dir != DoorDirection.down or
self.source.dir == DoorDirection.down and self.dest.dir != DoorDirection.up):
raise Exception(f"Door {source.name} ({self.source.dir}) and Door {dest.name} ({self.dest.dir}) have mismatched directions.")
class Level:
name: str
display_name: str
rooms: list[Room]
room_connections: list[RoomConnection]
def __init__(self, name: str, display_name: str, rooms: list[Room], room_connections: list[RoomConnection]):
self.name = name
self.display_name = display_name
self.rooms = rooms.copy()
self.room_connections = room_connections.copy()
def load_logic_data() -> dict[str, Level]:
from .data.CelesteLevelData import all_levels
#for _, level in all_levels.items():
# print(level.display_name)
#
# for room in level.rooms:
# print(" " + room.display_name)
#
# for region in room.regions:
# print(" " + region.name)
#
# for location in region.locations:
# print(" " + location.display_name)
return all_levels