Mgamerz icon

MMBN3 White Enhanced Bosses ASM v0.002 FLAMEMAN

Mgamerz | PRO | 12/28/16 12:25:17 AM UTC | 0 ⭐ | 248 👁️ | Never ⏰ | []
ASM (NASM) |

0 B

|

None

|

0 👍

/

0 👎

;Mgamerz Enhanced Enemies Mod v0.001
;12/17/2016
;Apply to MMBN3 ENGLISH WHITE version only! Apply with ARMIPS:
;armips filename.asm
 
input   equ whitevanilla.gba    ; Put the name of your ROM here
 
fspace equ 0x800000; Put the ROM free space offset here
 
.gba
.open input,output.gba,8000000h
 
rngaddress EQU 0x080016a2|1b
timestopchipcall EQU 08008E54h
GetPlayer EQU 0x080013E0
RecoveryChipRoutine EQU 0x80ADB04
SpawnObject EQU 0x08003084
;FieldConfiguration EQU 0x
SetPanelTeam EQU 0x0800B7C4
PlaySound EQU 0x080005B8
CalculateAIConstantsAddress EQU 0x080AEDBC
GetPanelType EQU 0x0800B4F2
 
;Flashman Chips
flashmanchipcount EQU 1h
flashmanChipHookAddress EQU 0x080B6726
 
gutsmanChipHookAddress EQU 0x080B8592
gutsmanZPunchHookAddress EQU 0x080B84C6
gutsmanChipCount EQU 4h
gutsmanZPunchChipCount EQU 3h
gutsmanMovesBeforeZPunch EQU 6h
 
teamfamily EQU 3Fh
areafamily EQU 0h
stagefamily EQU 4h
trumpyfamily EQU 26h
sensorfamily EQU 3Eh
grabbackfamily EQU 15h
mpchipfamily EQU 18h
div EQU swi 6
 
 
.org 0x08235154 ;Free Space
.area 0xDAC
.align 2
 
rngRoutine:
    ldr r0, =rngaddress
    bx r0
    pop r15
 
;setPanelToTeam:
;   ldr r0 =fieldConfiguration
 
;callTimestopChipWithoutTimestop:
    ;requires routine and parameters.
 
 
    ;healSelf:
    ;r0 = how much to heal
    ;r5 = who to heal
    ;bl RecoveryChipRoutine
 
;===========================FLASH MAN==========================
armRaiseMove:
    ;Hijack at 080D7A66
    push r1-r3,r5,r6
    push r14
    ;b performBulbSpawn
    push r0
    ldrh r0, [r5,24h] ;GetHP
    ldr r4, =12Ch
    cmp r0,r4
    pop r0
    bgt notInRecoveryThreshold
 
    inRecoveryThreshold:
        ldr r0, =0x7D ;how much HP to heal at level 0 (125)
        ldr r4, [r5,4h] ;get level
        add r4, r4, 1h ; prevents *0
        mul r0, r4 ;multiply how much HP to heal
        bl RecoveryChipRoutine
        mov r1, 0h ;0 = Navi Pixelated Summon
        strb r1, [r5, 0xC]
        mov r1, 1h  ;Give Flashman another chip to use as he may run out.
        strb r1, [r5,0x1A] ;0xA offset is how many chips an AI have to use, but r5 is incremented by 4 here for some reason.
        pop r1
        pop r1-r3,r5,r6; including get rid of return address, we will set our own to resume control
 
        ldr r0, =080D7A84h|1b
        bx r0 ;ignore post spawn
 
    notInRecoveryThreshold:
        
        push r1-r3
        bl rngRoutine ;GET RNG result AND PUT INTO r0
        mov r1, 02h ;remainder 0 = steal, anything else = bulbs
        swi 6; Divide, remainder is in r1
        mov r0, 0h
        cmp r0, r1
        pop r1
        pop r2
        pop r3
        bne performBulbSpawn
        stealPanels:
            ;position is on the stack it seems...?
            ;Make the areagrab noise since it won't automatically
            mov r0, 0x78
            bl PlaySound
            mov r0, 9h
            b armRaiseFinish
            pop r1-r3,r5,r6
            ldr r0, =080D7A84h|1b
            bx r0 ;ignore post spawn
 
        performBulbSpawn:
            mov r0, 11h
 
        armRaiseFinish:
            bl SpawnObject
            pop r15 ;finish
 
snakeTrace:
    push r14
    bl GetPlayer ;load X,Y into r0 r1
    ;r0 - Player Current X
    ;r1 - Player Current Y
    push r1
    push r3 ;temporary variable
    ldr r1, [sp,0xC] ;next X pos of bulb
    add r1, r1, 1h
    cmp r0, r1 ;compare next X+1 (same) vs Megaman's X
    ldrb r2,[r5,13h] ; highjacks at 080D7688 ;Current Y position
    bne snakeTraceNotLinedUp ;exit if numbers aren't the same
    snakeTraceLinedUp:
        pop r3
        pop r0 ;Player Y
        cmp r2, r0 ;compare Y coordinates. also used by the routine we return to
        bne snakeTraceDifferentYs
        b snakeTraceLinedUpExit ;same Ys.
 
        snakeTraceDifferentYs:
            str r1, [sp,0x4] ;store same X if our current Y is not the same as the player Y (hit and leave field)
 
        snakeTraceLinedUpExit:
            pop r15
 
    snakeTraceNotLinedUp:
        pop r3
        mov r1, 1h
        cmp r1, 1h ;force same y
        pop r1
        ;mov r2, 1h
        ;cmp r2, 2h ;Same Y
        pop r15
    
 
loadFlashmanRandomChip:
    push r14
    bl rngRoutine ;GET RNG result AND PUT INTO r0
    mov r1, flashmanchipcount ;number of chips in selection list
    swi 6; Divide
    ldr r0,=@@flashmanChipLookupTable ;Load address of chip lookup table (defined below)
    mov r2,0x03 ; Load r2 with 3
    mul r1,r2 ;Multiply the remainder result by the size of the structs (3 bytes) - this will provide an offset into the chip lookup table
    add r0,r0,r1 ;calculate the address using the offset, store in r0
    ldrb r3, [r0,0x02] ;load byte from *r0 (offset 2 forward) into r3 (dmg)
    ldrb r1, [r0,0x01] ;load byte from *r0 (offset 1 forward) into r1 (sublevel) 
    ldrb r0, [r0,0x00] ;load byte from *r0 (direct addr) into r0 (family)
    ldrb r2,[r5,16h] ; ...this is actually the object's alliance enemy/allied
    pop r15
 
.pool
@@flashmanChipLookupTable:
;.byte teamfamily, 1h, 0h
.byte areafamily, 0h, 10h
;.byte stagefamily, 4h, 0h
;.byte trumpyfamily, 2h, 0h
 
;Flashman zap throwdown
.align 4
flashmanThrowdownAction:
    push r1,r2,r5,r6
    push r14
    bl rngRoutine ;GET RNG result AND PUT INTO r0
    mov r1, 0x3 ;number of available actions
    swi 6; Divide
    ldr r0,=flashmanThrowdownLookupTable ;Load address of actions
    add r0,r0,r1 ;calculate the address using the offset, store in r0
    ldrb r1, [r0, 1h] ;sound check
    ldrb r0, [r0] ;action
    tst r1, r1
    bne playThrowdownSFX
    b finishFlashmanThrowdown
    playThrowdownSFX:
        push r0
        mov r0, r1
        bl PlaySound
        pop r0
 
    finishFlashmanThrowdown:
        pop r15
 
.pool
flashmanThrowdownLookupTable:
.byte 0x35, 0x00 ;Mine
.byte 0x3A, 0x9D ;YoYo
.byte 0x10, 0x00 ;ZapAttack
 
;-----------------------------GUTS MAN----------------------
 
;This block is unused right now due to spam issues
;loadGutsmanRandomChip:
;push r14
;bl rngRoutine
;mov r1, gutsmanchipcount
;swi 6; Divide
;ldr r0,=@@gutsmanChipLookupTable ;Load address of chip lookup table (defined below)
;mov r2,0x03 ; Load r2 with 3
;mul r1,r2 ;Multiple the remainder result by the size of the structs (3 bytes) - this will provide ;an offset into the chip lookup table
;add r0,r0,r1 ;calculate the address using the offset, store in r0
;ldrb r3, [r0,0x02] ;load byte from *r0 (offset 2 forward) into r3 (dmg)
;ldrb r1, [r0,0x01] ;load byte from *r0 (offset 1 forward) into r1 (sublevel) 
;ldrb r0, [r0,0x00] ;load byte from *r0 (direct addr) into r0 (family)
;ldrb r2,[r5,16h] ; ...this is actually the object's alliance enemy/allied
;pop r15
 
 
;.pool
;@@gutsmanChipLookupTable:
;.byte teamfamily, 0h, 0h
;.byte areafamily, 1h, 10h
;.byte stagefamily, 4h, 0h
;.byte trumpyfamily, 1h, 0h
;.align 4
loadZPunchRandomChip:
    .align 2
    push r14
    bl rngRoutine
    mov r1, gutsmanZPunchChipCount
    swi 6; Divide
    ldr r0,=@@gutsmanZPunchLookupTable ;Load address of chip lookup table (defined below)
    mov r2,0x03 ; Load r2 with 3
    mul r1,r2 ;Multiple the remainder result by the size of the structs (3 bytes) - this will provide an offset into the chip lookup table
    add r0,r0,r1 ;calculate the address using the offset, store in r0
    ldrb r3, [r0,0x02] ;load byte from *r0 (offset 2 forward) into r3 (dmg)
    ldrb r1, [r0,0x01] ;load byte from *r0 (offset 1 forward) into r1 (sublevel) 
    ldrb r0, [r0,0x00] ;load byte from *r0 (direct addr) into r0 (family;)
    ldrb r2,[r5,16h] ; ...this is actually the object's alliance enemy/allied
    pop r15
 
.pool
@@gutsmanZPunchLookupTable:
;.byte teamfamily, 0h, 0h
;.byte areafamily, 1h, 10h
;.byte stagefamily, 4h, 0h
.byte trumpyfamily, 2h, 0h
.byte sensorfamily, 2h, 100h
.byte mpchipfamily, 2h, 0h
 
;=================GRAB REVENGE===========
.align 2
grabRevengeResetBoard:
    push r14
    //Read what team called this chip 00 = Player 04 = Enemy. This conveniently also means the return value can be used as an offset to the list of timer pointers
    ldrb r0, [r5,6h]
    //lsr r2, r2, 2h //Divide by 4 - 1 is enemy 0 is us
    //mul r1, r2, 4h ;0x4 = no offset, 1x4 = 2nd offset (enemy team offset in the pool)
    ldr r1, =GrabRevengeMemoryTimerPointer
    //ldr r1, [r1] //Load pointer to first item in pool
    add r0, r1, r0 //get offset in the pool below
    ldr r0, [r0] //load the value into 40 from there
    mov r1, 5h ;timer value to set, will be incremented by 15 frames every time to create a nice 
    push r2,r3
    mov r2, 0h
    mov r3, 2h ;0-2 columns
    
    returnColumnsInCascade:
        strh r1, [r0] ;Set the current timer of panels to 5 frames. After those 5 frames the panels will return
        add r0, r0, 4h ;Go to next position for next row.
        push r0
        mov r0, 20h
        add r1, r1, r0
        pop r0
        //add r1, r1, 7h;Add 14 frames for cascade
        //add r1, r1, 7h;Add 14 frames for cascade
        add r2, r2, 1h ;Update counter
        cmp r2,r3
        ble returnColumnsInCascade
 
 
    pop r2,r3
    mov r0, 5h ;original code
    mov r1, 4h ;original code
    pop r15
 
.pool
GrabRevengeMemoryTimerPointer:
.dw 0200F7A4h ;Panels that the enemy stole from you will reset
.dw 0200F7B0h ;Panels that the player stole from the other team will reset
 
//=========================FLAME MAN======================================
flamemanNextCandleStateRandom: //return next state on r0.
    push r14
    push r2
    push r3
    strb r0, [r5,6h]
    bl rngRoutine
    mov r1, 4h //Number of candle states
    div //r1 = remainder
    mov r0, r1
    pop r3
    pop r2
    pop r15
 
flamemanYellowGuyChangeToHorizontalOnSameRowAsPlayer:
    push r14
    push r0-r2
    bl GetPlayer ;r0 = X, r1 = Y
    mov r3, r1 //Put player Y in r3
    pop r0-r2
    ldr r1,[r5,44h]
    tst r1,r1
    pop r15
 
flamemanYellowGuyChangeToVerticalOnSameColumnAsPlayer:
    push r14
    push r0-r2
    bl GetPlayer ;r0 = X, r1 = Y
    mov r3, r0 //Put player X in r3
    pop r0-r2
    ldr r1,[r5,40h]
    tst r1,r1
    pop r15
 
flamemanLevelUpCandleExtinguished:
    push r14
    push r1,r2
    ldrb r0, [r5,0Ch] //Read state that is being extinguished
    //Check if it is 0x4 (Level Up)
    cmp r0, 8h //8 as its left shifted by 1.
    bne @@flamemanCandleExtinguishedFinish
 
    //We are extinguishing a level up flame
    ldr r0,[r5,4Ch] //Get Pointer To Parent
    ldrb r2, [r0,4h] //Get current level
    sub r2, 1h
    strb r2,[r0,4h] //Store level -1
    bl flamemanUpdateAIConstantsAddress
 
    @@flamemanCandleExtinguishedFinish:
        pop r1,r2
        mov r0, 0h //Original Code
        strb r0,[r5,0Ch] //Original Code
        pop r15
 
flamemanLevelUpCandleActivating:
    push r1,r2,r14
    ldrh r0, [r5,20h] //Read activation timer
    cmp r0, 0h
    bne @@flamemanEndCandleActivation
    add r0, 1h
    strh r0, [r5,20h] //Set candle timer to 1 (it doesn't count down while active) to indicate this effect has already been applied
    ldrh r0, [r5,24h] //Get HP
    lsl r0, r0, 0x2 //Quadruple HP
    strh r0, [r5,24h]
    //strh r0, [r5,26h]
    ldr r0,[r5,4Ch] //Get Pointer To Parent
    ldrb r2, [r0,4h] //Get current level
    add r2, 1h
    strb r2,[r0,4h] //Store level +1
    bl flamemanUpdateAIConstantsAddress //R0 = FlamemanPointer, R2 = Level
    @@flamemanEndCandleActivation:
    pop r1,r2,r15
 
.align 4
flamemanCustomCandleActivationArray:
    .word 0x080E2CC9 //Green Empty Function
    .word 0x080E2CCD //Red Empty Function
    .word 0x080E2CD1 //Yellow Empty Function
    //NEW ONE HERE //NEW Level Up Startup Function
    .word flamemanLevelUpCandleActivating|1b
 
flamemanUpdateAIConstantsAddress:
    push r14
    push r5 //This operation requires r5 to point to flameman memory
    mov r5, r0 //Update R5 with the one referenced by the candle parent
    ldr r0, =flamemanNewAIData ;Flameman AI Base
    mov r1, 0Ah ;Flameman's data is 0xA bytes wide per level
    bl CalculateAIConstantsAddress
    pop r5
    pop r15
 
    .pool
    //Flameman New AI Data
    flamemanNewAIData:
    //      0    1    2    3    4    5    6    7    8    9 
    .byte 0x3C,0x3C,0x3C,0x1E,0x2D,0x08,0xA4,0x0A,0x10,0x78 //Normal
    .byte 0x64,0x50,0x50,0x19,0x28,0x0C,0x90,0x09,0x18,0x64 //Alpha
    .byte 0x96,0x96,0x96,0x14,0x23,0x15,0x70,0x08,0x20,0x50 //Beta
    .byte 0xC8,0xC8,0xC8,0x0F,0x1E,0x20,0x58,0x06,0x20,0x3C //Omega
    .byte 0xE1,0xE1,0xE1,0x0A,0x0F,0x25,0x44,0x03,0x28,0x28 //Omega + 1
    .byte 0xFA,0xFA,0xFA,0x05,0x0A,0x28,0x40,0x01,0x30,0x16 //Omega + 2
 
    //Candle info
    //[02038A74]=CURRENT SELF POINTER
    //[02038AC0]=PARENT POINTER
    //r5+4C = Parent Pointer
 
flamemanAdditionalAttacksWithLevelUpCandleCheck:
    push r0-r2,r14
    mov r0, #0x0 //init bitmask
 
    //Check Candle 1
    ldr r1, [r5,#0x60]
    tst r1,r1 //Candle 1 is not null check
    beq @@CheckCandle2
    ldrb r2, [r1,#0xC]
    orr r0, r2 //add effect to bitmask
 
    //Check Candle 2
    @@CheckCandle2:
    ldr r1, [r5,#0x64]
    tst r1,r1 //Candle 2 is not null check
    beq @@candleEffectCheck
    ldrb r2, [r1,#0xC]
    orr r0, r2
 
    @@candleEffectCheck:
    //8 = Leveled Up
    mov r1, 8h
    and r0, r1
    tst r0, r0
    beq @@flamemanLaunchStandardFlameAttack
 
    //Different Flame Attack
    @@flamemanLaunchDifferentFlameAttack:
        bl rngRoutine ;GET RNG result AND PUT INTO r0
        mov r1, 0x4
        swi 6
        tst r1,r1
        bne @@flamemanLaunchModifiedFlameAttack
        @@flamemanSpawnFireRatn:
            //Check if square we are going to spawn in is occupied by something. If it is, we shouldn't spawn a ratton
            pop r0-r2
            push r0-r2 //we might still need these...
            mov r0, r1
            mov r1, r2 //move positions to correct registers
            bl GetPanelType
            mov r1, 0xE
            lsl r1, r1, 0x18 //Occupied mask
            //mov r1, 0x10 //broken panel mask
            //orr r0, r1 //also check if panel is broken
            tst r0, r1
            bne @@flamemanLaunchModifiedFlameAttack //it is occupied
 
            //Spawn Ratton
            pop r0-r2 //Discard
            mov r0, 0xC8 //HP of Object
            mov r3, 1h //Enemy Team
            mov r4, 0h //Memory Space that will be assigned to this object on spawn (by spawnobject)
            mov r6, 0xE6 //230 Damage
            bl 0x080DA218 ;SpawnRatton
            pop r15
 
        @@flamemanLaunchModifiedFlameAttack:
            //Recover a small amount of HP
            push r4
            ldr r0, =0xA ;how much HP to heal at level 0 (125)
            ldrb r4, [r5,4h] ;get level
            add r4, r4, 1h ; prevents *0
            mul r0, r4 ;multiply how much HP to heal
            bl RecoveryChipRoutine
            pop r4
            mov r0, 30h
            lsl r1, r1, 18h
            orr r4, r1
        @@flamemanLaunchStandardFlameAttack:
            pop r0-r2
            bl 0x080E317E ;FlamemanSpawnInitialFlameTower
            pop r15
 
.align 4
.pool 
@@FlamemanCustomFlameAttackPattern:
.word 0x06280C00 //Straight Flame
.word 0x02241401 //WideFlame
.word 0x32241401 //WideFlame, goes 3 rows deep (at least)
.word 0x02241400
 
.endarea
;==================HOOKS================
.org 0x080E2F84 //ldr r1,[r5,44h]
flamemanYellowGuyYHook:
    bl flamemanYellowGuyChangeToHorizontalOnSameRowAsPlayer
 
.org 0x080E2F2A //ldr r1,[r5,40h]
flamemanYellowGuyXHook:
    bl flamemanYellowGuyChangeToVerticalOnSameColumnAsPlayer
 
.org 0x080E2D46 //strb r0,[r5,6h]
flamemanRandomCandleStateHook:
    push r14
    nop
    bl flamemanNextCandleStateRandom
    pop r15
 
.org 0x080E2C10 //mov r0, 0h
flamemanCandleExtinguishLevelUpHook:
    bl flamemanLevelUpCandleExtinguished
 
.org 0x080C57C0 //bl FlameManSpawnInitialTower
    bl flamemanAdditionalAttacksWithLevelUpCandleCheck
 
.org 0x080F9E9E 
grabRevengeResetBoardHook:
    bl grabRevengeResetBoard
 
.org flashmanChipHookAddress
flashmanChipHook:
    bl loadFlashmanRandomChip
    nop
 
.org 0x080D7A66 ;Flashman Bulb Spawn (Arm Raise Move to heal)
flashmanArmRaiseMoveHook:
    bl armRaiseMove
    nop
    nop
 
.org 0x080D7664 ;In the Follow Trace Y calculation area (will only affect follow and not zigzag tracer)
snakeTraceHook:
    bl snakeTrace
    nop
    nop
 
.org 0x080D7850 ;Flashman zap throwdown
flashmanThrowdownHook:
    bl flashmanThrowdownAction
 
;.org gutsmanChipHookAddress
;gutsmanAreaGrabChipHook:
;bl loadGutsmanRandomChip
;nop
 
.org gutsmanZPunchHookAddress
    bl loadZPunchRandomChip ;Hook z-punch chip and use my routine instead
    ;nop
 
;=================PATCHES===============
 
;--------------HP SPEED PATCH---------------------
;Makes HP display change faster than default (which is 2)
.org 0x08010E22
    mov r0, 4h
 
;------------FLAME MAN PATCHES----------
.org 0x080C53EC
    mov r1, 5h ;Candle 1 at X=5 rather than X=6
 
.org 0x080C5406
    mov r1, 5h ;Candle 2 at X=5 rather than X=6
 
.org 0x080E2F98
    cmp r0, r3 ;YellowGuyY will now go left on same row rather than 3
 
.org 0x080E2F40
    cmp r0, r3 ;YellowGuyX will now go down on same row rather than 3
 
.org 0x080E2CB8
    .word flamemanCustomCandleActivationArray ;Rewrite the activation array pointer to my own so the level up function can activate (and avoid crash)
 
.org 0x080C5440
    .word flamemanNewAIData ;Repoint AI data to my own so we have omega +2 levels
 
.org 0x080C5916
    .byte 7h ;Allow flameman to attack from back row 
 
.org 0x080C5424
    //Patch out Flameman's Off-Limits 6,2 position.
    nop
    nop
    nop
    nop
 
.org 0x080C566A 
    //Patch out FlameMan's additional off-limits 6,2 check
    nop
    nop
 
.org 0x080E30B8
    //Patch out flames despawning on holes
    nop
    nop
 
.org 0x080C559A
    cmp r0, #0x6 //Flameman 6 moves in upper half HP instead of 8
 
.org 0x080C55B8
    cmp r0, #0x3 //Flameman 3 moves in lower half of HP instead of 5
 
;--------------Flashman Patches---------
 
;--------------GUTSMAN PATCHES----------
;.org 0x080B7DF0 ;gutsmans sets move counter here to 0xFF so Z-Punch attack no longer comes up
;mov r0, #0x0 ;reset to 0 instead so he can use it again. this is the amount of moves he has done.
 
;.org 0x080b7de8 ;gutsmans checks if his move count is same as this in order to activate Z-Punch
;cmp r0, gutsmanMovesBeforeZPunch
 
;.org 0x080b7dcc ;gutsman checks this value and if his hp is less he can use zpunch. by default it is division by 2 by shifting to the right by 1.
;add r1, r1, 1 ;add 1 HP so its one over the max. Can use zpunch anytime now
 
.org 0x080b8590 ;gutsmans areagrab response chip
mov r0, grabbackfamily
mov r1, 0x1 ;grabrevenge sublevel
ldrb r2,[r5,#0x16]
mov r3,#0x32 ;50 damage per hit
 
;-.org 0x080ae6a4 ;gutsman beta AI - delay after shockwave
;-.byte 0x64 ;contact damage
 
.org 0x08019b68 ;flashman beta HP
.byte 0xe8
.byte 0x03 ;1000 HP 0x03e8
 
.close
 ; eof

Comments