• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Development: [FR] Trainer Interrupting Battle

239
Posts
8
Years
  • Age 31
  • Seen Apr 15, 2024
I know this was implemented in the Emerald battle engine upgrade, and there was some work done by FBI a few years ago on it, but I want to have a collaborative effort on getting this working for Fire Red, especially for hacks that won't use the dissasembly projects.

EDIT: Delta posted a link to a more completed version.

I researched this topic a year ago or so and was able to get a working version with some graphical side effects that I will explain later.

Background info for the curious:
Spoiler:


Implementation:
So wherever we want to have the trainer interrupt, we need a battle script to be run at that point in the battle. A logical place for the end-of-turn would be after all of the weather, etc checks that occur. Fortunately, there is a table at 0x17bc4 that runs end-of-turn routines and battle scripts if necessary. There is a limiter at 0x17b98 so we can easily repoint and expand this table to add more global end-of-turn effects (note that the last entry in the original table must always be last)

We can run a battle script from here to make the trainer enter the screen, play a custom string, and then use my whirlwind animation hack to push the trainer offscreen, resetting all of the object targets (introduced in the spoiler above).

Note: this works from a functional standpoint, but the trainer sprite is still prevalent in the VRAM or object data structure somehow and will show up again in several attacks such as Pound, Agility, etc. Entering the pokemon menu and returning fixes this problem.


Here is a test case using the global end-of-turn battle scripts. The following code expands the global effect table and loads a string and battle script if a specific byte in RAM is set (so you can load a certain number of strings). It includes the whirlwind anim hack. It deletes the trainer object afterwards but the aforementioned graphical issue is not solved.
Spoiler:


Of course, there are plenty of other applications and ways to implement this design. The meat of the research is around cmd53 and the pokemon objects. The main holdup for this to be complete is finding a way to completely remove the trainer sprite from the graphics after the fact. This may be a trivial solution with a simple in-game function, but I have limited experience with graphics-based asm. I will note that going into the pokemon party menu and returning fixes the problem, making me think that it has something to do with the object data.
 

Attachments

  • bs_macros.asm
    21.7 KB · Views: 10
Last edited:
51
Posts
9
Years
  • Age 33
  • Seen Nov 18, 2023
I have problems about compiling. I download the asm file, but if it compiles it is empty ...
What should I write inside?
For example I insert first routine at 900000, second routine at A00000, I must edit them?
Thancks for help :)
 
239
Posts
8
Years
  • Age 31
  • Seen Apr 15, 2024
Someone has already done it.

Good find! I'll update the main post.

I have problems about compiling. I download the asm file, but if it compiles it is empty ...
What should I write inside?
For example I insert first routine at 900000, second routine at A00000, I must edit them?
Thancks for help :)

For assembly files with .org locations, it will assemble empty files except for the data at the specified offsets, so the assembled file just looks empty. Of course, I would refer you to Delta's posted link since it is more complete and more functional than my test case :)
 
51
Posts
9
Years
  • Age 33
  • Seen Nov 18, 2023
I have a compatibility problem with this version https://gitgud.io/pfero/trainer_sliding and the MrDollSteak's Decap. and Attack Rombase (Version 1.5a).

I change source/scripts.h and source/scripts.s with 0x300 (before 0x200)
I change version/firered/insert.asm:bs_execute_handle and source/script_commands.h with 0xFF (before 0xF8).
But...it doesn't work :(
I see the offset 0x080d77be is used by both patch...I don't understand how to fix this problem...Help me please :(
 

Delta231

A noob
681
Posts
7
Years
I have a compatibility problem with this version https://gitgud.io/pfero/trainer_sliding and the MrDollSteak's Decap. and Attack Rombase (Version 1.5a).

I change source/scripts.h and source/scripts.s with 0x300 (before 0x200)
I change version/firered/insert.asm:bs_execute_handle and source/script_commands.h with 0xFF (before 0xF8).
But...it doesn't work :(
I see the offset 0x080d77be is used by both patch...I don't understand how to fix this problem...Help me please :(

You just need to change linker script which is in version/firered and read install.md as well for compiling instructions.

There is a big note there I will put it here.


The patch modifies the battle string loading function, to load a custom string, for string 0x200 (by default). This only happens when using a string ID bigger than 0x17C (for Emerald) or 0x181 (for Fire Red). If you've applied a patch that repoints the table or otherwise modifies the code to expand the table, you might encounter problems with it. This might happen for example when combined with patches that add new move effects. Consider either modifying the string ID used in the patch (source/scripts.h and source/scripts.s) or modifying string_hook (in the corresponding version/ directory) to do whatever suits you best.

Additionally, for Fire Red only, this patch implements the trainer_back_slide battle scripting command. This isn't done by repointing and expanding the battle scripting command table, but by hooking one of the functions that reads it, and overriding the behavior for command 0xf8. If you've applied any patch that repoints this table or otherwise modifies the code to expand the table, you might encounter problems with it. This might happen for example when combined with patches that add new move effects. Consider modifying the command ID used in the patch (version/firered/insert.asm:bs_execute_handle and source/script_commands.h) and/or pointing the command to the right function (version/firered/insert.asm:battlecommand_trainer_back_slide, see the generated *.patched.sym file for an address).
 
Last edited:
51
Posts
9
Years
  • Age 33
  • Seen Nov 18, 2023
I had already read those instructions and done what they said to do, but as I wrote at some point I can not continue because two routines come into conflict and a bug occurs.
 
51
Posts
9
Years
  • Age 33
  • Seen Nov 18, 2023
Solved (more or less...)!
If you want to apply in a hack rom patched by MrDollSteak's Decap. and Attack Rombase (Version 1.5a)...
1) change source/scripts.h and source/scripts.s with 0x300 (before 0x200);
2) change version/firered/insert.asm:bs_execute_handle and source/script_commands.h with 0xFF (before 0xF8);
3) edit trainer_sliding-master\version\firered like this:

Code:
.arm.little
.thumb

.open "firered.gba", "firered.patched.gba", 0x08000000

.definelabel free_space, 0x08(offset)

.definelabel battle_malloc_hook, 0x0802e0fa
.definelabel battle_malloc_ret, 0x0802e104|1

.definelabel battle_free_hook, 0x0802e1f8
.definelabel battle_free_ret, 0x0802e200|1

.definelabel sliding_hook, 0x08013c26
.definelabel sliding_hook_buffer, 0x02023DD0
.definelabel sliding_hook_turn_value_cleanup, 0x08015330|1
.definelabel sliding_hook_ret, 0x08013d20|1
.definelabel sliding_hook_continue, 0x08013c30|1

.definelabel string_hook, 0x080d77be
.definelabel string_hook_buffer, 0x0202298c
.definelabel string_hook_ret, 0x080d77e2|1
.definelabel string_hook_decode, 0x080d77dc|1

bs_commands equ 0x0895f480
.definelabel battlecommand_trainer_slide_ptr, (bs_commands + 0x53 * 4)
.definelabel battlecommand_trainer_slide, 0x080250dc|1

.definelabel bs_execute_hook, 0x08015c58
.definelabel battlecommand_trainer_back_slide_battler, 0x02023bc4
.definelabel battlecommand_trainer_back_slide_emit, 0x0800e114|1
.definelabel battlecommand_trainer_back_slide_unk, 0x08017248|1

.org battle_malloc_hook
    ldr r0, =battle_malloc_handle|1
    bx r0
.pool

.org battle_free_hook
    ldr r0, =battle_free_handle|1
    bx r0
.pool

.org sliding_hook
    ldr r0, =sliding_hook_handle|1
    bx r0
.pool

.org string_hook
    ldr r0, =string_hook_handle|1
    bx r0
.pool

.org battlecommand_trainer_slide_ptr
.word battlecommand_trainer_slide_handle|1

.org bs_execute_hook
    ldr r2, =bs_execute_handle|1
    bx r2
.pool


.org free_space
.incbin "trainer_sliding_firered.bin"
.include "trainer_sliding_firered.asm"

battle_malloc_handle:
; This hooks right after all of the other battle structs have been allocated

; State:
; r0-r3, lr are fair game.

	; Run the few instructions we've overwritten
    mov r0, r5
    ldr r1, =calloc
    bl @@bx_r1
    mov r1, r0
    str r1, [r4]

    bl battle_malloc

    ldr r1, =battle_malloc_ret
@@bx_r1:
    bx r1

.pool

battle_free_handle:
; This hooks right after all of the other battle structs have been freed

; State:
; r5 = 0
; r0-r3, lr are fair game.

    ; Run the few instructions we've overwritten
    ldr r0, [r4]
    ldr r1, =free
    bl @@bx_r1
    str r5, [r4]

    bl battle_free

    ldr r1, =battle_free_ret
@@bx_r1:
    bx r1

.pool

sliding_hook_handle:
; This hooks right after Perish Song/Future Sight are handled at the end of a turn.
; If a battle effect is executed, it's supposed to return early.

; State:
; r0-r3, lr are fair game.

; Return hooks:
; sliding_hook_ret:
;   Returns early from the hooked function.
; sliding_hook_continue:
;   Continues running the end-of-turn function, looking for other effects to
;   run and doing other end-of-turn-y stuff.

	bl turn_end_slide
	cmp r0, #0
	beq @@continue

	ldr r1, =sliding_hook_ret
	bx r1

@@continue:
	; Run the few instructions we've overwritten
    mov r0, #0
    ldr r1, =sliding_hook_turn_value_cleanup
    bl @@bx_r1
	ldr r2, =sliding_hook_buffer
	ldr r0, [r2]

	ldr r1, =sliding_hook_continue
@@bx_r1:
	bx r1

.pool

string_hook_handle:
; This hooks the default case in the battle string engine,
;  when a string ID is higher than the size of the string table.
; We can load our custom strings here.

; State:
; r6 = String ID
; r0-r10, lr are fair game.

; Addresses of interest:
; string_buffer:
;   Contains the resulting string to print. Not sure how big it is.
;   Should be filled with a single terminator (0xFF) if no string was found.

; Return hooks:
; string_hook_decode:
;   r7 = String pointer
;   Decodes the string, copying it to string_hook_buffer in the process.
; string_hook_ret:
;   Returns from the string loading function. Should have a valid string in
;   string_hook_buffer.

    mov r0, r6
    bl get_custom_string
    cmp r0, #0
    beq @@ret

    ; Decode the string
	mov r7, r0
	ldr r0, =string_hook_decode
	bx r0

@@ret:
	; Run the few instructions we've overwritten
	ldr r1, =string_hook_buffer
	mov r0, #0xFF
	strb r0, [r1]
	ldr r0, =string_hook_ret
	bx r0

.pool

battlecommand_trainer_slide_handle:
; This replaces the original trainer_slide command.

; We extend the trainer_slide command to handle some values in a different fashion.
; See slide_save_obj for details.

    push {lr}
    bl slide_save_obj
    cmp r0, #0
    pop {r0}
    bne @@bx_r0
    mov lr, r0
    ldr r0, =battlecommand_trainer_slide
@@bx_r0:
    bx r0

.pool

bs_execute_handle:
; This hooks the function used to run some battle scripts,
;  after fetching the next command ID from bs_pointer.
; There are more functions where the command IDs are interpreted,
;  but this is the only one that we need to interpret trainer_back_slide.

; State:
; r0 = Command ID
; r1 = Pointer to bs_commands
; r2, r3 and lr are fair game, the rest should be preserved.

    pop {r2}
    mov lr, r2

    cmp r0, #0xf9
    beq battlecommand_trainer_back_slide

    ; Run whatever command was called upon
    lsl r0, #2
    add r0, r1
    ldr r0, [r0]
    bx r0

.pool

battlecommand_trainer_back_slide:
; Port of trainer_back_slide from emerald into firered

    push {r4, r5, lr}

    ; Get the argument, make sure any value bigger than 1 is 1.
    ldr r5, =bs_pointer
    ldr r0, [r5]
    ldrb r0, [r0, #1]
    cmp r0, #0
    beq @@zero
    mov r0, #1
@@zero:

    ; Get and save the position
    ldr r1, =get_battler_at_position
    bl @@bx_r1
    ldr r4, =battlecommand_trainer_back_slide_battler
    strb r0, [r4]

    ; Emit a trainer slide back
    mov r0, #0
    ldr r1, =battlecommand_trainer_back_slide_emit  ; Fun fact: This function is never used in the game
    bl @@bx_r1

    ; Call an unknown function
    ldrb r0, [r4]
    ldr r1, =battlecommand_trainer_back_slide_unk
    bl @@bx_r1

    ; Skip to the next command
    ldr r0, [r5]
    add r0, #2
    str r0, [r5]

    pop {r4, r5}
    pop {r0}
    bx r0

@@bx_r1:
    bx r1

.pool

; Set up a dummy table for the sliding trainer messages.
_sliding_trainers:
    .word 0
    .word 0

.org sliding_trainers
.word _sliding_trainers

.close
 
Back
Top