Files
SteamWar/LobbySystem/src/de/steamwar/lobby/particle/BodyMotion.java
T
YoyoNow 872da5f62a Open wings with speed and let them flap or breathe
All wings stand 5 degrees open when idle and open up to 15 degrees the
faster the player moves. The wings design gets a motion setting to flap
slowly, flap quickly or breathe; picking a motion redraws the wing every
2 ticks so it looks smooth, and going back to none restores every 20.
Body rotation tracking now also tracks speed, as BodyMotion.
2026-10-26 13:55:58 +01:00

108 lines
3.9 KiB
Java

/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.lobby.particle;
import lombok.experimental.UtilityClass;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.WeakHashMap;
// How a player's body moves: the direction it faces, worked out the way the Minecraft client turns the body, because
// the server's own body rotation for players does not follow what other players see, and how fast it moves.
// Walking turns the body towards the walking direction; standing still it only turns once the head looks more than
// 50 degrees to the side.
@UtilityClass
public class BodyMotion {
private final float MAX_HEAD_TURN = 50;
private final double MOVING = 0.0025;
private final Map<Player, State> states = new WeakHashMap<>();
private static class State {
private float body;
private double x;
private double y;
private double z;
// Blocks per tick, smoothed so single steps do not jerk the wings
private double speed;
}
// Called once per server tick for every online player
public void tick(Player player) {
Location location = player.getLocation();
State state = states.get(player);
if (state == null) {
state = new State();
state.body = location.getYaw();
state.x = location.getX();
state.y = location.getY();
state.z = location.getZ();
states.put(player, state);
return;
}
float head = location.getYaw();
double dx = location.getX() - state.x;
double dy = location.getY() - state.y;
double dz = location.getZ() - state.z;
state.x = location.getX();
state.y = location.getY();
state.z = location.getZ();
state.speed = state.speed * 0.8 + Math.sqrt(dx * dx + dy * dy + dz * dz) * 0.2;
float target = state.body;
if (dx * dx + dz * dz > MOVING) {
target = (float) Math.toDegrees(Math.atan2(dz, dx)) - 90;
// Walking backwards keeps the body facing forwards
float away = Math.abs(wrap(head) - target);
if (away > 95 && away < 265) {
target -= 180;
}
}
state.body += wrap(target - state.body) * 0.3f;
float headTurn = wrap(head - state.body);
if (Math.abs(headTurn) > MAX_HEAD_TURN) {
state.body += headTurn - Math.signum(headTurn) * MAX_HEAD_TURN;
}
}
// The tracked body direction in degrees, or the head direction for players not tracked yet
public float yaw(Player player) {
State state = states.get(player);
return state == null ? player.getLocation().getYaw() : state.body;
}
// Smoothed speed in blocks per tick; sprinting is about 0.28, fast elytra flight well above 1
public double speed(Player player) {
State state = states.get(player);
return state == null ? 0 : state.speed;
}
private float wrap(float degrees) {
float wrapped = degrees % 360;
if (wrapped >= 180) wrapped -= 360;
if (wrapped < -180) wrapped += 360;
return wrapped;
}
}