Skip to content

shipinit (r)ba#5025

Merged
aMannus merged 10 commits intoHarbourMasters:developfrom
briaguya0:shipinit-rba
Mar 26, 2025
Merged

shipinit (r)ba#5025
aMannus merged 10 commits intoHarbourMasters:developfrom
briaguya0:shipinit-rba

Conversation

@briaguya0
Copy link
Contributor

@briaguya0 briaguya0 commented Feb 8, 2025

what this does:

what this doesn't do:

  • as much cleanup as i'd like, but it's one of those "have to stop somewhere" things

Build Artifacts

Comment on lines -1531 to -1532
UIWidgets::PaddedEnhancementCheckbox("Original RBA Values", CVAR_ENHANCEMENT("RestoreRBAValues"), true, false);
UIWidgets::Tooltip("Restores the original outcomes when performing Reverse Bottle Adventure.");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the biggest reason i'd like to see this land. having (R)BA "just work" is much better than having people check a box to restore the values

@briaguya0
Copy link
Contributor Author

i didn't mention the other reason i'd like to land this (which is the reason #4650 was made in the first place) - relying on out of bounds indexing of arrays is bad. even though byteswapping the inventory "works" - it's still relying on memory layout in a very fragile way.

Copy link
Contributor

@Pepe20129 Pepe20129 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ITEM_MASK_GERUDO, ITEM_MASK_TRUTH, ITEM_SONG_NOCTURNE & ITEM_SONG_PRELUDE do land in padding bytes, but in n64, they're still stored, meaning it's possible to write something to the bytes with RBA to then read them with BA.
I don't know if we should reproduce this or if it's too niche, but good to mention it anyways.

#include "variables.h"
}

void DoBA(u8 itemOnCRight) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemOnCRight is always gSaveContext.equips.buttonItems[3], could not pass it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in c009bc8

}
}

void DoRBA(uint8_t itemOnCRight, uint8_t itemToPutInBottle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as DoBA

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in c009bc8

@briaguya0
Copy link
Contributor Author

ITEM_MASK_GERUDO, ITEM_MASK_TRUTH, ITEM_SONG_NOCTURNE & ITEM_SONG_PRELUDE do land in padding bytes, but in n64, they're still stored, meaning it's possible to write something to the bytes with RBA to then read them with BA. I don't know if we should reproduce this or if it's too niche, but good to mention it anyways.

nocturne and prelude were mentioned in the original PR's description #4650

There is a slight deviation from vanilla behavior. ITEM_SONG_NOCTURNE and ITEM_SONG_PRELUDE read and write to padding bytes in the save struct. Theoretically, you could use BA to write a value to them, then read it with RBA. This would be a more complicated thing to implement since it'll add new fields in the save struct. I couldn't find any actual use of this behavior in speedruns or rando tricks, so I've elected to not fully implement it.

didn't see anything about ITEM_MASK_GERUDO or ITEM_MASK_TRUTH - @Rozelette did you look into those at all?

@Rozelette
Copy link
Contributor

It is my mistake, I forgot to mention ITEM_MASK_GERUDO or ITEM_MASK_TRUTH in my original PR. In DoBA/DoRBA, they are called out as also being in padding.

To support it, I'd recommend adding additional fields in the save struct. But I do think it's rather niche.

Also, thanks for taking the time to convert this to ShipInit!

@Rozelette
Copy link
Contributor

Is there any particular reason you converted the if/else chain, as well as the switch statement, to be early returns? In my experience, while early returns are appropriate for preconditions, they are not beneficial for the main function logic. If anything needs to be added at the end of the function, it is inconvenient if there are a bunch of early returns that then need to be changed. I feel like the early returns make the function more confusing. In addition, they aren't going to make the function any more efficient since any modern compiler will collapse the jumps anyways.

Sorry if the comment is unexpected, but since you've essentially taken over my PR, I felt it was appropriate.

@briaguya0
Copy link
Contributor Author

briaguya0 commented Feb 9, 2025

i updated the PR description after changing that, where i said

i switched them away from being giant if/else blocks into ifs with early returns. that at least makes it easier to tell from a glance that only one of the things in there should happen, and when it happens we're done.

re:

If anything needs to be added at the end of the function, it is inconvenient if there are a bunch of early returns that then need to be changed.

do you expect to add things to the end of the function? if so, why would it go in there instead of in a new function?

re:

I feel like the early returns make the function more confusing

i feel the opposite. when i first saw the giant if / else block i found myself scrolling to the bottom to try to figure out if something else was going to happen. by using early returns it's clear that what is in that if block is the only thing that happens in that scenario.

i'd like to get other opinions on this. if the general sense is the if / else chain is clearer i can revert that change

@Rozelette
Copy link
Contributor

Rozelette commented Feb 9, 2025

do you expect to add things to the end of the function?

I have no idea, but I shouldn't have to worry about it. Perhaps I am debugging an issue and want to add some logging at the end of the function. It would be much more convenient if the function didn't have a ton of unneeded early returns.

by using early returns it's clear that what is in that if block is the only thing that happens in that scenario

I disagree. At least with an if/else chain the intent is obvious that only one scenario could happen. With individual ifs, theoritically each condition could happen and you have to consider each one to see if there's a return in there to see if they're actually mutually exclusive.

I know this is the eternal early return debate but I am proselytizing that this is a situation where it is not appropriate.

@briaguya0
Copy link
Contributor Author

also @Rozelette

re:

you've essentially taken over my PR

i'm sorry for stepping on your toes like that. it started as just moving the existing stuff over to use shipinit, but then i "self reviewed" the PR and remembered my thoughts from when you first made the PR

my thoughts then were:

  • it doesn't seem easy to read/understand
  • there's giant combo of if/else and switch/case
  • it includes a bunch of bitwise operations without documenting them
  • there are giant functions just thrown straight into z_param

with this PR i started by addressing the "thrown straight into z_param" part, but then started on the "giant combo of if/else and switch/case" part

i'm happy to close this PR if you'd like to just pull the shipinit part (changes up through c009bc8) over into your PR

@Rozelette
Copy link
Contributor

i'm sorry for stepping on your toes like that

I don't have a problem with it at all! In fact I think the best course of action would be to merge this PR and close my original one, since this uses the modern ShipInit methodology. I just said that as a justification to throw in my 2c, since I don't usually review PRs and, to be honest, I felt a little bit of ownership. RBA was always going to be implementing a complicated table into code so it's bound to be a little messy.

@briaguya0
Copy link
Contributor Author

I don't have a problem with it at all! In fact I think the best course of action would be to merge this PR and close my original one, since this uses the modern ShipInit methodology

good deal! i think it sounds like we both want to get to a place where this is at least somewhat easy to read/understand. i'll revert the early return changes and see if i can come up with something that better aligns with your original intent

@briaguya0 briaguya0 marked this pull request as draft February 9, 2025 12:21
@briaguya0
Copy link
Contributor Author

@Rozelette i pushed a commit breaking up the logic into functions, separating the "which cases are we handling" and the "how do we handle those cases" logic

i think it makes it a lot easier to glance at DoBA and DoRBA and get a sense of what they're doing without being overwhelmed

i'd love to hear your thoughts!

@Rozelette
Copy link
Contributor

I think this is a much better way at handling the BA/RBA behaviors. It chunks it into digestible portions. And, it isolates the padding bytes, which is where we may want to improve our behavior in the future. I have no qualms. I apprentice your considerations!

@Rozelette
Copy link
Contributor

I looked at the logic and it seems to be correct. I know you mentioned that there is some bit magic and there indeed is, but it seems to be correct to me. At the very least, if we do get bug reports that it is incorrect, the logic is isolated so that it is easy to correct.

@briaguya0 briaguya0 marked this pull request as ready for review February 12, 2025 12:50
@aMannus aMannus added this to the 9.0.0 milestone Mar 26, 2025
@aMannus
Copy link
Contributor

aMannus commented Mar 26, 2025

Merge pending conflicts

@aMannus aMannus merged commit 9ff4940 into HarbourMasters:develop Mar 26, 2025
5 checks passed
@serprex serprex mentioned this pull request Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants