[Solved] Death Animations (4.5)

PasseGaming

Active member
So, I'd like add death animations for my player character and monsters for Spirit Impel. I tried doing what I did in 4.1 to work but I can't seem to get it working. Mind you this is for the scrolling shooter (so, there all sorts of weirdness involved when trying to do anything). Anyhow, I just want a simple two frame death animation when the player dies. Same with the monsters. Does or has anyone tried this with the shooter module? Have you had any luck?
 

AllDarnDavey

Active member
PasseGaming said:
So, I'd like add death animations for my player character and monsters for Spirit Impel. I tried doing what I did in 4.1 to work but I can't seem to get it working. Mind you this is for the scrolling shooter (so, there all sorts of weirdness involved when trying to do anything). Anyhow, I just want a simple two frame death animation when the player dies. Same with the monsters. Does or has anyone tried this with the shooter module? Have you had any luck?
The easiest way to add death animations for enemies is to make an explosion VFX and change the doHandleHurtMonster script to spawn that in when the object is destroyed. I actually just figured out something very similar for making bullet hit VFX for my game.
You'll want to replace your doHandleHurtMonster shooter script with this. I kept in all the normal stuff allowing for monsters with more than a single hit of health, and the bit about counting monsters to unlock the scroll.
Code:
	DEC Object_health,x
	LDA Object_health,x
	CMP #$00
	BNE +
		;;;; 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 
	+:
	CountObjects #%00001000
	BNE +notZeroCount
		;;; if there are no more monsters left, we want to disable
		;;; the edge check for scrolling.
		LDA ScreenFlags00
		AND #%11101111
		STA ScreenFlags00

+notZeroCount:
Of course this is just a single VFX for all enemies dying, you'll have to get more creative if you want custom death VFX per enemy type.
 

AllDarnDavey

Active member
Okay, player death time. This one is a bit more complicated, but not as bad as I thought it would be.
You'll need to backup and replace your player hurt script with this one I named hurtWithLives_shooter_DeathAnim.asm
Code:
	LDA gameHandler
	AND #%10000000
	BEQ +canHurtPlayer
		JMP +skipHurt
+canHurtPlayer:
	Dec myLives
	LDA myLives
	BNE +myLivesNotZero
		JMP RESET ;; game over.
		;;;; also could warp to game over screen here instead.

+myLivesNotZero	
	ChangeActionStep player1_object, #$07
+skipHurt

Then setup the player action 07 with your death animation and change the action settings to match these:
PlayerDeath.PNG

It looks like the newest NESMaker has started setting up some things needed to make this work, like this "GoToContinue" end action which is pretty much load the last continue we need. BUT we'll need to tweak GoToConinue code with a few changes needed for the shooter module, like force the player to face right, and reset the boss health.
We need to replace TimerEndScripts with this version I've named TimerEndScripts_ShooterFix.asm (the changes are at line 160)
Code:
;;; 00 = Loop
end_loop_action:

	RTS
;;; 01 = Advance
advance_action:
		LDA Object_frame,x
		LSR
		LSR
		LSR
		AND #%00000111
		CLC
		ADC #$01
		AND #%00000111
		STA tempB
		;STA tempD ;; the action frame that was assigned during create macro.
		;;;; object behavior tables are with the lut table.
		;;;; the lut table is in bank 1C.
		; SwitchBank #$1C
			; ;;; Then, get the behavior for that object on that frame number.
			; LDY Object_type,x
			; LDA ObjectBehaviorTableLo,y
			; STA pointer
			; LDA ObjectBehaviorTableHi,y
			; STA pointer+1
			; LDY tempD
			; LDA (pointer),y
			; AND #%00001111
			; STA tempB
		; ReturnBank
		LDA tempB
		ASL
		ASL
		ASL
		STA tempC
		LDA Object_frame,x
		AND #%11000111
		ORA tempC
		STA Object_frame,x
	
		TXA 
		STA tempA
		
		DoObjectAction tempA, tempB
	;	;arg0 = what object?
		;arg1 = what step behavior?
	RTS
;;; 02 = Repeat
repeat_action:
	LDA Object_frame,x
		LSR
		LSR
		LSR
		AND #%00000111
		STA tempB
	
		TXA 
		STA tempA
	
		DoObjectAction tempA, tempB
	;	;arg0 = what object?
		;arg1 = what step behavior?
	RTS
;;; 03 = Go To First
goToFirst_action:
	
			LDA #$00
			STA tempB
		;;;; HURT END
		LDA Object_frame,x
		AND #%00111000
		CMP #%00111000
		BNE +notHurtFrame
			LDA #$00
			STA Object_h_speed_hi,x
			STA Object_h_speed_lo,x
			STA Object_v_speed_hi,x
			STA Object_v_speed_lo,x
			LDA Object_direction,x
			AND #%00001111
			STA Object_direction,x
		;;;;;;;;;;;;;;;;;;;;
		+notHurtFrame
		
		LDA Object_frame,x
		AND #%11000111
		STA Object_frame,x
		
	
		TXA 
		STA tempA
		
		DoObjectAction tempA, tempB
	;	;arg0 = what object?
		;arg1 = what step behavior?
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;; turn off move towards point if current action is 7.
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	RTS
;;; 04 = Go To Last
goToLast_action:
			LDA #$07
			STA tempB
	
		
		LDA Object_frame,x
		ora #%00111000
		STA Object_frame,x
		
		TXA 
		STA tempA
		
		DoObjectAction tempA, tempB
	;	;arg0 = what object?
		;arg1 = what step behavior?
	RTS
;;; 05 = Go To Previous
goToPrev_action:
		LDA Object_frame,x
	LDA Object_frame,x
		LSR
		LSR
		LSR
		AND #%00000111
		SEC
		SBC #$01
		AND #%00000111
		STA tempB ;; the action frame that was assigned during create macro.
		;;;; object behavior tables are with the lut table.
		;;;; the lut table is in bank 1C.
		ASL
		ASL
		ASL
		STA tempC
		LDA Object_frame,x
		AND #%11000111
		ORA tempC
		STA Object_frame,x
		
		TXA 
		STA tempA
		
		DoObjectAction tempA, tempB
	;	;arg0 = what object?
		;arg1 = what step behavior?
	RTS
;;; 06 = Destroy Me
destroyMe_action:
	DestroyObject
	RTS
;;; 07 = Go To Warp
goToWarp_action:
	WarpToScreen warpToMap, warpToScreen, #$01
	RTS
;;; 08 = Show Message (1)
show_message_action

	RTS
;;; 09 = Go To Continue
goToContinue_action:
	LDA continueMap
	STA warpMap
	
	LDA continueScreen
	STA currentNametable
	AND #%00001111
	STA camX_hi
	
	LDX player1_object
	STA Object_screen,x
	
	LDA #$02 ;; this is continue type warp.
	STA screenTransitionType ;; is of warp type
	
	LDA #$00
	STA camX
	sta camX_lo	
	
	LDA updateScreenData
	AND #%11111011
	STA updateScreenData
	LDA scrollByte
	AND #%00111110
	ORA #%00000010
	STA scrollByte
	LDA #$00
	STA scrollOffsetCounter
	
	LDA gameHandler
	ORA #%10000000
	STA gameHandler ;; this will set the next game loop to update the screen.
	ChangeActionStep player1_object, #$00
	StopMoving player1_object, #$FF, #$00
	ChangeFacingDirection player1_object, #FACE_RIGHT	
	
	LDA #$05 
	STA bossHealth ;; resets boss health for this level.
	RTS
;;; 10 = Restart Screen
restartScreen_action:
	LDA #$02
	STA screenTransitionType
	LDA gameHandler
	ORA #%10000000
	STA gameHandler ;; this will set the next game loop to update the screen.
	ChangeActionStep player1_object, #$00
	LDX player1_object
	LDA #$00000000
	STA Object_direction,x
	RTS
;;; 11 = Restart Game
restartGame_action:
	JMP RESET
	RTS
;;; 12 = User 0
userEnd0_action:

	RTS
;;; 13 = User 1
userEnd1_action:

	RTS

	
EndAnimAndActions_Lo:
	.db #<end_loop_action, #<advance_action, #<repeat_action, #<goToFirst_action, #<goToLast_action, #<goToPrev_action, #<destroyMe_action, #<goToWarp_action
	.db #<show_message_action, #<goToContinue_action, #<restartScreen_action, #<restartGame_action, #<userEnd0_action, #<userEnd1_action
	
EndAnimAndActions_Hi:
	.db #>end_loop_action, #>advance_action, #>repeat_action, #>goToFirst_action, #>goToLast_action, #>goToPrev_action, #>destroyMe_action, #>goToWarp_action
	.db #>show_message_action, #>goToContinue_action, #>restartScreen_action, #>restartGame_action, #>userEnd0_action, #>userEnd1_action

Unfortunately, you cannot just plug in the updated TimerendScripts in settings. This is the slightly complicated bit, you'll have to backup and update the Bank1C.asm file and make it point to your new TimerEndScripts file. The reference is on line7 of Bank1C, just change it to point to the updated TimerEndScripts_ShooterFix file and path.

Code:
;;; ALL THE DATA FIRST
.include "ScreenData\ObjectInfo.dat"
.include "ScreenData\ObjectPointers.pnt"
.include "ScreenData\ObjectData\ObjectLutTable.dat"

;=========================================================
.include ROOT\Game\MOD_MyScripts\Subroutines\TimerEndScripts_ShooterFix.asm ;;----THIS LINE RIGHT HERE NEEDS TO POINT TO YOUR NEW FILE
.include ROOT\Game\Subroutines\doObjectAction.asm
;========================================================

Then you can make your player 07 action perform whatever death animation you want, and after it plays, then it will restart from the last continue point.
PlayerDeath.gif

EDIT:
Fixed missing line with hurtWithLives_shooter_DeathAnim script.
 

Jonny

Well-known member
Sorry I misunderstood what you wanted. I guess if monsters have only one hit things are a lot easier as you can just use the hurt action.

I'll need this too, especially for bosses so thanks for all this info AllDarnDavey.
 

PasseGaming

Active member
Okay, I got the monster death animation working! Thank you so much Davey. This really adds a lot to the game believe it or not. Now to attempt the players animation.

Jonny said:
Sorry I misunderstood what you wanted. I guess if monsters have only one hit things are a lot easier as you can just use the hurt action.

I'll need this too, especially for bosses so thanks for all this info AllDarnDavey.

I tried following that bit of the tutorial before that Joe provided, for whatever reason I never managed to get it working correctly. I had always thought it was because of the module I am using.
 

PasseGaming

Active member
Okay, I am getting a "unknown label" error for line 4 for the hurtWithLives_shooter_DeathAnim.asm script. I'm not sure why, that's a valid bit. not sure what the issue is. Everything else seemed to go well. Then again, I can't playtest it but it complies just fine.

AllDarnDavey said:
Okay, player death time. This one is a bit more complicated, but not as bad as I thought it would be.
You'll need to backup and replace your player hurt script with this one I named hurtWithLives_shooter_DeathAnim.asm
Code:
	LDA gameHandler
	AND #%10000000
	BEQ +canHurtPlayer
		JMP +skipHurt
+canHurtPlayer:
	Dec myLives
	LDA myLives
	BNE +myLivesNotZero
		JMP RESET ;; game over.
		;;;; also could warp to game over screen here instead.

+myLivesNotZero	
	ChangeActionStep player1_object, #$07

Then setup the player action 07 with your death animation and change the action settings to match these:
PlayerDeath.PNG
 

AllDarnDavey

Active member
PasseGaming said:
Okay, I am getting a "unknown label" error for line 4 for the hurtWithLives_shooter_DeathAnim.asm script. I'm not sure why, that's a valid bit. not sure what the issue is. Everything else seemed to go well. Then again, I can't playtest it but it complies just fine.

Oops. Looks like I missed a line when I posted it. Edited my post to fix it. You just need to add +skipHurt to the end. Line 4 has an instruction to jump to that label, but the label is missing. We just need that label at the end to skip everything and do nothing if the gameHandler is zero.
 

PasseGaming

Active member
Okay, it works but there is some weirdness going on. Sometimes it shoots me back to the title screen, other times it takes me back to the beginning of the stage and also occasionally takes more then one life. https://www.filehosting.org/file/details/897172/SpiritImpelTest.nes this here is a link for the rom, do you mind giving it a look see? maybe you can figure out what the issue is from just playing it? I could also provide my code for check points if that helps.

I figure it could be a slew of different reasons, perhaps my checkpoint script? Though, I'm not sure how that would interfere with this.
 

AllDarnDavey

Active member
If you still have a number of lives enabled (even if they aren't in the HUD) it would shoot you back to the start screen when you run out of lives. Otherwise it would only go back to the last check point. Maybe you have 2 areas reducing lives?

I'll take a look when I get a spare minute, see if I see anything.
 

Bucket Mouse

Active member
The title is wrong, this is not solved -- we don't have a solution for enemy death animations either. I think I'm on the right track but it's not working.

This is my altered code in doHandleHurtMonster for the monster switching to its hurt animation. If Object_health,x equals zero, it moves to destroy the object...but I dummed it out and put in its place another action step change to 06, which has the death animation followed by DestroyMe.

Code:
		ChangeActionStep temp, #$07
		DEC Object_health,x
		LDA Object_health,x
		BEQ +dontSkipHurtingThisObject
			JMP +doSkipHurtingThisObject
		+dontSkipHurtingThisObject:
			;; maintain x and y positions for dropable location
			LDA Object_x_hi,x
			STA tempA
			LDA Object_y_hi,x
			STA tempB
;			DestroyObject
		ChangeActionStep temp, #$06

IT DOESN'T WORK.... it instead makes the enemy never die. DESTROY ME IS RIGHT THERE, YOU STUPID PROGRAM!! READ IT!!
 

PasseGaming

Active member
I followed the code Davey gave me and it worked just fine. Then again it was for the shooter module, so that maybe it. I take it that the code didn't work for you then?
 

Bucket Mouse

Active member
Davey's code requires destroying the monster, then loading another monster for the sole purpose of playing an exploding animation -- which means you have one less monster to work with on that screen.

Although it might be possible to reload the same monster, just in Action Step 6. So I tried to do just that:

Code:
		ChangeActionStep temp, #$07
		DEC Object_health,x
		LDA Object_health,x
		BEQ +dontSkipHurtingThisObject
			JMP +doSkipHurtingThisObject
		+dontSkipHurtingThisObject:
			;; maintain x and y positions for dropable location
			LDA Object_x_hi,x
			STA tempA
			LDA Object_y_hi,x
			STA tempB
			LDA Object_id,x
			STA tempC
			DestroyObject
CreateObject tempA, tempB, tempC, #$06

It replaced the monster with ----- a duplicate of the player sprite. Which means 00 was loaded into object_id, which COULD mean it wasn't being used. But if the right variable isn't object_id than what is it??
 

Bucket Mouse

Active member
Good news -- I discovered the right variable. It's object_type.

But bad news as well -- I've found another bug and it makes no sense at all. I can only respawn the same sprite in a different Action State if its hit points are 1 or 0. If the monster takes multiple hits to kill, I instead have to make a duplicate of that monster, set its HP to 1 and spawn THAT one for the end animation.

As I've written it here, it DECs Object_Id by 1 so it will look at the sixth Action State of the previous monster on the list. But you could also INC the value if you wanted the one after it.

Code:
		ChangeActionStep temp, #$07
		DEC Object_health,x
		LDA Object_health,x
		BEQ +dontSkipHurtingThisObject
			JMP +doSkipHurtingThisObject
		+dontSkipHurtingThisObject:
			;; maintain x and y positions for dropable location
			LDA Object_x_hi,x
			STA tempA
			LDA Object_y_hi,x
			STA tempB
			LDA Object_type,x
			STA tempC
			DEC tempC
			DestroyObject
CreateObject tempA, tempB, tempC, #$06

And yeah, it does what I want but man, there has to be a better way. Dale, where are you?
 

AllDarnDavey

Active member
EnemyHurtAndDeath.gif
Okay, for Passe's shooter we setup a generic death VFX because that made more sense for that type of game. But it'sa death VFX not custom death animation, but we can do that too, just need to tweak the hurtMonster script a bit.
Make and assign a new hurtMonster, give it a new name like hurtMonster_DieAnimation.asm and give it this code:
Code:
	TXA
	STA temp
	GetActionStep temp
	CMP #$07 ;; we will use action step 7 for hurt.
	BNE +
		JMP +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again until the timer wears off.
	+
	CMP #$06 ;; we will use action step 6 for die.
	BNE +
		JMP +doSkipHurtingThisObject ;; if he is dying, he can't be hurt again.
	+
		StopMoving temp, #$FF, #$00 ;;stop it from moving
		DEC Object_health,x	;;take off 1 point of health
		LDA Object_health,x	;;load it's health into accumulator for comparison
		BEQ +die	;;if zero health die
			ChangeActionStep temp, #$07	;change to hurt action
			PlaySound #sfx_damage	;;play hurt sound
			JMP +doSkipHurtingThisObject	;;skip the rest
		+die
			ChangeActionStep temp, #$06	;;change to death action
			PlaySound #sfx_dead	;; play death sound
			CountObjects #%00001000	;;count enemies onscreen for monster locks
			CMP #$02	;;because the object kills itself at the end of it's death animation and this script doesn't kill it anymore we check for 1 monster left or 0 using BCC which means less than 02
			BCC +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
	+doSkipHurtingThisObject
Next, make sure your enemy has health more then 1, and make hurt and death animations for it.
Use these settings for action 7 (we're leaving action 7 as hurt because it's a pain to change)
hurtAction.PNG

Also, we need to set up action 6 as our death animation with these settings:
deathAction.PNG

The end animation and end action settings as well as animation used are the important bits, tweak the rest to the monster specifics.

If you get the enemy moving crazy when in the hurt state try going into your doHandlePysics script finding the settings for RECOIL_SPEED_HI & RECOIL_SPEED_LO and lower them, or even zero them out. I found my probe enemies did not like recoil one bit.

The player death is a little more tricky as you can see from the solution we use for Passe's shooter game. I'll see about posting on player death when I have more time.
 

dale_coop

Moderator
Staff member
Bucket Mouse said:
Dale, where are you?

I am here, I haven't really followed the issue... As far as I understand it's about monster hurt animation and monster death animation?

Hmm... pretty sure it's your changeActionStep to hurt state that interferes with the changeActionStep to the death animation.
You could try something like this for your hurtMonster_custom.asm:

Code:
	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.
	CMP #$06 ;; we will use action step 6 for death.
	BEQ +doSkipHurtingThisObject ;; if he is dead, he can't be dead again.

		DEC Object_health,x
		LDA Object_health,x
		BEQ +dontSkipHurtingThisObject
			ChangeActionStep temp, #$07			;; change to HURT animation
			JMP +doSkipHurtingThisObject
		+dontSkipHurtingThisObject:
			;; maintain x and y positions for dropable location
			LDA Object_x_hi,x
			STA tempA
			LDA Object_y_hi,x
			STA tempB
			ChangeActionStep temp, #$06			;; change to DEATH animation
			
			
			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:
	+doSkipHurtingThisObject:
 

Bucket Mouse

Active member
I'll test it more when I wake up tomorrow, but in order for it to work in the Adventure module I have to work around the Recoil script which is in the middle of it:

Code:
	TXA
	PHA
	STA temp

	+dontSkipHurtingThisObject:
		;;;;;;;;;;;;;;;;;; Here, we need to determine the direction.
		;;;;;;;;;;;;;;;;;; To do that, we can compare positions of self and other's center.
		;;;;;;;;;;;;;;;;;;;; HANDLE RECOIL
			TXA
			PHA
			LDX selfObject
			LDA Object_direction,x
			AND #%11110000
			STA temp1
			PLA
			TAX
			
			LDA Object_direction,x
			AND #%00001111
			ORA temp1
			STA Object_direction,x
			
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	GetActionStep temp
	CMP #$07 ;; we will use action step 7 for hurt.
	BEQ +doSkipHurtingThisObject ;; if he is hurt, he can't be hurt again.
	GetActionStep temp
	CMP #$06 ;; we will use action step 6 for death.
	BEQ +doSkipHurtingThisObject ;; if he is dead, he can't be dead again.
	DEC Object_health,x
		LDA Object_health,x
		BEQ +dontSkipHurtingThisObject
			ChangeActionStep temp, #$07			;; change to HURT animation
			JMP +doSkipHurtingThisObject
		+dontSkipHurtingThisObject:
			;; maintain x and y positions for dropable location
			LDA Object_x_hi,x
			STA tempA
			LDA Object_y_hi,x
			STA tempB
			ChangeActionStep temp, #$06			;; change to DEATH animation
			
			
			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:
	+doSkipHurtingThisObject:

Thing is though, that makes the Stop Moving AI cease to work, you get the recoil instead, and I just had that fixed thanks to Joseph Rossi on the NESMaker Facebook page...it had to be rewritten because the Move Towards Player AI was incompatible with it.

Code:
LDA Object_direction,x
AND #%00000111
STA Object_direction,x
 
Top Bottom