Update from Git Manager GUI
This commit is contained in:
256
src/main/java/de/fussball/plugin/game/Ball.java
Normal file
256
src/main/java/de/fussball/plugin/game/Ball.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package de.fussball.plugin.game;
|
||||
|
||||
import de.fussball.plugin.Fussball;
|
||||
import de.fussball.plugin.utils.MessageUtil;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.profile.PlayerTextures;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Ball {
|
||||
|
||||
private static final String BALL_TEXTURE_URL =
|
||||
"https://textures.minecraft.net/texture/451f8cfcfb85d77945dc6a3618414093e70436b46d2577b28c727f1329b7265e";
|
||||
|
||||
// Reibung: jeder Tick wird horizontale Geschwindigkeit um diesen Faktor reduziert
|
||||
private static final double FRICTION = 0.82;
|
||||
// Unter dieser Geschwindigkeit → Ball stoppt komplett
|
||||
private static final double MIN_VELOCITY = 0.04;
|
||||
|
||||
private final Game game;
|
||||
private final Fussball plugin;
|
||||
private ArmorStand entity;
|
||||
private final Location spawnLocation;
|
||||
private boolean active = false;
|
||||
|
||||
// ── Torwart-Halten ───────────────────────────────────────────────────────
|
||||
private boolean heldByGoalkeeper = false;
|
||||
private Player holdingPlayer = null;
|
||||
|
||||
public Ball(Game game, Fussball plugin, Location spawnLocation) {
|
||||
this.game = game;
|
||||
this.plugin = plugin;
|
||||
this.spawnLocation = spawnLocation.clone();
|
||||
}
|
||||
|
||||
// ── Config-Helfer ────────────────────────────────────────────────────────
|
||||
|
||||
private double cfg(String path, double def) {
|
||||
return plugin.getConfig().getDouble(path, def);
|
||||
}
|
||||
|
||||
// ── Spawnen ──────────────────────────────────────────────────────────────
|
||||
|
||||
public void spawn() {
|
||||
if (spawnLocation == null || spawnLocation.getWorld() == null) {
|
||||
plugin.getLogger().severe("[Fussball] Ball-Spawn Location ist null!"); return;
|
||||
}
|
||||
if (entity != null && !entity.isDead()) entity.remove();
|
||||
|
||||
spawnLocation.getWorld().loadChunk(spawnLocation.getBlockX() >> 4, spawnLocation.getBlockZ() >> 4);
|
||||
|
||||
entity = spawnLocation.getWorld().spawn(
|
||||
spawnLocation, ArmorStand.class, CreatureSpawnEvent.SpawnReason.CUSTOM, false,
|
||||
stand -> {
|
||||
stand.setVisible(false);
|
||||
stand.setGravity(true);
|
||||
stand.setCollidable(false);
|
||||
stand.setInvulnerable(true);
|
||||
stand.setPersistent(false);
|
||||
stand.setSilent(true);
|
||||
stand.setSmall(true);
|
||||
stand.setArms(false);
|
||||
stand.setBasePlate(false);
|
||||
stand.setCustomName("§e⚽ Fußball");
|
||||
stand.setCustomNameVisible(true);
|
||||
stand.getEquipment().setHelmet(createBallItem());
|
||||
}
|
||||
);
|
||||
|
||||
if (entity == null) {
|
||||
plugin.getLogger().severe("[Fussball] ArmorStand konnte nicht gespawnt werden!"); return;
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
|
||||
private ItemStack createBallItem() {
|
||||
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta == null) return skull;
|
||||
meta.setDisplayName("§e⚽ Fußball");
|
||||
try {
|
||||
PlayerProfile profile = Bukkit.createPlayerProfile(
|
||||
UUID.nameUUIDFromBytes("FussballBall".getBytes()), "FussballBall");
|
||||
PlayerTextures textures = profile.getTextures();
|
||||
textures.setSkin(new URL(BALL_TEXTURE_URL));
|
||||
profile.setTextures(textures);
|
||||
meta.setOwnerProfile(profile);
|
||||
} catch (MalformedURLException e) {
|
||||
plugin.getLogger().warning("[Fussball] Ball-Textur URL ungültig: " + e.getMessage());
|
||||
}
|
||||
skull.setItemMeta(meta);
|
||||
return skull;
|
||||
}
|
||||
|
||||
// ── Schuss ───────────────────────────────────────────────────────────────
|
||||
|
||||
/** Normaler Schuss (Rechtsklick) */
|
||||
public void kick(Player player) {
|
||||
if (entity == null || entity.isDead() || !active) return;
|
||||
Location ballLoc = entity.getLocation();
|
||||
Vector dir = getKickDirection(player);
|
||||
|
||||
double kickVertical = cfg("ball.kick-vertical", 0.3);
|
||||
double kickPower = cfg("ball.kick-power", 1.1);
|
||||
double sprintKickPower = cfg("ball.sprint-kick-power", 1.8);
|
||||
|
||||
dir.setY(kickVertical);
|
||||
dir.multiply(player.isSprinting() ? sprintKickPower : kickPower);
|
||||
applyKick(dir, ballLoc, 1.8f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aufgeladener Schuss (Shift gedrückt halten → loslassen)
|
||||
* power = 0.0 (kurz gehalten) bis 1.0 (voll aufgeladen, ~1.5s)
|
||||
*/
|
||||
public void chargedKick(Player player, double power) {
|
||||
if (entity == null || entity.isDead() || !active) return;
|
||||
Location ballLoc = entity.getLocation();
|
||||
Vector dir = getKickDirection(player);
|
||||
|
||||
double minPower = cfg("ball.charged-min-power", 1.3);
|
||||
double maxPower = cfg("ball.charged-max-power", 3.8);
|
||||
double actualMultiplier = minPower + (maxPower - minPower) * power;
|
||||
|
||||
dir.setY(0.25 + power * 0.25); // mehr Loft bei vollem Schuss
|
||||
dir.multiply(actualMultiplier);
|
||||
float pitch = 1.0f + (float) (power * 0.8f);
|
||||
applyKick(dir, ballLoc, pitch);
|
||||
|
||||
// Feuer-Partikel ab 70% Ladung
|
||||
if (power > 0.7) {
|
||||
ballLoc.getWorld().spawnParticle(Particle.FLAME, ballLoc, 12, 0.2, 0.2, 0.2, 0.06);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector getKickDirection(Player player) {
|
||||
Vector dir = entity.getLocation().toVector().subtract(player.getLocation().toVector());
|
||||
if (dir.lengthSquared() < 0.0001) dir = player.getLocation().getDirection();
|
||||
dir.normalize();
|
||||
return dir;
|
||||
}
|
||||
|
||||
private void applyKick(Vector dir, Location ballLoc, float soundPitch) {
|
||||
entity.setVelocity(dir);
|
||||
ballLoc.getWorld().playSound(ballLoc, Sound.ENTITY_GENERIC_SMALL_FALL, 1.0f, soundPitch);
|
||||
ballLoc.getWorld().spawnParticle(Particle.POOF, ballLoc, 5, 0.1, 0.1, 0.1, 0.05);
|
||||
}
|
||||
|
||||
// ── Physik ───────────────────────────────────────────────────────────────
|
||||
|
||||
public void applyFriction() {
|
||||
if (heldByGoalkeeper) return; // Kein Reibung wenn der TW den Ball hält
|
||||
if (entity == null || entity.isDead() || !active) return;
|
||||
Vector vel = entity.getVelocity();
|
||||
double hx = vel.getX() * FRICTION;
|
||||
double hz = vel.getZ() * FRICTION;
|
||||
if (Math.abs(hx) < MIN_VELOCITY) hx = 0;
|
||||
if (Math.abs(hz) < MIN_VELOCITY) hz = 0;
|
||||
entity.setVelocity(new Vector(hx, vel.getY(), hz));
|
||||
}
|
||||
|
||||
// ── Torwart-Mechanik ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Torwart greift den Ball – Ball schwebt vor ihm und folgt ihm.
|
||||
* Prüft vorher, ob er im erlaubten Bereich ist.
|
||||
*/
|
||||
public void holdBall(Player goalkeeper) {
|
||||
if (entity == null || entity.isDead() || !active) return;
|
||||
|
||||
// NEUE REGEL: Prüfen, ob Spieler im 2-Block-Radius am Tor ist
|
||||
if (!game.isAllowedToHoldBall(goalkeeper)) {
|
||||
goalkeeper.sendMessage(MessageUtil.error("Du kannst den Ball nur innerhalb von 2 Blöcken am Tor halten!"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.heldByGoalkeeper = true;
|
||||
this.holdingPlayer = goalkeeper;
|
||||
entity.setGravity(false);
|
||||
entity.setVelocity(new Vector(0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Muss jeden Tick aufgerufen werden damit der Ball dem TW folgt.
|
||||
*/
|
||||
public void updateHeldPosition() {
|
||||
if (!heldByGoalkeeper || holdingPlayer == null || entity == null || entity.isDead()) return;
|
||||
Location hold = holdingPlayer.getLocation().clone();
|
||||
hold.add(holdingPlayer.getLocation().getDirection().normalize().multiply(0.8));
|
||||
hold.setY(hold.getY() + 1.0);
|
||||
entity.teleport(hold);
|
||||
entity.setVelocity(new Vector(0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Torwart lässt den Ball los ohne zu werfen (z.B. fallen lassen).
|
||||
*/
|
||||
public void releaseBall() {
|
||||
this.heldByGoalkeeper = false;
|
||||
this.holdingPlayer = null;
|
||||
if (entity != null && !entity.isDead()) {
|
||||
entity.setGravity(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Torwart wirft den Ball mit gegebener Stärke (0.5 – 2.5).
|
||||
*/
|
||||
public void throwBall(Player goalkeeper, double power) {
|
||||
releaseBall();
|
||||
if (entity == null || entity.isDead()) return;
|
||||
Vector dir = goalkeeper.getLocation().getDirection();
|
||||
dir.setY(dir.getY() + 0.25);
|
||||
dir.multiply(power);
|
||||
entity.setVelocity(dir);
|
||||
Location loc = entity.getLocation();
|
||||
loc.getWorld().playSound(loc, Sound.ENTITY_SNOWBALL_THROW, 1f, 1.1f);
|
||||
loc.getWorld().spawnParticle(Particle.POOF, loc, 5, 0.1, 0.1, 0.1, 0.04);
|
||||
}
|
||||
|
||||
public boolean isHeld() { return heldByGoalkeeper; }
|
||||
public Player getHoldingPlayer() { return holdingPlayer; }
|
||||
|
||||
// ── Util ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public void returnToCenter() {
|
||||
if (entity != null && !entity.isDead()) {
|
||||
entity.teleport(spawnLocation);
|
||||
entity.setVelocity(new Vector(0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (entity != null && !entity.isDead()) entity.remove();
|
||||
entity = null;
|
||||
active = false;
|
||||
}
|
||||
|
||||
public ArmorStand getEntity() { return entity; }
|
||||
public boolean isActive() { return active; }
|
||||
public Location getSpawnLocation() { return spawnLocation; }
|
||||
|
||||
public double getDistanceTo(Player player) {
|
||||
if (entity == null || entity.isDead()) return Double.MAX_VALUE;
|
||||
if (!entity.getWorld().equals(player.getWorld())) return Double.MAX_VALUE;
|
||||
return entity.getLocation().distance(player.getLocation());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user