Skip to content

Commit

Permalink
Noita: Update to use new Options API (#2370)
Browse files Browse the repository at this point in the history
Reworking the options to make it work with the new options API.
Also reworked stuff in several spots to use world: NoitaWorld instead of multiworld: MultiWorld
  • Loading branch information
ScipioWright authored Jan 19, 2024
1 parent 1307754 commit 5f9ce2b
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 301 deletions.
42 changes: 0 additions & 42 deletions worlds/noita/Events.py

This file was deleted.

166 changes: 0 additions & 166 deletions worlds/noita/Rules.py

This file was deleted.

31 changes: 17 additions & 14 deletions worlds/noita/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from BaseClasses import Item, Tutorial
from worlds.AutoWorld import WebWorld, World
from . import Events, Items, Locations, Options, Regions, Rules
from typing import Dict, Any
from . import events, items, locations, regions, rules
from .options import NoitaOptions


class NoitaWeb(WebWorld):
Expand All @@ -24,13 +26,14 @@ class NoitaWorld(World):
"""

game = "Noita"
option_definitions = Options.noita_options
options: NoitaOptions
options_dataclass = NoitaOptions

item_name_to_id = Items.item_name_to_id
location_name_to_id = Locations.location_name_to_id
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
location_name_groups = Locations.location_name_groups
item_name_groups = items.item_name_groups
location_name_groups = locations.location_name_groups
data_version = 2

web = NoitaWeb()
Expand All @@ -40,21 +43,21 @@ def generate_early(self):
raise Exception("Noita yaml's slot name has invalid character(s).")

# 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 fill_slot_data(self) -> Dict[str, Any]:
return self.options.as_dict("death_link", "victory_condition", "path_option", "hidden_chests",
"pedestal_checks", "orbs_as_checks", "bosses_as_checks", "extra_orbs", "shop_price")

def create_regions(self) -> None:
Regions.create_all_regions_and_connections(self.multiworld, self.player)
Events.create_all_events(self.multiworld, self.player)
regions.create_all_regions_and_connections(self)

def create_item(self, name: str) -> Item:
return Items.create_item(self.player, name)
return items.create_item(self.player, name)

def create_items(self) -> None:
Items.create_all_items(self.multiworld, self.player)
items.create_all_items(self)

def set_rules(self) -> None:
Rules.create_all_rules(self.multiworld, self.player)
rules.create_all_rules(self)

def get_filler_item_name(self) -> str:
return self.multiworld.random.choice(Items.filler_items)
return self.random.choice(items.filler_items)
43 changes: 43 additions & 0 deletions worlds/noita/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Dict, TYPE_CHECKING
from BaseClasses import Item, ItemClassification, Location, Region
from . import items, locations

if TYPE_CHECKING:
from . import NoitaWorld


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(player: int, region: Region, item: str) -> Location:
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(world: "NoitaWorld", created_regions: Dict[str, Region]) -> None:
for region_name, event in event_locks.items():
region = created_regions[region_name]
create_locked_location_event(world.player, region, event)

world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.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",
}
44 changes: 25 additions & 19 deletions worlds/noita/Items.py → worlds/noita/items.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import itertools
from collections import Counter
from typing import Dict, List, NamedTuple, Set
from typing import Dict, List, NamedTuple, Set, TYPE_CHECKING

from BaseClasses import Item, ItemClassification, MultiWorld
from .Options import BossesAsChecks, VictoryCondition, ExtraOrbs
from BaseClasses import Item, ItemClassification
from .options import BossesAsChecks, VictoryCondition, ExtraOrbs

if TYPE_CHECKING:
from . import NoitaWorld
else:
NoitaWorld = object


class ItemData(NamedTuple):
Expand Down Expand Up @@ -44,39 +49,40 @@ 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, 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()
if multiworld.bad_effects[player].value == 0:
if not world.options.bad_effects:
del filler_pool["Trap"]

return multiworld.random.choices(population=list(filler_pool.keys()),
weights=list(filler_pool.values()),
k=count)
return world.random.choices(population=list(filler_pool.keys()),
weights=list(filler_pool.values()),
k=count)


def create_all_items(multiworld: MultiWorld, player: int) -> None:
locations_to_fill = len(multiworld.get_unfilled_locations(player))
def create_all_items(world: NoitaWorld) -> None:
player = world.player
locations_to_fill = len(world.multiworld.get_unfilled_locations(player))

itempool = (
create_fixed_item_pool()
+ create_orb_items(multiworld.victory_condition[player], multiworld.extra_orbs[player])
+ create_spatial_awareness_item(multiworld.bosses_as_checks[player])
+ create_kantele(multiworld.victory_condition[player])
+ create_orb_items(world.options.victory_condition, world.options.extra_orbs)
+ create_spatial_awareness_item(world.options.bosses_as_checks)
+ create_kantele(world.options.victory_condition)
)

# if there's not enough shop-allowed items in the pool, we can encounter gen issues
# 39 is the number of shop-valid items we need to guarantee
if len(itempool) < 39:
itempool += create_random_items(multiworld, player, shop_only_filler_weights, 39 - len(itempool))
itempool += create_random_items(world, shop_only_filler_weights, 39 - len(itempool))
# this is so that it passes tests and gens if you have minimal locations and only one player
if multiworld.players == 1:
for location in multiworld.get_unfilled_locations(player):
if world.multiworld.players == 1:
for location in world.multiworld.get_unfilled_locations(player):
if "Shop Item" in location.name:
location.item = create_item(player, itempool.pop())
locations_to_fill = len(multiworld.get_unfilled_locations(player))
locations_to_fill = len(world.multiworld.get_unfilled_locations(player))

itempool += create_random_items(multiworld, player, filler_weights, locations_to_fill - len(itempool))
multiworld.itempool += [create_item(player, name) for name in itempool]
itempool += create_random_items(world, filler_weights, locations_to_fill - len(itempool))
world.multiworld.itempool += [create_item(player, name) for name in itempool]


# 110000 - 110032
Expand Down
Loading

0 comments on commit 5f9ce2b

Please sign in to comment.