diff --git a/worlds/kdl3/Client.py b/worlds/kdl3/Client.py index a1e68f8b67..5a91896e98 100644 --- a/worlds/kdl3/Client.py +++ b/worlds/kdl3/Client.py @@ -139,7 +139,7 @@ class KDL3SNIClient(SNIClient): ctx.game = self.game ctx.rom = rom_name - ctx.items_handling = 0b111 # always remote items + ctx.items_handling = 0b101 # default local items with remote start inventory ctx.allow_collect = True if "gift" not in ctx.command_processor.commands: ctx.command_processor.commands["gift"] = cmd_gift @@ -147,6 +147,7 @@ class KDL3SNIClient(SNIClient): death_link = await snes_read(ctx, KDL3_DEATH_LINK_ADDR, 1) if death_link: await ctx.update_death_link(bool(death_link[0] & 0b1)) + ctx.items_handling |= (death_link[0] & 0b10) # set local items if enabled return True async def pop_item(self, ctx, in_stage): diff --git a/worlds/kdl3/Options.py b/worlds/kdl3/Options.py index 336bd33bc5..c09c09deda 100644 --- a/worlds/kdl3/Options.py +++ b/worlds/kdl3/Options.py @@ -1,11 +1,18 @@ import random from dataclasses import dataclass -from Options import DeathLink, Choice, Toggle, OptionDict, Range, PlandoBosses, DefaultOnToggle, \ +from Options import DeathLinkMixin, Choice, Toggle, OptionDict, Range, PlandoBosses, DefaultOnToggle, \ PerGameCommonOptions from .Names import LocationName +class RemoteItems(DefaultOnToggle): + """ + Enables receiving items from your own world, primarily for co-op play. + """ + display_name = "Remote Items" + + class Goal(Choice): """ Zero: collect the Heart Stars, and defeat Zero in the Hyper Zone. @@ -26,6 +33,7 @@ class Goal(Choice): return cls.name_lookup[value].upper() return super().get_option_name(value) + class GoalSpeed(Choice): """ Normal: the goal is unlocked after purifying the five bosses @@ -292,6 +300,7 @@ class KirbyFlavor(OptionDict): A custom color for Kirby. To use a custom color, set the preset to Custom and then define a dict of keys from "1" to "15", with their values being an HTML hex color. """ + display_name = "Custom Kirby Flavor" default = { "1": "B01810", "2": "F0E0E8", @@ -348,6 +357,7 @@ class GooeyFlavor(OptionDict): A custom color for Gooey. To use a custom color, set the preset to Custom and then define a dict of keys from "1" to "15", with their values being an HTML hex color. """ + display_name = "Custom Gooey Flavor" default = { "1": "000808", "2": "102838", @@ -399,8 +409,8 @@ class Gifting(Toggle): @dataclass -class KDL3Options(PerGameCommonOptions): - death_link: DeathLink +class KDL3Options(PerGameCommonOptions, DeathLinkMixin): + remote_items: RemoteItems game_language: GameLanguage goal: Goal goal_speed: GoalSpeed diff --git a/worlds/kdl3/Rom.py b/worlds/kdl3/Rom.py index 0505b2a14a..917ea02fb7 100644 --- a/worlds/kdl3/Rom.py +++ b/worlds/kdl3/Rom.py @@ -273,11 +273,6 @@ class RomData: def write_bytes(self, offset: int, values: typing.Sequence) -> None: self.file[offset:offset + len(values)] = values - def write_crc(self) -> None: - crc = (sum(self.file[:0x7FDC] + self.file[0x7FE0:]) + 0x01FE) & 0xFFFF - inv = crc ^ 0xFFFF - self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF]) - def get_bytes(self) -> bytes: return bytes(self.file) @@ -342,8 +337,9 @@ def write_consumable_sprites(rom: RomData, consumables: bool, stars: bool): class KDL3PatchExtensions(APPatchExtension): game = "Kirby's Dream Land 3" + @staticmethod - def apply_post_patch(caller: APProcedurePatch, rom: bytes): + def apply_post_patch(_: APProcedurePatch, rom: bytes): rom_data = RomData(rom) target_language = rom_data.read_byte(0x3C020) rom_data.write_byte(0x7FD9, target_language) @@ -361,9 +357,6 @@ class KDL3PatchExtensions(APPatchExtension): rom_data.write_bytes(0x460A, [0x00, 0xA0, 0x39, 0x20, 0xA9, 0x39, 0x30, 0xB2, 0x39, 0x40, 0xBB, 0x39, 0x50, 0xC4, 0x39]) write_consumable_sprites(rom_data, rom_data.read_byte(0x3D018) > 0, rom_data.read_byte(0x3D01A) > 0) - rom_name = rom_data.read_bytes(0x3C000, 21) - rom_data.write_bytes(0x7FC0, rom_name) - rom_data.write_crc() return rom_data.get_bytes() @@ -375,6 +368,7 @@ class KDL3ProcedurePatch(APProcedurePatch, APTokenMixin): ("apply_bsdiff4", ["kdl3_basepatch.bsdiff4"]), ("apply_tokens", ["token_patch.bin"]), ("apply_post_patch", []), + ("write_snes_crc", []) ] name: bytes # used to pass to init @@ -435,10 +429,12 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch): for room in rooms: room.music = world.random.choice(music_choices) for room in room_music: - patch.write_token(APTokenTypes.WRITE, room + 2, world.random.choice(music_choices).to_bytes(1, "little")) + patch.write_token(APTokenTypes.WRITE, room + 2, + world.random.choice(music_choices).to_bytes(1, "little")) for i in range(5): # level themes - patch.write_token(APTokenTypes.WRITE, 0x133F2 + i, world.random.choice(music_choices).to_bytes(1, "little")) + patch.write_token(APTokenTypes.WRITE, 0x133F2 + i, + world.random.choice(music_choices).to_bytes(1, "little")) # Zero patch.write_token(APTokenTypes.WRITE, 0x9AE79, world.random.choice(music_choices).to_bytes(1, "little")) # Heart Star success and fail @@ -446,7 +442,7 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch): patch.write_token(APTokenTypes.WRITE, 0x4A38D, world.random.choice(music_choices).to_bytes(1, "little")) for room in rooms: - room.patch(patch, bool(world.options.consumables.value), True) + room.patch(patch, bool(world.options.consumables.value), not bool(world.options.remote_items.value)) if world.options.virtual_console in [1, 3]: # Flash Reduction @@ -471,14 +467,15 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch): struct.pack("H", world.required_heart_stars if world.options.goal_speed == 1 else 0xFFFF)) patch.write_token(APTokenTypes.WRITE, 0x3D00C, world.options.goal_speed.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D00E, world.options.open_world.value.to_bytes(2, "little")) - patch.write_token(APTokenTypes.WRITE, 0x3D010, world.options.death_link.value.to_bytes(2, "little")) + patch.write_token(APTokenTypes.WRITE, 0x3D010, ((world.options.remote_items.value << 1) + + world.options.death_link.value).to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D012, world.options.goal.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D014, world.options.stage_shuffle.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D016, world.options.ow_boss_requirement.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D018, world.options.consumables.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D01A, world.options.starsanity.value.to_bytes(2, "little")) patch.write_token(APTokenTypes.WRITE, 0x3D01C, world.options.gifting.value.to_bytes(2, "little") - if world.multiworld.players > 1 else bytes([0])) + if world.multiworld.players > 1 else bytes([0])) patch.write_token(APTokenTypes.WRITE, 0x3D01E, world.options.strict_bosses.value.to_bytes(2, "little")) # don't write gifting for solo game, since there's no one to send anything to @@ -558,6 +555,9 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch): patch.write_token(APTokenTypes.WRITE, 0x3C000, bytes(patch.name)) patch.write_token(APTokenTypes.WRITE, 0x3C020, world.options.game_language.value.to_bytes(1, "little")) + patch.write_token(APTokenTypes.COPY, 0x7FC0, (0x3C000, 21)) + patch.write_token(APTokenTypes.COPY, 0x7FD9, (0x3C020, 1)) + # handle palette if world.options.kirby_flavor_preset.value != 0: for addr in kirby_target_palettes: diff --git a/worlds/kdl3/src/kdl3_basepatch.asm b/worlds/kdl3/src/kdl3_basepatch.asm index 4ddbd2b105..4a5b82fde6 100644 --- a/worlds/kdl3/src/kdl3_basepatch.asm +++ b/worlds/kdl3/src/kdl3_basepatch.asm @@ -160,7 +160,6 @@ CopyAbilityAnimalOverride: STA $39DF, X RTL -org $079A00 HeartStarCheck: TXA CMP #$0000 ; is this level 1 @@ -201,7 +200,6 @@ HeartStarCheck: SEC RTL -org $079A80 OpenWorldUnlock: PHX LDX $900E ; Are we on open world? @@ -224,7 +222,6 @@ OpenWorldUnlock: PLX RTL -org $079B00 MainLoopHook: STA $D4 INC $3524 @@ -265,7 +262,6 @@ MainLoopHook: .Return: RTL -org $079B80 HeartStarGraphicFix: LDA #$0000 PHX @@ -299,7 +295,6 @@ HeartStarGraphicFix: PLX RTL -org $079BF0 ParseItemQueue: ; Local item queue parsing NOP @@ -427,7 +422,6 @@ ParseItemQueue: LDA #$0000 BRA .PlaySFXLong -org $079D00 AnimalFriendSpawn: PHA CPX #$0002 ; is this an animal friend? @@ -446,7 +440,6 @@ AnimalFriendSpawn: LDA #$9999 RTL -org $079E00 WriteBWRAM: LDY #$6001 ;starting addr LDA #$1FFE ;bytes to write @@ -479,7 +472,6 @@ WriteBWRAM: .Return: RTL -org $079E80 ConsumableSet: PHA PHX @@ -536,7 +528,6 @@ ConsumableSet: AND #$00FF RTL -org $079F00 NormalGoalSet: PHX LDA $07D012 @@ -557,7 +548,6 @@ NormalGoalSet: STA $5AC1 ; cutscene RTL -org $079F80 FinalIcebergFix: PHX PHY @@ -591,7 +581,6 @@ FinalIcebergFix: PLX RTL -org $07A000 StrictBosses: PHX LDA $901E ; Do we have strict bosses enabled? @@ -618,7 +607,6 @@ StrictBosses: LDA $53CD RTL -org $07A030 NintenHalken: LDX #$0005 .Halken: @@ -636,7 +624,6 @@ NintenHalken: LDA #$0001 RTL -org $07A080 StageCompleteSet: PHX LDA $5AC1 ; completed stage cutscene @@ -667,6 +654,16 @@ StageCompleteSet: DEC ASL TAX + PHX + LDA $8200, X + AND #$00FF + BNE .ApplyClear + TXA + LSR + INC + JSL ApplyLocalCheck + .ApplyClear: + PLX LDA #$0001 ORA $8200, X STA $8200, X @@ -676,7 +673,6 @@ StageCompleteSet: CMP $53CB RTL -org $07A100 OpenWorldBossUnlock: PHX PHY @@ -740,7 +736,6 @@ OpenWorldBossUnlock: PLX RTL -org $07A180 GooeySpawn: PHY PHX @@ -776,7 +771,6 @@ GooeySpawn: PLY RTL -org $07A200 SpeedTrap: PHX LDX $8082 ; do we have slowness @@ -788,7 +782,6 @@ SpeedTrap: EOR #$FFFF RTL -org $07A280 HeartStarVisual: CPX #$0000 BEQ .SkipInx @@ -852,7 +845,6 @@ HeartStarVisual: .Return: RTL -org $07A300 LoadFont: JSL $00D29F ; play sfx PHX @@ -923,7 +915,6 @@ LoadFont: PLX RTL -org $07A380 HeartStarVisual2: LDA #$2C80 STA $0000, Y @@ -1037,7 +1028,6 @@ HeartStarVisual2: STA $0000, Y RTL -org $07A480 HeartStarSelectFix: PHX TXA @@ -1059,15 +1049,22 @@ HeartStarSelectFix: AND #$00FF RTL -org $07A500 HeartStarCutsceneFix: TAX LDA $53D3 DEC STA $5AC3 + LDA $53A7, X + AND #$00FF + BNE .Return + PHX + TXA + ORA #$0100 + JSL ApplyLocalCheck + PLX + .Return RTL -org $07A510 GiftGiving: CMP #$0008 .This: @@ -1083,7 +1080,6 @@ GiftGiving: PLX JML $CABC18 -org $07A550 PauseMenu: JSL $00D29F PHX @@ -1144,7 +1140,6 @@ PauseMenu: PLX RTL -org $07A600 StarsSet: PHA PHX @@ -1214,11 +1209,10 @@ StarsSet: STA $39D7 BRA .Return -org $07A680 ApplyLocalCheck: ; args: A-address of check following $08B000 TAX - LDA $08B000, X + LDA $09B000, X AND #$00FF TAY LDX #$0000 @@ -1231,7 +1225,8 @@ ApplyLocalCheck: BCC .Loop BRA .Return ; this is dangerous, could lose a check here .Apply: - STY $C000, X + TYA + STA $C000, X .Return: RTL @@ -1272,4 +1267,4 @@ org $07E040 db $3D, $05 org $07F000 -incbin "APPauseIcons.bin" \ No newline at end of file +incbin "APPauseIcons.dat" \ No newline at end of file