Skeetendo

’Cause all games were better on the GBC

You are not logged in.

  • Index
  • → Help/Question
  • → [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

#1 2019-01-10 18:04:48

Kamy41
Member
Registered: 2019-01-09
Post 4/10

[Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Good evening to everyone :)
I'm new on this forum and I'd like to tell you how much I appreciate all your work on the disassembly of Pokemon Red, so, thank you :)
My quesion is this, I achieved to make an overworld encounter with Mew, close to the truck of VermilionDock (without using Strenght), but I'd like to make Mew showing up just after a certain event (beating Blue on route 22). I tried to emulate the script of the SilpScope that appears right after the fight with Giovanni, but as a result the sprite of Mew is still visible even before the event, and DOESN'T  DISAPPEAR anymore after I caught it.
Could you help me? Here's the script I tried to write (I'm a very noob in progamming, I know)
Thank you very much :)

VermilionDockScript:
    call VermilionDockSubscript1
    call EnableAutoTextBoxDrawing
    jr z, .MewScript1    
.MewScript1
    CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE
    ld a, $a
    ld a, HS_MEW
    ld [wMissableObjectIndex], a
    predef ShowObject
    jr z, .MewScript2
.MewScript2
    ld hl, MewTrainerHeader
    ld hl, MewTrainerHeader
    ld de, .ScriptPointers
    ld a, [wVermilionDockCurScript]
    call ExecuteCurMapScriptInTable
    ld [wVermilionDockCurScript], a
    ret
    
.ScriptPointers
    dw CheckFightingMapTrainers
    dw DisplayEnemyTrainerTextAndStartBattle
    dw EndTrainerBattle
    
VermilionDockTextPointers:
    dw MewText
    
MewTrainerHeader:
    dbEventFlagBit EVENT_BEAT_MEW
    db ($0 << 4) ; trainer's view range
    dwEventFlagAddress EVENT_BEAT_MEW
    dw MewBattleText ; TextBeforeBattle
    dw MewBattleText ; TextAfterBattle
    dw MewBattleText ; TextEndBattle
    dw MewBattleText ; TextEndBattle

        db $ff

MewText:
    TX_ASM
    ld hl, MewTrainerHeader
    call TalkToTrainer
    jp TextScriptEnd

MewBattleText:
    TX_FAR _MewBattleText
    TX_ASM
    ld a, MEW
    call PlayCry
    call WaitForSoundToFinish
    jp TextScriptEnd
    
VermilionDockSubscript1:
        call EnableAutoTextBoxDrawing
    CheckEventHL EVENT_STARTED_WALKING_OUT_OF_DOCK
    jr nz, .asm_1db8d
    CheckEventReuseHL EVENT_GOT_HM01
    ret z
    ld a, [wDestinationWarpID]
    cp $1 ......

Offline

#2 2019-01-11 05:21:14

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,125/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Hello Kamy, welcome.

Hide/show data is one of the more clunky things to work with in RBY.

Before I get into the specifics, I'll give some comments on your code.

These two lines:

...
    jr z, .MewScript1
.MewScript1
...

don't have an affect on the outcome of the code.
'jr' is a type of jump instruction. A jump instruction changes the flow of execution by jumping to the destination (the destination in this case is .MewScript1).
This means that after the 'jr' instruction executes, the next instruction to execute will be the instruction found at the destination (.MewScript1).
Since the destination already comes immediately after the jump instruction (in this case), it would have already been the next instruction to be executed even without the jump instruction. So by omitting these two lines, the end result behavior of the code is the same.

The 'z' that is part of that instruction changes the jump into a "conditional jump". The condition is that the jump will only be taken if the 'z' flag is set. The jump will NOT be taken if the 'z' flag is NOT set.
If the jump is not taken, the next instruction to be executed will be the instruction that follows the jump instruction, instead of the destination - the destination would be "ignored".

The 'z' flag is part of a special CPU register called the "flag" register, or the 'f' register.
The state of the 'z' flag (whether it is set or not) is updated based on the result of the previous arithmetic instruction that was executed.
Specifically, the 'z' flag is set if the previous arithmetic instruction was a calculation that resulted in 0, and the 'z' flag is unset if the previous arithmetic instruction was a calculation that resulted in anything non-zero. Some operations do not change the state of the 'z' flag (most of the non-arithmetic operations).

Next, lets look at these few lines:

.MewScript1
    CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE
    ld a, $a
    ld a, HS_MEW

First, the CheckEvent.
'CheckEvent' is a macro we made that will get replaced with some plain asm instructions when the source code is being assembled. This is purely to help make our jobs a little bit easier when reading the source code or making modifications.
But to be able to use the CheckEvent macro, we should be familiar with its definition, from macros/event_macros.asm.
I'm actually going to simplify the definition because we don't need to overload ourselves by looking at too much at a time:

;\1 = event index
CheckEvent: MACRO
event_byte = ((\1) / 8)
    ld a, [wEventFlags + event_byte]
    bit (\1) % 8, a
ENDM

In this simplified version, there are only 2 instructions.
The first instruction (the 'ld' instruction) loads the byte that contains the event flag for the requested event into register 'a' (the requested event is EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE in this case). How it calculates this is not so important right now, but you can study it if you want.
The important part is the last line of the macro body - the 'bit' instruction.
This instruction tests the correct bit (the bit corresponding to the requested event) of register 'a' and sets the 'z' flag if that bit is 0, and unsets the 'z' flag if that bit is 1.
So in other words, the 'z' flag is the output value of the CheckEvent macro.
What this means is if you use the CheckEvent macro, you should make use of the value in the 'z' flag with some conditional logic, by taking different actions based on the state of the 'z' flag.
In this case that means take different actions based on whether or not the event has occurred.
However, in your code you make no "branching logic" based on the 'z' flag after you use CheckEvent.

Next you have these two lines after you use CheckEvent:

ld a, $a
ld a, HS_MEW

The first load instruction loads the value $0A into register 'a'.
The second load instruction loads the value HS_MEW into register a - overwriting the current value ($0A).
This means that the first load instruction has no affect on the behavior of the code.
There is no use in loading a value into the same register twice in a row. The first value will always be overwritten.

I'll add my advice for getting this Mew battle to work in another reply.

Offline

#3 2019-01-11 06:17:50

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,126/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

So the number one issue you are noticing when testing is that Mew is visible even before the rival fight, and that Mew doesn't disappear after the Mew battle.

Hopefully based on my feedback so far, you can see that the issue is that you are calling "ShowObject" every single frame.

The routine "VermilionDockScript" is called every frame while in the Vermilion Dock, and your custom script loads HS_MEW into wMissableObjectIndex then calls ShowObject.
There are no conditional jumps in the top part of that routine that would allow for skipping the call to ShowObject.

So you need to call ShowObject only if "CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE" does NOT set the 'z' flag.

However, there is another issue that arises.
Say you have already completed the rival battle.
Then you walk into the Vermilion Dock.
The script executes which detects that you have completed the rival battle, so it shows Mew.
You battle Mew, the battle ends, which exits the battle screen and returns to the Vermilion Dock.
The script executes which detects that you have completed the rival battle, so it shows Mew.

So by only showing Mew based on the result of the CheckEvent, you can fix the problem of Mew showing up too early, but then Mew will still show up forever.

To fix it so that after the first Mew battle Mew will disappear forever, there are two main approaches:

1. Create another new event flag (EVENT_BATTLED_MEW for example).
Upon talking to Mew and starting the battle, set this event to true.
Update the Vermilion Dock script so that you only call ShowObject if the rival has been fought and Mew has NOT been fought.
This approach has the downside of having to create an extra event flag, and another downside that this event logic is more complicated and it is being tested every frame over and over which is a bit unfortunate.
However, this approach has the advantage that the Vermilion Dock script is the only map script that needs modification.

2. Instead of calling ShowObject in the Vermilion Dock script, call ShowObject for HS_MEW in the Route 22 script instead, after battling the rival for the second time.
This approach has the advantage of not needing any new event flags created, and it also has the advantage that there is one central piece of code (the rival code in the Route 22 script) that is responsible for showing Mew and this code doesn't run every frame - it only runs the one time you beat the rival for the second time.
However, this approach has the downside of requiring you to modify multiple map scripts just to add a custom event to one map. I don't think this is much of a downside though.

Offline

#4 2019-01-14 18:19:21

Kamy41
Member
Registered: 2019-01-09
Post 5/10

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Hello Danny :)
I tried as you told me to write a second script in the Route22 section but something still doesn't work (it's my fault obviously, programming is not assebling altogether portions of different scripts).
However, at the moment I can't continue with Pokered due to the job. Therefore, for now I'd like to thank you for your help :)

Ps. I even don't now what kind of programming language is this XD ?!! It's something called Z80? Is there any tutorial or something I can view to learn about it?
Thank you and good evening :)

Offline

#5 2019-01-16 00:18:10

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,127/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

It's an assembly language that is similar to Z80 assembly. It is commonly referred to as GBZ80, but that name is unofficial.
You can find many resources in the links in this thread: https://hax.iimarckus.org/topic/6675/

If your Route 22 script is not working right and you have the spare time, you can post your modified code here and we can take a look at it.

Offline

#6 2019-01-23 21:16:15

Kamy41
Member
Registered: 2019-01-09
Post 8/10

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Danny-E 33 wrote:

It's an assembly language that is similar to Z80 assembly. It is commonly referred to as GBZ80, but that name is unofficial.
You can find many resources in the links in this thread: https://hax.iimarckus.org/topic/6675/

If your Route 22 script is not working right and you have the spare time, you can post your modified code here and we can take a look at it.

Good Evening :) I did it !!! I tried with the first way you exposed to make mew trigger and then disappear and worked :) the ship sails and so everything seems ok. I'd preferred to follow the second method but I can't figure out why it doesn't work.
However, thank you so much :)

I'll post the script to let you see it

VermilionDockScript:
    call EnableAutoTextBoxDrawing
    call VermilionDockSubscript1
    ld a, HS_MEW
        ld [wMissableObjectIndex], a
        predef HideObject
    CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE
    jr z, VermilionDockSubscript1
    CheckEvent EVENT_BEATED_MEW
    jr z, MewShow
    ld hl, MewTrainerHeader
    ld de, .ScriptPointers
    ld a, [wVermilionDockCurScript]
    call ExecuteCurMapScriptInTable
    ld [wVermilionDockCurScript], a
    ret
    
.ScriptPointers
    dw CheckFightingMapTrainers
    dw DisplayEnemyTrainerTextAndStartBattle
    dw EndTrainerBattle
    
MewShow:
    ld a, HS_MEW
        ld [wMissableObjectIndex], a
        predef ShowObject
    
VermilionDockSubscript1:
           CheckEventHL EVENT_STARTED_WALKING_OUT_OF_DOCK
    jr nz, .asm_1db8d
    CheckEventReuseHL EVENT_GOT_HM01
    ret z
    ld a, [wDestinationWarpID]
    cp $1
    ret nz
    CheckEventReuseHL EVENT_SS_ANNE_LEFT
    jp z, VermilionDock_1db9b
    SetEventReuseHL EVENT_STARTED_WALKING_OUT_OF_DOCK
    call Delay3
    ld hl, wd730
    set 7, [hl]
    ld hl, wSimulatedJoypadStatesEnd
    ld a, D_UP
    ld [hli], a
    ld [hli], a
    ld [hl], a
    ld a, $3
    ld [wSimulatedJoypadStatesIndex], a
    xor a
    ld [wSpriteStateData2 + $06], a
    ld [wOverrideSimulatedJoypadStatesMask], a
    dec a
    ld [wJoyIgnore], a
    ret

............................

    ld a, $d ; water block
    ld [hli], a
    ld [hli], a
    ld [hli], a
    ld [hl], a

    ld a, SFX_SS_ANNE_HORN
    call PlaySound
    ld c, 120
    call DelayFrames
    ret
    
VermilionDockTextPointers:
    dw MewText
    
MewTrainerHeader:
        dbEventFlagBit EVENT_BEAT_MEW
    db ($0 << 4) ; trainer's view range
    dwEventFlagAddress EVENT_BEAT_MEW
    dw MewBattleText ; TextBeforeBattle
    dw MewBattleText ; TextAfterBattle
    dw MewBattleText ; TextEndBattle
    dw MewBattleText ; TextEndBattle

        db $ff

MewText:
    TX_ASM
    SetEvent EVENT_BEATED_MEW
    ld hl, MewTrainerHeader
    call TalkToTrainer
    jp TextScriptEnd

MewBattleText:
    TX_FAR _MewBattleText
    TX_ASM
    SetEvent EVENT_BEATED_MEW
    ld a, MEW
    call PlayCry
    call WaitForSoundToFinish
    jp TextScriptEnd

Offline

#7 2019-01-24 05:53:47

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,128/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Very nice, congratulations.

Your code looks very good.
Your use of "CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE" and "CheckEvent EVENT_BEATED_MEW" to conditionally show Mew is exactly right.

Here's some feedback on your code.

These three lines...

VermilionDockScript:
    call EnableAutoTextBoxDrawing
    call VermilionDockSubscript1
    ld a, HS_MEW                   ; <---
    ld [wMissableObjectIndex], a   ; <--- these three lines
    predef HideObject              ; <---

are unnecessary.

The rest of your code already correctly shows Mew whenever it needs to be shown, and battling Mew will automatically hide it.
It is redundant work to hide Mew at the start of every frame.



Also, the original script (which you renamed to VermilionDockSubscript1, which is good) is now executed multiple times per frame in some situations.
Based on this code:

VermilionDockScript:
    call EnableAutoTextBoxDrawing
    call VermilionDockSubscript1    ; <--- you call it once here (which is good)
    ld a, HS_MEW
    ld [wMissableObjectIndex], a
    predef HideObject
    CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE
    jr z, VermilionDockSubscript1   ; <--- you conditionally jump to it a second time (unnecessary, and could possibly have consequences)
    CheckEvent EVENT_BEATED_MEW
    jr z, MewShow                   ; <--- jump to MewShow, which will "fall through" to VermilionDockSubscript1, therefore executing it a second time

Instead, it should execute VermilionDockSubscript1 exactly once per frame.
To do this, replace "jr z, VermilionDockSubscript1" with "jr z, .dontShowMew" and create a new label called ".dontShowMew" after the jump to MewShow.
Like this:

VermilionDockScript:
    call EnableAutoTextBoxDrawing
    call VermilionDockSubscript1
    ld a, HS_MEW
    ld [wMissableObjectIndex], a
    predef HideObject
    CheckEvent EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE
    jr z, .dontShowMew
    CheckEvent EVENT_BEATED_MEW
    jr z, MewShow
.dontShowMew
    ld hl, MewTrainerHeader

Or you can call the label whatever you want.
But it guarantees that if the player has not beat the Route 22 2nd rival battle, that only showing Mew is skipped, but the script executes normally otherwise.

And add a "ret" instruction to the end of MewShow so that the MewShow routine does not "fall through" to the VermilionDockSubscript1 routine:

MewShow:
    ld a, HS_MEW
    ld [wMissableObjectIndex], a
    predef ShowObject
    ret               ; <---


Lastly, let's look at the end of your custom script:

...
    jr z, MewShow
    ld hl, MewTrainerHeader
    ld de, .ScriptPointers
    ld a, [wVermilionDockCurScript]
    call ExecuteCurMapScriptInTable
    ld [wVermilionDockCurScript], a
    ret

If the jump to MewShow is taken, the rest of this script is not executed.
The last part of this script is for handling engaging in battles, so this should be executed every frame.
I'm actually a little surprised everything is working for you in your testing, since ExecuteCurMapScriptInTable is never called if Mew is being shown. Maybe it still worked because Mew is not a traditional trainer. (Mew doesn't engage the player. The player must talk to Mew.)

Regardless, you should make sure this code executes every frame for consistency.
To do this, use "call z" instead of "jr z" when executing MewShow:

...
    call z, MewShow
    ld hl, MewTrainerHeader
    ld de, .ScriptPointers
    ld a, [wVermilionDockCurScript]
    call ExecuteCurMapScriptInTable
    ld [wVermilionDockCurScript], a
    ret

This guarantees that after MewShow is finished, execution returns to this point and the rest of the script is executed.



Overall, nice work, and very cool that you got this working.

Offline

#8 2019-01-24 10:51:37

Kamy41
Member
Registered: 2019-01-09
Post 9/10

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Good morning Danny :)
Thanks for your feedback, I'd like to know just two other things (and then I'll quit for a while with Pokered)
The first three lines you said are unnecessary (those that HIDE mew at the beginning of the script) are actually NECESSARY becouse without them Mew is still visible even if in Hide_Show_Data it's set to Hide as predef, so I think that this function doesn't work properly.

Do you think I did something wrong in here? maybe ordering the function at the end and the map ID in the right place?

dw MapHS58
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHS5E   ; <---- Map ID of Vermiliondock
    dw MapHSXX
    dw MapHS60
    dw MapHSXX
    dw MapHSXX

........

MapHS5E:
        db VERMILION_DOCK,$01,Hide
    
    db $FF,$01,Show

Last question, it's possible to trigger the mew event even through a CONSTANT? for example HS_VIRIDIAN_GYM_GIOVANNI (once giovanni disappears, mew appears)? If yes, how can I do?

Thank you very much :)

Last edited by Kamy41 (2019-01-24 10:53:00)

Offline

#9 2019-01-24 23:43:30

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,129/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Kamy41 wrote:

Good morning Danny :)
Thanks for your feedback, I'd like to know just two other things (and then I'll quit for a while with Pokered)
The first three lines you said are unnecessary (those that HIDE mew at the beginning of the script) are actually NECESSARY becouse without them Mew is still visible even if in Hide_Show_Data it's set to Hide as predef, so I think that this function doesn't work properly.

Do you think I did something wrong in here? maybe ordering the function at the end and the map ID in the right place?

dw MapHS58
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHSXX
    dw MapHS5E   ; <---- Map ID of Vermiliondock
    dw MapHSXX
    dw MapHS60
    dw MapHSXX
    dw MapHSXX

........

MapHS5E:
        db VERMILION_DOCK,$01,Hide
    
    db $FF,$01,Show

I think this is because of save file corruption. I think your changes to data/hide_show_data.asm are all correct.
This is why I said working with hide/show data is clunky.
If you make changes to the structure of the data in data/hide_show_data.asm you almost always corrupt any existing save files.

Basically, the new code expects a certain value to be in the save file for each hide/show object in the game.
But if the save file was created with older code, then the save file and the data in the rom (the data that comes from data/hide_show_data.asm) are de-synchronized.
The end result is hide/show objects simply behave unpredictably.
The only easy fix is to use a fresh save file every time you modify data/hide_show_data.asm.

Kamy41 wrote:

Last question, it's possible to trigger the mew event even through a CONSTANT? for example HS_VIRIDIAN_GYM_GIOVANNI (once giovanni disappears, mew appears)? If yes, how can I do?

Instead of showing Mew based on the state of HS_VIRIDIAN_GYM_GIOVANNI, simply show Mew in the same code that changes the value of HS_VIRIDIAN_GYM_GIOVANNI.

In other words, this code...

ViridianGymText1:

    [...]

    ld a, HS_VIRIDIAN_GYM_GIOVANNI
    ld [wMissableObjectIndex], a
    predef HideObject

...is responsible for hiding Giovanni.

Simply show Mew at the same time:

ViridianGymText1:

    [...]

    ld a, HS_VIRIDIAN_GYM_GIOVANNI
    ld [wMissableObjectIndex], a
    predef HideObject

    ld a, HS_MEW
    ld [wMissableObjectIndex], a
    predef ShowObject

Offline

#10 2019-01-25 17:03:34

Kamy41
Member
Registered: 2019-01-09
Post 10/10

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Yes, I've found that the savefile was corrupted, now everything makes sense...
Thank you Danny :)

Offline

#11 2019-01-25 19:51:05

Danny-E 33
Administrator
Registered: 2012-06-09
Post 1,130/1,134

Re: [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

You're welcome!

One last piece of feedback. I noticed in your git repository on GitHub that you make many commits per day, with each commit changing a few lines of code.

Making a commit is not necessary for building and testing.

You can change code, build, test, change code, build, test, over and over.

Then, at the end, when you are happy with the result you can make a commit and then push that one commit to GitHub.

Offline

  • Index
  • → Help/Question
  • → [Pokered] Showing pokemon in Overworld through ''CHECKEVENT''

Board footer

Powered by FluxBB