69 Commits

Author SHA1 Message Date
MarioSpore
bccc83f864 Fixed Exhaust Pipes not being logically required for WV 2025-10-03 23:07:15 -04:00
SomeJakeGuy
6409721841 Fixed an issue where RingLink could disconnect because BizHawk is paused, so moved it to gamewatcher to be started there instead. 2025-10-03 22:23:16 -04:00
MarioSpore
d3a7b014bd Added gadget_rando & some description adjustments 2025-10-03 16:50:06 -04:00
MarioSpore
3ec8631203 Adjustments for move/gadget rando options 2025-10-03 16:44:34 -04:00
MarioSpore
2081912a39 Fixed Exception error 2025-10-03 16:40:20 -04:00
MarioSpore
67bbde2556 Log for sending locations now retriggers when exiting out of the game 2025-10-02 20:47:32 -04:00
MarioSpore
0503c2ead3 Extra space for newly added initial_cutscene_checker 2025-10-02 20:12:21 -04:00
MarioSpore
7a642cc1a9 Add ram address for additional checking in demo mode checking 2025-10-02 16:27:45 -04:00
MarioSpore
eb8d44e975 Update setup_en.md 2025-10-01 16:44:24 -04:00
MarioSpore
1f35f5fa93 Update setup_en.md 2025-10-01 15:26:16 -04:00
MarioSpore
6bc819f4bc Removed test loggers that was for testing 2025-09-30 01:32:12 -04:00
MarioSpore
bb0c5f5b9a Fixed "WL - South Shore - First Visit" location not generating due to missed space 2025-09-30 01:14:31 -04:00
MarioSpore
0e397c7079 Forgot PC for abbreviation in rules 2025-09-30 00:59:38 -04:00
MarioSpore
2572a25089 Abbreviation of items part 4 w/ rules adjusted with new abbreviations 2025-09-30 00:58:59 -04:00
MarioSpore
dcdf168618 Abbreviation of items part 3 2025-09-30 00:52:38 -04:00
MarioSpore
8a8136a267 Add address for whoville vacuum tube! 2025-09-30 00:50:14 -04:00
MarioSpore
5f158497f9 more inital names added to list 2025-09-29 18:56:02 -04:00
MarioSpore
bc74e67e07 Moverando item renames pt 2 2025-09-29 18:10:18 -04:00
MarioSpore
dbc592dad0 Moverando item renames 2025-09-29 18:05:14 -04:00
MarioSpore
afe1345e34 Added a whole bunch of location groups 2025-09-27 23:09:13 -04:00
MarioSpore
410df2a948 Redone options by various name changes and grammatical changes 2025-09-27 16:11:10 -04:00
MarioSpore
7c6eada7b2 Renamed movesanity to move_rando 2025-09-27 12:15:04 -04:00
MarioSpore
8e1217d1a5 Fixed small issue with location groups & ram watching with goal 2025-09-26 21:40:52 -04:00
MarioSpore
5615277705 Forgot to put Who Dump Vacuum Tube as part of a list for location groups 2025-09-26 18:52:46 -04:00
MarioSpore
2de0f9d766 Crazy I figured this out. Location groups fully works 2025-09-26 18:09:18 -04:00
MarioSpore
4da88cf794 Yeah Jake is right. The ClassVar code is not needed. 2025-09-26 18:05:20 -04:00
MarioSpore
e2cc1b5de7 Possibly implemented location groups? 2025-09-26 17:11:48 -04:00
MarioSpore
103a6f79d1 Maybe fix for compensating for multiple item groups in a list? 2025-09-26 16:52:57 -04:00
MarioSpore
17861c1050 Add abbreviations to various items for ease of typing 2025-09-24 18:56:57 -04:00
MarioSpore
a63c33a711 Added list[str] for location groups for inevitable location group implementation 2025-09-22 23:25:40 -04:00
MarioSpore
c84ef117c8 Removed arbitrary second_item_group variable when we start to implement multiple item groups 2025-09-22 23:24:27 -04:00
MarioSpore
834096f282 Fixed item groups 2025-09-22 23:23:41 -04:00
MarioSpore
0a31d96ee4 Make grinch goal earlier by triggering goal when "Ending (Santa costume)" region is loaded 2025-09-22 21:47:42 -04:00
MarioSpore
3edb733dcb Ringlink toggle command implement 2025-09-22 21:38:06 -04:00
MarioSpore
9c01cc31e0 Found address that handles when grinch is wearing a disguise. Sets address to 0 when "Dump it to Crumpit" is activated and as a failsafe if he is wearing one or not. 2025-09-22 20:36:48 -04:00
MarioSpore
ea8262855e Abbreviated regions & blueprints 2025-09-20 21:41:40 -04:00
MarioSpore
98ea11887e Added Artamiss to credits 2025-09-20 17:21:02 -04:00
MarioSpore
18aefcd3f2 Changed "Access" to "Tube" for planned Progressive vacuum implementation 2025-09-20 15:49:43 -04:00
MarioSpore
3a13332533 Renamed description from "Access" to "Tubes" 2025-09-20 15:48:40 -04:00
MarioSpore
c279ef7bc6 - Major refactoring of location, region, and goal item names 2025-09-20 15:48:24 -04:00
MarioSpore
55ef0cc8c9 Damage is considered "exhaustion" according to the game manual 2025-09-18 22:15:07 -04:00
MarioSpore
babc4f441c Changed "Vacuum Access" to "Vacuum Tube" to reflect the game manual 2025-09-18 22:12:11 -04:00
MarioSpore
92d932da55 Psuedocode generic giftsanity logic 2025-09-14 17:47:03 -04:00
MarioSpore
b1b65a3adf Minor option tweaks 2025-09-14 00:17:32 -04:00
MarioSpore
d6f5e87ccf Option changes based on feedback along with typos and punctuation issues 2025-09-14 00:11:21 -04:00
MarioSpore
079239ea70 Minor tweaks in bios setup 2025-09-13 14:29:03 -04:00
MarioSpore
93bac29e8c Added section for requiring PSX bios and how to set it up 2025-09-13 14:24:55 -04:00
MarioSpore
c9fea29d92 Fixed item groups? 2025-09-13 13:38:08 -04:00
MarioSpore
e17895902e Add unique id to compensate for multiple grinch games in one multiworld 2025-09-13 00:58:12 -04:00
MarioSpore
1596550111 Merge pull request #2 from MarioSpore/devjake
Fixed previous fixes for address optimization.
2025-09-12 23:16:01 -04:00
SomeJakeGuy
c888e17845 Fixed previous fixes for address optimization. 2025-09-12 23:10:19 -04:00
MarioSpore
3dee611b51 Fixed double receive eggs to self 2025-09-12 22:22:54 -04:00
MarioSpore
d6c7a04316 Fixed negative int bug 2025-09-12 21:27:11 -04:00
MarioSpore
900c8a519a Fixed SADX/SA2B crashes? 2025-09-12 05:44:49 -04:00
MarioSpore
43acc9f003 Minefield logical access oversight 2025-09-11 19:35:42 -04:00
MarioSpore
96eb8fcd9a Fix ringlink output 2025-09-11 00:11:25 -04:00
MarioSpore
d4bd682ac9 Fix location send speed code 2025-09-10 23:19:36 -04:00
MarioSpore
61d4783f61 Minor setup doc tweak 2025-09-10 18:49:31 -04:00
MarioSpore
00fff466ff Updated setup docs that actually make more sense 2025-09-10 18:36:38 -04:00
MarioSpore
d8483bef6e Fixes client crash if the emulator is paused with ringlink enabled. Still won't be able to send out ringlink when this occurs 2025-09-09 23:09:56 -04:00
MarioSpore
56a198fcfd Vastly improved speed of remove_physical_items, constant_address_update, & receiving_items_handler 2025-09-09 22:06:07 -04:00
MarioSpore
4e362dc722 Fixed output for ring link to be in a loop 2025-09-09 21:01:56 -04:00
MarioSpore
cfcfc9ecfd Fixed ring link input 2025-09-09 17:44:04 -04:00
MarioSpore
a3a415adfd Merge pull request #1 from MarioSpore/jake_ring-link-fix
Fix some small issues with async on_package and changing things to be async starts instead
2025-09-09 05:46:14 -04:00
SomeJakeGuy
14c95aa85b Fix some small issues with async on_package and changing things to be async starts instead. 2025-09-09 01:29:32 -04:00
MarioSpore
8d941dad6f Adjust import of options to fix AttributeError for previous commit 2025-09-09 00:23:36 -04:00
MarioSpore
8628f6637a Fully implement ringlink 2025-09-09 00:04:09 -04:00
MarioSpore
17b7914c35 Now passing ring link option value to client 2025-09-08 22:48:43 -04:00
MarioSpore
b8dfd5ce4c Adds mount crumpit crate locations 2025-09-08 22:41:22 -04:00
9 changed files with 862 additions and 663 deletions

View File

@@ -1,8 +1,10 @@
import time import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Sequence
import asyncio import asyncio
import NetUtils import NetUtils
import copy import copy
import uuid
import Utils
from .Locations import grinch_locations, GrinchLocation from .Locations import grinch_locations, GrinchLocation
from .Items import ALL_ITEMS_TABLE, MISSION_ITEMS_TABLE, GADGETS_TABLE, KEYS_TABLE, GrinchItemData #, SLEIGH_PARTS_TABLE from .Items import ALL_ITEMS_TABLE, MISSION_ITEMS_TABLE, GADGETS_TABLE, KEYS_TABLE, GrinchItemData #, SLEIGH_PARTS_TABLE
import worlds._bizhawk as bizhawk import worlds._bizhawk as bizhawk
@@ -24,20 +26,29 @@ MAX_DEMO_MODE_CHECK = 30
# List of Menu Map IDs # List of Menu Map IDs
MENU_MAP_IDS: list[int] = [0x00, 0x02, 0x35, 0x36, 0x37] MENU_MAP_IDS: list[int] = [0x00, 0x02, 0x35, 0x36, 0x37]
MAX_EGGS: int = 200
EGG_COUNT_ADDR: int = 0x010058
EGG_ADDR_BYTESIZE: int = 2
class GrinchClient(BizHawkClient): class GrinchClient(BizHawkClient):
game = "The Grinch" game = "The Grinch"
system = "PSX" system = "PSX"
patch_suffix = ".apgrinch" patch_suffix = ".apgrinch"
items_handling = 0b111 items_handling = 0b111
demo_mode_buffer = 0 demo_mode_buffer: int = 0
last_map_location = -1 last_map_location: int = -1
ingame_log = False ingame_log: bool = False
previous_egg_count: int = 0
send_ring_link: bool = False
unique_client_id: int = 0
ring_link_enabled = False
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.last_received_index = 0 self.last_received_index = 0
self.loading_bios_msg = False self.loading_bios_msg = False
self.loc_unlimited_eggs = False self.loc_unlimited_eggs = False
self.unique_client_id = 0
async def validate_rom(self, ctx: "BizHawkClientContext") -> bool: async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
from CommonClient import logger from CommonClient import logger
@@ -60,13 +71,15 @@ class GrinchClient(BizHawkClient):
logger.error("Invalid rom detected. You are not playing Grinch USA Version.") logger.error("Invalid rom detected. You are not playing Grinch USA Version.")
raise Exception("Invalid rom detected. You are not playing Grinch USA Version.") raise Exception("Invalid rom detected. You are not playing Grinch USA Version.")
ctx.command_processor.commands["ringlink"] = _cmd_ringlink
except Exception: except Exception:
return False return False
ctx.game = self.game ctx.game = self.game
ctx.items_handling = self.items_handling ctx.items_handling = self.items_handling
ctx.want_slot_data = True ctx.want_slot_data = True
ctx.watcher_timeout = 0.25 ctx.watcher_timeout = 0.125
self.loading_bios_msg = False self.loading_bios_msg = False
return True return True
@@ -77,11 +90,27 @@ class GrinchClient(BizHawkClient):
match cmd: match cmd:
case "Connected": # On Connect case "Connected": # On Connect
self.loc_unlimited_eggs = bool(ctx.slot_data["give_unlimited_eggs"]) self.loc_unlimited_eggs = bool(ctx.slot_data["give_unlimited_eggs"])
self.unique_client_id = self._get_uuid()
logger.info("You are now connected to the client. "+ logger.info("You are now connected to the client. "+
"There may be a slight delay to check you are not in demo mode before locations start to send.") "There may be a slight delay to check you are not in demo mode before locations start to send.")
# tags = args.get("tags", [])
# if "RingLink" in tags: self.ring_link_enabled = bool(ctx.slot_data["ring_link"])
# ring_link_input(self, args["data"])
tags = copy.deepcopy(ctx.tags)
if self.ring_link_enabled:
ctx.tags.add("RingLink")
else:
ctx.tags -= { "RingLink" }
if tags != ctx.tags:
Utils.async_start(ctx.send_msgs([{"cmd": "ConnectUpdate", "tags": ctx.tags}]), "Update RingLink Tags")
case "Bounced":
if "tags" not in args:
return
if "RingLink" in ctx.tags and "RingLink" in args["tags"] and args["data"]["source"] != self.unique_client_id:
Utils.async_start(self.ring_link_input(args["data"]["amount"], ctx), "SyncEggs")
async def set_auth(self, ctx: "BizHawkClientContext") -> None: async def set_auth(self, ctx: "BizHawkClientContext") -> None:
await ctx.get_username() await ctx.get_username()
@@ -96,12 +125,16 @@ class GrinchClient(BizHawkClient):
if not await self.ingame_checker(ctx): if not await self.ingame_checker(ctx):
return return
if not any(task.get_name() == "Grinch EggLink" for task in asyncio.all_tasks()):
print("EggLink")
self.send_ring_link = True
Utils.async_start(self.ring_link_output(ctx), name="Grinch EggLink")
await self.location_checker(ctx) await self.location_checker(ctx)
await self.receiving_items_handler(ctx) await self.receiving_items_handler(ctx)
await self.goal_checker(ctx) await self.goal_checker(ctx)
await self.option_handler(ctx) await self.option_handler(ctx)
await self.constant_address_update(ctx) await self.constant_address_update(ctx)
# await self.ring_link_input(args["args"])
except bizhawk.RequestFailedError as ex: except bizhawk.RequestFailedError as ex:
# The connector didn't respond. Exit handler and return to main loop to reconnect # The connector didn't respond. Exit handler and return to main loop to reconnect
@@ -117,9 +150,22 @@ class GrinchClient(BizHawkClient):
from CommonClient import logger from CommonClient import logger
# Update the AP Server to know what locations are not checked yet. # Update the AP Server to know what locations are not checked yet.
local_locations_checked: list[int] = [] local_locations_checked: list[int] = []
addr_list_to_read: list[tuple[int, int, str]] = []
local_ap_locations: set[int] = copy.deepcopy(ctx.missing_locations) local_ap_locations: set[int] = copy.deepcopy(ctx.missing_locations)
# Loop through the first time of everything left to create the list of RAM addresses to read / monitor.
for missing_location in local_ap_locations:
grinch_loc_name = ctx.location_names.lookup_in_game(missing_location)
grinch_loc_ram_data = grinch_locations[grinch_loc_name]
missing_addr_list: list[tuple[int, int, str]] = [(read_addr.ram_address, read_addr.bit_size, "MainRAM") for
read_addr in grinch_loc_ram_data.update_ram_addr]
addr_list_to_read = [*addr_list_to_read, *missing_addr_list]
returned_bytes: list[bytes] = await bizhawk.read(ctx.bizhawk_ctx, addr_list_to_read)
# Now loop through everything again and this time get the byte value from the above read, convert to int,
# and check to see if that ram address has our expected value.
for missing_location in local_ap_locations: for missing_location in local_ap_locations:
# local_location = ctx.location_names.lookup_in_game(missing_location)
# Missing location is the AP ID & we need to convert it back to a location name within our game. # Missing location is the AP ID & we need to convert it back to a location name within our game.
# Using the location name, we can then get the Grinch ram data from there. # Using the location name, we can then get the Grinch ram data from there.
grinch_loc_name = ctx.location_names.lookup_in_game(missing_location) grinch_loc_name = ctx.location_names.lookup_in_game(missing_location)
@@ -130,13 +176,13 @@ class GrinchClient(BizHawkClient):
ram_checked_list: list[bool] = [] ram_checked_list: list[bool] = []
for addr_to_update in grinch_loc_ram_data.update_ram_addr: for addr_to_update in grinch_loc_ram_data.update_ram_addr:
is_binary = True if not addr_to_update.binary_bit_pos is None else False is_binary = True if not addr_to_update.binary_bit_pos is None else False
current_ram_address_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( orig_index: int = addr_list_to_read.index((addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM"))
addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little") value_read_from_bizhawk: int = int.from_bytes(returned_bytes[orig_index], "little")
if is_binary: if is_binary:
ram_checked_list.append((current_ram_address_value & (1 << addr_to_update.binary_bit_pos)) > 0) ram_checked_list.append((value_read_from_bizhawk & (1 << addr_to_update.binary_bit_pos)) > 0)
else: else:
expected_int_value = addr_to_update.value expected_int_value = addr_to_update.value
ram_checked_list.append(expected_int_value == current_ram_address_value) ram_checked_list.append(expected_int_value == value_read_from_bizhawk)
if all(ram_checked_list): if all(ram_checked_list):
local_locations_checked.append(GrinchLocation.get_apid(grinch_loc_ram_data.id)) local_locations_checked.append(GrinchLocation.get_apid(grinch_loc_ram_data.id))
@@ -155,9 +201,9 @@ class GrinchClient(BizHawkClient):
RECV_ITEM_ADDR, RECV_ITEM_BITSIZE, "MainRAM")]))[0], "little") RECV_ITEM_ADDR, RECV_ITEM_BITSIZE, "MainRAM")]))[0], "little")
if len(ctx.items_received) == self.last_received_index: if len(ctx.items_received) == self.last_received_index:
return return
# Ensures we only get the new items that we want to give the player # Ensures we only get the new items that we want to give the player
new_items_only = ctx.items_received[self.last_received_index:] new_items_only = ctx.items_received[self.last_received_index:]
ram_addr_dict: dict[int, list[int]] = {}
for item_received in new_items_only: for item_received in new_items_only:
local_item = ctx.item_names.lookup_in_game(item_received.item) local_item = ctx.item_names.lookup_in_game(item_received.item)
@@ -165,6 +211,9 @@ class GrinchClient(BizHawkClient):
for addr_to_update in grinch_item_ram_data.update_ram_addr: for addr_to_update in grinch_item_ram_data.update_ram_addr:
is_binary = True if not addr_to_update.binary_bit_pos is None else False is_binary = True if not addr_to_update.binary_bit_pos is None else False
if addr_to_update.ram_address in ram_addr_dict.keys():
current_ram_address_value = ram_addr_dict[addr_to_update.ram_address][0]
else:
current_ram_address_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( current_ram_address_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little") addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little")
if is_binary: if is_binary:
@@ -177,20 +226,22 @@ class GrinchClient(BizHawkClient):
current_ram_address_value = addr_to_update.value current_ram_address_value = addr_to_update.value
# Write the updated value back into RAM # Write the updated value back into RAM
await self.update_and_validate_address(ctx, addr_to_update.ram_address, current_ram_address_value, addr_to_update.bit_size) ram_addr_dict[addr_to_update.ram_address] = [current_ram_address_value, addr_to_update.bit_size]
# await bizhawk.write(ctx.bizhawk_ctx, [(addr_to_update.ram_address,
# current_ram_address_value.to_bytes(addr_to_update.bit_size, "little"), "MainRAM")])
self.last_received_index += 1 self.last_received_index += 1
await self.update_and_validate_address(ctx, RECV_ITEM_ADDR, self.last_received_index, RECV_ITEM_BITSIZE)
# Update the latest received item index to ram as well.
ram_addr_dict[RECV_ITEM_ADDR] = [self.last_received_index, RECV_ITEM_BITSIZE]
await bizhawk.write(ctx.bizhawk_ctx, self.convert_dict_to_ram_list(ram_addr_dict))
async def goal_checker(self, ctx: "BizHawkClientContext"): async def goal_checker(self, ctx: "BizHawkClientContext"):
if not ctx.finished_game: if not ctx.finished_game:
goal_loc = grinch_locations["Neutralizing Santa"] goal_loc = grinch_locations["MC - Sleigh Ride - Neutralizing Santa"]
goal_ram_address = goal_loc.update_ram_addr[0] goal_ram_address = goal_loc.update_ram_addr[0]
current_ram_address_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( current_ram_address_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
goal_ram_address.ram_address, goal_ram_address.bit_size, "MainRAM")]))[0], "little") goal_ram_address.ram_address, goal_ram_address.bit_size, "MainRAM")]))[0], "little")
if (current_ram_address_value & (1 << goal_ram_address.binary_bit_pos)) > 0: # if (current_ram_address_value & (1 << goal_ram_address.binary_bit_pos)) > 0:
if current_ram_address_value == goal_ram_address.value:
ctx.finished_game = True ctx.finished_game = True
await ctx.send_msgs([{ await ctx.send_msgs([{
"cmd": "StatusUpdate", "cmd": "StatusUpdate",
@@ -199,14 +250,16 @@ class GrinchClient(BizHawkClient):
# This function's entire purpose is to take away items we physically received ingame, but have not received from AP # This function's entire purpose is to take away items we physically received ingame, but have not received from AP
async def remove_physical_items(self, ctx: "BizHawkClientContext"): async def remove_physical_items(self, ctx: "BizHawkClientContext"):
ram_addr_dict: dict[int, list[int]] = {}
list_recv_itemids: list[int] = [netItem.item for netItem in ctx.items_received] list_recv_itemids: list[int] = [netItem.item for netItem in ctx.items_received]
items_to_check: dict[str, GrinchItemData] = {**GADGETS_TABLE} #, **SLEIGH_PARTS_TABLE items_to_check: dict[str, GrinchItemData] = {**GADGETS_TABLE} #, **SLEIGH_PARTS_TABLE
heart_count = len(list(item_id for item_id in list_recv_itemids if item_id == 42570)) heart_count = len(list(item_id for item_id in list_recv_itemids if item_id == 42570))
heart_item_data = ALL_ITEMS_TABLE["Heart of Stone"] heart_item_data = ALL_ITEMS_TABLE["Heart of Stone"]
await self.update_and_validate_address(ctx, heart_item_data.update_ram_addr[0].ram_address, min(heart_count, 4), 1) ram_addr_dict[heart_item_data.update_ram_addr[0].ram_address] = [min(heart_count, 4), 1]
# Setting Who Lake Mission Count back to 0 to prevent warping after completing 3 missions # Setting mission count for all accesses back to 0 to prevent warping/unlocking after completing 3 missions
await self.update_and_validate_address(ctx,0x0100F0, 0, 4) ram_addr_dict[0x0100F0] = [0, 4]
for (item_name, item_data) in items_to_check.items(): for (item_name, item_data) in items_to_check.items():
# If item is an event or already been received, ignore. # If item is an event or already been received, ignore.
@@ -217,15 +270,31 @@ class GrinchClient(BizHawkClient):
for addr_to_update in item_data.update_ram_addr: for addr_to_update in item_data.update_ram_addr:
is_binary = True if not addr_to_update.binary_bit_pos is None else False is_binary = True if not addr_to_update.binary_bit_pos is None else False
if is_binary: if is_binary:
if addr_to_update.ram_address in ram_addr_dict.keys():
current_bin_value = ram_addr_dict[addr_to_update.ram_address][0]
else:
current_bin_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( current_bin_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little") addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little")
current_bin_value &= ~(1 << addr_to_update.binary_bit_pos) current_bin_value &= ~(1 << addr_to_update.binary_bit_pos)
await self.update_and_validate_address(ctx, addr_to_update.ram_address, current_bin_value, 1) ram_addr_dict[addr_to_update.ram_address] = [current_bin_value, 1]
else: else:
await self.update_and_validate_address(ctx, addr_to_update.ram_address, 0, 1) ram_addr_dict[addr_to_update.ram_address] = [0, addr_to_update.bit_size]
await bizhawk.write(ctx.bizhawk_ctx, self.convert_dict_to_ram_list(ram_addr_dict))
def convert_dict_to_ram_list(self, addr_dict: dict[int, list[int]]) -> list[tuple[int, Sequence[int], str]]:
addr_list_to_update: list[tuple[int, Sequence[int], str]] = []
for (key, val) in addr_dict.items():
addr_list_to_update.append((key, val[0].to_bytes(val[1], "little"), "MainRAM"))
return addr_list_to_update
# Removes the regional access until you actually received it from AP. # Removes the regional access until you actually received it from AP.
async def constant_address_update(self, ctx: "BizHawkClientContext"): async def constant_address_update(self, ctx: "BizHawkClientContext"):
ram_addr_dict: dict[int, list[int]] = {}
list_recv_itemids: list[int] = [netItem.item for netItem in ctx.items_received] list_recv_itemids: list[int] = [netItem.item for netItem in ctx.items_received]
items_to_check: dict[str, GrinchItemData] = {**KEYS_TABLE, **MISSION_ITEMS_TABLE} items_to_check: dict[str, GrinchItemData] = {**KEYS_TABLE, **MISSION_ITEMS_TABLE}
@@ -238,27 +307,36 @@ class GrinchClient(BizHawkClient):
for addr_to_update in item_data.update_ram_addr: for addr_to_update in item_data.update_ram_addr:
is_binary = True if not addr_to_update.binary_bit_pos is None else False is_binary = True if not addr_to_update.binary_bit_pos is None else False
if is_binary: if is_binary:
if addr_to_update.ram_address in ram_addr_dict.keys():
current_bin_value = ram_addr_dict[addr_to_update.ram_address][0]
else:
current_bin_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( current_bin_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little") addr_to_update.ram_address, addr_to_update.bit_size, "MainRAM")]))[0], "little")
if GrinchLocation.get_apid(item_data.id) in list_recv_itemids: if GrinchLocation.get_apid(item_data.id) in list_recv_itemids:
current_bin_value |= (1 << addr_to_update.binary_bit_pos) current_bin_value |= (1 << addr_to_update.binary_bit_pos)
else: else:
current_bin_value &= ~(1 << addr_to_update.binary_bit_pos) current_bin_value &= ~(1 << addr_to_update.binary_bit_pos)
await self.update_and_validate_address(ctx, addr_to_update.ram_address, current_bin_value, 1)
ram_addr_dict[addr_to_update.ram_address] = [current_bin_value, 1]
else: else:
if GrinchLocation.get_apid(item_data.id) in list_recv_itemids: if GrinchLocation.get_apid(item_data.id) in list_recv_itemids:
await self.update_and_validate_address(ctx, addr_to_update.ram_address, addr_to_update.value, 1) ram_addr_dict[addr_to_update.ram_address] = [addr_to_update.value, addr_to_update.bit_size]
else: else:
await self.update_and_validate_address(ctx, addr_to_update.ram_address, 0, 1) ram_addr_dict[addr_to_update.ram_address] = [0, addr_to_update.bit_size]
await bizhawk.write(ctx.bizhawk_ctx, self.convert_dict_to_ram_list(ram_addr_dict))
async def ingame_checker(self, ctx: "BizHawkClientContext"): async def ingame_checker(self, ctx: "BizHawkClientContext"):
from CommonClient import logger from CommonClient import logger
ingame_map_id = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [( ingame_map_id = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
0x010000, 1, "MainRAM")]))[0], "little") 0x010000, 1, "MainRAM")]))[0], "little")
initial_cutscene_checker = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(
0x010094, 1, "MainRAM")]))[0], "little")
#If not in game or at a menu, or loading the publisher logos #If not in game or at a menu, or loading the publisher logos
if ingame_map_id <= 0x04 or ingame_map_id >= 0x35: if ingame_map_id <= 0x04 or ingame_map_id >= 0x35:
self.ingame_log = False
return False return False
#If grinch has changed maps #If grinch has changed maps
@@ -268,6 +346,7 @@ class GrinchClient(BizHawkClient):
# Reset our demo mode checker just in case the game is in demo mode. # Reset our demo mode checker just in case the game is in demo mode.
self.demo_mode_buffer = 0 self.demo_mode_buffer = 0
self.ingame_log = False self.ingame_log = False
if initial_cutscene_checker != 1:
return False return False
# Update the previous map we were on to be the current map. # Update the previous map we were on to be the current map.
@@ -291,49 +370,67 @@ class GrinchClient(BizHawkClient):
async def option_handler(self, ctx: "BizHawkClientContext"): async def option_handler(self, ctx: "BizHawkClientContext"):
if self.loc_unlimited_eggs: if self.loc_unlimited_eggs:
max_eggs: int = 200 await bizhawk.write(ctx.bizhawk_ctx, [(EGG_COUNT_ADDR, MAX_EGGS.to_bytes(2,"little"), "MainRAM")])
await bizhawk.write(ctx.bizhawk_ctx, [(0x010058, max_eggs.to_bytes(2,"little"), "MainRAM")])
async def update_and_validate_address(self, ctx: "BizHawkClientContext", address_to_validate: int, expected_value: int, byte_size: int): async def ring_link_output(self, ctx: "BizHawkClientContext"):
await bizhawk.write(ctx.bizhawk_ctx, [(address_to_validate, expected_value.to_bytes(byte_size, "little"), "MainRAM")]) from CommonClient import logger
current_value = int.from_bytes((await bizhawk.read(ctx.bizhawk_ctx, [(address_to_validate, byte_size, "MainRAM")]))[0], "little") while self.send_ring_link and ctx.slot:
if not current_value == expected_value:
if address_to_validate == 0x010000 or address_to_validate == 0x08FB94: # TODO Temporairly skips teleportation addresses; to be changed later on. try:
current_egg_count = int.from_bytes(
(await bizhawk.read(ctx.bizhawk_ctx, [(EGG_COUNT_ADDR, EGG_ADDR_BYTESIZE, "MainRAM")]))[0], "little")
if (current_egg_count - self.previous_egg_count) != 0:
msg = {
"cmd": "Bounce",
"data": {
"time": time.time(),
"source": self.unique_client_id,
"amount": current_egg_count - self.previous_egg_count
},
"tags": ["RingLink"]
}
await ctx.send_msgs([msg])
self.previous_egg_count = current_egg_count
# logger.info(f"RingLink: You sent {str(current_egg_count - self.previous_egg_count)} rotten eggs.")
await asyncio.sleep(0.1)
except Exception as ex:
logger.error("While monitoring grinch's egg count ingame, an error occurred. Details:"+ str(ex))
self.send_ring_link = False
if not ctx.slot:
logger.info("You must be connected to the multi-world in order for RingLink to work properly.")
async def ring_link_input(self, egg_amount: int, ctx: "BizHawkClientContext"):
from CommonClient import logger
game_egg_count = int.from_bytes(
(await bizhawk.read(ctx.bizhawk_ctx, [(EGG_COUNT_ADDR, EGG_ADDR_BYTESIZE, "MainRAM")]))[0], "little")
non_neg_eggs = game_egg_count + egg_amount if game_egg_count + egg_amount > 0 else 0
current_egg_count = min(non_neg_eggs, MAX_EGGS)
await bizhawk.write(ctx.bizhawk_ctx, [(EGG_COUNT_ADDR,
int(current_egg_count).to_bytes(EGG_ADDR_BYTESIZE, "little"), "MainRAM")])
self.previous_egg_count = current_egg_count
# logger.info(f"RingLink: You received {str(egg_amount)} rotten eggs.")
def _get_uuid(self) -> int:
string_id = str(uuid.uuid4())
uid: int = 0
for char in string_id:
uid += ord(char)
return uid
def _cmd_ringlink(self):
"""Toggle ringling from client. Overrides default setting."""
if not self.ctx.slot:
return return
raise Exception("Unable to update address as expected. Address: "+ str(address_to_validate)+"; Expected Value: "+str(expected_value)) Utils.async_start(_update_ring_link(self.ctx, not "RingLink" in self.ctx.tags), name="Update RingLink")
# async def ring_link_output(self, ctx: "BizHawkClientContext", byte_size: int): async def _update_ring_link(ctx: "BizHawkClientContext", ring_link: bool):
# bizhawk.seek(0x010058) """Helper function to set Ring Link connection tag on/off and update the connection if already connected."""
# byte_size = 2 old_tags = copy.deepcopy(ctx.tags)
# current_eggs = int.from_bytes(byte_size=2, byteorder="little") if ring_link:
# difference = current_eggs - ctx.previous_eggs ctx.tags.add("RingLink")
# ctx.previous_eggs = current_eggs + ctx.ring_link_eggs else:
# ctx.tags -= {"RingLink"}
# if difference != 0: if old_tags != ctx.tags and ctx.server and not ctx.server.socket.closed:
# # logger.info("got here with a difference of " + str(difference)) await ctx.send_msgs([{"cmd": "ConnectUpdate", "tags": ctx.tags}])
# msg = {
# "cmd": "Bounce",
# "slots": [ctx.slot],
# "data": {
# "time": time.time(),
# "source": ctx.slot,
# "amount": difference
# },
# "tags": ["RingLink"]
# }
# await ctx.send_msgs([msg])
#
# # here write new ring value back into file
# bizhawk.seek(0x010058)
# if current_eggs + ctx.ring_link_eggs < 0:
# ctx.ring_link_eggs = -current_eggs
# bizhawk.write(int(current_eggs + ctx.ring_link_eggs).to_bytes(byte_size=2, byteorder="little"))
# ctx.ring_link_eggs = 0
#
# async def ring_link_input(self, data):
# amount = data["amount"]
# source = data["source"]
# if source == self.slot:
# return
# else:
# self.ring_link_eggs += amount

View File

@@ -5,11 +5,10 @@ from BaseClasses import Item
from BaseClasses import ItemClassification as IC #IC can be any name, saves having to type the whole word in code from BaseClasses import ItemClassification as IC #IC can be any name, saves having to type the whole word in code
class GrinchItemData(NamedTuple): class GrinchItemData(NamedTuple):
item_group: str #arbituary that can be whatever it can be, basically the field/property for item groups item_group: list[str] #arbituary that can be whatever it can be, basically the field/property for item groups
id: Optional[int] id: Optional[int]
classification: IC classification: IC
update_ram_addr: list[GrinchRamData] update_ram_addr: list[GrinchRamData]
second_item_group: Optional[str] = None
class GrinchItem(Item): class GrinchItem(Item):
game: str = "The Grinch" game: str = "The Grinch"
@@ -32,24 +31,54 @@ def get_item_names_per_category() -> dict[str, set[str]]:
categories: dict[str, set[str]] = {} categories: dict[str, set[str]] = {}
for name, data in ALL_ITEMS_TABLE.items(): for name, data in ALL_ITEMS_TABLE.items():
categories.setdefault(data.item_group, set()).add(name) for group in data.item_group: # iterate over each category
categories.setdefault(group, set()).add(name)
return categories return categories
REL: str = "Rotten Egg Launcher"
RS: str = "Rocket Spring"
SS: str = "Slime Shooter"
OCD: str = "Octopus Climbing Device"
MM: str = "Marine Mobile"
GC: str = "Grinch Copter"
WV: str = "Whoville Vacuum Tube"
WF: str = "Who Forest Vacuum Tube"
WD: str = "Who Dump Vacuum Tube"
WL: str = "Who Lake Vacuum Tube"
VT: str = "Progressive Vacuum Tube"
PC: str = "Pancake"
SR: str = "Sleigh Room Key"
BB: str = "Bad Breath"
SE: str = "Seize"
MX: str = "Max"
SN: str = "Sneak"
WC: str = "Who Cloak"
PB: str = "Painting Bucket"
SC: str = "Scissors"
GB: str = "Glue Bucket"
CCAC: str = "Cable Car Access Card"
DRL: str = "Drill"
RP: str = "Rope"
HK: str = "Hook"
ST: str = "Sculpting Tools"
HMR: str = "Hammer"
SCL: str = "Scout Clothes"
#Gadgets #Gadgets
#All gadgets require at least 4 different blueprints to be unlocked in the computer in Mount Crumpit. #All gadgets require at least 4 different blueprints to be unlocked in the computer in Mount Crumpit.
GADGETS_TABLE: dict[str, GrinchItemData] = { GADGETS_TABLE: dict[str, GrinchItemData] = {
"Binoculars": GrinchItemData("Gadgets", 100, IC.useful, "Binoculars": GrinchItemData(["Gadgets"], 100, IC.useful,
[GrinchRamData(0x0102B6, value=0x40), GrinchRamData(0x0102B7, value=0x41), [GrinchRamData(0x0102B6, value=0x40), GrinchRamData(0x0102B7, value=0x41),
GrinchRamData(0x0102B8, value=0x44), GrinchRamData(0x0102B9, value=0x45), GrinchRamData(0x0102B8, value=0x44), GrinchRamData(0x0102B9, value=0x45),
# GrinchRamData(0x0100BC, binary_bit_pos=0) # GrinchRamData(0x0100BC, binary_bit_pos=0)
]), ]),
"Rotten Egg Launcher": GrinchItemData("Gadgets", 101, IC.progression, "Rotten Egg Launcher": GrinchItemData(["Gadgets"], 101, IC.progression,
[GrinchRamData(0x0102BA, value=0x40), GrinchRamData(0x0102BB, value=0x41), [GrinchRamData(0x0102BA, value=0x40), GrinchRamData(0x0102BB, value=0x41),
GrinchRamData(0x0102BC, value=0x44), GrinchRamData(0x0102BD, value=0x45), GrinchRamData(0x0102BC, value=0x44), GrinchRamData(0x0102BD, value=0x45),
# GrinchRamData(0x0100BC, binary_bit_pos=1) # GrinchRamData(0x0100BC, binary_bit_pos=1)
]), ]),
"Rocket Spring": GrinchItemData("Gadgets", 102, IC.progression, "Rocket Spring": GrinchItemData(["Gadgets"], 102, IC.progression,
[GrinchRamData(0x0102BE, value=0x40), GrinchRamData(0x0102BF, value=0x41), [GrinchRamData(0x0102BE, value=0x40), GrinchRamData(0x0102BF, value=0x41),
GrinchRamData(0x0102C0, value=0x42), GrinchRamData(0x0102C1, value=0x44), GrinchRamData(0x0102C0, value=0x42), GrinchRamData(0x0102C1, value=0x44),
GrinchRamData(0x0102C2, value=0x45), GrinchRamData(0x0102C3, value=0x46), GrinchRamData(0x0102C2, value=0x45), GrinchRamData(0x0102C3, value=0x46),
@@ -57,7 +86,7 @@ GADGETS_TABLE: dict[str, GrinchItemData] = {
GrinchRamData(0x0102C6, value=0x4A), GrinchRamData(0x0102C6, value=0x4A),
# GrinchRamData(0x0100BC, binary_bit_pos=2) # GrinchRamData(0x0100BC, binary_bit_pos=2)
]), ]),
"Slime Shooter": GrinchItemData("Gadgets", 103, IC.progression, "Slime Shooter": GrinchItemData(["Gadgets", "Slime Gun"], 103, IC.progression,
[GrinchRamData(0x0102C7, value=0x40), GrinchRamData(0x0102C8, value=0x41), [GrinchRamData(0x0102C7, value=0x40), GrinchRamData(0x0102C8, value=0x41),
GrinchRamData(0x0102C9, value=0x42), GrinchRamData(0x0102CA, value=0x44), GrinchRamData(0x0102C9, value=0x42), GrinchRamData(0x0102CA, value=0x44),
GrinchRamData(0x0102CB, value=0x45), GrinchRamData(0x0102CC, value=0x46), GrinchRamData(0x0102CB, value=0x45), GrinchRamData(0x0102CC, value=0x46),
@@ -65,7 +94,7 @@ GADGETS_TABLE: dict[str, GrinchItemData] = {
GrinchRamData(0x0102CF, value=0x4A), GrinchRamData(0x0102CF, value=0x4A),
# GrinchRamData(0x0100BC, binary_bit_pos=3) # GrinchRamData(0x0100BC, binary_bit_pos=3)
]), ]),
"Octopus Climbing Device": GrinchItemData("Gadgets", 104, IC.progression, "Octopus Climbing Device": GrinchItemData(["Gadgets"], 104, IC.progression,
[GrinchRamData(0x0102D0, value=0x40), GrinchRamData(0x0102D1, value=0x41), [GrinchRamData(0x0102D0, value=0x40), GrinchRamData(0x0102D1, value=0x41),
GrinchRamData(0x0102D2, value=0x42), GrinchRamData(0x0102D3, value=0x44), GrinchRamData(0x0102D2, value=0x42), GrinchRamData(0x0102D3, value=0x44),
GrinchRamData(0x0102D4, value=0x45), GrinchRamData(0x0102D5, value=0x46), GrinchRamData(0x0102D4, value=0x45), GrinchRamData(0x0102D5, value=0x46),
@@ -73,7 +102,7 @@ GADGETS_TABLE: dict[str, GrinchItemData] = {
GrinchRamData(0x0102D8, value=0x4A), GrinchRamData(0x0102D8, value=0x4A),
# GrinchRamData(0x0100BC, binary_bit_pos=4) # GrinchRamData(0x0100BC, binary_bit_pos=4)
]), ]),
"Marine Mobile": GrinchItemData("Gadgets", 105, IC.progression, "Marine Mobile": GrinchItemData(["Gadgets"], 105, IC.progression,
[GrinchRamData(0x0102D9, value=0x40), GrinchRamData(0x0102DA, value=0x41), [GrinchRamData(0x0102D9, value=0x40), GrinchRamData(0x0102DA, value=0x41),
GrinchRamData(0x0102DB, value=0x42), GrinchRamData(0x0102DC, value=0x43), GrinchRamData(0x0102DB, value=0x42), GrinchRamData(0x0102DC, value=0x43),
GrinchRamData(0x0102DD, value=0x44), GrinchRamData(0x0102DE, value=0x45), GrinchRamData(0x0102DD, value=0x44), GrinchRamData(0x0102DE, value=0x45),
@@ -84,7 +113,7 @@ GADGETS_TABLE: dict[str, GrinchItemData] = {
GrinchRamData(0x0102E7, value=0x4E), GrinchRamData(0x0102E8, value=0x4F), GrinchRamData(0x0102E7, value=0x4E), GrinchRamData(0x0102E8, value=0x4F),
# GrinchRamData(0x0100BC, binary_bit_pos=5) # GrinchRamData(0x0100BC, binary_bit_pos=5)
]), ]),
"Grinch Copter": GrinchItemData("Gadgets", 106, IC.progression, "Grinch Copter": GrinchItemData(["Gadgets"], 106, IC.progression,
[GrinchRamData(0x0102E9, value=0x40), GrinchRamData(0x0102EA, value=0x41), [GrinchRamData(0x0102E9, value=0x40), GrinchRamData(0x0102EA, value=0x41),
GrinchRamData(0x0102EB, value=0x42), GrinchRamData(0x0102EC, value=0x43), GrinchRamData(0x0102EB, value=0x42), GrinchRamData(0x0102EC, value=0x43),
GrinchRamData(0x0102ED, value=0x44), GrinchRamData(0x0102EE, value=0x45), GrinchRamData(0x0102ED, value=0x44), GrinchRamData(0x0102EE, value=0x45),
@@ -99,120 +128,120 @@ GADGETS_TABLE: dict[str, GrinchItemData] = {
#Mission Specific Items #Mission Specific Items
MISSION_ITEMS_TABLE: dict[str, GrinchItemData] = { MISSION_ITEMS_TABLE: dict[str, GrinchItemData] = {
"Who Cloak": GrinchItemData("Mission Specific Items", 200, IC.progression, "Who Cloak": GrinchItemData(["Mission Specific Items", "Useful Items"], 200, IC.progression,
[GrinchRamData(0x0101F9, binary_bit_pos=0)], second_item_group="Useful Items"), [GrinchRamData(0x0101F9, binary_bit_pos=0)]),
"Painting Bucket": GrinchItemData("Mission Specific Items", 201, IC.progression_deprioritized, "Painting Bucket": GrinchItemData(["Mission Specific Items", "Useful Items"], 201, IC.progression_deprioritized,
[GrinchRamData(0x0101F9, binary_bit_pos=1)], second_item_group="Useful Items"), [GrinchRamData(0x0101F9, binary_bit_pos=1)]),
"Scissors": GrinchItemData("Mission Specific Items", 202, IC.progression_deprioritized, "Scissors": GrinchItemData(["Mission Specific Items", "Useful Items"], 202, IC.progression_deprioritized,
[GrinchRamData(0x0101F9, binary_bit_pos=6), GrinchRamData(0x0100C2, binary_bit_pos=1)], [GrinchRamData(0x0101F9, binary_bit_pos=6), GrinchRamData(0x0100C2, binary_bit_pos=1)]),
second_item_group="Useful Items"), "Glue Bucket": GrinchItemData(["Mission Specific Items", "Useful Items"], 203, IC.progression_deprioritized,
"Glue Bucket": GrinchItemData("Mission Specific Items", 203, IC.progression_deprioritized, [GrinchRamData(0x0101F9, binary_bit_pos=4)]),
[GrinchRamData(0x0101F9, binary_bit_pos=4)], second_item_group="Useful Items"), "Cable Car Access Card": GrinchItemData(["Mission Specific Items", "Useful Items"], 204, IC.progression,
"Cable Car Access Card": GrinchItemData("Mission Specific Items", 204, IC.progression, [GrinchRamData(0x0101F9, binary_bit_pos=5)]),
[GrinchRamData(0x0101F9, binary_bit_pos=5)], second_item_group="Useful Items"), "Drill": GrinchItemData(["Mission Specific Items", "Useful Items"], 205, IC.progression_deprioritized,
"Drill": GrinchItemData("Mission Specific Items", 205, IC.progression_deprioritized, [GrinchRamData(0x0101FA, binary_bit_pos=2)]),
[GrinchRamData(0x0101FA, binary_bit_pos=2)], second_item_group="Useful Items"), "Rope": GrinchItemData(["Mission Specific Items", "Useful Items"], 206, IC.progression_deprioritized,
"Rope": GrinchItemData("Mission Specific Items", 206, IC.progression_deprioritized, [GrinchRamData(0x0101FA, binary_bit_pos=1)]),
[GrinchRamData(0x0101FA, binary_bit_pos=1)], second_item_group="Useful Items"), "Hook": GrinchItemData(["Mission Specific Items", "Useful Items"], 207, IC.progression_deprioritized,
"Hook": GrinchItemData("Mission Specific Items", 207, IC.progression_deprioritized, [GrinchRamData(0x0101FA, binary_bit_pos=0)]),
[GrinchRamData(0x0101FA, binary_bit_pos=0)], second_item_group="Useful Items"), "Sculpting Tools": GrinchItemData(["Mission Specific Items", "Useful Items"], 208, IC.progression_deprioritized,
"Sculpting Tools": GrinchItemData("Mission Specific Items", 208, IC.progression_deprioritized, [GrinchRamData(0x0101F9, binary_bit_pos=2)]),
[GrinchRamData(0x0101F9, binary_bit_pos=2)], second_item_group="Useful Items"), "Hammer": GrinchItemData(["Mission Specific Items", "Useful Items"], 209, IC.progression_deprioritized,
"Hammer": GrinchItemData("Mission Specific Items", 209, IC.progression_deprioritized, [GrinchRamData(0x0101F9, binary_bit_pos=3)]),
[GrinchRamData(0x0101F9, binary_bit_pos=3)], second_item_group="Useful Items"), "Scout Clothes": GrinchItemData(["Mission Specific Items", "Useful Items"], 210, IC.progression,
"Scout Clothes": GrinchItemData("Mission Specific Items", 210, IC.progression, [GrinchRamData(0x0101F9, binary_bit_pos=7)])
[GrinchRamData(0x0101F9, binary_bit_pos=7)], second_item_group="Useful Items")
} }
#Sleigh Parts #Sleigh Parts
# SLEIGH_PARTS_TABLE: dict[str, GrinchItemData] = { # SLEIGH_PARTS_TABLE: dict[str, GrinchItemData] = {
# "Exhaust Pipes": GrinchItemData("Sleigh Parts", 300, IC.progression_skip_balancing, # "Exhaust Pipes": GrinchItemData(["Sleigh Parts"], 300, IC.progression_skip_balancing,
# [GrinchRamData(0x0101FB, binary_bit_pos=2)]), # [GrinchRamData(0x0101FB, binary_bit_pos=2)]),
# "GPS": GrinchItemData("Sleigh Parts", 301, IC.useful, # "GPS": GrinchItemData(["Sleigh Parts"], 301, IC.useful,
# [GrinchRamData(0x0101FB, binary_bit_pos=5)]), # [GrinchRamData(0x0101FB, binary_bit_pos=5)]),
# "Tires": GrinchItemData("Sleigh Parts", 302, IC.progression_skip_balancing, # "Tires": GrinchItemData(["Sleigh Parts"], 302, IC.progression_skip_balancing,
# [GrinchRamData(0x0101FB, binary_bit_pos=4)]), # [GrinchRamData(0x0101FB, binary_bit_pos=4)]),
# "Skis": GrinchItemData("Sleigh Parts", 303, IC.progression_skip_balancing, # "Skis": GrinchItemData(["Sleigh Parts"], 303, IC.progression_skip_balancing,
# [GrinchRamData(0x0101FB, binary_bit_pos=3)]), # [GrinchRamData(0x0101FB, binary_bit_pos=3)]),
# "Twin-End Tuba": GrinchItemData("Sleigh Parts", 304, IC.progression_skip_balancing, # "Twin-End Tuba": GrinchItemData(["Sleigh Parts"], 304, IC.progression_skip_balancing,
# [GrinchRamData(0x0101FB, binary_bit_pos=6)]) # [GrinchRamData(0x0101FB, binary_bit_pos=6)])
# } # }
#Access Keys #Access Keys
KEYS_TABLE: dict[str, GrinchItemData] = { KEYS_TABLE: dict[str, GrinchItemData] = {
# "Whoville Vacuum Access": GrinchItemData("Vacuum Access", 400, IC.progression, "Whoville Vacuum Tube": GrinchItemData(["Vacuum Tubes"], 400, IC.progression,
# [GrinchRamData()]), [GrinchRamData(0x010200, binary_bit_pos=1)]),
"Who Forest Vacuum Access": GrinchItemData("Vacuum Access", 401, IC.progression, "Who Forest Vacuum Tube": GrinchItemData(["Vacuum Tubes"], 401, IC.progression,
[GrinchRamData(0x0100AA, binary_bit_pos=2)]), [GrinchRamData(0x0100AA, binary_bit_pos=2)]),
"Who Dump Vacuum Access": GrinchItemData("Vacuum Access", 402, IC.progression, "Who Dump Vacuum Tube": GrinchItemData(["Vacuum Tubes"], 402, IC.progression,
[GrinchRamData(0x0100AA, binary_bit_pos=3)]), [GrinchRamData(0x0100AA, binary_bit_pos=3)]),
"Who Lake Vacuum Access": GrinchItemData("Vacuum Access", 403, IC.progression, "Who Lake Vacuum Tube": GrinchItemData(["Vacuum Tubes"], 403, IC.progression,
[GrinchRamData(0x0100AA, binary_bit_pos=4)]), [GrinchRamData(0x0100AA, binary_bit_pos=4)]),
# "Progressive Vacuum Access": GrinchItemData("Vacuum Access", 404, IC.progression, # "Progressive Vacuum Tube": GrinchItemData(["Vacuum Tubes"], 404, IC.progression,
# [GrinchRamData()]), # [GrinchRamData()]),
# "Spin N' Win Door Unlock": GrinchItemData("Supadow Door Unlocks", 405, IC.progression, # "Spin N' Win Door Unlock": GrinchItemData(["Supadow Door Unlocks"], 405, IC.progression,
# [GrinchRamData()]), # [GrinchRamData()]),
# "Dankamania Door Unlock": GrinchItemData("Supadow Door Unlocks", 406, IC.progression, # "Dankamania Door Unlock": GrinchItemData(["Supadow Door Unlocks"], 406, IC.progression,
# [GrinchRamData()]), # [GrinchRamData()]),
# "The Copter Race Contest Door Unlock": GrinchItemData("Supadow Door Unlocks", 407, IC.progression, # "The Copter Race Contest Door Unlock": GrinchItemData("Supadow Door Unlocks", 407, IC.progression,
# [GrinchRamData()]), # [GrinchRamData()]),
# "Progressive Supadow Door Unlock": GrinchItemData("Supadow Door Unlocks", 408, IC.progression, # "Progressive Supadow Door Unlock": GrinchItemData("Supadow Door Unlocks", 408, IC.progression,
# [GrinchRamData()]), # [GrinchRamData()]),
# "Bike Race Access": GrinchItemData("Supadow Door Unlocks", 409, IC.progression, # "Bike Race Access": GrinchItemData(["Supadow Door Unlocks", 409, IC.progression,
# [GrinchRamData()]) # [GrinchRamData()])
"Sleigh Room Key": GrinchItemData("Sleigh Room", 410, IC.progression, "Sleigh Room Key": GrinchItemData(["Sleigh Room"], 410, IC.progression,
[GrinchRamData(0x010200, binary_bit_pos=6), GrinchRamData(0x0100AA, binary_bit_pos=5)]) [GrinchRamData(0x010200, binary_bit_pos=6), GrinchRamData(0x0100AA, binary_bit_pos=5)])
} }
#Misc Items #Misc Items
MISC_ITEMS_TABLE: dict[str, GrinchItemData] = { MISC_ITEMS_TABLE: dict[str, GrinchItemData] = {
# This item may not function properly if you receive it during a loading screen or in Mount Crumpit # This item may not function properly if you receive it during a loading screen or in Mount Crumpit
# "Fully Healed Grinch": GrinchItemData("Health Items", 500, IC.filler, # "Fully Healed Grinch": GrinchItemData(["Health Items", "Filler"], 500, IC.filler,
# [GrinchRamData(0x0E8FDC, value=120)]), # [GrinchRamData(0x0E8FDC, value=120)]),
"5 Rotten Eggs": GrinchItemData("Rotten Egg Bundles", 502, IC.filler, "5 Rotten Eggs": GrinchItemData(["Rotten Egg Bundles", "Filler"], 502, IC.filler,
[GrinchRamData(0x010058, value=5, update_existing_value=True, max_count=200, bit_size=2)]), [GrinchRamData(0x010058, value=5, update_existing_value=True, max_count=200, bit_size=2)]),
"10 Rotten Eggs": GrinchItemData("Rotten Egg Bundles", 503, IC.filler, "10 Rotten Eggs": GrinchItemData(["Rotten Egg Bundles", "Filler"], 503, IC.filler,
[GrinchRamData(0x010058, value=10, update_existing_value=True, max_count=200, bit_size=2)]), [GrinchRamData(0x010058, value=10, update_existing_value=True, max_count=200, bit_size=2)]),
"20 Rotten Eggs": GrinchItemData("Rotten Egg Bundles", 504, IC.filler, "20 Rotten Eggs": GrinchItemData(["Rotten Egg Bundles", "Filler"], 504, IC.filler,
[GrinchRamData(0x010058, value=20, update_existing_value=True, max_count=200, bit_size=2)]) [GrinchRamData(0x010058, value=20, update_existing_value=True, max_count=200, bit_size=2)])
} }
USEFUL_IC_TABLE: dict[str, GrinchItemData] = { USEFUL_IC_TABLE: dict[str, GrinchItemData] = {
"Heart of Stone": GrinchItemData("Health Items", 501, IC.useful, "Heart of Stone": GrinchItemData(["Health Items"], 501, IC.useful,
[GrinchRamData(0x0100ED, value=1, update_existing_value=True, max_count=4)]) [GrinchRamData(0x0100ED, value=1, update_existing_value=True, max_count=4)])
} }
#Traps #Traps
TRAPS_TABLE: dict[str, GrinchItemData] = { TRAPS_TABLE: dict[str, GrinchItemData] = {
# alias to Ice Trap for traplink # alias to Ice Trap for traplink
# "Freeze Trap": GrinchItemData("Traps", 600, IC.trap, [GrinchRamData()]), # "Freeze Trap": GrinchItemData(["Traps"], 600, IC.trap, [GrinchRamData()]),
# "Bee Trap": GrinchItemData("Traps", 601, IC.trap, [GrinchRamData()]), # "Bee Trap": GrinchItemData(["Traps"], 601, IC.trap, [GrinchRamData()]),
# "Electrocution Trap": GrinchItemData("Traps", 602, IC.trap, [GrinchRamData()]), # "Electrocution Trap": GrinchItemData(["Traps"], 602, IC.trap, [GrinchRamData()]),
# alias to Slowness Trap for traplink # alias to Slowness Trap for traplink
# "Tip Toe Trap": GrinchItemData("Traps", 603, IC.trap, [GrinchRamData()]), # "Tip Toe Trap": GrinchItemData(["Traps"], 603, IC.trap, [GrinchRamData()]),
# This item may not function properly if you receive it during a loading screen or in Mount Crumpit # This item may not function properly if you receive it during a loading screen or in Mount Crumpit
# "Damage Trap": GrinchItemData("Traps", 604, IC.trap, [GrinchRamData(0x0E8FDC, value=-20, update_existing_value=True)]), # alias to Exhaustion Trap
"Depletion Trap": GrinchItemData("Traps", 605, IC.trap, [GrinchRamData(0x010058, value=0, bit_size=2)]), # "Damage Trap": GrinchItemData(["Traps"], 604, IC.trap, [GrinchRamData(0x0E8FDC, value=-20, update_existing_value=True)]),
"Dump it to Crumpit": GrinchItemData("Traps", 606, IC.trap, #Alias to Home Trap for traplink "Depletion Trap": GrinchItemData(["Traps"], 605, IC.trap, [GrinchRamData(0x010058, value=0, bit_size=2)]),
[GrinchRamData(0x010000, value=0x05), GrinchRamData(0x08FB94, value=1)]), "Dump it to Crumpit": GrinchItemData(["Traps"], 606, IC.trap, #Alias to Home Trap for traplink
[GrinchRamData(0x010000, value=0x05), GrinchRamData(0x08FB94, value=1), GrinchRamData(0x0100B4, value=0)]),
#alias to Spring Trap for traplink #alias to Spring Trap for traplink
# "Rocket Spring Trap": GrinchItemData("Traps", 607, IC.trap, [GrinchRamData()]), # "Rocket Spring Trap": GrinchItemData(["Traps"], 607, IC.trap, [GrinchRamData()]),
#alias to Home Trap for traplink #alias to Home Trap for traplink
"Who sent me back?": GrinchItemData("Traps", 608, IC.trap, [GrinchRamData(0x08FB94, value=1)]), "Who sent me back?": GrinchItemData(["Traps"], 608, IC.trap, [GrinchRamData(0x08FB94, value=1)]),
# "Cutscene Trap": GrinchItemData("Traps", 609, IC.trap, [GrinchRamData()]), # "Cutscene Trap": GrinchItemData(["Traps"], 609, IC.trap, [GrinchRamData()]),
# "No Vac Trap": GrinchItemData("Traps", 610, IC.trap, [GrinchRamData(0x0102DA, value=0]), # "No Vac Trap": GrinchItemData(["Traps"], 610, IC.trap, [GrinchRamData(0x0102DA, value=0]),
# "Invisible Trap": GrinchItemData("Traps", 611, IC.trap, [GrinchRamData(0x0102DA, value=0, bit_size=4)]) # "Invisible Trap": GrinchItemData(["Traps"], 611, IC.trap, [GrinchRamData(0x0102DA, value=0, bit_size=4)])
# "Child Trap": GrinchItemData("Traps", 612, IC.trap,[GrinchRamData()]) # "Child Trap": GrinchItemData(["Traps"], 612, IC.trap,[GrinchRamData()])
# "Disable Jump Trap": GrinchItemData("Traps", 613, IC.trap,[GrinchRamData(0x010026, binary_bit_pos=6)]) # "Disable Jump Trap": GrinchItemData(["Traps"], 613, IC.trap,[GrinchRamData(0x010026, binary_bit_pos=6)])
} }
#Movesets #Movesets
# MOVES_TABLE: dict[str, GrinchItemData] = { # MOVES_TABLE: dict[str, GrinchItemData] = {
# "Bad Breath": GrinchItemData("Movesets", 700, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=1)]), # "Bad Breath": GrinchItemData(["Movesets"], 700, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=1)]),
# "Pancake": GrinchItemData("Movesets", 701, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=2)]), # "Pancake": GrinchItemData(["Movesets"], 701, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=2)]),
# "Push & Pull": GrinchItemData("Movesets", 702, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=3)]), # "Push & Pull": GrinchItemData(["Movesets"], 702, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=3)]),
# "Max": GrinchItemData("Movesets", 703, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=4)]), # "Max": GrinchItemData(["Movesets"], 703, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=4)]),
# "Tip Toe": GrinchItemData("Movesets", 704, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=5)]) # "Tip Toe": GrinchItemData(["Movesets"], 704, IC.progression, [GrinchRamData(0x0100BB, binary_bit_pos=5)])
# } # }
#Double star combines all dictionaries from each individual list together #Double star combines all dictionaries from each individual list together
ALL_ITEMS_TABLE: dict[str, GrinchItemData] = { ALL_ITEMS_TABLE: dict[str, GrinchItemData] = {

View File

@@ -6,7 +6,7 @@ from BaseClasses import Location, Region
class GrinchLocationData(NamedTuple): class GrinchLocationData(NamedTuple):
region: str region: str
location_group: str location_group: Optional[list[str]]
id: Optional[int] id: Optional[int]
update_ram_addr: list[GrinchRamData] update_ram_addr: list[GrinchRamData]
reset_addr: Optional[list[GrinchRamData]] = None # Addresses to update once we find the item reset_addr: Optional[list[GrinchRamData]] = None # Addresses to update once we find the item
@@ -28,166 +28,178 @@ class GrinchLocation(Location):
self.type = data.location_group self.type = data.location_group
self.address = self.address self.address = self.address
def get_location_names_per_category() -> dict[str, set[str]]:
categories: dict[str, set[str]] = {}
for name, data in grinch_locations.items():
if data.location_group is None:
continue
for group in data.location_group: # iterate over each category
categories.setdefault(group, set()).add(name)
return categories
grinch_locations = { grinch_locations = {
#Going to use current map id as indicator whether or not you visited a location #Going to use current map id as indicator whether or not you visited a location
#Visitsanity #Visitsanity
"Enter Whoville": GrinchLocationData("Whoville", "Visitsanity", 100, [GrinchRamData(0x010000, value=0x07)]), "WV - First Visit": GrinchLocationData("Whoville", ["Visitsanity", "Whoville"], 100, [GrinchRamData(0x010000, value=0x07)]),
"Enter the Post Office": GrinchLocationData("Post Office", "Visitsanity", 101, [GrinchRamData(0x010000, value=0x0A)]), "WV - Post Office - First Visit": GrinchLocationData("Post Office", ["Visitsanity", "Whoville", "Post Office"], 101, [GrinchRamData(0x010000, value=0x0A)]),
"Enter the Town Hall": GrinchLocationData("City Hall", "Visitsanity", 102, [GrinchRamData(0x010000, value=0x08)]), "WV - City Hall - First Visit": GrinchLocationData("City Hall", ["Visitsanity", "Whoville", "City Hall"], 102, [GrinchRamData(0x010000, value=0x08)]),
"Enter the Countdown-To-Xmas Clock Tower": GrinchLocationData("Countdown to X-Mas Clock Tower", "Visitsanity", 103, [GrinchRamData(0x010000, value=0x09)]), "WV - Clock Tower - First Visit": GrinchLocationData("Clock Tower", ["Visitsanity", "Whoville", "Clock Tower"], 103, [GrinchRamData(0x010000, value=0x09)]),
"Enter Who Forest": GrinchLocationData("Who Forest", "Visitsanity", 104, [GrinchRamData(0x010000, value=0x0B)]), "WF - First Visit": GrinchLocationData("Who Forest", ["Visitsanity", "Who Forest"], 104, [GrinchRamData(0x010000, value=0x0B)]),
"Enter the Ski Resort": GrinchLocationData("Ski Resort", "Visitsanity", 105, [GrinchRamData(0x010000, value=0x0C)]), "WF - Ski Resort - First Visit": GrinchLocationData("Ski Resort", ["Visitsanity", "Who Forest", "Ski Resort"], 105, [GrinchRamData(0x010000, value=0x0C)]),
"Enter the Civic Center": GrinchLocationData("Civic Center", "Visitsanity", 106, [GrinchRamData(0x010000, value=0x0D)]), "WF - Civic Center - First Visit": GrinchLocationData("Civic Center", ["Visitsanity", "Who Forest", "Civic Center"], 106, [GrinchRamData(0x010000, value=0x0D)]),
"Enter Who Dump": GrinchLocationData("Who Dump", "Visitsanity", 107, [GrinchRamData(0x010000, value=0x0E)]), "WD - First Visit": GrinchLocationData("Who Dump", ["Visitsanity", "Who Dump"], 107, [GrinchRamData(0x010000, value=0x0E)]),
"Enter the Minefield": GrinchLocationData("Minefield", "Visitsanity", 108, [GrinchRamData(0x010000, value=0x11)]), "WD - Minefield - First Visit": GrinchLocationData("Minefield", ["Visitsanity", "Who Dump", "Minefield"], 108, [GrinchRamData(0x010000, value=0x11)]),
"Enter the Power Plant": GrinchLocationData("Power Plant", "Visitsanity", 109, [GrinchRamData(0x010000, value=0x10)]), "WD - Power Plant - First Visit": GrinchLocationData("Power Plant", ["Visitsanity", "Who Dump", "Power Plant"], 109, [GrinchRamData(0x010000, value=0x10)]),
"Enter the Generator Building": GrinchLocationData("Generator Building", "Visitsanity", 110, [GrinchRamData(0x010000, value=0x0F)]), "WD - Generator Building - First Visit": GrinchLocationData("Generator Building", ["Visitsanity", "Who Dump", "Generator Building"], 110, [GrinchRamData(0x010000, value=0x0F)]),
"Enter Who Lake": GrinchLocationData("Who Lake", "Visitsanity", 111, [GrinchRamData(0x010000, value=0x12)]), "WL - South Shore - First Visit": GrinchLocationData("Who Lake", ["Visitsanity", "Who Lake", "South Shore"], 111, [GrinchRamData(0x010000, value=0x12)]),
"Enter the Submarine World": GrinchLocationData("Submarine World", "Visitsanity", 112, [GrinchRamData(0x010000, value=0x17)]), "WL - Submarine World - First Visit": GrinchLocationData("Submarine World", ["Visitsanity", "Who Lake", "Submarine World"], 112, [GrinchRamData(0x010000, value=0x17)]),
"Enter the Scout's Hut": GrinchLocationData("Scout's Hut", "Visitsanity", 113, [GrinchRamData(0x010000, value=0x13)]), "WL - Scout's Hut - First Visit": GrinchLocationData("Scout's Hut", ["Visitsanity", "Who Lake", "Scout's Hut"], 113, [GrinchRamData(0x010000, value=0x13)]),
"Enter the North Shore": GrinchLocationData("North Shore", "Visitsanity", 114, [GrinchRamData(0x010000, value=0x14)]), "WL - North Shore - First Visit": GrinchLocationData("North Shore", ["Visitsanity", "Who Lake", "North Shore"], 114, [GrinchRamData(0x010000, value=0x14)]),
"Enter the Mayor's Villa": GrinchLocationData("Mayor's Villa", "Visitsanity", 115, [GrinchRamData(0x010000, value=0x16)]), "WL - Mayor's Villa - First Visit": GrinchLocationData("Mayor's Villa", ["Visitsanity", "Who Lake", "Mayor's Villa"], 115, [GrinchRamData(0x010000, value=0x16)]),
#Need to find mission completion address for handful of locations that are not documented. #Need to find mission completion address for handful of locations that are not documented.
#Missions that have value are those ones we need to find the check for #Missions that have value are those ones we need to find the check for
#Whoville Missions #Whoville Missions
"Shuffling The Mail": GrinchLocationData("Post Office", "Whoville Missions", 201, [GrinchRamData(0x0100BE, binary_bit_pos=0)]), "WV - Post Office - Shuffling The Mail": GrinchLocationData("Post Office", ["Whoville Missions", "Missions", "Whoville", "Post Office"], 201, [GrinchRamData(0x0100BE, binary_bit_pos=0)]),
"Smashing Snowmen": GrinchLocationData("Whoville", "Whoville Missions", 200, [GrinchRamData(0x0100C5, value=10)]), "WV - Smashing Snowmen": GrinchLocationData("Whoville", ["Whoville Missions", "Missions", "Whoville"], 200, [GrinchRamData(0x0100C5, value=10)]),
"Painting The Mayor's Posters": GrinchLocationData("Whoville", "Whoville Missions", 202, [GrinchRamData(0x0100C6, value=10)]), "WV - Painting The Mayor's Posters": GrinchLocationData("Whoville", ["Whoville Missions", "Missions", "Whoville"], 202, [GrinchRamData(0x0100C6, value=10)]),
"Launching Eggs Into Houses": GrinchLocationData("Whoville", "Whoville Missions", 203, [GrinchRamData(0x0100C7, value=10)]), "WV - Launching Eggs Into Houses": GrinchLocationData("Whoville", ["Whoville Missions", "Missions", "Whoville"], 203, [GrinchRamData(0x0100C7, value=10)]),
"Modifying The Mayor's Statue": GrinchLocationData("City Hall", "Whoville Missions", 204, [GrinchRamData(0x0100BE, binary_bit_pos=1)]), "WV - City Hall - Modifying The Mayor's Statue": GrinchLocationData("City Hall", ["Whoville Missions", "Missions", "Whoville", "City Hall"], 204, [GrinchRamData(0x0100BE, binary_bit_pos=1)]),
"Advancing The Countdown-To-Xmas Clock": GrinchLocationData("Countdown to X-Mas Clock Tower", "Whoville Missions", 205, [GrinchRamData(0x0100BE, binary_bit_pos=2)]), "WV - Clock Tower - Advancing The Countdown-To-Xmas Clock": GrinchLocationData("Clock Tower", ["Whoville Missions", "Missions", "Whoville", "Clock Tower"], 205, [GrinchRamData(0x0100BE, binary_bit_pos=2)]),
"Squashing All Gifts in Whoville": GrinchLocationData("Whoville", "Whoville Missions", 206, [GrinchRamData(0x01005C, value=500, bit_size=2)]), "WV - Squashing All Gifts": GrinchLocationData("Whoville", ["Whoville Missions", "Missions", "Giftsanity", "Whoville"], 206, [GrinchRamData(0x01005C, value=500, bit_size=2)]),
#Who Forest Missions #Who Forest Missions
"Making Xmas Trees Droop": GrinchLocationData("Who Forest", "Who Forest Missions", 300, [GrinchRamData(0x0100C8, value=10)]), "WF - Making Xmas Trees Droop": GrinchLocationData("Who Forest", ["Who Forest Missions", "Missions", "Who Forest"], 300, [GrinchRamData(0x0100C8, value=10)]),
"Sabotaging Snow Cannon With Glue": GrinchLocationData("Who Forest", "Who Forest Missions", 301, [GrinchRamData(0x0100BE, binary_bit_pos=3)]), "WF - Sabotaging Snow Cannon With Glue": GrinchLocationData("Who Forest", ["Who Forest Missions", "Missions", "Who Forest"], 301, [GrinchRamData(0x0100BE, binary_bit_pos=3)]),
"Putting Beehives In Cabins": GrinchLocationData("Who Forest", "Who Forest Missions", 302, [GrinchRamData(0x0100CA, value=10)]), "WF - Putting Beehives In Cabins": GrinchLocationData("Who Forest", ["Who Forest Missions", "Missions", "Who Forest"], 302, [GrinchRamData(0x0100CA, value=10)]),
"Sliming The Mayor's Skis": GrinchLocationData("Ski Resort", "Who Forest Missions", 303, [GrinchRamData(0x0100BE, binary_bit_pos=4)]), "WF - Ski Resort - Sliming The Mayor's Skis": GrinchLocationData("Ski Resort", ["Who Forest Missions", "Missions", "Who Forest", "Ski Resort"], 303, [GrinchRamData(0x0100BE, binary_bit_pos=4)]),
"Replacing The Candles On The Cake With Fireworks": GrinchLocationData("Civic Center", "Who Forest Missions", 304, [GrinchRamData(0x0100BE, binary_bit_pos=5)]), "WF - Civic Center - Replacing The Candles On The Cake With Fireworks": GrinchLocationData("Civic Center", ["Who Forest Missions", "Missions", "Who Forest", "Civic Center"], 304, [GrinchRamData(0x0100BE, binary_bit_pos=5)]),
"Squashing All Gifts in Who Forest": GrinchLocationData("Who Forest", "Who Forest Missions", 305, [GrinchRamData(0x01005E, value=750, bit_size=2)]), "WF - Squashing All Gifts": GrinchLocationData("Who Forest", ["Who Forest Missions", "Missions", "Giftsanity", "Who Forest"], 305, [GrinchRamData(0x01005E, value=750, bit_size=2)]),
#Who Dump Missions #Who Dump Missions
"Stealing Food From Birds": GrinchLocationData("Who Dump", "Who Dump Missions", 400, [GrinchRamData(0x0100CB, value=10)]), "WD - Stealing Food From Birds": GrinchLocationData("Who Dump", ["Who Dump Missions", "Missions", "Who Dump"], 400, [GrinchRamData(0x0100CB, value=10)]),
"Feeding The Computer With Robot Parts": GrinchLocationData("Who Dump", "Who Dump Missions", 401, [GrinchRamData(0x0100BF, binary_bit_pos=2)]), "WD - Feeding The Computer With Robot Parts": GrinchLocationData("Who Dump", ["Who Dump Missions", "Missions", "Who Dump"], 401, [GrinchRamData(0x0100BF, binary_bit_pos=2)]),
"Infesting The Mayor's House With Rats": GrinchLocationData("Who Dump", "Who Dump Missions", 402, [GrinchRamData(0x0100BE, binary_bit_pos=6)]), "WD - Infesting The Mayor's House With Rats": GrinchLocationData("Who Dump", ["Who Dump Missions", "Missions", "Who Dump"], 402, [GrinchRamData(0x0100BE, binary_bit_pos=6)]),
"Conducting The Stinky Gas To Who-Bris' Shack": GrinchLocationData("Who Dump", "Who Dump Missions", 403, [GrinchRamData(0x0100BE, binary_bit_pos=7)]), "WD - Conducting The Stinky Gas To Who-Bris' Shack": GrinchLocationData("Who Dump", ["Who Dump Missions", "Missions", "Who Dump"], 403, [GrinchRamData(0x0100BE, binary_bit_pos=7)]),
"Shaving Who Dump Guardian": GrinchLocationData("Minefield", "Who Dump Missions", 404, [GrinchRamData(0x0100BF, binary_bit_pos=0)]), "WD - Minefield - Shaving Who Dump Guardian": GrinchLocationData("Minefield", ["Who Dump Missions", "Missions", "Who Dump", "Minefield"], 404, [GrinchRamData(0x0100BF, binary_bit_pos=0)]),
"Short-Circuiting Power-Plant": GrinchLocationData("Generator Building", "Who Dump Missions", 405, [GrinchRamData(0x0100BF, binary_bit_pos=1)]), "WD - Generator Building - Short-Circuiting Power-Plant": GrinchLocationData("Generator Building", ["Who Dump Missions", "Missions", "Who Dump", "Generator Building"], 405, [GrinchRamData(0x0100BF, binary_bit_pos=1)]),
"Squashing All Gifts in Who Dump": GrinchLocationData("Who Dump", "Who Dump Missions", 406, [GrinchRamData(0x010060, value=750, bit_size=2)]), "WD - Squashing All Gifts": GrinchLocationData("Who Dump", ["Who Dump Missions", "Missions", "Who Dump", "Giftsanity"], 406, [GrinchRamData(0x010060, value=750, bit_size=2)]),
#Who Lake Missions #Who Lake Missions
"Putting Thistles In Shorts": GrinchLocationData("Who Lake", "Who Lake Missions", 500, [GrinchRamData(0x0100E5, value=10)]), "WL - South Shore - Putting Thistles In Shorts": GrinchLocationData("Who Lake", ["Who Lake Missions", "Missions", "Who Lake", "South Shore", "South Shore Missions"], 500, [GrinchRamData(0x0100E5, value=10)]),
"Sabotaging The Tents": GrinchLocationData("Who Lake", "Who Lake Missions", 501, [GrinchRamData(0x0100E6, value=10)]), "WL - South Shore - Sabotaging The Tents": GrinchLocationData("Who Lake", ["Who Lake Missions", "Missions", "Who Lake", "South Shore", "South Shore Missions"], 501, [GrinchRamData(0x0100E6, value=10)]),
"Drilling Holes In Canoes": GrinchLocationData("North Shore", "Who Lake Missions", 502, [GrinchRamData(0x0100EE, value=10)]), "WL - North Shore - Drilling Holes In Canoes": GrinchLocationData("North Shore", ["Who Lake Missions", "Missions", "Who Lake", "North Shore"], 502, [GrinchRamData(0x0100EE, value=10)]),
"Modifying The Marine Mobile": GrinchLocationData("Submarine World", "Who Lake Missions", 503, [GrinchRamData(0x0100BF, binary_bit_pos=4)]), "WL - Submarine World - Modifying The Marine Mobile": GrinchLocationData("Submarine World", ["Who Lake Missions", "Missions", "Who Lake", "Submarine World"], 503, [GrinchRamData(0x0100BF, binary_bit_pos=4)]),
"Hooking The Mayor's Bed To The Motorboat": GrinchLocationData("Mayor's Villa", "Who Lake Missions", 504, [GrinchRamData(0x0100BF, binary_bit_pos=3)]), "WL - Mayor's Villa - Hooking The Mayor's Bed To The Motorboat": GrinchLocationData("Mayor's Villa", ["Who Lake Missions", "Missions", "Who Lake", "Mayor's Villa"], 504, [GrinchRamData(0x0100BF, binary_bit_pos=3)]),
"Squashing All Gifts in Who Lake": GrinchLocationData("Who Lake", "Who Lake Missions", 505, [GrinchRamData(0x010062, value=1000, bit_size=2)]), "WL - Squashing All Gifts": GrinchLocationData("Who Lake", ["Who Lake Missions", "Missions", "Who Lake", "Giftsanity"], 505, [GrinchRamData(0x010062, value=1000, bit_size=2)]),
#Need to find binary values for individual blueprints, but all ram addresses are found #Need to find binary values for individual blueprints, but all ram addresses are found
#Blueprints #Blueprints
#Binoculars Blueprints #Binoculars Blueprints
"Binoculars Blueprint - Post Office Roof": GrinchLocationData("Whoville", "Binocular Blueprints", 600, [GrinchRamData(0x01020B, binary_bit_pos=2)]), "WV - Binoculars BP on Post Office Roof": GrinchLocationData("Whoville", ["Binocular Blueprints", "Blueprints", "Whoville", "Whoville Blueprints"], 600, [GrinchRamData(0x01020B, binary_bit_pos=2)]),
"Binoculars Blueprint - City Hall Library - Left Side": GrinchLocationData("City Hall", "Binocular Blueprints", 601, [GrinchRamData(0x01021F, binary_bit_pos=6)]), "WV - City Hall - Binoculars BP left side of Library": GrinchLocationData("City Hall", ["Binocular Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "City Hall", "City Hall Blueprints"], 601, [GrinchRamData(0x01021F, binary_bit_pos=6)]),
"Binoculars Blueprint - City Hall Library - Front Side": GrinchLocationData("City Hall", "Binocular Blueprints", 602, [GrinchRamData(0x01021F, binary_bit_pos=5)]), "WV - City Hall - Binoculars BP front side of Library": GrinchLocationData("City Hall", ["Binocular Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "City Hall", "City Hall Blueprints"], 602, [GrinchRamData(0x01021F, binary_bit_pos=5)]),
"Binoculars Blueprint - City Hall Library - Right Side": GrinchLocationData("City Hall", "Binocular Blueprints", 603, [GrinchRamData(0x01021F, binary_bit_pos=4)]), "WV - City Hall - Binoculars BP right side of Library": GrinchLocationData("City Hall", ["Binocular Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "City Hall", "City Hall Blueprints"], 603, [GrinchRamData(0x01021F, binary_bit_pos=4)]),
#Rotten Egg Launcher Blueprints #Rotten Egg Launcher Blueprints
"REL Blueprint - Outside City Hall": GrinchLocationData("Whoville", "Rotten Egg Launcher Blueprints", 700, [GrinchRamData(0x01020B, binary_bit_pos=0)]), "WV - REL BP left of City Hall": GrinchLocationData("Whoville", ["Rotten Egg Launcher Blueprints", "Blueprints", "Whoville", "Whoville Blueprints"], 700, [GrinchRamData(0x01020B, binary_bit_pos=0)]),
"REL Blueprint - Outside Clock Tower": GrinchLocationData("Whoville", "Rotten Egg Launcher Blueprints", 701, [GrinchRamData(0x01020B, binary_bit_pos=1)]), "WV - REL BP left of Clock Tower": GrinchLocationData("Whoville", ["Rotten Egg Launcher Blueprints", "Blueprints", "Whoville", "Whoville Blueprints"], 701, [GrinchRamData(0x01020B, binary_bit_pos=1)]),
"REL Blueprint - Post Office - Inside Silver Room": GrinchLocationData("Post Office", "Rotten Egg Launcher Blueprints", 702, [GrinchRamData(0x01021C, binary_bit_pos=1)]), "WV - Post Office - REL BP inside Silver Room": GrinchLocationData("Post Office", ["Rotten Egg Launcher Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "Post Office", "Post Office Blueprints"], 702, [GrinchRamData(0x01021C, binary_bit_pos=1)]),
"REL Blueprint - Post Office - After Mission Completion": GrinchLocationData("Post Office", "Rotten Egg Launcher Blueprints", 703, [GrinchRamData(0x01021C, binary_bit_pos=2)]), "WV - Post Office - REL BP at Entrance Door after Mission Completion": GrinchLocationData("Post Office", ["Rotten Egg Launcher Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "Post Office", "Post Office Blueprints"], 703, [GrinchRamData(0x01021C, binary_bit_pos=2)]),
#Rocket Spring Blueprints #Rocket Spring Blueprints
"RS Blueprint - Behind Vacuum": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 800, [GrinchRamData(0x010243, binary_bit_pos=3)]), "WF - RS BP behind Vacuum Tube": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 800, [GrinchRamData(0x010243, binary_bit_pos=3)]),
"RS Blueprint - Front of 2nd House near entrance": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 801, [GrinchRamData(0x010243, binary_bit_pos=1)]), "WF - RS BP in front of 2nd House near Vacuum Tube": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 801, [GrinchRamData(0x010243, binary_bit_pos=1)]),
"RS Blueprint - Near Tree House on Ground": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 802, [GrinchRamData(0x010243, binary_bit_pos=4)]), "WF - RS BP near Tree House on Ground": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 802, [GrinchRamData(0x010243, binary_bit_pos=4)]),
"RS Blueprint - Near Cable Car House": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 804, [GrinchRamData(0x010242, binary_bit_pos=7)]), "WF - RS BP behind Cable Car House": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 804, [GrinchRamData(0x010242, binary_bit_pos=7)]),
"RS Blueprint - Near Who Snowball in Cave": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 805, [GrinchRamData(0x010242, binary_bit_pos=6)]), "WF - RS BP near Who Snowball in Cave": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 805, [GrinchRamData(0x010242, binary_bit_pos=6)]),
"RS Blueprint - Branch Platform Closest to Glue Cannon": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 806, [GrinchRamData(0x010243, binary_bit_pos=2)]), "WF - RS BP on Branch Platform closest to Glue Cannon": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 806, [GrinchRamData(0x010243, binary_bit_pos=2)]),
"RS Blueprint - Branch Platform Near Beast": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 807, [GrinchRamData(0x010243, binary_bit_pos=0)]), "WF - RS BP on Branch Platform Near Beast": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 807, [GrinchRamData(0x010243, binary_bit_pos=0)]),
"RS Blueprint - Branch Platform Ledge Grab House": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 808, [GrinchRamData(0x010243, binary_bit_pos=6)]), "WF - RS BP on Branch Platform Elevated next to House": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 808, [GrinchRamData(0x010243, binary_bit_pos=6)]),
"RS Blueprint - On Tree House": GrinchLocationData("Who Forest", "Rocket Spring Blueprints", 809, [GrinchRamData(0x010243, binary_bit_pos=5)]), "WF - RS BP on Tree House": GrinchLocationData("Who Forest", ["Rocket Spring Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 809, [GrinchRamData(0x010243, binary_bit_pos=5)]),
#Slime Shooter Blueprints #Slime Shooter Blueprints
"SS Blueprint - Branch Platform Elevated House": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 900, [GrinchRamData(0x010244, binary_bit_pos=3)]), "WF - SS BP in Branch Platform Elevated House": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 900, [GrinchRamData(0x010244, binary_bit_pos=3)]),
"SS Blueprint - Branch Platform House next to Beast": GrinchLocationData("Who Forest", "Slime Shooter Blueprint", 901, [GrinchRamData(0x010243, binary_bit_pos=7)]), "WF - SS BP in Branch Platform House next to Beast": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 901, [GrinchRamData(0x010243, binary_bit_pos=7)]),
"SS Blueprint - House near Civic Center Cave": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 902, [GrinchRamData(0x010244, binary_bit_pos=2)]), "WF - SS BP in House in front of Civic Center Cave": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 902, [GrinchRamData(0x010244, binary_bit_pos=2)]),
"SS Blueprint - House next to Tree House": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 903, [GrinchRamData(0x010244, binary_bit_pos=1)]), "WF - SS BP in House next to Tree House": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 903, [GrinchRamData(0x010244, binary_bit_pos=1)]),
"SS Blueprint - House across from Tree House": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 904, [GrinchRamData(0x010244, binary_bit_pos=5)]), "WF - SS BP in House across from Tree House": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 904, [GrinchRamData(0x010244, binary_bit_pos=5)]),
"SS Blueprint - 2nd House near entrance right side": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 905, [GrinchRamData(0x010244, binary_bit_pos=4)]), "WF - SS BP in 2nd House near Vacuum Tube Right Side": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 905, [GrinchRamData(0x010244, binary_bit_pos=4)]),
"SS Blueprint - 2nd House near entrance left side": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 906, [GrinchRamData(0x010244, binary_bit_pos=7)]), "WF - SS BP in 2nd House near Vacuum Tube Left Side": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 906, [GrinchRamData(0x010244, binary_bit_pos=7)]),
"SS Blueprint - 2nd House near entrance inbetween blueprints": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 907, [GrinchRamData(0x010244, binary_bit_pos=6)]), "WF - SS BP in 2nd House near Vacuum Tube inbetween Blueprints": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 907, [GrinchRamData(0x010244, binary_bit_pos=6)]),
"SS Blueprint - House near entrance": GrinchLocationData("Who Forest", "Slime Shooter Blueprints", 908, [GrinchRamData(0x010244, binary_bit_pos=0)]), "WF - SS BP in House near Vacuum Tube": GrinchLocationData("Who Forest", ["Slime Shooter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints"], 908, [GrinchRamData(0x010244, binary_bit_pos=0)]),
#Octopus Climbing Device #Octopus Climbing Device
"OCD Blueprint - Middle Pipe": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1001, [GrinchRamData(0x010252, binary_bit_pos=3)]), "WD - OCD BP inside Middle Pipe": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1001, [GrinchRamData(0x010252, binary_bit_pos=3)]),
"OCD Blueprint - Right Pipe": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1002, [GrinchRamData(0x010252, binary_bit_pos=5)]), "WD - OCD BP inside Right Pipe": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1002, [GrinchRamData(0x010252, binary_bit_pos=5)]),
"OCD Blueprint - Mayor's House Rat Vent": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1003, [GrinchRamData(0x010252, binary_bit_pos=1)]), "WD - OCD BP in Vent to Mayor's House": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1003, [GrinchRamData(0x010252, binary_bit_pos=1)]),
"OCD Blueprint - Left Pipe": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1004, [GrinchRamData(0x010252, binary_bit_pos=4)]), "WD - OCD BP inside Left Pipe": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1004, [GrinchRamData(0x010252, binary_bit_pos=4)]),
"OCD Blueprint - Near Power Plant Wall on right side": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1005, [GrinchRamData(0x010252, binary_bit_pos=0)]), "WD - OCD BP near Right Side of Power Plant Wall": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1005, [GrinchRamData(0x010252, binary_bit_pos=0)]),
"OCD Blueprint - Near Who-Bris' Shack": GrinchLocationData("Who Dump", "Octopus Climbing Device Blueprints", 1006, [GrinchRamData(0x010252, binary_bit_pos=2)]), "WD - OCD BP near Who-Bris' Shack": GrinchLocationData("Who Dump", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints"], 1006, [GrinchRamData(0x010252, binary_bit_pos=2)]),
"OCD Blueprint - Guardian's House - Left Side": GrinchLocationData("Minefield", "Octopus Climbing Device Blueprints", 1007, [GrinchRamData(0x01026E, binary_bit_pos=2)]), "WD - Minefield - OCD BP on Left Side of House": GrinchLocationData("Minefield", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Minefield", "Minefield Blueprints"], 1007, [GrinchRamData(0x01026E, binary_bit_pos=2)]),
"OCD Blueprint - Guardian's House - Right Side": GrinchLocationData("Minefield", "Octopus Climbing Device Blueprints", 1008, [GrinchRamData(0x01026E, binary_bit_pos=4)]), "WD - Minefield - OCD BP on Right Side of Shack": GrinchLocationData("Minefield", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Minefield", "Minefield Blueprints"], 1008, [GrinchRamData(0x01026E, binary_bit_pos=4)]),
"OCD Blueprint - Inside Guardian's House": GrinchLocationData("Minefield", "Octopus Climbing Device Blueprints", 1009, [GrinchRamData(0x01026E, binary_bit_pos=3)]), "WD - Minefield - OCD BP inside Guardian's House": GrinchLocationData("Minefield", ["Octopus Climbing Device Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Minefield", "Minefield Blueprints"], 1009, [GrinchRamData(0x01026E, binary_bit_pos=3)]),
#Marine Mobile Blueprints #Marine Mobile Blueprints
"MM Blueprint - South Shore - Bridge to Scout's Hut": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1100, [GrinchRamData(0x010281, binary_bit_pos=5)]), "WL - South Shore - MM BP on Bridge to Scout's Hut": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1100, [GrinchRamData(0x010281, binary_bit_pos=5)]),
"MM Blueprint - South Shore - Tent near Porcupine": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1101, [GrinchRamData(0x010281, binary_bit_pos=6)]), "WL - South Shore - MM BP across from Tent near Porcupine": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1101, [GrinchRamData(0x010281, binary_bit_pos=6)]),
"MM Blueprint - South Shore - Near Outhouse": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1102, [GrinchRamData(0x010281, binary_bit_pos=7)]), "WL - South Shore - MM BP near Outhouse": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1102, [GrinchRamData(0x010281, binary_bit_pos=7)]),
"MM Blueprint - South Shore - Near Hill Bridge": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1103, [GrinchRamData(0x010282, binary_bit_pos=0)]), "WL - South Shore - MM BP near Hill Bridge": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1103, [GrinchRamData(0x010282, binary_bit_pos=0)]),
"MM Blueprint - South Shore - Scout's Hut Roof": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1104, [GrinchRamData(0x010281, binary_bit_pos=4)]), "WL - South Shore - MM BP on Scout's Hut Roof": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1104, [GrinchRamData(0x010281, binary_bit_pos=4)]),
"MM Blueprint - South Shore - Grass Platform": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1105, [GrinchRamData(0x010281, binary_bit_pos=2)]), "WL - South Shore - MM BP on Grass Platform": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1105, [GrinchRamData(0x010281, binary_bit_pos=2)]),
"MM Blueprint - South Shore - Zipline by Beast": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1106, [GrinchRamData(0x010281, binary_bit_pos=3)]), "WL - South Shore - MM BP across Zipline Platform": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1106, [GrinchRamData(0x010281, binary_bit_pos=3)]),
"MM Blueprint - South Shore - Behind Summer Beast": GrinchLocationData("Who Lake", "Marine Mobile Blueprints", 1107, [GrinchRamData(0x010282, binary_bit_pos=1)]), "WL - South Shore - MM BP behind Summer Beast": GrinchLocationData("Who Lake", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "South Shore", "South Shore Blueprints"], 1107, [GrinchRamData(0x010282, binary_bit_pos=1)]),
"MM Blueprint - North Shore - Below Bridge": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1108, [GrinchRamData(0x010293, binary_bit_pos=0)]), "WL - North Shore - MM BP below Bridge": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1108, [GrinchRamData(0x010293, binary_bit_pos=0)]),
"MM Blueprint - North Shore - Behind Skunk Hut": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1109, [GrinchRamData(0x010293, binary_bit_pos=2)]), "WL - North Shore - MM BP behind Skunk Hut": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1109, [GrinchRamData(0x010293, binary_bit_pos=2)]),
"MM Blueprint - North Shore - Inside Skunk Hut": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1110, [GrinchRamData(0x010292, binary_bit_pos=6)]), "WL - North Shore - MM BP inside Skunk Hut": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1110, [GrinchRamData(0x010292, binary_bit_pos=6)]),
"MM Blueprint - North Shore - Fenced in Area": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1111, [GrinchRamData(0x010292, binary_bit_pos=7)]), "WL - North Shore - MM BP inside House's Fence": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1111, [GrinchRamData(0x010292, binary_bit_pos=7)]),
"MM Blueprint - North Shore - Boulder Box near Bridge": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1112, [GrinchRamData(0x010293, binary_bit_pos=3)]), "WL - North Shore - MM BP inside Boulder Box near Bridge": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1112, [GrinchRamData(0x010293, binary_bit_pos=3)]),
"MM Blueprint - North Shore - Boulder Box behind Skunk Hut": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1113, [GrinchRamData(0x010293, binary_bit_pos=4)]), "WL - North Shore - MM BP inside Boulder Box behind Skunk Hut": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1113, [GrinchRamData(0x010293, binary_bit_pos=4)]),
"MM Blueprint - North Shore - Inside Drill House": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1114, [GrinchRamData(0x010292, binary_bit_pos=5)]), "WL - North Shore - MM BP inside Drill House": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1114, [GrinchRamData(0x010292, binary_bit_pos=5)]),
"MM Blueprint - North Shore - Crow Platform near Drill House": GrinchLocationData("North Shore", "Marine Mobile Blueprints", 1115, [GrinchRamData(0x010293, binary_bit_pos=1)]), "WL - North Shore - MM BP on Crow Platform near Drill House": GrinchLocationData("North Shore", ["Marine Mobile Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "North Shore", "North Shore Blueprints"], 1115, [GrinchRamData(0x010293, binary_bit_pos=1)]),
#Grinch Copter Blueprints #Grinch Copter Blueprints
"GC Blueprint - Whoville City Hall - Safe Room": GrinchLocationData("City Hall", "Grinch Copter Blueprints", 1200, [GrinchRamData(0x01021F, binary_bit_pos=7)]), "WV - City Hall - GC BP in Safe Room": GrinchLocationData("City Hall", ["Grinch Copter Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "City Hall", "City Hall Blueprints"], 1200, [GrinchRamData(0x01021F, binary_bit_pos=7)]),
"GC Blueprint - Whoville City Hall - Statue Room": GrinchLocationData("City Hall", "Grinch Copter Blueprints", 1201, [GrinchRamData(0x010220, binary_bit_pos=0)]), "WV - City Hall - GC BP in Statue Room": GrinchLocationData("City Hall", ["Grinch Copter Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "City Hall", "City Hall Blueprints"], 1201, [GrinchRamData(0x010220, binary_bit_pos=0)]),
"GC Blueprint - Whoville Clock Tower - Before Bells": GrinchLocationData("Countdown to X-Mas Clock Tower", "Grinch Copter Blueprints", 1202, [GrinchRamData(0x010216, binary_bit_pos=3)]), "WV - Clock Tower - GC BP in Bedroom": GrinchLocationData("Clock Tower", ["Grinch Copter Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "Clock Tower", "Clock Tower Blueprints"], 1202, [GrinchRamData(0x010216, binary_bit_pos=3)]),
"GC Blueprint - Whoville Clock Tower - After Bells": GrinchLocationData("Countdown to X-Mas Clock Tower", "Grinch Copter Blueprints", 1203, [GrinchRamData(0x010216, binary_bit_pos=2)]), "WV - Clock Tower - GC BP in Bell Room": GrinchLocationData("Clock Tower", ["Grinch Copter Blueprints", "Blueprints", "Whoville", "Whoville Blueprints", "Clock Tower", "Clock Tower Blueprints"], 1203, [GrinchRamData(0x010216, binary_bit_pos=2)]),
"GC Blueprint - Who Forest Ski Resort - Inside Dog's Fence": GrinchLocationData("Ski Resort", "Grinch Copter Blueprints", 1204, [GrinchRamData(0x010234, binary_bit_pos=7)]), "WF - Ski Resort - GC BP inside Dog's Fence": GrinchLocationData("Ski Resort", ["Grinch Copter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints", "Ski Resort", "Ski Resort Blueprints"], 1204, [GrinchRamData(0x010234, binary_bit_pos=7)]),
"GC Blueprint - Who Forest Ski Resort - Max Cave": GrinchLocationData("Ski Resort", "Grinch Copter Blueprints", 1205, [GrinchRamData(0x010234, binary_bit_pos=6)]), "WF - Ski Resort - GC BP in Max Cave": GrinchLocationData("Ski Resort", ["Grinch Copter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints", "Ski Resort", "Ski Resort Blueprints"], 1205, [GrinchRamData(0x010234, binary_bit_pos=6)]),
"GC Blueprint - Who Forest Civic Center - Climb across Bat Cave wall": GrinchLocationData("Civic Center", "Grinch Copter Blueprints", 1206, [GrinchRamData(0x01022A, binary_bit_pos=7)]), "WF - Civic Center - GC BP on Left Side in Bat Cave Wall": GrinchLocationData("Civic Center", ["Grinch Copter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints", "Civic Center", "Civic Center Blueprints"], 1206, [GrinchRamData(0x01022A, binary_bit_pos=7)]),
"GC Blueprint - Who Forest Civic Center - Shoot Icicle in Bat Entrance": GrinchLocationData("Civic Center", "Grinch Copter Blueprints", 1207, [GrinchRamData(0x01022B, binary_bit_pos=0)]), "WF - Civic Center - GC BP in Frozen Ice": GrinchLocationData("Civic Center", ["Grinch Copter Blueprints", "Blueprints", "Who Forest", "Who Forest Blueprints", "Civic Center", "Civic Center Blueprints"], 1207, [GrinchRamData(0x01022B, binary_bit_pos=0)]),
"GC Blueprint - Who Dump Power Plant - Max Cave": GrinchLocationData("Power Plant", "Grinch Copter Blueprints", 1208, [GrinchRamData(0x010265, binary_bit_pos=1)]), "WD - Power Plant - GC BP in Max Cave": GrinchLocationData("Power Plant", ["Grinch Copter Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Power Plant", "Power Plant Blueprints"], 1208, [GrinchRamData(0x010265, binary_bit_pos=1)]),
"GC Blueprint - Who Dump Power Plant - After First Gate": GrinchLocationData("Power Plant", "Grinch Copter Blueprints", 1209, [GrinchRamData(0x010265, binary_bit_pos=2)]), "WD - Power Plant - GC BP After First Gate": GrinchLocationData("Power Plant", ["Grinch Copter Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Power Plant", "Power Plant Blueprints"], 1209, [GrinchRamData(0x010265, binary_bit_pos=2)]),
"GC Blueprint - Who Dump Generator Building - Before Mission": GrinchLocationData("Generator Building", "Grinch Copter Blueprints", 1210, [GrinchRamData(0x01026B, binary_bit_pos=0)]), "WD - Generator Building - GC BP on the Highest Platform": GrinchLocationData("Generator Building", ["Grinch Copter Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Generator Building", "Generator Building Blueprints"], 1210, [GrinchRamData(0x01026B, binary_bit_pos=0)]),
"GC Blueprint - Who Dump Generator Building - After Mission": GrinchLocationData("Generator Building", "Grinch Copter Blueprints", 1211, [GrinchRamData(0x01026B, binary_bit_pos=1)]), "WD - Generator Building - GC BP at the Entrance after Mission Completion": GrinchLocationData("Generator Building", ["Grinch Copter Blueprints", "Blueprints", "Who Dump", "Who Dump Blueprints", "Generator Building", "Generator Building Blueprints"], 1211, [GrinchRamData(0x01026B, binary_bit_pos=1)]),
"GC Blueprint - Who Lake South Shore - Submarine World - Above Surface": GrinchLocationData("Submarine World", "Grinch Copter Blueprints", 1212, [GrinchRamData(0x010289, binary_bit_pos=3)]), "WL - Submarine World - GC BP Just Below Water Surface": GrinchLocationData("Submarine World", ["Grinch Copter Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "Submarine World", "Submarine World Blueprints"], 1212, [GrinchRamData(0x010289, binary_bit_pos=3)]),
"GC Blueprint - Who Lake South Shore - Submarine World - Underwater": GrinchLocationData("Submarine World", "Grinch Copter Blueprints", 1213, [GrinchRamData(0x010289, binary_bit_pos=4)]), "WL - Submarine World - GC BP Underwater": GrinchLocationData("Submarine World", ["Grinch Copter Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "Submarine World", "Submarine World Blueprints"], 1213, [GrinchRamData(0x010289, binary_bit_pos=4)]),
"GC Blueprint - Who Lake North Shore - Mayor's Villa - Tree Branch": GrinchLocationData("Mayor's Villa", "Grinch Copter Blueprints", 1214, [GrinchRamData(0x010275, binary_bit_pos=7)]), "WL - Mayor's Villa - GC BP on Tree Branch": GrinchLocationData("Mayor's Villa", ["Grinch Copter Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "Mayor's Villa", "Mayor's Villa Blueprints"], 1214, [GrinchRamData(0x010275, binary_bit_pos=7)]),
"GC Blueprint - Who Lake North Shore - Mayor's Villa - Cave": GrinchLocationData("Mayor's Villa", "Grinch Copter Blueprints", 1215, [GrinchRamData(0x010275, binary_bit_pos=6)]), "WL - Mayor's Villa - GC BP in Pirate's Cave": GrinchLocationData("Mayor's Villa", ["Grinch Copter Blueprints", "Blueprints", "Who Lake", "Who Lake Blueprints", "Mayor's Villa", "Mayor's Villa Blueprints"], 1215, [GrinchRamData(0x010275, binary_bit_pos=6)]),
#Sleigh Room Locations #Sleigh Room Locations
"Stealing All Gifts": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1300, [GrinchRamData(0x0100BF, binary_bit_pos=6)]), "MC - Sleigh Ride - Stealing All Gifts": GrinchLocationData("Sleigh Room", ["Sleigh Ride"], 1300, [GrinchRamData(0x0100BF, binary_bit_pos=6)]),
"Neutralizing Santa": GrinchLocationData("Sleigh Room", "Sleigh Ride", None, [GrinchRamData(0x0100BF, binary_bit_pos=7)]), "MC - Sleigh Ride - Neutralizing Santa": GrinchLocationData("Sleigh Room", None, None, [GrinchRamData(0x010000, value=0x3E)]),#[GrinchRamData(0x0100BF, binary_bit_pos=7)]),
#Heart of Stones #Heart of Stones
"Heart of Stone - Whoville's Post Office": GrinchLocationData("Post Office", "Heart of Stones", 1400, [GrinchRamData(0x0101FA, binary_bit_pos=6)]), "WV - Post Office - Heart of Stone": GrinchLocationData("Post Office", ["Heart of Stones", "Whoville", "Post Office"], 1400, [GrinchRamData(0x0101FA, binary_bit_pos=6)]),
"Heart of Stone - Who Forest's Ski Resort": GrinchLocationData("Ski Resort", "Heart of Stones", 1401, [GrinchRamData(0x0101FA, binary_bit_pos=7)]), "WF - Ski Resort - Heart of Stone": GrinchLocationData("Ski Resort", ["Heart of Stones", "Who Forest", "Ski Resort"], 1401, [GrinchRamData(0x0101FA, binary_bit_pos=7)]),
"Heart of Stone - Who Dump's Minefield": GrinchLocationData("Minefield", "Heart of Stones", 1402, [GrinchRamData(0x0101FB, binary_bit_pos=0)]), "WD - Minefield - Heart of Stone": GrinchLocationData("Minefield", ["Heart of Stones", "Who Dump", "Minefield"], 1402, [GrinchRamData(0x0101FB, binary_bit_pos=0)]),
"Heart of Stone - Who Lake's North Shore": GrinchLocationData("North Shore", "Heart of Stones", 1403, [GrinchRamData(0x0101FB, binary_bit_pos=1)]), "WL - North Shore - Heart of Stone": GrinchLocationData("North Shore", ["Heart of Stones", "Who Lake", "North Shore"], 1403, [GrinchRamData(0x0101FB, binary_bit_pos=1)]),
#Supadow Minigames #Supadow Minigames
# "Spin N' Win - Easy": GrinchLocationData("Spin N' Win Supadow", "Supadow Minigames", 1500, [GrinchRamData()]), # "Spin N' Win - Easy": GrinchLocationData("Spin N' Win", ["Supadow Minigames", "Spin N' Win"], 1500, [GrinchRamData()]),
# "Spin N' Win - Hard": GrinchLocationData("Spin N' Win Supadow", "Supadow Minigames", 1501, [GrinchRamData()]), # "Spin N' Win - Hard": GrinchLocationData("Spin N' Win", ["Supadow Minigames", "Spin N' Win"], 1501, [GrinchRamData()]),
# "Spin N' Win - Real Tough": GrinchLocationData("Spin N' Win Supadow", "Supadow Minigames", 1502, [GrinchRamData()]), # "Spin N' Win - Real Tough": GrinchLocationData("Spin N' Win", ["Supadow Minigames", "Spin N' Win"], 1502, [GrinchRamData()]),
# "Dankamania - Easy - 15 Points": GrinchLocationData("Dankamania Supadow", "Supadow Minigames", 1503, [GrinchRamData()]), # "Dankamania - Easy - 15 Points": GrinchLocationData("Dankamania", ["Supadow Minigames", "Dankamania"], 1503, [GrinchRamData()]),
# "Dankamania - Hard - 15 Points": GrinchLocationData("Dankamania Supadow", "Supadow Minigames", 1504, [GrinchRamData()]), # "Dankamania - Hard - 15 Points": GrinchLocationData("Dankamania", ["Supadow Minigames", "Dankamania"], 1504, [GrinchRamData()]),
# "Dankamania - Real Tough - 15 Points": GrinchLocationData("Dankamania Supadow", "Supadow Minigames", 1505, [GrinchRamData()]), # "Dankamania - Real Tough - 15 Points": GrinchLocationData("Dankamania", ["Supadow Minigames", "Dankamania"], 1505, [GrinchRamData()]),
# "The Copter Race Contest - Easy": GrinchLocationData("The Copter Race Contest Supadow", "Supadow Minigames", 1506, [GrinchRamData()]), # "The Copter Race Contest - Easy": GrinchLocationData("The Copter Race Contest", ["Supadow Minigames", "The Copter Race Contest"], 1506, [GrinchRamData()]),
# "The Copter Race Contest - Hard": GrinchLocationData("The Copter Race Contest Supadow", "Supadow Minigames", 1507, [GrinchRamData()]), # "The Copter Race Contest - Hard": GrinchLocationData("The Copter Race Contest", ["Supadow Minigames", "The Copter Race Contest"], 1507, [GrinchRamData()]),
# "The Copter Race Contest - Real Tough": GrinchLocationData("The Copter Race Contest Supadow", "Supadow Minigames", 1508, [GrinchRamData()]), # "The Copter Race Contest - Real Tough": GrinchLocationData("The Copter Race Contest", ["Supadow Minigames", "The Copter Race Contest"], 1508, [GrinchRamData()]),
# "Bike Race - 1st Place": GrinchLocationData("Bike Race", "Supadow Minigames", 1509, [GrinchRamData()]), # "Bike Race - 1st Place": GrinchLocationData("Bike Race", ["Supadow Minigames", "Bike Race"], 1509, [GrinchRamData()]),
# "Bike Race - Top 2": GrinchLocationData("Bike Race", "Supadow Minigames", 1510, [GrinchRamData()]), # "Bike Race - Top 2": GrinchLocationData("Bike Race", ["Supadow Minigames", "Bike Race"], 1510, [GrinchRamData()]),
# "Bike Race - Top 3": GrinchLocationData("Bike Race", "Supadow Minigames", 1511, [GrinchRamData()]), # "Bike Race - Top 3": GrinchLocationData("Bike Race", ["Supadow Minigames", "Bike Race"], 1511, [GrinchRamData()]),
# Sleigh Part Locations # Sleigh Part Locations
"Exhaust Pipes in Whoville": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1600, [GrinchRamData(0x0101FB, binary_bit_pos=2)]), "WV - Exhaust Pipes": GrinchLocationData("Sleigh Room", ["Sleigh Ride", "Whoville"], 1600, [GrinchRamData(0x0101FB, binary_bit_pos=2)]),
"Skis in Who Forest": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1601, [GrinchRamData(0x0101FB, binary_bit_pos=3)]), "WF - Skis": GrinchLocationData("Sleigh Room", ["Sleigh Ride", "Who Forest"], 1601, [GrinchRamData(0x0101FB, binary_bit_pos=3)]),
"Tires in Who Dump": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1602, [GrinchRamData(0x0101FB, binary_bit_pos=4)]), "WD - Tires": GrinchLocationData("Sleigh Room", ["Sleigh Ride", "Who Dump"], 1602, [GrinchRamData(0x0101FB, binary_bit_pos=4)]),
"Twin-End Tuba in Submarine World": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1603, [GrinchRamData(0x0101FB, binary_bit_pos=6)]), "WL - Submarine World - Twin-End Tuba": GrinchLocationData("Sleigh Room", ["Sleigh Ride", "Who Lake", "South Shore"], 1603, [GrinchRamData(0x0101FB, binary_bit_pos=6)]),
"GPS in Who Lake": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1604, [GrinchRamData(0x0101FB, binary_bit_pos=5)]), "WL - South Shore - GPS": GrinchLocationData("Sleigh Room", ["Sleigh Ride", "Who Lake", "Submarine World"], 1604, [GrinchRamData(0x0101FB, binary_bit_pos=5)]),
# Mount Crumpit Locations # Mount Crumpit Locations
# "1st Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1700, [GrinchRamData(0x095343, value=1)]), "MC - 1st Crate Squashed": GrinchLocationData("Mount Crumpit", ["Mount Crumpit"], 1700, [GrinchRamData(0x095343, value=1)]),
# "2nd Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1701, [GrinchRamData(0x095343, value=2)]), "MC - 2nd Crate Squashed": GrinchLocationData("Mount Crumpit", ["Mount Crumpit"], 1701, [GrinchRamData(0x095343, value=2)]),
# "3rd Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1702, [GrinchRamData(0x095343, value=3)]), "MC - 3rd Crate Squashed": GrinchLocationData("Mount Crumpit", ["Mount Crumpit"], 1702, [GrinchRamData(0x095343, value=3)]),
# "4th Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1703, [GrinchRamData(0x095343, value=4)]), "MC - 4th Crate Squashed": GrinchLocationData("Mount Crumpit", ["Mount Crumpit"], 1703, [GrinchRamData(0x095343, value=4)]),
# "5th Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1704, [GrinchRamData(0x095343, value=5)]), "MC - 5th Crate Squashed": GrinchLocationData("Mount Crumpit", ["Mount Crumpit"], 1704, [GrinchRamData(0x095343, value=5)]),
} }
def grinch_locations_to_id() -> dict[str,int]: def grinch_locations_to_id() -> dict[str,int]:

View File

@@ -1,7 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from Options import FreeText, NumericOption, Toggle, DefaultOnToggle, Choice, TextChoice, Range, NamedRange, OptionList, \ from Options import FreeText, NumericOption, Toggle, DefaultOnToggle, Choice, TextChoice, Range, NamedRange, OptionList, \
PerGameCommonOptions PerGameCommonOptions, OptionSet
class StartingArea(Choice): class StartingArea(Choice):
""" """
@@ -21,7 +21,7 @@ class ProgressiveVacuum(Toggle):#DefaultOnToggle
Enabled: Whoville > Who Forest > Who Dump > Who Lake Enabled: Whoville > Who Forest > Who Dump > Who Lake
""" """
display_name = "Progressive Vacuum Access" display_name = "Progressive Vacuum Tubes"
class Missionsanity(Choice): class Missionsanity(Choice):
""" """
@@ -39,51 +39,95 @@ class Missionsanity(Choice):
option_both = 3 option_both = 3
default = 1 default = 1
class AnnoyingLocations(DefaultOnToggle): class ExcludeEnvironments(OptionSet):
"""Makes certain long, annoying, and tedious checks to be excluded [NOT IMPLEMENTED]""" """
display_name = "Annoying Locations" Allows entire environments to be an excluded location to ensure you are not logically required to enter the environment along
with any and all checks that are in that environment too.
WARNING: Excluding too many environments may cause generation to fail.
[NOT IMPLEMENTED]
Valid keys: "Whoville", "Who Forest", "Who Dump", "Who Lake", "Post Office", "Clock Tower", "City Hall",
"Ski Resort", "Civic Center", "Minefield", "Power Plant", "Generator Building", "Scout's Hut",
"North Shore", "Mayor's Villa", "Sleigh Ride"
"""
display_name = "Exclude Environments"
valid_keys = {"Whoville", "Who Forest", "Who Dump", "Who Lake", "Post Office", "Clock Tower", "City Hall",
"Ski Resort", "Civic Center", "Minefield", "Power Plant", "Generator Building", "Scout's Hut",
"North Shore", "Mayor's Villa", "Sleigh Ride"}
class ProgressiveGadget(Toggle):#DefaultOnToggle class ProgressiveGadget(Toggle):#DefaultOnToggle
""" """
Determines whether you get access to a gadget as individual blueprint count [NOT IMPLEMENTED] Determines whether you get access to a gadget as individual blueprint count. [NOT IMPLEMENTED]
""" """
display_name = "Progressive Gadgets" display_name = "Progressive Gadgets"
class Supadow(Toggle): class Supadow(Toggle):
"""Enables completing minigames through the Supadows in Mount Crumpit as checks. (9 locations) [NOT IMPLEMENTED]""" """Enables completing minigames through the Supadows in Mount Crumpit as checks. NOT IMPLEMENTED]"""
display_name = "Supadow Minigame Locations" display_name = "Supadow Minigames"
class Gifts(Range):
"""
Considers how many gifts must be squashed per check.
Enabling this will also enable squashing all gifts in a region mission along side this. [NOT IMPLEMENTED]
"""
display_name = "Gifts Squashed per Check"
range_start = 0
range_end = 300
default = 0
class Gifts(Toggle): class GadgetRando(OptionSet):
"""Missions that require you to squash every present in a level. (4 locations) [NOT IMPLEMENTED]""" """
display_name = "Gift Collection Locations" Randomizes Grinch's gadgets along with randomizing Binoculars into the pool. [NOT IMPLEMENTED]
"""
display_name = "Gadgets Randomized"
default = [
"Binoculars",
"Rotten Egg Launcher",
"Rocket Spring",
"Slime Shooter",
"Octopus Climbing Device",
"Marine Mobile",
"Grinch Copter"
]
class Moverando(OptionSet):
"""Randomizes Grinch's moveset along with randomizing max into the pool. [NOT IMPLEMENTED]
class Movesanity(Toggle): Valid keys: "Pancake", "Seize", "Max", "Bad Breath", "Sneak"
"""Randomizes Grinch's moveset along with randomizing max into the pool. [NOT IMPLEMENTED]""" """
display_name = "Movesanity" display_name = "Moves Randomized"
default = [
"Pancake",
"Seize",
"Max",
"Bad Breath",
"Sneak"
]
class UnlimitedEggs(Toggle): class UnlimitedEggs(Toggle):
"""Determine whether or not you run out of rotten eggs when you utilize your gadgets.""" """Determine whether or not you run out of rotten eggs when you utilize your gadgets."""
display_name = "Unlimited Rotten Eggs" display_name = "Unlimited Rotten Eggs"
class RingLinkOption(Toggle): class RingLinkOption(Toggle):
"""Whenever this is toggled, your ammo is linked with other ringlink-compatible games that also have this enabled. [NOT IMPLEMENTED]""" """Whenever this is toggled, your ammo is linked with other ringlink-compatible games that also have this enabled."""
display_name = "Ring Link"
class TrapLinkOption(Toggle): class TrapLinkOption(Toggle):
"""If a trap is sent from Grinch, traps that are compatible with other games are triggered aswell. [NOT IMPLEMENTED]""" """If a trap is sent from Grinch, traps that are compatible with other games are triggered as well. [NOT IMPLEMENTED]"""
display_name = "Trap Link"
@dataclass @dataclass
class GrinchOptions(PerGameCommonOptions):#DeathLinkMixin class GrinchOptions(PerGameCommonOptions):#DeathLinkMixin
starting_area: StartingArea starting_area: StartingArea
progressive_vacuum: ProgressiveVacuum progressive_vacuum: ProgressiveVacuum
missionsanity: Missionsanity missionsanity: Missionsanity
annoying_locations: AnnoyingLocations exclude_environments: ExcludeEnvironments
progressive_gadget: ProgressiveGadget progressive_gadget: ProgressiveGadget
supadow_minigames: Supadow supadow_minigames: Supadow
giftsanity: Gifts giftsanity: Gifts
movesanity: Movesanity gadget_rando: GadgetRando
move_rando: Moverando
unlimited_eggs: UnlimitedEggs unlimited_eggs: UnlimitedEggs
ring_link: RingLinkOption ring_link: RingLinkOption
trap_link: TrapLinkOption trap_link: TrapLinkOption

View File

@@ -19,7 +19,7 @@ mainareas_list = [
subareas_list = [ subareas_list = [
"Post Office", "Post Office",
"City Hall", "City Hall",
"Countdown to X-Mas Tower", "Clock Tower",
"Ski Resort", "Ski Resort",
"Civic Center", "Civic Center",
"Minefield", "Minefield",
@@ -83,12 +83,12 @@ def connect_regions(world: "GrinchWorld"):
grinchconnect(world, "Mount Crumpit", "Who Dump") grinchconnect(world, "Mount Crumpit", "Who Dump")
grinchconnect(world, "Mount Crumpit", "Who Lake") grinchconnect(world, "Mount Crumpit", "Who Lake")
grinchconnect(world, "Mount Crumpit", "Sleigh Room") grinchconnect(world, "Mount Crumpit", "Sleigh Room")
grinchconnect(world, "Mount Crumpit", "Spin N' Win Supadow") grinchconnect(world, "Mount Crumpit", "Spin N' Win")
grinchconnect(world, "Mount Crumpit", "Dankamania Supadow") grinchconnect(world, "Mount Crumpit", "Dankamania")
grinchconnect(world, "Mount Crumpit", "The Copter Race Contest Supadow") grinchconnect(world, "Mount Crumpit", "The Copter Race Contest")
grinchconnect(world, "Whoville", "Post Office") grinchconnect(world, "Whoville", "Post Office")
grinchconnect(world, "Whoville", "City Hall") grinchconnect(world, "Whoville", "City Hall")
grinchconnect(world, "Whoville", "Countdown to X-Mas Clock Tower") grinchconnect(world, "Whoville", "Clock Tower")
grinchconnect(world, "Who Forest", "Ski Resort") grinchconnect(world, "Who Forest", "Ski Resort")
grinchconnect(world, "Who Forest", "Civic Center") grinchconnect(world, "Who Forest", "Civic Center")
grinchconnect(world, "Who Dump", "Minefield") grinchconnect(world, "Who Dump", "Minefield")

View File

@@ -4,6 +4,7 @@ import Utils
from BaseClasses import CollectionState from BaseClasses import CollectionState
from worlds.AutoWorld import World from worlds.AutoWorld import World
from worlds.generic.Rules import add_rule from worlds.generic.Rules import add_rule
from .Items import *
#Adds all rules from access_rules_dict to locations #Adds all rules from access_rules_dict to locations
def set_location_rules(world: World): def set_location_rules(world: World):
@@ -31,418 +32,415 @@ def interpret_rule(rule_set: list[list[str]], player: int):
#Each item in the list is a separate list of rules. Each separate list is just an "OR" condition. #Each item in the list is a separate list of rules. Each separate list is just an "OR" condition.
rules_dict: dict[str,list[list[str]]] = { rules_dict: dict[str,list[list[str]]] = {
"Enter Whoville": [ "WV - First Visit": [
[] []
], ],
"Enter the Post Office": [ "WV - Post Office - First Visit": [
[] []
], ],
"Enter the Town Hall": [ "WV - City Hall - First Visit": [
[] []
], ],
"Enter the Countdown-To-Xmas Clock Tower": [ "WV - Clock Tower - First Visit": [
[] []
], ],
"Enter Who Forest": [ "WF - First Visit": [
[] []
], ],
"Enter the Ski Resort": [ "WF - Ski Resort - First Visit": [
[] []
], ],
"Enter the Civic Center": [ "WF - Civic Center - First Visit": [
[] []
], ],
"Enter Who Dump": [ "WD - First Visit": [
[] []
], ],
"Enter the Minefield": [ "WD - Minefield - First Visit": [
[] []
], ],
"Enter the Power Plant": [ "WD - Power Plant - First Visit": [
[] []
], ],
"Enter the Generator Building": [ "WD - Generator Building - First Visit": [
[] []
], ],
"Enter Who Lake": [ "WL - South Shore - First Visit": [
[] []
], ],
"Enter the Submarine World": [ "WL - Submarine World - First Visit": [
[] []
], ],
"Enter the Scout's Hut": [ "WL - Scout's Hut - First Visit": [
[] []
], ],
"Enter the North Shore": [ "WL - North Shore - First Visit": [
[] []
], ],
"Enter the Mayor's Villa": [ "WL - Mayor's Villa - First Visit": [
[] []
], ],
"Shuffling The Mail": [ "WV - Post Office - Shuffling The Mail": [
[] []
], ],
"Smashing Snowmen": [ "WV - Smashing Snowmen": [
[] []
], ],
"Painting The Mayor's Posters": [ "WV - Painting The Mayor's Posters": [
["Painting Bucket"] [PB]
], ],
"Launching Eggs Into Houses": [ "WV - Launching Eggs Into Houses": [
["Rotten Egg Launcher"] [REL]
], ],
"Modifying The Mayor's Statue": [ "WV - City Hall - Modifying The Mayor's Statue": [
["Sculpting Tools"] [ST]
], ],
"Advancing The Countdown-To-Xmas Clock": [ "WV - Clock Tower - Advancing The Countdown-To-Xmas Clock": [
["Hammer", "Rocket Spring"] [HMR, RS]
], ],
"Squashing All Gifts in Whoville": [ "WV - Squashing All Gifts": [
["Grinch Copter", "Slime Shooter", "Rotten Egg Launcher", "Who Cloak", "Rocket Spring"] [GC, SS, REL, WC, RS]
], ],
"Making Xmas Trees Droop": [ "WF - Making Xmas Trees Droop": [
["Rotten Egg Launcher"] [REL]
], ],
"Sabotaging Snow Cannon With Glue": [ "WF - Sabotaging Snow Cannon With Glue": [
["Glue Bucket", "Rocket Spring"], [GB, RS],
["Glue Bucket", "Grinch Copter"] [GB, GC]
], ],
"Putting Beehives In Cabins": [ "WF - Putting Beehives In Cabins": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"Sliming The Mayor's Skis": [ "WF - Ski Resort - Sliming The Mayor's Skis": [
["Slime Shooter", "Rotten Egg Launcher"] [SS, REL]
], ],
"Replacing The Candles On The Cake With Fireworks": [ "WF - Civic Center - Replacing The Candles On The Cake With Fireworks": [
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Rocket Spring"] [REL, OCD, RS]
], ],
"Squashing All Gifts in Who Forest": [ "WF - Squashing All Gifts": [
["Grinch Copter", "Cable Car Access Card", "Slime Shooter", "Rotten Egg Launcher"], [GC, CCAC, SS, REL],
["Octopus Climbing Device", "Rocket Spring", "Cable Car Access Card", "Slime Shooter", "Rotten Egg Launcher"] [OCD, RS, CCAC, SS, REL]
], ],
"Stealing Food From Birds": [ "WD - Stealing Food From Birds": [
["Rocket Spring", "Rotten Egg Launcher"] [RS, REL]
], ],
"Feeding The Computer With Robot Parts": [ "WD - Feeding The Computer With Robot Parts": [
["Rocket Spring", "Rotten Egg Launcher"] [RS, REL]
], ],
"Infesting The Mayor's House With Rats": [ "WD - Infesting The Mayor's House With Rats": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"Conducting The Stinky Gas To Who-Bris' Shack": [ "WD - Conducting The Stinky Gas To Who-Bris' Shack": [
["Rocket Spring", "Rotten Egg Launcher"] [RS, REL]
], ],
"Shaving Who Dump Guardian": [ "WD - Minefield - Shaving Who Dump Guardian": [
["Scissors", "Grinch Copter"], [SC, GC],
["Scissors", "Slime Shooter", "Rocket Spring"] [SC, SS, RS]
], ],
"Short-Circuiting Power-Plant": [ "WD - Generator Building - Short-Circuiting Power-Plant": [
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Slime Shooter", "Rocket Spring"] [REL, OCD, SS, RS]
], ],
"Squashing All Gifts in Who Dump": [ "WD - Squashing All Gifts": [
["Grinch Copter", "Rocket Spring", "Slime Shooter", "Rotten Egg Launcher"], [GC, RS, SS, REL],
["Octopus Climbing Device", "Rocket Spring", "Slime Shooter", "Rotten Egg Launcher"] [OCD, RS, SS, REL]
], ],
"Putting Thistles In Shorts": [ "WL - South Shore - Putting Thistles In Shorts": [
["Rotten Egg Launcher", "Octopus Climbing Device"], [REL, OCD],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"Sabotaging The Tents": [ "WL - South Shore - Sabotaging The Tents": [
["Octopus Climbing Device", "Rocket Spring"], [OCD, RS],
["Grinch Copter"] [GC]
], ],
"Drilling Holes In Canoes": [ "WL - North Shore - Drilling Holes In Canoes": [
["Drill"] [DRL]
# ["Drill", "Max"] # [DRL, MX]
], ],
"Modifying The Marine Mobile": [ "WL - Submarine World - Modifying The Marine Mobile": [
[] []
], ],
"Hooking The Mayor's Bed To The Motorboat": [ "WL - Mayor's Villa - Hooking The Mayor's Bed To The Motorboat": [
["Rope", "Hook", "Rotten Egg Launcher", "Scout Clothes"] [RP, HK, REL, SCL]
], ],
"Squashing All Gifts in Who Lake": [ "WL - Squashing All Gifts": [
["Grinch Copter", "Marine Mobile", "Scout Clothes", "Rotten Egg Launcher", "Hook", "Rope"], [GC, MM, SCL, REL, HK, RP],
["Octopus Climbing Device", "Rocket Spring", "Marine Mobile", "Scout Clothes", "Rotten Egg Launcher", "Hook", "Rope"] [OCD, RS, MM, SCL, REL, HK, RP]
], ],
"Binoculars Blueprint - Post Office Roof": [ "WV - Binoculars BP on Post Office Roof": [
[] []
], ],
"Binoculars Blueprint - City Hall Library - Left Side": [ "WV - City Hall - Binoculars BP left side of Library": [
[] []
], ],
"Binoculars Blueprint - City Hall Library - Front Side": [ "WV - City Hall - Binoculars BP front side of Library": [
[] []
], ],
"Binoculars Blueprint - City Hall Library - Right Side": [ "WV - City Hall - Binoculars BP right side of Library": [
[] []
], ],
"REL Blueprint - Outside City Hall": [ "WV - REL BP left of City Hall": [
[] []
], ],
"REL Blueprint - Outside Clock Tower": [ "WV - REL BP left of Clock Tower": [
[] []
], ],
"REL Blueprint - Post Office - Inside Silver Room": [ "WV - Post Office - REL BP inside Silver Room": [
["Who Cloak"] [WC]
# ["Who Cloak", "Max"] # [WC, MX]
], ],
"REL Blueprint - Post Office - After Mission Completion": [ "WV - Post Office - REL BP at Entrance Door after Mission Completion": [
["Who Cloak"] [WC]
# ["Who Cloak", "Max"] # [WC, MX]
], ],
"RS Blueprint - Behind Vacuum": [ "WF - RS BP behind Vacuum Tube": [
[] []
], ],
"RS Blueprint - Front of 2nd House near entrance": [ "WF - RS BP in front of 2nd House near Vacuum Tube": [
[] []
], ],
"RS Blueprint - Near Tree House on Ground": [ "WF - RS BP near Tree House on Ground": [
[] []
], ],
"RS Blueprint - Near Cable Car House": [ "WF - RS BP behind Cable Car House": [
[] []
], ],
"RS Blueprint - Near Who Snowball in Cave": [ "WF - RS BP near Who Snowball in Cave": [
[] []
], ],
"RS Blueprint - Branch Platform Closest to Glue Cannon": [ "WF - RS BP on Branch Platform closest to Glue Cannon": [
[] []
], ],
"RS Blueprint - Branch Platform Near Beast": [ "WF - RS BP on Branch Platform Near Beast": [
[] []
], ],
"RS Blueprint - Branch Platform Ledge Grab House": [ "WF - RS BP on Branch Platform Elevated next to House": [
[] []
], ],
"RS Blueprint - On Tree House": [ "WF - RS BP on Tree House": [
["Rotten Egg Launcher"], [REL],
["Grinch Copter"] [GC]
], ],
"SS Blueprint - Branch Platform Elevated House": [ "WF - SS BP in Branch Platform Elevated House": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - Branch Platform House next to Beast": [ "WF - SS BP in Branch Platform House next to Beast": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - House near Civic Center Cave": [ "WF - SS BP in House in front of Civic Center Cave": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - House next to Tree House": [ "WF - SS BP in House next to Tree House": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - House across from Tree House": [ "WF - SS BP in House across from Tree House": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - 2nd House near entrance right side": [ "WF - SS BP in 2nd House near Vacuum Tube Right Side": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - 2nd House near entrance left side": [ "WF - SS BP in 2nd House near Vacuum Tube Left Side": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - 2nd House near entrance inbetween blueprints": [ "WF - SS BP in 2nd House near Vacuum Tube inbetween Blueprints": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"SS Blueprint - House near entrance": [ "WF - SS BP in House near Vacuum Tube": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"OCD Blueprint - Middle Pipe": [ "WD - OCD BP inside Middle Pipe": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Slime Shooter", "Rocket Spring"], [SS, RS],
["Slime Shooter", "Grinch Copter"] [SS, GC]
], ],
"OCD Blueprint - Right Pipe": [ "WD - OCD BP inside Right Pipe": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"OCD Blueprint - Mayor's House Rat Vent": [ "WD - OCD BP in Vent to Mayor's House": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"OCD Blueprint - Left Pipe": [ "WD - OCD BP inside Left Pipe": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Slime Shooter", "Rocket Spring"], [SS, RS],
["Slime Shooter", "Grinch Copter"] [SS, GC]
], ],
"OCD Blueprint - Near Power Plant Wall on right side": [ "WD - OCD BP near Right Side of Power Plant Wall": [
["Rotten Egg Launcher", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Slime Shooter", "Rocket Spring"], [SS, RS],
["Slime Shooter", "Grinch Copter"] [SS, GC]
], ],
"OCD Blueprint - Near Who-Bris' Shack": [ "WD - OCD BP near Who-Bris' Shack": [
["Rotten Egg Launcher", "Rocket Spring"] [REL, RS]
], ],
"OCD Blueprint - Guardian's House - Left Side": [ "WD - Minefield - OCD BP on Left Side of House": [
[] []
# ["Rotten Egg Launcher", "Grinch Copter"], # [REL, GC],
# ["Rotten Egg Launcher", "Slime Shooter", "Rocket Spring"] # [REL, SS, RS]
# ["Max"] # [MX]
], ],
"OCD Blueprint - Guardian's House - Right Side": [ "WD - Minefield - OCD BP on Right Side of Shack": [
["Grinch Copter"], [GC],
["Slime Shooter", "Rocket Spring"] [SS, RS]
], ],
"OCD Blueprint - Inside Guardian's House": [ "WD - Minefield - OCD BP inside Guardian's House": [
[] []
# ["Rotten Egg Launcher", "Grinch Copter"], # [REL, GC],
# ["Rotten Egg Launcher", "Slime Shooter", "Rocket Spring"] # [REL, SS, RS]
# ["Max"] # [MX]
], ],
"MM Blueprint - South Shore - Bridge to Scout's Hut": [ "WL - South Shore - MM BP on Bridge to Scout's Hut": [
[] []
], ],
"MM Blueprint - South Shore - Tent near Porcupine": [ "WL - South Shore - MM BP across from Tent near Porcupine": [
[] []
], ],
"MM Blueprint - South Shore - Near Outhouse": [ "WL - South Shore - MM BP near Outhouse": [
[] []
], ],
"MM Blueprint - South Shore - Near Hill Bridge": [ "WL - South Shore - MM BP near Hill Bridge": [
[] []
], ],
"MM Blueprint - South Shore - Scout's Hut Roof": [ "WL - South Shore - MM BP on Scout's Hut Roof": [
["Rocket Spring"], [RS],
["Grinch Copter"] [GC]
], ],
"MM Blueprint - South Shore - Grass Platform": [ "WL - South Shore - MM BP on Grass Platform": [
["Rocket Spring"], [RS],
["Grinch Copter"] [GC]
], ],
"MM Blueprint - South Shore - Zipline by Beast": [ "WL - South Shore - MM BP across Zipline Platform": [
["Rocket Spring", "Octopus Climbing Device"], [RS, OCD],
["Grinch Copter"] [GC]
], ],
"MM Blueprint - South Shore - Behind Summer Beast": [ "WL - South Shore - MM BP behind Summer Beast": [
["Rotten Egg Launcher", "Octopus Climbing Device"], [REL, OCD],
["Grinch Copter"] [GC]
], ],
"MM Blueprint - South Shore - Below Bridge": [ "WL - North Shore - MM BP below Bridge": [
[] []
], ],
"MM Blueprint - North Shore - Below Bridge": [ "WL - North Shore - MM BP behind Skunk Hut": [
[] []
], ],
"MM Blueprint - North Shore - Behind Skunk Hut": [ "WL - North Shore - MM BP inside Skunk Hut": [
[]
# [MX]
],
"WL - North Shore - MM BP inside House's Fence": [
[]
# [MX]
],
"WL - North Shore - MM BP inside Boulder Box near Bridge": [
[] []
], ],
"MM Blueprint - North Shore - Inside Skunk Hut": [ "WL - North Shore - MM BP inside Boulder Box behind Skunk Hut": [
[]
# ["Max"]
],
"MM Blueprint - North Shore - Fenced in Area": [
[]
# ["Max"]
],
"MM Blueprint - North Shore - Boulder Box near Bridge": [
[] []
], ],
"MM Blueprint - North Shore - Boulder Box behind Skunk Hut": [ "WL - North Shore - MM BP inside Drill House": [
[] []
], ],
"MM Blueprint - North Shore - Inside Drill House": [ "WL - North Shore - MM BP on Crow Platform near Drill House": [
[] []
], ],
"MM Blueprint - North Shore - Crow Platform near Drill House": [ "WV - City Hall - GC BP in Safe Room": [
[] []
], ],
"GC Blueprint - Whoville City Hall - Safe Room": [ "WV - City Hall - GC BP in Statue Room": [
[] []
], ],
"GC Blueprint - Whoville City Hall - Statue Room": [ "WV - Clock Tower - GC BP in Bedroom": [
[RS]
# [MX, RS]
],
"WV - Clock Tower - GC BP in Bell Room": [
[RS]
],
"WF - Ski Resort - GC BP inside Dog's Fence": [
[] []
], ],
"GC Blueprint - Whoville Clock Tower - Before Bells": [ "WF - Ski Resort - GC BP in Max Cave": [
["Rocket Spring"]
# ["Max", "Rocket Spring"]
],
"GC Blueprint - Whoville Clock Tower - After Bells": [
["Rocket Spring"]
],
"GC Blueprint - Who Forest Ski Resort - Inside Dog's Fence": [
[] []
# [MX]
], ],
"GC Blueprint - Who Forest Ski Resort - Max Cave": [ "WF - Civic Center - GC BP on Left Side in Bat Cave Wall": [
[GC],
[OCD, RS]
],
"WF - Civic Center - GC BP in Frozen Ice": [
[REL, GC],
[REL, OCD, RS],
[SS, GC],
[SS, OCD, RS]
],
"WD - Power Plant - GC BP in Max Cave": [
[] []
# ["Max"] # [MX]
], ],
"GC Blueprint - Who Forest Civic Center - Climb across Bat Cave wall": [ "WD - Power Plant - GC BP After First Gate": [
["Grinch Copter"], [REL, RS],
["Octopus Climbing Device", "Rocket Spring"] [GC]
# [MX, REL, RS]
], ],
"GC Blueprint - Who Forest Civic Center - Shoot Icicle in Bat Entrance": [ "WD - Generator Building - GC BP on the Highest Platform": [
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Rocket Spring"], [REL, OCD, SS, RS]
["Slime Shooter", "Grinch Copter"],
["Slime Shooter", "Octopus Climbing Device", "Rocket Spring"]
], ],
"GC Blueprint - Who Dump Power Plant - Max Cave": [ "WD - Generator Building - GC BP at the Entrance after Mission Completion": [
[] [REL, GC],
# ["Max"] [REL, OCD, SS, RS]
], ],
"GC Blueprint - Who Dump Power Plant - After First Gate": [ "WL - Submarine World - GC BP Just Below Water Surface": [
["Rotten Egg Launcher", "Rocket Spring"], [MM]
["Grinch Copter"]
# ["Max", "Rotten Egg Launcher", "Rocket Spring"]
], ],
"GC Blueprint - Who Dump Generator Building - Before Mission": [ "WL - Submarine World - GC BP Underwater": [
["Rotten Egg Launcher", "Grinch Copter"], [MM]
["Rotten Egg Launcher", "Octopus Climbing Device", "Slime Shooter", "Rocket Spring"]
], ],
"GC Blueprint - Who Dump Generator Building - After Mission": [ "WL - Mayor's Villa - GC BP on Tree Branch": [
["Rotten Egg Launcher", "Grinch Copter"], [GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Slime Shooter", "Rocket Spring"] [REL, RS]
], ],
"GC Blueprint - Who Lake South Shore - Submarine World - Above Surface": [ "WL - Mayor's Villa - GC BP in Pirate's Cave": [
["Marine Mobile"] [GC],
[REL, RS]
], ],
"GC Blueprint - Who Lake South Shore - Submarine World - Underwater": [ "MC - Sleigh Ride - Stealing All Gifts": [
["Marine Mobile"]
],
"GC Blueprint - Who Lake North Shore - Mayor's Villa - Tree Branch": [
["Grinch Copter"],
["Rotten Egg Launcher", "Rocket Spring"]
],
"GC Blueprint - Who Lake North Shore - Mayor's Villa - Cave": [
["Grinch Copter"],
["Rotten Egg Launcher", "Rocket Spring"]
],
"Stealing All Gifts": [
# ["Exhaust Pipes", "Tires", "Skis", "Twin-End Tuba"] # ["Exhaust Pipes", "Tires", "Skis", "Twin-End Tuba"]
["Rotten Egg Launcher", "Who Forest Vacuum Access", "Who Dump Vacuum Access", "Who Lake Vacuum Access", "Rocket Spring", "Marine Mobile"] [REL, WV, WF, WD, WL, RS, MM]
], ],
"Neutralizing Santa": [ "MC - Sleigh Ride - Neutralizing Santa": [
# ["Exhaust Pipes", "Tires", "Skis", "Twin-End Tuba"] # ["Exhaust Pipes", "Tires", "Skis", "Twin-End Tuba"]
["Rotten Egg Launcher", "Who Forest Vacuum Access", "Who Dump Vacuum Access", "Who Lake Vacuum Access", "Rocket Spring", "Marine Mobile"] [REL, WV, WF, WD, WL, RS, MM]
], ],
"Heart of Stone - Whoville's Post Office": [ "WV - Post Office - Heart of Stone": [
[] []
], ],
"Heart of Stone - Who Forest's Ski Resort": [ "WF - Ski Resort - Heart of Stone": [
[] []
], ],
"Heart of Stone - Who Dump's Minefield": [ "WD - Minefield - Heart of Stone": [
["Grinch Copter"], [GC],
["Rotten Egg Launcher", "Slime Shooter", "Rocket Spring"] [REL, SS, RS]
], ],
"Heart of Stone - Who Lake's North Shore": [ "WL - North Shore - Heart of Stone": [
[] []
# ["Max"] # [MX]
], ],
"Spin N' Win - Easy": [ "Spin N' Win - Easy": [
[] []
@@ -480,111 +478,125 @@ rules_dict: dict[str,list[list[str]]] = {
"Bike Race - Top 3": [ "Bike Race - Top 3": [
[] []
], ],
"Exhaust Pipes in Whoville": [ "WV - Exhaust Pipes": [
["Rotten Egg Launcher"] [WV, REL]
], ],
"Skis in Who Forest": [ "WF - Skis": [
["Who Forest Vacuum Access"] [WF]
], ],
"Tires in Who Dump": [ "WD - Tires": [
["Who Dump Vacuum Access", "Rocket Spring", "Rotten Egg Launcher"] [WD, RS, REL]
], ],
"Twin-End Tuba in Submarine World": [ "WL - Submarine World - Twin-End Tuba": [
["Who Lake Vacuum Access", "Marine Mobile"] [WL, MM]
], ],
"GPS in Who Lake": [ "WL - South Shore - GPS": [
["Who Lake Vacuum Access", "Rotten Egg Launcher"] [WL, REL]
# ], ],
# "1st Crate Squashed": [ "MC - 1st Crate Squashed": [
# [] []
# ], ],
# "2nd Crate Squashed": [ "MC - 2nd Crate Squashed": [
# [] []
# ], ],
# "3rd Crate Squashed": [ "MC - 3rd Crate Squashed": [
# [] []
# ], ],
# "4th Crate Squashed": [ "MC - 4th Crate Squashed": [
# [] []
# ], ],
# "5th Crate Squashed": [ "MC - 5th Crate Squashed": [
# [] []
] ]
# "Green Present": [
# []
# ],
# "Red Present": [
# []
# ],
# "Pink Present": [
# [REL],
# [PC]
# ],
# "Yellow Present": [
# [PC]
# ]
} }
access_rules_dict: dict[str,list[list[str]]] = { access_rules_dict: dict[str,list[list[str]]] = {
"Whoville": [ "Whoville": [
[] [WV]
], ],
"Post Office": [ "Post Office": [
["Who Cloak"] [WC]
], ],
"City Hall": [ "City Hall": [
["Rotten Egg Launcher"] [REL]
], ],
"Countdown to X-Mas Clock Tower": [ "Clock Tower": [
[] []
], ],
"Who Forest": [ "Who Forest": [
["Who Forest Vacuum Access"], [WF],
# ["Progressive Vacuum Access": 1] # [VT: 1]
], ],
"Ski Resort": [ "Ski Resort": [
["Cable Car Access Card"] [CCAC]
], ],
"Civic Center": [ "Civic Center": [
["Grinch Copter"], [GC],
["Octopus Climbing Device"] [OCD]
], ],
"Who Dump": [ "Who Dump": [
["Who Dump Vacuum Access"], [WD],
# ["Progressive Vacuum Access": 2] # [VT: 2]
], ],
"Minefield": [ "Minefield": [
["Rotten Egg Launcher", "Slime Shooter", "Rocket Spring"], [REL, RS],
["Rotten Egg Launcher", "Grinch Copter"] [REL, GC]
], ],
"Power Plant": [ "Power Plant": [
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Slime Shooter", "Grinch Copter"], [SS, GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Slime Shooter", "Rocket Spring"] [REL, OCD, SS, RS]
], ],
"Generator Building": [ "Generator Building": [
["Rotten Egg Launcher", "Grinch Copter"], [REL, GC],
["Rotten Egg Launcher", "Octopus Climbing Device", "Slime Shooter", "Rocket Spring"] [REL, OCD, SS, RS]
], ],
"Who Lake": [ "Who Lake": [
["Who Lake Vacuum Access"], [WL],
# ["Progressive Vacuum Access": 3] # [VT: 3]
], ],
"Scout's Hut": [ "Scout's Hut": [
["Grinch Copter"], [GC],
["Rocket Spring"] [RS]
], ],
"North Shore": [ "North Shore": [
["Scout Clothes"] [SCL]
], ],
"Mayor's Villa": [ "Mayor's Villa": [
["Scout Clothes"] [SCL]
], ],
"Submarine World": [ "Submarine World": [
["Marine Mobile"] [MM]
], ],
"Sleigh Room": [ "Sleigh Room": [
["Sleigh Room Key"] [SR]
], ],
"Spin N' Win Supadow": [ "Spin N' Win": [
[] []
# ["Spin N' Win Door Unlock"], # ["Spin N' Win Door Unlock"],
# ["Progressive Supadow Door Unlock"] # ["Progressive Supadow Door Unlock"]
], ],
"Dankamania Supadow": [ "Dankamania": [
[] []
# ["Dankamania Door Unlock"], # ["Dankamania Door Unlock"],
# ["Progressive Supadow Door Unlock: 2"] # ["Progressive Supadow Door Unlock: 2"]
], ],
"The Copter Race Contest Supadow": [ "The Copter Race Contest": [
[] []
# ["The Copter Race Contest Door Unlock"], # ["The Copter Race Contest Door Unlock"],
# ["Progressive Supadow Door Unlock: 3"] # ["Progressive Supadow Door Unlock: 3"]

View File

@@ -1,6 +1,6 @@
from BaseClasses import Region, Item, ItemClassification from BaseClasses import Region, Item, ItemClassification
from .Locations import grinch_locations_to_id, grinch_locations, GrinchLocation from .Locations import grinch_locations_to_id, grinch_locations, GrinchLocation, get_location_names_per_category
from .Items import grinch_items_to_id, GrinchItem, ALL_ITEMS_TABLE, MISC_ITEMS_TABLE from .Items import grinch_items_to_id, GrinchItem, ALL_ITEMS_TABLE, MISC_ITEMS_TABLE, get_item_names_per_category
from .Regions import connect_regions from .Regions import connect_regions
from .Rules import set_location_rules from .Rules import set_location_rules
@@ -8,8 +8,9 @@ from .Client import *
from typing import ClassVar from typing import ClassVar
from worlds.AutoWorld import World from worlds.AutoWorld import World
from Options import OptionError
from . import Options from .Options import GrinchOptions
from .Rules import access_rules_dict from .Rules import access_rules_dict
@@ -21,14 +22,18 @@ class GrinchWorld(World):
item_name_to_id: ClassVar[dict[str,int]] = grinch_items_to_id() item_name_to_id: ClassVar[dict[str,int]] = grinch_items_to_id()
location_name_to_id: ClassVar[dict[str,int]] = grinch_locations_to_id() location_name_to_id: ClassVar[dict[str,int]] = grinch_locations_to_id()
required_client_version = (0, 6, 3) required_client_version = (0, 6, 3)
item_name_groups = get_item_names_per_category()
location_name_groups = get_location_names_per_category()
def __init__(self, *args, **kwargs): #Pulls __init__ function and takes control from there in BaseClasses.py def __init__(self, *args, **kwargs): #Pulls __init__ function and takes control from there in BaseClasses.py
self.origin_region_name: str = "Mount Crumpit" self.origin_region_name: str = "Mount Crumpit"
super(GrinchWorld, self).__init__(*args, **kwargs) super(GrinchWorld, self).__init__(*args, **kwargs)
def generate_early(self) -> None: #Special conditions changed before generation occurs def generate_early(self) -> None: #Special conditions changed before generation occurs
pass if self.options.ring_link == 1 and self.options.unlimited_eggs == 1:
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}")
def create_regions(self): #Generates all regions for the multiworld def create_regions(self): #Generates all regions for the multiworld
for region_name in access_rules_dict.keys(): for region_name in access_rules_dict.keys():
@@ -37,7 +42,7 @@ class GrinchWorld(World):
for location, data in grinch_locations.items(): for location, data in grinch_locations.items():
region = self.get_region(data.region) region = self.get_region(data.region)
entry = GrinchLocation(self.player, location, region, data) entry = GrinchLocation(self.player, location, region, data)
if location == "Neutralizing Santa": if location == "MC - Sleigh Ride - Neutralizing Santa":
entry.place_locked_item(Item("Goal", ItemClassification.progression, None, self.player)) entry.place_locked_item(Item("Goal", ItemClassification.progression, None, self.player))
region.locations.append(entry) region.locations.append(entry)
connect_regions(self) connect_regions(self)
@@ -72,7 +77,7 @@ class GrinchWorld(World):
def fill_slot_data(self): def fill_slot_data(self):
return { return {
"give_unlimited_eggs": self.options.unlimited_eggs.value, "give_unlimited_eggs": self.options.unlimited_eggs.value,
"ring_link": self.options.ring_link.value,
} }
def generate_output(self, output_directory: str) -> None: def generate_output(self, output_directory: str) -> None:

View File

@@ -1,5 +1,7 @@
- Credit to Raven-187 & Hacc on gamehacking.org for providing the addresses for various cheat codes. Without them, this - Credit to Raven-187 & Hacc on gamehacking.org for providing the addresses for various cheat codes. Without them, this
would of made RAM searching much more tedious. would of made RAM searching much more tedious.
- Shoutouts to SomeJakeGuy for basically teaching me how to code in general. - Shoutouts to SomeJakeGuy for basically teaching me how to code in general along with starting decompilation of the game
into motion
- Shoutouts to BootsinSoots for helping with the implementation of the logic rules code itself. - Shoutouts to BootsinSoots for helping with the implementation of the logic rules code itself.
- Thanks to the Grinch PS1 speedrunning discord community server for encouraging the production of this randomizer. - Thanks to the Grinch PS1 speedrunning discord community server for encouraging the production of this randomizer.
- Credit to Artamiss for the vast majority of BZZ decompilation and general backend coding.

View File

@@ -1,31 +1,44 @@
# The Grinch - Setup Guide # The Grinch (PS1) - Setup Guide
## Required Software ## Required Software
- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases). Please use version 0.6.2 or later for integrated - [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases). Please use version 0.6.3 or later for integrated
BizHawk support. BizHawk support.
- Legally obtained NTSC Bin ROM file, probably named something like `Grinch, The (USA) (En,Fr,Es).bin`. - Legally obtained NTSC Bin ROM file, probably named something like `Grinch, The (USA) (En,Fr,Es).bin`.
- CUE files may work, but I have not tested this. The game's CUE file should also work aswell along side the BIN file if you have troubles opening the BIN file.
- [BizHawk](https://tasvideos.org/BizHawk/ReleaseHistory) Version 2.9.1 is supported, but I can't promise if any version is stable or not. - [BizHawk](https://tasvideos.org/BizHawk/ReleaseHistory) Versions 2.9.1 & 2.10 is supported, but I can't promise if any version is stable or not. As of September 2025, the Grinch may not be compatible with 2.11 yet.
- The latest `grinch.apworld` file. You can find this on the [Releases page](https://github.com/MarioSpore/Grinch-AP/releases/latest). Put this in your `Archipelago/custom_worlds` folder. - The latest `grinch.apworld` file. You can find this on the [Releases page](https://github.com/MarioSpore/Grinch-AP/releases/latest). Put this in your `Archipelago/custom_worlds` folder.
- PSX BIOS Firmware bin file, which is required to run the game through Bizhawk. The file you need should be
named something like `SCPH-5501.BIN`.
## Configuring your Config (.yaml) file ## Configuring BizHawk
Once you have installed BizHawk, open `EmuHawk.exe` and change the following settings:
### What is a config file and why do I need one? - If you're using BizHawk 2.7 or 2.8, go to `Config > Customize`. On the Advanced tab, switch the Lua Core from
`NLua+KopiLua` to `Lua+LuaInterface`, then restart EmuHawk. (If you're using BizHawk 2.9, you can skip this step.)
- Under `Config > Customize`, check the "Run in background" option to prevent disconnecting from the client while you're
tabbed out of EmuHawk.
- Under `Config > Preferred Cores > PSX`, select NymaShock.
- Open any PlayStation game in EmuHawk and go to `Config > Controllers…` to configure your inputs. If you can't click
`Controllers…`, it's because you need to load a game first.
You may need to invert Sensitivity for the up/down axis to -100%.
This can be found under Analog Controls through `Config > Controllers…`.
Depending on your controller, you may also want to tweak the Deadzone. Something like 6% is recommended for a DualShock 4.
- Consider clearing keybinds in `Config > Hotkeys…` if you don't intend to use them. Select the keybind and press Esc to
clear it.
- You are required to legally obtain a PSX Bios BIN firmware file for the game to be opened. To import this, you go to
`Config > Firmware... > Tools` and scrolling until you see the PlayStation tab. You might right click on the bios region
and click `Set Customization` or `Import` on the top of the window. Then a window should open, telling you what file to
import, which is the BIN file required. The bios should be recognized if Bizhawk displays a checkmark beside it, saying
the bios version you have is stable and should run without issues. If a bios is already been imported and the game
runs fine by itself, you may skip this step.
See the guide on setting up a basic YAML at the Archipelago setup ## Generating a Game
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file? 1. Create your options file (YAML). After installing the `grinch.apworld` file, you can generate a template within the Archipelago Launcher by clicking `Generate Template Settings`.
2. Follow the general Archipelago instructions for [generating a game](https://archipelago.gg/tutorial/Archipelago/setup/en#generating-a-game).
The Player options page on the website allows you to configure your personal 3. Open `ArchipelagoLauncher.exe`
options and export a config file from them: [The Grinch Player Options Page](../player-options) 4. Select "BizHawk Client" in the right-side column. On your first time opening BizHawk Client, you will also be asked to
locate `EmuHawk.exe` in your BizHawk install.
### Verifying your config file
If you would like to validate your config file to make sure it works, you may do
so on the YAML Validator page: [YAML Validation page](/check)
## Joining a MultiWorld Game
### Connect to the Multiserver ### Connect to the Multiserver
@@ -37,18 +50,3 @@ script. Navigate to your Archipelago install folder and open `data/lua/connector
To connect the client to the multiserver simply put `<address>:<port>` on the text field on top and To connect the client to the multiserver simply put `<address>:<port>` on the text field on top and
press enter (if the server uses a password, type in the bottom text field press enter (if the server uses a password, type in the bottom text field
`/connect <address>:<port> [password]`) `/connect <address>:<port> [password]`)
## Hosting a MultiWorld game
The recommended way to host a game is to use our hosting service. The process is relatively simple:
1. Collect config files from your players.
2. Upload the config files to the Generate page above.
- Generate page: [WebHost Seed Generation Page](/generate)
3. Wait a moment while the seed is generated.
4. When the seed is generated, you will be redirected to a "Seed Info" page.
5. Click "Create New Room". This will take you to the server page. Provide the link to this page to
your players, so they may download their patch files from there.
6. Note that a link to a MultiWorld Tracker is at the top of the room page. The tracker shows the
progress of all players in the game. Any observers may also be given the link to this page.
7. Once all players have joined, you may begin playing.