4.5.9 Monsters With Hit Points In Platformer Module

Bucket Mouse

Active member
To my shock, I've discovered monsters with hit points are not standard in modules that are not Adventure. This means it is not possible in the unaltered Platformer module to create a boss that takes more than one hit to go down! Sure hope someone got fired for that blunder.

But we can fix it! Go to the PlatformBase module and find the hurtPlayer-PlatformBase script (should be in the Common folder). Open it and around line 20, you'll spot this:

LDX otherObject
That's the moment your sprite's foot hits the monster. We're gonna borrow some code from Adventure and paste it right below that, The end result should look like this:

Code:
        LDX otherObject
            TXA
    STA temp
    GetActionStep temp
    CMP #$07 ;; we will use action step 7 for hurt.
    BEQ +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again.
        ChangeActionStep temp, #$07
        DEC Object_health,x
        LDA Object_health,x
        BNE +doSkipHurtingThisObject
            DestroyObject
    +doSkipHurtingThisObject
       
        CountObjects #%00001000
..and it continues from there unaltered.

Now your bosses have hit points and won't disappear instantly. You set these hit points by going to the monster GUI in NESMaker, hitting the "Object Details" button, hitting the "Details" tab and adjusting the Health number. KEEP IN MIND the default for every monster in Platformer is zero, and this must be changed to at least one! When hit points are applied, "zero" is interpreted as "invincible."

Note: if you've modified your platformer to use melee weapons or guns, you might also have to paste this code somewhere else. I don't know where that would be at this time, as I don't have a game ready that's been modified like that. If you find it yourself, post a follow-up.


BONUS PROJECT: GIVE YOUR BOSS A DEATH ANIMATION
If you want your boss to have a death animation before it disappears, then take the code above and replace "DestroyObject" with this:

ChangeActionStep temp, #$06

Now assign that animation to Action Step 6 in the monster GUI. Be sure to set the End Action to "Destroy Me."
In fact, if you use this you will have to make every monster's Action Step 6 end in Destroy Me or they will live forever. That's why this is optional. Not a big deal for everyone, but it might be for some people.
 

9Panzer

Well-known member
I remember having a tough time with this when I first started. I'll do you one better @Bucket Mouse.
If you want a monster to have a death animation you could just use action step 6 and run the animation - but the challenge there is the monster is still a monster until its destroyed. So if you touch him while he's animating his death animation then you can still be hurt. What I did was make action step 6 destroy the monster immediately and run a monster AI
That is an object without any flags.

doMonsterDeath:

;;;; play vfx
;;;; destroy this object
TXA ;; Store X in stack
PHA
TAX
LDA Object_x_hi,x ;; Get this object's origin location
CLC
ADC self_center_x
STA tempA
LDA Object_y_hi,x ;; Get this object's origin location
CLC
ADC self_center_y
STA tempB
LDA Object_screen,x
CLC
ADC #$00
STA tempD
DestroyObject
CreateObjectOnScreen tempA, tempB, #$08, #$00, tempD
PLA ;; Restore X from stack
TAX
RTS

What this does is pulls the location of the monster stores it into and then creates the new object on that location. In my case I used my object #08 for my deaths.

 

DrJondo

New member
Hi,
I'm trying to get this run in the arcade platform modul and the monsters with hitpoints are working fine, but when i change the "DestroyObject" to
"ChangeActionStep temp, #$06" i get a error.

hurtPlayer-PlatformBase.asm(15): Branch out of range.
hurtPlayer-PlatformBase.asm(16): Branch out of range.

(15) BEQ +doHurtPlayer ;; equal to zero
(16) BMI +doHurtPlayer ;; or negative
what am I doing wrong?

;;;;;;;;;;;;;;;;;; Presumes there is a variable called myLives defined in user variables.
;;;;;;;;;;;;;;;;;; You could also create your own variable for this.

LDA gameHandler
AND #%10000000
BEQ +canHurtPlayer
JMP +skipHurt
+canHurtPlayer:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;; is the monster below our feet?
;;;;;;;;;; and are we moving downward?

LDA Object_v_speed_hi,x
BEQ +doHurtPlayer ;; equal to zero
BMI +doHurtPlayer ;; or negative
;; here we are moving downward.
TXA
PHA
LDX otherObject
TXA
STA temp
GetActionStep temp
CMP #$07 ;; we will use action step 7 for hurt.
BEQ +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again.
ChangeActionStep temp, #$07
DEC Object_health,x
LDA Object_health,x
BNE +doSkipHurtingThisObject
ChangeActionStep temp, #$06
+doSkipHurtingThisObject

CountObjects #%00001000

BNE +notZeroCount
LDA scrollByte
ORA #%00000010
STA scrollByte
;;; if there are no more monsters left, we want to disable
;;; the edge check for scrolling.
LDA ScreenFlags00
AND #%11101111
STA ScreenFlags00
+notZeroCount
PLA
TAX

;; Do a hop
LDA #$FC
STA Object_v_speed_hi,x


JMP +skipHurt


+doHurtPlayer

Dec myLives
LDA myLives
BNE myLivesNotZero
JMP RESET ;; game over.
;;;; also could warp to game over screen here instead.
myLivesNotZero:

LDA continueMap
STA warpMap

LDA continueX
STA newX
LDA continueY
STA newY

LDA continueScreen
STA warpToScreen
STA camScreen






WarpToScreen warpToMap, warpToScreen, #$02
+skipHurt
 

9Panzer

Well-known member
That is a pretty common error with BNQ - BNE is the solution but I'm not familiar enough to tell you how to use it yet.
 

Knietfeld

Member
All branch opcodes like BCS, BMI, BPL, and BEQ can only branch up to 127 bytes forward and 128 bytes backward in your code due to the way it sets the new address to branch off to.

Your error is caused because the address it is trying to go to is out of that range of 127 bytes. Meaning there's too much code between the branch opcodes and +doHurtPlayer.

When you put in a macro like ChangeActionStep, what actually happens is all the code of that macro is stuck in that place where you wrote the name of it. Since ChangeActionStep is a longer macro than DestroyObject it made the branches out of range of their target address.

To fix that you can either condense code, move things all around so the address is closer, or use a JMP opcode instead. I would try something like

LDA Object_v_speed_hi,x
BPL + ;; if it's positive then go to +
JMP doHurtPlayer
;; if it's negative or equal
+

I might be wrong about the code itself. I don't use BPL that often so it might still trigger if equal but that's the basic idea. I hope that helps!
 

DrJondo

New member
Hi,
thx for your help guys !
I have no experience in coding so i have no idea what im doing, but i look like its working now.

@Knietfeld
i copied your suggestion in to the "hurtPlayer-PlatformBase.asm" but there was a error.

hurtPlayer-PlatformBase.asm(16): unnown label

(14) LDA Object_v_speed_hi,x
(15) BPL + ;; if it's positive then go to +
(16) JMP doHurtPlayer
(17) ;; if it's negative or equal
(18) +

so i tried to insert a + like this:
(16) JMP +doHurtPlayer

now its working and i dont know why :unsure:
thx :)
;;;;;;;;;;;;;;;;;; Presumes there is a variable called myLives defined in user variables.
;;;;;;;;;;;;;;;;;; You could also create your own variable for this.

LDA gameHandler
AND #%10000000
BEQ +canHurtPlayer
JMP +skipHurt
+canHurtPlayer:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;; is the monster below our feet?
;;;;;;;;;; and are we moving downward?

LDA Object_v_speed_hi,x
BPL + ;; if it's positive then go to +
JMP +doHurtPlayer
;; if it's negative or equal
+
TXA
PHA
LDX otherObject
TXA
STA temp
GetActionStep temp
CMP #$07 ;; we will use action step 7 for hurt.
BEQ +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again.
ChangeActionStep temp, #$07
DEC Object_health,x
LDA Object_health,x
BNE +doSkipHurtingThisObject
ChangeActionStep temp, #$06
+doSkipHurtingThisObject

CountObjects #%00001000

BNE +notZeroCount
LDA scrollByte
ORA #%00000010
STA scrollByte
;;; if there are no more monsters left, we want to disable
;;; the edge check for scrolling.
LDA ScreenFlags00
AND #%11101111
STA ScreenFlags00
+notZeroCount
PLA
TAX

;; Do a hop
LDA #$FC
STA Object_v_speed_hi,x


JMP +skipHurt


+doHurtPlayer

Dec myLives
LDA myLives
BNE myLivesNotZero
JMP RESET ;; game over.
;;;; also could warp to game over screen here instead.
myLivesNotZero:

LDA continueMap
STA warpMap

LDA continueX
STA newX
LDA continueY
STA newY

LDA continueScreen
STA warpToScreen
STA camScreen






WarpToScreen warpToMap, warpToScreen, #$02
+skipHurt
 
Last edited:

Knietfeld

Member
Oh yeah, I just didn't type it right. +doHurtPlayer is the address, not doHurtPlayer. That + is important.

I'm glad you got it working!
 
I tried this for the metroidvania tutorial with no success. Is there a way to get this code running in that module ?
EDIT : got this working by editing out destroy object. BUT now the enemies stays on screen and I have to make a death animation I guess .
will look into that.
 
Last edited:

DrJondo

New member
Hi,
i also have still a problem with this, my monsters have hitpoints now but when change
"DestroyObject"
with
ChangeActionStep temp, #$06
it seems to get ignored and does not change to ActionStep 6 and i cant kill the monster anymore.
 

vanderblade

Active member
Hey @9Panzer. I'm trying to make it so my monsters in the Metroidvania mod can have more than 1 hit point. I have everything working, including a death animation, in my current script, but anytime I add more than 1 hit point, the monster disappears after getting hit once (no death animation). Do you mind sharing how you implemented this in the Metroidvania mod?

Edit: N/M I found the answer in this other thread of yours: https://www.nesmakers.com/index.php...nia-monster-damage-and-death.6689/#post-34449

I feel like I'm slowly fixing all the basic things with the Metroidvania mod by following the trailblazing of you, Davey, and others that came before. I might release an Updated Metroidvania Module when I have the basic functionality restored so people can just start with all this out of the gate.
 
Last edited:

9Panzer

Well-known member
Hey @vanderblade did you edit your object collisions yet?

There is a line that over rides the HP GUI slider in there. You need to comment those parts out. See the bolded part below will be around line 135 of your dohandleobjectcollions_Platformer.asm

isMonsterWeaponCol:
JSR getOtherColBox
JSR doCompareBoundingBoxes
;;; if they have collided, it is a 1
;;; if not, it is a zero.
BEQ +skipCollision

TXA
STA otherObject
;; There was a collision between a monster and a weapon.
;; weapon is self.
;; monster is other.
;DestroyObject
;ChangeActionStep otherObject, #$07


JSR doHandleHurtMonster

LDX selfObject
DestroyObject
JMP +done
+skipCollision
+notAmonsterWeaponCollision
 

9Panzer

Well-known member
I should have read your entire post haha. Glad you figured it out :)
That's funny you call me a trailblazer ... I stumbled through this process with everyone else help. I never imagined I'd be giving tips haha.
 

vanderblade

Active member
I only mean that you are solving all these issues with the Metroidvania mod, making it easier on the rest of us. I had to go through the same thing with the brawler mod, and while I can bring over a lot of that custom code, there are key differences I have to figure out.
 

PewkoGames

Member
Will this work in the Maze Module? I'm making a game similar to space invaders only its essentially one long boss battle. You have to shoot the world until it blows up.
 

Attachments

  • game_007.png
    game_007.png
    1.6 KB · Views: 13

Bucket Mouse

Active member
I haven't tested it in Maze, but given that Maze is closer to Adventure than Platformer is, I don't see why it wouldn't work.
 

PewkoGames

Member
I'm looking in the player hurt script in the maze module and I don't see an LDA Other Object in it. Where would I paste the script?
 

Bucket Mouse

Active member
You mean LDX OtherObject, And if it's not there, we're gonna have to put it there:

Code:
    TXA
    PHA
        LDX otherObject
            TXA
    STA temp
    GetActionStep temp
    CMP #$07 ;; we will use action step 7 for hurt.
    BEQ +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again.
        ChangeActionStep temp, #$07
        DEC Object_health,x
        LDA Object_health,x
        BNE +doSkipHurtingThisObject
        TriggerScreen screenType
            DestroyObject
    +doSkipHurtingThisObject
    PLA
    TAX
This might change where, exactly, the code goes. My guess is under +canHurtPlayer, but I don't have a game in development that uses the Maze module. I could be wrong.

The bigger problem is that this was made for Platformer and is meant for collisions between the player and the monster, and YOUR game is a vertical shooter that involves collisions between a PROJECTILE and the monster, so the whole thing might be moot in your case. I'm not sure why you're not just using the Adventure module for this, where all those things are built in.
 

PewkoGames

Member
Thats actually a goiod idea. But Its an arcade style game so I need a scoring system though which the adventure module doesen't have
 
Top Bottom