From be0f23beb3ff546fa493f2cbd0831879254e7fb4 Mon Sep 17 00:00:00 2001 From: LiquidCat64 <74896918+LiquidCat64@users.noreply.github.com> Date: Sun, 20 Apr 2025 07:46:57 -0600 Subject: [PATCH] CV64: Some DeathLink Adjustments (#4727) --- worlds/cv64/client.py | 44 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/worlds/cv64/client.py b/worlds/cv64/client.py index cec5f551..849ca44a 100644 --- a/worlds/cv64/client.py +++ b/worlds/cv64/client.py @@ -10,12 +10,20 @@ from worlds._bizhawk.client import BizHawkClient if TYPE_CHECKING: from worlds._bizhawk.context import BizHawkClientContext +DEATHLINK_AREA_NUMBERS = [0, 1, 1, 2, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, + 7, 9, 8, 6, 12, 12, 13, 11, 12, 5, 2, 10, 13, 13] + +DEATHLINK_AREA_NAMES = ["Forest of Silence", "Castle Wall", "Villa", "Tunnel", "Underground Waterway", "Castle Center", + "Duel Tower", "Tower of Execution", "Tower of Science", "Tower of Sorcery", "Room of Clocks", + "Clock Tower", "Castle Keep", "Level: You Cheated"] + class Castlevania64Client(BizHawkClient): game = "Castlevania 64" system = "N64" patch_suffix = ".apcv64" self_induced_death = False + time_of_sent_death = None received_deathlinks = 0 death_causes = [] currently_shopping = False @@ -62,15 +70,19 @@ class Castlevania64Client(BizHawkClient): return if "tags" not in args: return - if "DeathLink" in args["tags"] and args["data"]["source"] != ctx.slot_info[ctx.slot].name: + if "DeathLink" in args["tags"] and args["data"]["time"] != self.time_of_sent_death: self.received_deathlinks += 1 if "cause" in args["data"]: cause = args["data"]["cause"] + # If the other game sent a death with a blank string for the cause, use the default death message. + if cause == "": + cause = f"{args['data']['source']} killed you without a word!" # Truncate the death cause message at 120 characters. if len(cause) > 120: cause = cause[0:120] else: - cause = f"{args['data']['source']} killed you!" + # If the other game sent a death with no cause at all, use the default death message. + cause = f"{args['data']['source']} killed you without a word!" self.death_causes.append(cause) async def game_watcher(self, ctx: "BizHawkClientContext") -> None: @@ -115,11 +127,30 @@ class Castlevania64Client(BizHawkClient): if "DeathLink" in ctx.tags and save_struct[0xA4] & 0x80 and not self.self_induced_death and not \ deathlink_induced_death: self.self_induced_death = True - if save_struct[0xA4] & 0x08: - # Special death message for dying while having the Vamp status. - await ctx.send_death(f"{ctx.player_names[ctx.slot]} became a vampire and drank your blood!") + + # If the player died at the Castle Keep exterior map on one of the Room of Clocks boss towers + # (determinable by checking the entrance value as well as the map value), consider Room of Clocks the + # actual area of death. + if save_struct[0xAD] == 0x14 and save_struct[0xAF] in [0, 1]: + area_of_death = DEATHLINK_AREA_NAMES[10] + # Otherwise, determine what area the player perished in from the current map ID. else: - await ctx.send_death(f"{ctx.player_names[ctx.slot]} perished. Dracula has won!") + area_of_death = DEATHLINK_AREA_NAMES[DEATHLINK_AREA_NUMBERS[save_struct[0xAD]]] + + # If we had the Vamp status while dying, use a special message. + if save_struct[0xA4] & 0x08: + death_message = (f"{ctx.player_names[ctx.slot]} became a vampire at {area_of_death} and drank your " + f"blood!") + # Otherwise, use the generic one. + else: + death_message = f"{ctx.player_names[ctx.slot]} perished in {area_of_death}. Dracula has won!" + + # Send the death. + await ctx.send_death(death_message) + + # Record the time in which the death was sent so when we receive the packet we can tell it wasn't our + # own death. ctx.on_deathlink overwrites it later, so it MUST be grabbed now. + self.time_of_sent_death = ctx.last_death_link # Write any DeathLinks received along with the corresponding death cause starting with the oldest. # To minimize Bizhawk Write jank, the DeathLink write will be prioritized over the item received one. @@ -208,6 +239,7 @@ class Castlevania64Client(BizHawkClient): # Send game clear if we're in either any ending cutscene or the credits state. if not ctx.finished_game and (0x26 <= int(cutscene_value) <= 0x2E or game_state == 0x0000000B): + ctx.finished_game = True await ctx.send_msgs([{ "cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL