Make ice balls freeze entities instead of slowing them down - #121
Merged
Conversation
An ice ball no longer hands out slowness: it traps whatever it hits in a block of ice. While frozen, an entity cannot move, its animations hold still, it takes on the pale blue of the ice, its hitbox is filled by an actual ice block model, and it turns solid — others walk into it and stand on top of it. A player walking into its side sends it sliding straight along that axis. How well an entity holds up depends on its bulk: anything from an iron golem upwards cracks the ice open well before it melts and hurts itself doing so, and the bosses listed under `super_mario:freeze_immune` are never trapped at all, only hurt. A frozen player can smash their movement keys to melt their way out sooner. The freeze lives in a synced, persistent `FreezeState` attachment rather than on the entities themselves, which is what makes every living entity freezable without any of them knowing about it. It holds the start and end of the freeze as game times, so the whole thing only has to reach the clients once and they can work the rest out on their own. Closes #118 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CYij5pCRAcCecyXMAKSsz2
Hugman76
force-pushed
the
claude/issue-118-ikarms
branch
from
August 24, 2026 16:38
8a84cbf to
1e72a50
Compare
Member
Author
|
Feature has been playtested and validated |
# Conflicts: # mubble-super_mario/src/client/java/fr/hugman/mubble/super_mario/client/references/SuperMarioRenderStateDataKeys.java # mubble-super_mario/src/client/resources/super_mario.client.mixins.json # mubble-super_mario/src/datagen/java/fr/hugman/mubble/super_mario/data/provider/SuperMarioEnglishLangProvider.java # mubble-super_mario/src/datagen/java/fr/hugman/mubble/super_mario/data/provider/SuperMarioEntityTypeTagsProvider.java # mubble-super_mario/src/main/java/fr/hugman/mubble/super_mario/tags/SuperMarioEntityTypeTags.java
Hugman76
commented
Sep 6, 2026
Tint frozen entities through a shader over the ice block's palette, the way the Gold power-up does, in place of the flat multiply on `getModelTint`. The six colours are the whole palette of the vanilla ice block texture, and the render type is only substituted where vanilla settled on one, so an invisible mob stays invisible inside the ice. Rename `super_mario:melts_freeze` to `super_mario:melts_frozen_entities`. Cut the comments back across the freezing code: drop the ones that restate what the code says, and shorten those that were carrying more prose than the point needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAdapm2U1S6rjAc4sCqwrp
`/freeze set` no longer flips the target when the value is left off: the `<true|false>` is mandatory, and omitting it is now an error rather than a silent toggle. It also takes a duration, in ticks, or the literal `infinite` for a freeze that only a command or fire calls off. `FreezeState` carries that as an `endsAt` of `NEVER`, which nothing chips away at. A duration alongside `false` is turned down, since it means nothing when thawing. Split "cannot be frozen at all" from "an ice ball leaves it alone". A creative player is only the latter, so the command — an operator's tool — can now freeze one, while a stray ice ball still cannot. The tick that lets unfreezable entities out follows the narrower rule, so a commanded freeze survives it. An ice ball that hits an already frozen entity now bursts against it the way it would against a wall, with the block-hit sound: it no longer hurts what is inside, nor cracks the ice it landed on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAdapm2U1S6rjAc4sCqwrp
A block of ice rides the surface instead of sinking. The lift is what it displaces, scaled so it cancels gravity at FLOAT_SUBMERSION, so the block comes to rest with that fraction of its height under the waterline: pushed up while it rides lower, falling back while it rides higher. Ice really floats with almost all of itself under. Sitting higher than that keeps the head of whoever is inside above water, so a frozen mob shoved into a pond bobs rather than drowns, and the cube stays in view. Water also drags on a sliding block of ice much harder than air does, so a shove into a pond runs itself out rather than skating across. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAdapm2U1S6rjAc4sCqwrp
`/freeze set <target> <time>` replaces `<true|false> [<ticks>|infinite]`. The time is the whole of it: `0` thaws, `infinite` is a freeze that only a command or fire calls off, and anything else freezes for that many ticks. Setting a time on an entity that is already frozen now replaces what it was serving rather than being turned down, the way `/effect give` replaces an effect. `0` on an entity that is not frozen still fails, the way `/effect clear` does with nothing to clear. That leaves `already_frozen` and `thaw_duration` with no caller, so both are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAdapm2U1S6rjAc4sCqwrp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #118.
An ice ball no longer hands out a slowness effect: it traps whatever it hits in a block of ice.
What a frozen entity does
LivingEntity.isImmobile()returns true, which cuts off the movement input and skipsserverAiStep()entirely. The same check runs on the client that moves a player, so a frozen player cannot walk out of their own block of ice either.super_mario:core/frozen_entity, which remaps the entity onto the six colours of the vanilla ice block texture. Built the same way as the Gold power-up's shader, with its own render pipeline and render type.canBeCollidedWithreturns true while frozen, so others walk into it and stand on top of it.FLOAT_SUBMERSION, which is where it comes to rest. That is set well above what real ice would ride at, so the head of whoever is inside stays out of the water.Who resists it
Resistance is judged on hitbox volume, with the bosses named one by one since nothing about a hitbox tells them apart from any other oversized mob:
/freezestill takes themsuper_mario:freeze_immune(ender dragon, wither), spectatorsThe last row is what
Freezing.isUnfreezablecovers — nothing at all can hold those. Everything above it isFreezing.resistanceOf, which is only what an ice ball asks.The issue asks to copy the "big" conditions from the bubble catching system. Bubbles have since landed on
dev(#113) and broughtsuper_mario:bubble_can_trap/super_mario:bubble_cannot_trapwith them, so the two systems now judge "big" by different rules — the volume rule here, tags there. Aligning them is left as follow-up rather than folded into this branch.A frozen player can smash any of their movement keys to melt their way out sooner, 15 ticks per press, sent over a new
super_mario:freeze/strugglepayload./freeze/freeze set <target> <time>puts an entity in the ice by hand, and/freeze query <target>reads back whether one is in there, answering with the usual1or0.The time is the whole of it, and it is mandatory — there is no toggle to trip over:
<time>0/effect cleardoes with nothing to clearinfiniteHow it is stored
The freeze lives in a synced, persistent
FreezeStateattachment rather than on the entities themselves, which is what makes every living entity freezable without any of them knowing about it. It holds the start and the end of the freeze as game times rather than as a countdown, so the whole thing only has to reach the clients once and they work the rest out on their own — a countdown would have been a packet per entity per tick. An endless freeze is that end set toNEVER.Tests
FreezeGameTestcovers the three resistances, the solidity, the sliding, the floating and the struggling;FreezeCommandGameTestcovers the command;BallGameTest.iceballSlowsDownWhatItHitsbecameiceballFreezesWhatItHits, alongside a test that an ice ball bursts on what is already frozen../gradlew runDatagen && ./gradlew test && ./gradlew runGameTest— all 184 game tests pass, datagen reports no drift, and the unit tests are green.The ice shader is not covered by those: render pipelines compile lazily on first use, so it wants a look during playtesting. The mixin behind it was checked by booting the client headless with
-Dmixin.debug.export=true—FrozenLivingEntityRendererMixinapplies toLivingEntityRenderercleanly and its@ModifyReturnValuewraps all four ofgetRenderType's vanilla returns.🤖 Generated with Claude Code
https://claude.ai/code/session_01CYij5pCRAcCecyXMAKSsz2