A Celeste mod for controlling gravity in realtime (yes, actually real GravityHelper).
Madeline's Speed
vector always represents the direction to the floor, regardless of gravity.
This means that while inverted, her Speed.Y
will be the opposite sign of her actual velocity as displayed in CelesteTAS.
It was done this way to ensure that any tech will work on the ceiling since the game thinks it's the floor, and most third-party mods "just work".
The majority of hooks in GravityHelper (over 100!) are for entity interaction compatibility, or to ensure that collisions are done in the correct direction and from the correct corner of the hitbox.
Implementing floor and corner correction were tricky, since it needs to check collisions in the opposite direction to movement and nudge Madeline in the opposite direction she would normally go.
The biggest hurdle by far was implementing dash correction on upside-down jumpthrus (both MaxHelpingHand's and GravityHelper's own). It's possible that there are still slight inconsistencies with it, and I welcome any TASers to submit issues to GitHub.
Any Entity
that supports inverted gravity will have a tracked GravityComponent
that indicates how GravityHelper should
perform the flip. The default procedure is:
- The
Entity
's position is moved to the opposite side of the current hitbox (this means it could be stuck in the ceiling or floor). - The hitbox is moved such that it has the same absolute coordinates as it did prior to the flip (hopefully unsticking it).
Sprite
s are moved and flipped to correspond to the new position and direction.- If the entity is an
Actor
, the Y component of theSpeed
property (if it exists) is inverted so that momentum is preserved.
A GravityComponent
can be added to any entity to allow it to flip visually on demand, but movement will only be affected
for Actor
s that provide an UpdateSpeed
action.
Grabbing a Holdable
while inverted will also invert that entity, even if you then release it.
Gravity for that entity will reset after a short period of time (default is 2 seconds), but is configurable with a
BehaviorGravityController
.
Adding a GravityListener
component to an entity will trigger an action when gravity changes for Madeline or another entity.
This can be filtered by type using GravityListener(typeof(SomeEntity))
or for a specific entity with GravityListener(someEntity)
.
There is a convenience PlayerGravityListener
class that will specifically listen for changes to Madeline's gravity.
There is also a TriggerComponent
component that acts in a similar way to Trigger
entities but adds support for collisions
with any entity. A GravityTriggerComponent
is provided that will set the gravity on a supported entity.
This is currently only used by GravityTrigger
and GravityField
.
To add support for GravityHelper, add a GravityComponent
to your entity that implements the following actions.
Note that these will be executed in the order listed.
UpdatePosition
: Update the position of the entity. By default this will move the entity to the opposite side of the currentCollider
.UpdateColliders
: Update the position and size of any colliders in your entity. By default this will move the currentCollider
such thatTop = -Bottom
, restoring the absolute bounds that were affected byUpdatePosition
. This also applies toHoldable.PickupCollider
.UpdateSpeed
: Required forActor
s that have a speed affected by gravity. Update theSpeed
property (or similar) to invert theY
component and multiply byMomentumMultiplier
. Actors do not have a common interface for this property, so it needs to be done manually.UpdateVisuals
: Update the position and scaling of any sprites etc. in your entity. GravityHelper cannot safely guess this.
Player
(of course).TheoCrystal
Glider
(jellies)
- "Dash trail all the time" is correctly supported while inverted.
FancyFallingBlock
CustomSpring
- It just works, no changes required.
- Crowns are rendered in the correct place and upside-down.
UpsideDownJumpThru
Portal
- Gravity is correctly maintained between save states.
- It just works, no changes required.
- Added hooks to correctly invert the tail. Also fixes the missing tail trail.
All entities provided follow a common theme of blue = normal, red = inverted, purple = toggle.
An invisible trigger that will change a supported Actor
's gravity when it enters.
Can be configured to support Madeline and/or any holdable/non-holdable Actor
.
An invisible trigger that will set Madeline's initial gravity if placed over a player spawn entity. Will optionally apply this gravity setting when returning from a berry/cassette bubble, since return bubbles will always set regular gravity when triggered. This was the safest solution given that return bubbles do not work well while inverted. When placing a spawn on the ceiling, nudge the player spawn entity 5 pixels upward to ensure Madeline is standing and doesn't fall upward.
A BadelineBoost
(Badeline orb) that supports going downward instead of upward, and can optionally change
Madeline's gravity on touch. Gravity types can be defined per node.
A Booster
(bubble) that will change Madeline's gravity when she is released.
A Bumper
that will change Madeline's gravity upon touch.
A controller entity that manages:
- How long
Holdable
s stay inverted before resetting - The gravity change cooldown on
GravitySpring
s - The gravity change cooldown on
GravitySwitch
es - Whether
Holdable
s will triggerGravitySwitch
es
A controller entity that manages:
- The default sounds for gravity changes, if an entity supports it
- Defining a music param to change upon gravity flip
A controller entity that manages:
- Opacity of the background, arrows, and particles of
GravityField
s - Opacity of
GravityLine
s, including the flash time
A controller entity that manages:
- Enabling VVVVVV-mode, which allows Madeline to flip gravity at will by pressing jump while on the ground
- The sound to play when flipping with jump
- Whether or not to disable dash while VVVVVV mode is active (defaults to true)
- Whether or not to disable grab while VVVVVV mode is active (defaults to true)
A DreamBlock
that will change Madeline's gravity upon exit.
A subclass of GravityTrigger
that will display seeker barrier-style particles and overlaid arrow decals.
Can be configured to support Madeline and/or any holdable/non-holdable Actor
.
A line entity (position + one node) that will trigger a gravity change when Madeline (or a supported Actor) crosses it. Similar to VVVVVV inversion planes. Can be any orientation, even oblique. Traversal checks are done by checking which side of the line the entity is on this frame rather than using collisions. This means that it's theoretically "uncheesable" since you can't warp through it without triggering it, regardless of your speed.
A Refill
that provides a single 'charge' that causes the next dash to toggle gravity.
Has a cool sprite and animated indicator above Madeline's head. Can be configured to provide more than one charge, but the entity looks the same.
A Spring
that will change Madeline's gravity upon touch. Can be attached to the ceiling. Will change gravity of other Actors (Theo etc.) if supported.
A CoreModeToggle
-style switch that will change gravity upon touch. Holdables will also trigger the switch if thrown/dropped (can be disabled).
A platform similar to JumpthruPlatform
that allows Actor
s to fall through it but not move upward.
Similar to the entity of the same name in MaxHelpingHand, and both are supported.
A Lookout
(binoculars/watchtower) that is rendered upside-down and should be attached to the ceiling.
A trigger that will enable or disable VVVVVV-mode. Requires that a VVVVVV controller exist with mode set to "trigger-based".
There are currently four different kinds of controller. Behavior, Visual, Sound, and VVVVVV. Most of the time you will only ever need zero or one of each of these types.
Controllers flagged as "persistent" act as a global default setting, and will be loaded at all times, regardless of which room the controller is added to. You can customise individual rooms by adding a non-persistent controller of a given type, which will set the defaults for just that room.
There must always be exactly zero or one persistent controllers in a map of a given type if you wish to use it. Having two or more persistent controllers of the same type will result in undefined behaviour. Additionally, you cannot have multiple controllers of the same type in a single room.
Most things.
https://github.com/swoolcock/GravityHelper/issues
Make maps and playtest. If you find anything not in the issue list that doesn't work as expected, please let me know. I always welcome decent pull requests or even the occasional "me too" reply as long as it has additional information.
- VampireFlower for all their testing and TASing. Without you GravityHelper would probably not exist.
- coloursofnoise for the initial proof-of-concept hooks.
- JaThePlayer for the original inspiration.
- Viv for the Badeline chaser hooks.
- maddie480 for putting up with my upside-down jumpthru complaints for months.
- Cruor/Vexatos for Ahorn/Lönn.
- 0x0ade and the Everest team for Everest.
- The Strawberry Jam team.
- The Celeste modding community for waiting patiently for years.
- Maddy/Noel/Kevin/Lena and all the EXOK team for being amazing people and creating a work of art enjoyed by so many people around the world.
- Anyone else I've missed.
Go forth and make cool stuff! <3