mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Noita: Modernization Refactor (#4980)
This commit is contained in:
@@ -38,7 +38,7 @@ class NoitaWorld(World):
|
|||||||
web = NoitaWeb()
|
web = NoitaWeb()
|
||||||
|
|
||||||
def generate_early(self) -> None:
|
def generate_early(self) -> None:
|
||||||
if not self.multiworld.get_player_name(self.player).isascii():
|
if not self.player_name.isascii():
|
||||||
raise Exception("Noita yaml's slot name has invalid character(s).")
|
raise Exception("Noita yaml's slot name has invalid character(s).")
|
||||||
|
|
||||||
# Returned items will be sent over to the client
|
# Returned items will be sent over to the client
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from typing import Dict, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from BaseClasses import Item, ItemClassification, Location, Region
|
from BaseClasses import Item, ItemClassification, Location, Region
|
||||||
from . import items, locations
|
from . import items, locations
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ if TYPE_CHECKING:
|
|||||||
from . import NoitaWorld
|
from . import NoitaWorld
|
||||||
|
|
||||||
|
|
||||||
def create_event(player: int, name: str) -> Item:
|
def create_event_item(player: int, name: str) -> Item:
|
||||||
return items.NoitaItem(name, ItemClassification.progression, None, player)
|
return items.NoitaItem(name, ItemClassification.progression, None, player)
|
||||||
|
|
||||||
|
|
||||||
@@ -16,13 +16,13 @@ def create_location(player: int, name: str, region: Region) -> Location:
|
|||||||
|
|
||||||
def create_locked_location_event(player: int, region: Region, item: str) -> Location:
|
def create_locked_location_event(player: int, region: Region, item: str) -> Location:
|
||||||
new_location = create_location(player, item, region)
|
new_location = create_location(player, item, region)
|
||||||
new_location.place_locked_item(create_event(player, item))
|
new_location.place_locked_item(create_event_item(player, item))
|
||||||
|
|
||||||
region.locations.append(new_location)
|
region.locations.append(new_location)
|
||||||
return new_location
|
return new_location
|
||||||
|
|
||||||
|
|
||||||
def create_all_events(world: "NoitaWorld", created_regions: Dict[str, Region]) -> None:
|
def create_all_events(world: "NoitaWorld", created_regions: dict[str, Region]) -> None:
|
||||||
for region_name, event in event_locks.items():
|
for region_name, event in event_locks.items():
|
||||||
region = created_regions[region_name]
|
region = created_regions[region_name]
|
||||||
create_locked_location_event(world.player, region, event)
|
create_locked_location_event(world.player, region, event)
|
||||||
@@ -31,7 +31,7 @@ def create_all_events(world: "NoitaWorld", created_regions: Dict[str, Region]) -
|
|||||||
|
|
||||||
|
|
||||||
# Maps region names to event names
|
# Maps region names to event names
|
||||||
event_locks: Dict[str, str] = {
|
event_locks: dict[str, str] = {
|
||||||
"The Work": "Victory",
|
"The Work": "Victory",
|
||||||
"Mines": "Portal to Holy Mountain 1",
|
"Mines": "Portal to Holy Mountain 1",
|
||||||
"Coal Pits": "Portal to Holy Mountain 2",
|
"Coal Pits": "Portal to Holy Mountain 2",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import itertools
|
import itertools
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from typing import Dict, List, NamedTuple, Set, TYPE_CHECKING
|
from typing import NamedTuple, TYPE_CHECKING
|
||||||
|
|
||||||
from BaseClasses import Item, ItemClassification
|
from BaseClasses import Item, ItemClassification
|
||||||
from .options import BossesAsChecks, VictoryCondition, ExtraOrbs
|
from .options import BossesAsChecks, VictoryCondition, ExtraOrbs
|
||||||
@@ -27,12 +27,12 @@ def create_item(player: int, name: str) -> Item:
|
|||||||
return NoitaItem(name, item_data.classification, item_data.code, player)
|
return NoitaItem(name, item_data.classification, item_data.code, player)
|
||||||
|
|
||||||
|
|
||||||
def create_fixed_item_pool() -> List[str]:
|
def create_fixed_item_pool() -> list[str]:
|
||||||
required_items: Dict[str, int] = {name: data.required_num for name, data in item_table.items()}
|
required_items: dict[str, int] = {name: data.required_num for name, data in item_table.items()}
|
||||||
return list(Counter(required_items).elements())
|
return list(Counter(required_items).elements())
|
||||||
|
|
||||||
|
|
||||||
def create_orb_items(victory_condition: VictoryCondition, extra_orbs: ExtraOrbs) -> List[str]:
|
def create_orb_items(victory_condition: VictoryCondition, extra_orbs: ExtraOrbs) -> list[str]:
|
||||||
orb_count = extra_orbs.value
|
orb_count = extra_orbs.value
|
||||||
if victory_condition == VictoryCondition.option_pure_ending:
|
if victory_condition == VictoryCondition.option_pure_ending:
|
||||||
orb_count = orb_count + 11
|
orb_count = orb_count + 11
|
||||||
@@ -41,15 +41,15 @@ def create_orb_items(victory_condition: VictoryCondition, extra_orbs: ExtraOrbs)
|
|||||||
return ["Orb" for _ in range(orb_count)]
|
return ["Orb" for _ in range(orb_count)]
|
||||||
|
|
||||||
|
|
||||||
def create_spatial_awareness_item(bosses_as_checks: BossesAsChecks) -> List[str]:
|
def create_spatial_awareness_item(bosses_as_checks: BossesAsChecks) -> list[str]:
|
||||||
return ["Spatial Awareness Perk"] if bosses_as_checks.value >= BossesAsChecks.option_all_bosses else []
|
return ["Spatial Awareness Perk"] if bosses_as_checks.value >= BossesAsChecks.option_all_bosses else []
|
||||||
|
|
||||||
|
|
||||||
def create_kantele(victory_condition: VictoryCondition) -> List[str]:
|
def create_kantele(victory_condition: VictoryCondition) -> list[str]:
|
||||||
return ["Kantele"] if victory_condition.value >= VictoryCondition.option_pure_ending else []
|
return ["Kantele"] if victory_condition.value >= VictoryCondition.option_pure_ending else []
|
||||||
|
|
||||||
|
|
||||||
def create_random_items(world: NoitaWorld, weights: Dict[str, int], count: int) -> List[str]:
|
def create_random_items(world: NoitaWorld, weights: dict[str, int], count: int) -> list[str]:
|
||||||
filler_pool = weights.copy()
|
filler_pool = weights.copy()
|
||||||
if not world.options.bad_effects:
|
if not world.options.bad_effects:
|
||||||
filler_pool["Trap"] = 0
|
filler_pool["Trap"] = 0
|
||||||
@@ -87,7 +87,7 @@ def create_all_items(world: NoitaWorld) -> None:
|
|||||||
|
|
||||||
|
|
||||||
# 110000 - 110032
|
# 110000 - 110032
|
||||||
item_table: Dict[str, ItemData] = {
|
item_table: dict[str, ItemData] = {
|
||||||
"Trap": ItemData(110000, "Traps", ItemClassification.trap),
|
"Trap": ItemData(110000, "Traps", ItemClassification.trap),
|
||||||
"Extra Max HP": ItemData(110001, "Pickups", ItemClassification.useful),
|
"Extra Max HP": ItemData(110001, "Pickups", ItemClassification.useful),
|
||||||
"Spell Refresher": ItemData(110002, "Pickups", ItemClassification.filler),
|
"Spell Refresher": ItemData(110002, "Pickups", ItemClassification.filler),
|
||||||
@@ -122,7 +122,7 @@ item_table: Dict[str, ItemData] = {
|
|||||||
"Broken Wand": ItemData(110031, "Items", ItemClassification.filler),
|
"Broken Wand": ItemData(110031, "Items", ItemClassification.filler),
|
||||||
}
|
}
|
||||||
|
|
||||||
shop_only_filler_weights: Dict[str, int] = {
|
shop_only_filler_weights: dict[str, int] = {
|
||||||
"Trap": 15,
|
"Trap": 15,
|
||||||
"Extra Max HP": 25,
|
"Extra Max HP": 25,
|
||||||
"Spell Refresher": 20,
|
"Spell Refresher": 20,
|
||||||
@@ -135,7 +135,7 @@ shop_only_filler_weights: Dict[str, int] = {
|
|||||||
"Extra Life Perk": 10,
|
"Extra Life Perk": 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
filler_weights: Dict[str, int] = {
|
filler_weights: dict[str, int] = {
|
||||||
**shop_only_filler_weights,
|
**shop_only_filler_weights,
|
||||||
"Gold (200)": 15,
|
"Gold (200)": 15,
|
||||||
"Gold (1000)": 6,
|
"Gold (1000)": 6,
|
||||||
@@ -152,22 +152,10 @@ filler_weights: Dict[str, int] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# These helper functions make the comprehensions below more readable
|
filler_items: list[str] = list(filter(lambda item: item_table[item].classification == ItemClassification.filler,
|
||||||
def get_item_group(item_name: str) -> str:
|
item_table.keys()))
|
||||||
return item_table[item_name].group
|
item_name_to_id: dict[str, int] = {name: data.code for name, data in item_table.items()}
|
||||||
|
|
||||||
|
item_name_groups: dict[str, set[str]] = {
|
||||||
def item_is_filler(item_name: str) -> bool:
|
group: set(item_names) for group, item_names in itertools.groupby(item_table, lambda item: item_table[item].group)
|
||||||
return item_table[item_name].classification == ItemClassification.filler
|
|
||||||
|
|
||||||
|
|
||||||
def item_is_perk(item_name: str) -> bool:
|
|
||||||
return item_table[item_name].group == "Perks"
|
|
||||||
|
|
||||||
|
|
||||||
filler_items: List[str] = list(filter(item_is_filler, item_table.keys()))
|
|
||||||
item_name_to_id: Dict[str, int] = {name: data.code for name, data in item_table.items()}
|
|
||||||
|
|
||||||
item_name_groups: Dict[str, Set[str]] = {
|
|
||||||
group: set(item_names) for group, item_names in itertools.groupby(item_table, get_item_group)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Locations are specific points that you would obtain an item at.
|
# Locations are specific points that you would obtain an item at.
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from typing import Dict, NamedTuple, Optional, Set
|
from typing import NamedTuple
|
||||||
|
|
||||||
from BaseClasses import Location
|
from BaseClasses import Location
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ class LocationFlag(IntEnum):
|
|||||||
# Only the first Hidden Chest and Pedestal are mapped here, the others are created in Regions.
|
# Only the first Hidden Chest and Pedestal are mapped here, the others are created in Regions.
|
||||||
# ltype key: "Chest" = Hidden Chests, "Pedestal" = Pedestals, "Boss" = Boss, "Orb" = Orb.
|
# ltype key: "Chest" = Hidden Chests, "Pedestal" = Pedestals, "Boss" = Boss, "Orb" = Orb.
|
||||||
# 110000-110671
|
# 110000-110671
|
||||||
location_region_mapping: Dict[str, Dict[str, LocationData]] = {
|
location_region_mapping: dict[str, dict[str, LocationData]] = {
|
||||||
"Coal Pits Holy Mountain": {
|
"Coal Pits Holy Mountain": {
|
||||||
"Coal Pits Holy Mountain Shop Item 1": LocationData(110000),
|
"Coal Pits Holy Mountain Shop Item 1": LocationData(110000),
|
||||||
"Coal Pits Holy Mountain Shop Item 2": LocationData(110001),
|
"Coal Pits Holy Mountain Shop Item 2": LocationData(110001),
|
||||||
@@ -207,15 +207,15 @@ location_region_mapping: Dict[str, Dict[str, LocationData]] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_location_range(location_name: str, base_id: int, amt: int) -> Dict[str, int]:
|
def make_location_range(location_name: str, base_id: int, amt: int) -> dict[str, int]:
|
||||||
if amt == 1:
|
if amt == 1:
|
||||||
return {location_name: base_id}
|
return {location_name: base_id}
|
||||||
return {f"{location_name} {i+1}": base_id + i for i in range(amt)}
|
return {f"{location_name} {i+1}": base_id + i for i in range(amt)}
|
||||||
|
|
||||||
|
|
||||||
location_name_groups: Dict[str, Set[str]] = {"Shop": set(), "Orb": set(), "Boss": set(), "Chest": set(),
|
location_name_groups: dict[str, set[str]] = {"Shop": set(), "Orb": set(), "Boss": set(), "Chest": set(),
|
||||||
"Pedestal": set()}
|
"Pedestal": set()}
|
||||||
location_name_to_id: Dict[str, int] = {}
|
location_name_to_id: dict[str, int] = {}
|
||||||
|
|
||||||
|
|
||||||
for region_name, location_group in location_region_mapping.items():
|
for region_name, location_group in location_region_mapping.items():
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Regions are areas in your game that you travel to.
|
# Regions are areas in your game that you travel to.
|
||||||
from typing import Dict, List, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from BaseClasses import Entrance, Region
|
from BaseClasses import Entrance, Region
|
||||||
from . import locations
|
from . import locations
|
||||||
@@ -36,28 +36,21 @@ def create_region(world: "NoitaWorld", region_name: str) -> Region:
|
|||||||
return new_region
|
return new_region
|
||||||
|
|
||||||
|
|
||||||
def create_regions(world: "NoitaWorld") -> Dict[str, Region]:
|
def create_regions(world: "NoitaWorld") -> dict[str, Region]:
|
||||||
return {name: create_region(world, name) for name in noita_regions}
|
return {name: create_region(world, name) for name in noita_regions}
|
||||||
|
|
||||||
|
|
||||||
# An "Entrance" is really just a connection between two regions
|
|
||||||
def create_entrance(player: int, source: str, destination: str, regions: Dict[str, Region]) -> Entrance:
|
|
||||||
entrance = Entrance(player, f"From {source} To {destination}", regions[source])
|
|
||||||
entrance.connect(regions[destination])
|
|
||||||
return entrance
|
|
||||||
|
|
||||||
|
|
||||||
# Creates connections based on our access mapping in `noita_connections`.
|
# Creates connections based on our access mapping in `noita_connections`.
|
||||||
def create_connections(player: int, regions: Dict[str, Region]) -> None:
|
def create_connections(regions: dict[str, Region]) -> None:
|
||||||
for source, destinations in noita_connections.items():
|
for source, destinations in noita_connections.items():
|
||||||
new_entrances = [create_entrance(player, source, destination, regions) for destination in destinations]
|
for destination in destinations:
|
||||||
regions[source].exits = new_entrances
|
regions[source].connect(regions[destination])
|
||||||
|
|
||||||
|
|
||||||
# Creates all regions and connections. Called from NoitaWorld.
|
# Creates all regions and connections. Called from NoitaWorld.
|
||||||
def create_all_regions_and_connections(world: "NoitaWorld") -> None:
|
def create_all_regions_and_connections(world: "NoitaWorld") -> None:
|
||||||
created_regions = create_regions(world)
|
created_regions = create_regions(world)
|
||||||
create_connections(world.player, created_regions)
|
create_connections(created_regions)
|
||||||
create_all_events(world, created_regions)
|
create_all_events(world, created_regions)
|
||||||
|
|
||||||
world.multiworld.regions += created_regions.values()
|
world.multiworld.regions += created_regions.values()
|
||||||
@@ -75,7 +68,7 @@ def create_all_regions_and_connections(world: "NoitaWorld") -> None:
|
|||||||
# - Lake is connected to The Laboratory, since the bosses are hard without specific set-ups (which means late game)
|
# - Lake is connected to The Laboratory, since the bosses are hard without specific set-ups (which means late game)
|
||||||
# - Snowy Depths connects to Lava Lake orb since you need digging for it, so fairly early is acceptable
|
# - Snowy Depths connects to Lava Lake orb since you need digging for it, so fairly early is acceptable
|
||||||
# - Ancient Laboratory is connected to the Coal Pits, so that Ylialkemisti isn't sphere 1
|
# - Ancient Laboratory is connected to the Coal Pits, so that Ylialkemisti isn't sphere 1
|
||||||
noita_connections: Dict[str, List[str]] = {
|
noita_connections: dict[str, list[str]] = {
|
||||||
"Menu": ["Forest"],
|
"Menu": ["Forest"],
|
||||||
"Forest": ["Mines", "Floating Island", "Desert", "Snowy Wasteland"],
|
"Forest": ["Mines", "Floating Island", "Desert", "Snowy Wasteland"],
|
||||||
"Frozen Vault": ["The Vault"],
|
"Frozen Vault": ["The Vault"],
|
||||||
@@ -117,4 +110,4 @@ noita_connections: Dict[str, List[str]] = {
|
|||||||
###
|
###
|
||||||
}
|
}
|
||||||
|
|
||||||
noita_regions: List[str] = sorted(set(noita_connections.keys()).union(*noita_connections.values()))
|
noita_regions: list[str] = sorted(set(noita_connections.keys()).union(*noita_connections.values()))
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
from typing import List, NamedTuple, Set, TYPE_CHECKING
|
from typing import NamedTuple, TYPE_CHECKING
|
||||||
|
|
||||||
from BaseClasses import CollectionState
|
|
||||||
from . import items, locations
|
from . import items, locations
|
||||||
from .options import BossesAsChecks, VictoryCondition
|
from .options import BossesAsChecks, VictoryCondition
|
||||||
from worlds.generic import Rules as GenericRules
|
from worlds.generic import Rules as GenericRules
|
||||||
@@ -16,7 +15,7 @@ class EntranceLock(NamedTuple):
|
|||||||
items_needed: int
|
items_needed: int
|
||||||
|
|
||||||
|
|
||||||
entrance_locks: List[EntranceLock] = [
|
entrance_locks: list[EntranceLock] = [
|
||||||
EntranceLock("Mines", "Coal Pits Holy Mountain", "Portal to Holy Mountain 1", 1),
|
EntranceLock("Mines", "Coal Pits Holy Mountain", "Portal to Holy Mountain 1", 1),
|
||||||
EntranceLock("Coal Pits", "Snowy Depths Holy Mountain", "Portal to Holy Mountain 2", 2),
|
EntranceLock("Coal Pits", "Snowy Depths Holy Mountain", "Portal to Holy Mountain 2", 2),
|
||||||
EntranceLock("Snowy Depths", "Hiisi Base Holy Mountain", "Portal to Holy Mountain 3", 3),
|
EntranceLock("Snowy Depths", "Hiisi Base Holy Mountain", "Portal to Holy Mountain 3", 3),
|
||||||
@@ -27,7 +26,7 @@ entrance_locks: List[EntranceLock] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
holy_mountain_regions: List[str] = [
|
holy_mountain_regions: list[str] = [
|
||||||
"Coal Pits Holy Mountain",
|
"Coal Pits Holy Mountain",
|
||||||
"Snowy Depths Holy Mountain",
|
"Snowy Depths Holy Mountain",
|
||||||
"Hiisi Base Holy Mountain",
|
"Hiisi Base Holy Mountain",
|
||||||
@@ -38,7 +37,7 @@ holy_mountain_regions: List[str] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
wand_tiers: List[str] = [
|
wand_tiers: list[str] = [
|
||||||
"Wand (Tier 1)", # Coal Pits
|
"Wand (Tier 1)", # Coal Pits
|
||||||
"Wand (Tier 2)", # Snowy Depths
|
"Wand (Tier 2)", # Snowy Depths
|
||||||
"Wand (Tier 3)", # Hiisi Base
|
"Wand (Tier 3)", # Hiisi Base
|
||||||
@@ -48,29 +47,21 @@ wand_tiers: List[str] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
items_hidden_from_shops: Set[str] = {"Gold (200)", "Gold (1000)", "Potion", "Random Potion", "Secret Potion",
|
items_hidden_from_shops: set[str] = {"Gold (200)", "Gold (1000)", "Potion", "Random Potion", "Secret Potion",
|
||||||
"Chaos Die", "Greed Die", "Kammi", "Refreshing Gourd", "Sädekivi", "Broken Wand",
|
"Chaos Die", "Greed Die", "Kammi", "Refreshing Gourd", "Sädekivi", "Broken Wand",
|
||||||
"Powder Pouch"}
|
"Powder Pouch"}
|
||||||
|
|
||||||
perk_list: List[str] = list(filter(items.item_is_perk, items.item_table.keys()))
|
perk_list: list[str] = list(filter(lambda item: items.item_table[item].group == "Perks", items.item_table.keys()))
|
||||||
|
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
# Helper Functions
|
# Helper Function
|
||||||
# ----------------
|
# ----------------
|
||||||
|
|
||||||
|
|
||||||
def has_perk_count(state: CollectionState, player: int, amount: int) -> bool:
|
def forbid_items_at_locations(world: "NoitaWorld", shop_locations: set[str], forbidden_items: set[str]) -> None:
|
||||||
return sum(state.count(perk, player) for perk in perk_list) >= amount
|
|
||||||
|
|
||||||
|
|
||||||
def has_orb_count(state: CollectionState, player: int, amount: int) -> bool:
|
|
||||||
return state.count("Orb", player) >= amount
|
|
||||||
|
|
||||||
|
|
||||||
def forbid_items_at_locations(world: "NoitaWorld", shop_locations: Set[str], forbidden_items: Set[str]) -> None:
|
|
||||||
for shop_location in shop_locations:
|
for shop_location in shop_locations:
|
||||||
location = world.multiworld.get_location(shop_location, world.player)
|
location = world.get_location(shop_location)
|
||||||
GenericRules.forbid_items_for_player(location, forbidden_items, world.player)
|
GenericRules.forbid_items_for_player(location, forbidden_items, world.player)
|
||||||
|
|
||||||
|
|
||||||
@@ -104,38 +95,38 @@ def ban_early_high_tier_wands(world: "NoitaWorld") -> None:
|
|||||||
|
|
||||||
def lock_holy_mountains_into_spheres(world: "NoitaWorld") -> None:
|
def lock_holy_mountains_into_spheres(world: "NoitaWorld") -> None:
|
||||||
for lock in entrance_locks:
|
for lock in entrance_locks:
|
||||||
location = world.multiworld.get_entrance(f"From {lock.source} To {lock.destination}", world.player)
|
location = world.get_entrance(f"{lock.source} -> {lock.destination}")
|
||||||
GenericRules.set_rule(location, lambda state, evt=lock.event: state.has(evt, world.player))
|
GenericRules.set_rule(location, lambda state, evt=lock.event: state.has(evt, world.player))
|
||||||
|
|
||||||
|
|
||||||
def holy_mountain_unlock_conditions(world: "NoitaWorld") -> None:
|
def holy_mountain_unlock_conditions(world: "NoitaWorld") -> None:
|
||||||
victory_condition = world.options.victory_condition.value
|
victory_condition = world.options.victory_condition.value
|
||||||
for lock in entrance_locks:
|
for lock in entrance_locks:
|
||||||
location = world.multiworld.get_location(lock.event, world.player)
|
location = world.get_location(lock.event)
|
||||||
|
|
||||||
if victory_condition == VictoryCondition.option_greed_ending:
|
if victory_condition == VictoryCondition.option_greed_ending:
|
||||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||||
has_perk_count(state, world.player, items_needed//2)
|
state.has_group_unique("Perks", world.player, items_needed // 2)
|
||||||
)
|
)
|
||||||
elif victory_condition == VictoryCondition.option_pure_ending:
|
elif victory_condition == VictoryCondition.option_pure_ending:
|
||||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||||
has_perk_count(state, world.player, items_needed//2) and
|
state.has_group_unique("Perks", world.player, items_needed // 2) and
|
||||||
has_orb_count(state, world.player, items_needed)
|
state.has("Orb", world.player, items_needed)
|
||||||
)
|
)
|
||||||
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
||||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||||
has_perk_count(state, world.player, items_needed//2) and
|
state.has_group_unique("Perks", world.player, items_needed // 2) and
|
||||||
has_orb_count(state, world.player, items_needed * 3)
|
state.has("Orb", world.player, items_needed * 3)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def biome_unlock_conditions(world: "NoitaWorld") -> None:
|
def biome_unlock_conditions(world: "NoitaWorld") -> None:
|
||||||
lukki_entrances = world.multiworld.get_region("Lukki Lair", world.player).entrances
|
lukki_entrances = world.get_region("Lukki Lair").entrances
|
||||||
magical_entrances = world.multiworld.get_region("Magical Temple", world.player).entrances
|
magical_entrances = world.get_region("Magical Temple").entrances
|
||||||
wizard_entrances = world.multiworld.get_region("Wizards' Den", world.player).entrances
|
wizard_entrances = world.get_region("Wizards' Den").entrances
|
||||||
for entrance in lukki_entrances:
|
for entrance in lukki_entrances:
|
||||||
entrance.access_rule = lambda state: state.has("Melee Immunity Perk", world.player) and\
|
entrance.access_rule = lambda state: (
|
||||||
state.has("All-Seeing Eye Perk", world.player)
|
state.has_all(("Melee Immunity Perk", "All-Seeing Eye Perk"), world.player))
|
||||||
for entrance in magical_entrances:
|
for entrance in magical_entrances:
|
||||||
entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", world.player)
|
entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", world.player)
|
||||||
for entrance in wizard_entrances:
|
for entrance in wizard_entrances:
|
||||||
@@ -144,12 +135,12 @@ def biome_unlock_conditions(world: "NoitaWorld") -> None:
|
|||||||
|
|
||||||
def victory_unlock_conditions(world: "NoitaWorld") -> None:
|
def victory_unlock_conditions(world: "NoitaWorld") -> None:
|
||||||
victory_condition = world.options.victory_condition.value
|
victory_condition = world.options.victory_condition.value
|
||||||
victory_location = world.multiworld.get_location("Victory", world.player)
|
victory_location = world.get_location("Victory")
|
||||||
|
|
||||||
if victory_condition == VictoryCondition.option_pure_ending:
|
if victory_condition == VictoryCondition.option_pure_ending:
|
||||||
victory_location.access_rule = lambda state: has_orb_count(state, world.player, 11)
|
victory_location.access_rule = lambda state: state.has("Orb", world.player, 11)
|
||||||
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
||||||
victory_location.access_rule = lambda state: has_orb_count(state, world.player, 33)
|
victory_location.access_rule = lambda state: state.has("Orb", world.player, 33)
|
||||||
|
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
@@ -168,5 +159,5 @@ def create_all_rules(world: "NoitaWorld") -> None:
|
|||||||
|
|
||||||
# Prevent the Map perk (used to find Toveri) from being on Toveri (boss)
|
# Prevent the Map perk (used to find Toveri) from being on Toveri (boss)
|
||||||
if world.options.bosses_as_checks.value >= BossesAsChecks.option_all_bosses:
|
if world.options.bosses_as_checks.value >= BossesAsChecks.option_all_bosses:
|
||||||
toveri = world.multiworld.get_location("Toveri", world.player)
|
toveri = world.get_location("Toveri")
|
||||||
GenericRules.forbid_items_for_player(toveri, {"Spatial Awareness Perk"}, world.player)
|
GenericRules.forbid_items_for_player(toveri, {"Spatial Awareness Perk"}, world.player)
|
||||||
|
Reference in New Issue
Block a user