vg-json-data / sm-json-data

JSON representations of Super Metroid Game Data
Other
23 stars 11 forks source link

Refine Moat shinespark strats and item collection #1559

Closed blkerby closed 1 week ago

blkerby commented 2 weeks ago

This PR was prompted by insomniaspeed mentioning a strat where you come in from the right of Moat with a shinecharge, jump across, grab the item, and spark back to the right: https://discord.com/channels/1053421401354285129/1053436236628496454/1247939732776812565. It turns out to be fairly complicated in that there are a lot of possible combinations of whether you carry the shinecharge into the room vs. gain it while running in, and whether you spark back to the door vs. spark through the door (left or right door). Also you can enter from either the left or right side of the room; the left-side entry is less significant but could still be useful as a way to grab the item on the way while sparking right, rather than having to make another trip back into the left room (which might be heated) after grabbing the item.

In general, the room wasn't accurately modeling which strats are required to grab the item. There can be scenarios where you could want to skip picking up an ETank, in order to save the refill for a later time. It's not applicable to Map Rando logic but still seems worth representing accurately. We can do this now that we have the "collectsItems" strat property. There would still be more strats that could be added for skipping past the item; I'm not trying to be complete with them in this PR, just trying to make sure the existing strats are accurate and that the most basic ones are included.

The other thing added here is a new "shineChargeFrames" logical requirement. For a while now, Map Rando has been treating shinecharge frames as a resource type like energy, missiles, etc., so the capability has been there to support something like this. It allows us to represent shinecharges that span multiple strats, a generalization of what we were already able to do with cross-room strats. Strats that use this should also use the new boolean strat properties "startsWithShineCharge" / "endsWithShineCharge" also added in this PR, as a way to mark whether the strat is expected to start with an active shinecharge and/or end with one. In this room, the "shineChargeFrames" helps us avoid a blowup in complexity in terms of amount of obstacles and/or nodes required to represent things accurately. It should be useful in many other situations too.

Technically, the required shinecharge frames for a through-door spark from the item pedestal probably could be a little less if you are going through to the opposite door (as the camera scroll is more favorable) vs. doubling back through the door you came in. This could be represented using one more obstacle, but I didn't think it was necessarily worth the extra complexity to try to model that. Shinecharge frames can already be minimized even further by initiating the spark with a hero shot before collecting the item, just with a slightly higher shinespark energy cost; so that seems to make it not matter very much.

blkerby commented 2 weeks ago

In case it wasn't already clear, the idea is that in general visiting an item node means you have the option to pick up the item (assuming its "unlock" requirement is satisfied, where applicable) but are not required to do so. We use "collectsItems" here as a way to encode that a strat must unavoidably collect an item.

osse101 commented 1 week ago

How are startsWithShineCharge and endsWithShineCharge useful? With shineChargeFrames as a resource that can be tracked across strats, you either have frames to spend or you don't. Is it a randomizer thing to know how to handle strats or maybe a way to prevent logic errors as something we'd want to test for?

Another place this sequences comes to mind is in screw attack room, picking up the item and shinesparking out. Everest crab climb has the same concept of a continuation of a strat, but does not involve shinesparks.

They also look similar to a more generalized version of entrance/exit conditions - stratStartCondition and stratEndCondition, but I don't think that's a beneficial direction to go, based off of just these. I see adding strat properties as adding clutter, but it really doesn't harm anything to do that.

blkerby commented 1 week ago

How are startsWithShineCharge and endsWithShineCharge useful? With shineChargeFrames as a resource that can be tracked across strats, you either have frames to spend or you don't. Is it a randomizer thing to know how to handle strats or maybe a way to prevent logic errors as something we'd want to test for?

Yeah the purpose of endsWithShineCharge is to mark strats where the ending shinecharge frames are valid. In theory if every strat had timing data, i.e. through a hypothetical frames requirement, then you wouldn't need it. But since we don't have that timing data, we need to be able to restrict the ways that shinecharge frames are logically allowed to carry over from one strat to another. Otherwise the randomizer would think that you could do a strat that has a canShineCharge requirement in it to gain 180 shinecharge frames, then do arbitrarily more strats afterward that don't mention anything about shinecharge frames, and you would still have those 180 frames available at the end. The startsWithShineCharge plays the same role but for reverse graph traversals.

Another place this sequences comes to mind is in screw attack room, picking up the item and shinesparking out. Everest crab climb has the same concept of a continuation of a strat, but does not involve shinesparks.

Yeah, Grapple Beam Room is another example too. I can work on those other rooms afterward, just wanted to keep the initial PR focused on Moat to make sure we're satisfied with the proposed schema before applying it in more places.

They also look similar to a more generalized version of entrance/exit conditions - stratStartCondition and stratEndCondition, but I don't think that's a beneficial direction to go, based off of just these. I see adding strat properties as adding clutter, but it really doesn't harm anything to do that.

The thought also crossed my mind that these properties could be generalized or combined with exit/entrance conditions, but it's not yet clear to me what that would look like. For the most part, exit/entrance condition do apply specifically to door transitions, so in general I'm not sure if would make sense to allow them to apply more broadly to in-room strats. Maybe the picture changes later as things develop more. I agree it feels a bit cluttered but I'm not sure where else to put these properties.

osse101 commented 1 week ago

You could update remaining shinespark frames on every strat to 0 if there are not used in the strat, or the new number if they are being used. But I don't like that since it adds work to unrelated strats and will have special cases.

I'm thinking that starts/endsWithShineCharge are daisy chained together and could instead be a general startStratSequence + endStratSequence or whatever to call it. {stratSequence: "A"} where links do the connections and they share a sequence ID, except you still need to know start/stop. s/eWithShineCharge could work cross-room, but start/exit conditions are in place for that. I dunno that calling them ShineCharge is important information. Right now it doesn't impact enough strats to overthink it 🧠

blkerby commented 1 week ago

Looks good to me. I wonder if it's easier to come in at 2, open the door at 2, go to 3, spark out 2. Maybe it's not easier than the hero shot? I'm not seeing a good way to model it either.

Opening the door before jumping across is possible and does seem a little easier than the hero shot spark; not sure if it's worth adding an extra obstacle to model though? For the case where you enter shinecharged (where you weren't already going to need to turn back to the door), doing it that way seems to need to use more shinecharge frames, at least if you're leaving in top position since you'll need to jump for that even if not doing a hero shot. Leaving in bottom position it looks like maybe it's a wash

blkerby commented 1 week ago

You could update remaining shinespark frames on every strat to 0 if there are not used in the strat, or the new number if they are being used. But I don't like that since it adds work to unrelated strats and will have special cases.

I'm thinking that starts/endsWithShineCharge are daisy chained together and could instead be a general startStratSequence + endStratSequence or whatever to call it. {stratSequence: "A"} where links do the connections and they share a sequence ID, except you still need to know start/stop. s/eWithShineCharge could work cross-room, but start/exit conditions are in place for that. I dunno that calling them ShineCharge is important information. Right now it doesn't impact enough strats to overthink it 🧠

Yeah, I'm having a hard time thinking of an example of what else we would use this for aside from keeping shinecharge frames across strats; I imagine there must be other things but they aren't coming to mind yet. When having strats in a sequence, the randomizer is still going to need to know when shinecharge frames are valid to carry over across strats; if there turn out to be other purposes for sequencing strats, then it might be unsound to assume that shinecharge frames always carry over. I think it could make sense to be explicit about exactly what kind of state is carried over across strats, similar to what we do with exit/entrance conditions. If we add more types of state like that, where it's not only about shinecharge frames anymore, then maybe we could also put them in a container property to organize them like we did with exit/entrance conditions.

I guess the main alternative would be to redefine exit/entrance conditions into something that is more general and can apply both across rooms and within rooms. It could be renamed to "endCondition/startCondition" instead of exitCondition/entranceCondition. It does have a certain appeal to it, and maybe it would be cleaner and more powerful. But that's a bigger change and I think I'd rather postpone that until we have a stronger reason for it and a clearer idea of how it would be used. It seems like "starts/endsWithShineCharge" could cover what we need for now?

osse101 commented 1 week ago

Yes, I think starts/endsWithShineCharge does what we want from it for now.

blkerby commented 1 week ago
  • moat through door CWJ needs speedbooster:false

Done.

* Does shineSparkLeniencyFrames affect `shineChargeFrames`, mostly in these start/endWithShinecharge cases?  It looks like the strats account for the difficulty of movements already but there is always going to be a difference in movement optimization.

The way the randomizer is working, the leniency frames get subtracted from the 180 at the beginning when the frames are first initialized by a canShinecharge. So they would be taken into account everywhere.