19 Commits

Author SHA1 Message Date
MarioSpore
b3749b7fe3 Somehow, OR conditional logic was STILL not being considered. This should fix it. 2025-09-07 12:56:26 -04:00
MarioSpore
3aaf625282 Comment out mount crumpit checks until v1.1 2025-09-06 21:54:53 -04:00
MarioSpore
9df2360b8b Adds no jump trap 2025-09-06 21:03:08 -04:00
MarioSpore
d61ac9a135 Implement crate tutorial checks and logic 2025-09-06 21:02:54 -04:00
MarioSpore
c8fc56d7c4 No longer requires REL if you have GC for the guardian house right side location 2025-09-06 20:10:52 -04:00
MarioSpore
51aad167cc Update ap connection detection to only after the slot name is entered and you fully connect 2025-09-06 20:05:36 -04:00
MarioSpore
e2def66522 Added comment explaining recently added except block 2025-09-06 19:43:57 -04:00
MarioSpore
73e9d9d577 Added 2nd exception if theres other error types while playing the game 2025-09-06 19:37:46 -04:00
MarioSpore
a5d7ff65c1 Update constant address update to always give or take away mission specific items/keys 2025-09-06 19:22:34 -04:00
MarioSpore
05bf60abf7 Part 2 of fixing test_default_all_state_can_reach_everything to comply with banadium 2025-09-06 17:26:38 -04:00
MarioSpore
7f627e2c07 Remove duplication of "Supadow" option 2025-09-06 17:00:59 -04:00
MarioSpore
19e0fe1286 Temporairly disable supadow regions to comply with banadium to test_default_all_state_can_reach_everything 2025-09-06 16:59:00 -04:00
MarioSpore
b390974019 Fixes
"AssertionError: True is not false : Unexpected assignment to GrinchWorld.options!"
2025-09-06 16:50:38 -04:00
MarioSpore
9da65fab09 psuedocode more traplink 2025-09-05 20:18:33 -04:00
MarioSpore
02d2eab5a4 psuedocode more traplink 2025-09-05 20:18:20 -04:00
MarioSpore
985c8b681b ring link psuedocode part 3 2025-09-05 00:09:25 -04:00
MarioSpore
cf5a4012c0 ring link psuedocode part 2 2025-09-05 00:02:01 -04:00
MarioSpore
c59e75ef7b Ring link psuedocode, thanks for graymondgt for getting this started 2025-09-04 23:48:53 -04:00
MarioSpore
2dbe344348 More trap link psuedo code 2025-09-04 22:51:14 -04:00
6 changed files with 97 additions and 17 deletions

View File

@@ -1,3 +1,4 @@
import time
from typing import TYPE_CHECKING
import asyncio
import NetUtils
@@ -78,6 +79,9 @@ class GrinchClient(BizHawkClient):
self.loc_unlimited_eggs = bool(ctx.slot_data["give_unlimited_eggs"])
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.")
# tags = args.get("tags", [])
# if "RingLink" in tags:
# ring_link_input(self, args["data"])
async def set_auth(self, ctx: "BizHawkClientContext") -> None:
await ctx.get_username()
@@ -85,7 +89,7 @@ class GrinchClient(BizHawkClient):
async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
from CommonClient import logger
#If the player is not connected to an AP Server, or their connection was disconnected.
if ctx.server is None or ctx.server.socket.closed or ctx.slot_data is None:
if not ctx.slot:
return
try:
@@ -97,11 +101,17 @@ class GrinchClient(BizHawkClient):
await self.goal_checker(ctx)
await self.option_handler(ctx)
await self.constant_address_update(ctx)
# await self.ring_link_input(args["args"])
except bizhawk.RequestFailedError as ex:
# The connector didn't respond. Exit handler and return to main loop to reconnect
logger.error("Failure to connect / authenticate the grinch. Error details: " + str(ex))
pass
except Exception as genericEx:
# For all other errors, catch this and let the client gracefully disconnect
logger.error("Unknown error occurred while playing the grinch. Error details: " + str(genericEx))
await ctx.disconnect(False)
pass
async def location_checker(self, ctx: "BizHawkClientContext"):
from CommonClient import logger
@@ -221,19 +231,25 @@ class GrinchClient(BizHawkClient):
for (item_name, item_data) in items_to_check.items():
# If item is an event or already been received, ignore.
if item_data.id is None or GrinchLocation.get_apid(item_data.id) in list_recv_itemids:
if item_data.id is None: # or GrinchLocation.get_apid(item_data.id) in list_recv_itemids:
continue
# This assumes we don't have the item so we must set all the data to 0
# This will either constantly update the item to ensure you still have it or take it away if you don't deserve it
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
if is_binary:
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")
current_bin_value &= ~(1 << addr_to_update.binary_bit_pos)
if GrinchLocation.get_apid(item_data.id) in list_recv_itemids:
current_bin_value |= (1 << addr_to_update.binary_bit_pos)
else:
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)
else:
await self.update_and_validate_address(ctx, addr_to_update.ram_address, 0, 1)
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)
else:
await self.update_and_validate_address(ctx, addr_to_update.ram_address, 0, 1)
async def ingame_checker(self, ctx: "BizHawkClientContext"):
from CommonClient import logger
@@ -285,3 +301,39 @@ class GrinchClient(BizHawkClient):
if address_to_validate == 0x010000 or address_to_validate == 0x08FB94: # TODO Temporairly skips teleportation addresses; to be changed later on.
return
raise Exception("Unable to update address as expected. Address: "+ str(address_to_validate)+"; Expected Value: "+str(expected_value))
# async def ring_link_output(self, ctx: "BizHawkClientContext", byte_size: int):
# bizhawk.seek(0x010058)
# byte_size = 2
# current_eggs = int.from_bytes(byte_size=2, byteorder="little")
# difference = current_eggs - ctx.previous_eggs
# ctx.previous_eggs = current_eggs + ctx.ring_link_eggs
#
# if difference != 0:
# # logger.info("got here with a difference of " + str(difference))
# 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

@@ -203,6 +203,7 @@ TRAPS_TABLE: dict[str, GrinchItemData] = {
# "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)])
# "Child Trap": GrinchItemData("Traps", 612, IC.trap,[GrinchRamData()])
# "Disable Jump Trap": GrinchItemData("Traps", 613, IC.trap,[GrinchRamData(0x010026, binary_bit_pos=6)])
}
#Movesets
@@ -228,12 +229,15 @@ ALL_ITEMS_TABLE: dict[str, GrinchItemData] = {
# Psuedocoding traplink table
# BEE_TRAP_EQUIV = ["Army Trap", "Buyon Trap", "Ghost", "Gooey Bag", "OmoTrap", "Police Trap"]
# ICE_TRAP_EQUIV = ["Chaos Control Trap", "Freeze Trap", "Frozen Trap", "Honey Trap", "Paralyze Trap", "Stun Trap", "Bubble Trap"]
# DAMAGE_TRAP_EQUIV = ["Banana Trap", "Bomb", "Bonk Trap", "Fire Trap", "Laughter Trap", "Nut Trap", "Push Trap", "Squash Trap", "Thwimp Trap", "TNT Barrel Trap", "Meteor Trap"]
# DAMAGE_TRAP_EQUIV = ["Banana Trap", "Bomb", "Bonk Trap", "Fire Trap", "Laughter Trap", "Nut Trap", "Push Trap",
# "Squash Trap", "Thwimp Trap", "TNT Barrel Trap", "Meteor Trap", "Double Damage", "Spike Ball Trap"]
# SPRING_TRAP_EQUIV = ["Eject Ability", "Hiccup Trap", "Jump Trap", "Jumping Jacks Trap", "Whoops! Trap"]
# HOME_TRAP_EQUIV = ["Blue Balls Curse", "Instant Death Trap"]
# HOME_TRAP_EQUIV = ["Blue Balls Curse", "Instant Death Trap", "Get Out Trap"]
# SLOWNESS_TRAP_EQUIV = ["Iron Boots Trap", "Slow Trap", "Sticky Floor Trap"]
# CUTSCENE_TRAP_EQUIV = ["Phone Trap"]
# ELEC_TRAP_EQUIV = []
# DEPL_TRAP_EQUIV = ["Dry Trap"]
def grinch_items_to_id() -> dict[str, int]:
item_mappings: dict[str, int] = {}

View File

@@ -182,6 +182,12 @@ grinch_locations = {
"Tires in Who Dump": GrinchLocationData("Sleigh Room", "Sleigh Ride", 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)]),
"GPS in Who Lake": GrinchLocationData("Sleigh Room", "Sleigh Ride", 1604, [GrinchRamData(0x0101FB, binary_bit_pos=5)]),
# Mount Crumpit Locations
# "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)]),
# "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)]),
# "5th Crate Squashed": GrinchLocationData("Mount Crumpit", "Mount Crumpit", 1704, [GrinchRamData(0x095343, value=5)]),
}
def grinch_locations_to_id() -> dict[str,int]:

View File

@@ -80,7 +80,6 @@ class GrinchOptions(PerGameCommonOptions):#DeathLinkMixin
progressive_vacuum: ProgressiveVacuum
missionsanity: Missionsanity
annoying_locations: AnnoyingLocations
minigamesanity: Supadow
progressive_gadget: ProgressiveGadget
supadow_minigames: Supadow
giftsanity: Gifts

View File

@@ -26,7 +26,7 @@ def interpret_rule(rule_set: list[list[str]], player: int):
access_list: list[Callable[[CollectionState], bool]] = []
for item_set in rule_set:
access_list.append(lambda state: state.has_all(item_set, player))
access_list.append(lambda state, items=tuple(item_set): state.has_all(items, player))
return access_list
#Each item in the list is a separate list of rules. Each separate list is just an "OR" condition.
@@ -295,7 +295,7 @@ rules_dict: dict[str,list[list[str]]] = {
# ["Max"]
],
"OCD Blueprint - Guardian's House - Right Side": [
["Rotten Egg Launcher", "Grinch Copter"],
["Grinch Copter"],
["Slime Shooter", "Rocket Spring"]
],
"OCD Blueprint - Inside Guardian's House": [
@@ -494,7 +494,22 @@ rules_dict: dict[str,list[list[str]]] = {
],
"GPS in Who Lake": [
["Who Lake Vacuum Access", "Rotten Egg Launcher"]
],
# ],
# "1st Crate Squashed": [
# []
# ],
# "2nd Crate Squashed": [
# []
# ],
# "3rd Crate Squashed": [
# []
# ],
# "4th Crate Squashed": [
# []
# ],
# "5th Crate Squashed": [
# []
]
}
@@ -560,19 +575,23 @@ access_rules_dict: dict[str,list[list[str]]] = {
["Sleigh Room Key"]
],
"Spin N' Win Supadow": [
["Spin N' Win Door Unlock"],
[]
# ["Spin N' Win Door Unlock"],
# ["Progressive Supadow Door Unlock"]
],
"Dankamania Supadow": [
["Dankamania Door Unlock"],
[]
# ["Dankamania Door Unlock"],
# ["Progressive Supadow Door Unlock: 2"]
],
"The Copter Race Contest Supadow": [
["The Copter Race Contest Door Unlock"],
[]
# ["The Copter Race Contest Door Unlock"],
# ["Progressive Supadow Door Unlock: 3"]
],
"Bike Race": [
["Bike Race Access"],
[]
# ["Bike Race Access"],
# ["Progressive Supadow Door Unlock: 4"]
]
}

View File

@@ -16,7 +16,7 @@ from .Rules import access_rules_dict
class GrinchWorld(World):
game: ClassVar[str] = "The Grinch"
options_dataclass = Options.GrinchOptions
options = Options.GrinchOptions
options: Options.GrinchOptions
topology_present = True #not an open world game, very linear
item_name_to_id: ClassVar[dict[str,int]] = grinch_items_to_id()
location_name_to_id: ClassVar[dict[str,int]] = grinch_locations_to_id()