157 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			157 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | 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) | ||
|  | } |