You are not logged in.
Pages: 1
I'm sure you've all seen this video before Which shows off some unused text. Presumably it was left over from when losing to enemy trainers didn't end the game. But did you know that most of the functionality to restore it already exists in the game's code?
Today, I'm gonna show you how use the code left behind to give enemy trainers victory quotes if they win (in five minutes or less!)
We start with the code that makes the player black out once all their pokemon are fainted. Interestingly, this code DOES already display a win quote, but only for the first Rival fight. It looks like this:
; called when player is out of usable mons.
; prints approriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
HandlePlayerBlackOut:
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr z, .notSony1Battle
ld a, [wCurOpponent]
cp OPP_SONY1 ;is the current opponent Rival1?
jr nz, .notSony1Battle ;if not, don't print our message.
coord hl, 0, 0
lb bc, 8, 21
call ClearScreenArea ;clear the top-right of the screen in case there's a pokemon there.
call ScrollTrainerPicAfterBattle
ld c, 40
call DelayFrames
ld hl, Sony1WinText
call PrintText
ld a, [wCurMap]
cp OAKS_LAB
ret z ; starter battle in oak's lab: don't black out
.notSony1Battle
ld b, SET_PAL_BATTLE_BLACK
call RunPaletteCommand
ld hl, PlayerBlackedOutText2
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr nz, .noLinkBattle
ld hl, LinkBattleLostText
.noLinkBattle
call PrintText
ld a, [wd732]
res 5, a
ld [wd732], a
call ClearScreen
scf
ret
This code slides in a trainer sprite, and prints a message but only if we are fighting SONY1. We want it to print a message if we are fighting any trainer! So let's change the check. We want something like:
; called when player is out of usable mons.
; prints approriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
HandlePlayerBlackOut:
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr z, .notSony1Battle
ld a, [wIsInBattle]
dec a ;is the battle a wild battle?
jr z, .notSony1Battle ;if so, don't print our message.
coord hl, 0, 0
;rest of method here...
We're getting there. When the player loses to a trainer the game will show the message "BLUE: Yeah! Am I great or what?" How could we change this so the game prints a unique message for every trainer? Let's go searching... How does the game print a unique message for every trainer beaten? Our search leads us to;
TrainerBattleVictory:
call EndLowHealthAlarm
ld b, MUSIC_DEFEATED_GYM_LEADER
ld a, [wGymLeaderNo]
and a
jr nz, .gymleader
ld b, MUSIC_DEFEATED_TRAINER
.gymleader
ld a, [wTrainerClass]
cp SONY3 ; final battle against rival
jr nz, .notrival
ld b, MUSIC_DEFEATED_GYM_LEADER
ld hl, wFlags_D733
set 1, [hl]
.notrival
ld a, [wLinkState]
cp LINK_STATE_BATTLING
ld a, b
call nz, PlayBattleVictoryMusic
ld hl, TrainerDefeatedText
call PrintText
ld a, [wLinkState]
cp LINK_STATE_BATTLING
ret z
call ScrollTrainerPicAfterBattle
ld c, 40
call DelayFrames
call PrintEndBattleText ;<<<<<<<<<<<<<<<<<<<<<<WE FOUND IT!
; win money
ld hl, MoneyForWinningText
call PrintText
ld de, wPlayerMoney + 2
ld hl, wAmountMoneyWon + 2
ld c, $3
predef_jump AddBCDPredef
So, 'PrintEndBattleText' prints out text once a battle is over. If we go back to 'HandlePlayerBlackOut:' and call 'PrintEndBattleText' we'll have different text for every trainer! Let's do it;
; called when player is out of usable mons.
; prints approriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
HandlePlayerBlackOut:
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr z, .notSony1Battle
ld a, [wIsInBattle]
dec a ;is the battle a wild battle?
jr z, .notSony1Battle ;if so, don't print our message.
coord hl, 0, 0
lb bc, 8, 21
call ClearScreenArea ;clear the top-right of the screen in case there's a pokemon there.
call ScrollTrainerPicAfterBattle
ld c, 40
call DelayFrames
;ld hl, Sony1WinText ;We don't need these two lines anymore.
;call PrintText ;feel free to delete!
call PrintEndBattleText
ld a, [wCurMap]
cp OAKS_LAB
ret z ; starter battle in oak's lab: don't black out
.notSony1Battle
ld b, SET_PAL_BATTLE_BLACK
call RunPaletteCommand
ld hl, PlayerBlackedOutText2
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr nz, .noLinkBattle
ld hl, LinkBattleLostText
.noLinkBattle
call PrintText
ld a, [wd732]
res 5, a
ld [wd732], a
call ClearScreen
scf
ret
Surprisingly, that's all you have to do to restore (most if not all) the Rival win quotes from that youtube video at the top! It all has to do with this little method. Yes, the game does actually load lose battle quotes for trainers by default! If we search for all instances of 'SaveEndBattleTextPointers' We can see that lose quotes ([wEndBattleLoseTextPointer]) are set in various scripts and in TalkToTrainer:: but they are usually set to the same thing as win quotes. In the case TalkToTrainer, they are loaded from trainer headers but there is actually a bug which prevents them from loading correctly. So, let's fix ReadTrainerHeaderInfo::
; reads specific information from trainer header (pointed to at wTrainerHeaderPtr)
; a: offset in header data
; 0 -> flag's bit (into wTrainerHeaderFlagBit)
; 2 -> flag's byte ptr (into hl)
; 4 -> before battle text (into hl)
; 6 -> after battle text (into hl)
; 8 -> win battle text (into hl)
; A -> lose battle text (into de)
ReadTrainerHeaderInfo::
push de
push af
ld d, $0
ld e, a
ld hl, wTrainerHeaderPtr
ld a, [hli]
ld l, [hl]
ld h, a
add hl, de
pop af
and a
jr nz, .nonZeroOffset
ld a, [hl]
ld [wTrainerHeaderFlagBit], a ; store flag's bit
jr .done
.nonZeroOffset
cp $2
jr z, .readPointer ; read flag's byte ptr
cp $4
jr z, .readPointer ; read before battle text
cp $6
jr z, .readPointer ; read after battle text
cp $8
jr z, .readPointer ; read end battle text
cp $a
jr nz, .done
pop de ;BUGFIX: we pushed de onto the stack at the top, so we have to pop it off the stack to keep things symmetrical.
ld a, [hli] ; read end battle text (2) but override the result afterwards (XXX why, bug?)
ld d, [hl]
ld e, a
ret ;BUGFIX: de contains the lose quote loaded from the trainer header. We are done here.
;jr .done ;BUGFIX: this replaces de (the value we just loaded) with the value on the top of the stack. That's not the behavior we want.
.readPointer
ld a, [hli]
ld h, [hl]
ld l, a
.done
pop de
ret
That's everything! All that's left is to edit all the scripts and trainer headers to have lose quotes instead of two win quotes. (due to limitations, the win and lose quote pointers have to be in the same place. But you can TX_FAR to the actual text that is located anywhere). I hope this tutorial wasn't too confusing. Next time, we'll look at how to get Mimic's menu working over link cable.
Offline
That's actually fairly easy to understand. Except for the TX_FARing part, though.
Offline
TX_FAR is a macro in pokered for the text command that looks up text in any bank. just search for it in the pokered codebase and you can see how it is used. But, here is an example:
ViridianGymBattleLoseText:
TX_FAR _ViridianGymBattleLoseText
db "@"
ViridianGymBattleWinText:
TX_FAR _ViridianGymBattleWinText
db "@"
for win / lose quotes, ViridianGymBattleLoseText and ViridianGymBattleWinText have to be in the same bank (and they are the pointers you reference in the trainer's header.) But _ViridianGymBattleLoseText and _ViridianGymBattleWinText can be anywhere they'll fit. The text engine can look them up (because you told it to with TX_FAR.)
Offline
Which of the four pointer spots from the top would the "lose" text go, in relation to the "challenge" text, the "win" text, and the "post-battle" text?
Offline
This line right here is a hint. It appears to be in the order before, after, win, lose. But, try it and see!
Offline
Thanks a ton, I used this to restore RIVAL's text in my hack!!
Offline
Pages: 1