forked from SteamWar/SteamWar
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.
108 lines
3.9 KiB
Java
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;
|
|
}
|
|
}
|