User Tools

Site Tools


metroid:asm:health_blocks

Table of Contents

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 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 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 #<HealRate
                sta HealthLoChange
                jsr AddHealth
                
                lda FrameCount          ; Every 20th frame...
                and #$1F
                bne +
                jsr $CBCA               ; Play sound
            
            *
        .endif
        
        .ifdef HurtBlocks
            lda TouchingHurtyBlock
            beq +                           ; Touching healy block?
            
                .ifndef HurtWhenBlinking
                    lda SamusBlink          ; Don't hurt samus if blinking
                    bne +
                .endif
                
                .ifdef NoHurtWithVaria
                    lda SamusGear           ; Don't take damage with varia
                    and #gr_VARIA
                    bne +
                .endif
            
                ; Subtract health
                .if HurtRate > $00FF        ; Only set high byte if > $FF
                    lda #>HurtRate
                    sta HealthHiChange
                .endif
                lda #<HurtRate
                sta HealthLoChange
                jsr SubtractHealth
                
                .ifdef BounceOnHurt
                    lda SamusBlink          ; Only bounce back when not blinking
                    bne +
                    
                    lda #$FF                ; This causes hit animation to occur
                    sta SamusHit
                .else
                    lda FrameCount      ; Every 20th frame...
                    and #$1F
                    bne +
                    jsr $CBCA           ; Play sound
                .endif
                
            *
        .endif
        
        
        jmp LavaAndMoveCheck        ; Return from hijack

        
    EndOfCode:
        ; If the assembled code extends beyond the available unused space,
        ; produce an error.
        
        .if EndOfCode >= $CAF5
            .error Code exceeded available size!
        .endif
metroid/asm/health_blocks.txt · Last modified: 2015/02/28 22:59 by 127.0.0.1