News: 11 March 2016 - Forum Rules

Author Topic: SMB1. How to make spiny an independent sprite that won't spawn lakitu?  (Read 1925 times)


  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Might be wrong sub, but let's go for it!

Using the disassembly of SMB1, how difficult would it be to make the Spiny not always spawn lakitu? Spiny apparently calls the Frenzy function of the Lakitu.. and I don't know enough about ASM coding on how to sever this automatic call.

What's even more interesting is that a spiny cannot be spawned on the ground.. It won't be able to walk. I might have to mirror how a goomba is coded to make this work? Or are we talking far exceeding what the game can do unless I go through and optimize code?

This is probably a shotgun blast of a question but I would appreciate anyone who could point me in a good direction. Relevant code is below:

Code: [Select]
;$00 - used to hold horizontal difference
;$01-$03 - used to hold difference adjusters

      .byte $15, $30, $40

         lda Enemy_State,x          ;check lakitu's enemy state
         and #%00100000             ;for d5 set
         beq ChkLS                  ;if not set, continue with code
         jmp MoveD_EnemyVertically  ;otherwise jump to move defeated lakitu downwards
ChkLS:   lda Enemy_State,x          ;if lakitu's enemy state not set at all,
         beq Fr12S                  ;go ahead and continue with code
         lda #$00
         sta LakituMoveDirection,x  ;otherwise initialize moving direction to move to left
         sta EnemyFrenzyBuffer      ;initialize frenzy buffer
         lda #$10
         bne SetLSpd                ;load horizontal speed and do unconditional branch
Fr12S:   lda #Goomba
         sta EnemyFrenzyBuffer      ;set spiny identifier in frenzy buffer
         ldy #$02
LdLDa:   lda LakituDiffAdj,y        ;load values
         sta $0001,y                ;store in zero page
         bpl LdLDa                  ;do this until all values are stired
         jsr PlayerLakituDiff       ;execute sub to set speed and create spinys
SetLSpd: sta LakituMoveSpeed,x      ;set movement speed returned from sub
         ldy #$01                   ;set moving direction to right by default
         lda LakituMoveDirection,x
         and #$01                   ;get LSB of moving direction
         bne SetLMov                ;if set, branch to the end to use moving direction
         lda LakituMoveSpeed,x
         eor #$ff                   ;get two's compliment of moving speed
         adc #$01
         sta LakituMoveSpeed,x      ;store as new moving speed
         iny                        ;increment moving direction to left
SetLMov: sty Enemy_MovingDir,x      ;store moving direction
         jmp MoveEnemyHorizontally  ;move lakitu horizontally

           ldy #$00                   ;set Y for default value
           jsr PlayerEnemyDiff        ;get horizontal difference between enemy and player
           bpl ChkLakDif              ;branch if enemy is to the right of the player
           iny                        ;increment Y for left of player
           lda $00
           eor #$ff                   ;get two's compliment of low byte of horizontal difference
           adc #$01                   ;store two's compliment as horizontal difference
           sta $00
ChkLakDif: lda $00                    ;get low byte of horizontal difference
           cmp #$3c                   ;if within a certain distance of player, branch
           bcc ChkPSpeed
           lda #$3c                   ;otherwise set maximum distance
           sta $00
           lda Enemy_ID,x             ;check if lakitu is in our current enemy slot
           cmp #Lakitu
           bne ChkPSpeed              ;if not, branch elsewhere
           tya                        ;compare contents of Y, now in A
           cmp LakituMoveDirection,x  ;to what is being used as horizontal movement direction
           beq ChkPSpeed              ;if moving toward the player, branch, do not alter
           lda LakituMoveDirection,x  ;if moving to the left beyond maximum distance,
           beq SetLMovD               ;branch and alter without delay
           dec LakituMoveSpeed,x      ;decrement horizontal speed
           lda LakituMoveSpeed,x      ;if horizontal speed not yet at zero, branch to leave
           bne ExMoveLak
SetLMovD:  tya                        ;set horizontal direction depending on horizontal
           sta LakituMoveDirection,x  ;difference between enemy and player if necessary
ChkPSpeed: lda $00
           and #%00111100             ;mask out all but four bits in the middle
           lsr                        ;divide masked difference by four
           sta $00                    ;store as new value
           ldy #$00                   ;init offset
           lda Player_X_Speed
           beq SubDifAdj              ;if player not moving horizontally, branch
           lda ScrollAmount
           beq SubDifAdj              ;if scroll speed not set, branch to same place
           iny                        ;otherwise increment offset
           lda Player_X_Speed
           cmp #$19                   ;if player not running, branch
           bcc ChkSpinyO
           lda ScrollAmount
           cmp #$02                   ;if scroll speed below a certain amount, branch
           bcc ChkSpinyO              ;to same place
           iny                        ;otherwise increment once more
ChkSpinyO: lda Enemy_ID,x             ;check for spiny object
           cmp #Spiny
           bne ChkEmySpd              ;branch if not found
           lda Player_X_Speed         ;if player not moving, skip this part
           bne SubDifAdj
ChkEmySpd: lda Enemy_Y_Speed,x        ;check vertical speed
           bne SubDifAdj              ;branch if nonzero
           ldy #$00                   ;otherwise reinit offset
SubDifAdj: lda $0001,y                ;get one of three saved values from earlier
           ldy $00                    ;get saved horizontal difference
SPixelLak: sec                        ;subtract one for each pixel of horizontal difference
           sbc #$01                   ;from one of three saved values
           bpl SPixelLak              ;branch until all pixels are subtracted, to adjust difference
ExMoveLak: rts                        ;leave!!!


  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: SMB1. How to make spiny an independent sprite that won't spawn lakitu?
« Reply #1 on: October 12, 2016, 05:57:48 pm »
My first guess:

Code: [Select]
ChkSpinyO: lda Enemy_ID,x             ;check for spiny object
           cmp #Spiny
           bne ChkEmySpd              ;branch if not found

That checks to see if the enemy is a spiny... and jumps ahead (presumably bypassing the Lakitu generation) if it isn't.

So to remove the special case for spiny, make it ALWAYS jump ahead.

I'd replace the cmp/bne here with a "jmp ChkEmySpd / nop"

No idea if that would work or not, but that'd be the first thing I'd try.


  • Jr. Member
  • **
  • Posts: 52
    • View Profile
Re: SMB1. How to make spiny an independent sprite that won't spawn lakitu?
« Reply #2 on: October 12, 2016, 06:04:36 pm »
OK. Worth a shot.


Turns out that this actually makes Spinies impossible to load, both from Lakitu and from a direct spawn.

I see that spiny constant is $12, and over in the twelfth slot under the init functions is an InitEnemyFrenzy function.

Code: [Select]

      .word InitHorizFlySwimEnemy  ;for objects $10-$1f
      .word InitLakitu
      .word InitEnemyFrenzy
      .word NoInitCode
      .word InitEnemyFrenzy
      .word InitEnemyFrenzy
      .word InitEnemyFrenzy
      .word InitEnemyFrenzy
      .word EndFrenzy
      .word NoInitCode
      .word NoInitCode
      .word InitShortFirebar
      .word InitShortFirebar
      .word InitShortFirebar
      .word InitShortFirebar
      .word InitLongFirebar

« Last Edit: October 12, 2016, 07:56:20 pm by segwayspeedracer »