From 43519215a6aa9ee7cc6ca01f66301c943ce998c2 Mon Sep 17 00:00:00 2001 From: Aly Date: Thu, 2 May 2024 18:12:07 -0700 Subject: [PATCH] Add neon rank --- worlds/neon_white/Items.py | 51 +++++++++++++++++++--------------- worlds/neon_white/Locations.py | 5 ++++ worlds/neon_white/Rules.py | 10 ++++++- worlds/neon_white/__init__.py | 43 +++++++++++++++++++++------- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/worlds/neon_white/Items.py b/worlds/neon_white/Items.py index be32be215104..c76313265162 100644 --- a/worlds/neon_white/Items.py +++ b/worlds/neon_white/Items.py @@ -1,26 +1,24 @@ from BaseClasses import Item, ItemClassification +from worlds.neon_white import Locations base_item_namespace = 2874297668000000 -chapter_unlock_offset = 1000 +level_unlock_offset = 1000 cards_offset = 2000 card_abilities_offset = 3000 insight_offset = 4000 +neon_rank_offset = 5000 -chapters = [ - 'M01 Rebirth', - 'M02 Killer Inside', - 'M03 Only Shallow', - 'M04 The Old City', - 'M05 The Burn That Cures', - 'M06 Covenant', - 'M07 Reckoning', - 'M08 Benediction', - 'M09 Apocrypha', - 'M10 The Third Temple', - 'M11 Thousand Pound Butterfly', - 'M12 Hand of God' -] +levels = [] + +chapter_num = 1 +for chapter in Locations.job_names: + level_num = 1 + for level in chapter: + levels.append(f'M{chapter_num:02d}L{level_num:02d} {level}') + level_num += 1 + + chapter_num += 1 cards = [ 'Card: Katana', @@ -52,13 +50,17 @@ ('Insight: Green', 4) ] +neon_rank = [ + ('Neon Rank', 100) +] + all_items = {} item_name_to_id = {} item_individual_offset = 0 -for chapter in chapters: - all_items[chapter] = 1 - item_name_to_id[chapter] = base_item_namespace + chapter_unlock_offset + item_individual_offset +for level in levels: + all_items[level] = 1 + item_name_to_id[level] = base_item_namespace + level_unlock_offset + item_individual_offset item_individual_offset += 1 item_individual_offset = 0 @@ -79,21 +81,26 @@ item_name_to_id[name] = base_item_namespace + insight_offset + item_individual_offset item_individual_offset += 1 +all_items['Neon Rank'] = 100 +item_name_to_id['Neon Rank'] = base_item_namespace + neon_rank_offset + item_name_groups = { - 'Chapters': set(chapters), + 'Levels': set(levels), 'Cards': set(cards), 'CardAbilities': set(card_abilities), - 'Insights': set([name for (name, _) in insights]) + 'Insights': set([name for (name, _) in insights]), + 'NeonRank': {'Neon Rank'} } -progression_items = set(chapters) | set(cards) | set(card_abilities) +progression_items = {'Neon Rank'} | set(cards) | set(card_abilities) | {'M12L02 Absolution'} +useful_items = set(levels) | {'Insight: Yellow', 'Insight: Red', 'Insight: Violet'} def item_classification(item: str) -> ItemClassification: if item in progression_items: return ItemClassification.progression - elif item in cards or item in card_abilities: + elif item in useful_items: return ItemClassification.useful else: return ItemClassification.filler diff --git a/worlds/neon_white/Locations.py b/worlds/neon_white/Locations.py index b5fbba461ae1..1c9fa7fb34d7 100644 --- a/worlds/neon_white/Locations.py +++ b/worlds/neon_white/Locations.py @@ -549,6 +549,7 @@ def map_medals(self, location_name_to_id = {} location_name_to_path = {} location_name_lfunct: NeonWhiteLocations[str] = NeonWhiteLocations.deep_empty() +level_name_lfunct: NeonWhiteLocationsJobs[str] = NeonWhiteLocationsJobs.deep_empty() medal_name_offsets = [ ('Bronze', job_bronze_offset), @@ -563,11 +564,13 @@ def map_medals(self, for chapter_field in dataclasses.fields(location_name_lfunct.jobs): chapter_path = ["jobs", chapter_field.name] chapter_lfunct = getattr(location_name_lfunct.jobs, chapter_field.name) + chapter_lname_lfunct = getattr(level_name_lfunct, chapter_field.name) job_name_list = job_names[chapter_idx] job_idx = 0 for job_field in dataclasses.fields(chapter_lfunct): job_path = chapter_path + [job_field.name] job_lfunct = getattr(chapter_lfunct, job_field.name) + job_lname_lfunct = getattr(chapter_lname_lfunct, job_field.name) job_full_name = f'M{chapter_idx + 1:02d}L{job_idx + 1:02d} {job_name_list[job_idx]}' for (medal_name, offset) in medal_name_offsets: @@ -577,6 +580,7 @@ def map_medals(self, location_name_to_id[medal_full_name] = base_location_namespace + offset + total_job_id location_name_to_path[medal_full_name] = medal_path setattr(job_lfunct, medal_field_name, medal_full_name) + setattr(job_lname_lfunct, medal_field_name, job_full_name) all_locations.append(medal_full_name) if hasattr(job_lfunct, 'gift'): @@ -585,6 +589,7 @@ def map_medals(self, location_name_to_id[gift_full_name] = base_location_namespace + job_gift_offset + total_job_id location_name_to_path[gift_full_name] = gift_path setattr(job_lfunct, 'gift', gift_full_name) + setattr(job_lname_lfunct, 'gift', job_full_name) all_locations.append(gift_full_name) total_job_id += 1 diff --git a/worlds/neon_white/Rules.py b/worlds/neon_white/Rules.py index 82dcbfc36d94..3accb756331b 100644 --- a/worlds/neon_white/Rules.py +++ b/worlds/neon_white/Rules.py @@ -572,4 +572,12 @@ def cascade_medals(jm: NeonWhiteLocationsJobMedals[Variable[bool]]) -> NeonWhite return plus -reachability_full = reachability.map_medals(cascade_medals) +def level_required(path: list[str], variable: Variable[bool]) -> Variable[bool]: + if path[0] == "jobs": + from worlds.neon_white.Locations import level_name_lfunct + return variable & HasItemVariable(level_name_lfunct.lget(path[1:])) + else: + return variable + + +reachability_full = reachability.map_medals(cascade_medals).lmap([], level_required) diff --git a/worlds/neon_white/__init__.py b/worlds/neon_white/__init__.py index 87dd9125da90..61e8794cf409 100644 --- a/worlds/neon_white/__init__.py +++ b/worlds/neon_white/__init__.py @@ -3,7 +3,7 @@ from .Items import (item_name_to_id as i_item_name_to_id, all_items as i_all_items, item_name_groups as i_item_name_groups, item_classification as i_item_classification, NeonWhiteItem, - chapters as i_chapters) + levels as i_levels) from .Locations import location_name_to_id as l_location_name_to_id, all_locations as l_all_locations, \ job_names as l_job_names, NeonWhiteLocation, giftless_jobs as l_giftless_jobs, \ @@ -48,6 +48,22 @@ def generate_early(self) -> None: self.reward_gifts = self.options.reward_gifts.value self.reward_sidequests = self.options.reward_sidequests.value + self.location_count = 0 + for chapter in range(0, 12): + chapter_l = l_job_names[chapter] + job_idx = 1 + for job in chapter_l: + self.location_count += len(self.reward_medals) + + if job not in l_giftless_jobs and self.reward_gifts: + self.location_count += 1 + + job_idx += 1 + + if self.reward_sidequests: + for (companion, companion_l) in l_companion_sidequests: + self.location_count += len(companion_l) + def create_item(self, name: str) -> NeonWhiteItem: return NeonWhiteItem(name, i_item_classification(name), self.item_name_to_id[name], self.player) @@ -57,13 +73,12 @@ def create_event(self, name: str) -> NeonWhiteItem: def create_items(self) -> None: # TODO: exclusions total_item_count = 0 - location_count = len(l_all_locations) for item_name in i_all_items: count = i_all_items[item_name] total_item_count += count self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)] - junk = location_count - total_item_count + junk = self.location_count - total_item_count # TODO: actual junk items self.multiworld.itempool += [self.create_item('Insight: Green') for _ in range(junk)] @@ -92,12 +107,10 @@ def create_regions(self) -> None: chapter_region.add_locations(chapter_dict, NeonWhiteLocation) if chapter == 11: - absolution_loc = NeonWhiteLocation(self.player, 'M12L02 Absolution: Clear', None, chapter_region) + absolution_loc = NeonWhiteLocation(self.player, 'M12L02 Absolution: Clear', None, menu_region) chapter_region.locations.append(absolution_loc) - def has_item_capture(item: str): - return lambda state: state.has(item, self.player) - menu_region.connect(chapter_region, rule=has_item_capture(i_chapters[chapter])) + menu_region.connect(chapter_region) if self.reward_sidequests: for (companion, companion_l) in l_companion_sidequests: @@ -136,7 +149,17 @@ def lset_rule(path: list[str], rule) -> None: reachability_full.lmap([], lset_rule) + def clear_rule(state) -> bool: + has_level = state.has('M12L02 Absolution', self.player) + has_cards = state.has( + 'Card: Book of Life', self.player + ) and state.has( + 'Ability: Book of Life', self.player + ) and state.has( + 'Card: Dominion', self.player + ) + has_rank = state.has('Neon Rank', self.player, 70) + return has_level and has_cards and has_rank + set_rule(self.multiworld.get_location('M12L02 Absolution: Clear', self.player), - lambda state: state.has('Card: Book of Life', self.player) and - state.has('Ability: Book of Life', self.player) and - state.has('Card: Dominion', self.player)) + clear_rule)