Skeetendo

’Cause all games were better on the GBC

You are not logged in.

  • Index
  • → Help/Question
  • → Make the Game Check Whether a Pokemon Can Learn a Certain Move

#1 2017-07-08 05:58:42

coraldev
Member
Registered: 2017-07-08
Post 1/12

Make the Game Check Whether a Pokemon Can Learn a Certain Move

I've been working on a romhack called "Pokemon Coral Version" for a while. I've made a bit of progress and will eventually make a thread dedicated to it, but for now I've hit a wall.

I'm trying to change the way HMs work and have the game check whether any of your party Pokemon CAN learn the move instead of whether or not they HAVE learned the move. I've been messing around with a custom routine to make the game ask you if you want to surf when you check the water and have a Pokemon in your party that can learn Surf. It's based on "CanLearnTMHMMove" in "engine/tmhm.asm", but it isn't working. I'm not great with ASM, but I know some basic stuff and can usually brute force my way through simple ASM modifications, but I'm not having any luck with this.

Anyone have any tips or ideas to help?

Offline

#2 2017-07-08 06:16:38

Rangi
Member
Registered: 2016-05-09
Post 753/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

event/overworld.asm has two places that check whether you can Surf: SurfFunction.TrySurf and TrySurfOW.

SurfFunction.TrySurf is called when you select Surf from the party menu, so you don't need to add your custom check to it.

That leaves TrySurfOW. You can see how it has a series of condition checks that jump to .quit if any of them fail. In particular, you want to change this one:

ld d, SURF
call CheckPartyMove
jr c, .quit

Basically replace CheckPartyMove with a new CheckPartyCanLearnMove routine. You can base it on the CheckPartyMove routine's code, and use the CanLearnTMHMMove routine from engine/tmhm.asm to check whether the Pokémon can learn Surf. Be sure to set CurPartyMon to the first one that can learn Surf, just like CheckpartyMove does, so it'll print "so-and-so used Surf!" correctly.


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#3 2017-07-08 06:49:48

coraldev
Member
Registered: 2017-07-08
Post 2/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

Rangi wrote:

event/overworld.asm has two places that check whether you can Surf: SurfFunction.TrySurf and TrySurfOW.

SurfFunction.TrySurf is called when you select Surf from the party menu, so you don't need to add your custom check to it.

That leaves TrySurfOW. You can see how it has a series of condition checks that jump to .quit if any of them fail. In particular, you want to change this one:

ld d, SURF
call CheckPartyMove
jr c, .quit

Basically replace CheckPartyMove with a new CheckPartyCanLearnMove routine. You can base it on the CheckPartyMove routine's code, and use the CanLearnTMHMMove routine from engine/tmhm.asm to check whether the Pokémon can learn Surf. Be sure to set CurPartyMon to the first one that can learn Surf, just like CheckpartyMove does, so it'll print "so-and-so used Surf!" correctly.

Ok. So I set up a custom routine called "CheckPartyCanLearnMove" and called for that instead of "CheckPartyMove". I then copied "CheckPartyMove" and changed the label to "CheckPartyCanLearnMove".

This is what I have:

CheckPartyCanLearnMove: ; c742
; Check if a monster in your party has move d.

    ld e, 0
    xor a
    ld [CurPartyMon], a
.loop
    ld c, e
    ld b, 0
    ld hl, PartySpecies
    add hl, bc
    ld a, [hl]
    and a
    jr z, .no
    cp a, -1
    jr z, .no
    cp a, EGG
    jr z, .next

    ld bc, PARTYMON_STRUCT_LENGTH
    ld hl, PartyMon1Moves
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES
.check
    ld a, [hli]
    cp d
    jr z, .yes
    dec b
    jr nz, .check

.next
    inc e
    jr .loop

.yes
    ld a, e
    ld [CurPartyMon], a ; which mon has the move
    xor a
    ret
.no
    scf
    ret

Now I you say I can use the "CanLearnTMHMMove" routine. Where do I run that check? The obvious place would be here:

ld hl, PartyMon1Moves

I tried this, but checking the water once offsets the entire world's graphics by 2 tiles, and checking it again prompts the Surf text regardless of who's in the party.

Offline

#4 2017-07-08 07:00:18

Rangi
Member
Registered: 2016-05-09
Post 754/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

Ok. So I set up a custom routine called "CheckPartyCanLearnMove" and called for that instead of "CheckPartyMove". I then copied "CheckPartyMove" and changed the label to "CheckPartyCanLearnMove".

Now I you say I can use the "CanLearnTMHMMove" routine. Where do I run that check? The obvious place would be here:

ld hl, PartyMon1Moves

I tried this, but checking the water once offsets the entire world's graphics by 2 tiles, and checking it again prompts the Surf text regardless of who's in the party.

You say that's "the obvious place", so it seems like you at least understand the gist of the routine, even if each individual line's purpose isn't clear. You're correct. This chunk checks if Pokémon e has move d:

ld bc, PARTYMON_STRUCT_LENGTH
    ld hl, PartyMon1Moves
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES
.check
    ld a, [hli]
    cp d
    jr z, .yes
    dec b
    jr nz, .check

You want it to check if Pokémon e can learn move d, assuming that d is a TM or HM move (since CanLearnTMHMMove relies on the learnable move sets in base data).

The problem is, you can't just call CanLearnTMHMMove and expect it to work. It takes its input in certain WRAM locations, not in registers d and e; and when it runs, it changes the values of the other registers, which you were depending on.

If you look at the source code of CanLearnTMHMMove, it takes as input a species in [CurPartySpecies] and a move in [wPutativeTMHMMove]. So you want to initialize those values before calling it. (Careful to initialize [CurPartySpecies] with the species, not the 0-to-5 index within the party that [CurPartyMon] holds.) You should also surround the call in push/pop pairs, i.e.:

push de
call CanLearnTMHMMove
pop de

(If you study how CheckPartyMove works, you can see that the value of de needs to be preserved, but bc and hl don't.)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#5 2017-07-08 07:10:57

Rangi
Member
Registered: 2016-05-09
Post 755/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

By the way, this should go in the Help/Question forum. (Can a mod move it?)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#6 2017-07-08 07:11:07

coraldev
Member
Registered: 2017-07-08
Post 3/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

First off, thank you so much for bearing with me. It's pretty clear that I don't completely understand what I'm doing yet.

Rangi wrote:

The problem is, you can't just call CanLearnTMHMMove and expect it to work. It takes its input in certain WRAM locations, not in registers d and e; and when it runs, it changes the values of the other registers, which you were depending on.

Right. Ok. That makes sense.

If you look at the source code of CanLearnTMHMMove, it takes as input a species in [CurPartySpecies] and a move in [wPutativeTMHMMove]. So you want to initialize those values before calling it. (Careful to initialize [CurPartySpecies] with the species, not the 0-to-5 index within the party that [CurPartyMon] holds.)

This is the part I'm struggling to understand. How would I initialize the values? Is there somewhere I can study a similar situation so I can get an idea of how it works?

Offline

#7 2017-07-08 07:15:19

Rangi
Member
Registered: 2016-05-09
Post 756/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

This is the part I'm struggling to understand. How would I initialize the values? Is there somewhere I can study a similar situation so I can get an idea of how it works?

You initialize a WRAM address like this:

ld [CurPartySpecies], a

If your problem is that you don't know where to get the proper values from to initialize them:

• CurPartySpecies needs to be the species. You already have the species in a; you're checking it against −1 and EGG. So that's simple.
• wPutativeTMHMMove needs to be the move. The move is in d.

Remember, you have to load to WRAM addresses from register a because it's the accumulator. So you'd do this:

ld a, d
ld [wPutativeTMHMMove], a

Last edited by Rangi (2017-07-08 07:16:50)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#8 2017-07-08 07:32:15

coraldev
Member
Registered: 2017-07-08
Post 4/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

So now I have this:

...
    ld bc, PARTYMON_STRUCT_LENGTH
    ld [CurPartySpecies], a
    ld a, d
    ld [wPutativeTMHMMove], a
    push de
    call CanLearnTMHMMove
    pop de
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES
...

Checking the water now changes all Pokemon in my party to the Pokemon before them in National Dex order.

Offline

#9 2017-07-08 07:39:06

Rangi
Member
Registered: 2016-05-09
Post 757/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

So now I have this:

...
    ld bc, PARTYMON_STRUCT_LENGTH
    ld [CurPartySpecies], a
    ld a, d
    ld [wPutativeTMHMMove], a
    push de
    call CanLearnTMHMMove
    pop de
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES
...

Checking the water now changes all Pokemon in my party to the Pokemon before them in National Dex order.

So it's this?

ld bc, PARTYMON_STRUCT_LENGTH
    ld [CurPartySpecies], a
    ld a, d
    ld [wPutativeTMHMMove], a
    push de
    call CanLearnTMHMMove
    pop de
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES
.check
    ld a, [hli]
    cp d
    jr z, .yes
    dec b
    jr nz, .check

Why did you keep PARTYMON_STRUCT_LENGTH and the whole .check block? Read the original chunk line by line and see why those lines are there. Annotated:

; Add PARTYMON_STRUCT_LENGTH * e to PartyMon1Moves
; (e is the current party mon, from 0 to 5)
; so it makes hl point to the e'th party mon's moves
    ld bc, PARTYMON_STRUCT_LENGTH
    ld hl, PartyMon1Moves
    ld a, e
    call AddNTimes
; Do the .check loop b times (NUM_MOVES times) (4 times)
    ld b, NUM_MOVES
.check
; If this move is equal to d (the desired TM/HM move), go to .yes
    ld a, [hli]
    cp d
    jr z, .yes
    dec b
    jr nz, .check

After you call CanLearnTMHMMove, you just need to check its return value in c. If it's 1, the Pokémon can learn the move.

Last edited by Rangi (2017-07-08 07:40:53)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#10 2017-07-08 08:12:58

coraldev
Member
Registered: 2017-07-08
Post 5/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

So if I understood you correctly, which I probably didn't, I don't need these 5 lines:

ld bc, PARTYMON_STRUCT_LENGTH
    ld hl, PartyMon1Moves
    ld a, e
    call AddNTimes
    ld b, NUM_MOVES

because the top 3 are looking to see if the e'th Pokemon in my party has learned the move, and the bottom two run the .check once for each move that the Pokemon has (which would be pointless).

And the only thing I need in .check is:

cp c
   jr nz, .yes

That is leaving me with this code:

CheckPartyCanLearnMove: ; c742
; Check if a monster in your party can learn move d.

    ld e, 0
    xor a
    ld [CurPartyMon], a
.loop
    ld c, e
    ld b, 0
    ld hl, PartySpecies
    add hl, bc
    ld a, [hl]
    and a
    jr z, .no
    cp a, -1
    jr z, .no
    cp a, EGG
    jr z, .next
    
    ld [CurPartySpecies], a
    ld a, d
    ld [wPutativeTMHMMove], a
    push de
    call CanLearnTMHMMove
    pop de
.check
    cp c
    jr nz, .yes

.next
    inc e
    jr .loop

.yes
    ld a, e
    ld [CurPartyMon], a ; which mon can learn the move
    xor a
    ret
.no
    scf
    ret

That isn't working. I'm trying my best to understand. I really am. I've been combing over the code line by line to try and understand where it's going wrong.

Offline

#11 2017-07-08 08:35:33

Rangi
Member
Registered: 2016-05-09
Post 758/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

That isn't working. I'm trying my best to understand. I really am. I've been combing over the code line by line to try and understand where it's going wrong.

"cp c" is the wrong instruction. You want to test whether c is nonzero, and if it is, jump to .yes. For that you would do:

ld a, c
and a
jr nz, .yes

What "cp c" does is it compares a with c. (Again, a is the accumulator register, so it's special, which is why it's the one that gets compared with things.) For example:

ld a, 5
ld c, 2 + 2
cp c                 ; compare a with c
jr z, .a_equals_c    ; z(ero) flag is set if a == c
jr c, .a_less_than_c ; c(arry) flag is set if a < c
jr .a_greater_than_c ; neither is set if a > c

Explanation of the correct code: "and X" sets a to the result of a & X, and sets the z(ero) flag if the result is 0. Thus "and a" calculates a & a, which will not change the value of a, but will set the z flag if a == 0.

In case you're not familiar with binary operations, here's an overview.

Last edited by Rangi (2017-07-08 08:36:41)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#12 2017-07-08 08:49:52

coraldev
Member
Registered: 2017-07-08
Post 6/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

Ok. So with all of these changes, surf now works only if I have at least 2 Pokemon in my party regardless of if they can learn Surf or not. If I do say yes, it says '[2nd Pokemon] USED SURF'. Also, the world distorts whenever I check the water.

CheckPartyCanLearnMove: ; c742
; Check if a monster in your party can learn move d.

    ld e, 0
    xor a
    ld [CurPartyMon], a
.loop
    ld c, e
    ld b, 0
    ld hl, PartySpecies
    add hl, bc
    ld a, [hl]
    and a
    jr z, .no
    cp a, -1
    jr z, .no
    cp a, EGG
    jr z, .next
    
    ld [CurPartySpecies], a
    ld a, d
    ld [wPutativeTMHMMove], a
    push de
    call CanLearnTMHMMove
    pop de
.check

    ld a, c
    and a
    jr nz, .yes

.next
    inc e
    jr .loop

.yes
    ld a, e
    ld [CurPartyMon], a ; which mon can learn the move
    xor a
    ret
.no
    scf
    ret

Offline

#13 2017-07-08 08:54:00

Rangi
Member
Registered: 2016-05-09
Post 759/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

By the way, it may help you to put comments over every few lines of code explaining what they're supposed to do. For one thing, it guides whoever's reading the code, whether it's somebody else or yourself a month later. It also explicitly says what you meant to do so you can notice if the code will actually do something else. Plus just the act of explaining what you want to do, in small but meaningful steps, can often make you realize the solution to problems.

Like "Check if a monster in your party can learn move d" is a good example—it concisely explains what the routine is supposed to do and what input it takes—but it's rather broad. On the other hand, a comment like "compare a with c" on the line "cp c" would be too limited. Look for logical "paragraphs" of code that you aren't yet able to take in all at once, study what they're doing, and summarize them in a top comment. Eventually you'll start having larger paragraphs because you get used to common patterns of code. Like this:

ld bc, PARTYMON_STRUCT_LENGTH
ld hl, PartyMon1Moves
ld a, e
call AddNTimes

You might want to add a comment like "hl = PartyMon1Moves + PARTYMON_STRUCT_LENGTH * e", or better yet "Set hl to the e'th party mon's moves". But later on you'll be familiar with the AddNTimes routine, and can just skim over those lines when reading the code, without needing the comment. Which means you'll be able to understand and summarize and write larger, more complicated routines.


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#14 2017-07-08 09:06:40

Rangi
Member
Registered: 2016-05-09
Post 760/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

Ok. So with all of these changes, surf now works only if I have at least 2 Pokemon in my party regardless of if they can learn Surf or not. If I do say yes, it says '[2nd Pokemon] USED SURF'. Also, the world distorts whenever I check the water.

Interesting. I haven't run any of this code, so I've missed something about it. I can help debug it later, but this might be an opportunity for you to learn how to use the BGB emulator's debugger.

GcsQL3o.png

You right-click on the emulator and go to "Other→Debugger" to open it.

Then go to the menu "Debug→breakpoints". You're debugging the CheckPartyCanLearnMove routine, so in the "PC=" box, paste "CheckPartyCanLearnMove" and click Add. (You can see that I've set a breakpoint to the CheckPartyMove routine.) (BGB knows that the label name "CheckPartyCanLearnMove" corresponds to a certain address in the ROM because of the .sym file generated by rgbds. Make sure that the sym file and the gbc file exist together, with the same name.)

A breakpoint is a point in the code that breaks execution. In other words, when you talk to the water and try to Surf, the game will pause as soon as the program counter reaches that routine. Then you can press F3 to step through the code line by line, or F9 to continue executing normally. While you're stepping through the lines, you can observe the register values in the top-right corner, and the WRAM values in the bottom-right. (Right-click the WRAM and click "Go to"; then you can paste a variable name from wram.asm and it'll jump to it.) Keep a mental model (or a pencil-and-paper model) of what you expect the code to be doing, and compare it with what you observe the code actually doing.

I'm really not sure what the bug is in this case. But it's most likely to occur in the new code, and the entire body of CanLearnTMHMMove is the largest block of new code you've introduced. So maybe try stubbing out the "call CanLearnTMHMMove"—replace it with a dummy return value, like "ld c, 1" or "ld c, 0". Then if it runs fine you know something in CanLearnTMHMMove is causing the bugs, and can work around it.


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#15 2017-07-08 09:14:32

coraldev
Member
Registered: 2017-07-08
Post 7/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

Ok. I'm about to sleep for the night, but I'll look into it more first thing when I wake up and report back. Again, thank you for bearing with me and trying to help. It is really appreciated.

Offline

#16 2017-07-08 09:15:49

Rangi
Member
Registered: 2016-05-09
Post 761/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

coraldev wrote:

Ok. So with all of these changes, surf now works only if I have at least 2 Pokemon in my party regardless of if they can learn Surf or not. If I do say yes, it says '[2nd Pokemon] USED SURF'. Also, the world distorts whenever I check the water.

On second thought, try using "farcall CanLearnTMHMMove" instead of "call". (You need farcall when the routine you're calling exists in a different ROM bank than the one you're in.)

Edit: Yeah, that was it.

Last edited by Rangi (2017-07-08 09:27:17)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

#17 2017-07-08 23:33:58

coraldev
Member
Registered: 2017-07-08
Post 8/12

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

Rangi wrote:

On second thought, try using "farcall CanLearnTMHMMove" instead of "call". (You need farcall when the routine you're calling exists in a different ROM bank than the one you're in.)

Edit: Yeah, that was it.

Thank you so much! It works perfectly now!

Offline

#18 2017-07-09 16:15:27

comet
Member
Registered: 2012-04-09
Post 679/679

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

Rangi wrote:

What "cp c" does is it compares a with c. (Again, a is the accumulator register, so it's special, which is why it's the one that gets compared with things.)

Better to say that `cp c` is just an alias for `cp a, c`.

Offline

#19 2017-07-09 18:06:37

Rangi
Member
Registered: 2016-05-09
Post 764/870

Re: Make the Game Check Whether a Pokemon Can Learn a Certain Move

comet wrote:
Rangi wrote:

What "cp c" does is it compares a with c. (Again, a is the accumulator register, so it's special, which is why it's the one that gets compared with things.)

Better to say that `cp c` is just an alias for `cp a, c`.

That's also valid, but then it looks like you could say "cp c, a" or "cp b, c" or so on. All of the cp opcodes have a as their first implicit argument.

(Here's a table with opcode descriptions and less clutter, but it's missing some of the GameBoy-specific opcodes.)

Last edited by Rangi (2017-07-09 18:08:55)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

  • Index
  • → Help/Question
  • → Make the Game Check Whether a Pokemon Can Learn a Certain Move

Board footer

Powered by FluxBB