Noita: implement new game (#1676)
* Noita: implement new game (#1676) --------- Co-authored-by: DaftBrit <87314354+DaftBrit@users.noreply.github.com> Co-authored-by: l.kelsall@b4rn.org.uk <l.kelsall@b4rn.org.uk> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> Co-authored-by: Scipio Wright <scipiowright@gmail.com> Co-authored-by: Scipio Wright <lightdemonjoe4@gmail.com> Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
parent
722757e18a
commit
4dc934729d
|
@ -44,6 +44,7 @@ Currently, the following games are supported:
|
|||
* Clique
|
||||
* Adventure
|
||||
* DLC Quest
|
||||
* Noita
|
||||
|
||||
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
|
||||
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
from typing import Dict
|
||||
|
||||
from BaseClasses import Item, ItemClassification, Location, MultiWorld, Region
|
||||
from . import Items, Locations
|
||||
|
||||
|
||||
def create_event(player: int, name: str) -> Item:
|
||||
return Items.NoitaItem(name, ItemClassification.progression, None, player)
|
||||
|
||||
|
||||
def create_location(player: int, name: str, region: Region) -> Location:
|
||||
return Locations.NoitaLocation(player, name, None, region)
|
||||
|
||||
|
||||
def create_locked_location_event(multiworld: MultiWorld, player: int, region_name: str, item: str) -> Location:
|
||||
region = multiworld.get_region(region_name, player)
|
||||
|
||||
new_location = create_location(player, item, region)
|
||||
new_location.place_locked_item(create_event(player, item))
|
||||
|
||||
region.locations.append(new_location)
|
||||
return new_location
|
||||
|
||||
|
||||
def create_all_events(multiworld: MultiWorld, player: int) -> None:
|
||||
for region, event in event_locks.items():
|
||||
create_locked_location_event(multiworld, player, region, event)
|
||||
|
||||
multiworld.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
|
||||
|
||||
# Maps region names to event names
|
||||
event_locks: Dict[str, str] = {
|
||||
"The Work": "Victory",
|
||||
"Mines": "Portal to Holy Mountain 1",
|
||||
"Coal Pits": "Portal to Holy Mountain 2",
|
||||
"Snowy Depths": "Portal to Holy Mountain 3",
|
||||
"Hiisi Base": "Portal to Holy Mountain 4",
|
||||
"Underground Jungle": "Portal to Holy Mountain 5",
|
||||
"The Vault": "Portal to Holy Mountain 6",
|
||||
"Temple of the Art": "Portal to Holy Mountain 7",
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
import itertools
|
||||
from collections import Counter
|
||||
from typing import Dict, List, NamedTuple, Optional, Set
|
||||
|
||||
from BaseClasses import Item, ItemClassification, MultiWorld
|
||||
from .Options import BossesAsChecks, VictoryCondition
|
||||
|
||||
|
||||
class ItemData(NamedTuple):
|
||||
code: Optional[int]
|
||||
group: str
|
||||
classification: ItemClassification = ItemClassification.progression
|
||||
required_num: int = 0
|
||||
|
||||
|
||||
class NoitaItem(Item):
|
||||
game: str = "Noita"
|
||||
|
||||
|
||||
def create_item(player: int, name: str) -> Item:
|
||||
item_data = item_table[name]
|
||||
return NoitaItem(name, item_data.classification, item_data.code, player)
|
||||
|
||||
|
||||
def create_fixed_item_pool() -> List[str]:
|
||||
required_items: Dict[str, int] = {name: data.required_num for name, data in item_table.items()}
|
||||
return list(Counter(required_items).elements())
|
||||
|
||||
|
||||
def create_orb_items(victory_condition: VictoryCondition) -> List[str]:
|
||||
orb_count = 0
|
||||
if victory_condition == VictoryCondition.option_pure_ending:
|
||||
orb_count = 11
|
||||
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
||||
orb_count = 33
|
||||
return ["Orb" for _ in range(orb_count)]
|
||||
|
||||
|
||||
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 []
|
||||
|
||||
|
||||
def create_kantele(victory_condition: VictoryCondition) -> List[str]:
|
||||
return ["Kantele"] if victory_condition.value >= VictoryCondition.option_pure_ending else []
|
||||
|
||||
|
||||
def create_random_items(multiworld: MultiWorld, player: int, random_count: int) -> List[str]:
|
||||
filler_pool = filler_weights.copy()
|
||||
if multiworld.bad_effects[player].value == 0:
|
||||
del filler_pool["Trap"]
|
||||
|
||||
return multiworld.random.choices(
|
||||
population=list(filler_pool.keys()),
|
||||
weights=list(filler_pool.values()),
|
||||
k=random_count
|
||||
)
|
||||
|
||||
|
||||
def create_all_items(multiworld: MultiWorld, player: int) -> None:
|
||||
sum_locations = len(multiworld.get_unfilled_locations(player))
|
||||
|
||||
itempool = (
|
||||
create_fixed_item_pool()
|
||||
+ create_orb_items(multiworld.victory_condition[player])
|
||||
+ create_spatial_awareness_item(multiworld.bosses_as_checks[player])
|
||||
+ create_kantele(multiworld.victory_condition[player])
|
||||
)
|
||||
|
||||
random_count = sum_locations - len(itempool)
|
||||
itempool += create_random_items(multiworld, player, random_count)
|
||||
|
||||
multiworld.itempool += [create_item(player, name) for name in itempool]
|
||||
|
||||
|
||||
# 110000 - 110032
|
||||
item_table: Dict[str, ItemData] = {
|
||||
"Trap": ItemData(110000, "Traps", ItemClassification.trap),
|
||||
"Extra Max HP": ItemData(110001, "Pickups", ItemClassification.useful),
|
||||
"Spell Refresher": ItemData(110002, "Pickups", ItemClassification.filler),
|
||||
"Potion": ItemData(110003, "Items", ItemClassification.filler),
|
||||
"Gold (200)": ItemData(110004, "Gold", ItemClassification.filler),
|
||||
"Gold (1000)": ItemData(110005, "Gold", ItemClassification.filler),
|
||||
"Wand (Tier 1)": ItemData(110006, "Wands", ItemClassification.useful),
|
||||
"Wand (Tier 2)": ItemData(110007, "Wands", ItemClassification.useful),
|
||||
"Wand (Tier 3)": ItemData(110008, "Wands", ItemClassification.useful),
|
||||
"Wand (Tier 4)": ItemData(110009, "Wands", ItemClassification.useful),
|
||||
"Wand (Tier 5)": ItemData(110010, "Wands", ItemClassification.useful),
|
||||
"Wand (Tier 6)": ItemData(110011, "Wands", ItemClassification.useful),
|
||||
"Kantele": ItemData(110012, "Wands", ItemClassification.useful),
|
||||
"Fire Immunity Perk": ItemData(110013, "Perks", ItemClassification.progression, 1),
|
||||
"Toxic Immunity Perk": ItemData(110014, "Perks", ItemClassification.progression, 1),
|
||||
"Explosion Immunity Perk": ItemData(110015, "Perks", ItemClassification.progression, 1),
|
||||
"Melee Immunity Perk": ItemData(110016, "Perks", ItemClassification.progression, 1),
|
||||
"Electricity Immunity Perk": ItemData(110017, "Perks", ItemClassification.progression, 1),
|
||||
"Tinker with Wands Everywhere Perk": ItemData(110018, "Perks", ItemClassification.progression, 1),
|
||||
"All-Seeing Eye Perk": ItemData(110019, "Perks", ItemClassification.progression, 1),
|
||||
"Spatial Awareness Perk": ItemData(110020, "Perks", ItemClassification.progression),
|
||||
"Extra Life Perk": ItemData(110021, "Repeatable Perks", ItemClassification.useful),
|
||||
"Orb": ItemData(110022, "Orbs", ItemClassification.progression_skip_balancing),
|
||||
"Random Potion": ItemData(110023, "Items", ItemClassification.filler),
|
||||
"Secret Potion": ItemData(110024, "Items", ItemClassification.filler),
|
||||
"Powder Pouch": ItemData(110025, "Items", ItemClassification.filler),
|
||||
"Chaos Die": ItemData(110026, "Items", ItemClassification.filler),
|
||||
"Greed Die": ItemData(110027, "Items", ItemClassification.filler),
|
||||
"Kammi": ItemData(110028, "Items", ItemClassification.filler),
|
||||
"Refreshing Gourd": ItemData(110029, "Items", ItemClassification.filler),
|
||||
"Sädekivi": ItemData(110030, "Items", ItemClassification.filler),
|
||||
"Broken Wand": ItemData(110031, "Items", ItemClassification.filler),
|
||||
|
||||
}
|
||||
|
||||
filler_weights: Dict[str, int] = {
|
||||
"Trap": 15,
|
||||
"Extra Max HP": 25,
|
||||
"Spell Refresher": 20,
|
||||
"Potion": 40,
|
||||
"Gold (200)": 15,
|
||||
"Gold (1000)": 6,
|
||||
"Wand (Tier 1)": 10,
|
||||
"Wand (Tier 2)": 8,
|
||||
"Wand (Tier 3)": 7,
|
||||
"Wand (Tier 4)": 6,
|
||||
"Wand (Tier 5)": 5,
|
||||
"Wand (Tier 6)": 4,
|
||||
"Extra Life Perk": 10,
|
||||
"Random Potion": 9,
|
||||
"Secret Potion": 10,
|
||||
"Powder Pouch": 10,
|
||||
"Chaos Die": 4,
|
||||
"Greed Die": 4,
|
||||
"Kammi": 4,
|
||||
"Refreshing Gourd": 4,
|
||||
"Sädekivi": 3,
|
||||
"Broken Wand": 10,
|
||||
}
|
||||
|
||||
|
||||
# These helper functions make the comprehensions below more readable
|
||||
def get_item_group(item_name: str) -> str:
|
||||
return item_table[item_name].group
|
||||
|
||||
|
||||
def item_is_filler(item_name: str) -> bool:
|
||||
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)
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
# Locations are specific points that you would obtain an item at.
|
||||
from enum import IntEnum
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import Location
|
||||
|
||||
|
||||
class NoitaLocation(Location):
|
||||
game: str = "Noita"
|
||||
|
||||
|
||||
class LocationData(NamedTuple):
|
||||
id: int
|
||||
flag: int = 0
|
||||
ltype: Optional[str] = ""
|
||||
|
||||
|
||||
class LocationFlag(IntEnum):
|
||||
none = 0
|
||||
main_path = 1
|
||||
side_path = 2
|
||||
main_world = 3
|
||||
parallel_worlds = 4
|
||||
|
||||
|
||||
# Mapping of items in each region.
|
||||
# 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.
|
||||
# 110000-110649
|
||||
location_region_mapping: Dict[str, Dict[str, LocationData]] = {
|
||||
"Coal Pits Holy Mountain": {
|
||||
"Coal Pits Holy Mountain Shop Item 1": LocationData(110000),
|
||||
"Coal Pits Holy Mountain Shop Item 2": LocationData(110001),
|
||||
"Coal Pits Holy Mountain Shop Item 3": LocationData(110002),
|
||||
"Coal Pits Holy Mountain Shop Item 4": LocationData(110003),
|
||||
"Coal Pits Holy Mountain Shop Item 5": LocationData(110004),
|
||||
"Coal Pits Holy Mountain Spell Refresh": LocationData(110005),
|
||||
},
|
||||
"Snowy Depths Holy Mountain": {
|
||||
"Snowy Depths Holy Mountain Shop Item 1": LocationData(110006),
|
||||
"Snowy Depths Holy Mountain Shop Item 2": LocationData(110007),
|
||||
"Snowy Depths Holy Mountain Shop Item 3": LocationData(110008),
|
||||
"Snowy Depths Holy Mountain Shop Item 4": LocationData(110009),
|
||||
"Snowy Depths Holy Mountain Shop Item 5": LocationData(110010),
|
||||
"Snowy Depths Holy Mountain Spell Refresh": LocationData(110011),
|
||||
},
|
||||
"Hiisi Base Holy Mountain": {
|
||||
"Hiisi Base Holy Mountain Shop Item 1": LocationData(110012),
|
||||
"Hiisi Base Holy Mountain Shop Item 2": LocationData(110013),
|
||||
"Hiisi Base Holy Mountain Shop Item 3": LocationData(110014),
|
||||
"Hiisi Base Holy Mountain Shop Item 4": LocationData(110015),
|
||||
"Hiisi Base Holy Mountain Shop Item 5": LocationData(110016),
|
||||
"Hiisi Base Holy Mountain Spell Refresh": LocationData(110017),
|
||||
},
|
||||
"Underground Jungle Holy Mountain": {
|
||||
"Underground Jungle Holy Mountain Shop Item 1": LocationData(110018),
|
||||
"Underground Jungle Holy Mountain Shop Item 2": LocationData(110019),
|
||||
"Underground Jungle Holy Mountain Shop Item 3": LocationData(110020),
|
||||
"Underground Jungle Holy Mountain Shop Item 4": LocationData(110021),
|
||||
"Underground Jungle Holy Mountain Shop Item 5": LocationData(110022),
|
||||
"Underground Jungle Holy Mountain Spell Refresh": LocationData(110023),
|
||||
},
|
||||
"Vault Holy Mountain": {
|
||||
"Vault Holy Mountain Shop Item 1": LocationData(110024),
|
||||
"Vault Holy Mountain Shop Item 2": LocationData(110025),
|
||||
"Vault Holy Mountain Shop Item 3": LocationData(110026),
|
||||
"Vault Holy Mountain Shop Item 4": LocationData(110027),
|
||||
"Vault Holy Mountain Shop Item 5": LocationData(110028),
|
||||
"Vault Holy Mountain Spell Refresh": LocationData(110029),
|
||||
},
|
||||
"Temple of the Art Holy Mountain": {
|
||||
"Temple of the Art Holy Mountain Shop Item 1": LocationData(110030),
|
||||
"Temple of the Art Holy Mountain Shop Item 2": LocationData(110031),
|
||||
"Temple of the Art Holy Mountain Shop Item 3": LocationData(110032),
|
||||
"Temple of the Art Holy Mountain Shop Item 4": LocationData(110033),
|
||||
"Temple of the Art Holy Mountain Shop Item 5": LocationData(110034),
|
||||
"Temple of the Art Holy Mountain Spell Refresh": LocationData(110035),
|
||||
},
|
||||
"Laboratory Holy Mountain": {
|
||||
"Laboratory Holy Mountain Shop Item 1": LocationData(110036),
|
||||
"Laboratory Holy Mountain Shop Item 2": LocationData(110037),
|
||||
"Laboratory Holy Mountain Shop Item 3": LocationData(110038),
|
||||
"Laboratory Holy Mountain Shop Item 4": LocationData(110039),
|
||||
"Laboratory Holy Mountain Shop Item 5": LocationData(110040),
|
||||
"Laboratory Holy Mountain Spell Refresh": LocationData(110041),
|
||||
},
|
||||
"Secret Shop": {
|
||||
"Secret Shop Item 1": LocationData(110042),
|
||||
"Secret Shop Item 2": LocationData(110043),
|
||||
"Secret Shop Item 3": LocationData(110044),
|
||||
"Secret Shop Item 4": LocationData(110045),
|
||||
},
|
||||
"Floating Island": {
|
||||
"Floating Island Orb": LocationData(110658, LocationFlag.main_path, "orb"),
|
||||
},
|
||||
"Pyramid": {
|
||||
"Kolmisilmän Koipi": LocationData(110649, LocationFlag.main_world, "boss"),
|
||||
"Pyramid Orb": LocationData(110659, LocationFlag.main_world, "orb"),
|
||||
"Sandcave Orb": LocationData(110662, LocationFlag.main_world, "orb"),
|
||||
},
|
||||
"Overgrown Cavern": {
|
||||
"Overgrown Cavern Chest": LocationData(110526, LocationFlag.main_world, "chest"),
|
||||
"Overgrown Cavern Pedestal": LocationData(110546, LocationFlag.main_world, "pedestal"),
|
||||
},
|
||||
"Lake": {
|
||||
"Syväolento": LocationData(110651, LocationFlag.main_world, "boss"),
|
||||
},
|
||||
"Frozen Vault": {
|
||||
"Frozen Vault Orb": LocationData(110660, LocationFlag.main_world, "orb"),
|
||||
"Frozen Vault Chest": LocationData(110566, LocationFlag.main_world, "chest"),
|
||||
"Frozen Vault Pedestal": LocationData(110586, LocationFlag.main_world, "pedestal"),
|
||||
},
|
||||
"Mines": {
|
||||
"Mines Chest": LocationData(110046, LocationFlag.main_path, "chest"),
|
||||
"Mines Pedestal": LocationData(110066, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
# Collapsed Mines is a very small area, combining it with the Mines. Leaving this here in case we change our minds.
|
||||
# "Collapsed Mines": {
|
||||
# "Collapsed Mines Chest": LocationData(110086, LocationFlag.main_path, "chest"),
|
||||
# "Collapsed Mines Pedestal": LocationData(110106, LocationFlag.main_path, "pedestal"),
|
||||
# },
|
||||
"Ancient Laboratory": {
|
||||
"Ylialkemisti": LocationData(110656, LocationFlag.side_path, "boss"),
|
||||
},
|
||||
"Abyss Orb Room": {
|
||||
"Sauvojen Tuntija": LocationData(110650, LocationFlag.side_path, "boss"),
|
||||
"Abyss Orb": LocationData(110665, LocationFlag.main_path, "orb"),
|
||||
},
|
||||
"Below Lava Lake": {
|
||||
"Lava Lake Orb": LocationData(110661, LocationFlag.side_path, "orb"),
|
||||
},
|
||||
"Coal Pits": {
|
||||
"Coal Pits Chest": LocationData(110126, LocationFlag.main_path, "chest"),
|
||||
"Coal Pits Pedestal": LocationData(110146, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"Fungal Caverns": {
|
||||
"Fungal Caverns Chest": LocationData(110166, LocationFlag.side_path, "chest"),
|
||||
"Fungal Caverns Pedestal": LocationData(110186, LocationFlag.side_path, "pedestal"),
|
||||
},
|
||||
"Snowy Depths": {
|
||||
"Snowy Depths Chest": LocationData(110206, LocationFlag.main_path, "chest"),
|
||||
"Snowy Depths Pedestal": LocationData(110226, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"Magical Temple": {
|
||||
"Magical Temple Orb": LocationData(110663, LocationFlag.side_path, "orb"),
|
||||
},
|
||||
"Hiisi Base": {
|
||||
"Hiisi Base Chest": LocationData(110246, LocationFlag.main_path, "chest"),
|
||||
"Hiisi Base Pedestal": LocationData(110266, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"Underground Jungle": {
|
||||
"Suomuhauki": LocationData(110648, LocationFlag.main_path, "boss"),
|
||||
"Underground Jungle Chest": LocationData(110286, LocationFlag.main_path, "chest"),
|
||||
"Underground Jungle Pedestal": LocationData(110306, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"Lukki Lair": {
|
||||
"Lukki Lair Orb": LocationData(110664, LocationFlag.side_path, "orb"),
|
||||
"Lukki Lair Chest": LocationData(110326, LocationFlag.side_path, "chest"),
|
||||
"Lukki Lair Pedestal": LocationData(110346, LocationFlag.side_path, "pedestal"),
|
||||
},
|
||||
"The Vault": {
|
||||
"The Vault Chest": LocationData(110366, LocationFlag.main_path, "chest"),
|
||||
"The Vault Pedestal": LocationData(110386, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"Temple of the Art": {
|
||||
"Gate Guardian": LocationData(110652, LocationFlag.main_path, "boss"),
|
||||
"Temple of the Art Chest": LocationData(110406, LocationFlag.main_path, "chest"),
|
||||
"Temple of the Art Pedestal": LocationData(110426, LocationFlag.main_path, "pedestal"),
|
||||
},
|
||||
"The Tower": {
|
||||
"The Tower Chest": LocationData(110606, LocationFlag.main_world, "chest"),
|
||||
"The Tower Pedestal": LocationData(110626, LocationFlag.main_world, "pedestal"),
|
||||
},
|
||||
"Wizard's Den": {
|
||||
"Mestarien Mestari": LocationData(110655, LocationFlag.main_world, "boss"),
|
||||
"Wizard's Den Orb": LocationData(110668, LocationFlag.main_world, "orb"),
|
||||
"Wizards' Den Chest": LocationData(110446, LocationFlag.main_world, "chest"),
|
||||
"Wizards' Den Pedestal": LocationData(110466, LocationFlag.main_world, "pedestal"),
|
||||
},
|
||||
"Powerplant": {
|
||||
"Kolmisilmän silmä": LocationData(110657, LocationFlag.main_world, "boss"),
|
||||
"Power Plant Chest": LocationData(110486, LocationFlag.main_world, "chest"),
|
||||
"Power Plant Pedestal": LocationData(110506, LocationFlag.main_world, "pedestal"),
|
||||
},
|
||||
"Snow Chasm": {
|
||||
"Unohdettu": LocationData(110653, LocationFlag.main_world, "boss"),
|
||||
"Snow Chasm Orb": LocationData(110667, LocationFlag.main_world, "orb"),
|
||||
},
|
||||
"Deep Underground": {
|
||||
"Limatoukka": LocationData(110647, LocationFlag.main_world, "boss"),
|
||||
},
|
||||
"The Laboratory": {
|
||||
"Kolmisilmä": LocationData(110646, LocationFlag.main_path, "boss"),
|
||||
},
|
||||
"Friend Cave": {
|
||||
"Toveri": LocationData(110654, LocationFlag.main_world, "boss"),
|
||||
},
|
||||
"The Work (Hell)": {
|
||||
"The Work (Hell) Orb": LocationData(110666, LocationFlag.main_world, "orb"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Iterating the hidden chest and pedestal locations here to avoid clutter above
|
||||
def generate_location_entries(locname: str, locinfo: LocationData) -> Dict[str, int]:
|
||||
if locinfo.ltype in ["chest", "pedestal"]:
|
||||
return {f"{locname} {i + 1}": locinfo.id + i for i in range(20)}
|
||||
return {locname: locinfo.id}
|
||||
|
||||
|
||||
location_name_to_id: Dict[str, int] = {}
|
||||
for location_group in location_region_mapping.values():
|
||||
for locname, locinfo in location_group.items():
|
||||
location_name_to_id.update(generate_location_entries(locname, locinfo))
|
|
@ -0,0 +1,89 @@
|
|||
from typing import Dict
|
||||
from Options import Choice, DeathLink, DefaultOnToggle, Option, Range
|
||||
|
||||
|
||||
class PathOption(Choice):
|
||||
"""Choose where you would like Hidden Chest and Pedestal checks to be placed.
|
||||
Main Path includes the main 7 biomes you typically go through to get to the final boss.
|
||||
Side Path includes the Lukki Lair and Fungal Caverns. 9 biomes total.
|
||||
Main World includes the full world (excluding parallel worlds). 14 biomes total.
|
||||
Note: The Collapsed Mines have been combined into the Mines as the biome is tiny."""
|
||||
display_name = "Path Option"
|
||||
option_main_path = 1
|
||||
option_side_path = 2
|
||||
option_main_world = 3
|
||||
default = 1
|
||||
|
||||
|
||||
class HiddenChests(Range):
|
||||
"""Number of hidden chest checks added to the applicable biomes."""
|
||||
display_name = "Hidden Chests per Biome"
|
||||
range_start = 0
|
||||
range_end = 20
|
||||
default = 3
|
||||
|
||||
|
||||
class PedestalChecks(Range):
|
||||
"""Number of checks that will spawn on pedestals in the applicable biomes."""
|
||||
display_name = "Pedestal Checks per Biome"
|
||||
range_start = 0
|
||||
range_end = 20
|
||||
default = 6
|
||||
|
||||
|
||||
class Traps(DefaultOnToggle):
|
||||
"""Whether negative effects on the Noita world are added to the item pool."""
|
||||
display_name = "Traps"
|
||||
|
||||
|
||||
class OrbsAsChecks(Choice):
|
||||
"""Decides whether finding the orbs that naturally spawn in the world count as checks.
|
||||
The Main Path option includes only the Floating Island and Abyss Orb Room orbs.
|
||||
The Side Path option includes the Main Path, Magical Temple, Lukki Lair, and Lava Lake orbs.
|
||||
The Main World option includes all 11 orbs."""
|
||||
display_name = "Orbs as Location Checks"
|
||||
option_no_orbs = 0
|
||||
option_main_path = 1
|
||||
option_side_path = 2
|
||||
option_main_world = 3
|
||||
default = 0
|
||||
|
||||
|
||||
class BossesAsChecks(Choice):
|
||||
"""Makes bosses count as location checks. The boss only needs to die, you do not need the kill credit.
|
||||
The Main Path option includes Gate Guardian, Suomuhauki, and Kolmisilmä.
|
||||
The Side Path option includes the Main Path bosses, Sauvojen Tuntija, and Ylialkemisti.
|
||||
The All Bosses option includes all 12 bosses."""
|
||||
display_name = "Bosses as Location Checks"
|
||||
option_no_bosses = 0
|
||||
option_main_path = 1
|
||||
option_side_path = 2
|
||||
option_all_bosses = 3
|
||||
default = 0
|
||||
|
||||
|
||||
# Note: the Sampo is an item that is picked up to trigger the boss fight at the normal ending location.
|
||||
# The sampo is required for every ending (having orbs and bringing the sampo to a different spot changes the ending).
|
||||
class VictoryCondition(Choice):
|
||||
"""Greed is to get to the bottom, beat the boss, and win the game.
|
||||
Pure is to get the 11 orbs in the main world, grab the sampo, and bring it to the mountain altar.
|
||||
Peaceful is to get all 33 orbs in main + parallel, grab the sampo, and bring it to the mountain altar.
|
||||
Orbs will be added to the randomizer pool according to what victory condition you chose.
|
||||
The base game orbs will not count towards these victory conditions."""
|
||||
display_name = "Victory Condition"
|
||||
option_greed_ending = 0
|
||||
option_pure_ending = 1
|
||||
option_peaceful_ending = 2
|
||||
default = 0
|
||||
|
||||
|
||||
noita_options: Dict[str, type(Option)] = {
|
||||
"death_link": DeathLink,
|
||||
"bad_effects": Traps,
|
||||
"victory_condition": VictoryCondition,
|
||||
"path_option": PathOption,
|
||||
"hidden_chests": HiddenChests,
|
||||
"pedestal_checks": PedestalChecks,
|
||||
"orbs_as_checks": OrbsAsChecks,
|
||||
"bosses_as_checks": BossesAsChecks,
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
# Regions are areas in your game that you travel to.
|
||||
from typing import Dict, Set
|
||||
|
||||
from BaseClasses import Entrance, MultiWorld, Region
|
||||
from . import Locations
|
||||
|
||||
|
||||
def add_location(player: int, loc_name: str, id: int, region: Region) -> None:
|
||||
location = Locations.NoitaLocation(player, loc_name, id, region)
|
||||
region.locations.append(location)
|
||||
|
||||
|
||||
def add_locations(multiworld: MultiWorld, player: int, region: Region) -> None:
|
||||
locations = Locations.location_region_mapping.get(region.name, {})
|
||||
for location_name, location_data in locations.items():
|
||||
location_type = location_data.ltype
|
||||
flag = location_data.flag
|
||||
|
||||
opt_orbs = multiworld.orbs_as_checks[player].value
|
||||
opt_bosses = multiworld.bosses_as_checks[player].value
|
||||
opt_paths = multiworld.path_option[player].value
|
||||
opt_num_chests = multiworld.hidden_chests[player].value
|
||||
opt_num_pedestals = multiworld.pedestal_checks[player].value
|
||||
|
||||
is_orb_allowed = location_type == "orb" and flag <= opt_orbs
|
||||
is_boss_allowed = location_type == "boss" and flag <= opt_bosses
|
||||
if flag == Locations.LocationFlag.none or is_orb_allowed or is_boss_allowed:
|
||||
add_location(player, location_name, location_data.id, region)
|
||||
elif location_type == "chest" and flag <= opt_paths:
|
||||
for i in range(opt_num_chests):
|
||||
add_location(player, f"{location_name} {i+1}", location_data.id + i, region)
|
||||
elif location_type == "pedestal" and flag <= opt_paths:
|
||||
for i in range(opt_num_pedestals):
|
||||
add_location(player, f"{location_name} {i+1}", location_data.id + i, region)
|
||||
|
||||
|
||||
# Creates a new Region with the locations found in `location_region_mapping` and adds them to the world.
|
||||
def create_region(multiworld: MultiWorld, player: int, region_name: str) -> Region:
|
||||
new_region = Region(region_name, player, multiworld)
|
||||
add_locations(multiworld, player, new_region)
|
||||
return new_region
|
||||
|
||||
|
||||
def create_regions(multiworld: MultiWorld, player: int) -> Dict[str, Region]:
|
||||
return {name: create_region(multiworld, player, 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(player, f"From {source} To {destination}", regions[source])
|
||||
entrance.connect(regions[destination])
|
||||
return entrance
|
||||
|
||||
|
||||
# Creates connections based on our access mapping in `noita_connections`.
|
||||
def create_connections(player: int, regions: Dict[str, Region]) -> None:
|
||||
for source, destinations in noita_connections.items():
|
||||
new_entrances = [create_entrance(player, source, destination, regions) for destination in destinations]
|
||||
regions[source].exits = new_entrances
|
||||
|
||||
|
||||
# Creates all regions and connections. Called from NoitaWorld.
|
||||
def create_all_regions_and_connections(multiworld: MultiWorld, player: int) -> None:
|
||||
created_regions = create_regions(multiworld, player)
|
||||
create_connections(player, created_regions)
|
||||
|
||||
multiworld.regions += created_regions.values()
|
||||
|
||||
|
||||
# Oh, what a tangled web we weave
|
||||
# Notes to create artificial spheres:
|
||||
# - Shaft is excluded to disconnect Mines from the Snowy Depths
|
||||
# - Lukki Lair is disconnected from The Vault
|
||||
# - Overgrown Cavern is connected to the Underground Jungle instead of the Desert due to similar difficulty
|
||||
# - Powerplant is disconnected from the Sandcave due to difficulty and sphere creation
|
||||
# - Snow Chasm is disconnected from the Snowy Wasteland
|
||||
# - Pyramid is connected to the Hiisi Base instead of the Desert due to similar difficulty
|
||||
# - Frozen Vault is connected to the Vault instead of the Snowy Wasteland due to similar difficulty
|
||||
noita_connections: Dict[str, Set[str]] = {
|
||||
"Menu": {"Forest"},
|
||||
"Forest": {"Mines", "Floating Island", "Desert", "Snowy Wasteland"},
|
||||
"Snowy Wasteland": {"Lake", "Forest"},
|
||||
"Frozen Vault": {"The Vault"},
|
||||
"Lake": {"Snowy Wasteland", "Desert"},
|
||||
"Desert": {"Lake", "Forest"},
|
||||
"Floating Island": {"Forest"},
|
||||
"Pyramid": {"Hiisi Base"},
|
||||
"Overgrown Cavern": {"Sandcave", "Undeground Jungle"},
|
||||
"Sandcave": {"Overgrown Cavern"},
|
||||
|
||||
###
|
||||
"Mines": {"Collapsed Mines", "Coal Pits Holy Mountain", "Lava Lake", "Forest"},
|
||||
"Collapsed Mines": {"Mines", "Dark Cave"},
|
||||
"Lava Lake": {"Mines", "Abyss Orb Room", "Below Lava Lake"},
|
||||
"Abyss Orb Room": {"Lava Lake"},
|
||||
"Below Lava Lake": {"Lava Lake"},
|
||||
"Dark Cave": {"Ancient Laboratory", "Collapsed Mines"},
|
||||
"Ancient Laboratory": {"Dark Cave"},
|
||||
|
||||
###
|
||||
"Coal Pits Holy Mountain": {"Coal Pits"},
|
||||
"Coal Pits": {"Coal Pits Holy Mountain", "Fungal Caverns", "Snowy Depths Holy Mountain"},
|
||||
"Fungal Caverns": {"Coal Pits"},
|
||||
|
||||
###
|
||||
"Snowy Depths Holy Mountain": {"Snowy Depths"},
|
||||
"Snowy Depths": {"Snowy Depths Holy Mountain", "Hiisi Base Holy Mountain", "Magical Temple"},
|
||||
"Magical Temple": {"Snowy Depths"},
|
||||
|
||||
###
|
||||
"Hiisi Base Holy Mountain": {"Hiisi Base"},
|
||||
"Hiisi Base": {"Hiisi Base Holy Mountain", "Secret Shop", "Pyramid", "Underground Jungle Holy Mountain"},
|
||||
"Secret Shop": {"Hiisi Base"},
|
||||
|
||||
###
|
||||
"Underground Jungle Holy Mountain": {"Underground Jungle"},
|
||||
"Underground Jungle": {"Underground Jungle Holy Mountain", "Dragoncave", "Overgrown Cavern", "Vault Holy Mountain",
|
||||
"Lukki Lair"},
|
||||
"Dragoncave": {"Underground Jungle"},
|
||||
"Lukki Lair": {"Underground Jungle", "Snow Chasm", "Frozen Vault"},
|
||||
"Snow Chasm": {},
|
||||
|
||||
###
|
||||
"Vault Holy Mountain": {"The Vault"},
|
||||
"The Vault": {"Vault Holy Mountain", "Frozen Vault", "Temple of the Art Holy Mountain"},
|
||||
|
||||
###
|
||||
"Temple of the Art Holy Mountain": {"Temple of the Art"},
|
||||
"Temple of the Art": {"Temple of the Art Holy Mountain", "Laboratory Holy Mountain", "The Tower",
|
||||
"Wizard's Den"},
|
||||
"Wizard's Den": {"Temple of the Art", "Powerplant"},
|
||||
"Powerplant": {"Wizard's Den", "Deep Underground"},
|
||||
"The Tower": {"Forest"},
|
||||
"Deep Underground": {},
|
||||
|
||||
###
|
||||
"Laboratory Holy Mountain": {"The Laboratory"},
|
||||
"The Laboratory": {"Laboratory Holy Mountain", "The Work", "Friend Cave", "The Work (Hell)"},
|
||||
"Friend Cave": {},
|
||||
"The Work": {},
|
||||
"The Work (Hell)": {},
|
||||
###
|
||||
}
|
||||
|
||||
noita_regions: Set[str] = set(noita_connections.keys()).union(*noita_connections.values())
|
|
@ -0,0 +1,153 @@
|
|||
from typing import List, NamedTuple, Set
|
||||
|
||||
from BaseClasses import CollectionState, MultiWorld
|
||||
from . import Items, Locations
|
||||
from .Options import BossesAsChecks, VictoryCondition
|
||||
from worlds.generic import Rules as GenericRules
|
||||
|
||||
|
||||
class EntranceLock(NamedTuple):
|
||||
source: str
|
||||
destination: str
|
||||
event: str
|
||||
items_needed: int
|
||||
|
||||
|
||||
entrance_locks: List[EntranceLock] = [
|
||||
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("Snowy Depths", "Hiisi Base Holy Mountain", "Portal to Holy Mountain 3", 3),
|
||||
EntranceLock("Hiisi Base", "Underground Jungle Holy Mountain", "Portal to Holy Mountain 4", 4),
|
||||
EntranceLock("Underground Jungle", "Vault Holy Mountain", "Portal to Holy Mountain 5", 5),
|
||||
EntranceLock("The Vault", "Temple of the Art Holy Mountain", "Portal to Holy Mountain 6", 6),
|
||||
EntranceLock("Temple of the Art", "Laboratory Holy Mountain", "Portal to Holy Mountain 7", 7),
|
||||
]
|
||||
|
||||
|
||||
holy_mountain_regions: List[str] = [
|
||||
"Coal Pits Holy Mountain",
|
||||
"Snowy Depths Holy Mountain",
|
||||
"Hiisi Base Holy Mountain",
|
||||
"Underground Jungle Holy Mountain",
|
||||
"Vault Holy Mountain",
|
||||
"Temple of the Art Holy Mountain",
|
||||
"Laboratory Holy Mountain",
|
||||
]
|
||||
|
||||
|
||||
wand_tiers: List[str] = [
|
||||
"Wand (Tier 1)", # Coal Pits
|
||||
"Wand (Tier 2)", # Snowy Depths
|
||||
"Wand (Tier 3)", # Hiisi Base
|
||||
"Wand (Tier 4)", # Underground Jungle
|
||||
"Wand (Tier 5)", # The Vault
|
||||
"Wand (Tier 6)", # Temple of the Art
|
||||
]
|
||||
|
||||
|
||||
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",
|
||||
"Powder Pouch"}
|
||||
|
||||
|
||||
perk_list: List[str] = list(filter(Items.item_is_perk, Items.item_table.keys()))
|
||||
|
||||
|
||||
# ----------------
|
||||
# Helper Functions
|
||||
# ----------------
|
||||
|
||||
|
||||
def has_perk_count(state: CollectionState, player: int, amount: int) -> bool:
|
||||
return sum(state.item_count(perk, player) for perk in perk_list) >= amount
|
||||
|
||||
|
||||
def has_orb_count(state: CollectionState, player: int, amount: int) -> bool:
|
||||
return state.item_count("Orb", player) >= amount
|
||||
|
||||
|
||||
def forbid_items_at_location(multiworld: MultiWorld, location_name: str, items: Set[str], player: int):
|
||||
location = multiworld.get_location(location_name, player)
|
||||
GenericRules.forbid_items_for_player(location, items, player)
|
||||
|
||||
|
||||
# ----------------
|
||||
# Rule Functions
|
||||
# ----------------
|
||||
|
||||
|
||||
# Prevent gold and potions from appearing as purchasable items in shops (because physics will destroy them)
|
||||
def ban_items_from_shops(multiworld: MultiWorld, player: int) -> None:
|
||||
for location_name in Locations.location_name_to_id.keys():
|
||||
if "Shop Item" in location_name:
|
||||
forbid_items_at_location(multiworld, location_name, items_hidden_from_shops, player)
|
||||
|
||||
|
||||
# Prevent high tier wands from appearing in early Holy Mountain shops
|
||||
def ban_early_high_tier_wands(multiworld: MultiWorld, player: int) -> None:
|
||||
for i, region_name in enumerate(holy_mountain_regions):
|
||||
wands_to_forbid = wand_tiers[i+1:]
|
||||
|
||||
locations_in_region = Locations.location_region_mapping[region_name].keys()
|
||||
for location_name in locations_in_region:
|
||||
forbid_items_at_location(multiworld, location_name, wands_to_forbid, player)
|
||||
|
||||
# Prevent high tier wands from appearing in the Secret shop
|
||||
wands_to_forbid = wand_tiers[3:]
|
||||
locations_in_region = Locations.location_region_mapping["Secret Shop"].keys()
|
||||
for location_name in locations_in_region:
|
||||
forbid_items_at_location(multiworld, location_name, wands_to_forbid, player)
|
||||
|
||||
|
||||
def lock_holy_mountains_into_spheres(multiworld: MultiWorld, player: int) -> None:
|
||||
for lock in entrance_locks:
|
||||
location = multiworld.get_entrance(f"From {lock.source} To {lock.destination}", player)
|
||||
GenericRules.set_rule(location, lambda state, evt=lock.event: state.has(evt, player))
|
||||
|
||||
|
||||
def holy_mountain_unlock_conditions(multiworld: MultiWorld, player: int) -> None:
|
||||
victory_condition = multiworld.victory_condition[player].value
|
||||
for lock in entrance_locks:
|
||||
location = multiworld.get_location(lock.event, player)
|
||||
|
||||
if victory_condition == VictoryCondition.option_greed_ending:
|
||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||
has_perk_count(state, player, items_needed//2)
|
||||
)
|
||||
elif victory_condition == VictoryCondition.option_pure_ending:
|
||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||
has_perk_count(state, player, items_needed//2) and
|
||||
has_orb_count(state, player, items_needed)
|
||||
)
|
||||
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
||||
location.access_rule = lambda state, items_needed=lock.items_needed: (
|
||||
has_perk_count(state, player, items_needed//2) and
|
||||
has_orb_count(state, player, items_needed * 3)
|
||||
)
|
||||
|
||||
|
||||
def victory_unlock_conditions(multiworld: MultiWorld, player: int) -> None:
|
||||
victory_condition = multiworld.victory_condition[player].value
|
||||
victory_location = multiworld.get_location("Victory", player)
|
||||
|
||||
if victory_condition == VictoryCondition.option_pure_ending:
|
||||
victory_location.access_rule = lambda state: has_orb_count(state, player, 11)
|
||||
elif victory_condition == VictoryCondition.option_peaceful_ending:
|
||||
victory_location.access_rule = lambda state: has_orb_count(state, player, 33)
|
||||
|
||||
|
||||
# ----------------
|
||||
# Main Function
|
||||
# ----------------
|
||||
|
||||
|
||||
def create_all_rules(multiworld: MultiWorld, player: int) -> None:
|
||||
ban_items_from_shops(multiworld, player)
|
||||
ban_early_high_tier_wands(multiworld, player)
|
||||
lock_holy_mountains_into_spheres(multiworld, player)
|
||||
holy_mountain_unlock_conditions(multiworld, player)
|
||||
victory_unlock_conditions(multiworld, player)
|
||||
|
||||
# Prevent the Map perk (used to find Toveri) from being on Toveri (boss)
|
||||
if multiworld.bosses_as_checks[player].value >= BossesAsChecks.option_all_bosses:
|
||||
forbid_items_at_location(multiworld, "Toveri", {"Spatial Awareness Perk"}, player)
|
|
@ -0,0 +1,55 @@
|
|||
from BaseClasses import Item, Tutorial
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from . import Events, Items, Locations, Options, Regions, Rules
|
||||
|
||||
|
||||
class NoitaWeb(WebWorld):
|
||||
tutorials = [Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Noita integration for Archipelago multiworld games.",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
"setup/en",
|
||||
["Heinermann", "ScipioWright", "DaftBrit"]
|
||||
)]
|
||||
theme = "partyTime"
|
||||
bug_report_page = "https://github.com/DaftBrit/NoitaArchipelago/issues"
|
||||
|
||||
|
||||
# Keeping World slim so that it's easier to comprehend
|
||||
class NoitaWorld(World):
|
||||
"""
|
||||
Noita is a magical action roguelite set in a world where every pixel is physically simulated. Fight, explore, melt,
|
||||
burn, freeze, and evaporate your way through the procedurally generated world using wands you've created yourself.
|
||||
"""
|
||||
|
||||
game = "Noita"
|
||||
option_definitions = Options.noita_options
|
||||
|
||||
item_name_to_id = Items.item_name_to_id
|
||||
location_name_to_id = Locations.location_name_to_id
|
||||
|
||||
item_name_groups = Items.item_name_groups
|
||||
data_version = 1
|
||||
|
||||
web = NoitaWeb()
|
||||
|
||||
# Returned items will be sent over to the client
|
||||
def fill_slot_data(self):
|
||||
return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions}
|
||||
|
||||
def create_regions(self) -> None:
|
||||
Regions.create_all_regions_and_connections(self.multiworld, self.player)
|
||||
Events.create_all_events(self.multiworld, self.player)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return Items.create_item(self.player, name)
|
||||
|
||||
def create_items(self) -> None:
|
||||
Items.create_all_items(self.multiworld, self.player)
|
||||
|
||||
def set_rules(self) -> None:
|
||||
Rules.create_all_rules(self.multiworld, self.player)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.multiworld.random.choice(Items.filler_items)
|
|
@ -0,0 +1,63 @@
|
|||
# Noita
|
||||
|
||||
## Where is the settings page?
|
||||
|
||||
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
|
||||
config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
Noita is a procedurally generated action roguelite. During runs in Noita you will find potions, wands, spells, perks,
|
||||
pickups, and chests. Shop items, chests/hearts hidden in the environment, and pedestal items will be replaced with
|
||||
location checks. Orbs and boss drops will optionally give location checks as well, if they are enabled in the settings.
|
||||
Noita items that can be found in other players' games include specific perks, orbs (optional), wands,
|
||||
hearts (Extra Max Health), gold, potions, and other items. If traps are enabled, some randomized negative effects can
|
||||
affect your game when found.
|
||||
|
||||
## What is the goal of Noita?
|
||||
|
||||
The vanilla goal of Noita is to progress through each level and beat the final boss, taking the Sampo
|
||||
(gear shaped object) through the portal, and interacting with the altar at the end. There are other endings as well
|
||||
which require you to gather a certain number of orbs and bring the sampo to an alternate altar.
|
||||
The Archipelago implementation maintains the same goals. While creating your YAML, you will choose what your goal will
|
||||
be. While the sampo's location is not randomized, orbs are added to the randomizer pool based on the number of orbs
|
||||
required for your goal.
|
||||
|
||||
Starting a fresh run after death will re-deliver *some* previously delivered items. The standard wand, potion, and perk
|
||||
pool are unaffected by the multiworld item pools. This will not present an issue with progression, and will make
|
||||
progression easier as the multiworld progresses.
|
||||
|
||||
## What Noita items can appear in other players' worlds?
|
||||
|
||||
Positive rewards can be:
|
||||
|
||||
* `Gold (200 or 1000)`
|
||||
* `Extra Max HP`
|
||||
* `Spell Refresher`
|
||||
* `Random Wand (Tier 1 - 6)`
|
||||
* `Potion`
|
||||
* `Orb`
|
||||
* `Immunity Perk`
|
||||
* `Extra Life`
|
||||
* `Other Helpful Perks`
|
||||
* `Miscellaneous Other Items`
|
||||
|
||||
Traps consist of all "Bad" and "Awful" events from Noita's native stream integration. Examples include:
|
||||
|
||||
* `Slow Player`
|
||||
* `Trailing Lava`
|
||||
* `Worm Rain`
|
||||
* `Spawning black holes`
|
||||
|
||||
### How many items are there?
|
||||
|
||||
The number of items is dependent on the settings you choose. Please check the information boxes next to the settings
|
||||
when setting up your YAML for more information.
|
||||
|
||||
## What does another world's item look like in Noita?
|
||||
|
||||
Other players' items will look like the Archipelago logo.
|
||||
|
||||
## Is Archipelago compatible with other Noita mods?
|
||||
|
||||
Yes, most other Noita mods *should* work. However, they have not been tested.
|
|
@ -0,0 +1,44 @@
|
|||
# Noita Setup Guide
|
||||
|
||||
## Installation
|
||||
|
||||
### Game
|
||||
|
||||
Go through the standard installation process for [Noita](https://noitagame.com/) on any of its supported platforms.
|
||||
|
||||
### Install Archipelago Mod
|
||||
|
||||
Download the Archipelago mod zip from the GitHub page:
|
||||
|
||||
[Archipelago Mod Download](https://github.com/DaftBrit/NoitaArchipelago/releases/latest)
|
||||
|
||||
Firstly, go to your Noita installation directory.
|
||||
|
||||
* **On Steam:** Find **Noita** in your Steam library. Right click, select *Manage* → *Browse local files*.
|
||||
* **On GOG Galaxy:** Find **Noita** in your Installed Games library. Right click, select *Manage installation* →
|
||||
*Show folder*.
|
||||
|
||||
Here you should see your game files and a folder called `mods`. Create a folder called `archipelago` and place all files
|
||||
from within the zip folder directly into the `archipelago` folder. After starting Noita, select the *Mods* menu. Here
|
||||
you should see the *Archipelago* mod listed.
|
||||
|
||||
In order to enable the mod you will first need to toggle **Unsafe mods** from *Disabled* to *Allowed*. This is required,
|
||||
as some external libraries are used by the mod in order to communicate with the Archipelago server. Once that is done,
|
||||
you can now enable the *Archipelago* mod (it should have an `[x]` next to it).
|
||||
|
||||
### Configure Archipelago Mod
|
||||
|
||||
In the Options menu, select Mod Settings. Under the Archipelago drop down, you will see the options for *Server*,
|
||||
*Port*, and *Slot*, where you can fill in the relevant information.
|
||||
|
||||
Once you start a new run in Noita, you should see "Connected to Archipelago server" in the bottom left of the screen. If
|
||||
you do not see this message, ensure that the mod is enabled and installed per the instructions above.
|
||||
|
||||
## Configuring your YAML File
|
||||
### What is a YAML and why do I need one?
|
||||
You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) here on the Archipelago website to learn
|
||||
about why Archipelago uses YAML files and what they're for.
|
||||
|
||||
### Where do I get a YAML?
|
||||
You can use the [game settings page for Noita](/games/Noita/player-settings) here on the Archipelago website to
|
||||
generate a YAML using a graphical interface.
|
Loading…
Reference in New Issue