dwrandomizer
dwrandomizer copied to clipboard
Another softlock possibility while cursed.
You can be softlocked in the following situation.
- You are cursed.
- The castle is surrounded by Swamp.
- You don't have Erdrick's Armour.
- You don't have the spells Heal or Healmore, or you don't have sufficient MP to cast the spell.
- You don't have any herbs on hand.
- You die either in battle, or on damage tiles.
- There isn't a town horizontally/vertically adjacent to the castle that is not haukness, or you have insufficient gold to stay at the inn of said town. (If there is a town, the other criteria is not having Magic armour, and likely insufficient gold to buy magic armour, assuming the town sells it.)
What happens, is the game gives you exactly one HP, and kicks you out of the castle right then and there, with no way to get to a town to recover HP/MP.
As a note, I have located the section of code that handles the giving of 1HP.
03:CB6B A5 CF LDA $00CF
03:CB6D 29 C0 AND #$C0 ;Check to see if cursed item is equipped
03:CB6F F0 07 BEQ $CB78 ;if so, give exactly 1 HP.
03:CB71 A9 01 LDA #$01
03:CB73 85 C5 STA $00C5
03:CB75 4C 96 CB JMP $CB96
;code after this point handles full HP/MP recovery.
Wrote a vpatch block, that changes the code a tiny bit. It gives the lesser of max HP or 75, and exactly 8MP on revive, if cursed, otherwise, it gives full HP/MP as normal.
vpatch(rom, 0x0cb6b, 85,
0xA9, 0x30, /* LDA #$30 ; Move always initalized stuff up here. */
0x85, 0x90, /* STA $0090 ; */
0xA9, 0x40, /* LDA #$40 ; */
0x85, 0x92, /* STA $0092 ; */
0xA9, 0x00, /* LDA #$00 ; */
0x85, 0xD6, /* STA $00D6 ; */
0x85, 0xDB, /* STA $00DB ; */
0x85, 0x91, /* STA $0091 ; */
0x85, 0x93, /* STA $0093 ; */
0xA2, 0x03, /* LDX #$03 ; */
0x86, 0x3A, /* STX $003A ; */
0x86, 0x8E, /* STX $008E ; */
0xE8, /* INX ; */
0x86, 0x3B, /* STX $003B ; */
0x86, 0x8F, /* STX $008F ; */
0xE8, /* INX ; */
0x86, 0x45, /* STX $0045 ; */
0xE8, /* INX ; */
0xE8, /* INX ; */
0x86, 0x4B, /* STX $004B ; */
0xE8, /* INX ; */
0x86, 0x4A, /* STX $004A ; End of always initialized variables. */
0xAD, 0x3A, 0x60, /* LDA $603A ; Check to see if game was just loaded. */
0xC9, 0x78, /* CMP #$78 ; */
0xD0, 0x25, /* BNE $CBBE ; If yes, skip restoring record. */
/* Preload Cursed HP/MP */
0x86, 0xC6, /* STX $00C6 ; MP = 8 - Enough to cast Healmore exactly once. */
0xA9, 0x4B, /* LDA $#4B ; HP = 75 */ /* If desired, compute how much HP is actually needed to make it to a town, and set that value instead. */
0xC5, 0xCA, /* CMP $00CA ; Or current maximum */
0x90, 0x02, /* BCC $CBA3 ; Whichever is less */
0xA5, 0xCA, /* LDA $00CA ; */
/ * cba3: */
0x85, 0xC5, /* STA $00C5 ; Store Max HP */
0xA5, 0xCF, /* LDA $00CF ; Is cursed belt/death necklace equipped? */
0x29, 0xC0, /* AND #$C0 ; */
0xD0, 0x13, /* BNE $CBBE ; If yes, skip full HP/MP restore */
0xA5, 0xCA, /* LDA $00CA ; Load Max HP */
0x85, 0xC5, /* STA $00C5 ; */
0xA5, 0xCB, /* LDA $00CB ; Load Max MP */
0x85, 0xC6, /* STA $00C6 ; */
0xAE, 0x39, 0x60, /* LDX $6039 ; */
0xA9, 0xAB, /* LDA #$AB ; */
0x9D, 0x45, 0x60, /* STA $6045,X ; */
0x8D, 0x3A, 0x60, /* STA $603A ; */
/* cbbe: */
0xEA, /* NOP ; */
0xEA /* NOP ; */
);
I confirmed that the code that preserves register X was not required, as the calls that follow clobbers register X without using whatever value it had.
Had another idea, which took me a while to write a vpatch for. The idea is if cursed, the HP/MP only restores fully up to a certain level, Code wise, that level is being defined as Level 7 plus/minus 2, randomly selected per seed.
dw_stats *stats;
stats = &rom->stats[mt_rand(5, 9)]; //Base Cursed HP off of stats randomly chosen from Level 5 to Level 9. (7 +/- 2)
vpatch(rom, 0xcb6b, 85,
0x84, 0xd6, /* STY $00D6 ; Register Y is definitely 0 by the time this code is reached. */
0x84, 0xdb, /* STY $00DB ; Moved the always initialized values on load/death to the top. */
0x84, 0x91, /* STY $0091 ; */
0x84, 0x93, /* STY $0093 ; */
0xa9, 0x30, /* LDA #$30 ; */
0x85, 0x90, /* STA $0090 ; */
0xa9, 0x40, /* LDA #$40 ; */
0x85, 0x92, /* STA $0092 ; */
0xa2, 0x08, /* LDX #$08 ; */
0x86, 0x4a, /* STX $004A ; */
0xca, /* DEX ; */
0x86, 0x4b, /* STX $004B ; */
0xa2, 0x05, /* LDX #$05 ; */
0x86, 0x45, /* STX $0045 ; */
0xca, /* DEX ; */
0x86, 0x8f, /* STX $008F ; */
0x86, 0x3b, /* STX $003B ; */
0xca, /* DEX ; */
0x86, 0x8e, /* STX $008E ; */
0x86, 0x3a, /* STX $003A ; End of Always Initialized Values. */
0xa2, 0x01, /* LDX #$01 ; HP/MP restore code completes two loops, MP is restored first. */
0xad, 0x3a, 0x60, /* LDA $603A ; Check to see if the save was just loaded. */
0xc9, 0x78, /* CMP #$78 ; If so, skip the rest of this code. */
0xd0, 0x27, /* BNE $CBC0 ; */
0xa0, stats->mp, /* LDY #$24 ; Store Max Cursed MP. (Based off of random selection of level 7 plus/minus 2 base MP) */
/* CB9B: */
0xa5, 0xcf, /* LDA $00CF ; Load Equiped items */
0x29, 0xc0, /* AND #$C0 ; Check against Death Necklace / Cursed Belt. */
0xf0, 0x05, /* BEQ $CBA6 ; If not cursed, directly load Max HP/MP. */
0x98, /* TYA ; Otherwise, move Cursed HP/MP into memory. */
0xd5, 0xca, /* CMP $CA,X ; Check to see if Cursed HP/MP is greater than Max HP/MP. */
0x90, 0x02, /* BCC $CBA8 ; If so, directly load Max HP/MP. */
/* CBA6: */
0xb5, 0xca, /* LDA $CA,X ; */
/* CBA8: */
0x95, 0xc5, /* STA $C5,X ; Store Loaded HP/MP value. */
0xa0, stats->hp, /* LDY #$32 ; Set Max Cursed HP. (Again, Level 7 plus/minus 2 base HP) */
0xca, /* DEX ; */
0xf0, 0xec, /* BEQ $CB9B ; Loop again if MP was set, otherwise continue on. */
0xa5, 0xcf, /* LDA $00CF ; Check to see if cursed. */
0x29, 0xc0, /* AND #$C0 ; */
0xd0, 0x0b, /* BNE $CBC0 ; */
0xae, 0x39, 0x60, /* LDX $6039 ; If not, mark save as already loaded. */
0xa9, 0xab, /* LDA #$AB ; */
0x9d, 0x45, 0x60, /* STA $6045,X ; */
0x8d, 0x3a, 0x60 /* STA $603A ; */
/* CBC0: */
);