Make bosses not reset on hurt [4.1+]

Hey everybody, I know a big issue a lot of us have faced is that our monsters reset to action 0 when they get hurt.
This has been pretty annoying and makes bosses way too easy to beat.

I have a little update that can help with this issue. You will have to edit a system script, so be sure to make a backup.
Thanks to Mugi and Dale_Coop for pointing me in the right direction for this!

OK, you will need to edit your routines\module\system\HandleObjectUpdates.asm script.
Look for the label that says HandleObjectTimers:

You are going to want to change the code that follows it to this (or similar):
Code:
HandleObjectTimers:
;; Updated version by Chronicler Of Legends 3_3_19
;;; handle object timers, tool
	LDA Object_timer_0,x
	BEQ ObjectTimer0Finished
	DEC Object_timer_0,x
	LDA Object_timer_0,x
	BNE ObjectTimer0Finished ;; timer is still going.
	;;;;; object timer 0 has cycled and is finished.
	;;;;; right now, we'll use this to flip hurt/invincible to just invincible, to normal.
	LDA Object_status,x
	AND #%00000001 ;; is it hurt and invincible?
	BEQ notHurtAndInvincible
	;; hurt and invincible
	LDA Object_status,x
	AND #%11111110
	ORA #%00000010
	STA Object_status,x
    
    ;; Is this a player, boss, or other object?
    LDA Object_type,x
    CMP player1_object
    BNE +
    ;; Changed this to custom invincibility timer.
    LDA #PLR_INVINC_TIM ;; my custom invincibility timer constant
	STA Object_timer_0,x
    JMP handleObjectTimer_afterSetInvincibility   
    
+:  ;; Not player object
    ;; Is it a boss?
    LDA Object_type,x
    CMP #ENEMY_BOSS1_ID  ;; CUSTOM ID SET IN USER CONSTANTS, you can change this to however you want to flag an enemy as a 'boss'
    BNE +
    LDA #INVINCIBILITY_TIMER ;; Same as a normal enemy
	STA Object_timer_0,x
    JMP handleObjectTimer_afterSetInvincibility
    
 ;+: ;; Add other things that you want to have custom invulnerablity timers here.
 ; LDA Object_type,x ; this can be any type of comparison that flags the object
 ; CMP #SOME_ID_HERE
 ; BNE +
 ; LDA #CUSTOM_TIMER
 ; STA Object_timer_0,x
 ; JMP handleObjectTimer_afterSetInvincibility

+:
    ;; Normal object
	LDA #INVINCIBILITY_TIMER
	STA Object_timer_0,x
    
handleObjectTimer_afterSetInvincibility:
    ;; Is it a boss?
    LDA Object_type,x
    CMP #ENEMY_BOSS1_ID
    BNE +
    JMP ObjectTimer0Finished ;; Skip resetting the movement
+:
	LDA Object_movement,x
	AND #%00001111
	STA Object_movement,x
	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
	
	
	
	
	JMP ObjectTimer0Finished
notHurtAndInvincible:
	LDA Object_status,x
	AND #%00000010
	BEQ ObjectTimer0Finished ;; no longer invincible...not sure what this timer would be doing at this point.
	;;; it IS still invincible, need to change it back to normal.
	LDA Object_status,x
	AND #%11111100
	STA Object_status,x
	;;; activate state 0
	;ChangeObjectState #$00,#$02
    
    ;; Is it a boss?
    LDA Object_type,x
    CMP #ENEMY_BOSS1_ID
    BNE +
    JMP ObjectTimer0Finished ;; Don't reset the action for bosses.
+:
	JSR DoNewAction
	
ObjectTimer0Finished:
	RTS

There are a few Constants in there you will need to create to make it work,
but this script does a few things:
1) You can place custom invulnerability timers on the player, bosses, or other objects
2) It halts the movement of normal enemies on hurt, but not bosses
3) For normal enemies, it resets the CURRENT action they are on, but not set them back to 0.
4) For a boss (or any other enemy you may add an ID for) it will give them invulnerability time after getting hit, but WILL NOT reset their actions.
This way you can have a boss that can actually fight back!

Now I am sure this script isn't perfect, it depends how you have your enemies and bosses setup. But it shouldn't be too difficult to customize to your needs.
One suggestion I have had already (Thanks Cargo) is to replace one of your objects action flags with a 'not stunnable flag' and check that instead of object ID.
That way you could have bosses that have specific animations that they are vulnerable to being 'stunned'

Anyways, I hope this helps somebody out with their bosses and other enemies!
 

Tishero

New member
This is great, but I have one small problem... When I increase the iframes for the player and take damage. the player (skates) to the opposite side of the screen. Also, how do I flag an enemy as a boss?
 
As for flagging bosses, see this part of the code:

Code:
;; Is it a boss?
    LDA Object_type,x
    CMP #ENEMY_BOSS1_ID  ;; CUSTOM ID SET IN USER CONSTANTS, you can change this to however you want to flag an enemy as a 'boss'
    BNE +

For each boss in your game you create a user constant with its ID. (Count down to it in your monster assets starting with #16 as your first monster. That number is its ID)
Then you add a small section of code just like above to check for that ID and do things different accordingly.

I am investigating the other issue now.

EDIT:
I'll use this post too as a suggestion plug if Joe is reading:
It would be super helpful in a lot of cases if we had an 'IsBoss' checkbox or similar for monsters.
I have so much specialized code that uses checks like the above to make sure it only does it to either normal enemies or boss monsters (whichever is needed)
 

dale_coop

Moderator
Staff member
Agreed, more object_flags would be so useful :)

Else you could use for example a combo "Monster" + "Pickup" for your bosses... so you could check
Code:
	LDA Object_flags,x
	AND #%00010100 ;; is it a boss monster type?
	BEQ isNotBossMonster
	;; it IS a Boss Monster:

Because the obj collision code check in that order (if I remember it well):
- Player ?
else
- NPC ?
else
- Monster ?
else
-Pickup ?
else
end.
 

WillElm

New member
I put this change into HandleUpdateObjects.asm in 4.1.5, and it doesn't seem to work out of the box. It does have an effect: the monster that I flag as a boss has halted movement. But, it still resets to action 0, and the other monsters do as well. I wonder if the HandleHurtMonster script is in the way somehow?
 

dale_coop

Moderator
Staff member
Yep, in the HandleHurtMonster script, you could try commenting out the line (4):
Code:
	; ChangeObjectState #$00,#$02

If not results... could you share your HandleUpdateObjects.asm script?
 

WillElm

New member
I got it to work by commenting out line 21 (ChangeObjectState #$00,#$02) and by uncommenting all hurt-related constant declarations in HandlePlayerHurt and Constants.asm.

In doing this, I started wondering how redundant the rest of my HandleHurtMonster script is. I think I might be using one that's different from the one you were referring to, since you said that the ChangeObjectState is line 4.

Code:
;;; what should we do with the monster?
        ;;; monster is loaded in x
        LDA Object_vulnerability,x
        AND #%00000100 ;; is it weapon immune?
        BEQ notWeaponImmune
        ;PlaySound #SFX_MISS
        JMP skipHurtingMonsterAndSound
    notWeaponImmune:
        
        LDA Object_status,x
        AND #HURT_STATUS_MASK
        BEQ dontskipHurtingMonster
        JMP skipHurtingMonster
    dontskipHurtingMonster:
        LDA Object_status,x
        ORA #%00000001
        STA Object_status,x
        LDA #HURT_TIMER
        STA Object_timer_0,x
        ;;; assume idle is in step 0
;        ChangeObjectState #$00,#$02
        ;;;; unfortunately this recoil is backwards
        LDA Object_status,x
        AND #%00000100
        BNE skipRecoilBecauseOnEdge
        LDA Object_vulnerability,x
        AND #%00001000 
        BNE skipRecoilBecauseOnEdge ;; skip recoil because bit is flipped to ignore recoil
        
        LDA selfCenterX
        STA recoil_otherX
        LDA selfCenterY
        STA recoil_otherY
        LDA otherCenterX
        STA recoil_selfX
        LDA otherCenterY
        STA recoil_selfY
        JSR DetermineRecoilDirection
    skipRecoilBecauseOnEdge:
        LDA Object_health,x
        SEC
        SBC #$01
        CMP #$01
        BCC +
        JMP notMonsterDeath
    +
    LDA Object_x_hi,x
    STA temp
    LDA Object_y_hi,x
    STA temp1
        DeactivateCurrentObject


    CreateObject temp, temp1, #OBJ_MONSTER_DEATH, #$00, currentNametable

        PlaySound #SND_SPLAT
        ;;;;;;;;;;;;;;;;;; ok, so now we also add points to score
        ;LDY Object_type,x
        ;LDA ObjectWorth,y
        ;STA temp
;       AddValue #$03, GLOBAL_Player1_Score, temp
                ;arg0 = how many places this value has.
                ;arg1 = home variable
                ;arg2 = amount to add ... places?
        ;; and this should trip the update hud flag?
        
        ;;;; 
    

    TXA
    STA tempy

    AddValue #$08, myScore, #$01, #$00

    ;STA hudElementTilesToLoad
    ;   LDA #$00
    ;   STA hudElementTilesMax
        ; LDA DrawHudBytes
        ; ora #HUD_myScore
        ; STA DrawHudBytes
    UpdateHud HUD_myScore
    LDX tempy

        JSR HandleDrops
        JSR HandleToggleScrolling
        
        CountObjects #$00001000, #$00
        BEQ +
        JMP ++
    +
        .include SCR_KILLED_LAST_MONSTER
    ++
        
        JMP skipHurtingMonster
    notMonsterDeath
        STA Object_health,x
    skipHurtingMonster: 
        ;PlaySound #SFX_MONSTER_HURT
    
    skipHurtingMonsterAndSound:
        LDX tempx
        ;; what should we do with the projectile?
;         DeactivateCurrentObject
;     INC tempx
 

dale_coop

Moderator
Staff member
Ok, for your line 21, yes it was that code I was talking about
(on my script it was on line 4... but I think it depends on which module you are).

But, why comment out all the HURT related code? I think you should keep it. It's aprt of the engine.
You can reduce some values if you need (if you want a shorter recoil, or shorter hurt state, ...), but commenting it out ell the code?
Could you elaborate?
 

WillElm

New member
dale_coop said:
Ok, for your line 21, yes it was that code I was talking about
(on my script it was on line 4... but I think it depends on which module you are).

But, why comment out all the HURT related code? I think you should keep it. It's aprt of the engine.
You can reduce some values if you need (if you want a shorter recoil, or shorter hurt state, ...), but commenting it out ell the code?
Could you elaborate?

To clarify: I commented out all the hurt related constant declarations in my playerhurt script, not the hurt code itself. Because these declarations were redundant, since the same constants are declared in the project settings.
 

crazygrouptrio

Active member
Trying to make this script work but all monsters still reset to State 0 after being hurt, and I've commented out the ChangeObjectState line in HandleUpdateObjects and HandleHurtMonster?
 

DanielT1985

Member
It only causes the player and objects to freeze at the first frame/state they were at when they first loaded. Any way to fix this?
 
Top Bottom