======Health Blocks====== This hack adds a new type of tile to heal and/or hurt Samus. There are a number of configurable options in the code. ====Assembling==== This code is meant to be assembled with [[http://snarfblam.com/words/?p=210|snarfblasm]], which will produce an IPS patch. It should be possible with other assemblers, but minor syntax changes will be required and each section (identified by .PATCH directive) will need to be assembled and inserted separately. This code requires an additional file, the [[metroid:asm:defines|Defines file]], in the same folder and named "labels.asm". This file declares all the existing variables used in Metroid. To assemble, place the following each in the same folder: snarfblasm, labels.asm, and the code below in a file named "healthblocks.asm". Run snarfblasm on the command line with the command: snarfblasm healthblocks.asm This should produce a file named "healthblocks.ips", which can be applied to an __unexpanded__ Metroid ROM, using a patcher such as Lunar IPS. ====Options==== There are a number of options in the "Config" section that can be configured. Place a semi-colon before a "define" to disable a feature or remove the semi-colon to enable a feature. The features are described below. * **HealBlocks** - Include healing blocks. * **HurtBlocks** - Include damaging blocks. * **HealSolid** - Healing blocks are solid and can not be passed through by the player. Note that enemies will still be able to pass through these blocks. * **HurtSolid** - Damaging blocks are solid and can not be passed through by the player. Note that enemies will still be able to pass through these blocks. * **NoHurtWithVaria** - Player is immune to damaging effects of hurt blocks when equipped with Varia. * **BounceOnHurt** - Causes the player to bounce back when hurt by damaging blocks, in the same manner as when hurt by an enemy. This includes momentary invincibility blink. * **HurtWhileBlinking** - Causes the player to take damage during invincibility blink. Can be combined with BounceOnHurt to use the hurt bouce-back but continuous health drain. * **DEBUG** - This option enabled debug code that was used while writing the hack There are some additional configurable values. * **HealyTile** - Specifies the tile number that has healing effects. This corresponds to the tile numbers shown in the left-most pane in Editroid's structure editor. * **HurtyTile** - Specifies the tile number that has damaging effects. This corresponds to the tile numbers shown in the left-most pane in Editroid's structure editor. * **HealRate** - Specifies the amount of health restored per frame. Note that this value is in a fixed-point BCD format, specifying tenths of health units. For example, to restore 1 health per frame, use the value $0010. To restore 2.5 health per frame, use the value $0025. To restore 1 energy tank per frame, use the value $1000. * **HurtRate** - Specifies the amount of health lost per frame. Uses the same format as HealRate. If BounceOnHurt is enabled (without HurtWhileBlinking), this is effectively the amount of health the player will lose each time he touches the block. ====Code==== ; --------------------------------------------------------------------------- ; Health Blocks ; ; by snarf and Drevan Zero ; ; 2/3/2013 - Version 1.0 ; --------------------------------------------------------------------------- .include labels.asm ; Includes all Metroid variables declared in the Metroid disassembly. ; Very convenient. ; --------------------------------------------------------------------------- ; Config ; --------------------------------------------------------------------------- ; To disable an option, place a semi-colon before the .define. ; To enable an option, remove a semi-colon. ;.define DEBUG ; Include debugging code .define HealBlocks ; Include heal blocks .define HurtBlocks ; Include hurt blocks ;.define HealSolid ; Heal blocks are solid ;.define HurtSolid ; Hurt blocks are solid ;.define BounceOnHurt ; Get thrown back when touching a hurt block ;.define HurtWhenBlinking ; Drain health while blinking .define NoHurtWithVaria ; Player is immune to hurt blocks with Varia HealyTile = $FD ; (Tile number must be greater than $80) HurtyTile = $FE ; (Tile number must be greater than $80) ; These two values use BCD. Use $, but write it as if decimal. E.g. to ; heal 1.5 health per frame, set HealRate to $0015 HealRate = $0001 ; Tenths of health added per frame HurtRate = $0001 ; Tenths of health subtracted per frame ; --------------------------------------------------------------------------- ; Declarations ; --------------------------------------------------------------------------- ; Variables ------------ TouchingHealyBlock = $C9 TouchingHurtyBlock = $CA ; Existing Functions---- MakeCartRAMPtr = $E96A IsBlastTile = $E9BE Exit16 = $E81D LavaAndMoveCheck = $E269 AddHealth = $CEF9 SubtractHealth = $CE92 ; --------------------------------------------------------------------------- ; Code ; --------------------------------------------------------------------------- ; --------------------------------------- .PATCH 07:E7E6 ; Hit test Hijack ; --------------------------------------- ; This part of the routine is unchanged HitTestHijack: LE7E6: jsr MakeCartRAMPtr ;($E96A)Find object position in room RAM. ldy #$00 lda ($04),y ; get tile value cmp #$4E beq $E81E jsr $95C0 jsr $D651 bcc Exit16 ; CF = 0 if tile # < $80 (solid tile)... CRASH!!! cmp #$A0 ; is tile >= A0h? (walkable tile) ; These branches are changed to use the new IsWalkableTile that does our additional ; testing for healy blocks. ;(its okay to overflow into next function because it is now unused) ; bcs IsWalkableTile ; jmp IsBlastTile ; tile is $80-$9F (blastable tiles) bcc + jmp IsWalkableTile * jmp IsBlastTile ; --------------------------------------- .PATCH 07:CD81 ; Per frame Hijack ; --------------------------------------- ; Relacing this: ; jsr LavaAndMoveCheck ; With this: jsr HealthBlockCheck ; HealyBlockCheck will jump to LavaAndMoveCheck ; --------------------------------------- .PATCH 07:CA35 ; NEW CODE ; --------------------------------------- IsWalkableTile: ; New implementation of IsWalkableTile that does ; additional testing for healy blocks ldy IsSamus beq @NotSamus ; special case for Samus ; Test for healing block .ifdef HealBlocks cmp #HealyTile ; Is it a healy block? bne @NotHealyBlock lda #$01 sta TouchingHealyBlock .ifdef DEBUG sta TankCount ; Give samus 1 energy tank (debug only) .endif .ifdef HealSolid clc ; Return, solid block .else sec ; Return, air block .endif rts @NotHealyBlock: .endif ; Test for hurting block .ifdef HurtBlocks cmp #HurtyTile ; Is it a hurty block? bne @NotHurtyBlock lda #$01 sta TouchingHurtyBlock .ifdef DEBUG lda #$02 sta TankCount ; Give samus 2 energy tanks (debug only) .endif .ifdef HurtSolid clc ; Return, solid block .else sec ; Return, air block .endif rts @NotHurtyBlock: .endif pha ; Preserve tile number lda #$00 sta TouchingHealyBlock sta TouchingHurtyBlock .ifdef DEBUG sta TankCount ; Remove energy tank .endif pla ; Proceed with normal hit testing ; Below code is unchagned from original routine dey ; = 0 sty SamusDoorData cmp #$A0 ; crash with tile #$A0? (scroll toggling door) beq + cmp #$A1 ; crash with tile #$A1? (horizontal scrolling door) bne ++ inc SamusDoorData * inc SamusDoorData * @NotSamus: dex beq + jsr $E98E jmp $E7E6 * sec ; no crash ; NO ; Exit16: rts HealthBlockCheck: ; New routine that checks the TouchingHealyBlock variable and ; heals samus if applicable .ifdef HealBlocks lda TouchingHealyBlock beq + ; Touching healy block? ; Add health .if HealRate > $00FF lda #>HealRate ; Only set high byte if > $FF sta HealthHiChange .endif lda # $00FF ; Only set high byte if > $FF lda #>HurtRate sta HealthHiChange .endif lda #= $CAF5 .error Code exceeded available size! .endif