Add the Bubble Flower power-up - #113
Conversation
…nd supporting registrations Co-authored-by: Hugman76 <36963654+Hugman76@users.noreply.github.com>
…tBubblePowerUpAction Co-authored-by: Hugman76 <36963654+Hugman76@users.noreply.github.com>
Co-authored-by: Hugman76 <36963654+Hugman76@users.noreply.github.com>
Co-authored-by: Hugman76 <36963654+Hugman76@users.noreply.github.com>
…ging The entity did not compile (startRiding(Entity, boolean) does not exist in this version) and several behaviours were inverted or missing compared to the design in issue #111. Bubble entity, rewritten: - float upwards as horizontal speed drops, instead of the other way around - hold whatever is inside as a regular passenger, so vanilla syncs and saves it, and the client bounding box (and sprite scale) is finally correct - rebound off blocks from the actual movement rather than from a pre-emptive ray cast, with a squish animation and no rebound spam while resting - persist lifetimes as saved counters instead of comparing to tickCount, which is not persisted and made reloaded bubbles immortal - swallow Super Mario entities into their capture loot, hold items as collectibles, and pop for anything that touches a filled bubble - restore a trapped mob's AI from removePassenger, the single point every way out of a bubble goes through - keep the stomper's horizontal momentum by reading getKnownMovement() - steer towards nearby targets like an aim assist rather than homing in Power-up recharging: - only sync power-up properties when the charge count actually changes, rather than on every tick of a running cooldown - split addEntity() into useCharge() and trackEntity(), so the tracked entity list no longer grows forever outside FROM_ACTIVE_ENTITIES - shoot projectiles centered on the eye line instead of resting their bottom on it Adds the capture loot table, the trap blacklist and collectible item tags, a placeholder bubble texture, and the client-side tumble for trapped entities. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings in the port to 26.2-snapshot-3 and the *Keys -> *Ids reference rename. Conflict resolutions: - registries and datagen providers keep dev's *Ids references, with the bubble entries added back on top - tag providers move to dev's builder(...) form taking ids; the module's FIREBALL id is qualified because vanilla now exposes one under that name too - CameraRenderState moved to the state.level package, so BubbleRenderer follows Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- run applyEffectsFromBlocks() after move(). Entity.move() only records the blocks that were crossed; without this call nothing runs their "entity inside" behaviour, which is why bubbles went through Nether portals without teleporting and never pressed pressure plates - notify the block a bubble rebounds against through onProjectileHit, so target blocks (and anything else keyed on projectile hits) react - pop on any damage above zero instead of only on projectile and explosion sources, so a bare fist or a sword pops a bubble like it would a painting. Wind charges still deal no damage, so they only blow it around - pop on contact with a foreign projectile straight from the bubble's own collision scan, rather than relying on the projectile choosing to deal damage to a target that has no health Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The cooldown timers and refill conditions need to be checked up against Mario Wonder again, I'm not sure about them. |
|
After trying in wonder, firing a bubble opens a window of roughly 1,2 seconds where you can fire at most 2 bubbles (counting the initial bubble). Then that window rests until the next time you fire a bubble. |
In Wonder, shooting a bubble opens a window of about 1.2 seconds that fits two bubbles, counting the one that opened it. The window then runs out and resets, waiting for the next shot. - New BURST_RECHARGE charge counting: the first use opens a window of `interval` ticks, and every charge comes back at once once it runs out. - Charge setup moves into a PowerUpCharges record, so the counting, the number of charges and the interval all come from data instead of being guessed from whichever fields happen to be set on the action. - The countdown is now started from inside the properties, so an action no longer has to set the cooldown itself. - The Bubble Flower shoots 2 bubbles per 24 tick window. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NyTB2gv3uaiYrdtDdRTbor
Brings in the port to 26.2 release, the koopa shell rework, the cloud platform placement search, ball trail particles and the new test and testmod module. Conflict resolutions: - EntityMixin keeps dev's cheaper move() hook, which reads the resolved movement out of a local instead of running collide() a second time, along with the fall grace this branch adds - CollectibleItem keeps the shared cosmetics helper over the inlined copy, with dev's renamed stack config call - SpawnCloudPlatformPowerUpAction keeps dev's placement search, calling useCharge() since addEntity() was split in two on this branch Test fallout from the API changes this branch makes: - the game tests and their data pack fixtures move to PowerUpCharges and to useCharge(), the charge count having moved into a record - the PowerUpProperties unit tests drive the cooldown through useCharge() and the constructor, setCooldown() no longer being public, and cover the interval that the codecs gained - super_mario:bubble_can_trap is now written out empty rather than left undeclared, and is exempt from the "a declared tag must not be empty" check: it exists so data packs can make something trappable that the automatic size and health criteria turn down Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| // Shipped empty on purpose: the opposite exception, for data packs to make something trappable that the | ||
| // automatic size and health criteria turn down. Still written out, so the tag is there to be added to. | ||
| builder(BUBBLE_CAN_TRAP); |
There was a problem hiding this comment.
I believe we don't HAVE to put the tag in the datagen to make it available for other datapacks
| SuperMarioEntityTypes.BUBBLE, | ||
| Optional.empty(), // the bubble plays its own "appear" sound as it spawns | ||
| 0.4f, | ||
| // Wonder gives a 1.2s window per burst, and two bubbles to spend in it. |
| /** Items that come back to the world as a {@code mubble:collectible} rather than as a plain item entity. */ | ||
| public static final TagKey<Item> SPAWNS_AS_COLLECTIBLE = bind("spawns_as_collectible"); |
There was a problem hiding this comment.
This tag should be renamed as bubble/catch_as_collectible. Comment is unnecessary.
| /** Ticks before an empty bubble pops on its own. */ | ||
| public static final int DEFAULT_LIFETIME = 100; | ||
| /** Ticks before a bubble pops after it caught something. */ | ||
| public static final int DEFAULT_FILLED_LIFETIME = 100; | ||
| /** Grace period during which the bubble ignores its owner, so it does not pop right where it spawned. */ | ||
| public static final int OWNER_POP_DELAY = 20; | ||
| /** | ||
| * Grace period during which the owner cannot stomp their own bubble. Without it, shooting one while airborne | ||
| * hands out a free bounce straight away, which is enough to fly forever. | ||
| */ | ||
| public static final int OWNER_STOMP_DELAY = 10; | ||
| /** Ticks the capture animation lasts before the caught entity turns into its loot. */ | ||
| public static final int ABSORB_DURATION = 12; | ||
| /** Ticks the squish animation lasts after rebounding against a block. */ | ||
| public static final int SQUISH_DURATION = 6; | ||
| /** Ticks the wobble lasts after the bubble closes around something. */ | ||
| public static final int CAPTURE_WOBBLE_DURATION = 10; | ||
|
|
||
| public static final double ATTRACT_RADIUS = 2.0; | ||
| public static final float MAX_TRAPPABLE_SIZE = 2.0f; | ||
| /** Zombies and skeletons sit exactly at 20 HP, and the issue lists them as trappable. */ | ||
| public static final float MAX_TRAPPABLE_HEALTH = 20.0f; | ||
| public static final float BASE_SIZE = 0.75f; | ||
| public static final float TRAPPED_PADDING = 0.25f; | ||
| public static final float MAX_SIZE = MAX_TRAPPABLE_SIZE + TRAPPED_PADDING; | ||
|
|
||
| private static final double AIR_FRICTION = 0.97; | ||
| /** Horizontal speed above which the bubble does not rise at all. */ | ||
| private static final double FLOAT_SPEED_THRESHOLD = 0.25; | ||
| private static final double FLOAT_MAX_UP = 0.04; | ||
| private static final double FLOAT_LERP = 0.12; | ||
| private static final double REBOUND_RESTITUTION = 0.6; | ||
| /** Below this, a blocked component is just a bubble resting against a block, not a rebound worth animating. */ | ||
| private static final double MIN_REBOUND_SPEED = 0.01; | ||
| /** How much of the way towards the target the heading turns each tick. */ | ||
| private static final double AIM_ASSIST_STRENGTH = 0.09; | ||
| /** Cosine of the half-angle of the cone the target has to be in. Roughly 55 degrees. */ | ||
| private static final double AIM_ASSIST_MIN_DOT = 0.57; | ||
| private static final float STOMP_BOOST = 0.7f; | ||
| /** | ||
| * Blocks of free fall handed out by a bounce. Vanilla clamps accumulated fall to one block when a | ||
| * wind charge throws you up; a bubble is kinder than that and starts the next fall in credit, so that | ||
| * chaining bounces does not end in a broken ankle. | ||
| */ | ||
| private static final double BOUNCE_FALL_GRACE = 2.0; | ||
| /** How fast the drawn size catches up with the real one when the bubble swallows or releases something. */ | ||
| private static final float SIZE_LERP = 0.4F; | ||
| /** Peak of the squash-and-stretch that runs when the bubble closes around something. */ | ||
| private static final float CAPTURE_WOBBLE_AMOUNT = 0.3F; |
There was a problem hiding this comment.
There are too many comments here. No need to clear them all, but some constants are very easy to understand without any comment.
| builder.define(DATA_FILLED_LIFETIME, DEFAULT_FILLED_LIFETIME); | ||
| } | ||
|
|
||
| // ---------------------------------------------------------------- state |
There was a problem hiding this comment.
This is a weird comment
. It should be replaced with capitalized Intellij region/endregion
|
|
||
| @Override | ||
| public EntityDimensions getDimensions(Pose pose) { | ||
| // Always a cube, whatever is inside. |
- Drop the empty `bubble_can_trap` entry from the entity type tag datagen: data packs can define the tag themselves, the mod does not have to ship the file for it to be usable. The dynamic content game test now skips data pack hooks entirely instead of only skipping the emptiness check. - Rename `mubble:spawns_as_collectible` to `mubble:bubble/catch_as_collectible`. - Replace the horizontal rule section comments in `Bubble` with capitalized IntelliJ region/endregion markers. - Trim comments that only restate what they sit on: the Wonder burst note in the power-up datagen, the constants in `Bubble` whose names already say it, and the one in `getDimensions`.
Adds the Bubble Flower and the Bubble entity it shoots, as described in #111.
In Bubble form the player shoots bubbles that drift forward, catch what they run into, and can be bounced
on. Shooting one opens a 1.2 second window that fits two bubbles, counting the one that opened it; once
the window runs out both come back at once, and the next shot opens a fresh one.
Bubble entity
so target blocks and the like react.
resizes to a slightly larger cube around whatever it holds; the drawn size eases into place while the
hitbox changes in one step.
bubble goes. The tumble leans along the bubble's heading at the moment of capture.
super_mario:gameplay/bubble_capture— a coin by default, and a real collectible entity rather than astored stack, so it keeps a coin's size and spin.
around. Explosions and portals work because the bubble runs the usual "inside block" effects.
their own bubble for a short grace period, otherwise shooting one while airborne is a free flight.
What can be trapped
Entities under 2 blocks in both directions with a base max health of 20 or less, or anything in
super_mario:bubble_can_trap. Health is read from the base attribute, so a leader zombie still counts asan ordinary zombie.
super_mario:bubble_cannot_trapholds the exceptions — bosses, and players.Empty bubbles steer slightly towards a nearby trappable entity, as an aim assist rather than as homing.
Core module
The power-up charge system needed work to support firing in bursts:
BURST_RECHARGEcharge counting: the first use opens a window of a set length, the uses that followrun out the same window, and every charge comes back at once when it is over. The Bubble Flower runs on
2 charges per 24 tick window.
TIMED_RECHARGEcharge counting, one charge back at a fixed interval, built on the previouslyunused cooldown field. Both countdowns are now started from inside the properties, so an action never
has to drive the cooldown itself.
PowerUpChargesrecord (counting, max, interval), so itis configured from data instead of being guessed from whichever fields the action happens to set:
shoot_projectiletakes a singlechargesfield, and any counting can be paired with any max andinterval.
a running cooldown.
addEntity()split intouseCharge()andtrackEntity(), so the tracked entity list no longer growsforever outside
FROM_ACTIVE_ENTITIES.Notes
super_mariomodule; the charge system changes are inmubble-core.lifetime,filled_lifetime,texture,capture_motion, andvanilla
Passengersfor what it holds../gradlew runDatagenafter checking out: the generatedsounds.jsonstill lists the rebound soundthat this branch removes.
Closes #111