Files
Grinch-AP/worlds/grinch/__init__.py

139 lines
6.1 KiB
Python
Raw Normal View History

2025-11-13 21:12:43 -07:00
import math
from BaseClasses import Region, Item, ItemClassification
from .Locations import grinch_locations_to_id, grinch_locations, GrinchLocation, get_location_names_per_category
2025-11-13 21:12:43 -07:00
from .Items import (
grinch_items_to_id,
GrinchItem,
ALL_ITEMS_TABLE,
MISC_ITEMS_TABLE,
get_item_names_per_category,
TRAPS_TABLE,
)
from .Regions import connect_regions
from .Rules import set_location_rules
2025-08-06 23:34:52 -04:00
from .Client import *
2025-08-25 19:48:45 -04:00
from typing import ClassVar
2025-07-25 19:33:51 -04:00
from worlds.AutoWorld import World
2025-10-03 16:40:20 -04:00
from Options import OptionError
2025-07-25 19:33:51 -04:00
from .Options import GrinchOptions
from .Rules import access_rules_dict
2025-07-25 19:33:51 -04:00
class GrinchWorld(World):
game: ClassVar[str] = "The Grinch"
options_dataclass = Options.GrinchOptions
options: Options.GrinchOptions
2025-11-13 21:12:43 -07:00
topology_present = True # not an open world game, very linear
item_name_to_id: ClassVar[dict[str, int]] = grinch_items_to_id()
location_name_to_id: ClassVar[dict[str, int]] = grinch_locations_to_id()
required_client_version = (0, 6, 3)
item_name_groups = get_item_names_per_category()
location_name_groups = get_location_names_per_category()
2025-11-13 21:12:43 -07:00
def __init__(self, *args, **kwargs): # Pulls __init__ function and takes control from there in BaseClasses.py
self.origin_region_name: str = "Mount Crumpit"
super(GrinchWorld, self).__init__(*args, **kwargs)
2025-11-13 21:12:43 -07:00
def generate_early(self) -> None: # Special conditions changed before generation occurs
2025-09-09 00:04:09 -04:00
if self.options.ring_link == 1 and self.options.unlimited_eggs == 1:
2025-11-13 21:12:43 -07:00
raise OptionError(
"Cannot enable both unlimited rotten eggs and ring links. You can only enable one of these at a time."
+ f"The following player's YAML needs to be fixed: {self.player_name}"
)
2025-09-09 00:04:09 -04:00
2025-11-13 21:12:43 -07:00
def create_regions(self): # Generates all regions for the multiworld
for region_name in access_rules_dict.keys():
self.multiworld.regions.append(Region(region_name, self.player, self.multiworld))
self.multiworld.regions.append(Region("Mount Crumpit", self.player, self.multiworld))
for location, data in grinch_locations.items():
region = self.get_region(data.region)
entry = GrinchLocation(self.player, location, region, data)
2025-09-20 21:41:40 -04:00
if location == "MC - Sleigh Ride - Neutralizing Santa":
entry.place_locked_item(Item("Goal", ItemClassification.progression, None, self.player))
region.locations.append(entry)
connect_regions(self)
2025-11-13 21:12:43 -07:00
def create_item(self, item: str) -> GrinchItem: # Creates specific items on demand
if item in ALL_ITEMS_TABLE.keys():
return GrinchItem(item, self.player, ALL_ITEMS_TABLE[item])
raise Exception(f"Invalid item name: {item}")
2025-11-13 21:12:43 -07:00
def create_items(self): # Generates all items for the multiworld
self_itempool: list[GrinchItem] = []
for item, data in ALL_ITEMS_TABLE.items():
self_itempool.append(self.create_item(item))
if item == "Heart of Stone":
for _ in range(3):
self_itempool.append(self.create_item(item))
2025-07-28 23:12:47 -04:00
2025-11-13 21:12:43 -07:00
<<<<<<< HEAD
2025-07-28 23:12:47 -04:00
#Get number of current unfilled locations
2025-11-13 23:07:32 -05:00
unfilled_locations: int = (
len(self.multiworld.get_unfilled_locations(self.player)) - len(self_itempool)
)
2025-11-13 21:12:43 -07:00
=======
# Get number of current unfilled locations
unfilled_locations: int = (
len(self.multiworld.get_unfilled_locations(self.player)) - len(ALL_ITEMS_TABLE.keys()) - 3
)
filler_locations: int = math.floor(unfilled_locations * (1 - (self.options.trap_percentage / 100)))
trap_locations: int = math.floor(unfilled_locations * (self.options.trap_percentage / 100))
# This catches the extra 1 or 2 unfilled_locations that come up from the math.floor()
extra_locations = unfilled_locations - (filler_locations + trap_locations)
filler_locations != extra_locations
>>>>>>> 31333183 (Adding weights for filler and traps)
2025-07-28 23:12:47 -04:00
2025-11-05 19:45:50 -05:00
# Total available weight sum
total_fillerweights = sum(self.options.filler_weight[filler] for filler in MISC_ITEMS_TABLE)
# Fill remaining locations according to weight ratio
for filler in MISC_ITEMS_TABLE:
2025-11-13 21:12:43 -07:00
# This ratio is a decimal between 0 and 1, and when multiplied by 100 is the % of that filler
# item in the available unfilled locations
filler_weight_ratio = self.options.filler_weight[filler] / total_fillerweights
2025-11-13 21:12:43 -07:00
filler_count = round(filler_locations * filler_weight_ratio)
2025-11-05 19:45:50 -05:00
for _ in range(filler_count):
self_itempool.append(self.create_item(filler))
2025-11-13 21:12:43 -07:00
# # Make sure we don't underfill (in case of rounding losses)
# while len(self_itempool) < unfilled_locations:
# self_itempool.append(self.create_item(self.get_other_filler_item(list(MISC_ITEMS_TABLE.keys()))))
2025-11-05 19:45:50 -05:00
# Total available weight sum
if self.options.trap_percentage > 0:
total_trapweights = sum(self.options.trap_weight[trap] for trap in TRAPS_TABLE)
2025-11-13 21:12:43 -07:00
if self.options.trap_percentage > 0 && total_trapweights <= 0:
raise Exception("ERROR: Traps are enabled, but all trap weights are zero or undefined")
for trap in TRAPS_TABLE:
trap_weight_ratio = self.options.trap_weight[trap] / total_trapweights
2025-11-13 21:12:43 -07:00
trap_count = round(trap_locations * trap_weight_ratio)
for _ in range(trap_count):
self_itempool.append(self.create_item(trap))
self.multiworld.itempool += self_itempool
def set_rules(self):
self.multiworld.completion_condition[self.player] = lambda state: state.has("Goal", self.player)
set_location_rules(self)
2025-07-28 23:12:47 -04:00
def get_other_filler_item(self, other_filler: list[str]) -> str:
return self.random.choices(other_filler)[0]
def fill_slot_data(self):
return {
"give_unlimited_eggs": self.options.unlimited_eggs.value,
"ring_link": self.options.ring_link.value,
}
def generate_output(self, output_directory: str) -> None:
# print("")
2025-11-13 21:12:43 -07:00
pass