Files
Grinch-AP/worlds/celeste64/__init__.py
PoryGone bf8c840293 Celeste 64: v1.3 Content Update (#4581)
### Features:

- New optional Location Checks
	- Checkpointsanity
- Hair Color
	- Allows for setting of Maddy's hair color in each of No Dash, One Dash, Two Dash, and Feather states
- Other Player Ghosts
	- A game config option allows you to see ghosts of other Celeste 64 players in the multiworld

### Quality of Life:

- Checkpoint Warping
	- Received Checkpoint items allow for warping to their respective checkpoint
		- These items are on their respective checkpoint location if Checkpointsanity is disabled
	- Logic accounts for being able to warp to otherwise inaccessible areas
	- Checkpoints are a possible option for a starting item on Standard Logic + Move Shuffle + Checkpointsanity
- New Options toggle to enable/disable background input

### Bug Fixes:

- Traffic Blocks now correctly appear disabled within Cassettes
2025-03-17 02:46:34 +01:00

211 lines
9.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from copy import deepcopy
from typing import Dict, List, Tuple
from BaseClasses import ItemClassification, Location, Region, Tutorial
from worlds.AutoWorld import WebWorld, World
from .Items import Celeste64Item, unlockable_item_data_table, move_item_data_table, item_data_table,\
checkpoint_item_data_table, item_table
from .Locations import Celeste64Location, strawberry_location_data_table, friend_location_data_table,\
sign_location_data_table, car_location_data_table, checkpoint_location_data_table,\
location_table
from .Names import ItemName, LocationName
from .Options import Celeste64Options, celeste_64_option_groups, resolve_options
class Celeste64WebWorld(WebWorld):
theme = "ice"
setup_en = Tutorial(
tutorial_name="Start Guide",
description="A guide to playing Celeste 64 in Archipelago.",
language="English",
file_name="guide_en.md",
link="guide/en",
authors=["PoryGone"]
)
tutorials = [setup_en]
option_groups = celeste_64_option_groups
class Celeste64World(World):
"""Relive the magic of Celeste Mountain alongside Madeline in this small, heartfelt 3D platformer.
Created in a week(ish) by the Celeste team to celebrate the games sixth anniversary 🍓✨"""
# Class Data
game = "Celeste 64"
web = Celeste64WebWorld()
options_dataclass = Celeste64Options
options: Celeste64Options
location_name_to_id = location_table
item_name_to_id = item_table
# Instance Data
strawberries_required: int
active_logic_mapping: Dict[str, List[List[str]]]
active_region_logic_mapping: Dict[Tuple[str], List[List[str]]]
madeline_one_dash_hair_color: int
madeline_two_dash_hair_color: int
madeline_no_dash_hair_color: int
madeline_feather_hair_color: int
def generate_early(self) -> None:
resolve_options(self)
def create_item(self, name: str) -> Celeste64Item:
# Only make required amount of strawberries be Progression
if getattr(self, "strawberries_required", None) and name == ItemName.strawberry:
classification: ItemClassification = ItemClassification.filler
self.prog_strawberries = getattr(self, "prog_strawberries", 0)
if self.prog_strawberries < self.strawberries_required:
classification = ItemClassification.progression_skip_balancing
self.prog_strawberries += 1
return Celeste64Item(name, classification, item_data_table[name].code, self.player)
else:
return Celeste64Item(name, item_data_table[name].type, item_data_table[name].code, self.player)
def create_items(self) -> None:
item_pool: List[Celeste64Item] = []
location_count: int = 30
if self.options.friendsanity:
location_count += 9
if self.options.signsanity:
location_count += 5
if self.options.carsanity:
location_count += 2
item_pool += [self.create_item(name)
for name in unlockable_item_data_table.keys()
if name not in self.options.start_inventory]
chosen_start_item: str = ""
if self.options.move_shuffle:
if self.options.logic_difficulty == "standard":
possible_unwalls: List[str] = [name for name in move_item_data_table.keys()
if name != ItemName.skid_jump]
if self.options.checkpointsanity:
possible_unwalls.extend([name for name in checkpoint_item_data_table.keys()
if name != ItemName.checkpoint_1 and name != ItemName.checkpoint_10])
# If the start_inventory already includes a move or checkpoint, don't worry about giving it one
if not [item for item in possible_unwalls if item in self.multiworld.precollected_items[self.player]]:
chosen_start_item = self.random.choice(possible_unwalls)
if self.options.carsanity:
intro_car_loc: Location = self.multiworld.get_location(LocationName.car_1, self.player)
intro_car_loc.place_locked_item(self.create_item(chosen_start_item))
location_count -= 1
else:
self.multiworld.push_precollected(self.create_item(chosen_start_item))
item_pool += [self.create_item(name)
for name in move_item_data_table.keys()
if name not in self.multiworld.precollected_items[self.player]
and name != chosen_start_item]
else:
for start_move in move_item_data_table.keys():
self.multiworld.push_precollected(self.create_item(start_move))
if self.options.checkpointsanity:
location_count += 9
goal_checkpoint_loc: Location = self.multiworld.get_location(LocationName.checkpoint_10, self.player)
goal_checkpoint_loc.place_locked_item(self.create_item(ItemName.checkpoint_10))
item_pool += [self.create_item(name)
for name in checkpoint_item_data_table.keys()
if name not in self.multiworld.precollected_items[self.player]
and name != ItemName.checkpoint_10
and name != chosen_start_item]
else:
for item_name in checkpoint_item_data_table.keys():
checkpoint_loc: Location = self.multiworld.get_location(item_name, self.player)
checkpoint_loc.place_locked_item(self.create_item(item_name))
real_total_strawberries: int = min(self.options.total_strawberries.value, location_count - len(item_pool))
self.strawberries_required = int(real_total_strawberries * (self.options.strawberries_required_percentage / 100))
item_pool += [self.create_item(ItemName.strawberry) for _ in range(real_total_strawberries)]
filler_item_count: int = location_count - len(item_pool)
item_pool += [self.create_item(ItemName.raspberry) for _ in range(filler_item_count)]
self.multiworld.itempool += item_pool
def create_regions(self) -> None:
from .Regions import region_data_table
# Create regions.
for region_name in region_data_table.keys():
region = Region(region_name, self.player, self.multiworld)
self.multiworld.regions.append(region)
# Create locations.
for region_name, region_data in region_data_table.items():
region = self.multiworld.get_region(region_name, self.player)
region.add_locations({
location_name: location_data.address for location_name, location_data in strawberry_location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
if self.options.friendsanity:
region.add_locations({
location_name: location_data.address for location_name, location_data in friend_location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
if self.options.signsanity:
region.add_locations({
location_name: location_data.address for location_name, location_data in sign_location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
if self.options.carsanity:
region.add_locations({
location_name: location_data.address for location_name, location_data in car_location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
region.add_locations({
location_name: location_data.address for location_name, location_data in checkpoint_location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
from .Rules import connect_region
connect_region(self, region, region_data_table[region_name].connecting_regions)
# Have to do this here because of other games using State in a way that's bad
from .Rules import set_rules
set_rules(self)
def get_filler_item_name(self) -> str:
return ItemName.raspberry
def fill_slot_data(self):
return {
"death_link": self.options.death_link.value,
"death_link_amnesty": self.options.death_link_amnesty.value,
"strawberries_required": self.strawberries_required,
"move_shuffle": self.options.move_shuffle.value,
"friendsanity": self.options.friendsanity.value,
"signsanity": self.options.signsanity.value,
"carsanity": self.options.carsanity.value,
"checkpointsanity": self.options.checkpointsanity.value,
"madeline_one_dash_hair_color": self.madeline_one_dash_hair_color,
"madeline_two_dash_hair_color": self.madeline_two_dash_hair_color,
"madeline_no_dash_hair_color": self.madeline_no_dash_hair_color,
"madeline_feather_hair_color": self.madeline_feather_hair_color,
"badeline_chaser_source": self.options.badeline_chaser_source.value,
"badeline_chaser_frequency": self.options.badeline_chaser_frequency.value,
"badeline_chaser_speed": self.options.badeline_chaser_speed.value,
}