Jan 10, 2024

Opening Up Breakable Walls With Subweapons In CV3

CV3 has very strict rules for breakable walls. One of those strict rules is that only Trevor's whip, Sypha's staff, Grant's dagger, and Alucard's fireballs can break walls. But what if you want to make walls breakable by other means? Well, I'm here to help you with that. It is a very simple feat that you can accomplish by changing just one byte in the ROM!

 

 Removing The Whip Limitation

Breakable walls have a finite state machine at their core. While in state 0, they will check for collisions with weapons. If the PC is on the last frame of a normal attack (when the weapon's damage variable is set), then the wall will check for collisions with the primary weapon only. The only time subweapons will be checked is if the PC is on any other frame of the attack or not attacking at all. 

You may be wondering why the program even checks for subweapon collisions against breakable walls if only primary weapons affect the walls. Grant's dagger and Alucard's fireballs do not occupy the primary weapon's data structure, so they are for all intents and purposes counted as subweapons, even though they do not require holding UP on the D-pad.

Resolving this minor issue is fairly simple. The entire instruction can be found at $00B149 in the ROM for CV3j or $00B18D for CV3u.

AD 30 06 D0 03 4C 3E B2 4C 8F B1 CV3u
AD 30 06 D0 03 4C F8 B1 4C 4B B1 CV3j
    |      |       |        |
if damage  |       |        |
          == 0     |        |
           check subweapons |
                     else check weapon

Instruction AD tells the program to check a variable, in this case the damage variable $0630. Instruction D0 then tells the program to jump 3 bytes if the damage variable is anything other than 0, which would be the case when the PC is on the last frame of a normal attack. Instruction 4C tells it to jump to another subroutine, then exit all subroutines after it runs. Address $B1F8 is the subroutine for subweapon checks, while $B14B is the primary weapon check. Both subroutines exit upon completion. 

If we wanted to check for both weapon and subweapon collisions at the same time, we would need to change 4C4BB1 to 204BB1. Instruction 20 tells the program to jump to another subroutine, then resume this current subroutine after running the other. Unfortunately, if we leave it at that, it would cause an error, because the next line of code is unrelated to this. We would need to also move our new instruction back 3 bytes.

Now that D0 instruction is going to swap the behaviors of the weapon and subweapon collisions. We don't want that. Instead, we will want to change it to instruction F0, which will instead check if the damage variable is #00. In other words, only check the primary weapon if it is on the last frame of animation. The code will be slightly different in CV3u due to different addresses, but the instruction changes are the same. So now our final line of code should be

AD 30 06 F0 03 20 8F B1 4C 3E B2 CV3u
AD 30 06 F0 03 20 4B B1 4C F8 B1 CV3j
    |      |       |        |
if damage  |       |        |
          >0       |        |
              check weapon  |
                     then check subweapons

Of course, all of this is optional and unlikely to actually affect gameplay significantly. If players are throwing subweapons while attacking with the primary weapon, they will probably be too focused on other things to notice the subweapons not breaking walls all the time.

 

Enabling Other Subweapons

If we know that Grant's dagger and Alucard's fireball are treated as subweapons, and that they are the only two subweapons allowed to break walls, there obviously is code checking what type of subweapon hit the breakable wall. Fortunately for us, that conditional is just complex enough that it offers up quite a bit of flexibility, but is simple enough that we can utilize it even with just 1 byte change. You can find the conditional in the ROM at address $00B214 in CV3j. 

BD 4E 05 C9 0A F0 13 C9 08 F0 0F
    |      |     |     |     |
switch object    |     |     |
      case Fire: |     |     |
       check collision |     |
                case Dagger: |
                       check collision

This time the code is essentially a switch statement called within a with() loop. Let's suppose you are running off CV3u and didn't need Grant's dagger. Perhaps you want to make the Holy Water break walls instead. We only need to alter one of the cases. In this case, we could just change 08 to 04, which is the ID of the Holy Water subweapon (not the drop). This would retain Alucard's fireballs. 

What if you instead wanted to allow all subweapons to damage breakable walls? You would need to turn this into an if-else chain, and fortunately you only need to change one or two bytes here to make that happen. If you change either F0 into D0, you will enable all subweapons except for whichever subweapon was specified. If you alter the first one, you prevent Alucard's fireball from breaking walls. If you alter the second one, you prevent Grant's dagger from breaking walls. If you change both to D0, you just succeeded in enabling all subweapons to break walls. If you wanted all subweapons except, say, the cross, you would change 0A F0 to 02 D0, which would essentially make the code "if Subweapon[X].Object != obj_Cross_Weapon then check_collisions()". You wouldn't need to make any further changes, because the check against Grant's dagger becomes redundant.


Important: Disclaimer

If ou use reVamp to edit the ROM and make your own levels, it is very difficult to add breakable walls to levels. The editor was designed to pretty much only allow you to edit pre-existing terrain objects. If a room didn't have any terrain objects, you were out of luck. If you hack the ROM yourself, you can of course easily add breakable walls into rooms that do not have any terrain objects. However, the game itself has a hard limit on how many breakable walls you can have and where you can place them.

As reVamp's little disclaimer tells you, there is a hard limit of two breakable walls per room. It erroneously states the walls must be at least one screen apart, but the first breakable block you encounter in the game disproves that. I don't know where the developer even got that idea.

What reVamp does not tell you is that there appears to be a hard limit of four walls per block. I say "appears" because two bytes of zero page RAM I have not yet identified but act as "destroy flags" would fall within this permissible range. Unfortunately, using reVamp, you cannot reach this limit. Each block has an ID assigned to it. The game does not assign that ID automatically, so you must define it yourself. The breakable wall editor in reVamp does not give you that ability. If you go into the actual ROM and give a breakable block an ID greater than #03, the wall may not spawn when entering the room. An ID of #05 should make it spawn all the time, since it would point to gamepad 2.

No comments:

Post a Comment

©TheouAegis Productions™. Powered by Blogger.