Let the wing beat touch beat the entry's own wings

On a Wings entry the touch adds a beat after each jump to the wings' motion
and draws nothing of its own; picking it redraws the wings every 2 ticks like
a motion does. Other designs keep the dragon breath wings.
This commit is contained in:
2026-10-26 14:59:32 +01:00
parent 9cb4e0b7db
commit b2300c128e
5 changed files with 45 additions and 9 deletions
@@ -112,7 +112,14 @@ public class Designs {
public ParticleElement build(ParameterValues values, ParticleElement particle) {
WingShape shape = values.get(WING_SHAPE);
return new Wing(particle, values.get(WING_SIZE), shape.design(values.get(WING_DETAIL)),
values.get(WING_COLORING) == WingColoring.ALONG_WING, values.get(WING_SPARKLE), values.get(WING_MOTION));
values.get(WING_COLORING) == WingColoring.ALONG_WING, values.get(WING_SPARKLE), values.get(WING_MOTION),
values.get(EntryParameters.TOUCH) == Touches.JUMP_WINGS);
}
// The wing beat touch makes these wings beat instead of drawing wings of its own
@Override
public boolean absorbsTouch(Touch touch) {
return touch == Touches.JUMP_WINGS;
}
@Override
@@ -133,11 +140,12 @@ public class Designs {
@Override
public void adjust(Parameter<?> changed, ParameterValues values) {
if (changed != WING_MOTION) return;
if (changed != WING_MOTION && changed != EntryParameters.TOUCH) return;
boolean moving = values.get(WING_MOTION) != Wing.Motion.NONE || values.get(EntryParameters.TOUCH) == Touches.JUMP_WINGS;
int interval = values.get(EntryParameters.INTERVAL);
if (values.get(WING_MOTION) != Wing.Motion.NONE && interval > MOVING_WING_INTERVAL) {
if (moving && interval > MOVING_WING_INTERVAL) {
values.set(EntryParameters.INTERVAL, MOVING_WING_INTERVAL);
} else if (values.get(WING_MOTION) == Wing.Motion.NONE && interval == MOVING_WING_INTERVAL) {
} else if (!moving && interval == MOVING_WING_INTERVAL) {
values.set(EntryParameters.INTERVAL, STILL_WING_INTERVAL);
}
}
@@ -73,7 +73,12 @@ public abstract class ParticleDesign {
}
// Called after a design setting changed, to adjust others that depend on it
// Whether the design shows the touch itself instead of the touch drawing its own particles
public boolean absorbsTouch(Touch touch) {
return false;
}
// Called after a setting changed, to adjust others that depend on it
public void adjust(Parameter<?> changed, ParameterValues values) {
}
@@ -39,13 +39,16 @@ public class Touches {
public final Touch NONE = new Touch("none", Material.GRAY_DYE, 0, () -> ParticleRequirement.NO_REQUIRMENT, () -> null);
// On a Wings entry the entry's own wings beat; on others it draws dragon breath wings for the beat
public final Touch JUMP_WINGS = new Touch("jump_wings", Material.DRAGON_BREATH, 2, Unlocks.WARGEAR_SEASON_2022,
() -> new Delayed(new Wing(new SimpleParticle(Particle.DRAGON_BREATH, 0, 0, 0, 0, 1), 0.15, WingDesign.COMPLEX, false, 100, Wing.Motion.JUMP_BEAT), 2));
private final List<Touch> ALL = List.of(
NONE,
// Bursts on jumping, landing or sneaking
new Touch("jump_cloud", Material.SNOWBALL, 1, Unlocks.AIRSHIP_PLACEMENT,
() -> new EventBurst(EventBurst.Trigger.JUMP, new SimpleParticle(Particle.CLOUD, 0.3F, 0.05F, 0.3F, 0.02, 12))),
new Touch("jump_wings", Material.DRAGON_BREATH, 2, Unlocks.WARGEAR_SEASON_2022,
() -> new Delayed(new Wing(new SimpleParticle(Particle.DRAGON_BREATH, 0, 0, 0, 0, 1), 0.15, WingDesign.COMPLEX, false, 100, Wing.Motion.JUMP_BEAT), 2)),
JUMP_WINGS,
new Touch("landing_splash", Material.PRISMARINE_SHARD, 1, Unlocks.UNDERWATER_PLACEMENT,
() -> new EventBurst(EventBurst.Trigger.LAND, 1, new Ring(new SimpleParticle(Particle.SPLASH, 0, 0, 0, 0, 3), 1, 16))),
new Touch("crash_landing", Material.FEATHER, 1, Unlocks.ABSTURZ_PLACEMENT,
@@ -51,6 +51,8 @@ public class Wing extends DelegatingParticleElement {
// Share of the points shown each tick, in percent; below 100 the wing twinkles
private int density = 100;
private Motion motion = Motion.NONE;
// An extra beat after each jump on top of the motion
private boolean beatOnJump;
private double widest;
public Wing(ParticleElement particleElement, double size, WingDesign wingDesign) {
@@ -69,6 +71,11 @@ public class Wing extends DelegatingParticleElement {
}
}
public Wing(ParticleElement particleElement, double size, WingDesign wingDesign, boolean colorAlongWing, int density, Motion motion, boolean beatOnJump) {
this(particleElement, size, wingDesign, colorAlongWing, density, motion);
this.beatOnJump = beatOnJump;
}
@Override
public void tick(ParticleTickData particleTickData) {
if (motion == Motion.JUMP_BEAT && BodyMotion.ticksSinceJump(particleTickData.getPlayer()) > JUMP_BEAT_TICKS) {
@@ -108,14 +115,23 @@ public class Wing extends DelegatingParticleElement {
private double openDegrees(ParticleTickData particleTickData, double ticks) {
double speed = Math.min(1, BodyMotion.speed(particleTickData.getPlayer()) / FULL_OPEN_SPEED);
double open = IDLE_OPEN_DEGREES + (MOVING_OPEN_DEGREES - IDLE_OPEN_DEGREES) * speed;
if (beatOnJump) {
open += jumpBeat(particleTickData);
}
return switch (motion) {
case FLAP_SLOW -> open + FLAP_DEGREES * Math.sin(2 * Math.PI * ticks / 20);
case FLAP_FAST -> open + FLAP_DEGREES * Math.sin(2 * Math.PI * ticks / 10);
case JUMP_BEAT -> open + FLAP_DEGREES * Math.sin(Math.PI * BodyMotion.ticksSinceJump(particleTickData.getPlayer()) / JUMP_BEAT_TICKS);
case JUMP_BEAT -> open + jumpBeat(particleTickData);
default -> open;
};
}
// One swing back and forth during the ticks after a jump
private double jumpBeat(ParticleTickData particleTickData) {
long sinceJump = BodyMotion.ticksSinceJump(particleTickData.getPlayer());
return sinceJump > JUMP_BEAT_TICKS ? 0 : FLAP_DEGREES * Math.sin(Math.PI * sinceJump / JUMP_BEAT_TICKS);
}
// Full wings never draw a random number, so presets keep their exact look
private boolean hidden() {
return density < 100 && RANDOM.nextInt(100) >= density;
@@ -171,6 +171,9 @@ public class ParticleEntry {
// The entry's touch with its placement and visibility, or null; touches ignore conditions and timing
public ParticleElement buildTouch() {
if (design.absorbsTouch(values.get(TOUCH))) {
return null;
}
ParticleElement touch = values.get(TOUCH).element().get();
if (touch == null) {
return null;
@@ -206,7 +209,8 @@ public class ParticleEntry {
// Particle positions spawned per tick, used for the slot budget
public double cost() {
double perTick = design.usesEntryParticle() ? (double) design.points(values) * amount() : design.points(values);
return perTick / values.get(INTERVAL) + values.get(TOUCH).points();
int touchPoints = design.absorbsTouch(values.get(TOUCH)) ? 0 : values.get(TOUCH).points();
return perTick / values.get(INTERVAL) + touchPoints;
}
public JsonObject toJson() {